echarts-en.common.js 1.8 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022610236102461025610266102761028610296103061031610326103361034610356103661037610386103961040610416104261043610446104561046610476104861049610506105161052610536105461055610566105761058610596106061061610626106361064610656106661067610686106961070610716107261073610746107561076610776107861079610806108161082610836108461085610866108761088610896109061091
  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. /*
  7. * Licensed to the Apache Software Foundation (ASF) under one
  8. * or more contributor license agreements. See the NOTICE file
  9. * distributed with this work for additional information
  10. * regarding copyright ownership. The ASF licenses this file
  11. * to you under the Apache License, Version 2.0 (the
  12. * "License"); you may not use this file except in compliance
  13. * with the License. You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing,
  18. * software distributed under the License is distributed on an
  19. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20. * KIND, either express or implied. See the License for the
  21. * specific language governing permissions and limitations
  22. * under the License.
  23. */
  24. // (1) The code `if (__DEV__) ...` can be removed by build tool.
  25. // (2) If intend to use `__DEV__`, this module should be imported. Use a global
  26. // variable `__DEV__` may cause that miss the declaration (see #6535), or the
  27. // declaration is behind of the using position (for example in `Model.extent`,
  28. // And tools like rollup can not analysis the dependency if not import).
  29. var dev;
  30. // In browser
  31. if (typeof window !== 'undefined') {
  32. dev = window.__DEV__;
  33. }
  34. // In node
  35. else if (typeof global !== 'undefined') {
  36. dev = global.__DEV__;
  37. }
  38. if (typeof dev === 'undefined') {
  39. dev = true;
  40. }
  41. var __DEV__ = dev;
  42. /**
  43. * zrender: 生成唯一id
  44. *
  45. * @author errorrik (errorrik@gmail.com)
  46. */
  47. var idStart = 0x0907;
  48. var guid = function () {
  49. return idStart++;
  50. };
  51. /**
  52. * echarts设备环境识别
  53. *
  54. * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
  55. * @author firede[firede@firede.us]
  56. * @desc thanks zepto.
  57. */
  58. var env = {};
  59. if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {
  60. // In Weixin Application
  61. env = {
  62. browser: {},
  63. os: {},
  64. node: false,
  65. wxa: true, // Weixin Application
  66. canvasSupported: true,
  67. svgSupported: false,
  68. touchEventsSupported: true,
  69. domSupported: false
  70. };
  71. }
  72. else if (typeof document === 'undefined' && typeof self !== 'undefined') {
  73. // In worker
  74. env = {
  75. browser: {},
  76. os: {},
  77. node: false,
  78. worker: true,
  79. canvasSupported: true,
  80. domSupported: false
  81. };
  82. }
  83. else if (typeof navigator === 'undefined') {
  84. // In node
  85. env = {
  86. browser: {},
  87. os: {},
  88. node: true,
  89. worker: false,
  90. // Assume canvas is supported
  91. canvasSupported: true,
  92. svgSupported: true,
  93. domSupported: false
  94. };
  95. }
  96. else {
  97. env = detect(navigator.userAgent);
  98. }
  99. var env$1 = env;
  100. // Zepto.js
  101. // (c) 2010-2013 Thomas Fuchs
  102. // Zepto.js may be freely distributed under the MIT license.
  103. function detect(ua) {
  104. var os = {};
  105. var browser = {};
  106. // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
  107. // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  108. // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  109. // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  110. // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  111. // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
  112. // var touchpad = webos && ua.match(/TouchPad/);
  113. // var kindle = ua.match(/Kindle\/([\d.]+)/);
  114. // var silk = ua.match(/Silk\/([\d._]+)/);
  115. // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
  116. // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
  117. // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
  118. // var playbook = ua.match(/PlayBook/);
  119. // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
  120. var firefox = ua.match(/Firefox\/([\d.]+)/);
  121. // var safari = webkit && ua.match(/Mobile\//) && !chrome;
  122. // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
  123. var ie = ua.match(/MSIE\s([\d.]+)/)
  124. // IE 11 Trident/7.0; rv:11.0
  125. || ua.match(/Trident\/.+?rv:(([\d.]+))/);
  126. var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
  127. var weChat = (/micromessenger/i).test(ua);
  128. // Todo: clean this up with a better OS/browser seperation:
  129. // - discern (more) between multiple browsers on android
  130. // - decide if kindle fire in silk mode is android or not
  131. // - Firefox on Android doesn't specify the Android version
  132. // - possibly devide in os, device and browser hashes
  133. // if (browser.webkit = !!webkit) browser.version = webkit[1];
  134. // if (android) os.android = true, os.version = android[2];
  135. // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
  136. // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
  137. // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  138. // if (webos) os.webos = true, os.version = webos[2];
  139. // if (touchpad) os.touchpad = true;
  140. // if (blackberry) os.blackberry = true, os.version = blackberry[2];
  141. // if (bb10) os.bb10 = true, os.version = bb10[2];
  142. // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
  143. // if (playbook) browser.playbook = true;
  144. // if (kindle) os.kindle = true, os.version = kindle[1];
  145. // if (silk) browser.silk = true, browser.version = silk[1];
  146. // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
  147. // if (chrome) browser.chrome = true, browser.version = chrome[1];
  148. if (firefox) {
  149. browser.firefox = true;
  150. browser.version = firefox[1];
  151. }
  152. // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
  153. // if (webview) browser.webview = true;
  154. if (ie) {
  155. browser.ie = true;
  156. browser.version = ie[1];
  157. }
  158. if (edge) {
  159. browser.edge = true;
  160. browser.version = edge[1];
  161. }
  162. // It is difficult to detect WeChat in Win Phone precisely, because ua can
  163. // not be set on win phone. So we do not consider Win Phone.
  164. if (weChat) {
  165. browser.weChat = true;
  166. }
  167. // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
  168. // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
  169. // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
  170. // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
  171. // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
  172. return {
  173. browser: browser,
  174. os: os,
  175. node: false,
  176. // 原生canvas支持,改极端点了
  177. // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
  178. canvasSupported: !!document.createElement('canvas').getContext,
  179. svgSupported: typeof SVGRect !== 'undefined',
  180. // works on most browsers
  181. // IE10/11 does not support touch event, and MS Edge supports them but not by
  182. // default, so we dont check navigator.maxTouchPoints for them here.
  183. touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
  184. // <http://caniuse.com/#search=pointer%20event>.
  185. pointerEventsSupported: 'onpointerdown' in window
  186. // Firefox supports pointer but not by default, only MS browsers are reliable on pointer
  187. // events currently. So we dont use that on other browsers unless tested sufficiently.
  188. // Although IE 10 supports pointer event, it use old style and is different from the
  189. // standard. So we exclude that. (IE 10 is hardly used on touch device)
  190. && (browser.edge || (browser.ie && browser.version >= 11)),
  191. // passiveSupported: detectPassiveSupport()
  192. domSupported: typeof document !== 'undefined'
  193. };
  194. }
  195. // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
  196. // function detectPassiveSupport() {
  197. // // Test via a getter in the options object to see if the passive property is accessed
  198. // var supportsPassive = false;
  199. // try {
  200. // var opts = Object.defineProperty({}, 'passive', {
  201. // get: function() {
  202. // supportsPassive = true;
  203. // }
  204. // });
  205. // window.addEventListener('testPassive', function() {}, opts);
  206. // } catch (e) {
  207. // }
  208. // return supportsPassive;
  209. // }
  210. /**
  211. * @module zrender/core/util
  212. */
  213. // 用于处理merge时无法遍历Date等对象的问题
  214. var BUILTIN_OBJECT = {
  215. '[object Function]': 1,
  216. '[object RegExp]': 1,
  217. '[object Date]': 1,
  218. '[object Error]': 1,
  219. '[object CanvasGradient]': 1,
  220. '[object CanvasPattern]': 1,
  221. // For node-canvas
  222. '[object Image]': 1,
  223. '[object Canvas]': 1
  224. };
  225. var TYPED_ARRAY = {
  226. '[object Int8Array]': 1,
  227. '[object Uint8Array]': 1,
  228. '[object Uint8ClampedArray]': 1,
  229. '[object Int16Array]': 1,
  230. '[object Uint16Array]': 1,
  231. '[object Int32Array]': 1,
  232. '[object Uint32Array]': 1,
  233. '[object Float32Array]': 1,
  234. '[object Float64Array]': 1
  235. };
  236. var objToString = Object.prototype.toString;
  237. var arrayProto = Array.prototype;
  238. var nativeForEach = arrayProto.forEach;
  239. var nativeFilter = arrayProto.filter;
  240. var nativeSlice = arrayProto.slice;
  241. var nativeMap = arrayProto.map;
  242. var nativeReduce = arrayProto.reduce;
  243. // Avoid assign to an exported variable, for transforming to cjs.
  244. var methods = {};
  245. function $override(name, fn) {
  246. // Clear ctx instance for different environment
  247. if (name === 'createCanvas') {
  248. _ctx = null;
  249. }
  250. methods[name] = fn;
  251. }
  252. /**
  253. * Those data types can be cloned:
  254. * Plain object, Array, TypedArray, number, string, null, undefined.
  255. * Those data types will be assgined using the orginal data:
  256. * BUILTIN_OBJECT
  257. * Instance of user defined class will be cloned to a plain object, without
  258. * properties in prototype.
  259. * Other data types is not supported (not sure what will happen).
  260. *
  261. * Caution: do not support clone Date, for performance consideration.
  262. * (There might be a large number of date in `series.data`).
  263. * So date should not be modified in and out of echarts.
  264. *
  265. * @param {*} source
  266. * @return {*} new
  267. */
  268. function clone(source) {
  269. if (source == null || typeof source !== 'object') {
  270. return source;
  271. }
  272. var result = source;
  273. var typeStr = objToString.call(source);
  274. if (typeStr === '[object Array]') {
  275. if (!isPrimitive(source)) {
  276. result = [];
  277. for (var i = 0, len = source.length; i < len; i++) {
  278. result[i] = clone(source[i]);
  279. }
  280. }
  281. }
  282. else if (TYPED_ARRAY[typeStr]) {
  283. if (!isPrimitive(source)) {
  284. var Ctor = source.constructor;
  285. if (source.constructor.from) {
  286. result = Ctor.from(source);
  287. }
  288. else {
  289. result = new Ctor(source.length);
  290. for (var i = 0, len = source.length; i < len; i++) {
  291. result[i] = clone(source[i]);
  292. }
  293. }
  294. }
  295. }
  296. else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
  297. result = {};
  298. for (var key in source) {
  299. if (source.hasOwnProperty(key)) {
  300. result[key] = clone(source[key]);
  301. }
  302. }
  303. }
  304. return result;
  305. }
  306. /**
  307. * @memberOf module:zrender/core/util
  308. * @param {*} target
  309. * @param {*} source
  310. * @param {boolean} [overwrite=false]
  311. */
  312. function merge(target, source, overwrite) {
  313. // We should escapse that source is string
  314. // and enter for ... in ...
  315. if (!isObject$1(source) || !isObject$1(target)) {
  316. return overwrite ? clone(source) : target;
  317. }
  318. for (var key in source) {
  319. if (source.hasOwnProperty(key)) {
  320. var targetProp = target[key];
  321. var sourceProp = source[key];
  322. if (isObject$1(sourceProp)
  323. && isObject$1(targetProp)
  324. && !isArray(sourceProp)
  325. && !isArray(targetProp)
  326. && !isDom(sourceProp)
  327. && !isDom(targetProp)
  328. && !isBuiltInObject(sourceProp)
  329. && !isBuiltInObject(targetProp)
  330. && !isPrimitive(sourceProp)
  331. && !isPrimitive(targetProp)
  332. ) {
  333. // 如果需要递归覆盖,就递归调用merge
  334. merge(targetProp, sourceProp, overwrite);
  335. }
  336. else if (overwrite || !(key in target)) {
  337. // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
  338. // NOTE,在 target[key] 不存在的时候也是直接覆盖
  339. target[key] = clone(source[key], true);
  340. }
  341. }
  342. }
  343. return target;
  344. }
  345. /**
  346. * @param {Array} targetAndSources The first item is target, and the rests are source.
  347. * @param {boolean} [overwrite=false]
  348. * @return {*} target
  349. */
  350. function mergeAll(targetAndSources, overwrite) {
  351. var result = targetAndSources[0];
  352. for (var i = 1, len = targetAndSources.length; i < len; i++) {
  353. result = merge(result, targetAndSources[i], overwrite);
  354. }
  355. return result;
  356. }
  357. /**
  358. * @param {*} target
  359. * @param {*} source
  360. * @memberOf module:zrender/core/util
  361. */
  362. function extend(target, source) {
  363. for (var key in source) {
  364. if (source.hasOwnProperty(key)) {
  365. target[key] = source[key];
  366. }
  367. }
  368. return target;
  369. }
  370. /**
  371. * @param {*} target
  372. * @param {*} source
  373. * @param {boolean} [overlay=false]
  374. * @memberOf module:zrender/core/util
  375. */
  376. function defaults(target, source, overlay) {
  377. for (var key in source) {
  378. if (source.hasOwnProperty(key)
  379. && (overlay ? source[key] != null : target[key] == null)
  380. ) {
  381. target[key] = source[key];
  382. }
  383. }
  384. return target;
  385. }
  386. var createCanvas = function () {
  387. return methods.createCanvas();
  388. };
  389. methods.createCanvas = function () {
  390. return document.createElement('canvas');
  391. };
  392. // FIXME
  393. var _ctx;
  394. function getContext() {
  395. if (!_ctx) {
  396. // Use util.createCanvas instead of createCanvas
  397. // because createCanvas may be overwritten in different environment
  398. _ctx = createCanvas().getContext('2d');
  399. }
  400. return _ctx;
  401. }
  402. /**
  403. * 查询数组中元素的index
  404. * @memberOf module:zrender/core/util
  405. */
  406. function indexOf(array, value) {
  407. if (array) {
  408. if (array.indexOf) {
  409. return array.indexOf(value);
  410. }
  411. for (var i = 0, len = array.length; i < len; i++) {
  412. if (array[i] === value) {
  413. return i;
  414. }
  415. }
  416. }
  417. return -1;
  418. }
  419. /**
  420. * 构造类继承关系
  421. *
  422. * @memberOf module:zrender/core/util
  423. * @param {Function} clazz 源类
  424. * @param {Function} baseClazz 基类
  425. */
  426. function inherits(clazz, baseClazz) {
  427. var clazzPrototype = clazz.prototype;
  428. function F() {}
  429. F.prototype = baseClazz.prototype;
  430. clazz.prototype = new F();
  431. for (var prop in clazzPrototype) {
  432. clazz.prototype[prop] = clazzPrototype[prop];
  433. }
  434. clazz.prototype.constructor = clazz;
  435. clazz.superClass = baseClazz;
  436. }
  437. /**
  438. * @memberOf module:zrender/core/util
  439. * @param {Object|Function} target
  440. * @param {Object|Function} sorce
  441. * @param {boolean} overlay
  442. */
  443. function mixin(target, source, overlay) {
  444. target = 'prototype' in target ? target.prototype : target;
  445. source = 'prototype' in source ? source.prototype : source;
  446. defaults(target, source, overlay);
  447. }
  448. /**
  449. * Consider typed array.
  450. * @param {Array|TypedArray} data
  451. */
  452. function isArrayLike(data) {
  453. if (!data) {
  454. return;
  455. }
  456. if (typeof data === 'string') {
  457. return false;
  458. }
  459. return typeof data.length === 'number';
  460. }
  461. /**
  462. * 数组或对象遍历
  463. * @memberOf module:zrender/core/util
  464. * @param {Object|Array} obj
  465. * @param {Function} cb
  466. * @param {*} [context]
  467. */
  468. function each$1(obj, cb, context) {
  469. if (!(obj && cb)) {
  470. return;
  471. }
  472. if (obj.forEach && obj.forEach === nativeForEach) {
  473. obj.forEach(cb, context);
  474. }
  475. else if (obj.length === +obj.length) {
  476. for (var i = 0, len = obj.length; i < len; i++) {
  477. cb.call(context, obj[i], i, obj);
  478. }
  479. }
  480. else {
  481. for (var key in obj) {
  482. if (obj.hasOwnProperty(key)) {
  483. cb.call(context, obj[key], key, obj);
  484. }
  485. }
  486. }
  487. }
  488. /**
  489. * 数组映射
  490. * @memberOf module:zrender/core/util
  491. * @param {Array} obj
  492. * @param {Function} cb
  493. * @param {*} [context]
  494. * @return {Array}
  495. */
  496. function map(obj, cb, context) {
  497. if (!(obj && cb)) {
  498. return;
  499. }
  500. if (obj.map && obj.map === nativeMap) {
  501. return obj.map(cb, context);
  502. }
  503. else {
  504. var result = [];
  505. for (var i = 0, len = obj.length; i < len; i++) {
  506. result.push(cb.call(context, obj[i], i, obj));
  507. }
  508. return result;
  509. }
  510. }
  511. /**
  512. * @memberOf module:zrender/core/util
  513. * @param {Array} obj
  514. * @param {Function} cb
  515. * @param {Object} [memo]
  516. * @param {*} [context]
  517. * @return {Array}
  518. */
  519. function reduce(obj, cb, memo, context) {
  520. if (!(obj && cb)) {
  521. return;
  522. }
  523. if (obj.reduce && obj.reduce === nativeReduce) {
  524. return obj.reduce(cb, memo, context);
  525. }
  526. else {
  527. for (var i = 0, len = obj.length; i < len; i++) {
  528. memo = cb.call(context, memo, obj[i], i, obj);
  529. }
  530. return memo;
  531. }
  532. }
  533. /**
  534. * 数组过滤
  535. * @memberOf module:zrender/core/util
  536. * @param {Array} obj
  537. * @param {Function} cb
  538. * @param {*} [context]
  539. * @return {Array}
  540. */
  541. function filter(obj, cb, context) {
  542. if (!(obj && cb)) {
  543. return;
  544. }
  545. if (obj.filter && obj.filter === nativeFilter) {
  546. return obj.filter(cb, context);
  547. }
  548. else {
  549. var result = [];
  550. for (var i = 0, len = obj.length; i < len; i++) {
  551. if (cb.call(context, obj[i], i, obj)) {
  552. result.push(obj[i]);
  553. }
  554. }
  555. return result;
  556. }
  557. }
  558. /**
  559. * 数组项查找
  560. * @memberOf module:zrender/core/util
  561. * @param {Array} obj
  562. * @param {Function} cb
  563. * @param {*} [context]
  564. * @return {*}
  565. */
  566. function find(obj, cb, context) {
  567. if (!(obj && cb)) {
  568. return;
  569. }
  570. for (var i = 0, len = obj.length; i < len; i++) {
  571. if (cb.call(context, obj[i], i, obj)) {
  572. return obj[i];
  573. }
  574. }
  575. }
  576. /**
  577. * @memberOf module:zrender/core/util
  578. * @param {Function} func
  579. * @param {*} context
  580. * @return {Function}
  581. */
  582. function bind(func, context) {
  583. var args = nativeSlice.call(arguments, 2);
  584. return function () {
  585. return func.apply(context, args.concat(nativeSlice.call(arguments)));
  586. };
  587. }
  588. /**
  589. * @memberOf module:zrender/core/util
  590. * @param {Function} func
  591. * @return {Function}
  592. */
  593. function curry(func) {
  594. var args = nativeSlice.call(arguments, 1);
  595. return function () {
  596. return func.apply(this, args.concat(nativeSlice.call(arguments)));
  597. };
  598. }
  599. /**
  600. * @memberOf module:zrender/core/util
  601. * @param {*} value
  602. * @return {boolean}
  603. */
  604. function isArray(value) {
  605. return objToString.call(value) === '[object Array]';
  606. }
  607. /**
  608. * @memberOf module:zrender/core/util
  609. * @param {*} value
  610. * @return {boolean}
  611. */
  612. function isFunction$1(value) {
  613. return typeof value === 'function';
  614. }
  615. /**
  616. * @memberOf module:zrender/core/util
  617. * @param {*} value
  618. * @return {boolean}
  619. */
  620. function isString(value) {
  621. return objToString.call(value) === '[object String]';
  622. }
  623. /**
  624. * @memberOf module:zrender/core/util
  625. * @param {*} value
  626. * @return {boolean}
  627. */
  628. function isObject$1(value) {
  629. // Avoid a V8 JIT bug in Chrome 19-20.
  630. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  631. var type = typeof value;
  632. return type === 'function' || (!!value && type === 'object');
  633. }
  634. /**
  635. * @memberOf module:zrender/core/util
  636. * @param {*} value
  637. * @return {boolean}
  638. */
  639. function isBuiltInObject(value) {
  640. return !!BUILTIN_OBJECT[objToString.call(value)];
  641. }
  642. /**
  643. * @memberOf module:zrender/core/util
  644. * @param {*} value
  645. * @return {boolean}
  646. */
  647. function isTypedArray(value) {
  648. return !!TYPED_ARRAY[objToString.call(value)];
  649. }
  650. /**
  651. * @memberOf module:zrender/core/util
  652. * @param {*} value
  653. * @return {boolean}
  654. */
  655. function isDom(value) {
  656. return typeof value === 'object'
  657. && typeof value.nodeType === 'number'
  658. && typeof value.ownerDocument === 'object';
  659. }
  660. /**
  661. * Whether is exactly NaN. Notice isNaN('a') returns true.
  662. * @param {*} value
  663. * @return {boolean}
  664. */
  665. function eqNaN(value) {
  666. return value !== value;
  667. }
  668. /**
  669. * If value1 is not null, then return value1, otherwise judget rest of values.
  670. * Low performance.
  671. * @memberOf module:zrender/core/util
  672. * @return {*} Final value
  673. */
  674. function retrieve(values) {
  675. for (var i = 0, len = arguments.length; i < len; i++) {
  676. if (arguments[i] != null) {
  677. return arguments[i];
  678. }
  679. }
  680. }
  681. function retrieve2(value0, value1) {
  682. return value0 != null
  683. ? value0
  684. : value1;
  685. }
  686. function retrieve3(value0, value1, value2) {
  687. return value0 != null
  688. ? value0
  689. : value1 != null
  690. ? value1
  691. : value2;
  692. }
  693. /**
  694. * @memberOf module:zrender/core/util
  695. * @param {Array} arr
  696. * @param {number} startIndex
  697. * @param {number} endIndex
  698. * @return {Array}
  699. */
  700. function slice() {
  701. return Function.call.apply(nativeSlice, arguments);
  702. }
  703. /**
  704. * Normalize css liked array configuration
  705. * e.g.
  706. * 3 => [3, 3, 3, 3]
  707. * [4, 2] => [4, 2, 4, 2]
  708. * [4, 3, 2] => [4, 3, 2, 3]
  709. * @param {number|Array.<number>} val
  710. * @return {Array.<number>}
  711. */
  712. function normalizeCssArray(val) {
  713. if (typeof (val) === 'number') {
  714. return [val, val, val, val];
  715. }
  716. var len = val.length;
  717. if (len === 2) {
  718. // vertical | horizontal
  719. return [val[0], val[1], val[0], val[1]];
  720. }
  721. else if (len === 3) {
  722. // top | horizontal | bottom
  723. return [val[0], val[1], val[2], val[1]];
  724. }
  725. return val;
  726. }
  727. /**
  728. * @memberOf module:zrender/core/util
  729. * @param {boolean} condition
  730. * @param {string} message
  731. */
  732. function assert$1(condition, message) {
  733. if (!condition) {
  734. throw new Error(message);
  735. }
  736. }
  737. /**
  738. * @memberOf module:zrender/core/util
  739. * @param {string} str string to be trimed
  740. * @return {string} trimed string
  741. */
  742. function trim(str) {
  743. if (str == null) {
  744. return null;
  745. }
  746. else if (typeof str.trim === 'function') {
  747. return str.trim();
  748. }
  749. else {
  750. return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
  751. }
  752. }
  753. var primitiveKey = '__ec_primitive__';
  754. /**
  755. * Set an object as primitive to be ignored traversing children in clone or merge
  756. */
  757. function setAsPrimitive(obj) {
  758. obj[primitiveKey] = true;
  759. }
  760. function isPrimitive(obj) {
  761. return obj[primitiveKey];
  762. }
  763. /**
  764. * @constructor
  765. * @param {Object} obj Only apply `ownProperty`.
  766. */
  767. function HashMap(obj) {
  768. var isArr = isArray(obj);
  769. // Key should not be set on this, otherwise
  770. // methods get/set/... may be overrided.
  771. this.data = {};
  772. var thisMap = this;
  773. (obj instanceof HashMap)
  774. ? obj.each(visit)
  775. : (obj && each$1(obj, visit));
  776. function visit(value, key) {
  777. isArr ? thisMap.set(value, key) : thisMap.set(key, value);
  778. }
  779. }
  780. HashMap.prototype = {
  781. constructor: HashMap,
  782. // Do not provide `has` method to avoid defining what is `has`.
  783. // (We usually treat `null` and `undefined` as the same, different
  784. // from ES6 Map).
  785. get: function (key) {
  786. return this.data.hasOwnProperty(key) ? this.data[key] : null;
  787. },
  788. set: function (key, value) {
  789. // Comparing with invocation chaining, `return value` is more commonly
  790. // used in this case: `var someVal = map.set('a', genVal());`
  791. return (this.data[key] = value);
  792. },
  793. // Although util.each can be performed on this hashMap directly, user
  794. // should not use the exposed keys, who are prefixed.
  795. each: function (cb, context) {
  796. context !== void 0 && (cb = bind(cb, context));
  797. for (var key in this.data) {
  798. this.data.hasOwnProperty(key) && cb(this.data[key], key);
  799. }
  800. },
  801. // Do not use this method if performance sensitive.
  802. removeKey: function (key) {
  803. delete this.data[key];
  804. }
  805. };
  806. function createHashMap(obj) {
  807. return new HashMap(obj);
  808. }
  809. function concatArray(a, b) {
  810. var newArray = new a.constructor(a.length + b.length);
  811. for (var i = 0; i < a.length; i++) {
  812. newArray[i] = a[i];
  813. }
  814. var offset = a.length;
  815. for (i = 0; i < b.length; i++) {
  816. newArray[i + offset] = b[i];
  817. }
  818. return newArray;
  819. }
  820. function noop() {}
  821. var zrUtil = (Object.freeze || Object)({
  822. $override: $override,
  823. clone: clone,
  824. merge: merge,
  825. mergeAll: mergeAll,
  826. extend: extend,
  827. defaults: defaults,
  828. createCanvas: createCanvas,
  829. getContext: getContext,
  830. indexOf: indexOf,
  831. inherits: inherits,
  832. mixin: mixin,
  833. isArrayLike: isArrayLike,
  834. each: each$1,
  835. map: map,
  836. reduce: reduce,
  837. filter: filter,
  838. find: find,
  839. bind: bind,
  840. curry: curry,
  841. isArray: isArray,
  842. isFunction: isFunction$1,
  843. isString: isString,
  844. isObject: isObject$1,
  845. isBuiltInObject: isBuiltInObject,
  846. isTypedArray: isTypedArray,
  847. isDom: isDom,
  848. eqNaN: eqNaN,
  849. retrieve: retrieve,
  850. retrieve2: retrieve2,
  851. retrieve3: retrieve3,
  852. slice: slice,
  853. normalizeCssArray: normalizeCssArray,
  854. assert: assert$1,
  855. trim: trim,
  856. setAsPrimitive: setAsPrimitive,
  857. isPrimitive: isPrimitive,
  858. createHashMap: createHashMap,
  859. concatArray: concatArray,
  860. noop: noop
  861. });
  862. var ArrayCtor = typeof Float32Array === 'undefined'
  863. ? Array
  864. : Float32Array;
  865. /**
  866. * 创建一个向量
  867. * @param {number} [x=0]
  868. * @param {number} [y=0]
  869. * @return {Vector2}
  870. */
  871. function create(x, y) {
  872. var out = new ArrayCtor(2);
  873. if (x == null) {
  874. x = 0;
  875. }
  876. if (y == null) {
  877. y = 0;
  878. }
  879. out[0] = x;
  880. out[1] = y;
  881. return out;
  882. }
  883. /**
  884. * 复制向量数据
  885. * @param {Vector2} out
  886. * @param {Vector2} v
  887. * @return {Vector2}
  888. */
  889. function copy(out, v) {
  890. out[0] = v[0];
  891. out[1] = v[1];
  892. return out;
  893. }
  894. /**
  895. * 克隆一个向量
  896. * @param {Vector2} v
  897. * @return {Vector2}
  898. */
  899. function clone$1(v) {
  900. var out = new ArrayCtor(2);
  901. out[0] = v[0];
  902. out[1] = v[1];
  903. return out;
  904. }
  905. /**
  906. * 设置向量的两个项
  907. * @param {Vector2} out
  908. * @param {number} a
  909. * @param {number} b
  910. * @return {Vector2} 结果
  911. */
  912. function set(out, a, b) {
  913. out[0] = a;
  914. out[1] = b;
  915. return out;
  916. }
  917. /**
  918. * 向量相加
  919. * @param {Vector2} out
  920. * @param {Vector2} v1
  921. * @param {Vector2} v2
  922. */
  923. function add(out, v1, v2) {
  924. out[0] = v1[0] + v2[0];
  925. out[1] = v1[1] + v2[1];
  926. return out;
  927. }
  928. /**
  929. * 向量缩放后相加
  930. * @param {Vector2} out
  931. * @param {Vector2} v1
  932. * @param {Vector2} v2
  933. * @param {number} a
  934. */
  935. function scaleAndAdd(out, v1, v2, a) {
  936. out[0] = v1[0] + v2[0] * a;
  937. out[1] = v1[1] + v2[1] * a;
  938. return out;
  939. }
  940. /**
  941. * 向量相减
  942. * @param {Vector2} out
  943. * @param {Vector2} v1
  944. * @param {Vector2} v2
  945. */
  946. function sub(out, v1, v2) {
  947. out[0] = v1[0] - v2[0];
  948. out[1] = v1[1] - v2[1];
  949. return out;
  950. }
  951. /**
  952. * 向量长度
  953. * @param {Vector2} v
  954. * @return {number}
  955. */
  956. function len(v) {
  957. return Math.sqrt(lenSquare(v));
  958. }
  959. var length = len; // jshint ignore:line
  960. /**
  961. * 向量长度平方
  962. * @param {Vector2} v
  963. * @return {number}
  964. */
  965. function lenSquare(v) {
  966. return v[0] * v[0] + v[1] * v[1];
  967. }
  968. var lengthSquare = lenSquare;
  969. /**
  970. * 向量乘法
  971. * @param {Vector2} out
  972. * @param {Vector2} v1
  973. * @param {Vector2} v2
  974. */
  975. function mul(out, v1, v2) {
  976. out[0] = v1[0] * v2[0];
  977. out[1] = v1[1] * v2[1];
  978. return out;
  979. }
  980. /**
  981. * 向量除法
  982. * @param {Vector2} out
  983. * @param {Vector2} v1
  984. * @param {Vector2} v2
  985. */
  986. function div(out, v1, v2) {
  987. out[0] = v1[0] / v2[0];
  988. out[1] = v1[1] / v2[1];
  989. return out;
  990. }
  991. /**
  992. * 向量点乘
  993. * @param {Vector2} v1
  994. * @param {Vector2} v2
  995. * @return {number}
  996. */
  997. function dot(v1, v2) {
  998. return v1[0] * v2[0] + v1[1] * v2[1];
  999. }
  1000. /**
  1001. * 向量缩放
  1002. * @param {Vector2} out
  1003. * @param {Vector2} v
  1004. * @param {number} s
  1005. */
  1006. function scale(out, v, s) {
  1007. out[0] = v[0] * s;
  1008. out[1] = v[1] * s;
  1009. return out;
  1010. }
  1011. /**
  1012. * 向量归一化
  1013. * @param {Vector2} out
  1014. * @param {Vector2} v
  1015. */
  1016. function normalize(out, v) {
  1017. var d = len(v);
  1018. if (d === 0) {
  1019. out[0] = 0;
  1020. out[1] = 0;
  1021. }
  1022. else {
  1023. out[0] = v[0] / d;
  1024. out[1] = v[1] / d;
  1025. }
  1026. return out;
  1027. }
  1028. /**
  1029. * 计算向量间距离
  1030. * @param {Vector2} v1
  1031. * @param {Vector2} v2
  1032. * @return {number}
  1033. */
  1034. function distance(v1, v2) {
  1035. return Math.sqrt(
  1036. (v1[0] - v2[0]) * (v1[0] - v2[0])
  1037. + (v1[1] - v2[1]) * (v1[1] - v2[1])
  1038. );
  1039. }
  1040. var dist = distance;
  1041. /**
  1042. * 向量距离平方
  1043. * @param {Vector2} v1
  1044. * @param {Vector2} v2
  1045. * @return {number}
  1046. */
  1047. function distanceSquare(v1, v2) {
  1048. return (v1[0] - v2[0]) * (v1[0] - v2[0])
  1049. + (v1[1] - v2[1]) * (v1[1] - v2[1]);
  1050. }
  1051. var distSquare = distanceSquare;
  1052. /**
  1053. * 求负向量
  1054. * @param {Vector2} out
  1055. * @param {Vector2} v
  1056. */
  1057. function negate(out, v) {
  1058. out[0] = -v[0];
  1059. out[1] = -v[1];
  1060. return out;
  1061. }
  1062. /**
  1063. * 插值两个点
  1064. * @param {Vector2} out
  1065. * @param {Vector2} v1
  1066. * @param {Vector2} v2
  1067. * @param {number} t
  1068. */
  1069. function lerp(out, v1, v2, t) {
  1070. out[0] = v1[0] + t * (v2[0] - v1[0]);
  1071. out[1] = v1[1] + t * (v2[1] - v1[1]);
  1072. return out;
  1073. }
  1074. /**
  1075. * 矩阵左乘向量
  1076. * @param {Vector2} out
  1077. * @param {Vector2} v
  1078. * @param {Vector2} m
  1079. */
  1080. function applyTransform(out, v, m) {
  1081. var x = v[0];
  1082. var y = v[1];
  1083. out[0] = m[0] * x + m[2] * y + m[4];
  1084. out[1] = m[1] * x + m[3] * y + m[5];
  1085. return out;
  1086. }
  1087. /**
  1088. * 求两个向量最小值
  1089. * @param {Vector2} out
  1090. * @param {Vector2} v1
  1091. * @param {Vector2} v2
  1092. */
  1093. function min(out, v1, v2) {
  1094. out[0] = Math.min(v1[0], v2[0]);
  1095. out[1] = Math.min(v1[1], v2[1]);
  1096. return out;
  1097. }
  1098. /**
  1099. * 求两个向量最大值
  1100. * @param {Vector2} out
  1101. * @param {Vector2} v1
  1102. * @param {Vector2} v2
  1103. */
  1104. function max(out, v1, v2) {
  1105. out[0] = Math.max(v1[0], v2[0]);
  1106. out[1] = Math.max(v1[1], v2[1]);
  1107. return out;
  1108. }
  1109. var vector = (Object.freeze || Object)({
  1110. create: create,
  1111. copy: copy,
  1112. clone: clone$1,
  1113. set: set,
  1114. add: add,
  1115. scaleAndAdd: scaleAndAdd,
  1116. sub: sub,
  1117. len: len,
  1118. length: length,
  1119. lenSquare: lenSquare,
  1120. lengthSquare: lengthSquare,
  1121. mul: mul,
  1122. div: div,
  1123. dot: dot,
  1124. scale: scale,
  1125. normalize: normalize,
  1126. distance: distance,
  1127. dist: dist,
  1128. distanceSquare: distanceSquare,
  1129. distSquare: distSquare,
  1130. negate: negate,
  1131. lerp: lerp,
  1132. applyTransform: applyTransform,
  1133. min: min,
  1134. max: max
  1135. });
  1136. // TODO Draggable for group
  1137. // FIXME Draggable on element which has parent rotation or scale
  1138. function Draggable() {
  1139. this.on('mousedown', this._dragStart, this);
  1140. this.on('mousemove', this._drag, this);
  1141. this.on('mouseup', this._dragEnd, this);
  1142. this.on('globalout', this._dragEnd, this);
  1143. // this._dropTarget = null;
  1144. // this._draggingTarget = null;
  1145. // this._x = 0;
  1146. // this._y = 0;
  1147. }
  1148. Draggable.prototype = {
  1149. constructor: Draggable,
  1150. _dragStart: function (e) {
  1151. var draggingTarget = e.target;
  1152. if (draggingTarget && draggingTarget.draggable) {
  1153. this._draggingTarget = draggingTarget;
  1154. draggingTarget.dragging = true;
  1155. this._x = e.offsetX;
  1156. this._y = e.offsetY;
  1157. this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
  1158. }
  1159. },
  1160. _drag: function (e) {
  1161. var draggingTarget = this._draggingTarget;
  1162. if (draggingTarget) {
  1163. var x = e.offsetX;
  1164. var y = e.offsetY;
  1165. var dx = x - this._x;
  1166. var dy = y - this._y;
  1167. this._x = x;
  1168. this._y = y;
  1169. draggingTarget.drift(dx, dy, e);
  1170. this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
  1171. var dropTarget = this.findHover(x, y, draggingTarget).target;
  1172. var lastDropTarget = this._dropTarget;
  1173. this._dropTarget = dropTarget;
  1174. if (draggingTarget !== dropTarget) {
  1175. if (lastDropTarget && dropTarget !== lastDropTarget) {
  1176. this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
  1177. }
  1178. if (dropTarget && dropTarget !== lastDropTarget) {
  1179. this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
  1180. }
  1181. }
  1182. }
  1183. },
  1184. _dragEnd: function (e) {
  1185. var draggingTarget = this._draggingTarget;
  1186. if (draggingTarget) {
  1187. draggingTarget.dragging = false;
  1188. }
  1189. this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
  1190. if (this._dropTarget) {
  1191. this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
  1192. }
  1193. this._draggingTarget = null;
  1194. this._dropTarget = null;
  1195. }
  1196. };
  1197. function param(target, e) {
  1198. return {target: target, topTarget: e && e.topTarget};
  1199. }
  1200. /**
  1201. * Event Mixin
  1202. * @module zrender/mixin/Eventful
  1203. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1204. * pissang (https://www.github.com/pissang)
  1205. */
  1206. var arrySlice = Array.prototype.slice;
  1207. /**
  1208. * Event dispatcher.
  1209. *
  1210. * @alias module:zrender/mixin/Eventful
  1211. * @constructor
  1212. * @param {Object} [eventProcessor] The object eventProcessor is the scope when
  1213. * `eventProcessor.xxx` called.
  1214. * @param {Function} [eventProcessor.normalizeQuery]
  1215. * param: {string|Object} Raw query.
  1216. * return: {string|Object} Normalized query.
  1217. * @param {Function} [eventProcessor.filter] Event will be dispatched only
  1218. * if it returns `true`.
  1219. * param: {string} eventType
  1220. * param: {string|Object} query
  1221. * return: {boolean}
  1222. * @param {Function} [eventProcessor.afterTrigger] Call after all handlers called.
  1223. * param: {string} eventType
  1224. */
  1225. var Eventful = function (eventProcessor) {
  1226. this._$handlers = {};
  1227. this._$eventProcessor = eventProcessor;
  1228. };
  1229. Eventful.prototype = {
  1230. constructor: Eventful,
  1231. /**
  1232. * The handler can only be triggered once, then removed.
  1233. *
  1234. * @param {string} event The event name.
  1235. * @param {string|Object} [query] Condition used on event filter.
  1236. * @param {Function} handler The event handler.
  1237. * @param {Object} context
  1238. */
  1239. one: function (event, query, handler, context) {
  1240. return on(this, event, query, handler, context, true);
  1241. },
  1242. /**
  1243. * Bind a handler.
  1244. *
  1245. * @param {string} event The event name.
  1246. * @param {string|Object} [query] Condition used on event filter.
  1247. * @param {Function} handler The event handler.
  1248. * @param {Object} [context]
  1249. */
  1250. on: function (event, query, handler, context) {
  1251. return on(this, event, query, handler, context, false);
  1252. },
  1253. /**
  1254. * Whether any handler has bound.
  1255. *
  1256. * @param {string} event
  1257. * @return {boolean}
  1258. */
  1259. isSilent: function (event) {
  1260. var _h = this._$handlers;
  1261. return !_h[event] || !_h[event].length;
  1262. },
  1263. /**
  1264. * Unbind a event.
  1265. *
  1266. * @param {string} event The event name.
  1267. * @param {Function} [handler] The event handler.
  1268. */
  1269. off: function (event, handler) {
  1270. var _h = this._$handlers;
  1271. if (!event) {
  1272. this._$handlers = {};
  1273. return this;
  1274. }
  1275. if (handler) {
  1276. if (_h[event]) {
  1277. var newList = [];
  1278. for (var i = 0, l = _h[event].length; i < l; i++) {
  1279. if (_h[event][i].h !== handler) {
  1280. newList.push(_h[event][i]);
  1281. }
  1282. }
  1283. _h[event] = newList;
  1284. }
  1285. if (_h[event] && _h[event].length === 0) {
  1286. delete _h[event];
  1287. }
  1288. }
  1289. else {
  1290. delete _h[event];
  1291. }
  1292. return this;
  1293. },
  1294. /**
  1295. * Dispatch a event.
  1296. *
  1297. * @param {string} type The event name.
  1298. */
  1299. trigger: function (type) {
  1300. var _h = this._$handlers[type];
  1301. var eventProcessor = this._$eventProcessor;
  1302. if (_h) {
  1303. var args = arguments;
  1304. var argLen = args.length;
  1305. if (argLen > 3) {
  1306. args = arrySlice.call(args, 1);
  1307. }
  1308. var len = _h.length;
  1309. for (var i = 0; i < len;) {
  1310. var hItem = _h[i];
  1311. if (eventProcessor
  1312. && eventProcessor.filter
  1313. && hItem.query != null
  1314. && !eventProcessor.filter(type, hItem.query)
  1315. ) {
  1316. i++;
  1317. continue;
  1318. }
  1319. // Optimize advise from backbone
  1320. switch (argLen) {
  1321. case 1:
  1322. hItem.h.call(hItem.ctx);
  1323. break;
  1324. case 2:
  1325. hItem.h.call(hItem.ctx, args[1]);
  1326. break;
  1327. case 3:
  1328. hItem.h.call(hItem.ctx, args[1], args[2]);
  1329. break;
  1330. default:
  1331. // have more than 2 given arguments
  1332. hItem.h.apply(hItem.ctx, args);
  1333. break;
  1334. }
  1335. if (hItem.one) {
  1336. _h.splice(i, 1);
  1337. len--;
  1338. }
  1339. else {
  1340. i++;
  1341. }
  1342. }
  1343. }
  1344. eventProcessor && eventProcessor.afterTrigger
  1345. && eventProcessor.afterTrigger(type);
  1346. return this;
  1347. },
  1348. /**
  1349. * Dispatch a event with context, which is specified at the last parameter.
  1350. *
  1351. * @param {string} type The event name.
  1352. */
  1353. triggerWithContext: function (type) {
  1354. var _h = this._$handlers[type];
  1355. var eventProcessor = this._$eventProcessor;
  1356. if (_h) {
  1357. var args = arguments;
  1358. var argLen = args.length;
  1359. if (argLen > 4) {
  1360. args = arrySlice.call(args, 1, args.length - 1);
  1361. }
  1362. var ctx = args[args.length - 1];
  1363. var len = _h.length;
  1364. for (var i = 0; i < len;) {
  1365. var hItem = _h[i];
  1366. if (eventProcessor
  1367. && eventProcessor.filter
  1368. && hItem.query != null
  1369. && !eventProcessor.filter(type, hItem.query)
  1370. ) {
  1371. i++;
  1372. continue;
  1373. }
  1374. // Optimize advise from backbone
  1375. switch (argLen) {
  1376. case 1:
  1377. hItem.h.call(ctx);
  1378. break;
  1379. case 2:
  1380. hItem.h.call(ctx, args[1]);
  1381. break;
  1382. case 3:
  1383. hItem.h.call(ctx, args[1], args[2]);
  1384. break;
  1385. default:
  1386. // have more than 2 given arguments
  1387. hItem.h.apply(ctx, args);
  1388. break;
  1389. }
  1390. if (hItem.one) {
  1391. _h.splice(i, 1);
  1392. len--;
  1393. }
  1394. else {
  1395. i++;
  1396. }
  1397. }
  1398. }
  1399. eventProcessor && eventProcessor.afterTrigger
  1400. && eventProcessor.afterTrigger(type);
  1401. return this;
  1402. }
  1403. };
  1404. function normalizeQuery(host, query) {
  1405. var eventProcessor = host._$eventProcessor;
  1406. if (query != null && eventProcessor && eventProcessor.normalizeQuery) {
  1407. query = eventProcessor.normalizeQuery(query);
  1408. }
  1409. return query;
  1410. }
  1411. function on(eventful, event, query, handler, context, isOnce) {
  1412. var _h = eventful._$handlers;
  1413. if (typeof query === 'function') {
  1414. context = handler;
  1415. handler = query;
  1416. query = null;
  1417. }
  1418. if (!handler || !event) {
  1419. return eventful;
  1420. }
  1421. query = normalizeQuery(eventful, query);
  1422. if (!_h[event]) {
  1423. _h[event] = [];
  1424. }
  1425. for (var i = 0; i < _h[event].length; i++) {
  1426. if (_h[event][i].h === handler) {
  1427. return eventful;
  1428. }
  1429. }
  1430. var wrap = {
  1431. h: handler,
  1432. one: isOnce,
  1433. query: query,
  1434. ctx: context || eventful,
  1435. // FIXME
  1436. // Do not publish this feature util it is proved that it makes sense.
  1437. callAtLast: handler.zrEventfulCallAtLast
  1438. };
  1439. var lastIndex = _h[event].length - 1;
  1440. var lastWrap = _h[event][lastIndex];
  1441. (lastWrap && lastWrap.callAtLast)
  1442. ? _h[event].splice(lastIndex, 0, wrap)
  1443. : _h[event].push(wrap);
  1444. return eventful;
  1445. }
  1446. /**
  1447. * 事件辅助类
  1448. * @module zrender/core/event
  1449. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1450. */
  1451. var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
  1452. var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
  1453. function getBoundingClientRect(el) {
  1454. // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
  1455. return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};
  1456. }
  1457. // `calculate` is optional, default false
  1458. function clientToLocal(el, e, out, calculate) {
  1459. out = out || {};
  1460. // According to the W3C Working Draft, offsetX and offsetY should be relative
  1461. // to the padding edge of the target element. The only browser using this convention
  1462. // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
  1463. // not support the properties.
  1464. // (see http://www.jacklmoore.com/notes/mouse-position/)
  1465. // In zr painter.dom, padding edge equals to border edge.
  1466. // FIXME
  1467. // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
  1468. // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
  1469. // is too complex. So css-transfrom dont support in this case temporarily.
  1470. if (calculate || !env$1.canvasSupported) {
  1471. defaultGetZrXY(el, e, out);
  1472. }
  1473. // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
  1474. // ancestor element, so we should make sure el is positioned (e.g., not position:static).
  1475. // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
  1476. // zoom-factor, overflow / opacity layers, transforms ...)
  1477. // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
  1478. // <https://bugs.jquery.com/ticket/8523#comment:14>
  1479. // BTW3, In ff, offsetX/offsetY is always 0.
  1480. else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
  1481. out.zrX = e.layerX;
  1482. out.zrY = e.layerY;
  1483. }
  1484. // For IE6+, chrome, safari, opera. (When will ff support offsetX?)
  1485. else if (e.offsetX != null) {
  1486. out.zrX = e.offsetX;
  1487. out.zrY = e.offsetY;
  1488. }
  1489. // For some other device, e.g., IOS safari.
  1490. else {
  1491. defaultGetZrXY(el, e, out);
  1492. }
  1493. return out;
  1494. }
  1495. function defaultGetZrXY(el, e, out) {
  1496. // This well-known method below does not support css transform.
  1497. var box = getBoundingClientRect(el);
  1498. out.zrX = e.clientX - box.left;
  1499. out.zrY = e.clientY - box.top;
  1500. }
  1501. /**
  1502. * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.
  1503. * `calculate` is optional, default false.
  1504. */
  1505. function normalizeEvent(el, e, calculate) {
  1506. e = e || window.event;
  1507. if (e.zrX != null) {
  1508. return e;
  1509. }
  1510. var eventType = e.type;
  1511. var isTouch = eventType && eventType.indexOf('touch') >= 0;
  1512. if (!isTouch) {
  1513. clientToLocal(el, e, e, calculate);
  1514. e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
  1515. }
  1516. else {
  1517. var touch = eventType !== 'touchend'
  1518. ? e.targetTouches[0]
  1519. : e.changedTouches[0];
  1520. touch && clientToLocal(el, touch, e, calculate);
  1521. }
  1522. // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;
  1523. // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
  1524. // If e.which has been defined, if may be readonly,
  1525. // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
  1526. var button = e.button;
  1527. if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {
  1528. e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
  1529. }
  1530. // [Caution]: `e.which` from browser is not always reliable. For example,
  1531. // when press left button and `mousemove (pointermove)` in Edge, the `e.which`
  1532. // is 65536 and the `e.button` is -1. But the `mouseup (pointerup)` and
  1533. // `mousedown (pointerdown)` is the same as Chrome does.
  1534. return e;
  1535. }
  1536. /**
  1537. * @param {HTMLElement} el
  1538. * @param {string} name
  1539. * @param {Function} handler
  1540. */
  1541. function addEventListener(el, name, handler) {
  1542. if (isDomLevel2) {
  1543. // Reproduct the console warning:
  1544. // [Violation] Added non-passive event listener to a scroll-blocking <some> event.
  1545. // Consider marking event handler as 'passive' to make the page more responsive.
  1546. // Just set console log level: verbose in chrome dev tool.
  1547. // then the warning log will be printed when addEventListener called.
  1548. // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
  1549. // We have not yet found a neat way to using passive. Because in zrender the dom event
  1550. // listener delegate all of the upper events of element. Some of those events need
  1551. // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts.
  1552. // Before passive can be adopted, these issues should be considered:
  1553. // (1) Whether and how a zrender user specifies an event listener passive. And by default,
  1554. // passive or not.
  1555. // (2) How to tread that some zrender event listener is passive, and some is not. If
  1556. // we use other way but not preventDefault of mousewheel and touchmove, browser
  1557. // compatibility should be handled.
  1558. // var opts = (env.passiveSupported && name === 'mousewheel')
  1559. // ? {passive: true}
  1560. // // By default, the third param of el.addEventListener is `capture: false`.
  1561. // : void 0;
  1562. // el.addEventListener(name, handler /* , opts */);
  1563. el.addEventListener(name, handler);
  1564. }
  1565. else {
  1566. el.attachEvent('on' + name, handler);
  1567. }
  1568. }
  1569. function removeEventListener(el, name, handler) {
  1570. if (isDomLevel2) {
  1571. el.removeEventListener(name, handler);
  1572. }
  1573. else {
  1574. el.detachEvent('on' + name, handler);
  1575. }
  1576. }
  1577. /**
  1578. * preventDefault and stopPropagation.
  1579. * Notice: do not do that in zrender. Upper application
  1580. * do that if necessary.
  1581. *
  1582. * @memberOf module:zrender/core/event
  1583. * @method
  1584. * @param {Event} e : event对象
  1585. */
  1586. var stop = isDomLevel2
  1587. ? function (e) {
  1588. e.preventDefault();
  1589. e.stopPropagation();
  1590. e.cancelBubble = true;
  1591. }
  1592. : function (e) {
  1593. e.returnValue = false;
  1594. e.cancelBubble = true;
  1595. };
  1596. /**
  1597. * This method only works for mouseup and mousedown. The functionality is restricted
  1598. * for fault tolerance, See the `e.which` compatibility above.
  1599. *
  1600. * @param {MouseEvent} e
  1601. * @return {boolean}
  1602. */
  1603. function isMiddleOrRightButtonOnMouseUpDown(e) {
  1604. return e.which === 2 || e.which === 3;
  1605. }
  1606. /**
  1607. * To be removed.
  1608. * @deprecated
  1609. */
  1610. /**
  1611. * Only implements needed gestures for mobile.
  1612. */
  1613. var GestureMgr = function () {
  1614. /**
  1615. * @private
  1616. * @type {Array.<Object>}
  1617. */
  1618. this._track = [];
  1619. };
  1620. GestureMgr.prototype = {
  1621. constructor: GestureMgr,
  1622. recognize: function (event, target, root) {
  1623. this._doTrack(event, target, root);
  1624. return this._recognize(event);
  1625. },
  1626. clear: function () {
  1627. this._track.length = 0;
  1628. return this;
  1629. },
  1630. _doTrack: function (event, target, root) {
  1631. var touches = event.touches;
  1632. if (!touches) {
  1633. return;
  1634. }
  1635. var trackItem = {
  1636. points: [],
  1637. touches: [],
  1638. target: target,
  1639. event: event
  1640. };
  1641. for (var i = 0, len = touches.length; i < len; i++) {
  1642. var touch = touches[i];
  1643. var pos = clientToLocal(root, touch, {});
  1644. trackItem.points.push([pos.zrX, pos.zrY]);
  1645. trackItem.touches.push(touch);
  1646. }
  1647. this._track.push(trackItem);
  1648. },
  1649. _recognize: function (event) {
  1650. for (var eventName in recognizers) {
  1651. if (recognizers.hasOwnProperty(eventName)) {
  1652. var gestureInfo = recognizers[eventName](this._track, event);
  1653. if (gestureInfo) {
  1654. return gestureInfo;
  1655. }
  1656. }
  1657. }
  1658. }
  1659. };
  1660. function dist$1(pointPair) {
  1661. var dx = pointPair[1][0] - pointPair[0][0];
  1662. var dy = pointPair[1][1] - pointPair[0][1];
  1663. return Math.sqrt(dx * dx + dy * dy);
  1664. }
  1665. function center(pointPair) {
  1666. return [
  1667. (pointPair[0][0] + pointPair[1][0]) / 2,
  1668. (pointPair[0][1] + pointPair[1][1]) / 2
  1669. ];
  1670. }
  1671. var recognizers = {
  1672. pinch: function (track, event) {
  1673. var trackLen = track.length;
  1674. if (!trackLen) {
  1675. return;
  1676. }
  1677. var pinchEnd = (track[trackLen - 1] || {}).points;
  1678. var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
  1679. if (pinchPre
  1680. && pinchPre.length > 1
  1681. && pinchEnd
  1682. && pinchEnd.length > 1
  1683. ) {
  1684. var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);
  1685. !isFinite(pinchScale) && (pinchScale = 1);
  1686. event.pinchScale = pinchScale;
  1687. var pinchCenter = center(pinchEnd);
  1688. event.pinchX = pinchCenter[0];
  1689. event.pinchY = pinchCenter[1];
  1690. return {
  1691. type: 'pinch',
  1692. target: track[0].target,
  1693. event: event
  1694. };
  1695. }
  1696. }
  1697. // Only pinch currently.
  1698. };
  1699. var SILENT = 'silent';
  1700. function makeEventPacket(eveType, targetInfo, event) {
  1701. return {
  1702. type: eveType,
  1703. event: event,
  1704. // target can only be an element that is not silent.
  1705. target: targetInfo.target,
  1706. // topTarget can be a silent element.
  1707. topTarget: targetInfo.topTarget,
  1708. cancelBubble: false,
  1709. offsetX: event.zrX,
  1710. offsetY: event.zrY,
  1711. gestureEvent: event.gestureEvent,
  1712. pinchX: event.pinchX,
  1713. pinchY: event.pinchY,
  1714. pinchScale: event.pinchScale,
  1715. wheelDelta: event.zrDelta,
  1716. zrByTouch: event.zrByTouch,
  1717. which: event.which,
  1718. stop: stopEvent
  1719. };
  1720. }
  1721. function stopEvent(event) {
  1722. stop(this.event);
  1723. }
  1724. function EmptyProxy() {}
  1725. EmptyProxy.prototype.dispose = function () {};
  1726. var handlerNames = [
  1727. 'click', 'dblclick', 'mousewheel', 'mouseout',
  1728. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  1729. ];
  1730. /**
  1731. * @alias module:zrender/Handler
  1732. * @constructor
  1733. * @extends module:zrender/mixin/Eventful
  1734. * @param {module:zrender/Storage} storage Storage instance.
  1735. * @param {module:zrender/Painter} painter Painter instance.
  1736. * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
  1737. * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
  1738. */
  1739. var Handler = function (storage, painter, proxy, painterRoot) {
  1740. Eventful.call(this);
  1741. this.storage = storage;
  1742. this.painter = painter;
  1743. this.painterRoot = painterRoot;
  1744. proxy = proxy || new EmptyProxy();
  1745. /**
  1746. * Proxy of event. can be Dom, WebGLSurface, etc.
  1747. */
  1748. this.proxy = null;
  1749. /**
  1750. * {target, topTarget, x, y}
  1751. * @private
  1752. * @type {Object}
  1753. */
  1754. this._hovered = {};
  1755. /**
  1756. * @private
  1757. * @type {Date}
  1758. */
  1759. this._lastTouchMoment;
  1760. /**
  1761. * @private
  1762. * @type {number}
  1763. */
  1764. this._lastX;
  1765. /**
  1766. * @private
  1767. * @type {number}
  1768. */
  1769. this._lastY;
  1770. /**
  1771. * @private
  1772. * @type {module:zrender/core/GestureMgr}
  1773. */
  1774. this._gestureMgr;
  1775. Draggable.call(this);
  1776. this.setHandlerProxy(proxy);
  1777. };
  1778. Handler.prototype = {
  1779. constructor: Handler,
  1780. setHandlerProxy: function (proxy) {
  1781. if (this.proxy) {
  1782. this.proxy.dispose();
  1783. }
  1784. if (proxy) {
  1785. each$1(handlerNames, function (name) {
  1786. proxy.on && proxy.on(name, this[name], this);
  1787. }, this);
  1788. // Attach handler
  1789. proxy.handler = this;
  1790. }
  1791. this.proxy = proxy;
  1792. },
  1793. mousemove: function (event) {
  1794. var x = event.zrX;
  1795. var y = event.zrY;
  1796. var lastHovered = this._hovered;
  1797. var lastHoveredTarget = lastHovered.target;
  1798. // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
  1799. // (like 'setOption' or 'dispatchAction') in event handlers, we should find
  1800. // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.
  1801. // See #6198.
  1802. if (lastHoveredTarget && !lastHoveredTarget.__zr) {
  1803. lastHovered = this.findHover(lastHovered.x, lastHovered.y);
  1804. lastHoveredTarget = lastHovered.target;
  1805. }
  1806. var hovered = this._hovered = this.findHover(x, y);
  1807. var hoveredTarget = hovered.target;
  1808. var proxy = this.proxy;
  1809. proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');
  1810. // Mouse out on previous hovered element
  1811. if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1812. this.dispatchToElement(lastHovered, 'mouseout', event);
  1813. }
  1814. // Mouse moving on one element
  1815. this.dispatchToElement(hovered, 'mousemove', event);
  1816. // Mouse over on a new element
  1817. if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1818. this.dispatchToElement(hovered, 'mouseover', event);
  1819. }
  1820. },
  1821. mouseout: function (event) {
  1822. this.dispatchToElement(this._hovered, 'mouseout', event);
  1823. // There might be some doms created by upper layer application
  1824. // at the same level of painter.getViewportRoot() (e.g., tooltip
  1825. // dom created by echarts), where 'globalout' event should not
  1826. // be triggered when mouse enters these doms. (But 'mouseout'
  1827. // should be triggered at the original hovered element as usual).
  1828. var element = event.toElement || event.relatedTarget;
  1829. var innerDom;
  1830. do {
  1831. element = element && element.parentNode;
  1832. }
  1833. while (element && element.nodeType !== 9 && !(
  1834. innerDom = element === this.painterRoot
  1835. ));
  1836. !innerDom && this.trigger('globalout', {event: event});
  1837. },
  1838. /**
  1839. * Resize
  1840. */
  1841. resize: function (event) {
  1842. this._hovered = {};
  1843. },
  1844. /**
  1845. * Dispatch event
  1846. * @param {string} eventName
  1847. * @param {event=} eventArgs
  1848. */
  1849. dispatch: function (eventName, eventArgs) {
  1850. var handler = this[eventName];
  1851. handler && handler.call(this, eventArgs);
  1852. },
  1853. /**
  1854. * Dispose
  1855. */
  1856. dispose: function () {
  1857. this.proxy.dispose();
  1858. this.storage =
  1859. this.proxy =
  1860. this.painter = null;
  1861. },
  1862. /**
  1863. * 设置默认的cursor style
  1864. * @param {string} [cursorStyle='default'] 例如 crosshair
  1865. */
  1866. setCursorStyle: function (cursorStyle) {
  1867. var proxy = this.proxy;
  1868. proxy.setCursor && proxy.setCursor(cursorStyle);
  1869. },
  1870. /**
  1871. * 事件分发代理
  1872. *
  1873. * @private
  1874. * @param {Object} targetInfo {target, topTarget} 目标图形元素
  1875. * @param {string} eventName 事件名称
  1876. * @param {Object} event 事件对象
  1877. */
  1878. dispatchToElement: function (targetInfo, eventName, event) {
  1879. targetInfo = targetInfo || {};
  1880. var el = targetInfo.target;
  1881. if (el && el.silent) {
  1882. return;
  1883. }
  1884. var eventHandler = 'on' + eventName;
  1885. var eventPacket = makeEventPacket(eventName, targetInfo, event);
  1886. while (el) {
  1887. el[eventHandler]
  1888. && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
  1889. el.trigger(eventName, eventPacket);
  1890. el = el.parent;
  1891. if (eventPacket.cancelBubble) {
  1892. break;
  1893. }
  1894. }
  1895. if (!eventPacket.cancelBubble) {
  1896. // 冒泡到顶级 zrender 对象
  1897. this.trigger(eventName, eventPacket);
  1898. // 分发事件到用户自定义层
  1899. // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在
  1900. this.painter && this.painter.eachOtherLayer(function (layer) {
  1901. if (typeof (layer[eventHandler]) === 'function') {
  1902. layer[eventHandler].call(layer, eventPacket);
  1903. }
  1904. if (layer.trigger) {
  1905. layer.trigger(eventName, eventPacket);
  1906. }
  1907. });
  1908. }
  1909. },
  1910. /**
  1911. * @private
  1912. * @param {number} x
  1913. * @param {number} y
  1914. * @param {module:zrender/graphic/Displayable} exclude
  1915. * @return {model:zrender/Element}
  1916. * @method
  1917. */
  1918. findHover: function (x, y, exclude) {
  1919. var list = this.storage.getDisplayList();
  1920. var out = {x: x, y: y};
  1921. for (var i = list.length - 1; i >= 0; i--) {
  1922. var hoverCheckResult;
  1923. if (list[i] !== exclude
  1924. // getDisplayList may include ignored item in VML mode
  1925. && !list[i].ignore
  1926. && (hoverCheckResult = isHover(list[i], x, y))
  1927. ) {
  1928. !out.topTarget && (out.topTarget = list[i]);
  1929. if (hoverCheckResult !== SILENT) {
  1930. out.target = list[i];
  1931. break;
  1932. }
  1933. }
  1934. }
  1935. return out;
  1936. },
  1937. processGesture: function (event, stage) {
  1938. if (!this._gestureMgr) {
  1939. this._gestureMgr = new GestureMgr();
  1940. }
  1941. var gestureMgr = this._gestureMgr;
  1942. stage === 'start' && gestureMgr.clear();
  1943. var gestureInfo = gestureMgr.recognize(
  1944. event,
  1945. this.findHover(event.zrX, event.zrY, null).target,
  1946. this.proxy.dom
  1947. );
  1948. stage === 'end' && gestureMgr.clear();
  1949. // Do not do any preventDefault here. Upper application do that if necessary.
  1950. if (gestureInfo) {
  1951. var type = gestureInfo.type;
  1952. event.gestureEvent = type;
  1953. this.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);
  1954. }
  1955. }
  1956. };
  1957. // Common handlers
  1958. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  1959. Handler.prototype[name] = function (event) {
  1960. // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
  1961. var hovered = this.findHover(event.zrX, event.zrY);
  1962. var hoveredTarget = hovered.target;
  1963. if (name === 'mousedown') {
  1964. this._downEl = hoveredTarget;
  1965. this._downPoint = [event.zrX, event.zrY];
  1966. // In case click triggered before mouseup
  1967. this._upEl = hoveredTarget;
  1968. }
  1969. else if (name === 'mouseup') {
  1970. this._upEl = hoveredTarget;
  1971. }
  1972. else if (name === 'click') {
  1973. if (this._downEl !== this._upEl
  1974. // Original click event is triggered on the whole canvas element,
  1975. // including the case that `mousedown` - `mousemove` - `mouseup`,
  1976. // which should be filtered, otherwise it will bring trouble to
  1977. // pan and zoom.
  1978. || !this._downPoint
  1979. // Arbitrary value
  1980. || dist(this._downPoint, [event.zrX, event.zrY]) > 4
  1981. ) {
  1982. return;
  1983. }
  1984. this._downPoint = null;
  1985. }
  1986. this.dispatchToElement(hovered, name, event);
  1987. };
  1988. });
  1989. function isHover(displayable, x, y) {
  1990. if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
  1991. var el = displayable;
  1992. var isSilent;
  1993. while (el) {
  1994. // If clipped by ancestor.
  1995. // FIXME: If clipPath has neither stroke nor fill,
  1996. // el.clipPath.contain(x, y) will always return false.
  1997. if (el.clipPath && !el.clipPath.contain(x, y)) {
  1998. return false;
  1999. }
  2000. if (el.silent) {
  2001. isSilent = true;
  2002. }
  2003. el = el.parent;
  2004. }
  2005. return isSilent ? SILENT : true;
  2006. }
  2007. return false;
  2008. }
  2009. mixin(Handler, Eventful);
  2010. mixin(Handler, Draggable);
  2011. /**
  2012. * 3x2矩阵操作类
  2013. * @exports zrender/tool/matrix
  2014. */
  2015. var ArrayCtor$1 = typeof Float32Array === 'undefined'
  2016. ? Array
  2017. : Float32Array;
  2018. /**
  2019. * Create a identity matrix.
  2020. * @return {Float32Array|Array.<number>}
  2021. */
  2022. function create$1() {
  2023. var out = new ArrayCtor$1(6);
  2024. identity(out);
  2025. return out;
  2026. }
  2027. /**
  2028. * 设置矩阵为单位矩阵
  2029. * @param {Float32Array|Array.<number>} out
  2030. */
  2031. function identity(out) {
  2032. out[0] = 1;
  2033. out[1] = 0;
  2034. out[2] = 0;
  2035. out[3] = 1;
  2036. out[4] = 0;
  2037. out[5] = 0;
  2038. return out;
  2039. }
  2040. /**
  2041. * 复制矩阵
  2042. * @param {Float32Array|Array.<number>} out
  2043. * @param {Float32Array|Array.<number>} m
  2044. */
  2045. function copy$1(out, m) {
  2046. out[0] = m[0];
  2047. out[1] = m[1];
  2048. out[2] = m[2];
  2049. out[3] = m[3];
  2050. out[4] = m[4];
  2051. out[5] = m[5];
  2052. return out;
  2053. }
  2054. /**
  2055. * 矩阵相乘
  2056. * @param {Float32Array|Array.<number>} out
  2057. * @param {Float32Array|Array.<number>} m1
  2058. * @param {Float32Array|Array.<number>} m2
  2059. */
  2060. function mul$1(out, m1, m2) {
  2061. // Consider matrix.mul(m, m2, m);
  2062. // where out is the same as m2.
  2063. // So use temp variable to escape error.
  2064. var out0 = m1[0] * m2[0] + m1[2] * m2[1];
  2065. var out1 = m1[1] * m2[0] + m1[3] * m2[1];
  2066. var out2 = m1[0] * m2[2] + m1[2] * m2[3];
  2067. var out3 = m1[1] * m2[2] + m1[3] * m2[3];
  2068. var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
  2069. var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
  2070. out[0] = out0;
  2071. out[1] = out1;
  2072. out[2] = out2;
  2073. out[3] = out3;
  2074. out[4] = out4;
  2075. out[5] = out5;
  2076. return out;
  2077. }
  2078. /**
  2079. * 平移变换
  2080. * @param {Float32Array|Array.<number>} out
  2081. * @param {Float32Array|Array.<number>} a
  2082. * @param {Float32Array|Array.<number>} v
  2083. */
  2084. function translate(out, a, v) {
  2085. out[0] = a[0];
  2086. out[1] = a[1];
  2087. out[2] = a[2];
  2088. out[3] = a[3];
  2089. out[4] = a[4] + v[0];
  2090. out[5] = a[5] + v[1];
  2091. return out;
  2092. }
  2093. /**
  2094. * 旋转变换
  2095. * @param {Float32Array|Array.<number>} out
  2096. * @param {Float32Array|Array.<number>} a
  2097. * @param {number} rad
  2098. */
  2099. function rotate(out, a, rad) {
  2100. var aa = a[0];
  2101. var ac = a[2];
  2102. var atx = a[4];
  2103. var ab = a[1];
  2104. var ad = a[3];
  2105. var aty = a[5];
  2106. var st = Math.sin(rad);
  2107. var ct = Math.cos(rad);
  2108. out[0] = aa * ct + ab * st;
  2109. out[1] = -aa * st + ab * ct;
  2110. out[2] = ac * ct + ad * st;
  2111. out[3] = -ac * st + ct * ad;
  2112. out[4] = ct * atx + st * aty;
  2113. out[5] = ct * aty - st * atx;
  2114. return out;
  2115. }
  2116. /**
  2117. * 缩放变换
  2118. * @param {Float32Array|Array.<number>} out
  2119. * @param {Float32Array|Array.<number>} a
  2120. * @param {Float32Array|Array.<number>} v
  2121. */
  2122. function scale$1(out, a, v) {
  2123. var vx = v[0];
  2124. var vy = v[1];
  2125. out[0] = a[0] * vx;
  2126. out[1] = a[1] * vy;
  2127. out[2] = a[2] * vx;
  2128. out[3] = a[3] * vy;
  2129. out[4] = a[4] * vx;
  2130. out[5] = a[5] * vy;
  2131. return out;
  2132. }
  2133. /**
  2134. * 求逆矩阵
  2135. * @param {Float32Array|Array.<number>} out
  2136. * @param {Float32Array|Array.<number>} a
  2137. */
  2138. function invert(out, a) {
  2139. var aa = a[0];
  2140. var ac = a[2];
  2141. var atx = a[4];
  2142. var ab = a[1];
  2143. var ad = a[3];
  2144. var aty = a[5];
  2145. var det = aa * ad - ab * ac;
  2146. if (!det) {
  2147. return null;
  2148. }
  2149. det = 1.0 / det;
  2150. out[0] = ad * det;
  2151. out[1] = -ab * det;
  2152. out[2] = -ac * det;
  2153. out[3] = aa * det;
  2154. out[4] = (ac * aty - ad * atx) * det;
  2155. out[5] = (ab * atx - aa * aty) * det;
  2156. return out;
  2157. }
  2158. /**
  2159. * Clone a new matrix.
  2160. * @param {Float32Array|Array.<number>} a
  2161. */
  2162. function clone$2(a) {
  2163. var b = create$1();
  2164. copy$1(b, a);
  2165. return b;
  2166. }
  2167. var matrix = (Object.freeze || Object)({
  2168. create: create$1,
  2169. identity: identity,
  2170. copy: copy$1,
  2171. mul: mul$1,
  2172. translate: translate,
  2173. rotate: rotate,
  2174. scale: scale$1,
  2175. invert: invert,
  2176. clone: clone$2
  2177. });
  2178. /**
  2179. * 提供变换扩展
  2180. * @module zrender/mixin/Transformable
  2181. * @author pissang (https://www.github.com/pissang)
  2182. */
  2183. var mIdentity = identity;
  2184. var EPSILON = 5e-5;
  2185. function isNotAroundZero(val) {
  2186. return val > EPSILON || val < -EPSILON;
  2187. }
  2188. /**
  2189. * @alias module:zrender/mixin/Transformable
  2190. * @constructor
  2191. */
  2192. var Transformable = function (opts) {
  2193. opts = opts || {};
  2194. // If there are no given position, rotation, scale
  2195. if (!opts.position) {
  2196. /**
  2197. * 平移
  2198. * @type {Array.<number>}
  2199. * @default [0, 0]
  2200. */
  2201. this.position = [0, 0];
  2202. }
  2203. if (opts.rotation == null) {
  2204. /**
  2205. * 旋转
  2206. * @type {Array.<number>}
  2207. * @default 0
  2208. */
  2209. this.rotation = 0;
  2210. }
  2211. if (!opts.scale) {
  2212. /**
  2213. * 缩放
  2214. * @type {Array.<number>}
  2215. * @default [1, 1]
  2216. */
  2217. this.scale = [1, 1];
  2218. }
  2219. /**
  2220. * 旋转和缩放的原点
  2221. * @type {Array.<number>}
  2222. * @default null
  2223. */
  2224. this.origin = this.origin || null;
  2225. };
  2226. var transformableProto = Transformable.prototype;
  2227. transformableProto.transform = null;
  2228. /**
  2229. * 判断是否需要有坐标变换
  2230. * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
  2231. */
  2232. transformableProto.needLocalTransform = function () {
  2233. return isNotAroundZero(this.rotation)
  2234. || isNotAroundZero(this.position[0])
  2235. || isNotAroundZero(this.position[1])
  2236. || isNotAroundZero(this.scale[0] - 1)
  2237. || isNotAroundZero(this.scale[1] - 1);
  2238. };
  2239. var scaleTmp = [];
  2240. transformableProto.updateTransform = function () {
  2241. var parent = this.parent;
  2242. var parentHasTransform = parent && parent.transform;
  2243. var needLocalTransform = this.needLocalTransform();
  2244. var m = this.transform;
  2245. if (!(needLocalTransform || parentHasTransform)) {
  2246. m && mIdentity(m);
  2247. return;
  2248. }
  2249. m = m || create$1();
  2250. if (needLocalTransform) {
  2251. this.getLocalTransform(m);
  2252. }
  2253. else {
  2254. mIdentity(m);
  2255. }
  2256. // 应用父节点变换
  2257. if (parentHasTransform) {
  2258. if (needLocalTransform) {
  2259. mul$1(m, parent.transform, m);
  2260. }
  2261. else {
  2262. copy$1(m, parent.transform);
  2263. }
  2264. }
  2265. // 保存这个变换矩阵
  2266. this.transform = m;
  2267. var globalScaleRatio = this.globalScaleRatio;
  2268. if (globalScaleRatio != null && globalScaleRatio !== 1) {
  2269. this.getGlobalScale(scaleTmp);
  2270. var relX = scaleTmp[0] < 0 ? -1 : 1;
  2271. var relY = scaleTmp[1] < 0 ? -1 : 1;
  2272. var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;
  2273. var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;
  2274. m[0] *= sx;
  2275. m[1] *= sx;
  2276. m[2] *= sy;
  2277. m[3] *= sy;
  2278. }
  2279. this.invTransform = this.invTransform || create$1();
  2280. invert(this.invTransform, m);
  2281. };
  2282. transformableProto.getLocalTransform = function (m) {
  2283. return Transformable.getLocalTransform(this, m);
  2284. };
  2285. /**
  2286. * 将自己的transform应用到context上
  2287. * @param {CanvasRenderingContext2D} ctx
  2288. */
  2289. transformableProto.setTransform = function (ctx) {
  2290. var m = this.transform;
  2291. var dpr = ctx.dpr || 1;
  2292. if (m) {
  2293. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  2294. }
  2295. else {
  2296. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  2297. }
  2298. };
  2299. transformableProto.restoreTransform = function (ctx) {
  2300. var dpr = ctx.dpr || 1;
  2301. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  2302. };
  2303. var tmpTransform = [];
  2304. var originTransform = create$1();
  2305. transformableProto.setLocalTransform = function (m) {
  2306. if (!m) {
  2307. // TODO return or set identity?
  2308. return;
  2309. }
  2310. var sx = m[0] * m[0] + m[1] * m[1];
  2311. var sy = m[2] * m[2] + m[3] * m[3];
  2312. var position = this.position;
  2313. var scale$$1 = this.scale;
  2314. if (isNotAroundZero(sx - 1)) {
  2315. sx = Math.sqrt(sx);
  2316. }
  2317. if (isNotAroundZero(sy - 1)) {
  2318. sy = Math.sqrt(sy);
  2319. }
  2320. if (m[0] < 0) {
  2321. sx = -sx;
  2322. }
  2323. if (m[3] < 0) {
  2324. sy = -sy;
  2325. }
  2326. position[0] = m[4];
  2327. position[1] = m[5];
  2328. scale$$1[0] = sx;
  2329. scale$$1[1] = sy;
  2330. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  2331. };
  2332. /**
  2333. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  2334. */
  2335. transformableProto.decomposeTransform = function () {
  2336. if (!this.transform) {
  2337. return;
  2338. }
  2339. var parent = this.parent;
  2340. var m = this.transform;
  2341. if (parent && parent.transform) {
  2342. // Get local transform and decompose them to position, scale, rotation
  2343. mul$1(tmpTransform, parent.invTransform, m);
  2344. m = tmpTransform;
  2345. }
  2346. var origin = this.origin;
  2347. if (origin && (origin[0] || origin[1])) {
  2348. originTransform[4] = origin[0];
  2349. originTransform[5] = origin[1];
  2350. mul$1(tmpTransform, m, originTransform);
  2351. tmpTransform[4] -= origin[0];
  2352. tmpTransform[5] -= origin[1];
  2353. m = tmpTransform;
  2354. }
  2355. this.setLocalTransform(m);
  2356. };
  2357. /**
  2358. * Get global scale
  2359. * @return {Array.<number>}
  2360. */
  2361. transformableProto.getGlobalScale = function (out) {
  2362. var m = this.transform;
  2363. out = out || [];
  2364. if (!m) {
  2365. out[0] = 1;
  2366. out[1] = 1;
  2367. return out;
  2368. }
  2369. out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  2370. out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  2371. if (m[0] < 0) {
  2372. out[0] = -out[0];
  2373. }
  2374. if (m[3] < 0) {
  2375. out[1] = -out[1];
  2376. }
  2377. return out;
  2378. };
  2379. /**
  2380. * 变换坐标位置到 shape 的局部坐标空间
  2381. * @method
  2382. * @param {number} x
  2383. * @param {number} y
  2384. * @return {Array.<number>}
  2385. */
  2386. transformableProto.transformCoordToLocal = function (x, y) {
  2387. var v2 = [x, y];
  2388. var invTransform = this.invTransform;
  2389. if (invTransform) {
  2390. applyTransform(v2, v2, invTransform);
  2391. }
  2392. return v2;
  2393. };
  2394. /**
  2395. * 变换局部坐标位置到全局坐标空间
  2396. * @method
  2397. * @param {number} x
  2398. * @param {number} y
  2399. * @return {Array.<number>}
  2400. */
  2401. transformableProto.transformCoordToGlobal = function (x, y) {
  2402. var v2 = [x, y];
  2403. var transform = this.transform;
  2404. if (transform) {
  2405. applyTransform(v2, v2, transform);
  2406. }
  2407. return v2;
  2408. };
  2409. /**
  2410. * @static
  2411. * @param {Object} target
  2412. * @param {Array.<number>} target.origin
  2413. * @param {number} target.rotation
  2414. * @param {Array.<number>} target.position
  2415. * @param {Array.<number>} [m]
  2416. */
  2417. Transformable.getLocalTransform = function (target, m) {
  2418. m = m || [];
  2419. mIdentity(m);
  2420. var origin = target.origin;
  2421. var scale$$1 = target.scale || [1, 1];
  2422. var rotation = target.rotation || 0;
  2423. var position = target.position || [0, 0];
  2424. if (origin) {
  2425. // Translate to origin
  2426. m[4] -= origin[0];
  2427. m[5] -= origin[1];
  2428. }
  2429. scale$1(m, m, scale$$1);
  2430. if (rotation) {
  2431. rotate(m, m, rotation);
  2432. }
  2433. if (origin) {
  2434. // Translate back from origin
  2435. m[4] += origin[0];
  2436. m[5] += origin[1];
  2437. }
  2438. m[4] += position[0];
  2439. m[5] += position[1];
  2440. return m;
  2441. };
  2442. /**
  2443. * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js
  2444. * @see http://sole.github.io/tween.js/examples/03_graphs.html
  2445. * @exports zrender/animation/easing
  2446. */
  2447. var easing = {
  2448. /**
  2449. * @param {number} k
  2450. * @return {number}
  2451. */
  2452. linear: function (k) {
  2453. return k;
  2454. },
  2455. /**
  2456. * @param {number} k
  2457. * @return {number}
  2458. */
  2459. quadraticIn: function (k) {
  2460. return k * k;
  2461. },
  2462. /**
  2463. * @param {number} k
  2464. * @return {number}
  2465. */
  2466. quadraticOut: function (k) {
  2467. return k * (2 - k);
  2468. },
  2469. /**
  2470. * @param {number} k
  2471. * @return {number}
  2472. */
  2473. quadraticInOut: function (k) {
  2474. if ((k *= 2) < 1) {
  2475. return 0.5 * k * k;
  2476. }
  2477. return -0.5 * (--k * (k - 2) - 1);
  2478. },
  2479. // 三次方的缓动(t^3)
  2480. /**
  2481. * @param {number} k
  2482. * @return {number}
  2483. */
  2484. cubicIn: function (k) {
  2485. return k * k * k;
  2486. },
  2487. /**
  2488. * @param {number} k
  2489. * @return {number}
  2490. */
  2491. cubicOut: function (k) {
  2492. return --k * k * k + 1;
  2493. },
  2494. /**
  2495. * @param {number} k
  2496. * @return {number}
  2497. */
  2498. cubicInOut: function (k) {
  2499. if ((k *= 2) < 1) {
  2500. return 0.5 * k * k * k;
  2501. }
  2502. return 0.5 * ((k -= 2) * k * k + 2);
  2503. },
  2504. // 四次方的缓动(t^4)
  2505. /**
  2506. * @param {number} k
  2507. * @return {number}
  2508. */
  2509. quarticIn: function (k) {
  2510. return k * k * k * k;
  2511. },
  2512. /**
  2513. * @param {number} k
  2514. * @return {number}
  2515. */
  2516. quarticOut: function (k) {
  2517. return 1 - (--k * k * k * k);
  2518. },
  2519. /**
  2520. * @param {number} k
  2521. * @return {number}
  2522. */
  2523. quarticInOut: function (k) {
  2524. if ((k *= 2) < 1) {
  2525. return 0.5 * k * k * k * k;
  2526. }
  2527. return -0.5 * ((k -= 2) * k * k * k - 2);
  2528. },
  2529. // 五次方的缓动(t^5)
  2530. /**
  2531. * @param {number} k
  2532. * @return {number}
  2533. */
  2534. quinticIn: function (k) {
  2535. return k * k * k * k * k;
  2536. },
  2537. /**
  2538. * @param {number} k
  2539. * @return {number}
  2540. */
  2541. quinticOut: function (k) {
  2542. return --k * k * k * k * k + 1;
  2543. },
  2544. /**
  2545. * @param {number} k
  2546. * @return {number}
  2547. */
  2548. quinticInOut: function (k) {
  2549. if ((k *= 2) < 1) {
  2550. return 0.5 * k * k * k * k * k;
  2551. }
  2552. return 0.5 * ((k -= 2) * k * k * k * k + 2);
  2553. },
  2554. // 正弦曲线的缓动(sin(t))
  2555. /**
  2556. * @param {number} k
  2557. * @return {number}
  2558. */
  2559. sinusoidalIn: function (k) {
  2560. return 1 - Math.cos(k * Math.PI / 2);
  2561. },
  2562. /**
  2563. * @param {number} k
  2564. * @return {number}
  2565. */
  2566. sinusoidalOut: function (k) {
  2567. return Math.sin(k * Math.PI / 2);
  2568. },
  2569. /**
  2570. * @param {number} k
  2571. * @return {number}
  2572. */
  2573. sinusoidalInOut: function (k) {
  2574. return 0.5 * (1 - Math.cos(Math.PI * k));
  2575. },
  2576. // 指数曲线的缓动(2^t)
  2577. /**
  2578. * @param {number} k
  2579. * @return {number}
  2580. */
  2581. exponentialIn: function (k) {
  2582. return k === 0 ? 0 : Math.pow(1024, k - 1);
  2583. },
  2584. /**
  2585. * @param {number} k
  2586. * @return {number}
  2587. */
  2588. exponentialOut: function (k) {
  2589. return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
  2590. },
  2591. /**
  2592. * @param {number} k
  2593. * @return {number}
  2594. */
  2595. exponentialInOut: function (k) {
  2596. if (k === 0) {
  2597. return 0;
  2598. }
  2599. if (k === 1) {
  2600. return 1;
  2601. }
  2602. if ((k *= 2) < 1) {
  2603. return 0.5 * Math.pow(1024, k - 1);
  2604. }
  2605. return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
  2606. },
  2607. // 圆形曲线的缓动(sqrt(1-t^2))
  2608. /**
  2609. * @param {number} k
  2610. * @return {number}
  2611. */
  2612. circularIn: function (k) {
  2613. return 1 - Math.sqrt(1 - k * k);
  2614. },
  2615. /**
  2616. * @param {number} k
  2617. * @return {number}
  2618. */
  2619. circularOut: function (k) {
  2620. return Math.sqrt(1 - (--k * k));
  2621. },
  2622. /**
  2623. * @param {number} k
  2624. * @return {number}
  2625. */
  2626. circularInOut: function (k) {
  2627. if ((k *= 2) < 1) {
  2628. return -0.5 * (Math.sqrt(1 - k * k) - 1);
  2629. }
  2630. return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
  2631. },
  2632. // 创建类似于弹簧在停止前来回振荡的动画
  2633. /**
  2634. * @param {number} k
  2635. * @return {number}
  2636. */
  2637. elasticIn: function (k) {
  2638. var s;
  2639. var a = 0.1;
  2640. var p = 0.4;
  2641. if (k === 0) {
  2642. return 0;
  2643. }
  2644. if (k === 1) {
  2645. return 1;
  2646. }
  2647. if (!a || a < 1) {
  2648. a = 1;
  2649. s = p / 4;
  2650. }
  2651. else {
  2652. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2653. }
  2654. return -(a * Math.pow(2, 10 * (k -= 1))
  2655. * Math.sin((k - s) * (2 * Math.PI) / p));
  2656. },
  2657. /**
  2658. * @param {number} k
  2659. * @return {number}
  2660. */
  2661. elasticOut: function (k) {
  2662. var s;
  2663. var a = 0.1;
  2664. var p = 0.4;
  2665. if (k === 0) {
  2666. return 0;
  2667. }
  2668. if (k === 1) {
  2669. return 1;
  2670. }
  2671. if (!a || a < 1) {
  2672. a = 1;
  2673. s = p / 4;
  2674. }
  2675. else {
  2676. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2677. }
  2678. return (a * Math.pow(2, -10 * k)
  2679. * Math.sin((k - s) * (2 * Math.PI) / p) + 1);
  2680. },
  2681. /**
  2682. * @param {number} k
  2683. * @return {number}
  2684. */
  2685. elasticInOut: function (k) {
  2686. var s;
  2687. var a = 0.1;
  2688. var p = 0.4;
  2689. if (k === 0) {
  2690. return 0;
  2691. }
  2692. if (k === 1) {
  2693. return 1;
  2694. }
  2695. if (!a || a < 1) {
  2696. a = 1;
  2697. s = p / 4;
  2698. }
  2699. else {
  2700. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2701. }
  2702. if ((k *= 2) < 1) {
  2703. return -0.5 * (a * Math.pow(2, 10 * (k -= 1))
  2704. * Math.sin((k - s) * (2 * Math.PI) / p));
  2705. }
  2706. return a * Math.pow(2, -10 * (k -= 1))
  2707. * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
  2708. },
  2709. // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动
  2710. /**
  2711. * @param {number} k
  2712. * @return {number}
  2713. */
  2714. backIn: function (k) {
  2715. var s = 1.70158;
  2716. return k * k * ((s + 1) * k - s);
  2717. },
  2718. /**
  2719. * @param {number} k
  2720. * @return {number}
  2721. */
  2722. backOut: function (k) {
  2723. var s = 1.70158;
  2724. return --k * k * ((s + 1) * k + s) + 1;
  2725. },
  2726. /**
  2727. * @param {number} k
  2728. * @return {number}
  2729. */
  2730. backInOut: function (k) {
  2731. var s = 1.70158 * 1.525;
  2732. if ((k *= 2) < 1) {
  2733. return 0.5 * (k * k * ((s + 1) * k - s));
  2734. }
  2735. return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
  2736. },
  2737. // 创建弹跳效果
  2738. /**
  2739. * @param {number} k
  2740. * @return {number}
  2741. */
  2742. bounceIn: function (k) {
  2743. return 1 - easing.bounceOut(1 - k);
  2744. },
  2745. /**
  2746. * @param {number} k
  2747. * @return {number}
  2748. */
  2749. bounceOut: function (k) {
  2750. if (k < (1 / 2.75)) {
  2751. return 7.5625 * k * k;
  2752. }
  2753. else if (k < (2 / 2.75)) {
  2754. return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
  2755. }
  2756. else if (k < (2.5 / 2.75)) {
  2757. return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
  2758. }
  2759. else {
  2760. return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
  2761. }
  2762. },
  2763. /**
  2764. * @param {number} k
  2765. * @return {number}
  2766. */
  2767. bounceInOut: function (k) {
  2768. if (k < 0.5) {
  2769. return easing.bounceIn(k * 2) * 0.5;
  2770. }
  2771. return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
  2772. }
  2773. };
  2774. /**
  2775. * 动画主控制器
  2776. * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
  2777. * @config life(1000) 动画时长
  2778. * @config delay(0) 动画延迟时间
  2779. * @config loop(true)
  2780. * @config gap(0) 循环的间隔时间
  2781. * @config onframe
  2782. * @config easing(optional)
  2783. * @config ondestroy(optional)
  2784. * @config onrestart(optional)
  2785. *
  2786. * TODO pause
  2787. */
  2788. function Clip(options) {
  2789. this._target = options.target;
  2790. // 生命周期
  2791. this._life = options.life || 1000;
  2792. // 延时
  2793. this._delay = options.delay || 0;
  2794. // 开始时间
  2795. // this._startTime = new Date().getTime() + this._delay;// 单位毫秒
  2796. this._initialized = false;
  2797. // 是否循环
  2798. this.loop = options.loop == null ? false : options.loop;
  2799. this.gap = options.gap || 0;
  2800. this.easing = options.easing || 'Linear';
  2801. this.onframe = options.onframe;
  2802. this.ondestroy = options.ondestroy;
  2803. this.onrestart = options.onrestart;
  2804. this._pausedTime = 0;
  2805. this._paused = false;
  2806. }
  2807. Clip.prototype = {
  2808. constructor: Clip,
  2809. step: function (globalTime, deltaTime) {
  2810. // Set startTime on first step, or _startTime may has milleseconds different between clips
  2811. // PENDING
  2812. if (!this._initialized) {
  2813. this._startTime = globalTime + this._delay;
  2814. this._initialized = true;
  2815. }
  2816. if (this._paused) {
  2817. this._pausedTime += deltaTime;
  2818. return;
  2819. }
  2820. var percent = (globalTime - this._startTime - this._pausedTime) / this._life;
  2821. // 还没开始
  2822. if (percent < 0) {
  2823. return;
  2824. }
  2825. percent = Math.min(percent, 1);
  2826. var easing$$1 = this.easing;
  2827. var easingFunc = typeof easing$$1 === 'string' ? easing[easing$$1] : easing$$1;
  2828. var schedule = typeof easingFunc === 'function'
  2829. ? easingFunc(percent)
  2830. : percent;
  2831. this.fire('frame', schedule);
  2832. // 结束
  2833. if (percent === 1) {
  2834. if (this.loop) {
  2835. this.restart(globalTime);
  2836. // 重新开始周期
  2837. // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件
  2838. return 'restart';
  2839. }
  2840. // 动画完成将这个控制器标识为待删除
  2841. // 在Animation.update中进行批量删除
  2842. this._needsRemove = true;
  2843. return 'destroy';
  2844. }
  2845. return null;
  2846. },
  2847. restart: function (globalTime) {
  2848. var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
  2849. this._startTime = globalTime - remainder + this.gap;
  2850. this._pausedTime = 0;
  2851. this._needsRemove = false;
  2852. },
  2853. fire: function (eventType, arg) {
  2854. eventType = 'on' + eventType;
  2855. if (this[eventType]) {
  2856. this[eventType](this._target, arg);
  2857. }
  2858. },
  2859. pause: function () {
  2860. this._paused = true;
  2861. },
  2862. resume: function () {
  2863. this._paused = false;
  2864. }
  2865. };
  2866. // Simple LRU cache use doubly linked list
  2867. // @module zrender/core/LRU
  2868. /**
  2869. * Simple double linked list. Compared with array, it has O(1) remove operation.
  2870. * @constructor
  2871. */
  2872. var LinkedList = function () {
  2873. /**
  2874. * @type {module:zrender/core/LRU~Entry}
  2875. */
  2876. this.head = null;
  2877. /**
  2878. * @type {module:zrender/core/LRU~Entry}
  2879. */
  2880. this.tail = null;
  2881. this._len = 0;
  2882. };
  2883. var linkedListProto = LinkedList.prototype;
  2884. /**
  2885. * Insert a new value at the tail
  2886. * @param {} val
  2887. * @return {module:zrender/core/LRU~Entry}
  2888. */
  2889. linkedListProto.insert = function (val) {
  2890. var entry = new Entry(val);
  2891. this.insertEntry(entry);
  2892. return entry;
  2893. };
  2894. /**
  2895. * Insert an entry at the tail
  2896. * @param {module:zrender/core/LRU~Entry} entry
  2897. */
  2898. linkedListProto.insertEntry = function (entry) {
  2899. if (!this.head) {
  2900. this.head = this.tail = entry;
  2901. }
  2902. else {
  2903. this.tail.next = entry;
  2904. entry.prev = this.tail;
  2905. entry.next = null;
  2906. this.tail = entry;
  2907. }
  2908. this._len++;
  2909. };
  2910. /**
  2911. * Remove entry.
  2912. * @param {module:zrender/core/LRU~Entry} entry
  2913. */
  2914. linkedListProto.remove = function (entry) {
  2915. var prev = entry.prev;
  2916. var next = entry.next;
  2917. if (prev) {
  2918. prev.next = next;
  2919. }
  2920. else {
  2921. // Is head
  2922. this.head = next;
  2923. }
  2924. if (next) {
  2925. next.prev = prev;
  2926. }
  2927. else {
  2928. // Is tail
  2929. this.tail = prev;
  2930. }
  2931. entry.next = entry.prev = null;
  2932. this._len--;
  2933. };
  2934. /**
  2935. * @return {number}
  2936. */
  2937. linkedListProto.len = function () {
  2938. return this._len;
  2939. };
  2940. /**
  2941. * Clear list
  2942. */
  2943. linkedListProto.clear = function () {
  2944. this.head = this.tail = null;
  2945. this._len = 0;
  2946. };
  2947. /**
  2948. * @constructor
  2949. * @param {} val
  2950. */
  2951. var Entry = function (val) {
  2952. /**
  2953. * @type {}
  2954. */
  2955. this.value = val;
  2956. /**
  2957. * @type {module:zrender/core/LRU~Entry}
  2958. */
  2959. this.next;
  2960. /**
  2961. * @type {module:zrender/core/LRU~Entry}
  2962. */
  2963. this.prev;
  2964. };
  2965. /**
  2966. * LRU Cache
  2967. * @constructor
  2968. * @alias module:zrender/core/LRU
  2969. */
  2970. var LRU = function (maxSize) {
  2971. this._list = new LinkedList();
  2972. this._map = {};
  2973. this._maxSize = maxSize || 10;
  2974. this._lastRemovedEntry = null;
  2975. };
  2976. var LRUProto = LRU.prototype;
  2977. /**
  2978. * @param {string} key
  2979. * @param {} value
  2980. * @return {} Removed value
  2981. */
  2982. LRUProto.put = function (key, value) {
  2983. var list = this._list;
  2984. var map = this._map;
  2985. var removed = null;
  2986. if (map[key] == null) {
  2987. var len = list.len();
  2988. // Reuse last removed entry
  2989. var entry = this._lastRemovedEntry;
  2990. if (len >= this._maxSize && len > 0) {
  2991. // Remove the least recently used
  2992. var leastUsedEntry = list.head;
  2993. list.remove(leastUsedEntry);
  2994. delete map[leastUsedEntry.key];
  2995. removed = leastUsedEntry.value;
  2996. this._lastRemovedEntry = leastUsedEntry;
  2997. }
  2998. if (entry) {
  2999. entry.value = value;
  3000. }
  3001. else {
  3002. entry = new Entry(value);
  3003. }
  3004. entry.key = key;
  3005. list.insertEntry(entry);
  3006. map[key] = entry;
  3007. }
  3008. return removed;
  3009. };
  3010. /**
  3011. * @param {string} key
  3012. * @return {}
  3013. */
  3014. LRUProto.get = function (key) {
  3015. var entry = this._map[key];
  3016. var list = this._list;
  3017. if (entry != null) {
  3018. // Put the latest used entry in the tail
  3019. if (entry !== list.tail) {
  3020. list.remove(entry);
  3021. list.insertEntry(entry);
  3022. }
  3023. return entry.value;
  3024. }
  3025. };
  3026. /**
  3027. * Clear the cache
  3028. */
  3029. LRUProto.clear = function () {
  3030. this._list.clear();
  3031. this._map = {};
  3032. };
  3033. var kCSSColorTable = {
  3034. 'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],
  3035. 'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],
  3036. 'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],
  3037. 'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],
  3038. 'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],
  3039. 'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],
  3040. 'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],
  3041. 'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],
  3042. 'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],
  3043. 'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],
  3044. 'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],
  3045. 'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],
  3046. 'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],
  3047. 'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],
  3048. 'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],
  3049. 'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],
  3050. 'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],
  3051. 'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],
  3052. 'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],
  3053. 'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],
  3054. 'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],
  3055. 'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],
  3056. 'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],
  3057. 'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],
  3058. 'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],
  3059. 'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],
  3060. 'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],
  3061. 'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],
  3062. 'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],
  3063. 'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],
  3064. 'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],
  3065. 'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],
  3066. 'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],
  3067. 'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],
  3068. 'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],
  3069. 'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],
  3070. 'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],
  3071. 'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],
  3072. 'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],
  3073. 'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],
  3074. 'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],
  3075. 'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],
  3076. 'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],
  3077. 'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],
  3078. 'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],
  3079. 'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],
  3080. 'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],
  3081. 'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],
  3082. 'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],
  3083. 'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],
  3084. 'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],
  3085. 'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],
  3086. 'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],
  3087. 'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],
  3088. 'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],
  3089. 'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],
  3090. 'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],
  3091. 'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],
  3092. 'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],
  3093. 'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],
  3094. 'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],
  3095. 'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],
  3096. 'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],
  3097. 'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],
  3098. 'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],
  3099. 'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],
  3100. 'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],
  3101. 'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],
  3102. 'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],
  3103. 'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],
  3104. 'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],
  3105. 'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],
  3106. 'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],
  3107. 'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]
  3108. };
  3109. function clampCssByte(i) { // Clamp to integer 0 .. 255.
  3110. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  3111. return i < 0 ? 0 : i > 255 ? 255 : i;
  3112. }
  3113. function clampCssAngle(i) { // Clamp to integer 0 .. 360.
  3114. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  3115. return i < 0 ? 0 : i > 360 ? 360 : i;
  3116. }
  3117. function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0.
  3118. return f < 0 ? 0 : f > 1 ? 1 : f;
  3119. }
  3120. function parseCssInt(str) { // int or percentage.
  3121. if (str.length && str.charAt(str.length - 1) === '%') {
  3122. return clampCssByte(parseFloat(str) / 100 * 255);
  3123. }
  3124. return clampCssByte(parseInt(str, 10));
  3125. }
  3126. function parseCssFloat(str) { // float or percentage.
  3127. if (str.length && str.charAt(str.length - 1) === '%') {
  3128. return clampCssFloat(parseFloat(str) / 100);
  3129. }
  3130. return clampCssFloat(parseFloat(str));
  3131. }
  3132. function cssHueToRgb(m1, m2, h) {
  3133. if (h < 0) {
  3134. h += 1;
  3135. }
  3136. else if (h > 1) {
  3137. h -= 1;
  3138. }
  3139. if (h * 6 < 1) {
  3140. return m1 + (m2 - m1) * h * 6;
  3141. }
  3142. if (h * 2 < 1) {
  3143. return m2;
  3144. }
  3145. if (h * 3 < 2) {
  3146. return m1 + (m2 - m1) * (2 / 3 - h) * 6;
  3147. }
  3148. return m1;
  3149. }
  3150. function lerpNumber(a, b, p) {
  3151. return a + (b - a) * p;
  3152. }
  3153. function setRgba(out, r, g, b, a) {
  3154. out[0] = r; out[1] = g; out[2] = b; out[3] = a;
  3155. return out;
  3156. }
  3157. function copyRgba(out, a) {
  3158. out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
  3159. return out;
  3160. }
  3161. var colorCache = new LRU(20);
  3162. var lastRemovedArr = null;
  3163. function putToCache(colorStr, rgbaArr) {
  3164. // Reuse removed array
  3165. if (lastRemovedArr) {
  3166. copyRgba(lastRemovedArr, rgbaArr);
  3167. }
  3168. lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
  3169. }
  3170. /**
  3171. * @param {string} colorStr
  3172. * @param {Array.<number>} out
  3173. * @return {Array.<number>}
  3174. * @memberOf module:zrender/util/color
  3175. */
  3176. function parse(colorStr, rgbaArr) {
  3177. if (!colorStr) {
  3178. return;
  3179. }
  3180. rgbaArr = rgbaArr || [];
  3181. var cached = colorCache.get(colorStr);
  3182. if (cached) {
  3183. return copyRgba(rgbaArr, cached);
  3184. }
  3185. // colorStr may be not string
  3186. colorStr = colorStr + '';
  3187. // Remove all whitespace, not compliant, but should just be more accepting.
  3188. var str = colorStr.replace(/ /g, '').toLowerCase();
  3189. // Color keywords (and transparent) lookup.
  3190. if (str in kCSSColorTable) {
  3191. copyRgba(rgbaArr, kCSSColorTable[str]);
  3192. putToCache(colorStr, rgbaArr);
  3193. return rgbaArr;
  3194. }
  3195. // #abc and #abc123 syntax.
  3196. if (str.charAt(0) === '#') {
  3197. if (str.length === 4) {
  3198. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  3199. if (!(iv >= 0 && iv <= 0xfff)) {
  3200. setRgba(rgbaArr, 0, 0, 0, 1);
  3201. return; // Covers NaN.
  3202. }
  3203. setRgba(rgbaArr,
  3204. ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
  3205. (iv & 0xf0) | ((iv & 0xf0) >> 4),
  3206. (iv & 0xf) | ((iv & 0xf) << 4),
  3207. 1
  3208. );
  3209. putToCache(colorStr, rgbaArr);
  3210. return rgbaArr;
  3211. }
  3212. else if (str.length === 7) {
  3213. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  3214. if (!(iv >= 0 && iv <= 0xffffff)) {
  3215. setRgba(rgbaArr, 0, 0, 0, 1);
  3216. return; // Covers NaN.
  3217. }
  3218. setRgba(rgbaArr,
  3219. (iv & 0xff0000) >> 16,
  3220. (iv & 0xff00) >> 8,
  3221. iv & 0xff,
  3222. 1
  3223. );
  3224. putToCache(colorStr, rgbaArr);
  3225. return rgbaArr;
  3226. }
  3227. return;
  3228. }
  3229. var op = str.indexOf('(');
  3230. var ep = str.indexOf(')');
  3231. if (op !== -1 && ep + 1 === str.length) {
  3232. var fname = str.substr(0, op);
  3233. var params = str.substr(op + 1, ep - (op + 1)).split(',');
  3234. var alpha = 1; // To allow case fallthrough.
  3235. switch (fname) {
  3236. case 'rgba':
  3237. if (params.length !== 4) {
  3238. setRgba(rgbaArr, 0, 0, 0, 1);
  3239. return;
  3240. }
  3241. alpha = parseCssFloat(params.pop()); // jshint ignore:line
  3242. // Fall through.
  3243. case 'rgb':
  3244. if (params.length !== 3) {
  3245. setRgba(rgbaArr, 0, 0, 0, 1);
  3246. return;
  3247. }
  3248. setRgba(rgbaArr,
  3249. parseCssInt(params[0]),
  3250. parseCssInt(params[1]),
  3251. parseCssInt(params[2]),
  3252. alpha
  3253. );
  3254. putToCache(colorStr, rgbaArr);
  3255. return rgbaArr;
  3256. case 'hsla':
  3257. if (params.length !== 4) {
  3258. setRgba(rgbaArr, 0, 0, 0, 1);
  3259. return;
  3260. }
  3261. params[3] = parseCssFloat(params[3]);
  3262. hsla2rgba(params, rgbaArr);
  3263. putToCache(colorStr, rgbaArr);
  3264. return rgbaArr;
  3265. case 'hsl':
  3266. if (params.length !== 3) {
  3267. setRgba(rgbaArr, 0, 0, 0, 1);
  3268. return;
  3269. }
  3270. hsla2rgba(params, rgbaArr);
  3271. putToCache(colorStr, rgbaArr);
  3272. return rgbaArr;
  3273. default:
  3274. return;
  3275. }
  3276. }
  3277. setRgba(rgbaArr, 0, 0, 0, 1);
  3278. return;
  3279. }
  3280. /**
  3281. * @param {Array.<number>} hsla
  3282. * @param {Array.<number>} rgba
  3283. * @return {Array.<number>} rgba
  3284. */
  3285. function hsla2rgba(hsla, rgba) {
  3286. var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1
  3287. // NOTE(deanm): According to the CSS spec s/l should only be
  3288. // percentages, but we don't bother and let float or percentage.
  3289. var s = parseCssFloat(hsla[1]);
  3290. var l = parseCssFloat(hsla[2]);
  3291. var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  3292. var m1 = l * 2 - m2;
  3293. rgba = rgba || [];
  3294. setRgba(rgba,
  3295. clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
  3296. clampCssByte(cssHueToRgb(m1, m2, h) * 255),
  3297. clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
  3298. 1
  3299. );
  3300. if (hsla.length === 4) {
  3301. rgba[3] = hsla[3];
  3302. }
  3303. return rgba;
  3304. }
  3305. /**
  3306. * @param {Array.<number>} rgba
  3307. * @return {Array.<number>} hsla
  3308. */
  3309. function rgba2hsla(rgba) {
  3310. if (!rgba) {
  3311. return;
  3312. }
  3313. // RGB from 0 to 255
  3314. var R = rgba[0] / 255;
  3315. var G = rgba[1] / 255;
  3316. var B = rgba[2] / 255;
  3317. var vMin = Math.min(R, G, B); // Min. value of RGB
  3318. var vMax = Math.max(R, G, B); // Max. value of RGB
  3319. var delta = vMax - vMin; // Delta RGB value
  3320. var L = (vMax + vMin) / 2;
  3321. var H;
  3322. var S;
  3323. // HSL results from 0 to 1
  3324. if (delta === 0) {
  3325. H = 0;
  3326. S = 0;
  3327. }
  3328. else {
  3329. if (L < 0.5) {
  3330. S = delta / (vMax + vMin);
  3331. }
  3332. else {
  3333. S = delta / (2 - vMax - vMin);
  3334. }
  3335. var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;
  3336. var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;
  3337. var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;
  3338. if (R === vMax) {
  3339. H = deltaB - deltaG;
  3340. }
  3341. else if (G === vMax) {
  3342. H = (1 / 3) + deltaR - deltaB;
  3343. }
  3344. else if (B === vMax) {
  3345. H = (2 / 3) + deltaG - deltaR;
  3346. }
  3347. if (H < 0) {
  3348. H += 1;
  3349. }
  3350. if (H > 1) {
  3351. H -= 1;
  3352. }
  3353. }
  3354. var hsla = [H * 360, S, L];
  3355. if (rgba[3] != null) {
  3356. hsla.push(rgba[3]);
  3357. }
  3358. return hsla;
  3359. }
  3360. /**
  3361. * @param {string} color
  3362. * @param {number} level
  3363. * @return {string}
  3364. * @memberOf module:zrender/util/color
  3365. */
  3366. function lift(color, level) {
  3367. var colorArr = parse(color);
  3368. if (colorArr) {
  3369. for (var i = 0; i < 3; i++) {
  3370. if (level < 0) {
  3371. colorArr[i] = colorArr[i] * (1 - level) | 0;
  3372. }
  3373. else {
  3374. colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
  3375. }
  3376. if (colorArr[i] > 255) {
  3377. colorArr[i] = 255;
  3378. }
  3379. else if (color[i] < 0) {
  3380. colorArr[i] = 0;
  3381. }
  3382. }
  3383. return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
  3384. }
  3385. }
  3386. /**
  3387. * @param {string} color
  3388. * @return {string}
  3389. * @memberOf module:zrender/util/color
  3390. */
  3391. function toHex(color) {
  3392. var colorArr = parse(color);
  3393. if (colorArr) {
  3394. return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);
  3395. }
  3396. }
  3397. /**
  3398. * Map value to color. Faster than lerp methods because color is represented by rgba array.
  3399. * @param {number} normalizedValue A float between 0 and 1.
  3400. * @param {Array.<Array.<number>>} colors List of rgba color array
  3401. * @param {Array.<number>} [out] Mapped gba color array
  3402. * @return {Array.<number>} will be null/undefined if input illegal.
  3403. */
  3404. function fastLerp(normalizedValue, colors, out) {
  3405. if (!(colors && colors.length)
  3406. || !(normalizedValue >= 0 && normalizedValue <= 1)
  3407. ) {
  3408. return;
  3409. }
  3410. out = out || [];
  3411. var value = normalizedValue * (colors.length - 1);
  3412. var leftIndex = Math.floor(value);
  3413. var rightIndex = Math.ceil(value);
  3414. var leftColor = colors[leftIndex];
  3415. var rightColor = colors[rightIndex];
  3416. var dv = value - leftIndex;
  3417. out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));
  3418. out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));
  3419. out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));
  3420. out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));
  3421. return out;
  3422. }
  3423. /**
  3424. * @deprecated
  3425. */
  3426. var fastMapToColor = fastLerp;
  3427. /**
  3428. * @param {number} normalizedValue A float between 0 and 1.
  3429. * @param {Array.<string>} colors Color list.
  3430. * @param {boolean=} fullOutput Default false.
  3431. * @return {(string|Object)} Result color. If fullOutput,
  3432. * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
  3433. * @memberOf module:zrender/util/color
  3434. */
  3435. function lerp$1(normalizedValue, colors, fullOutput) {
  3436. if (!(colors && colors.length)
  3437. || !(normalizedValue >= 0 && normalizedValue <= 1)
  3438. ) {
  3439. return;
  3440. }
  3441. var value = normalizedValue * (colors.length - 1);
  3442. var leftIndex = Math.floor(value);
  3443. var rightIndex = Math.ceil(value);
  3444. var leftColor = parse(colors[leftIndex]);
  3445. var rightColor = parse(colors[rightIndex]);
  3446. var dv = value - leftIndex;
  3447. var color = stringify(
  3448. [
  3449. clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),
  3450. clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),
  3451. clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),
  3452. clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))
  3453. ],
  3454. 'rgba'
  3455. );
  3456. return fullOutput
  3457. ? {
  3458. color: color,
  3459. leftIndex: leftIndex,
  3460. rightIndex: rightIndex,
  3461. value: value
  3462. }
  3463. : color;
  3464. }
  3465. /**
  3466. * @deprecated
  3467. */
  3468. var mapToColor = lerp$1;
  3469. /**
  3470. * @param {string} color
  3471. * @param {number=} h 0 ~ 360, ignore when null.
  3472. * @param {number=} s 0 ~ 1, ignore when null.
  3473. * @param {number=} l 0 ~ 1, ignore when null.
  3474. * @return {string} Color string in rgba format.
  3475. * @memberOf module:zrender/util/color
  3476. */
  3477. function modifyHSL(color, h, s, l) {
  3478. color = parse(color);
  3479. if (color) {
  3480. color = rgba2hsla(color);
  3481. h != null && (color[0] = clampCssAngle(h));
  3482. s != null && (color[1] = parseCssFloat(s));
  3483. l != null && (color[2] = parseCssFloat(l));
  3484. return stringify(hsla2rgba(color), 'rgba');
  3485. }
  3486. }
  3487. /**
  3488. * @param {string} color
  3489. * @param {number=} alpha 0 ~ 1
  3490. * @return {string} Color string in rgba format.
  3491. * @memberOf module:zrender/util/color
  3492. */
  3493. function modifyAlpha(color, alpha) {
  3494. color = parse(color);
  3495. if (color && alpha != null) {
  3496. color[3] = clampCssFloat(alpha);
  3497. return stringify(color, 'rgba');
  3498. }
  3499. }
  3500. /**
  3501. * @param {Array.<number>} arrColor like [12,33,44,0.4]
  3502. * @param {string} type 'rgba', 'hsva', ...
  3503. * @return {string} Result color. (If input illegal, return undefined).
  3504. */
  3505. function stringify(arrColor, type) {
  3506. if (!arrColor || !arrColor.length) {
  3507. return;
  3508. }
  3509. var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
  3510. if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
  3511. colorStr += ',' + arrColor[3];
  3512. }
  3513. return type + '(' + colorStr + ')';
  3514. }
  3515. var color = (Object.freeze || Object)({
  3516. parse: parse,
  3517. lift: lift,
  3518. toHex: toHex,
  3519. fastLerp: fastLerp,
  3520. fastMapToColor: fastMapToColor,
  3521. lerp: lerp$1,
  3522. mapToColor: mapToColor,
  3523. modifyHSL: modifyHSL,
  3524. modifyAlpha: modifyAlpha,
  3525. stringify: stringify
  3526. });
  3527. /**
  3528. * @module echarts/animation/Animator
  3529. */
  3530. var arraySlice = Array.prototype.slice;
  3531. function defaultGetter(target, key) {
  3532. return target[key];
  3533. }
  3534. function defaultSetter(target, key, value) {
  3535. target[key] = value;
  3536. }
  3537. /**
  3538. * @param {number} p0
  3539. * @param {number} p1
  3540. * @param {number} percent
  3541. * @return {number}
  3542. */
  3543. function interpolateNumber(p0, p1, percent) {
  3544. return (p1 - p0) * percent + p0;
  3545. }
  3546. /**
  3547. * @param {string} p0
  3548. * @param {string} p1
  3549. * @param {number} percent
  3550. * @return {string}
  3551. */
  3552. function interpolateString(p0, p1, percent) {
  3553. return percent > 0.5 ? p1 : p0;
  3554. }
  3555. /**
  3556. * @param {Array} p0
  3557. * @param {Array} p1
  3558. * @param {number} percent
  3559. * @param {Array} out
  3560. * @param {number} arrDim
  3561. */
  3562. function interpolateArray(p0, p1, percent, out, arrDim) {
  3563. var len = p0.length;
  3564. if (arrDim === 1) {
  3565. for (var i = 0; i < len; i++) {
  3566. out[i] = interpolateNumber(p0[i], p1[i], percent);
  3567. }
  3568. }
  3569. else {
  3570. var len2 = len && p0[0].length;
  3571. for (var i = 0; i < len; i++) {
  3572. for (var j = 0; j < len2; j++) {
  3573. out[i][j] = interpolateNumber(
  3574. p0[i][j], p1[i][j], percent
  3575. );
  3576. }
  3577. }
  3578. }
  3579. }
  3580. // arr0 is source array, arr1 is target array.
  3581. // Do some preprocess to avoid error happened when interpolating from arr0 to arr1
  3582. function fillArr(arr0, arr1, arrDim) {
  3583. var arr0Len = arr0.length;
  3584. var arr1Len = arr1.length;
  3585. if (arr0Len !== arr1Len) {
  3586. // FIXME Not work for TypedArray
  3587. var isPreviousLarger = arr0Len > arr1Len;
  3588. if (isPreviousLarger) {
  3589. // Cut the previous
  3590. arr0.length = arr1Len;
  3591. }
  3592. else {
  3593. // Fill the previous
  3594. for (var i = arr0Len; i < arr1Len; i++) {
  3595. arr0.push(
  3596. arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])
  3597. );
  3598. }
  3599. }
  3600. }
  3601. // Handling NaN value
  3602. var len2 = arr0[0] && arr0[0].length;
  3603. for (var i = 0; i < arr0.length; i++) {
  3604. if (arrDim === 1) {
  3605. if (isNaN(arr0[i])) {
  3606. arr0[i] = arr1[i];
  3607. }
  3608. }
  3609. else {
  3610. for (var j = 0; j < len2; j++) {
  3611. if (isNaN(arr0[i][j])) {
  3612. arr0[i][j] = arr1[i][j];
  3613. }
  3614. }
  3615. }
  3616. }
  3617. }
  3618. /**
  3619. * @param {Array} arr0
  3620. * @param {Array} arr1
  3621. * @param {number} arrDim
  3622. * @return {boolean}
  3623. */
  3624. function isArraySame(arr0, arr1, arrDim) {
  3625. if (arr0 === arr1) {
  3626. return true;
  3627. }
  3628. var len = arr0.length;
  3629. if (len !== arr1.length) {
  3630. return false;
  3631. }
  3632. if (arrDim === 1) {
  3633. for (var i = 0; i < len; i++) {
  3634. if (arr0[i] !== arr1[i]) {
  3635. return false;
  3636. }
  3637. }
  3638. }
  3639. else {
  3640. var len2 = arr0[0].length;
  3641. for (var i = 0; i < len; i++) {
  3642. for (var j = 0; j < len2; j++) {
  3643. if (arr0[i][j] !== arr1[i][j]) {
  3644. return false;
  3645. }
  3646. }
  3647. }
  3648. }
  3649. return true;
  3650. }
  3651. /**
  3652. * Catmull Rom interpolate array
  3653. * @param {Array} p0
  3654. * @param {Array} p1
  3655. * @param {Array} p2
  3656. * @param {Array} p3
  3657. * @param {number} t
  3658. * @param {number} t2
  3659. * @param {number} t3
  3660. * @param {Array} out
  3661. * @param {number} arrDim
  3662. */
  3663. function catmullRomInterpolateArray(
  3664. p0, p1, p2, p3, t, t2, t3, out, arrDim
  3665. ) {
  3666. var len = p0.length;
  3667. if (arrDim === 1) {
  3668. for (var i = 0; i < len; i++) {
  3669. out[i] = catmullRomInterpolate(
  3670. p0[i], p1[i], p2[i], p3[i], t, t2, t3
  3671. );
  3672. }
  3673. }
  3674. else {
  3675. var len2 = p0[0].length;
  3676. for (var i = 0; i < len; i++) {
  3677. for (var j = 0; j < len2; j++) {
  3678. out[i][j] = catmullRomInterpolate(
  3679. p0[i][j], p1[i][j], p2[i][j], p3[i][j],
  3680. t, t2, t3
  3681. );
  3682. }
  3683. }
  3684. }
  3685. }
  3686. /**
  3687. * Catmull Rom interpolate number
  3688. * @param {number} p0
  3689. * @param {number} p1
  3690. * @param {number} p2
  3691. * @param {number} p3
  3692. * @param {number} t
  3693. * @param {number} t2
  3694. * @param {number} t3
  3695. * @return {number}
  3696. */
  3697. function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
  3698. var v0 = (p2 - p0) * 0.5;
  3699. var v1 = (p3 - p1) * 0.5;
  3700. return (2 * (p1 - p2) + v0 + v1) * t3
  3701. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  3702. + v0 * t + p1;
  3703. }
  3704. function cloneValue(value) {
  3705. if (isArrayLike(value)) {
  3706. var len = value.length;
  3707. if (isArrayLike(value[0])) {
  3708. var ret = [];
  3709. for (var i = 0; i < len; i++) {
  3710. ret.push(arraySlice.call(value[i]));
  3711. }
  3712. return ret;
  3713. }
  3714. return arraySlice.call(value);
  3715. }
  3716. return value;
  3717. }
  3718. function rgba2String(rgba) {
  3719. rgba[0] = Math.floor(rgba[0]);
  3720. rgba[1] = Math.floor(rgba[1]);
  3721. rgba[2] = Math.floor(rgba[2]);
  3722. return 'rgba(' + rgba.join(',') + ')';
  3723. }
  3724. function getArrayDim(keyframes) {
  3725. var lastValue = keyframes[keyframes.length - 1].value;
  3726. return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
  3727. }
  3728. function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
  3729. var getter = animator._getter;
  3730. var setter = animator._setter;
  3731. var useSpline = easing === 'spline';
  3732. var trackLen = keyframes.length;
  3733. if (!trackLen) {
  3734. return;
  3735. }
  3736. // Guess data type
  3737. var firstVal = keyframes[0].value;
  3738. var isValueArray = isArrayLike(firstVal);
  3739. var isValueColor = false;
  3740. var isValueString = false;
  3741. // For vertices morphing
  3742. var arrDim = isValueArray ? getArrayDim(keyframes) : 0;
  3743. var trackMaxTime;
  3744. // Sort keyframe as ascending
  3745. keyframes.sort(function (a, b) {
  3746. return a.time - b.time;
  3747. });
  3748. trackMaxTime = keyframes[trackLen - 1].time;
  3749. // Percents of each keyframe
  3750. var kfPercents = [];
  3751. // Value of each keyframe
  3752. var kfValues = [];
  3753. var prevValue = keyframes[0].value;
  3754. var isAllValueEqual = true;
  3755. for (var i = 0; i < trackLen; i++) {
  3756. kfPercents.push(keyframes[i].time / trackMaxTime);
  3757. // Assume value is a color when it is a string
  3758. var value = keyframes[i].value;
  3759. // Check if value is equal, deep check if value is array
  3760. if (!((isValueArray && isArraySame(value, prevValue, arrDim))
  3761. || (!isValueArray && value === prevValue))) {
  3762. isAllValueEqual = false;
  3763. }
  3764. prevValue = value;
  3765. // Try converting a string to a color array
  3766. if (typeof value === 'string') {
  3767. var colorArray = parse(value);
  3768. if (colorArray) {
  3769. value = colorArray;
  3770. isValueColor = true;
  3771. }
  3772. else {
  3773. isValueString = true;
  3774. }
  3775. }
  3776. kfValues.push(value);
  3777. }
  3778. if (!forceAnimate && isAllValueEqual) {
  3779. return;
  3780. }
  3781. var lastValue = kfValues[trackLen - 1];
  3782. // Polyfill array and NaN value
  3783. for (var i = 0; i < trackLen - 1; i++) {
  3784. if (isValueArray) {
  3785. fillArr(kfValues[i], lastValue, arrDim);
  3786. }
  3787. else {
  3788. if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
  3789. kfValues[i] = lastValue;
  3790. }
  3791. }
  3792. }
  3793. isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);
  3794. // Cache the key of last frame to speed up when
  3795. // animation playback is sequency
  3796. var lastFrame = 0;
  3797. var lastFramePercent = 0;
  3798. var start;
  3799. var w;
  3800. var p0;
  3801. var p1;
  3802. var p2;
  3803. var p3;
  3804. if (isValueColor) {
  3805. var rgba = [0, 0, 0, 0];
  3806. }
  3807. var onframe = function (target, percent) {
  3808. // Find the range keyframes
  3809. // kf1-----kf2---------current--------kf3
  3810. // find kf2 and kf3 and do interpolation
  3811. var frame;
  3812. // In the easing function like elasticOut, percent may less than 0
  3813. if (percent < 0) {
  3814. frame = 0;
  3815. }
  3816. else if (percent < lastFramePercent) {
  3817. // Start from next key
  3818. // PENDING start from lastFrame ?
  3819. start = Math.min(lastFrame + 1, trackLen - 1);
  3820. for (frame = start; frame >= 0; frame--) {
  3821. if (kfPercents[frame] <= percent) {
  3822. break;
  3823. }
  3824. }
  3825. // PENDING really need to do this ?
  3826. frame = Math.min(frame, trackLen - 2);
  3827. }
  3828. else {
  3829. for (frame = lastFrame; frame < trackLen; frame++) {
  3830. if (kfPercents[frame] > percent) {
  3831. break;
  3832. }
  3833. }
  3834. frame = Math.min(frame - 1, trackLen - 2);
  3835. }
  3836. lastFrame = frame;
  3837. lastFramePercent = percent;
  3838. var range = (kfPercents[frame + 1] - kfPercents[frame]);
  3839. if (range === 0) {
  3840. return;
  3841. }
  3842. else {
  3843. w = (percent - kfPercents[frame]) / range;
  3844. }
  3845. if (useSpline) {
  3846. p1 = kfValues[frame];
  3847. p0 = kfValues[frame === 0 ? frame : frame - 1];
  3848. p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
  3849. p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
  3850. if (isValueArray) {
  3851. catmullRomInterpolateArray(
  3852. p0, p1, p2, p3, w, w * w, w * w * w,
  3853. getter(target, propName),
  3854. arrDim
  3855. );
  3856. }
  3857. else {
  3858. var value;
  3859. if (isValueColor) {
  3860. value = catmullRomInterpolateArray(
  3861. p0, p1, p2, p3, w, w * w, w * w * w,
  3862. rgba, 1
  3863. );
  3864. value = rgba2String(rgba);
  3865. }
  3866. else if (isValueString) {
  3867. // String is step(0.5)
  3868. return interpolateString(p1, p2, w);
  3869. }
  3870. else {
  3871. value = catmullRomInterpolate(
  3872. p0, p1, p2, p3, w, w * w, w * w * w
  3873. );
  3874. }
  3875. setter(
  3876. target,
  3877. propName,
  3878. value
  3879. );
  3880. }
  3881. }
  3882. else {
  3883. if (isValueArray) {
  3884. interpolateArray(
  3885. kfValues[frame], kfValues[frame + 1], w,
  3886. getter(target, propName),
  3887. arrDim
  3888. );
  3889. }
  3890. else {
  3891. var value;
  3892. if (isValueColor) {
  3893. interpolateArray(
  3894. kfValues[frame], kfValues[frame + 1], w,
  3895. rgba, 1
  3896. );
  3897. value = rgba2String(rgba);
  3898. }
  3899. else if (isValueString) {
  3900. // String is step(0.5)
  3901. return interpolateString(kfValues[frame], kfValues[frame + 1], w);
  3902. }
  3903. else {
  3904. value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
  3905. }
  3906. setter(
  3907. target,
  3908. propName,
  3909. value
  3910. );
  3911. }
  3912. }
  3913. };
  3914. var clip = new Clip({
  3915. target: animator._target,
  3916. life: trackMaxTime,
  3917. loop: animator._loop,
  3918. delay: animator._delay,
  3919. onframe: onframe,
  3920. ondestroy: oneTrackDone
  3921. });
  3922. if (easing && easing !== 'spline') {
  3923. clip.easing = easing;
  3924. }
  3925. return clip;
  3926. }
  3927. /**
  3928. * @alias module:zrender/animation/Animator
  3929. * @constructor
  3930. * @param {Object} target
  3931. * @param {boolean} loop
  3932. * @param {Function} getter
  3933. * @param {Function} setter
  3934. */
  3935. var Animator = function (target, loop, getter, setter) {
  3936. this._tracks = {};
  3937. this._target = target;
  3938. this._loop = loop || false;
  3939. this._getter = getter || defaultGetter;
  3940. this._setter = setter || defaultSetter;
  3941. this._clipCount = 0;
  3942. this._delay = 0;
  3943. this._doneList = [];
  3944. this._onframeList = [];
  3945. this._clipList = [];
  3946. };
  3947. Animator.prototype = {
  3948. /**
  3949. * 设置动画关键帧
  3950. * @param {number} time 关键帧时间,单位是ms
  3951. * @param {Object} props 关键帧的属性值,key-value表示
  3952. * @return {module:zrender/animation/Animator}
  3953. */
  3954. when: function (time /* ms */, props) {
  3955. var tracks = this._tracks;
  3956. for (var propName in props) {
  3957. if (!props.hasOwnProperty(propName)) {
  3958. continue;
  3959. }
  3960. if (!tracks[propName]) {
  3961. tracks[propName] = [];
  3962. // Invalid value
  3963. var value = this._getter(this._target, propName);
  3964. if (value == null) {
  3965. // zrLog('Invalid property ' + propName);
  3966. continue;
  3967. }
  3968. // If time is 0
  3969. // Then props is given initialize value
  3970. // Else
  3971. // Initialize value from current prop value
  3972. if (time !== 0) {
  3973. tracks[propName].push({
  3974. time: 0,
  3975. value: cloneValue(value)
  3976. });
  3977. }
  3978. }
  3979. tracks[propName].push({
  3980. time: time,
  3981. value: props[propName]
  3982. });
  3983. }
  3984. return this;
  3985. },
  3986. /**
  3987. * 添加动画每一帧的回调函数
  3988. * @param {Function} callback
  3989. * @return {module:zrender/animation/Animator}
  3990. */
  3991. during: function (callback) {
  3992. this._onframeList.push(callback);
  3993. return this;
  3994. },
  3995. pause: function () {
  3996. for (var i = 0; i < this._clipList.length; i++) {
  3997. this._clipList[i].pause();
  3998. }
  3999. this._paused = true;
  4000. },
  4001. resume: function () {
  4002. for (var i = 0; i < this._clipList.length; i++) {
  4003. this._clipList[i].resume();
  4004. }
  4005. this._paused = false;
  4006. },
  4007. isPaused: function () {
  4008. return !!this._paused;
  4009. },
  4010. _doneCallback: function () {
  4011. // Clear all tracks
  4012. this._tracks = {};
  4013. // Clear all clips
  4014. this._clipList.length = 0;
  4015. var doneList = this._doneList;
  4016. var len = doneList.length;
  4017. for (var i = 0; i < len; i++) {
  4018. doneList[i].call(this);
  4019. }
  4020. },
  4021. /**
  4022. * 开始执行动画
  4023. * @param {string|Function} [easing]
  4024. * 动画缓动函数,详见{@link module:zrender/animation/easing}
  4025. * @param {boolean} forceAnimate
  4026. * @return {module:zrender/animation/Animator}
  4027. */
  4028. start: function (easing, forceAnimate) {
  4029. var self = this;
  4030. var clipCount = 0;
  4031. var oneTrackDone = function () {
  4032. clipCount--;
  4033. if (!clipCount) {
  4034. self._doneCallback();
  4035. }
  4036. };
  4037. var lastClip;
  4038. for (var propName in this._tracks) {
  4039. if (!this._tracks.hasOwnProperty(propName)) {
  4040. continue;
  4041. }
  4042. var clip = createTrackClip(
  4043. this, easing, oneTrackDone,
  4044. this._tracks[propName], propName, forceAnimate
  4045. );
  4046. if (clip) {
  4047. this._clipList.push(clip);
  4048. clipCount++;
  4049. // If start after added to animation
  4050. if (this.animation) {
  4051. this.animation.addClip(clip);
  4052. }
  4053. lastClip = clip;
  4054. }
  4055. }
  4056. // Add during callback on the last clip
  4057. if (lastClip) {
  4058. var oldOnFrame = lastClip.onframe;
  4059. lastClip.onframe = function (target, percent) {
  4060. oldOnFrame(target, percent);
  4061. for (var i = 0; i < self._onframeList.length; i++) {
  4062. self._onframeList[i](target, percent);
  4063. }
  4064. };
  4065. }
  4066. // This optimization will help the case that in the upper application
  4067. // the view may be refreshed frequently, where animation will be
  4068. // called repeatly but nothing changed.
  4069. if (!clipCount) {
  4070. this._doneCallback();
  4071. }
  4072. return this;
  4073. },
  4074. /**
  4075. * 停止动画
  4076. * @param {boolean} forwardToLast If move to last frame before stop
  4077. */
  4078. stop: function (forwardToLast) {
  4079. var clipList = this._clipList;
  4080. var animation = this.animation;
  4081. for (var i = 0; i < clipList.length; i++) {
  4082. var clip = clipList[i];
  4083. if (forwardToLast) {
  4084. // Move to last frame before stop
  4085. clip.onframe(this._target, 1);
  4086. }
  4087. animation && animation.removeClip(clip);
  4088. }
  4089. clipList.length = 0;
  4090. },
  4091. /**
  4092. * 设置动画延迟开始的时间
  4093. * @param {number} time 单位ms
  4094. * @return {module:zrender/animation/Animator}
  4095. */
  4096. delay: function (time) {
  4097. this._delay = time;
  4098. return this;
  4099. },
  4100. /**
  4101. * 添加动画结束的回调
  4102. * @param {Function} cb
  4103. * @return {module:zrender/animation/Animator}
  4104. */
  4105. done: function (cb) {
  4106. if (cb) {
  4107. this._doneList.push(cb);
  4108. }
  4109. return this;
  4110. },
  4111. /**
  4112. * @return {Array.<module:zrender/animation/Clip>}
  4113. */
  4114. getClips: function () {
  4115. return this._clipList;
  4116. }
  4117. };
  4118. var dpr = 1;
  4119. // If in browser environment
  4120. if (typeof window !== 'undefined') {
  4121. dpr = Math.max(window.devicePixelRatio || 1, 1);
  4122. }
  4123. /**
  4124. * config默认配置项
  4125. * @exports zrender/config
  4126. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  4127. */
  4128. /**
  4129. * debug日志选项:catchBrushException为true下有效
  4130. * 0 : 不生成debug数据,发布用
  4131. * 1 : 异常抛出,调试用
  4132. * 2 : 控制台输出,调试用
  4133. */
  4134. var debugMode = 0;
  4135. // retina 屏幕优化
  4136. var devicePixelRatio = dpr;
  4137. var log = function () {
  4138. };
  4139. if (debugMode === 1) {
  4140. log = function () {
  4141. for (var k in arguments) {
  4142. throw new Error(arguments[k]);
  4143. }
  4144. };
  4145. }
  4146. else if (debugMode > 1) {
  4147. log = function () {
  4148. for (var k in arguments) {
  4149. console.log(arguments[k]);
  4150. }
  4151. };
  4152. }
  4153. var zrLog = log;
  4154. /**
  4155. * @alias modue:zrender/mixin/Animatable
  4156. * @constructor
  4157. */
  4158. var Animatable = function () {
  4159. /**
  4160. * @type {Array.<module:zrender/animation/Animator>}
  4161. * @readOnly
  4162. */
  4163. this.animators = [];
  4164. };
  4165. Animatable.prototype = {
  4166. constructor: Animatable,
  4167. /**
  4168. * 动画
  4169. *
  4170. * @param {string} path The path to fetch value from object, like 'a.b.c'.
  4171. * @param {boolean} [loop] Whether to loop animation.
  4172. * @return {module:zrender/animation/Animator}
  4173. * @example:
  4174. * el.animate('style', false)
  4175. * .when(1000, {x: 10} )
  4176. * .done(function(){ // Animation done })
  4177. * .start()
  4178. */
  4179. animate: function (path, loop) {
  4180. var target;
  4181. var animatingShape = false;
  4182. var el = this;
  4183. var zr = this.__zr;
  4184. if (path) {
  4185. var pathSplitted = path.split('.');
  4186. var prop = el;
  4187. // If animating shape
  4188. animatingShape = pathSplitted[0] === 'shape';
  4189. for (var i = 0, l = pathSplitted.length; i < l; i++) {
  4190. if (!prop) {
  4191. continue;
  4192. }
  4193. prop = prop[pathSplitted[i]];
  4194. }
  4195. if (prop) {
  4196. target = prop;
  4197. }
  4198. }
  4199. else {
  4200. target = el;
  4201. }
  4202. if (!target) {
  4203. zrLog(
  4204. 'Property "'
  4205. + path
  4206. + '" is not existed in element '
  4207. + el.id
  4208. );
  4209. return;
  4210. }
  4211. var animators = el.animators;
  4212. var animator = new Animator(target, loop);
  4213. animator.during(function (target) {
  4214. el.dirty(animatingShape);
  4215. })
  4216. .done(function () {
  4217. // FIXME Animator will not be removed if use `Animator#stop` to stop animation
  4218. animators.splice(indexOf(animators, animator), 1);
  4219. });
  4220. animators.push(animator);
  4221. // If animate after added to the zrender
  4222. if (zr) {
  4223. zr.animation.addAnimator(animator);
  4224. }
  4225. return animator;
  4226. },
  4227. /**
  4228. * 停止动画
  4229. * @param {boolean} forwardToLast If move to last frame before stop
  4230. */
  4231. stopAnimation: function (forwardToLast) {
  4232. var animators = this.animators;
  4233. var len = animators.length;
  4234. for (var i = 0; i < len; i++) {
  4235. animators[i].stop(forwardToLast);
  4236. }
  4237. animators.length = 0;
  4238. return this;
  4239. },
  4240. /**
  4241. * Caution: this method will stop previous animation.
  4242. * So do not use this method to one element twice before
  4243. * animation starts, unless you know what you are doing.
  4244. * @param {Object} target
  4245. * @param {number} [time=500] Time in ms
  4246. * @param {string} [easing='linear']
  4247. * @param {number} [delay=0]
  4248. * @param {Function} [callback]
  4249. * @param {Function} [forceAnimate] Prevent stop animation and callback
  4250. * immediently when target values are the same as current values.
  4251. *
  4252. * @example
  4253. * // Animate position
  4254. * el.animateTo({
  4255. * position: [10, 10]
  4256. * }, function () { // done })
  4257. *
  4258. * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
  4259. * el.animateTo({
  4260. * shape: {
  4261. * width: 500
  4262. * },
  4263. * style: {
  4264. * fill: 'red'
  4265. * }
  4266. * position: [10, 10]
  4267. * }, 100, 100, 'cubicOut', function () { // done })
  4268. */
  4269. // TODO Return animation key
  4270. animateTo: function (target, time, delay, easing, callback, forceAnimate) {
  4271. animateTo(this, target, time, delay, easing, callback, forceAnimate);
  4272. },
  4273. /**
  4274. * Animate from the target state to current state.
  4275. * The params and the return value are the same as `this.animateTo`.
  4276. */
  4277. animateFrom: function (target, time, delay, easing, callback, forceAnimate) {
  4278. animateTo(this, target, time, delay, easing, callback, forceAnimate, true);
  4279. }
  4280. };
  4281. function animateTo(animatable, target, time, delay, easing, callback, forceAnimate, reverse) {
  4282. // animateTo(target, time, easing, callback);
  4283. if (isString(delay)) {
  4284. callback = easing;
  4285. easing = delay;
  4286. delay = 0;
  4287. }
  4288. // animateTo(target, time, delay, callback);
  4289. else if (isFunction$1(easing)) {
  4290. callback = easing;
  4291. easing = 'linear';
  4292. delay = 0;
  4293. }
  4294. // animateTo(target, time, callback);
  4295. else if (isFunction$1(delay)) {
  4296. callback = delay;
  4297. delay = 0;
  4298. }
  4299. // animateTo(target, callback)
  4300. else if (isFunction$1(time)) {
  4301. callback = time;
  4302. time = 500;
  4303. }
  4304. // animateTo(target)
  4305. else if (!time) {
  4306. time = 500;
  4307. }
  4308. // Stop all previous animations
  4309. animatable.stopAnimation();
  4310. animateToShallow(animatable, '', animatable, target, time, delay, reverse);
  4311. // Animators may be removed immediately after start
  4312. // if there is nothing to animate
  4313. var animators = animatable.animators.slice();
  4314. var count = animators.length;
  4315. function done() {
  4316. count--;
  4317. if (!count) {
  4318. callback && callback();
  4319. }
  4320. }
  4321. // No animators. This should be checked before animators[i].start(),
  4322. // because 'done' may be executed immediately if no need to animate.
  4323. if (!count) {
  4324. callback && callback();
  4325. }
  4326. // Start after all animators created
  4327. // Incase any animator is done immediately when all animation properties are not changed
  4328. for (var i = 0; i < animators.length; i++) {
  4329. animators[i]
  4330. .done(done)
  4331. .start(easing, forceAnimate);
  4332. }
  4333. }
  4334. /**
  4335. * @param {string} path=''
  4336. * @param {Object} source=animatable
  4337. * @param {Object} target
  4338. * @param {number} [time=500]
  4339. * @param {number} [delay=0]
  4340. * @param {boolean} [reverse] If `true`, animate
  4341. * from the `target` to current state.
  4342. *
  4343. * @example
  4344. * // Animate position
  4345. * el._animateToShallow({
  4346. * position: [10, 10]
  4347. * })
  4348. *
  4349. * // Animate shape, style and position in 100ms, delayed 100ms
  4350. * el._animateToShallow({
  4351. * shape: {
  4352. * width: 500
  4353. * },
  4354. * style: {
  4355. * fill: 'red'
  4356. * }
  4357. * position: [10, 10]
  4358. * }, 100, 100)
  4359. */
  4360. function animateToShallow(animatable, path, source, target, time, delay, reverse) {
  4361. var objShallow = {};
  4362. var propertyCount = 0;
  4363. for (var name in target) {
  4364. if (!target.hasOwnProperty(name)) {
  4365. continue;
  4366. }
  4367. if (source[name] != null) {
  4368. if (isObject$1(target[name]) && !isArrayLike(target[name])) {
  4369. animateToShallow(
  4370. animatable,
  4371. path ? path + '.' + name : name,
  4372. source[name],
  4373. target[name],
  4374. time,
  4375. delay,
  4376. reverse
  4377. );
  4378. }
  4379. else {
  4380. if (reverse) {
  4381. objShallow[name] = source[name];
  4382. setAttrByPath(animatable, path, name, target[name]);
  4383. }
  4384. else {
  4385. objShallow[name] = target[name];
  4386. }
  4387. propertyCount++;
  4388. }
  4389. }
  4390. else if (target[name] != null && !reverse) {
  4391. setAttrByPath(animatable, path, name, target[name]);
  4392. }
  4393. }
  4394. if (propertyCount > 0) {
  4395. animatable.animate(path, false)
  4396. .when(time == null ? 500 : time, objShallow)
  4397. .delay(delay || 0);
  4398. }
  4399. }
  4400. function setAttrByPath(el, path, name, value) {
  4401. // Attr directly if not has property
  4402. // FIXME, if some property not needed for element ?
  4403. if (!path) {
  4404. el.attr(name, value);
  4405. }
  4406. else {
  4407. // Only support set shape or style
  4408. var props = {};
  4409. props[path] = {};
  4410. props[path][name] = value;
  4411. el.attr(props);
  4412. }
  4413. }
  4414. /**
  4415. * @alias module:zrender/Element
  4416. * @constructor
  4417. * @extends {module:zrender/mixin/Animatable}
  4418. * @extends {module:zrender/mixin/Transformable}
  4419. * @extends {module:zrender/mixin/Eventful}
  4420. */
  4421. var Element = function (opts) { // jshint ignore:line
  4422. Transformable.call(this, opts);
  4423. Eventful.call(this, opts);
  4424. Animatable.call(this, opts);
  4425. /**
  4426. * 画布元素ID
  4427. * @type {string}
  4428. */
  4429. this.id = opts.id || guid();
  4430. };
  4431. Element.prototype = {
  4432. /**
  4433. * 元素类型
  4434. * Element type
  4435. * @type {string}
  4436. */
  4437. type: 'element',
  4438. /**
  4439. * 元素名字
  4440. * Element name
  4441. * @type {string}
  4442. */
  4443. name: '',
  4444. /**
  4445. * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值
  4446. * ZRender instance will be assigned when element is associated with zrender
  4447. * @name module:/zrender/Element#__zr
  4448. * @type {module:zrender/ZRender}
  4449. */
  4450. __zr: null,
  4451. /**
  4452. * 图形是否忽略,为true时忽略图形的绘制以及事件触发
  4453. * If ignore drawing and events of the element object
  4454. * @name module:/zrender/Element#ignore
  4455. * @type {boolean}
  4456. * @default false
  4457. */
  4458. ignore: false,
  4459. /**
  4460. * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪
  4461. * 该路径会继承被裁减对象的变换
  4462. * @type {module:zrender/graphic/Path}
  4463. * @see http://www.w3.org/TR/2dcontext/#clipping-region
  4464. * @readOnly
  4465. */
  4466. clipPath: null,
  4467. /**
  4468. * 是否是 Group
  4469. * @type {boolean}
  4470. */
  4471. isGroup: false,
  4472. /**
  4473. * Drift element
  4474. * @param {number} dx dx on the global space
  4475. * @param {number} dy dy on the global space
  4476. */
  4477. drift: function (dx, dy) {
  4478. switch (this.draggable) {
  4479. case 'horizontal':
  4480. dy = 0;
  4481. break;
  4482. case 'vertical':
  4483. dx = 0;
  4484. break;
  4485. }
  4486. var m = this.transform;
  4487. if (!m) {
  4488. m = this.transform = [1, 0, 0, 1, 0, 0];
  4489. }
  4490. m[4] += dx;
  4491. m[5] += dy;
  4492. this.decomposeTransform();
  4493. this.dirty(false);
  4494. },
  4495. /**
  4496. * Hook before update
  4497. */
  4498. beforeUpdate: function () {},
  4499. /**
  4500. * Hook after update
  4501. */
  4502. afterUpdate: function () {},
  4503. /**
  4504. * Update each frame
  4505. */
  4506. update: function () {
  4507. this.updateTransform();
  4508. },
  4509. /**
  4510. * @param {Function} cb
  4511. * @param {} context
  4512. */
  4513. traverse: function (cb, context) {},
  4514. /**
  4515. * @protected
  4516. */
  4517. attrKV: function (key, value) {
  4518. if (key === 'position' || key === 'scale' || key === 'origin') {
  4519. // Copy the array
  4520. if (value) {
  4521. var target = this[key];
  4522. if (!target) {
  4523. target = this[key] = [];
  4524. }
  4525. target[0] = value[0];
  4526. target[1] = value[1];
  4527. }
  4528. }
  4529. else {
  4530. this[key] = value;
  4531. }
  4532. },
  4533. /**
  4534. * Hide the element
  4535. */
  4536. hide: function () {
  4537. this.ignore = true;
  4538. this.__zr && this.__zr.refresh();
  4539. },
  4540. /**
  4541. * Show the element
  4542. */
  4543. show: function () {
  4544. this.ignore = false;
  4545. this.__zr && this.__zr.refresh();
  4546. },
  4547. /**
  4548. * @param {string|Object} key
  4549. * @param {*} value
  4550. */
  4551. attr: function (key, value) {
  4552. if (typeof key === 'string') {
  4553. this.attrKV(key, value);
  4554. }
  4555. else if (isObject$1(key)) {
  4556. for (var name in key) {
  4557. if (key.hasOwnProperty(name)) {
  4558. this.attrKV(name, key[name]);
  4559. }
  4560. }
  4561. }
  4562. this.dirty(false);
  4563. return this;
  4564. },
  4565. /**
  4566. * @param {module:zrender/graphic/Path} clipPath
  4567. */
  4568. setClipPath: function (clipPath) {
  4569. var zr = this.__zr;
  4570. if (zr) {
  4571. clipPath.addSelfToZr(zr);
  4572. }
  4573. // Remove previous clip path
  4574. if (this.clipPath && this.clipPath !== clipPath) {
  4575. this.removeClipPath();
  4576. }
  4577. this.clipPath = clipPath;
  4578. clipPath.__zr = zr;
  4579. clipPath.__clipTarget = this;
  4580. this.dirty(false);
  4581. },
  4582. /**
  4583. */
  4584. removeClipPath: function () {
  4585. var clipPath = this.clipPath;
  4586. if (clipPath) {
  4587. if (clipPath.__zr) {
  4588. clipPath.removeSelfFromZr(clipPath.__zr);
  4589. }
  4590. clipPath.__zr = null;
  4591. clipPath.__clipTarget = null;
  4592. this.clipPath = null;
  4593. this.dirty(false);
  4594. }
  4595. },
  4596. /**
  4597. * Add self from zrender instance.
  4598. * Not recursively because it will be invoked when element added to storage.
  4599. * @param {module:zrender/ZRender} zr
  4600. */
  4601. addSelfToZr: function (zr) {
  4602. this.__zr = zr;
  4603. // 添加动画
  4604. var animators = this.animators;
  4605. if (animators) {
  4606. for (var i = 0; i < animators.length; i++) {
  4607. zr.animation.addAnimator(animators[i]);
  4608. }
  4609. }
  4610. if (this.clipPath) {
  4611. this.clipPath.addSelfToZr(zr);
  4612. }
  4613. },
  4614. /**
  4615. * Remove self from zrender instance.
  4616. * Not recursively because it will be invoked when element added to storage.
  4617. * @param {module:zrender/ZRender} zr
  4618. */
  4619. removeSelfFromZr: function (zr) {
  4620. this.__zr = null;
  4621. // 移除动画
  4622. var animators = this.animators;
  4623. if (animators) {
  4624. for (var i = 0; i < animators.length; i++) {
  4625. zr.animation.removeAnimator(animators[i]);
  4626. }
  4627. }
  4628. if (this.clipPath) {
  4629. this.clipPath.removeSelfFromZr(zr);
  4630. }
  4631. }
  4632. };
  4633. mixin(Element, Animatable);
  4634. mixin(Element, Transformable);
  4635. mixin(Element, Eventful);
  4636. /**
  4637. * @module echarts/core/BoundingRect
  4638. */
  4639. var v2ApplyTransform = applyTransform;
  4640. var mathMin = Math.min;
  4641. var mathMax = Math.max;
  4642. /**
  4643. * @alias module:echarts/core/BoundingRect
  4644. */
  4645. function BoundingRect(x, y, width, height) {
  4646. if (width < 0) {
  4647. x = x + width;
  4648. width = -width;
  4649. }
  4650. if (height < 0) {
  4651. y = y + height;
  4652. height = -height;
  4653. }
  4654. /**
  4655. * @type {number}
  4656. */
  4657. this.x = x;
  4658. /**
  4659. * @type {number}
  4660. */
  4661. this.y = y;
  4662. /**
  4663. * @type {number}
  4664. */
  4665. this.width = width;
  4666. /**
  4667. * @type {number}
  4668. */
  4669. this.height = height;
  4670. }
  4671. BoundingRect.prototype = {
  4672. constructor: BoundingRect,
  4673. /**
  4674. * @param {module:echarts/core/BoundingRect} other
  4675. */
  4676. union: function (other) {
  4677. var x = mathMin(other.x, this.x);
  4678. var y = mathMin(other.y, this.y);
  4679. this.width = mathMax(
  4680. other.x + other.width,
  4681. this.x + this.width
  4682. ) - x;
  4683. this.height = mathMax(
  4684. other.y + other.height,
  4685. this.y + this.height
  4686. ) - y;
  4687. this.x = x;
  4688. this.y = y;
  4689. },
  4690. /**
  4691. * @param {Array.<number>} m
  4692. * @methods
  4693. */
  4694. applyTransform: (function () {
  4695. var lt = [];
  4696. var rb = [];
  4697. var lb = [];
  4698. var rt = [];
  4699. return function (m) {
  4700. // In case usage like this
  4701. // el.getBoundingRect().applyTransform(el.transform)
  4702. // And element has no transform
  4703. if (!m) {
  4704. return;
  4705. }
  4706. lt[0] = lb[0] = this.x;
  4707. lt[1] = rt[1] = this.y;
  4708. rb[0] = rt[0] = this.x + this.width;
  4709. rb[1] = lb[1] = this.y + this.height;
  4710. v2ApplyTransform(lt, lt, m);
  4711. v2ApplyTransform(rb, rb, m);
  4712. v2ApplyTransform(lb, lb, m);
  4713. v2ApplyTransform(rt, rt, m);
  4714. this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
  4715. this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
  4716. var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
  4717. var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
  4718. this.width = maxX - this.x;
  4719. this.height = maxY - this.y;
  4720. };
  4721. })(),
  4722. /**
  4723. * Calculate matrix of transforming from self to target rect
  4724. * @param {module:zrender/core/BoundingRect} b
  4725. * @return {Array.<number>}
  4726. */
  4727. calculateTransform: function (b) {
  4728. var a = this;
  4729. var sx = b.width / a.width;
  4730. var sy = b.height / a.height;
  4731. var m = create$1();
  4732. // 矩阵右乘
  4733. translate(m, m, [-a.x, -a.y]);
  4734. scale$1(m, m, [sx, sy]);
  4735. translate(m, m, [b.x, b.y]);
  4736. return m;
  4737. },
  4738. /**
  4739. * @param {(module:echarts/core/BoundingRect|Object)} b
  4740. * @return {boolean}
  4741. */
  4742. intersect: function (b) {
  4743. if (!b) {
  4744. return false;
  4745. }
  4746. if (!(b instanceof BoundingRect)) {
  4747. // Normalize negative width/height.
  4748. b = BoundingRect.create(b);
  4749. }
  4750. var a = this;
  4751. var ax0 = a.x;
  4752. var ax1 = a.x + a.width;
  4753. var ay0 = a.y;
  4754. var ay1 = a.y + a.height;
  4755. var bx0 = b.x;
  4756. var bx1 = b.x + b.width;
  4757. var by0 = b.y;
  4758. var by1 = b.y + b.height;
  4759. return !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
  4760. },
  4761. contain: function (x, y) {
  4762. var rect = this;
  4763. return x >= rect.x
  4764. && x <= (rect.x + rect.width)
  4765. && y >= rect.y
  4766. && y <= (rect.y + rect.height);
  4767. },
  4768. /**
  4769. * @return {module:echarts/core/BoundingRect}
  4770. */
  4771. clone: function () {
  4772. return new BoundingRect(this.x, this.y, this.width, this.height);
  4773. },
  4774. /**
  4775. * Copy from another rect
  4776. */
  4777. copy: function (other) {
  4778. this.x = other.x;
  4779. this.y = other.y;
  4780. this.width = other.width;
  4781. this.height = other.height;
  4782. },
  4783. plain: function () {
  4784. return {
  4785. x: this.x,
  4786. y: this.y,
  4787. width: this.width,
  4788. height: this.height
  4789. };
  4790. }
  4791. };
  4792. /**
  4793. * @param {Object|module:zrender/core/BoundingRect} rect
  4794. * @param {number} rect.x
  4795. * @param {number} rect.y
  4796. * @param {number} rect.width
  4797. * @param {number} rect.height
  4798. * @return {module:zrender/core/BoundingRect}
  4799. */
  4800. BoundingRect.create = function (rect) {
  4801. return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
  4802. };
  4803. /**
  4804. * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
  4805. * @module zrender/graphic/Group
  4806. * @example
  4807. * var Group = require('zrender/container/Group');
  4808. * var Circle = require('zrender/graphic/shape/Circle');
  4809. * var g = new Group();
  4810. * g.position[0] = 100;
  4811. * g.position[1] = 100;
  4812. * g.add(new Circle({
  4813. * style: {
  4814. * x: 100,
  4815. * y: 100,
  4816. * r: 20,
  4817. * }
  4818. * }));
  4819. * zr.add(g);
  4820. */
  4821. /**
  4822. * @alias module:zrender/graphic/Group
  4823. * @constructor
  4824. * @extends module:zrender/mixin/Transformable
  4825. * @extends module:zrender/mixin/Eventful
  4826. */
  4827. var Group = function (opts) {
  4828. opts = opts || {};
  4829. Element.call(this, opts);
  4830. for (var key in opts) {
  4831. if (opts.hasOwnProperty(key)) {
  4832. this[key] = opts[key];
  4833. }
  4834. }
  4835. this._children = [];
  4836. this.__storage = null;
  4837. this.__dirty = true;
  4838. };
  4839. Group.prototype = {
  4840. constructor: Group,
  4841. isGroup: true,
  4842. /**
  4843. * @type {string}
  4844. */
  4845. type: 'group',
  4846. /**
  4847. * 所有子孙元素是否响应鼠标事件
  4848. * @name module:/zrender/container/Group#silent
  4849. * @type {boolean}
  4850. * @default false
  4851. */
  4852. silent: false,
  4853. /**
  4854. * @return {Array.<module:zrender/Element>}
  4855. */
  4856. children: function () {
  4857. return this._children.slice();
  4858. },
  4859. /**
  4860. * 获取指定 index 的儿子节点
  4861. * @param {number} idx
  4862. * @return {module:zrender/Element}
  4863. */
  4864. childAt: function (idx) {
  4865. return this._children[idx];
  4866. },
  4867. /**
  4868. * 获取指定名字的儿子节点
  4869. * @param {string} name
  4870. * @return {module:zrender/Element}
  4871. */
  4872. childOfName: function (name) {
  4873. var children = this._children;
  4874. for (var i = 0; i < children.length; i++) {
  4875. if (children[i].name === name) {
  4876. return children[i];
  4877. }
  4878. }
  4879. },
  4880. /**
  4881. * @return {number}
  4882. */
  4883. childCount: function () {
  4884. return this._children.length;
  4885. },
  4886. /**
  4887. * 添加子节点到最后
  4888. * @param {module:zrender/Element} child
  4889. */
  4890. add: function (child) {
  4891. if (child && child !== this && child.parent !== this) {
  4892. this._children.push(child);
  4893. this._doAdd(child);
  4894. }
  4895. return this;
  4896. },
  4897. /**
  4898. * 添加子节点在 nextSibling 之前
  4899. * @param {module:zrender/Element} child
  4900. * @param {module:zrender/Element} nextSibling
  4901. */
  4902. addBefore: function (child, nextSibling) {
  4903. if (child && child !== this && child.parent !== this
  4904. && nextSibling && nextSibling.parent === this) {
  4905. var children = this._children;
  4906. var idx = children.indexOf(nextSibling);
  4907. if (idx >= 0) {
  4908. children.splice(idx, 0, child);
  4909. this._doAdd(child);
  4910. }
  4911. }
  4912. return this;
  4913. },
  4914. _doAdd: function (child) {
  4915. if (child.parent) {
  4916. child.parent.remove(child);
  4917. }
  4918. child.parent = this;
  4919. var storage = this.__storage;
  4920. var zr = this.__zr;
  4921. if (storage && storage !== child.__storage) {
  4922. storage.addToStorage(child);
  4923. if (child instanceof Group) {
  4924. child.addChildrenToStorage(storage);
  4925. }
  4926. }
  4927. zr && zr.refresh();
  4928. },
  4929. /**
  4930. * 移除子节点
  4931. * @param {module:zrender/Element} child
  4932. */
  4933. remove: function (child) {
  4934. var zr = this.__zr;
  4935. var storage = this.__storage;
  4936. var children = this._children;
  4937. var idx = indexOf(children, child);
  4938. if (idx < 0) {
  4939. return this;
  4940. }
  4941. children.splice(idx, 1);
  4942. child.parent = null;
  4943. if (storage) {
  4944. storage.delFromStorage(child);
  4945. if (child instanceof Group) {
  4946. child.delChildrenFromStorage(storage);
  4947. }
  4948. }
  4949. zr && zr.refresh();
  4950. return this;
  4951. },
  4952. /**
  4953. * 移除所有子节点
  4954. */
  4955. removeAll: function () {
  4956. var children = this._children;
  4957. var storage = this.__storage;
  4958. var child;
  4959. var i;
  4960. for (i = 0; i < children.length; i++) {
  4961. child = children[i];
  4962. if (storage) {
  4963. storage.delFromStorage(child);
  4964. if (child instanceof Group) {
  4965. child.delChildrenFromStorage(storage);
  4966. }
  4967. }
  4968. child.parent = null;
  4969. }
  4970. children.length = 0;
  4971. return this;
  4972. },
  4973. /**
  4974. * 遍历所有子节点
  4975. * @param {Function} cb
  4976. * @param {} context
  4977. */
  4978. eachChild: function (cb, context) {
  4979. var children = this._children;
  4980. for (var i = 0; i < children.length; i++) {
  4981. var child = children[i];
  4982. cb.call(context, child, i);
  4983. }
  4984. return this;
  4985. },
  4986. /**
  4987. * 深度优先遍历所有子孙节点
  4988. * @param {Function} cb
  4989. * @param {} context
  4990. */
  4991. traverse: function (cb, context) {
  4992. for (var i = 0; i < this._children.length; i++) {
  4993. var child = this._children[i];
  4994. cb.call(context, child);
  4995. if (child.type === 'group') {
  4996. child.traverse(cb, context);
  4997. }
  4998. }
  4999. return this;
  5000. },
  5001. addChildrenToStorage: function (storage) {
  5002. for (var i = 0; i < this._children.length; i++) {
  5003. var child = this._children[i];
  5004. storage.addToStorage(child);
  5005. if (child instanceof Group) {
  5006. child.addChildrenToStorage(storage);
  5007. }
  5008. }
  5009. },
  5010. delChildrenFromStorage: function (storage) {
  5011. for (var i = 0; i < this._children.length; i++) {
  5012. var child = this._children[i];
  5013. storage.delFromStorage(child);
  5014. if (child instanceof Group) {
  5015. child.delChildrenFromStorage(storage);
  5016. }
  5017. }
  5018. },
  5019. dirty: function () {
  5020. this.__dirty = true;
  5021. this.__zr && this.__zr.refresh();
  5022. return this;
  5023. },
  5024. /**
  5025. * @return {module:zrender/core/BoundingRect}
  5026. */
  5027. getBoundingRect: function (includeChildren) {
  5028. // TODO Caching
  5029. var rect = null;
  5030. var tmpRect = new BoundingRect(0, 0, 0, 0);
  5031. var children = includeChildren || this._children;
  5032. var tmpMat = [];
  5033. for (var i = 0; i < children.length; i++) {
  5034. var child = children[i];
  5035. if (child.ignore || child.invisible) {
  5036. continue;
  5037. }
  5038. var childRect = child.getBoundingRect();
  5039. var transform = child.getLocalTransform(tmpMat);
  5040. // TODO
  5041. // The boundingRect cacluated by transforming original
  5042. // rect may be bigger than the actual bundingRect when rotation
  5043. // is used. (Consider a circle rotated aginst its center, where
  5044. // the actual boundingRect should be the same as that not be
  5045. // rotated.) But we can not find better approach to calculate
  5046. // actual boundingRect yet, considering performance.
  5047. if (transform) {
  5048. tmpRect.copy(childRect);
  5049. tmpRect.applyTransform(transform);
  5050. rect = rect || tmpRect.clone();
  5051. rect.union(tmpRect);
  5052. }
  5053. else {
  5054. rect = rect || childRect.clone();
  5055. rect.union(childRect);
  5056. }
  5057. }
  5058. return rect || tmpRect;
  5059. }
  5060. };
  5061. inherits(Group, Element);
  5062. // https://github.com/mziccard/node-timsort
  5063. var DEFAULT_MIN_MERGE = 32;
  5064. var DEFAULT_MIN_GALLOPING = 7;
  5065. function minRunLength(n) {
  5066. var r = 0;
  5067. while (n >= DEFAULT_MIN_MERGE) {
  5068. r |= n & 1;
  5069. n >>= 1;
  5070. }
  5071. return n + r;
  5072. }
  5073. function makeAscendingRun(array, lo, hi, compare) {
  5074. var runHi = lo + 1;
  5075. if (runHi === hi) {
  5076. return 1;
  5077. }
  5078. if (compare(array[runHi++], array[lo]) < 0) {
  5079. while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
  5080. runHi++;
  5081. }
  5082. reverseRun(array, lo, runHi);
  5083. }
  5084. else {
  5085. while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
  5086. runHi++;
  5087. }
  5088. }
  5089. return runHi - lo;
  5090. }
  5091. function reverseRun(array, lo, hi) {
  5092. hi--;
  5093. while (lo < hi) {
  5094. var t = array[lo];
  5095. array[lo++] = array[hi];
  5096. array[hi--] = t;
  5097. }
  5098. }
  5099. function binaryInsertionSort(array, lo, hi, start, compare) {
  5100. if (start === lo) {
  5101. start++;
  5102. }
  5103. for (; start < hi; start++) {
  5104. var pivot = array[start];
  5105. var left = lo;
  5106. var right = start;
  5107. var mid;
  5108. while (left < right) {
  5109. mid = left + right >>> 1;
  5110. if (compare(pivot, array[mid]) < 0) {
  5111. right = mid;
  5112. }
  5113. else {
  5114. left = mid + 1;
  5115. }
  5116. }
  5117. var n = start - left;
  5118. switch (n) {
  5119. case 3:
  5120. array[left + 3] = array[left + 2];
  5121. case 2:
  5122. array[left + 2] = array[left + 1];
  5123. case 1:
  5124. array[left + 1] = array[left];
  5125. break;
  5126. default:
  5127. while (n > 0) {
  5128. array[left + n] = array[left + n - 1];
  5129. n--;
  5130. }
  5131. }
  5132. array[left] = pivot;
  5133. }
  5134. }
  5135. function gallopLeft(value, array, start, length, hint, compare) {
  5136. var lastOffset = 0;
  5137. var maxOffset = 0;
  5138. var offset = 1;
  5139. if (compare(value, array[start + hint]) > 0) {
  5140. maxOffset = length - hint;
  5141. while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
  5142. lastOffset = offset;
  5143. offset = (offset << 1) + 1;
  5144. if (offset <= 0) {
  5145. offset = maxOffset;
  5146. }
  5147. }
  5148. if (offset > maxOffset) {
  5149. offset = maxOffset;
  5150. }
  5151. lastOffset += hint;
  5152. offset += hint;
  5153. }
  5154. else {
  5155. maxOffset = hint + 1;
  5156. while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
  5157. lastOffset = offset;
  5158. offset = (offset << 1) + 1;
  5159. if (offset <= 0) {
  5160. offset = maxOffset;
  5161. }
  5162. }
  5163. if (offset > maxOffset) {
  5164. offset = maxOffset;
  5165. }
  5166. var tmp = lastOffset;
  5167. lastOffset = hint - offset;
  5168. offset = hint - tmp;
  5169. }
  5170. lastOffset++;
  5171. while (lastOffset < offset) {
  5172. var m = lastOffset + (offset - lastOffset >>> 1);
  5173. if (compare(value, array[start + m]) > 0) {
  5174. lastOffset = m + 1;
  5175. }
  5176. else {
  5177. offset = m;
  5178. }
  5179. }
  5180. return offset;
  5181. }
  5182. function gallopRight(value, array, start, length, hint, compare) {
  5183. var lastOffset = 0;
  5184. var maxOffset = 0;
  5185. var offset = 1;
  5186. if (compare(value, array[start + hint]) < 0) {
  5187. maxOffset = hint + 1;
  5188. while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
  5189. lastOffset = offset;
  5190. offset = (offset << 1) + 1;
  5191. if (offset <= 0) {
  5192. offset = maxOffset;
  5193. }
  5194. }
  5195. if (offset > maxOffset) {
  5196. offset = maxOffset;
  5197. }
  5198. var tmp = lastOffset;
  5199. lastOffset = hint - offset;
  5200. offset = hint - tmp;
  5201. }
  5202. else {
  5203. maxOffset = length - hint;
  5204. while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
  5205. lastOffset = offset;
  5206. offset = (offset << 1) + 1;
  5207. if (offset <= 0) {
  5208. offset = maxOffset;
  5209. }
  5210. }
  5211. if (offset > maxOffset) {
  5212. offset = maxOffset;
  5213. }
  5214. lastOffset += hint;
  5215. offset += hint;
  5216. }
  5217. lastOffset++;
  5218. while (lastOffset < offset) {
  5219. var m = lastOffset + (offset - lastOffset >>> 1);
  5220. if (compare(value, array[start + m]) < 0) {
  5221. offset = m;
  5222. }
  5223. else {
  5224. lastOffset = m + 1;
  5225. }
  5226. }
  5227. return offset;
  5228. }
  5229. function TimSort(array, compare) {
  5230. var minGallop = DEFAULT_MIN_GALLOPING;
  5231. var runStart;
  5232. var runLength;
  5233. var stackSize = 0;
  5234. var tmp = [];
  5235. runStart = [];
  5236. runLength = [];
  5237. function pushRun(_runStart, _runLength) {
  5238. runStart[stackSize] = _runStart;
  5239. runLength[stackSize] = _runLength;
  5240. stackSize += 1;
  5241. }
  5242. function mergeRuns() {
  5243. while (stackSize > 1) {
  5244. var n = stackSize - 2;
  5245. if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
  5246. if (runLength[n - 1] < runLength[n + 1]) {
  5247. n--;
  5248. }
  5249. }
  5250. else if (runLength[n] > runLength[n + 1]) {
  5251. break;
  5252. }
  5253. mergeAt(n);
  5254. }
  5255. }
  5256. function forceMergeRuns() {
  5257. while (stackSize > 1) {
  5258. var n = stackSize - 2;
  5259. if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
  5260. n--;
  5261. }
  5262. mergeAt(n);
  5263. }
  5264. }
  5265. function mergeAt(i) {
  5266. var start1 = runStart[i];
  5267. var length1 = runLength[i];
  5268. var start2 = runStart[i + 1];
  5269. var length2 = runLength[i + 1];
  5270. runLength[i] = length1 + length2;
  5271. if (i === stackSize - 3) {
  5272. runStart[i + 1] = runStart[i + 2];
  5273. runLength[i + 1] = runLength[i + 2];
  5274. }
  5275. stackSize--;
  5276. var k = gallopRight(array[start2], array, start1, length1, 0, compare);
  5277. start1 += k;
  5278. length1 -= k;
  5279. if (length1 === 0) {
  5280. return;
  5281. }
  5282. length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
  5283. if (length2 === 0) {
  5284. return;
  5285. }
  5286. if (length1 <= length2) {
  5287. mergeLow(start1, length1, start2, length2);
  5288. }
  5289. else {
  5290. mergeHigh(start1, length1, start2, length2);
  5291. }
  5292. }
  5293. function mergeLow(start1, length1, start2, length2) {
  5294. var i = 0;
  5295. for (i = 0; i < length1; i++) {
  5296. tmp[i] = array[start1 + i];
  5297. }
  5298. var cursor1 = 0;
  5299. var cursor2 = start2;
  5300. var dest = start1;
  5301. array[dest++] = array[cursor2++];
  5302. if (--length2 === 0) {
  5303. for (i = 0; i < length1; i++) {
  5304. array[dest + i] = tmp[cursor1 + i];
  5305. }
  5306. return;
  5307. }
  5308. if (length1 === 1) {
  5309. for (i = 0; i < length2; i++) {
  5310. array[dest + i] = array[cursor2 + i];
  5311. }
  5312. array[dest + length2] = tmp[cursor1];
  5313. return;
  5314. }
  5315. var _minGallop = minGallop;
  5316. var count1, count2, exit;
  5317. while (1) {
  5318. count1 = 0;
  5319. count2 = 0;
  5320. exit = false;
  5321. do {
  5322. if (compare(array[cursor2], tmp[cursor1]) < 0) {
  5323. array[dest++] = array[cursor2++];
  5324. count2++;
  5325. count1 = 0;
  5326. if (--length2 === 0) {
  5327. exit = true;
  5328. break;
  5329. }
  5330. }
  5331. else {
  5332. array[dest++] = tmp[cursor1++];
  5333. count1++;
  5334. count2 = 0;
  5335. if (--length1 === 1) {
  5336. exit = true;
  5337. break;
  5338. }
  5339. }
  5340. } while ((count1 | count2) < _minGallop);
  5341. if (exit) {
  5342. break;
  5343. }
  5344. do {
  5345. count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
  5346. if (count1 !== 0) {
  5347. for (i = 0; i < count1; i++) {
  5348. array[dest + i] = tmp[cursor1 + i];
  5349. }
  5350. dest += count1;
  5351. cursor1 += count1;
  5352. length1 -= count1;
  5353. if (length1 <= 1) {
  5354. exit = true;
  5355. break;
  5356. }
  5357. }
  5358. array[dest++] = array[cursor2++];
  5359. if (--length2 === 0) {
  5360. exit = true;
  5361. break;
  5362. }
  5363. count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
  5364. if (count2 !== 0) {
  5365. for (i = 0; i < count2; i++) {
  5366. array[dest + i] = array[cursor2 + i];
  5367. }
  5368. dest += count2;
  5369. cursor2 += count2;
  5370. length2 -= count2;
  5371. if (length2 === 0) {
  5372. exit = true;
  5373. break;
  5374. }
  5375. }
  5376. array[dest++] = tmp[cursor1++];
  5377. if (--length1 === 1) {
  5378. exit = true;
  5379. break;
  5380. }
  5381. _minGallop--;
  5382. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  5383. if (exit) {
  5384. break;
  5385. }
  5386. if (_minGallop < 0) {
  5387. _minGallop = 0;
  5388. }
  5389. _minGallop += 2;
  5390. }
  5391. minGallop = _minGallop;
  5392. minGallop < 1 && (minGallop = 1);
  5393. if (length1 === 1) {
  5394. for (i = 0; i < length2; i++) {
  5395. array[dest + i] = array[cursor2 + i];
  5396. }
  5397. array[dest + length2] = tmp[cursor1];
  5398. }
  5399. else if (length1 === 0) {
  5400. throw new Error();
  5401. // throw new Error('mergeLow preconditions were not respected');
  5402. }
  5403. else {
  5404. for (i = 0; i < length1; i++) {
  5405. array[dest + i] = tmp[cursor1 + i];
  5406. }
  5407. }
  5408. }
  5409. function mergeHigh(start1, length1, start2, length2) {
  5410. var i = 0;
  5411. for (i = 0; i < length2; i++) {
  5412. tmp[i] = array[start2 + i];
  5413. }
  5414. var cursor1 = start1 + length1 - 1;
  5415. var cursor2 = length2 - 1;
  5416. var dest = start2 + length2 - 1;
  5417. var customCursor = 0;
  5418. var customDest = 0;
  5419. array[dest--] = array[cursor1--];
  5420. if (--length1 === 0) {
  5421. customCursor = dest - (length2 - 1);
  5422. for (i = 0; i < length2; i++) {
  5423. array[customCursor + i] = tmp[i];
  5424. }
  5425. return;
  5426. }
  5427. if (length2 === 1) {
  5428. dest -= length1;
  5429. cursor1 -= length1;
  5430. customDest = dest + 1;
  5431. customCursor = cursor1 + 1;
  5432. for (i = length1 - 1; i >= 0; i--) {
  5433. array[customDest + i] = array[customCursor + i];
  5434. }
  5435. array[dest] = tmp[cursor2];
  5436. return;
  5437. }
  5438. var _minGallop = minGallop;
  5439. while (true) {
  5440. var count1 = 0;
  5441. var count2 = 0;
  5442. var exit = false;
  5443. do {
  5444. if (compare(tmp[cursor2], array[cursor1]) < 0) {
  5445. array[dest--] = array[cursor1--];
  5446. count1++;
  5447. count2 = 0;
  5448. if (--length1 === 0) {
  5449. exit = true;
  5450. break;
  5451. }
  5452. }
  5453. else {
  5454. array[dest--] = tmp[cursor2--];
  5455. count2++;
  5456. count1 = 0;
  5457. if (--length2 === 1) {
  5458. exit = true;
  5459. break;
  5460. }
  5461. }
  5462. } while ((count1 | count2) < _minGallop);
  5463. if (exit) {
  5464. break;
  5465. }
  5466. do {
  5467. count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
  5468. if (count1 !== 0) {
  5469. dest -= count1;
  5470. cursor1 -= count1;
  5471. length1 -= count1;
  5472. customDest = dest + 1;
  5473. customCursor = cursor1 + 1;
  5474. for (i = count1 - 1; i >= 0; i--) {
  5475. array[customDest + i] = array[customCursor + i];
  5476. }
  5477. if (length1 === 0) {
  5478. exit = true;
  5479. break;
  5480. }
  5481. }
  5482. array[dest--] = tmp[cursor2--];
  5483. if (--length2 === 1) {
  5484. exit = true;
  5485. break;
  5486. }
  5487. count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
  5488. if (count2 !== 0) {
  5489. dest -= count2;
  5490. cursor2 -= count2;
  5491. length2 -= count2;
  5492. customDest = dest + 1;
  5493. customCursor = cursor2 + 1;
  5494. for (i = 0; i < count2; i++) {
  5495. array[customDest + i] = tmp[customCursor + i];
  5496. }
  5497. if (length2 <= 1) {
  5498. exit = true;
  5499. break;
  5500. }
  5501. }
  5502. array[dest--] = array[cursor1--];
  5503. if (--length1 === 0) {
  5504. exit = true;
  5505. break;
  5506. }
  5507. _minGallop--;
  5508. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  5509. if (exit) {
  5510. break;
  5511. }
  5512. if (_minGallop < 0) {
  5513. _minGallop = 0;
  5514. }
  5515. _minGallop += 2;
  5516. }
  5517. minGallop = _minGallop;
  5518. if (minGallop < 1) {
  5519. minGallop = 1;
  5520. }
  5521. if (length2 === 1) {
  5522. dest -= length1;
  5523. cursor1 -= length1;
  5524. customDest = dest + 1;
  5525. customCursor = cursor1 + 1;
  5526. for (i = length1 - 1; i >= 0; i--) {
  5527. array[customDest + i] = array[customCursor + i];
  5528. }
  5529. array[dest] = tmp[cursor2];
  5530. }
  5531. else if (length2 === 0) {
  5532. throw new Error();
  5533. // throw new Error('mergeHigh preconditions were not respected');
  5534. }
  5535. else {
  5536. customCursor = dest - (length2 - 1);
  5537. for (i = 0; i < length2; i++) {
  5538. array[customCursor + i] = tmp[i];
  5539. }
  5540. }
  5541. }
  5542. this.mergeRuns = mergeRuns;
  5543. this.forceMergeRuns = forceMergeRuns;
  5544. this.pushRun = pushRun;
  5545. }
  5546. function sort(array, compare, lo, hi) {
  5547. if (!lo) {
  5548. lo = 0;
  5549. }
  5550. if (!hi) {
  5551. hi = array.length;
  5552. }
  5553. var remaining = hi - lo;
  5554. if (remaining < 2) {
  5555. return;
  5556. }
  5557. var runLength = 0;
  5558. if (remaining < DEFAULT_MIN_MERGE) {
  5559. runLength = makeAscendingRun(array, lo, hi, compare);
  5560. binaryInsertionSort(array, lo, hi, lo + runLength, compare);
  5561. return;
  5562. }
  5563. var ts = new TimSort(array, compare);
  5564. var minRun = minRunLength(remaining);
  5565. do {
  5566. runLength = makeAscendingRun(array, lo, hi, compare);
  5567. if (runLength < minRun) {
  5568. var force = remaining;
  5569. if (force > minRun) {
  5570. force = minRun;
  5571. }
  5572. binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
  5573. runLength = force;
  5574. }
  5575. ts.pushRun(lo, runLength);
  5576. ts.mergeRuns();
  5577. remaining -= runLength;
  5578. lo += runLength;
  5579. } while (remaining !== 0);
  5580. ts.forceMergeRuns();
  5581. }
  5582. // Use timsort because in most case elements are partially sorted
  5583. // https://jsfiddle.net/pissang/jr4x7mdm/8/
  5584. function shapeCompareFunc(a, b) {
  5585. if (a.zlevel === b.zlevel) {
  5586. if (a.z === b.z) {
  5587. // if (a.z2 === b.z2) {
  5588. // // FIXME Slow has renderidx compare
  5589. // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
  5590. // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
  5591. // return a.__renderidx - b.__renderidx;
  5592. // }
  5593. return a.z2 - b.z2;
  5594. }
  5595. return a.z - b.z;
  5596. }
  5597. return a.zlevel - b.zlevel;
  5598. }
  5599. /**
  5600. * 内容仓库 (M)
  5601. * @alias module:zrender/Storage
  5602. * @constructor
  5603. */
  5604. var Storage = function () { // jshint ignore:line
  5605. this._roots = [];
  5606. this._displayList = [];
  5607. this._displayListLen = 0;
  5608. };
  5609. Storage.prototype = {
  5610. constructor: Storage,
  5611. /**
  5612. * @param {Function} cb
  5613. *
  5614. */
  5615. traverse: function (cb, context) {
  5616. for (var i = 0; i < this._roots.length; i++) {
  5617. this._roots[i].traverse(cb, context);
  5618. }
  5619. },
  5620. /**
  5621. * 返回所有图形的绘制队列
  5622. * @param {boolean} [update=false] 是否在返回前更新该数组
  5623. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
  5624. *
  5625. * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
  5626. * @return {Array.<module:zrender/graphic/Displayable>}
  5627. */
  5628. getDisplayList: function (update, includeIgnore) {
  5629. includeIgnore = includeIgnore || false;
  5630. if (update) {
  5631. this.updateDisplayList(includeIgnore);
  5632. }
  5633. return this._displayList;
  5634. },
  5635. /**
  5636. * 更新图形的绘制队列。
  5637. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  5638. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  5639. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
  5640. */
  5641. updateDisplayList: function (includeIgnore) {
  5642. this._displayListLen = 0;
  5643. var roots = this._roots;
  5644. var displayList = this._displayList;
  5645. for (var i = 0, len = roots.length; i < len; i++) {
  5646. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  5647. }
  5648. displayList.length = this._displayListLen;
  5649. env$1.canvasSupported && sort(displayList, shapeCompareFunc);
  5650. },
  5651. _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
  5652. if (el.ignore && !includeIgnore) {
  5653. return;
  5654. }
  5655. el.beforeUpdate();
  5656. if (el.__dirty) {
  5657. el.update();
  5658. }
  5659. el.afterUpdate();
  5660. var userSetClipPath = el.clipPath;
  5661. if (userSetClipPath) {
  5662. // FIXME 效率影响
  5663. if (clipPaths) {
  5664. clipPaths = clipPaths.slice();
  5665. }
  5666. else {
  5667. clipPaths = [];
  5668. }
  5669. var currentClipPath = userSetClipPath;
  5670. var parentClipPath = el;
  5671. // Recursively add clip path
  5672. while (currentClipPath) {
  5673. // clipPath 的变换是基于使用这个 clipPath 的元素
  5674. currentClipPath.parent = parentClipPath;
  5675. currentClipPath.updateTransform();
  5676. clipPaths.push(currentClipPath);
  5677. parentClipPath = currentClipPath;
  5678. currentClipPath = currentClipPath.clipPath;
  5679. }
  5680. }
  5681. if (el.isGroup) {
  5682. var children = el._children;
  5683. for (var i = 0; i < children.length; i++) {
  5684. var child = children[i];
  5685. // Force to mark as dirty if group is dirty
  5686. // FIXME __dirtyPath ?
  5687. if (el.__dirty) {
  5688. child.__dirty = true;
  5689. }
  5690. this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
  5691. }
  5692. // Mark group clean here
  5693. el.__dirty = false;
  5694. }
  5695. else {
  5696. el.__clipPaths = clipPaths;
  5697. this._displayList[this._displayListLen++] = el;
  5698. }
  5699. },
  5700. /**
  5701. * 添加图形(Shape)或者组(Group)到根节点
  5702. * @param {module:zrender/Element} el
  5703. */
  5704. addRoot: function (el) {
  5705. if (el.__storage === this) {
  5706. return;
  5707. }
  5708. if (el instanceof Group) {
  5709. el.addChildrenToStorage(this);
  5710. }
  5711. this.addToStorage(el);
  5712. this._roots.push(el);
  5713. },
  5714. /**
  5715. * 删除指定的图形(Shape)或者组(Group)
  5716. * @param {string|Array.<string>} [el] 如果为空清空整个Storage
  5717. */
  5718. delRoot: function (el) {
  5719. if (el == null) {
  5720. // 不指定el清空
  5721. for (var i = 0; i < this._roots.length; i++) {
  5722. var root = this._roots[i];
  5723. if (root instanceof Group) {
  5724. root.delChildrenFromStorage(this);
  5725. }
  5726. }
  5727. this._roots = [];
  5728. this._displayList = [];
  5729. this._displayListLen = 0;
  5730. return;
  5731. }
  5732. if (el instanceof Array) {
  5733. for (var i = 0, l = el.length; i < l; i++) {
  5734. this.delRoot(el[i]);
  5735. }
  5736. return;
  5737. }
  5738. var idx = indexOf(this._roots, el);
  5739. if (idx >= 0) {
  5740. this.delFromStorage(el);
  5741. this._roots.splice(idx, 1);
  5742. if (el instanceof Group) {
  5743. el.delChildrenFromStorage(this);
  5744. }
  5745. }
  5746. },
  5747. addToStorage: function (el) {
  5748. if (el) {
  5749. el.__storage = this;
  5750. el.dirty(false);
  5751. }
  5752. return this;
  5753. },
  5754. delFromStorage: function (el) {
  5755. if (el) {
  5756. el.__storage = null;
  5757. }
  5758. return this;
  5759. },
  5760. /**
  5761. * 清空并且释放Storage
  5762. */
  5763. dispose: function () {
  5764. this._renderList =
  5765. this._roots = null;
  5766. },
  5767. displayableSortFunc: shapeCompareFunc
  5768. };
  5769. var SHADOW_PROPS = {
  5770. 'shadowBlur': 1,
  5771. 'shadowOffsetX': 1,
  5772. 'shadowOffsetY': 1,
  5773. 'textShadowBlur': 1,
  5774. 'textShadowOffsetX': 1,
  5775. 'textShadowOffsetY': 1,
  5776. 'textBoxShadowBlur': 1,
  5777. 'textBoxShadowOffsetX': 1,
  5778. 'textBoxShadowOffsetY': 1
  5779. };
  5780. var fixShadow = function (ctx, propName, value) {
  5781. if (SHADOW_PROPS.hasOwnProperty(propName)) {
  5782. return value *= ctx.dpr;
  5783. }
  5784. return value;
  5785. };
  5786. var ContextCachedBy = {
  5787. NONE: 0,
  5788. STYLE_BIND: 1,
  5789. PLAIN_TEXT: 2
  5790. };
  5791. // Avoid confused with 0/false.
  5792. var WILL_BE_RESTORED = 9;
  5793. var STYLE_COMMON_PROPS = [
  5794. ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
  5795. ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
  5796. ];
  5797. // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
  5798. // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
  5799. var Style = function (opts) {
  5800. this.extendFrom(opts, false);
  5801. };
  5802. function createLinearGradient(ctx, obj, rect) {
  5803. var x = obj.x == null ? 0 : obj.x;
  5804. var x2 = obj.x2 == null ? 1 : obj.x2;
  5805. var y = obj.y == null ? 0 : obj.y;
  5806. var y2 = obj.y2 == null ? 0 : obj.y2;
  5807. if (!obj.global) {
  5808. x = x * rect.width + rect.x;
  5809. x2 = x2 * rect.width + rect.x;
  5810. y = y * rect.height + rect.y;
  5811. y2 = y2 * rect.height + rect.y;
  5812. }
  5813. // Fix NaN when rect is Infinity
  5814. x = isNaN(x) ? 0 : x;
  5815. x2 = isNaN(x2) ? 1 : x2;
  5816. y = isNaN(y) ? 0 : y;
  5817. y2 = isNaN(y2) ? 0 : y2;
  5818. var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
  5819. return canvasGradient;
  5820. }
  5821. function createRadialGradient(ctx, obj, rect) {
  5822. var width = rect.width;
  5823. var height = rect.height;
  5824. var min = Math.min(width, height);
  5825. var x = obj.x == null ? 0.5 : obj.x;
  5826. var y = obj.y == null ? 0.5 : obj.y;
  5827. var r = obj.r == null ? 0.5 : obj.r;
  5828. if (!obj.global) {
  5829. x = x * width + rect.x;
  5830. y = y * height + rect.y;
  5831. r = r * min;
  5832. }
  5833. var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
  5834. return canvasGradient;
  5835. }
  5836. Style.prototype = {
  5837. constructor: Style,
  5838. /**
  5839. * @type {string}
  5840. */
  5841. fill: '#000',
  5842. /**
  5843. * @type {string}
  5844. */
  5845. stroke: null,
  5846. /**
  5847. * @type {number}
  5848. */
  5849. opacity: 1,
  5850. /**
  5851. * @type {number}
  5852. */
  5853. fillOpacity: null,
  5854. /**
  5855. * @type {number}
  5856. */
  5857. strokeOpacity: null,
  5858. /**
  5859. * @type {Array.<number>}
  5860. */
  5861. lineDash: null,
  5862. /**
  5863. * @type {number}
  5864. */
  5865. lineDashOffset: 0,
  5866. /**
  5867. * @type {number}
  5868. */
  5869. shadowBlur: 0,
  5870. /**
  5871. * @type {number}
  5872. */
  5873. shadowOffsetX: 0,
  5874. /**
  5875. * @type {number}
  5876. */
  5877. shadowOffsetY: 0,
  5878. /**
  5879. * @type {number}
  5880. */
  5881. lineWidth: 1,
  5882. /**
  5883. * If stroke ignore scale
  5884. * @type {Boolean}
  5885. */
  5886. strokeNoScale: false,
  5887. // Bounding rect text configuration
  5888. // Not affected by element transform
  5889. /**
  5890. * @type {string}
  5891. */
  5892. text: null,
  5893. /**
  5894. * If `fontSize` or `fontFamily` exists, `font` will be reset by
  5895. * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
  5896. * So do not visit it directly in upper application (like echarts),
  5897. * but use `contain/text#makeFont` instead.
  5898. * @type {string}
  5899. */
  5900. font: null,
  5901. /**
  5902. * The same as font. Use font please.
  5903. * @deprecated
  5904. * @type {string}
  5905. */
  5906. textFont: null,
  5907. /**
  5908. * It helps merging respectively, rather than parsing an entire font string.
  5909. * @type {string}
  5910. */
  5911. fontStyle: null,
  5912. /**
  5913. * It helps merging respectively, rather than parsing an entire font string.
  5914. * @type {string}
  5915. */
  5916. fontWeight: null,
  5917. /**
  5918. * It helps merging respectively, rather than parsing an entire font string.
  5919. * Should be 12 but not '12px'.
  5920. * @type {number}
  5921. */
  5922. fontSize: null,
  5923. /**
  5924. * It helps merging respectively, rather than parsing an entire font string.
  5925. * @type {string}
  5926. */
  5927. fontFamily: null,
  5928. /**
  5929. * Reserved for special functinality, like 'hr'.
  5930. * @type {string}
  5931. */
  5932. textTag: null,
  5933. /**
  5934. * @type {string}
  5935. */
  5936. textFill: '#000',
  5937. /**
  5938. * @type {string}
  5939. */
  5940. textStroke: null,
  5941. /**
  5942. * @type {number}
  5943. */
  5944. textWidth: null,
  5945. /**
  5946. * Only for textBackground.
  5947. * @type {number}
  5948. */
  5949. textHeight: null,
  5950. /**
  5951. * textStroke may be set as some color as a default
  5952. * value in upper applicaion, where the default value
  5953. * of textStrokeWidth should be 0 to make sure that
  5954. * user can choose to do not use text stroke.
  5955. * @type {number}
  5956. */
  5957. textStrokeWidth: 0,
  5958. /**
  5959. * @type {number}
  5960. */
  5961. textLineHeight: null,
  5962. /**
  5963. * 'inside', 'left', 'right', 'top', 'bottom'
  5964. * [x, y]
  5965. * Based on x, y of rect.
  5966. * @type {string|Array.<number>}
  5967. * @default 'inside'
  5968. */
  5969. textPosition: 'inside',
  5970. /**
  5971. * If not specified, use the boundingRect of a `displayable`.
  5972. * @type {Object}
  5973. */
  5974. textRect: null,
  5975. /**
  5976. * [x, y]
  5977. * @type {Array.<number>}
  5978. */
  5979. textOffset: null,
  5980. /**
  5981. * @type {string}
  5982. */
  5983. textAlign: null,
  5984. /**
  5985. * @type {string}
  5986. */
  5987. textVerticalAlign: null,
  5988. /**
  5989. * @type {number}
  5990. */
  5991. textDistance: 5,
  5992. /**
  5993. * @type {string}
  5994. */
  5995. textShadowColor: 'transparent',
  5996. /**
  5997. * @type {number}
  5998. */
  5999. textShadowBlur: 0,
  6000. /**
  6001. * @type {number}
  6002. */
  6003. textShadowOffsetX: 0,
  6004. /**
  6005. * @type {number}
  6006. */
  6007. textShadowOffsetY: 0,
  6008. /**
  6009. * @type {string}
  6010. */
  6011. textBoxShadowColor: 'transparent',
  6012. /**
  6013. * @type {number}
  6014. */
  6015. textBoxShadowBlur: 0,
  6016. /**
  6017. * @type {number}
  6018. */
  6019. textBoxShadowOffsetX: 0,
  6020. /**
  6021. * @type {number}
  6022. */
  6023. textBoxShadowOffsetY: 0,
  6024. /**
  6025. * Whether transform text.
  6026. * Only useful in Path and Image element
  6027. * @type {boolean}
  6028. */
  6029. transformText: false,
  6030. /**
  6031. * Text rotate around position of Path or Image
  6032. * Only useful in Path and Image element and transformText is false.
  6033. */
  6034. textRotation: 0,
  6035. /**
  6036. * Text origin of text rotation, like [10, 40].
  6037. * Based on x, y of rect.
  6038. * Useful in label rotation of circular symbol.
  6039. * By default, this origin is textPosition.
  6040. * Can be 'center'.
  6041. * @type {string|Array.<number>}
  6042. */
  6043. textOrigin: null,
  6044. /**
  6045. * @type {string}
  6046. */
  6047. textBackgroundColor: null,
  6048. /**
  6049. * @type {string}
  6050. */
  6051. textBorderColor: null,
  6052. /**
  6053. * @type {number}
  6054. */
  6055. textBorderWidth: 0,
  6056. /**
  6057. * @type {number}
  6058. */
  6059. textBorderRadius: 0,
  6060. /**
  6061. * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
  6062. * @type {number|Array.<number>}
  6063. */
  6064. textPadding: null,
  6065. /**
  6066. * Text styles for rich text.
  6067. * @type {Object}
  6068. */
  6069. rich: null,
  6070. /**
  6071. * {outerWidth, outerHeight, ellipsis, placeholder}
  6072. * @type {Object}
  6073. */
  6074. truncate: null,
  6075. /**
  6076. * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  6077. * @type {string}
  6078. */
  6079. blend: null,
  6080. /**
  6081. * @param {CanvasRenderingContext2D} ctx
  6082. */
  6083. bind: function (ctx, el, prevEl) {
  6084. var style = this;
  6085. var prevStyle = prevEl && prevEl.style;
  6086. // If no prevStyle, it means first draw.
  6087. // Only apply cache if the last time cachced by this function.
  6088. var notCheckCache = !prevStyle || ctx.__attrCachedBy !== ContextCachedBy.STYLE_BIND;
  6089. ctx.__attrCachedBy = ContextCachedBy.STYLE_BIND;
  6090. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  6091. var prop = STYLE_COMMON_PROPS[i];
  6092. var styleName = prop[0];
  6093. if (notCheckCache || style[styleName] !== prevStyle[styleName]) {
  6094. // FIXME Invalid property value will cause style leak from previous element.
  6095. ctx[styleName] =
  6096. fixShadow(ctx, styleName, style[styleName] || prop[1]);
  6097. }
  6098. }
  6099. if ((notCheckCache || style.fill !== prevStyle.fill)) {
  6100. ctx.fillStyle = style.fill;
  6101. }
  6102. if ((notCheckCache || style.stroke !== prevStyle.stroke)) {
  6103. ctx.strokeStyle = style.stroke;
  6104. }
  6105. if ((notCheckCache || style.opacity !== prevStyle.opacity)) {
  6106. ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
  6107. }
  6108. if ((notCheckCache || style.blend !== prevStyle.blend)) {
  6109. ctx.globalCompositeOperation = style.blend || 'source-over';
  6110. }
  6111. if (this.hasStroke()) {
  6112. var lineWidth = style.lineWidth;
  6113. ctx.lineWidth = lineWidth / (
  6114. (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
  6115. );
  6116. }
  6117. },
  6118. hasFill: function () {
  6119. var fill = this.fill;
  6120. return fill != null && fill !== 'none';
  6121. },
  6122. hasStroke: function () {
  6123. var stroke = this.stroke;
  6124. return stroke != null && stroke !== 'none' && this.lineWidth > 0;
  6125. },
  6126. /**
  6127. * Extend from other style
  6128. * @param {zrender/graphic/Style} otherStyle
  6129. * @param {boolean} overwrite true: overwrirte any way.
  6130. * false: overwrite only when !target.hasOwnProperty
  6131. * others: overwrite when property is not null/undefined.
  6132. */
  6133. extendFrom: function (otherStyle, overwrite) {
  6134. if (otherStyle) {
  6135. for (var name in otherStyle) {
  6136. if (otherStyle.hasOwnProperty(name)
  6137. && (overwrite === true
  6138. || (
  6139. overwrite === false
  6140. ? !this.hasOwnProperty(name)
  6141. : otherStyle[name] != null
  6142. )
  6143. )
  6144. ) {
  6145. this[name] = otherStyle[name];
  6146. }
  6147. }
  6148. }
  6149. },
  6150. /**
  6151. * Batch setting style with a given object
  6152. * @param {Object|string} obj
  6153. * @param {*} [obj]
  6154. */
  6155. set: function (obj, value) {
  6156. if (typeof obj === 'string') {
  6157. this[obj] = value;
  6158. }
  6159. else {
  6160. this.extendFrom(obj, true);
  6161. }
  6162. },
  6163. /**
  6164. * Clone
  6165. * @return {zrender/graphic/Style} [description]
  6166. */
  6167. clone: function () {
  6168. var newStyle = new this.constructor();
  6169. newStyle.extendFrom(this, true);
  6170. return newStyle;
  6171. },
  6172. getGradient: function (ctx, obj, rect) {
  6173. var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
  6174. var canvasGradient = method(ctx, obj, rect);
  6175. var colorStops = obj.colorStops;
  6176. for (var i = 0; i < colorStops.length; i++) {
  6177. canvasGradient.addColorStop(
  6178. colorStops[i].offset, colorStops[i].color
  6179. );
  6180. }
  6181. return canvasGradient;
  6182. }
  6183. };
  6184. var styleProto = Style.prototype;
  6185. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  6186. var prop = STYLE_COMMON_PROPS[i];
  6187. if (!(prop[0] in styleProto)) {
  6188. styleProto[prop[0]] = prop[1];
  6189. }
  6190. }
  6191. // Provide for others
  6192. Style.getGradient = styleProto.getGradient;
  6193. var Pattern = function (image, repeat) {
  6194. // Should do nothing more in this constructor. Because gradient can be
  6195. // declard by `color: {image: ...}`, where this constructor will not be called.
  6196. this.image = image;
  6197. this.repeat = repeat;
  6198. // Can be cloned
  6199. this.type = 'pattern';
  6200. };
  6201. Pattern.prototype.getCanvasPattern = function (ctx) {
  6202. return ctx.createPattern(this.image, this.repeat || 'repeat');
  6203. };
  6204. /**
  6205. * @module zrender/Layer
  6206. * @author pissang(https://www.github.com/pissang)
  6207. */
  6208. function returnFalse() {
  6209. return false;
  6210. }
  6211. /**
  6212. * 创建dom
  6213. *
  6214. * @inner
  6215. * @param {string} id dom id 待用
  6216. * @param {Painter} painter painter instance
  6217. * @param {number} number
  6218. */
  6219. function createDom(id, painter, dpr) {
  6220. var newDom = createCanvas();
  6221. var width = painter.getWidth();
  6222. var height = painter.getHeight();
  6223. var newDomStyle = newDom.style;
  6224. if (newDomStyle) { // In node or some other non-browser environment
  6225. newDomStyle.position = 'absolute';
  6226. newDomStyle.left = 0;
  6227. newDomStyle.top = 0;
  6228. newDomStyle.width = width + 'px';
  6229. newDomStyle.height = height + 'px';
  6230. newDom.setAttribute('data-zr-dom-id', id);
  6231. }
  6232. newDom.width = width * dpr;
  6233. newDom.height = height * dpr;
  6234. return newDom;
  6235. }
  6236. /**
  6237. * @alias module:zrender/Layer
  6238. * @constructor
  6239. * @extends module:zrender/mixin/Transformable
  6240. * @param {string} id
  6241. * @param {module:zrender/Painter} painter
  6242. * @param {number} [dpr]
  6243. */
  6244. var Layer = function (id, painter, dpr) {
  6245. var dom;
  6246. dpr = dpr || devicePixelRatio;
  6247. if (typeof id === 'string') {
  6248. dom = createDom(id, painter, dpr);
  6249. }
  6250. // Not using isDom because in node it will return false
  6251. else if (isObject$1(id)) {
  6252. dom = id;
  6253. id = dom.id;
  6254. }
  6255. this.id = id;
  6256. this.dom = dom;
  6257. var domStyle = dom.style;
  6258. if (domStyle) { // Not in node
  6259. dom.onselectstart = returnFalse; // 避免页面选中的尴尬
  6260. domStyle['-webkit-user-select'] = 'none';
  6261. domStyle['user-select'] = 'none';
  6262. domStyle['-webkit-touch-callout'] = 'none';
  6263. domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
  6264. domStyle['padding'] = 0;
  6265. domStyle['margin'] = 0;
  6266. domStyle['border-width'] = 0;
  6267. }
  6268. this.domBack = null;
  6269. this.ctxBack = null;
  6270. this.painter = painter;
  6271. this.config = null;
  6272. // Configs
  6273. /**
  6274. * 每次清空画布的颜色
  6275. * @type {string}
  6276. * @default 0
  6277. */
  6278. this.clearColor = 0;
  6279. /**
  6280. * 是否开启动态模糊
  6281. * @type {boolean}
  6282. * @default false
  6283. */
  6284. this.motionBlur = false;
  6285. /**
  6286. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  6287. * @type {number}
  6288. * @default 0.7
  6289. */
  6290. this.lastFrameAlpha = 0.7;
  6291. /**
  6292. * Layer dpr
  6293. * @type {number}
  6294. */
  6295. this.dpr = dpr;
  6296. };
  6297. Layer.prototype = {
  6298. constructor: Layer,
  6299. __dirty: true,
  6300. __used: false,
  6301. __drawIndex: 0,
  6302. __startIndex: 0,
  6303. __endIndex: 0,
  6304. incremental: false,
  6305. getElementCount: function () {
  6306. return this.__endIndex - this.__startIndex;
  6307. },
  6308. initContext: function () {
  6309. this.ctx = this.dom.getContext('2d');
  6310. this.ctx.dpr = this.dpr;
  6311. },
  6312. createBackBuffer: function () {
  6313. var dpr = this.dpr;
  6314. this.domBack = createDom('back-' + this.id, this.painter, dpr);
  6315. this.ctxBack = this.domBack.getContext('2d');
  6316. if (dpr !== 1) {
  6317. this.ctxBack.scale(dpr, dpr);
  6318. }
  6319. },
  6320. /**
  6321. * @param {number} width
  6322. * @param {number} height
  6323. */
  6324. resize: function (width, height) {
  6325. var dpr = this.dpr;
  6326. var dom = this.dom;
  6327. var domStyle = dom.style;
  6328. var domBack = this.domBack;
  6329. if (domStyle) {
  6330. domStyle.width = width + 'px';
  6331. domStyle.height = height + 'px';
  6332. }
  6333. dom.width = width * dpr;
  6334. dom.height = height * dpr;
  6335. if (domBack) {
  6336. domBack.width = width * dpr;
  6337. domBack.height = height * dpr;
  6338. if (dpr !== 1) {
  6339. this.ctxBack.scale(dpr, dpr);
  6340. }
  6341. }
  6342. },
  6343. /**
  6344. * 清空该层画布
  6345. * @param {boolean} [clearAll]=false Clear all with out motion blur
  6346. * @param {Color} [clearColor]
  6347. */
  6348. clear: function (clearAll, clearColor) {
  6349. var dom = this.dom;
  6350. var ctx = this.ctx;
  6351. var width = dom.width;
  6352. var height = dom.height;
  6353. var clearColor = clearColor || this.clearColor;
  6354. var haveMotionBLur = this.motionBlur && !clearAll;
  6355. var lastFrameAlpha = this.lastFrameAlpha;
  6356. var dpr = this.dpr;
  6357. if (haveMotionBLur) {
  6358. if (!this.domBack) {
  6359. this.createBackBuffer();
  6360. }
  6361. this.ctxBack.globalCompositeOperation = 'copy';
  6362. this.ctxBack.drawImage(
  6363. dom, 0, 0,
  6364. width / dpr,
  6365. height / dpr
  6366. );
  6367. }
  6368. ctx.clearRect(0, 0, width, height);
  6369. if (clearColor && clearColor !== 'transparent') {
  6370. var clearColorGradientOrPattern;
  6371. // Gradient
  6372. if (clearColor.colorStops) {
  6373. // Cache canvas gradient
  6374. clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
  6375. x: 0,
  6376. y: 0,
  6377. width: width,
  6378. height: height
  6379. });
  6380. clearColor.__canvasGradient = clearColorGradientOrPattern;
  6381. }
  6382. // Pattern
  6383. else if (clearColor.image) {
  6384. clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
  6385. }
  6386. ctx.save();
  6387. ctx.fillStyle = clearColorGradientOrPattern || clearColor;
  6388. ctx.fillRect(0, 0, width, height);
  6389. ctx.restore();
  6390. }
  6391. if (haveMotionBLur) {
  6392. var domBack = this.domBack;
  6393. ctx.save();
  6394. ctx.globalAlpha = lastFrameAlpha;
  6395. ctx.drawImage(domBack, 0, 0, width, height);
  6396. ctx.restore();
  6397. }
  6398. }
  6399. };
  6400. var requestAnimationFrame = (
  6401. typeof window !== 'undefined'
  6402. && (
  6403. (window.requestAnimationFrame && window.requestAnimationFrame.bind(window))
  6404. // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809
  6405. || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))
  6406. || window.mozRequestAnimationFrame
  6407. || window.webkitRequestAnimationFrame
  6408. )
  6409. ) || function (func) {
  6410. setTimeout(func, 16);
  6411. };
  6412. var globalImageCache = new LRU(50);
  6413. /**
  6414. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  6415. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  6416. */
  6417. function findExistImage(newImageOrSrc) {
  6418. if (typeof newImageOrSrc === 'string') {
  6419. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  6420. return cachedImgObj && cachedImgObj.image;
  6421. }
  6422. else {
  6423. return newImageOrSrc;
  6424. }
  6425. }
  6426. /**
  6427. * Caution: User should cache loaded images, but not just count on LRU.
  6428. * Consider if required images more than LRU size, will dead loop occur?
  6429. *
  6430. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  6431. * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
  6432. * @param {module:zrender/Element} [hostEl] For calling `dirty`.
  6433. * @param {Function} [cb] params: (image, cbPayload)
  6434. * @param {Object} [cbPayload] Payload on cb calling.
  6435. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  6436. */
  6437. function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
  6438. if (!newImageOrSrc) {
  6439. return image;
  6440. }
  6441. else if (typeof newImageOrSrc === 'string') {
  6442. // Image should not be loaded repeatly.
  6443. if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {
  6444. return image;
  6445. }
  6446. // Only when there is no existent image or existent image src
  6447. // is different, this method is responsible for load.
  6448. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  6449. var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload};
  6450. if (cachedImgObj) {
  6451. image = cachedImgObj.image;
  6452. !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
  6453. }
  6454. else {
  6455. image = new Image();
  6456. image.onload = image.onerror = imageOnLoad;
  6457. globalImageCache.put(
  6458. newImageOrSrc,
  6459. image.__cachedImgObj = {
  6460. image: image,
  6461. pending: [pendingWrap]
  6462. }
  6463. );
  6464. image.src = image.__zrImageSrc = newImageOrSrc;
  6465. }
  6466. return image;
  6467. }
  6468. // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
  6469. else {
  6470. return newImageOrSrc;
  6471. }
  6472. }
  6473. function imageOnLoad() {
  6474. var cachedImgObj = this.__cachedImgObj;
  6475. this.onload = this.onerror = this.__cachedImgObj = null;
  6476. for (var i = 0; i < cachedImgObj.pending.length; i++) {
  6477. var pendingWrap = cachedImgObj.pending[i];
  6478. var cb = pendingWrap.cb;
  6479. cb && cb(this, pendingWrap.cbPayload);
  6480. pendingWrap.hostEl.dirty();
  6481. }
  6482. cachedImgObj.pending.length = 0;
  6483. }
  6484. function isImageReady(image) {
  6485. return image && image.width && image.height;
  6486. }
  6487. var textWidthCache = {};
  6488. var textWidthCacheCounter = 0;
  6489. var TEXT_CACHE_MAX = 5000;
  6490. var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
  6491. var DEFAULT_FONT$1 = '12px sans-serif';
  6492. // Avoid assign to an exported variable, for transforming to cjs.
  6493. var methods$1 = {};
  6494. function $override$1(name, fn) {
  6495. methods$1[name] = fn;
  6496. }
  6497. /**
  6498. * @public
  6499. * @param {string} text
  6500. * @param {string} font
  6501. * @return {number} width
  6502. */
  6503. function getWidth(text, font) {
  6504. font = font || DEFAULT_FONT$1;
  6505. var key = text + ':' + font;
  6506. if (textWidthCache[key]) {
  6507. return textWidthCache[key];
  6508. }
  6509. var textLines = (text + '').split('\n');
  6510. var width = 0;
  6511. for (var i = 0, l = textLines.length; i < l; i++) {
  6512. // textContain.measureText may be overrided in SVG or VML
  6513. width = Math.max(measureText(textLines[i], font).width, width);
  6514. }
  6515. if (textWidthCacheCounter > TEXT_CACHE_MAX) {
  6516. textWidthCacheCounter = 0;
  6517. textWidthCache = {};
  6518. }
  6519. textWidthCacheCounter++;
  6520. textWidthCache[key] = width;
  6521. return width;
  6522. }
  6523. /**
  6524. * @public
  6525. * @param {string} text
  6526. * @param {string} font
  6527. * @param {string} [textAlign='left']
  6528. * @param {string} [textVerticalAlign='top']
  6529. * @param {Array.<number>} [textPadding]
  6530. * @param {Object} [rich]
  6531. * @param {Object} [truncate]
  6532. * @return {Object} {x, y, width, height, lineHeight}
  6533. */
  6534. function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate) {
  6535. return rich
  6536. ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate)
  6537. : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, truncate);
  6538. }
  6539. function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, truncate) {
  6540. var contentBlock = parsePlainText(text, font, textPadding, textLineHeight, truncate);
  6541. var outerWidth = getWidth(text, font);
  6542. if (textPadding) {
  6543. outerWidth += textPadding[1] + textPadding[3];
  6544. }
  6545. var outerHeight = contentBlock.outerHeight;
  6546. var x = adjustTextX(0, outerWidth, textAlign);
  6547. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  6548. var rect = new BoundingRect(x, y, outerWidth, outerHeight);
  6549. rect.lineHeight = contentBlock.lineHeight;
  6550. return rect;
  6551. }
  6552. function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate) {
  6553. var contentBlock = parseRichText(text, {
  6554. rich: rich,
  6555. truncate: truncate,
  6556. font: font,
  6557. textAlign: textAlign,
  6558. textPadding: textPadding,
  6559. textLineHeight: textLineHeight
  6560. });
  6561. var outerWidth = contentBlock.outerWidth;
  6562. var outerHeight = contentBlock.outerHeight;
  6563. var x = adjustTextX(0, outerWidth, textAlign);
  6564. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  6565. return new BoundingRect(x, y, outerWidth, outerHeight);
  6566. }
  6567. /**
  6568. * @public
  6569. * @param {number} x
  6570. * @param {number} width
  6571. * @param {string} [textAlign='left']
  6572. * @return {number} Adjusted x.
  6573. */
  6574. function adjustTextX(x, width, textAlign) {
  6575. // FIXME Right to left language
  6576. if (textAlign === 'right') {
  6577. x -= width;
  6578. }
  6579. else if (textAlign === 'center') {
  6580. x -= width / 2;
  6581. }
  6582. return x;
  6583. }
  6584. /**
  6585. * @public
  6586. * @param {number} y
  6587. * @param {number} height
  6588. * @param {string} [textVerticalAlign='top']
  6589. * @return {number} Adjusted y.
  6590. */
  6591. function adjustTextY(y, height, textVerticalAlign) {
  6592. if (textVerticalAlign === 'middle') {
  6593. y -= height / 2;
  6594. }
  6595. else if (textVerticalAlign === 'bottom') {
  6596. y -= height;
  6597. }
  6598. return y;
  6599. }
  6600. /**
  6601. * @public
  6602. * @param {stirng} textPosition
  6603. * @param {Object} rect {x, y, width, height}
  6604. * @param {number} distance
  6605. * @return {Object} {x, y, textAlign, textVerticalAlign}
  6606. */
  6607. function adjustTextPositionOnRect(textPosition, rect, distance) {
  6608. var x = rect.x;
  6609. var y = rect.y;
  6610. var height = rect.height;
  6611. var width = rect.width;
  6612. var halfHeight = height / 2;
  6613. var textAlign = 'left';
  6614. var textVerticalAlign = 'top';
  6615. switch (textPosition) {
  6616. case 'left':
  6617. x -= distance;
  6618. y += halfHeight;
  6619. textAlign = 'right';
  6620. textVerticalAlign = 'middle';
  6621. break;
  6622. case 'right':
  6623. x += distance + width;
  6624. y += halfHeight;
  6625. textVerticalAlign = 'middle';
  6626. break;
  6627. case 'top':
  6628. x += width / 2;
  6629. y -= distance;
  6630. textAlign = 'center';
  6631. textVerticalAlign = 'bottom';
  6632. break;
  6633. case 'bottom':
  6634. x += width / 2;
  6635. y += height + distance;
  6636. textAlign = 'center';
  6637. break;
  6638. case 'inside':
  6639. x += width / 2;
  6640. y += halfHeight;
  6641. textAlign = 'center';
  6642. textVerticalAlign = 'middle';
  6643. break;
  6644. case 'insideLeft':
  6645. x += distance;
  6646. y += halfHeight;
  6647. textVerticalAlign = 'middle';
  6648. break;
  6649. case 'insideRight':
  6650. x += width - distance;
  6651. y += halfHeight;
  6652. textAlign = 'right';
  6653. textVerticalAlign = 'middle';
  6654. break;
  6655. case 'insideTop':
  6656. x += width / 2;
  6657. y += distance;
  6658. textAlign = 'center';
  6659. break;
  6660. case 'insideBottom':
  6661. x += width / 2;
  6662. y += height - distance;
  6663. textAlign = 'center';
  6664. textVerticalAlign = 'bottom';
  6665. break;
  6666. case 'insideTopLeft':
  6667. x += distance;
  6668. y += distance;
  6669. break;
  6670. case 'insideTopRight':
  6671. x += width - distance;
  6672. y += distance;
  6673. textAlign = 'right';
  6674. break;
  6675. case 'insideBottomLeft':
  6676. x += distance;
  6677. y += height - distance;
  6678. textVerticalAlign = 'bottom';
  6679. break;
  6680. case 'insideBottomRight':
  6681. x += width - distance;
  6682. y += height - distance;
  6683. textAlign = 'right';
  6684. textVerticalAlign = 'bottom';
  6685. break;
  6686. }
  6687. return {
  6688. x: x,
  6689. y: y,
  6690. textAlign: textAlign,
  6691. textVerticalAlign: textVerticalAlign
  6692. };
  6693. }
  6694. /**
  6695. * Show ellipsis if overflow.
  6696. *
  6697. * @public
  6698. * @param {string} text
  6699. * @param {string} containerWidth
  6700. * @param {string} font
  6701. * @param {number} [ellipsis='...']
  6702. * @param {Object} [options]
  6703. * @param {number} [options.maxIterations=3]
  6704. * @param {number} [options.minChar=0] If truncate result are less
  6705. * then minChar, ellipsis will not show, which is
  6706. * better for user hint in some cases.
  6707. * @param {number} [options.placeholder=''] When all truncated, use the placeholder.
  6708. * @return {string}
  6709. */
  6710. function truncateText(text, containerWidth, font, ellipsis, options) {
  6711. if (!containerWidth) {
  6712. return '';
  6713. }
  6714. var textLines = (text + '').split('\n');
  6715. options = prepareTruncateOptions(containerWidth, font, ellipsis, options);
  6716. // FIXME
  6717. // It is not appropriate that every line has '...' when truncate multiple lines.
  6718. for (var i = 0, len = textLines.length; i < len; i++) {
  6719. textLines[i] = truncateSingleLine(textLines[i], options);
  6720. }
  6721. return textLines.join('\n');
  6722. }
  6723. function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
  6724. options = extend({}, options);
  6725. options.font = font;
  6726. var ellipsis = retrieve2(ellipsis, '...');
  6727. options.maxIterations = retrieve2(options.maxIterations, 2);
  6728. var minChar = options.minChar = retrieve2(options.minChar, 0);
  6729. // FIXME
  6730. // Other languages?
  6731. options.cnCharWidth = getWidth('国', font);
  6732. // FIXME
  6733. // Consider proportional font?
  6734. var ascCharWidth = options.ascCharWidth = getWidth('a', font);
  6735. options.placeholder = retrieve2(options.placeholder, '');
  6736. // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
  6737. // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.
  6738. var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
  6739. for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
  6740. contentWidth -= ascCharWidth;
  6741. }
  6742. var ellipsisWidth = getWidth(ellipsis, font);
  6743. if (ellipsisWidth > contentWidth) {
  6744. ellipsis = '';
  6745. ellipsisWidth = 0;
  6746. }
  6747. contentWidth = containerWidth - ellipsisWidth;
  6748. options.ellipsis = ellipsis;
  6749. options.ellipsisWidth = ellipsisWidth;
  6750. options.contentWidth = contentWidth;
  6751. options.containerWidth = containerWidth;
  6752. return options;
  6753. }
  6754. function truncateSingleLine(textLine, options) {
  6755. var containerWidth = options.containerWidth;
  6756. var font = options.font;
  6757. var contentWidth = options.contentWidth;
  6758. if (!containerWidth) {
  6759. return '';
  6760. }
  6761. var lineWidth = getWidth(textLine, font);
  6762. if (lineWidth <= containerWidth) {
  6763. return textLine;
  6764. }
  6765. for (var j = 0; ; j++) {
  6766. if (lineWidth <= contentWidth || j >= options.maxIterations) {
  6767. textLine += options.ellipsis;
  6768. break;
  6769. }
  6770. var subLength = j === 0
  6771. ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)
  6772. : lineWidth > 0
  6773. ? Math.floor(textLine.length * contentWidth / lineWidth)
  6774. : 0;
  6775. textLine = textLine.substr(0, subLength);
  6776. lineWidth = getWidth(textLine, font);
  6777. }
  6778. if (textLine === '') {
  6779. textLine = options.placeholder;
  6780. }
  6781. return textLine;
  6782. }
  6783. function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
  6784. var width = 0;
  6785. var i = 0;
  6786. for (var len = text.length; i < len && width < contentWidth; i++) {
  6787. var charCode = text.charCodeAt(i);
  6788. width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;
  6789. }
  6790. return i;
  6791. }
  6792. /**
  6793. * @public
  6794. * @param {string} font
  6795. * @return {number} line height
  6796. */
  6797. function getLineHeight(font) {
  6798. // FIXME A rough approach.
  6799. return getWidth('国', font);
  6800. }
  6801. /**
  6802. * @public
  6803. * @param {string} text
  6804. * @param {string} font
  6805. * @return {Object} width
  6806. */
  6807. function measureText(text, font) {
  6808. return methods$1.measureText(text, font);
  6809. }
  6810. // Avoid assign to an exported variable, for transforming to cjs.
  6811. methods$1.measureText = function (text, font) {
  6812. var ctx = getContext();
  6813. ctx.font = font || DEFAULT_FONT$1;
  6814. return ctx.measureText(text);
  6815. };
  6816. /**
  6817. * @public
  6818. * @param {string} text
  6819. * @param {string} font
  6820. * @param {Object} [truncate]
  6821. * @return {Object} block: {lineHeight, lines, height, outerHeight}
  6822. * Notice: for performance, do not calculate outerWidth util needed.
  6823. */
  6824. function parsePlainText(text, font, padding, textLineHeight, truncate) {
  6825. text != null && (text += '');
  6826. var lineHeight = retrieve2(textLineHeight, getLineHeight(font));
  6827. var lines = text ? text.split('\n') : [];
  6828. var height = lines.length * lineHeight;
  6829. var outerHeight = height;
  6830. if (padding) {
  6831. outerHeight += padding[0] + padding[2];
  6832. }
  6833. if (text && truncate) {
  6834. var truncOuterHeight = truncate.outerHeight;
  6835. var truncOuterWidth = truncate.outerWidth;
  6836. if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
  6837. text = '';
  6838. lines = [];
  6839. }
  6840. else if (truncOuterWidth != null) {
  6841. var options = prepareTruncateOptions(
  6842. truncOuterWidth - (padding ? padding[1] + padding[3] : 0),
  6843. font,
  6844. truncate.ellipsis,
  6845. {minChar: truncate.minChar, placeholder: truncate.placeholder}
  6846. );
  6847. // FIXME
  6848. // It is not appropriate that every line has '...' when truncate multiple lines.
  6849. for (var i = 0, len = lines.length; i < len; i++) {
  6850. lines[i] = truncateSingleLine(lines[i], options);
  6851. }
  6852. }
  6853. }
  6854. return {
  6855. lines: lines,
  6856. height: height,
  6857. outerHeight: outerHeight,
  6858. lineHeight: lineHeight
  6859. };
  6860. }
  6861. /**
  6862. * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
  6863. * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
  6864. *
  6865. * @public
  6866. * @param {string} text
  6867. * @param {Object} style
  6868. * @return {Object} block
  6869. * {
  6870. * width,
  6871. * height,
  6872. * lines: [{
  6873. * lineHeight,
  6874. * width,
  6875. * tokens: [[{
  6876. * styleName,
  6877. * text,
  6878. * width, // include textPadding
  6879. * height, // include textPadding
  6880. * textWidth, // pure text width
  6881. * textHeight, // pure text height
  6882. * lineHeihgt,
  6883. * font,
  6884. * textAlign,
  6885. * textVerticalAlign
  6886. * }], [...], ...]
  6887. * }, ...]
  6888. * }
  6889. * If styleName is undefined, it is plain text.
  6890. */
  6891. function parseRichText(text, style) {
  6892. var contentBlock = {lines: [], width: 0, height: 0};
  6893. text != null && (text += '');
  6894. if (!text) {
  6895. return contentBlock;
  6896. }
  6897. var lastIndex = STYLE_REG.lastIndex = 0;
  6898. var result;
  6899. while ((result = STYLE_REG.exec(text)) != null) {
  6900. var matchedIndex = result.index;
  6901. if (matchedIndex > lastIndex) {
  6902. pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
  6903. }
  6904. pushTokens(contentBlock, result[2], result[1]);
  6905. lastIndex = STYLE_REG.lastIndex;
  6906. }
  6907. if (lastIndex < text.length) {
  6908. pushTokens(contentBlock, text.substring(lastIndex, text.length));
  6909. }
  6910. var lines = contentBlock.lines;
  6911. var contentHeight = 0;
  6912. var contentWidth = 0;
  6913. // For `textWidth: 100%`
  6914. var pendingList = [];
  6915. var stlPadding = style.textPadding;
  6916. var truncate = style.truncate;
  6917. var truncateWidth = truncate && truncate.outerWidth;
  6918. var truncateHeight = truncate && truncate.outerHeight;
  6919. if (stlPadding) {
  6920. truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
  6921. truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
  6922. }
  6923. // Calculate layout info of tokens.
  6924. for (var i = 0; i < lines.length; i++) {
  6925. var line = lines[i];
  6926. var lineHeight = 0;
  6927. var lineWidth = 0;
  6928. for (var j = 0; j < line.tokens.length; j++) {
  6929. var token = line.tokens[j];
  6930. var tokenStyle = token.styleName && style.rich[token.styleName] || {};
  6931. // textPadding should not inherit from style.
  6932. var textPadding = token.textPadding = tokenStyle.textPadding;
  6933. // textFont has been asigned to font by `normalizeStyle`.
  6934. var font = token.font = tokenStyle.font || style.font;
  6935. // textHeight can be used when textVerticalAlign is specified in token.
  6936. var tokenHeight = token.textHeight = retrieve2(
  6937. // textHeight should not be inherited, consider it can be specified
  6938. // as box height of the block.
  6939. tokenStyle.textHeight, getLineHeight(font)
  6940. );
  6941. textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
  6942. token.height = tokenHeight;
  6943. token.lineHeight = retrieve3(
  6944. tokenStyle.textLineHeight, style.textLineHeight, tokenHeight
  6945. );
  6946. token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
  6947. token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
  6948. if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
  6949. return {lines: [], width: 0, height: 0};
  6950. }
  6951. token.textWidth = getWidth(token.text, font);
  6952. var tokenWidth = tokenStyle.textWidth;
  6953. var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto';
  6954. // Percent width, can be `100%`, can be used in drawing separate
  6955. // line when box width is needed to be auto.
  6956. if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
  6957. token.percentWidth = tokenWidth;
  6958. pendingList.push(token);
  6959. tokenWidth = 0;
  6960. // Do not truncate in this case, because there is no user case
  6961. // and it is too complicated.
  6962. }
  6963. else {
  6964. if (tokenWidthNotSpecified) {
  6965. tokenWidth = token.textWidth;
  6966. // FIXME: If image is not loaded and textWidth is not specified, calling
  6967. // `getBoundingRect()` will not get correct result.
  6968. var textBackgroundColor = tokenStyle.textBackgroundColor;
  6969. var bgImg = textBackgroundColor && textBackgroundColor.image;
  6970. // Use cases:
  6971. // (1) If image is not loaded, it will be loaded at render phase and call
  6972. // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
  6973. // image, and then the right size will be calculated here at the next tick.
  6974. // See `graphic/helper/text.js`.
  6975. // (2) If image loaded, and `textBackgroundColor.image` is image src string,
  6976. // use `imageHelper.findExistImage` to find cached image.
  6977. // `imageHelper.findExistImage` will always be called here before
  6978. // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
  6979. // which ensures that image will not be rendered before correct size calcualted.
  6980. if (bgImg) {
  6981. bgImg = findExistImage(bgImg);
  6982. if (isImageReady(bgImg)) {
  6983. tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
  6984. }
  6985. }
  6986. }
  6987. var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
  6988. tokenWidth += paddingW;
  6989. var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
  6990. if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
  6991. if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
  6992. token.text = '';
  6993. token.textWidth = tokenWidth = 0;
  6994. }
  6995. else {
  6996. token.text = truncateText(
  6997. token.text, remianTruncWidth - paddingW, font, truncate.ellipsis,
  6998. {minChar: truncate.minChar}
  6999. );
  7000. token.textWidth = getWidth(token.text, font);
  7001. tokenWidth = token.textWidth + paddingW;
  7002. }
  7003. }
  7004. }
  7005. lineWidth += (token.width = tokenWidth);
  7006. tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
  7007. }
  7008. line.width = lineWidth;
  7009. line.lineHeight = lineHeight;
  7010. contentHeight += lineHeight;
  7011. contentWidth = Math.max(contentWidth, lineWidth);
  7012. }
  7013. contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
  7014. contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
  7015. if (stlPadding) {
  7016. contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
  7017. contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
  7018. }
  7019. for (var i = 0; i < pendingList.length; i++) {
  7020. var token = pendingList[i];
  7021. var percentWidth = token.percentWidth;
  7022. // Should not base on outerWidth, because token can not be placed out of padding.
  7023. token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
  7024. }
  7025. return contentBlock;
  7026. }
  7027. function pushTokens(block, str, styleName) {
  7028. var isEmptyStr = str === '';
  7029. var strs = str.split('\n');
  7030. var lines = block.lines;
  7031. for (var i = 0; i < strs.length; i++) {
  7032. var text = strs[i];
  7033. var token = {
  7034. styleName: styleName,
  7035. text: text,
  7036. isLineHolder: !text && !isEmptyStr
  7037. };
  7038. // The first token should be appended to the last line.
  7039. if (!i) {
  7040. var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens;
  7041. // Consider cases:
  7042. // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
  7043. // (which is a placeholder) should be replaced by new token.
  7044. // (2) A image backage, where token likes {a|}.
  7045. // (3) A redundant '' will affect textAlign in line.
  7046. // (4) tokens with the same tplName should not be merged, because
  7047. // they should be displayed in different box (with border and padding).
  7048. var tokensLen = tokens.length;
  7049. (tokensLen === 1 && tokens[0].isLineHolder)
  7050. ? (tokens[0] = token)
  7051. // Consider text is '', only insert when it is the "lineHolder" or
  7052. // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
  7053. : ((text || !tokensLen || isEmptyStr) && tokens.push(token));
  7054. }
  7055. // Other tokens always start a new line.
  7056. else {
  7057. // If there is '', insert it as a placeholder.
  7058. lines.push({tokens: [token]});
  7059. }
  7060. }
  7061. }
  7062. function makeFont(style) {
  7063. // FIXME in node-canvas fontWeight is before fontStyle
  7064. // Use `fontSize` `fontFamily` to check whether font properties are defined.
  7065. var font = (style.fontSize || style.fontFamily) && [
  7066. style.fontStyle,
  7067. style.fontWeight,
  7068. (style.fontSize || 12) + 'px',
  7069. // If font properties are defined, `fontFamily` should not be ignored.
  7070. style.fontFamily || 'sans-serif'
  7071. ].join(' ');
  7072. return font && trim(font) || style.textFont || style.font;
  7073. }
  7074. /**
  7075. * @param {Object} ctx
  7076. * @param {Object} shape
  7077. * @param {number} shape.x
  7078. * @param {number} shape.y
  7079. * @param {number} shape.width
  7080. * @param {number} shape.height
  7081. * @param {number} shape.r
  7082. */
  7083. function buildPath(ctx, shape) {
  7084. var x = shape.x;
  7085. var y = shape.y;
  7086. var width = shape.width;
  7087. var height = shape.height;
  7088. var r = shape.r;
  7089. var r1;
  7090. var r2;
  7091. var r3;
  7092. var r4;
  7093. // Convert width and height to positive for better borderRadius
  7094. if (width < 0) {
  7095. x = x + width;
  7096. width = -width;
  7097. }
  7098. if (height < 0) {
  7099. y = y + height;
  7100. height = -height;
  7101. }
  7102. if (typeof r === 'number') {
  7103. r1 = r2 = r3 = r4 = r;
  7104. }
  7105. else if (r instanceof Array) {
  7106. if (r.length === 1) {
  7107. r1 = r2 = r3 = r4 = r[0];
  7108. }
  7109. else if (r.length === 2) {
  7110. r1 = r3 = r[0];
  7111. r2 = r4 = r[1];
  7112. }
  7113. else if (r.length === 3) {
  7114. r1 = r[0];
  7115. r2 = r4 = r[1];
  7116. r3 = r[2];
  7117. }
  7118. else {
  7119. r1 = r[0];
  7120. r2 = r[1];
  7121. r3 = r[2];
  7122. r4 = r[3];
  7123. }
  7124. }
  7125. else {
  7126. r1 = r2 = r3 = r4 = 0;
  7127. }
  7128. var total;
  7129. if (r1 + r2 > width) {
  7130. total = r1 + r2;
  7131. r1 *= width / total;
  7132. r2 *= width / total;
  7133. }
  7134. if (r3 + r4 > width) {
  7135. total = r3 + r4;
  7136. r3 *= width / total;
  7137. r4 *= width / total;
  7138. }
  7139. if (r2 + r3 > height) {
  7140. total = r2 + r3;
  7141. r2 *= height / total;
  7142. r3 *= height / total;
  7143. }
  7144. if (r1 + r4 > height) {
  7145. total = r1 + r4;
  7146. r1 *= height / total;
  7147. r4 *= height / total;
  7148. }
  7149. ctx.moveTo(x + r1, y);
  7150. ctx.lineTo(x + width - r2, y);
  7151. r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);
  7152. ctx.lineTo(x + width, y + height - r3);
  7153. r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);
  7154. ctx.lineTo(x + r4, y + height);
  7155. r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);
  7156. ctx.lineTo(x, y + r1);
  7157. r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);
  7158. }
  7159. var DEFAULT_FONT = DEFAULT_FONT$1;
  7160. // TODO: Have not support 'start', 'end' yet.
  7161. var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1};
  7162. var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1};
  7163. // Different from `STYLE_COMMON_PROPS` of `graphic/Style`,
  7164. // the default value of shadowColor is `'transparent'`.
  7165. var SHADOW_STYLE_COMMON_PROPS = [
  7166. ['textShadowBlur', 'shadowBlur', 0],
  7167. ['textShadowOffsetX', 'shadowOffsetX', 0],
  7168. ['textShadowOffsetY', 'shadowOffsetY', 0],
  7169. ['textShadowColor', 'shadowColor', 'transparent']
  7170. ];
  7171. /**
  7172. * @param {module:zrender/graphic/Style} style
  7173. * @return {module:zrender/graphic/Style} The input style.
  7174. */
  7175. function normalizeTextStyle(style) {
  7176. normalizeStyle(style);
  7177. each$1(style.rich, normalizeStyle);
  7178. return style;
  7179. }
  7180. function normalizeStyle(style) {
  7181. if (style) {
  7182. style.font = makeFont(style);
  7183. var textAlign = style.textAlign;
  7184. textAlign === 'middle' && (textAlign = 'center');
  7185. style.textAlign = (
  7186. textAlign == null || VALID_TEXT_ALIGN[textAlign]
  7187. ) ? textAlign : 'left';
  7188. // Compatible with textBaseline.
  7189. var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
  7190. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  7191. style.textVerticalAlign = (
  7192. textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]
  7193. ) ? textVerticalAlign : 'top';
  7194. var textPadding = style.textPadding;
  7195. if (textPadding) {
  7196. style.textPadding = normalizeCssArray(style.textPadding);
  7197. }
  7198. }
  7199. }
  7200. /**
  7201. * @param {CanvasRenderingContext2D} ctx
  7202. * @param {string} text
  7203. * @param {module:zrender/graphic/Style} style
  7204. * @param {Object|boolean} [rect] {x, y, width, height}
  7205. * If set false, rect text is not used.
  7206. * @param {Element|module:zrender/graphic/helper/constant.WILL_BE_RESTORED} [prevEl] For ctx prop cache.
  7207. */
  7208. function renderText(hostEl, ctx, text, style, rect, prevEl) {
  7209. style.rich
  7210. ? renderRichText(hostEl, ctx, text, style, rect, prevEl)
  7211. : renderPlainText(hostEl, ctx, text, style, rect, prevEl);
  7212. }
  7213. // Avoid setting to ctx according to prevEl if possible for
  7214. // performance in scenarios of large amount text.
  7215. function renderPlainText(hostEl, ctx, text, style, rect, prevEl) {
  7216. 'use strict';
  7217. var needDrawBg = needDrawBackground(style);
  7218. var prevStyle;
  7219. var checkCache = false;
  7220. var cachedByMe = ctx.__attrCachedBy === ContextCachedBy.PLAIN_TEXT;
  7221. // Only take and check cache for `Text` el, but not RectText.
  7222. if (prevEl !== WILL_BE_RESTORED) {
  7223. if (prevEl) {
  7224. prevStyle = prevEl.style;
  7225. checkCache = !needDrawBg && cachedByMe && prevStyle;
  7226. }
  7227. // Prevent from using cache in `Style::bind`, because of the case:
  7228. // ctx property is modified by other properties than `Style::bind`
  7229. // used, and Style::bind is called next.
  7230. ctx.__attrCachedBy = needDrawBg ? ContextCachedBy.NONE : ContextCachedBy.PLAIN_TEXT;
  7231. }
  7232. // Since this will be restored, prevent from using these props to check cache in the next
  7233. // entering of this method. But do not need to clear other cache like `Style::bind`.
  7234. else if (cachedByMe) {
  7235. ctx.__attrCachedBy = ContextCachedBy.NONE;
  7236. }
  7237. var styleFont = style.font || DEFAULT_FONT;
  7238. // PENDING
  7239. // Only `Text` el set `font` and keep it (`RectText` will restore). So theoretically
  7240. // we can make font cache on ctx, which can cache for text el that are discontinuous.
  7241. // But layer save/restore needed to be considered.
  7242. // if (styleFont !== ctx.__fontCache) {
  7243. // ctx.font = styleFont;
  7244. // if (prevEl !== WILL_BE_RESTORED) {
  7245. // ctx.__fontCache = styleFont;
  7246. // }
  7247. // }
  7248. if (!checkCache || styleFont !== (prevStyle.font || DEFAULT_FONT)) {
  7249. ctx.font = styleFont;
  7250. }
  7251. // Use the final font from context-2d, because the final
  7252. // font might not be the style.font when it is illegal.
  7253. // But get `ctx.font` might be time consuming.
  7254. var computedFont = hostEl.__computedFont;
  7255. if (hostEl.__styleFont !== styleFont) {
  7256. hostEl.__styleFont = styleFont;
  7257. computedFont = hostEl.__computedFont = ctx.font;
  7258. }
  7259. var textPadding = style.textPadding;
  7260. var textLineHeight = style.textLineHeight;
  7261. var contentBlock = hostEl.__textCotentBlock;
  7262. if (!contentBlock || hostEl.__dirtyText) {
  7263. contentBlock = hostEl.__textCotentBlock = parsePlainText(
  7264. text, computedFont, textPadding, textLineHeight, style.truncate
  7265. );
  7266. }
  7267. var outerHeight = contentBlock.outerHeight;
  7268. var textLines = contentBlock.lines;
  7269. var lineHeight = contentBlock.lineHeight;
  7270. var boxPos = getBoxPosition(outerHeight, style, rect);
  7271. var baseX = boxPos.baseX;
  7272. var baseY = boxPos.baseY;
  7273. var textAlign = boxPos.textAlign || 'left';
  7274. var textVerticalAlign = boxPos.textVerticalAlign;
  7275. // Origin of textRotation should be the base point of text drawing.
  7276. applyTextRotation(ctx, style, rect, baseX, baseY);
  7277. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  7278. var textX = baseX;
  7279. var textY = boxY;
  7280. if (needDrawBg || textPadding) {
  7281. // Consider performance, do not call getTextWidth util necessary.
  7282. var textWidth = getWidth(text, computedFont);
  7283. var outerWidth = textWidth;
  7284. textPadding && (outerWidth += textPadding[1] + textPadding[3]);
  7285. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  7286. needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  7287. if (textPadding) {
  7288. textX = getTextXForPadding(baseX, textAlign, textPadding);
  7289. textY += textPadding[0];
  7290. }
  7291. }
  7292. // Always set textAlign and textBase line, because it is difficute to calculate
  7293. // textAlign from prevEl, and we dont sure whether textAlign will be reset if
  7294. // font set happened.
  7295. ctx.textAlign = textAlign;
  7296. // Force baseline to be "middle". Otherwise, if using "top", the
  7297. // text will offset downward a little bit in font "Microsoft YaHei".
  7298. ctx.textBaseline = 'middle';
  7299. // Set text opacity
  7300. ctx.globalAlpha = style.opacity || 1;
  7301. // Always set shadowBlur and shadowOffset to avoid leak from displayable.
  7302. for (var i = 0; i < SHADOW_STYLE_COMMON_PROPS.length; i++) {
  7303. var propItem = SHADOW_STYLE_COMMON_PROPS[i];
  7304. var styleProp = propItem[0];
  7305. var ctxProp = propItem[1];
  7306. var val = style[styleProp];
  7307. if (!checkCache || val !== prevStyle[styleProp]) {
  7308. ctx[ctxProp] = fixShadow(ctx, ctxProp, val || propItem[2]);
  7309. }
  7310. }
  7311. // `textBaseline` is set as 'middle'.
  7312. textY += lineHeight / 2;
  7313. var textStrokeWidth = style.textStrokeWidth;
  7314. var textStrokeWidthPrev = checkCache ? prevStyle.textStrokeWidth : null;
  7315. var strokeWidthChanged = !checkCache || textStrokeWidth !== textStrokeWidthPrev;
  7316. var strokeChanged = !checkCache || strokeWidthChanged || style.textStroke !== prevStyle.textStroke;
  7317. var textStroke = getStroke(style.textStroke, textStrokeWidth);
  7318. var textFill = getFill(style.textFill);
  7319. if (textStroke) {
  7320. if (strokeWidthChanged) {
  7321. ctx.lineWidth = textStrokeWidth;
  7322. }
  7323. if (strokeChanged) {
  7324. ctx.strokeStyle = textStroke;
  7325. }
  7326. }
  7327. if (textFill) {
  7328. if (!checkCache || style.textFill !== prevStyle.textFill) {
  7329. ctx.fillStyle = textFill;
  7330. }
  7331. }
  7332. // Optimize simply, in most cases only one line exists.
  7333. if (textLines.length === 1) {
  7334. // Fill after stroke so the outline will not cover the main part.
  7335. textStroke && ctx.strokeText(textLines[0], textX, textY);
  7336. textFill && ctx.fillText(textLines[0], textX, textY);
  7337. }
  7338. else {
  7339. for (var i = 0; i < textLines.length; i++) {
  7340. // Fill after stroke so the outline will not cover the main part.
  7341. textStroke && ctx.strokeText(textLines[i], textX, textY);
  7342. textFill && ctx.fillText(textLines[i], textX, textY);
  7343. textY += lineHeight;
  7344. }
  7345. }
  7346. }
  7347. function renderRichText(hostEl, ctx, text, style, rect, prevEl) {
  7348. // Do not do cache for rich text because of the complexity.
  7349. // But `RectText` this will be restored, do not need to clear other cache like `Style::bind`.
  7350. if (prevEl !== WILL_BE_RESTORED) {
  7351. ctx.__attrCachedBy = ContextCachedBy.NONE;
  7352. }
  7353. var contentBlock = hostEl.__textCotentBlock;
  7354. if (!contentBlock || hostEl.__dirtyText) {
  7355. contentBlock = hostEl.__textCotentBlock = parseRichText(text, style);
  7356. }
  7357. drawRichText(hostEl, ctx, contentBlock, style, rect);
  7358. }
  7359. function drawRichText(hostEl, ctx, contentBlock, style, rect) {
  7360. var contentWidth = contentBlock.width;
  7361. var outerWidth = contentBlock.outerWidth;
  7362. var outerHeight = contentBlock.outerHeight;
  7363. var textPadding = style.textPadding;
  7364. var boxPos = getBoxPosition(outerHeight, style, rect);
  7365. var baseX = boxPos.baseX;
  7366. var baseY = boxPos.baseY;
  7367. var textAlign = boxPos.textAlign;
  7368. var textVerticalAlign = boxPos.textVerticalAlign;
  7369. // Origin of textRotation should be the base point of text drawing.
  7370. applyTextRotation(ctx, style, rect, baseX, baseY);
  7371. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  7372. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  7373. var xLeft = boxX;
  7374. var lineTop = boxY;
  7375. if (textPadding) {
  7376. xLeft += textPadding[3];
  7377. lineTop += textPadding[0];
  7378. }
  7379. var xRight = xLeft + contentWidth;
  7380. needDrawBackground(style) && drawBackground(
  7381. hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight
  7382. );
  7383. for (var i = 0; i < contentBlock.lines.length; i++) {
  7384. var line = contentBlock.lines[i];
  7385. var tokens = line.tokens;
  7386. var tokenCount = tokens.length;
  7387. var lineHeight = line.lineHeight;
  7388. var usedWidth = line.width;
  7389. var leftIndex = 0;
  7390. var lineXLeft = xLeft;
  7391. var lineXRight = xRight;
  7392. var rightIndex = tokenCount - 1;
  7393. var token;
  7394. while (
  7395. leftIndex < tokenCount
  7396. && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')
  7397. ) {
  7398. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
  7399. usedWidth -= token.width;
  7400. lineXLeft += token.width;
  7401. leftIndex++;
  7402. }
  7403. while (
  7404. rightIndex >= 0
  7405. && (token = tokens[rightIndex], token.textAlign === 'right')
  7406. ) {
  7407. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
  7408. usedWidth -= token.width;
  7409. lineXRight -= token.width;
  7410. rightIndex--;
  7411. }
  7412. // The other tokens are placed as textAlign 'center' if there is enough space.
  7413. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  7414. while (leftIndex <= rightIndex) {
  7415. token = tokens[leftIndex];
  7416. // Consider width specified by user, use 'center' rather than 'left'.
  7417. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  7418. lineXLeft += token.width;
  7419. leftIndex++;
  7420. }
  7421. lineTop += lineHeight;
  7422. }
  7423. }
  7424. function applyTextRotation(ctx, style, rect, x, y) {
  7425. // textRotation only apply in RectText.
  7426. if (rect && style.textRotation) {
  7427. var origin = style.textOrigin;
  7428. if (origin === 'center') {
  7429. x = rect.width / 2 + rect.x;
  7430. y = rect.height / 2 + rect.y;
  7431. }
  7432. else if (origin) {
  7433. x = origin[0] + rect.x;
  7434. y = origin[1] + rect.y;
  7435. }
  7436. ctx.translate(x, y);
  7437. // Positive: anticlockwise
  7438. ctx.rotate(-style.textRotation);
  7439. ctx.translate(-x, -y);
  7440. }
  7441. }
  7442. function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
  7443. var tokenStyle = style.rich[token.styleName] || {};
  7444. tokenStyle.text = token.text;
  7445. // 'ctx.textBaseline' is always set as 'middle', for sake of
  7446. // the bias of "Microsoft YaHei".
  7447. var textVerticalAlign = token.textVerticalAlign;
  7448. var y = lineTop + lineHeight / 2;
  7449. if (textVerticalAlign === 'top') {
  7450. y = lineTop + token.height / 2;
  7451. }
  7452. else if (textVerticalAlign === 'bottom') {
  7453. y = lineTop + lineHeight - token.height / 2;
  7454. }
  7455. !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(
  7456. hostEl,
  7457. ctx,
  7458. tokenStyle,
  7459. textAlign === 'right'
  7460. ? x - token.width
  7461. : textAlign === 'center'
  7462. ? x - token.width / 2
  7463. : x,
  7464. y - token.height / 2,
  7465. token.width,
  7466. token.height
  7467. );
  7468. var textPadding = token.textPadding;
  7469. if (textPadding) {
  7470. x = getTextXForPadding(x, textAlign, textPadding);
  7471. y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
  7472. }
  7473. setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
  7474. setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
  7475. setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
  7476. setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
  7477. setCtx(ctx, 'textAlign', textAlign);
  7478. // Force baseline to be "middle". Otherwise, if using "top", the
  7479. // text will offset downward a little bit in font "Microsoft YaHei".
  7480. setCtx(ctx, 'textBaseline', 'middle');
  7481. setCtx(ctx, 'font', token.font || DEFAULT_FONT);
  7482. var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
  7483. var textFill = getFill(tokenStyle.textFill || style.textFill);
  7484. var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);
  7485. // Fill after stroke so the outline will not cover the main part.
  7486. if (textStroke) {
  7487. setCtx(ctx, 'lineWidth', textStrokeWidth);
  7488. setCtx(ctx, 'strokeStyle', textStroke);
  7489. ctx.strokeText(token.text, x, y);
  7490. }
  7491. if (textFill) {
  7492. setCtx(ctx, 'fillStyle', textFill);
  7493. ctx.fillText(token.text, x, y);
  7494. }
  7495. }
  7496. function needDrawBackground(style) {
  7497. return !!(
  7498. style.textBackgroundColor
  7499. || (style.textBorderWidth && style.textBorderColor)
  7500. );
  7501. }
  7502. // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius, text}
  7503. // shape: {x, y, width, height}
  7504. function drawBackground(hostEl, ctx, style, x, y, width, height) {
  7505. var textBackgroundColor = style.textBackgroundColor;
  7506. var textBorderWidth = style.textBorderWidth;
  7507. var textBorderColor = style.textBorderColor;
  7508. var isPlainBg = isString(textBackgroundColor);
  7509. setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
  7510. setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
  7511. setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
  7512. setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
  7513. if (isPlainBg || (textBorderWidth && textBorderColor)) {
  7514. ctx.beginPath();
  7515. var textBorderRadius = style.textBorderRadius;
  7516. if (!textBorderRadius) {
  7517. ctx.rect(x, y, width, height);
  7518. }
  7519. else {
  7520. buildPath(ctx, {
  7521. x: x, y: y, width: width, height: height, r: textBorderRadius
  7522. });
  7523. }
  7524. ctx.closePath();
  7525. }
  7526. if (isPlainBg) {
  7527. setCtx(ctx, 'fillStyle', textBackgroundColor);
  7528. if (style.fillOpacity != null) {
  7529. var originalGlobalAlpha = ctx.globalAlpha;
  7530. ctx.globalAlpha = style.fillOpacity * style.opacity;
  7531. ctx.fill();
  7532. ctx.globalAlpha = originalGlobalAlpha;
  7533. }
  7534. else {
  7535. ctx.fill();
  7536. }
  7537. }
  7538. else if (isObject$1(textBackgroundColor)) {
  7539. var image = textBackgroundColor.image;
  7540. image = createOrUpdateImage(
  7541. image, null, hostEl, onBgImageLoaded, textBackgroundColor
  7542. );
  7543. if (image && isImageReady(image)) {
  7544. ctx.drawImage(image, x, y, width, height);
  7545. }
  7546. }
  7547. if (textBorderWidth && textBorderColor) {
  7548. setCtx(ctx, 'lineWidth', textBorderWidth);
  7549. setCtx(ctx, 'strokeStyle', textBorderColor);
  7550. if (style.strokeOpacity != null) {
  7551. var originalGlobalAlpha = ctx.globalAlpha;
  7552. ctx.globalAlpha = style.strokeOpacity * style.opacity;
  7553. ctx.stroke();
  7554. ctx.globalAlpha = originalGlobalAlpha;
  7555. }
  7556. else {
  7557. ctx.stroke();
  7558. }
  7559. }
  7560. }
  7561. function onBgImageLoaded(image, textBackgroundColor) {
  7562. // Replace image, so that `contain/text.js#parseRichText`
  7563. // will get correct result in next tick.
  7564. textBackgroundColor.image = image;
  7565. }
  7566. function getBoxPosition(blockHeiht, style, rect) {
  7567. var baseX = style.x || 0;
  7568. var baseY = style.y || 0;
  7569. var textAlign = style.textAlign;
  7570. var textVerticalAlign = style.textVerticalAlign;
  7571. // Text position represented by coord
  7572. if (rect) {
  7573. var textPosition = style.textPosition;
  7574. if (textPosition instanceof Array) {
  7575. // Percent
  7576. baseX = rect.x + parsePercent(textPosition[0], rect.width);
  7577. baseY = rect.y + parsePercent(textPosition[1], rect.height);
  7578. }
  7579. else {
  7580. var res = adjustTextPositionOnRect(
  7581. textPosition, rect, style.textDistance
  7582. );
  7583. baseX = res.x;
  7584. baseY = res.y;
  7585. // Default align and baseline when has textPosition
  7586. textAlign = textAlign || res.textAlign;
  7587. textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
  7588. }
  7589. // textOffset is only support in RectText, otherwise
  7590. // we have to adjust boundingRect for textOffset.
  7591. var textOffset = style.textOffset;
  7592. if (textOffset) {
  7593. baseX += textOffset[0];
  7594. baseY += textOffset[1];
  7595. }
  7596. }
  7597. return {
  7598. baseX: baseX,
  7599. baseY: baseY,
  7600. textAlign: textAlign,
  7601. textVerticalAlign: textVerticalAlign
  7602. };
  7603. }
  7604. function setCtx(ctx, prop, value) {
  7605. ctx[prop] = fixShadow(ctx, prop, value);
  7606. return ctx[prop];
  7607. }
  7608. /**
  7609. * @param {string} [stroke] If specified, do not check style.textStroke.
  7610. * @param {string} [lineWidth] If specified, do not check style.textStroke.
  7611. * @param {number} style
  7612. */
  7613. function getStroke(stroke, lineWidth) {
  7614. return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
  7615. ? null
  7616. // TODO pattern and gradient?
  7617. : (stroke.image || stroke.colorStops)
  7618. ? '#000'
  7619. : stroke;
  7620. }
  7621. function getFill(fill) {
  7622. return (fill == null || fill === 'none')
  7623. ? null
  7624. // TODO pattern and gradient?
  7625. : (fill.image || fill.colorStops)
  7626. ? '#000'
  7627. : fill;
  7628. }
  7629. function parsePercent(value, maxValue) {
  7630. if (typeof value === 'string') {
  7631. if (value.lastIndexOf('%') >= 0) {
  7632. return parseFloat(value) / 100 * maxValue;
  7633. }
  7634. return parseFloat(value);
  7635. }
  7636. return value;
  7637. }
  7638. function getTextXForPadding(x, textAlign, textPadding) {
  7639. return textAlign === 'right'
  7640. ? (x - textPadding[1])
  7641. : textAlign === 'center'
  7642. ? (x + textPadding[3] / 2 - textPadding[1] / 2)
  7643. : (x + textPadding[3]);
  7644. }
  7645. /**
  7646. * @param {string} text
  7647. * @param {module:zrender/Style} style
  7648. * @return {boolean}
  7649. */
  7650. function needDrawText(text, style) {
  7651. return text != null
  7652. && (text
  7653. || style.textBackgroundColor
  7654. || (style.textBorderWidth && style.textBorderColor)
  7655. || style.textPadding
  7656. );
  7657. }
  7658. /**
  7659. * Mixin for drawing text in a element bounding rect
  7660. * @module zrender/mixin/RectText
  7661. */
  7662. var tmpRect$1 = new BoundingRect();
  7663. var RectText = function () {};
  7664. RectText.prototype = {
  7665. constructor: RectText,
  7666. /**
  7667. * Draw text in a rect with specified position.
  7668. * @param {CanvasRenderingContext2D} ctx
  7669. * @param {Object} rect Displayable rect
  7670. */
  7671. drawRectText: function (ctx, rect) {
  7672. var style = this.style;
  7673. rect = style.textRect || rect;
  7674. // Optimize, avoid normalize every time.
  7675. this.__dirty && normalizeTextStyle(style, true);
  7676. var text = style.text;
  7677. // Convert to string
  7678. text != null && (text += '');
  7679. if (!needDrawText(text, style)) {
  7680. return;
  7681. }
  7682. // FIXME
  7683. // Do not provide prevEl to `textHelper.renderText` for ctx prop cache,
  7684. // but use `ctx.save()` and `ctx.restore()`. Because the cache for rect
  7685. // text propably break the cache for its host elements.
  7686. ctx.save();
  7687. // Transform rect to view space
  7688. var transform = this.transform;
  7689. if (!style.transformText) {
  7690. if (transform) {
  7691. tmpRect$1.copy(rect);
  7692. tmpRect$1.applyTransform(transform);
  7693. rect = tmpRect$1;
  7694. }
  7695. }
  7696. else {
  7697. this.setTransform(ctx);
  7698. }
  7699. // transformText and textRotation can not be used at the same time.
  7700. renderText(this, ctx, text, style, rect, WILL_BE_RESTORED);
  7701. ctx.restore();
  7702. }
  7703. };
  7704. /**
  7705. * 可绘制的图形基类
  7706. * Base class of all displayable graphic objects
  7707. * @module zrender/graphic/Displayable
  7708. */
  7709. /**
  7710. * @alias module:zrender/graphic/Displayable
  7711. * @extends module:zrender/Element
  7712. * @extends module:zrender/graphic/mixin/RectText
  7713. */
  7714. function Displayable(opts) {
  7715. opts = opts || {};
  7716. Element.call(this, opts);
  7717. // Extend properties
  7718. for (var name in opts) {
  7719. if (
  7720. opts.hasOwnProperty(name)
  7721. && name !== 'style'
  7722. ) {
  7723. this[name] = opts[name];
  7724. }
  7725. }
  7726. /**
  7727. * @type {module:zrender/graphic/Style}
  7728. */
  7729. this.style = new Style(opts.style, this);
  7730. this._rect = null;
  7731. // Shapes for cascade clipping.
  7732. this.__clipPaths = [];
  7733. // FIXME Stateful must be mixined after style is setted
  7734. // Stateful.call(this, opts);
  7735. }
  7736. Displayable.prototype = {
  7737. constructor: Displayable,
  7738. type: 'displayable',
  7739. /**
  7740. * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制
  7741. * Dirty flag. From which painter will determine if this displayable object needs brush
  7742. * @name module:zrender/graphic/Displayable#__dirty
  7743. * @type {boolean}
  7744. */
  7745. __dirty: true,
  7746. /**
  7747. * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件
  7748. * If ignore drawing of the displayable object. Mouse event will still be triggered
  7749. * @name module:/zrender/graphic/Displayable#invisible
  7750. * @type {boolean}
  7751. * @default false
  7752. */
  7753. invisible: false,
  7754. /**
  7755. * @name module:/zrender/graphic/Displayable#z
  7756. * @type {number}
  7757. * @default 0
  7758. */
  7759. z: 0,
  7760. /**
  7761. * @name module:/zrender/graphic/Displayable#z
  7762. * @type {number}
  7763. * @default 0
  7764. */
  7765. z2: 0,
  7766. /**
  7767. * z层level,决定绘画在哪层canvas中
  7768. * @name module:/zrender/graphic/Displayable#zlevel
  7769. * @type {number}
  7770. * @default 0
  7771. */
  7772. zlevel: 0,
  7773. /**
  7774. * 是否可拖拽
  7775. * @name module:/zrender/graphic/Displayable#draggable
  7776. * @type {boolean}
  7777. * @default false
  7778. */
  7779. draggable: false,
  7780. /**
  7781. * 是否正在拖拽
  7782. * @name module:/zrender/graphic/Displayable#draggable
  7783. * @type {boolean}
  7784. * @default false
  7785. */
  7786. dragging: false,
  7787. /**
  7788. * 是否相应鼠标事件
  7789. * @name module:/zrender/graphic/Displayable#silent
  7790. * @type {boolean}
  7791. * @default false
  7792. */
  7793. silent: false,
  7794. /**
  7795. * If enable culling
  7796. * @type {boolean}
  7797. * @default false
  7798. */
  7799. culling: false,
  7800. /**
  7801. * Mouse cursor when hovered
  7802. * @name module:/zrender/graphic/Displayable#cursor
  7803. * @type {string}
  7804. */
  7805. cursor: 'pointer',
  7806. /**
  7807. * If hover area is bounding rect
  7808. * @name module:/zrender/graphic/Displayable#rectHover
  7809. * @type {string}
  7810. */
  7811. rectHover: false,
  7812. /**
  7813. * Render the element progressively when the value >= 0,
  7814. * usefull for large data.
  7815. * @type {boolean}
  7816. */
  7817. progressive: false,
  7818. /**
  7819. * @type {boolean}
  7820. */
  7821. incremental: false,
  7822. /**
  7823. * Scale ratio for global scale.
  7824. * @type {boolean}
  7825. */
  7826. globalScaleRatio: 1,
  7827. beforeBrush: function (ctx) {},
  7828. afterBrush: function (ctx) {},
  7829. /**
  7830. * 图形绘制方法
  7831. * @param {CanvasRenderingContext2D} ctx
  7832. */
  7833. // Interface
  7834. brush: function (ctx, prevEl) {},
  7835. /**
  7836. * 获取最小包围盒
  7837. * @return {module:zrender/core/BoundingRect}
  7838. */
  7839. // Interface
  7840. getBoundingRect: function () {},
  7841. /**
  7842. * 判断坐标 x, y 是否在图形上
  7843. * If displayable element contain coord x, y
  7844. * @param {number} x
  7845. * @param {number} y
  7846. * @return {boolean}
  7847. */
  7848. contain: function (x, y) {
  7849. return this.rectContain(x, y);
  7850. },
  7851. /**
  7852. * @param {Function} cb
  7853. * @param {} context
  7854. */
  7855. traverse: function (cb, context) {
  7856. cb.call(context, this);
  7857. },
  7858. /**
  7859. * 判断坐标 x, y 是否在图形的包围盒上
  7860. * If bounding rect of element contain coord x, y
  7861. * @param {number} x
  7862. * @param {number} y
  7863. * @return {boolean}
  7864. */
  7865. rectContain: function (x, y) {
  7866. var coord = this.transformCoordToLocal(x, y);
  7867. var rect = this.getBoundingRect();
  7868. return rect.contain(coord[0], coord[1]);
  7869. },
  7870. /**
  7871. * 标记图形元素为脏,并且在下一帧重绘
  7872. * Mark displayable element dirty and refresh next frame
  7873. */
  7874. dirty: function () {
  7875. this.__dirty = this.__dirtyText = true;
  7876. this._rect = null;
  7877. this.__zr && this.__zr.refresh();
  7878. },
  7879. /**
  7880. * 图形是否会触发事件
  7881. * If displayable object binded any event
  7882. * @return {boolean}
  7883. */
  7884. // TODO, 通过 bind 绑定的事件
  7885. // isSilent: function () {
  7886. // return !(
  7887. // this.hoverable || this.draggable
  7888. // || this.onmousemove || this.onmouseover || this.onmouseout
  7889. // || this.onmousedown || this.onmouseup || this.onclick
  7890. // || this.ondragenter || this.ondragover || this.ondragleave
  7891. // || this.ondrop
  7892. // );
  7893. // },
  7894. /**
  7895. * Alias for animate('style')
  7896. * @param {boolean} loop
  7897. */
  7898. animateStyle: function (loop) {
  7899. return this.animate('style', loop);
  7900. },
  7901. attrKV: function (key, value) {
  7902. if (key !== 'style') {
  7903. Element.prototype.attrKV.call(this, key, value);
  7904. }
  7905. else {
  7906. this.style.set(value);
  7907. }
  7908. },
  7909. /**
  7910. * @param {Object|string} key
  7911. * @param {*} value
  7912. */
  7913. setStyle: function (key, value) {
  7914. this.style.set(key, value);
  7915. this.dirty(false);
  7916. return this;
  7917. },
  7918. /**
  7919. * Use given style object
  7920. * @param {Object} obj
  7921. */
  7922. useStyle: function (obj) {
  7923. this.style = new Style(obj, this);
  7924. this.dirty(false);
  7925. return this;
  7926. }
  7927. };
  7928. inherits(Displayable, Element);
  7929. mixin(Displayable, RectText);
  7930. /**
  7931. * @alias zrender/graphic/Image
  7932. * @extends module:zrender/graphic/Displayable
  7933. * @constructor
  7934. * @param {Object} opts
  7935. */
  7936. function ZImage(opts) {
  7937. Displayable.call(this, opts);
  7938. }
  7939. ZImage.prototype = {
  7940. constructor: ZImage,
  7941. type: 'image',
  7942. brush: function (ctx, prevEl) {
  7943. var style = this.style;
  7944. var src = style.image;
  7945. // Must bind each time
  7946. style.bind(ctx, this, prevEl);
  7947. var image = this._image = createOrUpdateImage(
  7948. src,
  7949. this._image,
  7950. this,
  7951. this.onload
  7952. );
  7953. if (!image || !isImageReady(image)) {
  7954. return;
  7955. }
  7956. // 图片已经加载完成
  7957. // if (image.nodeName.toUpperCase() == 'IMG') {
  7958. // if (!image.complete) {
  7959. // return;
  7960. // }
  7961. // }
  7962. // Else is canvas
  7963. var x = style.x || 0;
  7964. var y = style.y || 0;
  7965. var width = style.width;
  7966. var height = style.height;
  7967. var aspect = image.width / image.height;
  7968. if (width == null && height != null) {
  7969. // Keep image/height ratio
  7970. width = height * aspect;
  7971. }
  7972. else if (height == null && width != null) {
  7973. height = width / aspect;
  7974. }
  7975. else if (width == null && height == null) {
  7976. width = image.width;
  7977. height = image.height;
  7978. }
  7979. // 设置transform
  7980. this.setTransform(ctx);
  7981. if (style.sWidth && style.sHeight) {
  7982. var sx = style.sx || 0;
  7983. var sy = style.sy || 0;
  7984. ctx.drawImage(
  7985. image,
  7986. sx, sy, style.sWidth, style.sHeight,
  7987. x, y, width, height
  7988. );
  7989. }
  7990. else if (style.sx && style.sy) {
  7991. var sx = style.sx;
  7992. var sy = style.sy;
  7993. var sWidth = width - sx;
  7994. var sHeight = height - sy;
  7995. ctx.drawImage(
  7996. image,
  7997. sx, sy, sWidth, sHeight,
  7998. x, y, width, height
  7999. );
  8000. }
  8001. else {
  8002. ctx.drawImage(image, x, y, width, height);
  8003. }
  8004. // Draw rect text
  8005. if (style.text != null) {
  8006. // Only restore transform when needs draw text.
  8007. this.restoreTransform(ctx);
  8008. this.drawRectText(ctx, this.getBoundingRect());
  8009. }
  8010. },
  8011. getBoundingRect: function () {
  8012. var style = this.style;
  8013. if (!this._rect) {
  8014. this._rect = new BoundingRect(
  8015. style.x || 0, style.y || 0, style.width || 0, style.height || 0
  8016. );
  8017. }
  8018. return this._rect;
  8019. }
  8020. };
  8021. inherits(ZImage, Displayable);
  8022. var HOVER_LAYER_ZLEVEL = 1e5;
  8023. var CANVAS_ZLEVEL = 314159;
  8024. var EL_AFTER_INCREMENTAL_INC = 0.01;
  8025. var INCREMENTAL_INC = 0.001;
  8026. function parseInt10(val) {
  8027. return parseInt(val, 10);
  8028. }
  8029. function isLayerValid(layer) {
  8030. if (!layer) {
  8031. return false;
  8032. }
  8033. if (layer.__builtin__) {
  8034. return true;
  8035. }
  8036. if (typeof (layer.resize) !== 'function'
  8037. || typeof (layer.refresh) !== 'function'
  8038. ) {
  8039. return false;
  8040. }
  8041. return true;
  8042. }
  8043. var tmpRect = new BoundingRect(0, 0, 0, 0);
  8044. var viewRect = new BoundingRect(0, 0, 0, 0);
  8045. function isDisplayableCulled(el, width, height) {
  8046. tmpRect.copy(el.getBoundingRect());
  8047. if (el.transform) {
  8048. tmpRect.applyTransform(el.transform);
  8049. }
  8050. viewRect.width = width;
  8051. viewRect.height = height;
  8052. return !tmpRect.intersect(viewRect);
  8053. }
  8054. function isClipPathChanged(clipPaths, prevClipPaths) {
  8055. if (clipPaths === prevClipPaths) { // Can both be null or undefined
  8056. return false;
  8057. }
  8058. if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
  8059. return true;
  8060. }
  8061. for (var i = 0; i < clipPaths.length; i++) {
  8062. if (clipPaths[i] !== prevClipPaths[i]) {
  8063. return true;
  8064. }
  8065. }
  8066. }
  8067. function doClip(clipPaths, ctx) {
  8068. for (var i = 0; i < clipPaths.length; i++) {
  8069. var clipPath = clipPaths[i];
  8070. clipPath.setTransform(ctx);
  8071. ctx.beginPath();
  8072. clipPath.buildPath(ctx, clipPath.shape);
  8073. ctx.clip();
  8074. // Transform back
  8075. clipPath.restoreTransform(ctx);
  8076. }
  8077. }
  8078. function createRoot(width, height) {
  8079. var domRoot = document.createElement('div');
  8080. // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
  8081. domRoot.style.cssText = [
  8082. 'position:relative',
  8083. 'overflow:hidden',
  8084. 'width:' + width + 'px',
  8085. 'height:' + height + 'px',
  8086. 'padding:0',
  8087. 'margin:0',
  8088. 'border-width:0'
  8089. ].join(';') + ';';
  8090. return domRoot;
  8091. }
  8092. /**
  8093. * @alias module:zrender/Painter
  8094. * @constructor
  8095. * @param {HTMLElement} root 绘图容器
  8096. * @param {module:zrender/Storage} storage
  8097. * @param {Object} opts
  8098. */
  8099. var Painter = function (root, storage, opts) {
  8100. this.type = 'canvas';
  8101. // In node environment using node-canvas
  8102. var singleCanvas = !root.nodeName // In node ?
  8103. || root.nodeName.toUpperCase() === 'CANVAS';
  8104. this._opts = opts = extend({}, opts || {});
  8105. /**
  8106. * @type {number}
  8107. */
  8108. this.dpr = opts.devicePixelRatio || devicePixelRatio;
  8109. /**
  8110. * @type {boolean}
  8111. * @private
  8112. */
  8113. this._singleCanvas = singleCanvas;
  8114. /**
  8115. * 绘图容器
  8116. * @type {HTMLElement}
  8117. */
  8118. this.root = root;
  8119. var rootStyle = root.style;
  8120. if (rootStyle) {
  8121. rootStyle['-webkit-tap-highlight-color'] = 'transparent';
  8122. rootStyle['-webkit-user-select'] =
  8123. rootStyle['user-select'] =
  8124. rootStyle['-webkit-touch-callout'] = 'none';
  8125. root.innerHTML = '';
  8126. }
  8127. /**
  8128. * @type {module:zrender/Storage}
  8129. */
  8130. this.storage = storage;
  8131. /**
  8132. * @type {Array.<number>}
  8133. * @private
  8134. */
  8135. var zlevelList = this._zlevelList = [];
  8136. /**
  8137. * @type {Object.<string, module:zrender/Layer>}
  8138. * @private
  8139. */
  8140. var layers = this._layers = {};
  8141. /**
  8142. * @type {Object.<string, Object>}
  8143. * @private
  8144. */
  8145. this._layerConfig = {};
  8146. /**
  8147. * zrender will do compositing when root is a canvas and have multiple zlevels.
  8148. */
  8149. this._needsManuallyCompositing = false;
  8150. if (!singleCanvas) {
  8151. this._width = this._getSize(0);
  8152. this._height = this._getSize(1);
  8153. var domRoot = this._domRoot = createRoot(
  8154. this._width, this._height
  8155. );
  8156. root.appendChild(domRoot);
  8157. }
  8158. else {
  8159. var width = root.width;
  8160. var height = root.height;
  8161. if (opts.width != null) {
  8162. width = opts.width;
  8163. }
  8164. if (opts.height != null) {
  8165. height = opts.height;
  8166. }
  8167. this.dpr = opts.devicePixelRatio || 1;
  8168. // Use canvas width and height directly
  8169. root.width = width * this.dpr;
  8170. root.height = height * this.dpr;
  8171. this._width = width;
  8172. this._height = height;
  8173. // Create layer if only one given canvas
  8174. // Device can be specified to create a high dpi image.
  8175. var mainLayer = new Layer(root, this, this.dpr);
  8176. mainLayer.__builtin__ = true;
  8177. mainLayer.initContext();
  8178. // FIXME Use canvas width and height
  8179. // mainLayer.resize(width, height);
  8180. layers[CANVAS_ZLEVEL] = mainLayer;
  8181. mainLayer.zlevel = CANVAS_ZLEVEL;
  8182. // Not use common zlevel.
  8183. zlevelList.push(CANVAS_ZLEVEL);
  8184. this._domRoot = root;
  8185. }
  8186. /**
  8187. * @type {module:zrender/Layer}
  8188. * @private
  8189. */
  8190. this._hoverlayer = null;
  8191. this._hoverElements = [];
  8192. };
  8193. Painter.prototype = {
  8194. constructor: Painter,
  8195. getType: function () {
  8196. return 'canvas';
  8197. },
  8198. /**
  8199. * If painter use a single canvas
  8200. * @return {boolean}
  8201. */
  8202. isSingleCanvas: function () {
  8203. return this._singleCanvas;
  8204. },
  8205. /**
  8206. * @return {HTMLDivElement}
  8207. */
  8208. getViewportRoot: function () {
  8209. return this._domRoot;
  8210. },
  8211. getViewportRootOffset: function () {
  8212. var viewportRoot = this.getViewportRoot();
  8213. if (viewportRoot) {
  8214. return {
  8215. offsetLeft: viewportRoot.offsetLeft || 0,
  8216. offsetTop: viewportRoot.offsetTop || 0
  8217. };
  8218. }
  8219. },
  8220. /**
  8221. * 刷新
  8222. * @param {boolean} [paintAll=false] 强制绘制所有displayable
  8223. */
  8224. refresh: function (paintAll) {
  8225. var list = this.storage.getDisplayList(true);
  8226. var zlevelList = this._zlevelList;
  8227. this._redrawId = Math.random();
  8228. this._paintList(list, paintAll, this._redrawId);
  8229. // Paint custum layers
  8230. for (var i = 0; i < zlevelList.length; i++) {
  8231. var z = zlevelList[i];
  8232. var layer = this._layers[z];
  8233. if (!layer.__builtin__ && layer.refresh) {
  8234. var clearColor = i === 0 ? this._backgroundColor : null;
  8235. layer.refresh(clearColor);
  8236. }
  8237. }
  8238. this.refreshHover();
  8239. return this;
  8240. },
  8241. addHover: function (el, hoverStyle) {
  8242. if (el.__hoverMir) {
  8243. return;
  8244. }
  8245. var elMirror = new el.constructor({
  8246. style: el.style,
  8247. shape: el.shape,
  8248. z: el.z,
  8249. z2: el.z2,
  8250. silent: el.silent
  8251. });
  8252. elMirror.__from = el;
  8253. el.__hoverMir = elMirror;
  8254. hoverStyle && elMirror.setStyle(hoverStyle);
  8255. this._hoverElements.push(elMirror);
  8256. return elMirror;
  8257. },
  8258. removeHover: function (el) {
  8259. var elMirror = el.__hoverMir;
  8260. var hoverElements = this._hoverElements;
  8261. var idx = indexOf(hoverElements, elMirror);
  8262. if (idx >= 0) {
  8263. hoverElements.splice(idx, 1);
  8264. }
  8265. el.__hoverMir = null;
  8266. },
  8267. clearHover: function (el) {
  8268. var hoverElements = this._hoverElements;
  8269. for (var i = 0; i < hoverElements.length; i++) {
  8270. var from = hoverElements[i].__from;
  8271. if (from) {
  8272. from.__hoverMir = null;
  8273. }
  8274. }
  8275. hoverElements.length = 0;
  8276. },
  8277. refreshHover: function () {
  8278. var hoverElements = this._hoverElements;
  8279. var len = hoverElements.length;
  8280. var hoverLayer = this._hoverlayer;
  8281. hoverLayer && hoverLayer.clear();
  8282. if (!len) {
  8283. return;
  8284. }
  8285. sort(hoverElements, this.storage.displayableSortFunc);
  8286. // Use a extream large zlevel
  8287. // FIXME?
  8288. if (!hoverLayer) {
  8289. hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);
  8290. }
  8291. var scope = {};
  8292. hoverLayer.ctx.save();
  8293. for (var i = 0; i < len;) {
  8294. var el = hoverElements[i];
  8295. var originalEl = el.__from;
  8296. // Original el is removed
  8297. // PENDING
  8298. if (!(originalEl && originalEl.__zr)) {
  8299. hoverElements.splice(i, 1);
  8300. originalEl.__hoverMir = null;
  8301. len--;
  8302. continue;
  8303. }
  8304. i++;
  8305. // Use transform
  8306. // FIXME style and shape ?
  8307. if (!originalEl.invisible) {
  8308. el.transform = originalEl.transform;
  8309. el.invTransform = originalEl.invTransform;
  8310. el.__clipPaths = originalEl.__clipPaths;
  8311. // el.
  8312. this._doPaintEl(el, hoverLayer, true, scope);
  8313. }
  8314. }
  8315. hoverLayer.ctx.restore();
  8316. },
  8317. getHoverLayer: function () {
  8318. return this.getLayer(HOVER_LAYER_ZLEVEL);
  8319. },
  8320. _paintList: function (list, paintAll, redrawId) {
  8321. if (this._redrawId !== redrawId) {
  8322. return;
  8323. }
  8324. paintAll = paintAll || false;
  8325. this._updateLayerStatus(list);
  8326. var finished = this._doPaintList(list, paintAll);
  8327. if (this._needsManuallyCompositing) {
  8328. this._compositeManually();
  8329. }
  8330. if (!finished) {
  8331. var self = this;
  8332. requestAnimationFrame(function () {
  8333. self._paintList(list, paintAll, redrawId);
  8334. });
  8335. }
  8336. },
  8337. _compositeManually: function () {
  8338. var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;
  8339. var width = this._domRoot.width;
  8340. var height = this._domRoot.height;
  8341. ctx.clearRect(0, 0, width, height);
  8342. // PENDING, If only builtin layer?
  8343. this.eachBuiltinLayer(function (layer) {
  8344. if (layer.virtual) {
  8345. ctx.drawImage(layer.dom, 0, 0, width, height);
  8346. }
  8347. });
  8348. },
  8349. _doPaintList: function (list, paintAll) {
  8350. var layerList = [];
  8351. for (var zi = 0; zi < this._zlevelList.length; zi++) {
  8352. var zlevel = this._zlevelList[zi];
  8353. var layer = this._layers[zlevel];
  8354. if (layer.__builtin__
  8355. && layer !== this._hoverlayer
  8356. && (layer.__dirty || paintAll)
  8357. ) {
  8358. layerList.push(layer);
  8359. }
  8360. }
  8361. var finished = true;
  8362. for (var k = 0; k < layerList.length; k++) {
  8363. var layer = layerList[k];
  8364. var ctx = layer.ctx;
  8365. var scope = {};
  8366. ctx.save();
  8367. var start = paintAll ? layer.__startIndex : layer.__drawIndex;
  8368. var useTimer = !paintAll && layer.incremental && Date.now;
  8369. var startTime = useTimer && Date.now();
  8370. var clearColor = layer.zlevel === this._zlevelList[0]
  8371. ? this._backgroundColor : null;
  8372. // All elements in this layer are cleared.
  8373. if (layer.__startIndex === layer.__endIndex) {
  8374. layer.clear(false, clearColor);
  8375. }
  8376. else if (start === layer.__startIndex) {
  8377. var firstEl = list[start];
  8378. if (!firstEl.incremental || !firstEl.notClear || paintAll) {
  8379. layer.clear(false, clearColor);
  8380. }
  8381. }
  8382. if (start === -1) {
  8383. console.error('For some unknown reason. drawIndex is -1');
  8384. start = layer.__startIndex;
  8385. }
  8386. for (var i = start; i < layer.__endIndex; i++) {
  8387. var el = list[i];
  8388. this._doPaintEl(el, layer, paintAll, scope);
  8389. el.__dirty = el.__dirtyText = false;
  8390. if (useTimer) {
  8391. // Date.now can be executed in 13,025,305 ops/second.
  8392. var dTime = Date.now() - startTime;
  8393. // Give 15 millisecond to draw.
  8394. // The rest elements will be drawn in the next frame.
  8395. if (dTime > 15) {
  8396. break;
  8397. }
  8398. }
  8399. }
  8400. layer.__drawIndex = i;
  8401. if (layer.__drawIndex < layer.__endIndex) {
  8402. finished = false;
  8403. }
  8404. if (scope.prevElClipPaths) {
  8405. // Needs restore the state. If last drawn element is in the clipping area.
  8406. ctx.restore();
  8407. }
  8408. ctx.restore();
  8409. }
  8410. if (env$1.wxa) {
  8411. // Flush for weixin application
  8412. each$1(this._layers, function (layer) {
  8413. if (layer && layer.ctx && layer.ctx.draw) {
  8414. layer.ctx.draw();
  8415. }
  8416. });
  8417. }
  8418. return finished;
  8419. },
  8420. _doPaintEl: function (el, currentLayer, forcePaint, scope) {
  8421. var ctx = currentLayer.ctx;
  8422. var m = el.transform;
  8423. if (
  8424. (currentLayer.__dirty || forcePaint)
  8425. // Ignore invisible element
  8426. && !el.invisible
  8427. // Ignore transparent element
  8428. && el.style.opacity !== 0
  8429. // Ignore scale 0 element, in some environment like node-canvas
  8430. // Draw a scale 0 element can cause all following draw wrong
  8431. // And setTransform with scale 0 will cause set back transform failed.
  8432. && !(m && !m[0] && !m[3])
  8433. // Ignore culled element
  8434. && !(el.culling && isDisplayableCulled(el, this._width, this._height))
  8435. ) {
  8436. var clipPaths = el.__clipPaths;
  8437. // Optimize when clipping on group with several elements
  8438. if (!scope.prevElClipPaths
  8439. || isClipPathChanged(clipPaths, scope.prevElClipPaths)
  8440. ) {
  8441. // If has previous clipping state, restore from it
  8442. if (scope.prevElClipPaths) {
  8443. currentLayer.ctx.restore();
  8444. scope.prevElClipPaths = null;
  8445. // Reset prevEl since context has been restored
  8446. scope.prevEl = null;
  8447. }
  8448. // New clipping state
  8449. if (clipPaths) {
  8450. ctx.save();
  8451. doClip(clipPaths, ctx);
  8452. scope.prevElClipPaths = clipPaths;
  8453. }
  8454. }
  8455. el.beforeBrush && el.beforeBrush(ctx);
  8456. el.brush(ctx, scope.prevEl || null);
  8457. scope.prevEl = el;
  8458. el.afterBrush && el.afterBrush(ctx);
  8459. }
  8460. },
  8461. /**
  8462. * 获取 zlevel 所在层,如果不存在则会创建一个新的层
  8463. * @param {number} zlevel
  8464. * @param {boolean} virtual Virtual layer will not be inserted into dom.
  8465. * @return {module:zrender/Layer}
  8466. */
  8467. getLayer: function (zlevel, virtual) {
  8468. if (this._singleCanvas && !this._needsManuallyCompositing) {
  8469. zlevel = CANVAS_ZLEVEL;
  8470. }
  8471. var layer = this._layers[zlevel];
  8472. if (!layer) {
  8473. // Create a new layer
  8474. layer = new Layer('zr_' + zlevel, this, this.dpr);
  8475. layer.zlevel = zlevel;
  8476. layer.__builtin__ = true;
  8477. if (this._layerConfig[zlevel]) {
  8478. merge(layer, this._layerConfig[zlevel], true);
  8479. }
  8480. if (virtual) {
  8481. layer.virtual = virtual;
  8482. }
  8483. this.insertLayer(zlevel, layer);
  8484. // Context is created after dom inserted to document
  8485. // Or excanvas will get 0px clientWidth and clientHeight
  8486. layer.initContext();
  8487. }
  8488. return layer;
  8489. },
  8490. insertLayer: function (zlevel, layer) {
  8491. var layersMap = this._layers;
  8492. var zlevelList = this._zlevelList;
  8493. var len = zlevelList.length;
  8494. var prevLayer = null;
  8495. var i = -1;
  8496. var domRoot = this._domRoot;
  8497. if (layersMap[zlevel]) {
  8498. zrLog('ZLevel ' + zlevel + ' has been used already');
  8499. return;
  8500. }
  8501. // Check if is a valid layer
  8502. if (!isLayerValid(layer)) {
  8503. zrLog('Layer of zlevel ' + zlevel + ' is not valid');
  8504. return;
  8505. }
  8506. if (len > 0 && zlevel > zlevelList[0]) {
  8507. for (i = 0; i < len - 1; i++) {
  8508. if (
  8509. zlevelList[i] < zlevel
  8510. && zlevelList[i + 1] > zlevel
  8511. ) {
  8512. break;
  8513. }
  8514. }
  8515. prevLayer = layersMap[zlevelList[i]];
  8516. }
  8517. zlevelList.splice(i + 1, 0, zlevel);
  8518. layersMap[zlevel] = layer;
  8519. // Vitual layer will not directly show on the screen.
  8520. // (It can be a WebGL layer and assigned to a ZImage element)
  8521. // But it still under management of zrender.
  8522. if (!layer.virtual) {
  8523. if (prevLayer) {
  8524. var prevDom = prevLayer.dom;
  8525. if (prevDom.nextSibling) {
  8526. domRoot.insertBefore(
  8527. layer.dom,
  8528. prevDom.nextSibling
  8529. );
  8530. }
  8531. else {
  8532. domRoot.appendChild(layer.dom);
  8533. }
  8534. }
  8535. else {
  8536. if (domRoot.firstChild) {
  8537. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  8538. }
  8539. else {
  8540. domRoot.appendChild(layer.dom);
  8541. }
  8542. }
  8543. }
  8544. },
  8545. // Iterate each layer
  8546. eachLayer: function (cb, context) {
  8547. var zlevelList = this._zlevelList;
  8548. var z;
  8549. var i;
  8550. for (i = 0; i < zlevelList.length; i++) {
  8551. z = zlevelList[i];
  8552. cb.call(context, this._layers[z], z);
  8553. }
  8554. },
  8555. // Iterate each buildin layer
  8556. eachBuiltinLayer: function (cb, context) {
  8557. var zlevelList = this._zlevelList;
  8558. var layer;
  8559. var z;
  8560. var i;
  8561. for (i = 0; i < zlevelList.length; i++) {
  8562. z = zlevelList[i];
  8563. layer = this._layers[z];
  8564. if (layer.__builtin__) {
  8565. cb.call(context, layer, z);
  8566. }
  8567. }
  8568. },
  8569. // Iterate each other layer except buildin layer
  8570. eachOtherLayer: function (cb, context) {
  8571. var zlevelList = this._zlevelList;
  8572. var layer;
  8573. var z;
  8574. var i;
  8575. for (i = 0; i < zlevelList.length; i++) {
  8576. z = zlevelList[i];
  8577. layer = this._layers[z];
  8578. if (!layer.__builtin__) {
  8579. cb.call(context, layer, z);
  8580. }
  8581. }
  8582. },
  8583. /**
  8584. * 获取所有已创建的层
  8585. * @param {Array.<module:zrender/Layer>} [prevLayer]
  8586. */
  8587. getLayers: function () {
  8588. return this._layers;
  8589. },
  8590. _updateLayerStatus: function (list) {
  8591. this.eachBuiltinLayer(function (layer, z) {
  8592. layer.__dirty = layer.__used = false;
  8593. });
  8594. function updatePrevLayer(idx) {
  8595. if (prevLayer) {
  8596. if (prevLayer.__endIndex !== idx) {
  8597. prevLayer.__dirty = true;
  8598. }
  8599. prevLayer.__endIndex = idx;
  8600. }
  8601. }
  8602. if (this._singleCanvas) {
  8603. for (var i = 1; i < list.length; i++) {
  8604. var el = list[i];
  8605. if (el.zlevel !== list[i - 1].zlevel || el.incremental) {
  8606. this._needsManuallyCompositing = true;
  8607. break;
  8608. }
  8609. }
  8610. }
  8611. var prevLayer = null;
  8612. var incrementalLayerCount = 0;
  8613. for (var i = 0; i < list.length; i++) {
  8614. var el = list[i];
  8615. var zlevel = el.zlevel;
  8616. var layer;
  8617. // PENDING If change one incremental element style ?
  8618. // TODO Where there are non-incremental elements between incremental elements.
  8619. if (el.incremental) {
  8620. layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);
  8621. layer.incremental = true;
  8622. incrementalLayerCount = 1;
  8623. }
  8624. else {
  8625. layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);
  8626. }
  8627. if (!layer.__builtin__) {
  8628. zrLog('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);
  8629. }
  8630. if (layer !== prevLayer) {
  8631. layer.__used = true;
  8632. if (layer.__startIndex !== i) {
  8633. layer.__dirty = true;
  8634. }
  8635. layer.__startIndex = i;
  8636. if (!layer.incremental) {
  8637. layer.__drawIndex = i;
  8638. }
  8639. else {
  8640. // Mark layer draw index needs to update.
  8641. layer.__drawIndex = -1;
  8642. }
  8643. updatePrevLayer(i);
  8644. prevLayer = layer;
  8645. }
  8646. if (el.__dirty) {
  8647. layer.__dirty = true;
  8648. if (layer.incremental && layer.__drawIndex < 0) {
  8649. // Start draw from the first dirty element.
  8650. layer.__drawIndex = i;
  8651. }
  8652. }
  8653. }
  8654. updatePrevLayer(i);
  8655. this.eachBuiltinLayer(function (layer, z) {
  8656. // Used in last frame but not in this frame. Needs clear
  8657. if (!layer.__used && layer.getElementCount() > 0) {
  8658. layer.__dirty = true;
  8659. layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;
  8660. }
  8661. // For incremental layer. In case start index changed and no elements are dirty.
  8662. if (layer.__dirty && layer.__drawIndex < 0) {
  8663. layer.__drawIndex = layer.__startIndex;
  8664. }
  8665. });
  8666. },
  8667. /**
  8668. * 清除hover层外所有内容
  8669. */
  8670. clear: function () {
  8671. this.eachBuiltinLayer(this._clearLayer);
  8672. return this;
  8673. },
  8674. _clearLayer: function (layer) {
  8675. layer.clear();
  8676. },
  8677. setBackgroundColor: function (backgroundColor) {
  8678. this._backgroundColor = backgroundColor;
  8679. },
  8680. /**
  8681. * 修改指定zlevel的绘制参数
  8682. *
  8683. * @param {string} zlevel
  8684. * @param {Object} config 配置对象
  8685. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  8686. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  8687. * @param {number} [config.lastFrameAlpha=0.7]
  8688. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  8689. */
  8690. configLayer: function (zlevel, config) {
  8691. if (config) {
  8692. var layerConfig = this._layerConfig;
  8693. if (!layerConfig[zlevel]) {
  8694. layerConfig[zlevel] = config;
  8695. }
  8696. else {
  8697. merge(layerConfig[zlevel], config, true);
  8698. }
  8699. for (var i = 0; i < this._zlevelList.length; i++) {
  8700. var _zlevel = this._zlevelList[i];
  8701. if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {
  8702. var layer = this._layers[_zlevel];
  8703. merge(layer, layerConfig[zlevel], true);
  8704. }
  8705. }
  8706. }
  8707. },
  8708. /**
  8709. * 删除指定层
  8710. * @param {number} zlevel 层所在的zlevel
  8711. */
  8712. delLayer: function (zlevel) {
  8713. var layers = this._layers;
  8714. var zlevelList = this._zlevelList;
  8715. var layer = layers[zlevel];
  8716. if (!layer) {
  8717. return;
  8718. }
  8719. layer.dom.parentNode.removeChild(layer.dom);
  8720. delete layers[zlevel];
  8721. zlevelList.splice(indexOf(zlevelList, zlevel), 1);
  8722. },
  8723. /**
  8724. * 区域大小变化后重绘
  8725. */
  8726. resize: function (width, height) {
  8727. if (!this._domRoot.style) { // Maybe in node or worker
  8728. if (width == null || height == null) {
  8729. return;
  8730. }
  8731. this._width = width;
  8732. this._height = height;
  8733. this.getLayer(CANVAS_ZLEVEL).resize(width, height);
  8734. }
  8735. else {
  8736. var domRoot = this._domRoot;
  8737. // FIXME Why ?
  8738. domRoot.style.display = 'none';
  8739. // Save input w/h
  8740. var opts = this._opts;
  8741. width != null && (opts.width = width);
  8742. height != null && (opts.height = height);
  8743. width = this._getSize(0);
  8744. height = this._getSize(1);
  8745. domRoot.style.display = '';
  8746. // 优化没有实际改变的resize
  8747. if (this._width !== width || height !== this._height) {
  8748. domRoot.style.width = width + 'px';
  8749. domRoot.style.height = height + 'px';
  8750. for (var id in this._layers) {
  8751. if (this._layers.hasOwnProperty(id)) {
  8752. this._layers[id].resize(width, height);
  8753. }
  8754. }
  8755. each$1(this._progressiveLayers, function (layer) {
  8756. layer.resize(width, height);
  8757. });
  8758. this.refresh(true);
  8759. }
  8760. this._width = width;
  8761. this._height = height;
  8762. }
  8763. return this;
  8764. },
  8765. /**
  8766. * 清除单独的一个层
  8767. * @param {number} zlevel
  8768. */
  8769. clearLayer: function (zlevel) {
  8770. var layer = this._layers[zlevel];
  8771. if (layer) {
  8772. layer.clear();
  8773. }
  8774. },
  8775. /**
  8776. * 释放
  8777. */
  8778. dispose: function () {
  8779. this.root.innerHTML = '';
  8780. this.root =
  8781. this.storage =
  8782. this._domRoot =
  8783. this._layers = null;
  8784. },
  8785. /**
  8786. * Get canvas which has all thing rendered
  8787. * @param {Object} opts
  8788. * @param {string} [opts.backgroundColor]
  8789. * @param {number} [opts.pixelRatio]
  8790. */
  8791. getRenderedCanvas: function (opts) {
  8792. opts = opts || {};
  8793. if (this._singleCanvas && !this._compositeManually) {
  8794. return this._layers[CANVAS_ZLEVEL].dom;
  8795. }
  8796. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  8797. imageLayer.initContext();
  8798. imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);
  8799. if (opts.pixelRatio <= this.dpr) {
  8800. this.refresh();
  8801. var width = imageLayer.dom.width;
  8802. var height = imageLayer.dom.height;
  8803. var ctx = imageLayer.ctx;
  8804. this.eachLayer(function (layer) {
  8805. if (layer.__builtin__) {
  8806. ctx.drawImage(layer.dom, 0, 0, width, height);
  8807. }
  8808. else if (layer.renderToCanvas) {
  8809. imageLayer.ctx.save();
  8810. layer.renderToCanvas(imageLayer.ctx);
  8811. imageLayer.ctx.restore();
  8812. }
  8813. });
  8814. }
  8815. else {
  8816. // PENDING, echarts-gl and incremental rendering.
  8817. var scope = {};
  8818. var displayList = this.storage.getDisplayList(true);
  8819. for (var i = 0; i < displayList.length; i++) {
  8820. var el = displayList[i];
  8821. this._doPaintEl(el, imageLayer, true, scope);
  8822. }
  8823. }
  8824. return imageLayer.dom;
  8825. },
  8826. /**
  8827. * 获取绘图区域宽度
  8828. */
  8829. getWidth: function () {
  8830. return this._width;
  8831. },
  8832. /**
  8833. * 获取绘图区域高度
  8834. */
  8835. getHeight: function () {
  8836. return this._height;
  8837. },
  8838. _getSize: function (whIdx) {
  8839. var opts = this._opts;
  8840. var wh = ['width', 'height'][whIdx];
  8841. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  8842. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  8843. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  8844. if (opts[wh] != null && opts[wh] !== 'auto') {
  8845. return parseFloat(opts[wh]);
  8846. }
  8847. var root = this.root;
  8848. // IE8 does not support getComputedStyle, but it use VML.
  8849. var stl = document.defaultView.getComputedStyle(root);
  8850. return (
  8851. (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
  8852. - (parseInt10(stl[plt]) || 0)
  8853. - (parseInt10(stl[prb]) || 0)
  8854. ) | 0;
  8855. },
  8856. pathToImage: function (path, dpr) {
  8857. dpr = dpr || this.dpr;
  8858. var canvas = document.createElement('canvas');
  8859. var ctx = canvas.getContext('2d');
  8860. var rect = path.getBoundingRect();
  8861. var style = path.style;
  8862. var shadowBlurSize = style.shadowBlur * dpr;
  8863. var shadowOffsetX = style.shadowOffsetX * dpr;
  8864. var shadowOffsetY = style.shadowOffsetY * dpr;
  8865. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  8866. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  8867. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  8868. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  8869. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  8870. var width = rect.width + leftMargin + rightMargin;
  8871. var height = rect.height + topMargin + bottomMargin;
  8872. canvas.width = width * dpr;
  8873. canvas.height = height * dpr;
  8874. ctx.scale(dpr, dpr);
  8875. ctx.clearRect(0, 0, width, height);
  8876. ctx.dpr = dpr;
  8877. var pathTransform = {
  8878. position: path.position,
  8879. rotation: path.rotation,
  8880. scale: path.scale
  8881. };
  8882. path.position = [leftMargin - rect.x, topMargin - rect.y];
  8883. path.rotation = 0;
  8884. path.scale = [1, 1];
  8885. path.updateTransform();
  8886. if (path) {
  8887. path.brush(ctx);
  8888. }
  8889. var ImageShape = ZImage;
  8890. var imgShape = new ImageShape({
  8891. style: {
  8892. x: 0,
  8893. y: 0,
  8894. image: canvas
  8895. }
  8896. });
  8897. if (pathTransform.position != null) {
  8898. imgShape.position = path.position = pathTransform.position;
  8899. }
  8900. if (pathTransform.rotation != null) {
  8901. imgShape.rotation = path.rotation = pathTransform.rotation;
  8902. }
  8903. if (pathTransform.scale != null) {
  8904. imgShape.scale = path.scale = pathTransform.scale;
  8905. }
  8906. return imgShape;
  8907. }
  8908. };
  8909. /**
  8910. * 动画主类, 调度和管理所有动画控制器
  8911. *
  8912. * @module zrender/animation/Animation
  8913. * @author pissang(https://github.com/pissang)
  8914. */
  8915. // TODO Additive animation
  8916. // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
  8917. // https://developer.apple.com/videos/wwdc2014/#236
  8918. /**
  8919. * @typedef {Object} IZRenderStage
  8920. * @property {Function} update
  8921. */
  8922. /**
  8923. * @alias module:zrender/animation/Animation
  8924. * @constructor
  8925. * @param {Object} [options]
  8926. * @param {Function} [options.onframe]
  8927. * @param {IZRenderStage} [options.stage]
  8928. * @example
  8929. * var animation = new Animation();
  8930. * var obj = {
  8931. * x: 100,
  8932. * y: 100
  8933. * };
  8934. * animation.animate(node.position)
  8935. * .when(1000, {
  8936. * x: 500,
  8937. * y: 500
  8938. * })
  8939. * .when(2000, {
  8940. * x: 100,
  8941. * y: 100
  8942. * })
  8943. * .start('spline');
  8944. */
  8945. var Animation = function (options) {
  8946. options = options || {};
  8947. this.stage = options.stage || {};
  8948. this.onframe = options.onframe || function () {};
  8949. // private properties
  8950. this._clips = [];
  8951. this._running = false;
  8952. this._time;
  8953. this._pausedTime;
  8954. this._pauseStart;
  8955. this._paused = false;
  8956. Eventful.call(this);
  8957. };
  8958. Animation.prototype = {
  8959. constructor: Animation,
  8960. /**
  8961. * 添加 clip
  8962. * @param {module:zrender/animation/Clip} clip
  8963. */
  8964. addClip: function (clip) {
  8965. this._clips.push(clip);
  8966. },
  8967. /**
  8968. * 添加 animator
  8969. * @param {module:zrender/animation/Animator} animator
  8970. */
  8971. addAnimator: function (animator) {
  8972. animator.animation = this;
  8973. var clips = animator.getClips();
  8974. for (var i = 0; i < clips.length; i++) {
  8975. this.addClip(clips[i]);
  8976. }
  8977. },
  8978. /**
  8979. * 删除动画片段
  8980. * @param {module:zrender/animation/Clip} clip
  8981. */
  8982. removeClip: function (clip) {
  8983. var idx = indexOf(this._clips, clip);
  8984. if (idx >= 0) {
  8985. this._clips.splice(idx, 1);
  8986. }
  8987. },
  8988. /**
  8989. * 删除动画片段
  8990. * @param {module:zrender/animation/Animator} animator
  8991. */
  8992. removeAnimator: function (animator) {
  8993. var clips = animator.getClips();
  8994. for (var i = 0; i < clips.length; i++) {
  8995. this.removeClip(clips[i]);
  8996. }
  8997. animator.animation = null;
  8998. },
  8999. _update: function () {
  9000. var time = new Date().getTime() - this._pausedTime;
  9001. var delta = time - this._time;
  9002. var clips = this._clips;
  9003. var len = clips.length;
  9004. var deferredEvents = [];
  9005. var deferredClips = [];
  9006. for (var i = 0; i < len; i++) {
  9007. var clip = clips[i];
  9008. var e = clip.step(time, delta);
  9009. // Throw out the events need to be called after
  9010. // stage.update, like destroy
  9011. if (e) {
  9012. deferredEvents.push(e);
  9013. deferredClips.push(clip);
  9014. }
  9015. }
  9016. // Remove the finished clip
  9017. for (var i = 0; i < len;) {
  9018. if (clips[i]._needsRemove) {
  9019. clips[i] = clips[len - 1];
  9020. clips.pop();
  9021. len--;
  9022. }
  9023. else {
  9024. i++;
  9025. }
  9026. }
  9027. len = deferredEvents.length;
  9028. for (var i = 0; i < len; i++) {
  9029. deferredClips[i].fire(deferredEvents[i]);
  9030. }
  9031. this._time = time;
  9032. this.onframe(delta);
  9033. // 'frame' should be triggered before stage, because upper application
  9034. // depends on the sequence (e.g., echarts-stream and finish
  9035. // event judge)
  9036. this.trigger('frame', delta);
  9037. if (this.stage.update) {
  9038. this.stage.update();
  9039. }
  9040. },
  9041. _startLoop: function () {
  9042. var self = this;
  9043. this._running = true;
  9044. function step() {
  9045. if (self._running) {
  9046. requestAnimationFrame(step);
  9047. !self._paused && self._update();
  9048. }
  9049. }
  9050. requestAnimationFrame(step);
  9051. },
  9052. /**
  9053. * Start animation.
  9054. */
  9055. start: function () {
  9056. this._time = new Date().getTime();
  9057. this._pausedTime = 0;
  9058. this._startLoop();
  9059. },
  9060. /**
  9061. * Stop animation.
  9062. */
  9063. stop: function () {
  9064. this._running = false;
  9065. },
  9066. /**
  9067. * Pause animation.
  9068. */
  9069. pause: function () {
  9070. if (!this._paused) {
  9071. this._pauseStart = new Date().getTime();
  9072. this._paused = true;
  9073. }
  9074. },
  9075. /**
  9076. * Resume animation.
  9077. */
  9078. resume: function () {
  9079. if (this._paused) {
  9080. this._pausedTime += (new Date().getTime()) - this._pauseStart;
  9081. this._paused = false;
  9082. }
  9083. },
  9084. /**
  9085. * Clear animation.
  9086. */
  9087. clear: function () {
  9088. this._clips = [];
  9089. },
  9090. /**
  9091. * Whether animation finished.
  9092. */
  9093. isFinished: function () {
  9094. return !this._clips.length;
  9095. },
  9096. /**
  9097. * Creat animator for a target, whose props can be animated.
  9098. *
  9099. * @param {Object} target
  9100. * @param {Object} options
  9101. * @param {boolean} [options.loop=false] Whether loop animation.
  9102. * @param {Function} [options.getter=null] Get value from target.
  9103. * @param {Function} [options.setter=null] Set value to target.
  9104. * @return {module:zrender/animation/Animation~Animator}
  9105. */
  9106. // TODO Gap
  9107. animate: function (target, options) {
  9108. options = options || {};
  9109. var animator = new Animator(
  9110. target,
  9111. options.loop,
  9112. options.getter,
  9113. options.setter
  9114. );
  9115. this.addAnimator(animator);
  9116. return animator;
  9117. }
  9118. };
  9119. mixin(Animation, Eventful);
  9120. var TOUCH_CLICK_DELAY = 300;
  9121. var mouseHandlerNames = [
  9122. 'click', 'dblclick', 'mousewheel', 'mouseout',
  9123. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  9124. ];
  9125. var touchHandlerNames = [
  9126. 'touchstart', 'touchend', 'touchmove'
  9127. ];
  9128. var pointerEventNames = {
  9129. pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1
  9130. };
  9131. var pointerHandlerNames = map(mouseHandlerNames, function (name) {
  9132. var nm = name.replace('mouse', 'pointer');
  9133. return pointerEventNames[nm] ? nm : name;
  9134. });
  9135. function eventNameFix(name) {
  9136. return (name === 'mousewheel' && env$1.browser.firefox) ? 'DOMMouseScroll' : name;
  9137. }
  9138. // function onMSGestureChange(proxy, event) {
  9139. // if (event.translationX || event.translationY) {
  9140. // // mousemove is carried by MSGesture to reduce the sensitivity.
  9141. // proxy.handler.dispatchToElement(event.target, 'mousemove', event);
  9142. // }
  9143. // if (event.scale !== 1) {
  9144. // event.pinchX = event.offsetX;
  9145. // event.pinchY = event.offsetY;
  9146. // event.pinchScale = event.scale;
  9147. // proxy.handler.dispatchToElement(event.target, 'pinch', event);
  9148. // }
  9149. // }
  9150. /**
  9151. * Prevent mouse event from being dispatched after Touch Events action
  9152. * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
  9153. * 1. Mobile browsers dispatch mouse events 300ms after touchend.
  9154. * 2. Chrome for Android dispatch mousedown for long-touch about 650ms
  9155. * Result: Blocking Mouse Events for 700ms.
  9156. */
  9157. function setTouchTimer(instance) {
  9158. instance._touching = true;
  9159. clearTimeout(instance._touchTimer);
  9160. instance._touchTimer = setTimeout(function () {
  9161. instance._touching = false;
  9162. }, 700);
  9163. }
  9164. var domHandlers = {
  9165. /**
  9166. * Mouse move handler
  9167. * @inner
  9168. * @param {Event} event
  9169. */
  9170. mousemove: function (event) {
  9171. event = normalizeEvent(this.dom, event);
  9172. this.trigger('mousemove', event);
  9173. },
  9174. /**
  9175. * Mouse out handler
  9176. * @inner
  9177. * @param {Event} event
  9178. */
  9179. mouseout: function (event) {
  9180. event = normalizeEvent(this.dom, event);
  9181. var element = event.toElement || event.relatedTarget;
  9182. if (element !== this.dom) {
  9183. while (element && element.nodeType !== 9) {
  9184. // 忽略包含在root中的dom引起的mouseOut
  9185. if (element === this.dom) {
  9186. return;
  9187. }
  9188. element = element.parentNode;
  9189. }
  9190. }
  9191. this.trigger('mouseout', event);
  9192. },
  9193. /**
  9194. * Touch开始响应函数
  9195. * @inner
  9196. * @param {Event} event
  9197. */
  9198. touchstart: function (event) {
  9199. // Default mouse behaviour should not be disabled here.
  9200. // For example, page may needs to be slided.
  9201. event = normalizeEvent(this.dom, event);
  9202. // Mark touch, which is useful in distinguish touch and
  9203. // mouse event in upper applicatoin.
  9204. event.zrByTouch = true;
  9205. this._lastTouchMoment = new Date();
  9206. this.handler.processGesture(this, event, 'start');
  9207. // In touch device, trigger `mousemove`(`mouseover`) should
  9208. // be triggered, and must before `mousedown` triggered.
  9209. domHandlers.mousemove.call(this, event);
  9210. domHandlers.mousedown.call(this, event);
  9211. setTouchTimer(this);
  9212. },
  9213. /**
  9214. * Touch移动响应函数
  9215. * @inner
  9216. * @param {Event} event
  9217. */
  9218. touchmove: function (event) {
  9219. event = normalizeEvent(this.dom, event);
  9220. // Mark touch, which is useful in distinguish touch and
  9221. // mouse event in upper applicatoin.
  9222. event.zrByTouch = true;
  9223. this.handler.processGesture(this, event, 'change');
  9224. // Mouse move should always be triggered no matter whether
  9225. // there is gestrue event, because mouse move and pinch may
  9226. // be used at the same time.
  9227. domHandlers.mousemove.call(this, event);
  9228. setTouchTimer(this);
  9229. },
  9230. /**
  9231. * Touch结束响应函数
  9232. * @inner
  9233. * @param {Event} event
  9234. */
  9235. touchend: function (event) {
  9236. event = normalizeEvent(this.dom, event);
  9237. // Mark touch, which is useful in distinguish touch and
  9238. // mouse event in upper applicatoin.
  9239. event.zrByTouch = true;
  9240. this.handler.processGesture(this, event, 'end');
  9241. domHandlers.mouseup.call(this, event);
  9242. // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
  9243. // triggered in `touchstart`. This seems to be illogical, but by this mechanism,
  9244. // we can conveniently implement "hover style" in both PC and touch device just
  9245. // by listening to `mouseover` to add "hover style" and listening to `mouseout`
  9246. // to remove "hover style" on an element, without any additional code for
  9247. // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
  9248. // style" will remain for user view)
  9249. // click event should always be triggered no matter whether
  9250. // there is gestrue event. System click can not be prevented.
  9251. if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
  9252. domHandlers.click.call(this, event);
  9253. }
  9254. setTouchTimer(this);
  9255. },
  9256. pointerdown: function (event) {
  9257. domHandlers.mousedown.call(this, event);
  9258. // if (useMSGuesture(this, event)) {
  9259. // this._msGesture.addPointer(event.pointerId);
  9260. // }
  9261. },
  9262. pointermove: function (event) {
  9263. // FIXME
  9264. // pointermove is so sensitive that it always triggered when
  9265. // tap(click) on touch screen, which affect some judgement in
  9266. // upper application. So, we dont support mousemove on MS touch
  9267. // device yet.
  9268. if (!isPointerFromTouch(event)) {
  9269. domHandlers.mousemove.call(this, event);
  9270. }
  9271. },
  9272. pointerup: function (event) {
  9273. domHandlers.mouseup.call(this, event);
  9274. },
  9275. pointerout: function (event) {
  9276. // pointerout will be triggered when tap on touch screen
  9277. // (IE11+/Edge on MS Surface) after click event triggered,
  9278. // which is inconsistent with the mousout behavior we defined
  9279. // in touchend. So we unify them.
  9280. // (check domHandlers.touchend for detailed explanation)
  9281. if (!isPointerFromTouch(event)) {
  9282. domHandlers.mouseout.call(this, event);
  9283. }
  9284. }
  9285. };
  9286. function isPointerFromTouch(event) {
  9287. var pointerType = event.pointerType;
  9288. return pointerType === 'pen' || pointerType === 'touch';
  9289. }
  9290. // function useMSGuesture(handlerProxy, event) {
  9291. // return isPointerFromTouch(event) && !!handlerProxy._msGesture;
  9292. // }
  9293. // Common handlers
  9294. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  9295. domHandlers[name] = function (event) {
  9296. event = normalizeEvent(this.dom, event);
  9297. this.trigger(name, event);
  9298. };
  9299. });
  9300. /**
  9301. * 为控制类实例初始化dom 事件处理函数
  9302. *
  9303. * @inner
  9304. * @param {module:zrender/Handler} instance 控制类实例
  9305. */
  9306. function initDomHandler(instance) {
  9307. each$1(touchHandlerNames, function (name) {
  9308. instance._handlers[name] = bind(domHandlers[name], instance);
  9309. });
  9310. each$1(pointerHandlerNames, function (name) {
  9311. instance._handlers[name] = bind(domHandlers[name], instance);
  9312. });
  9313. each$1(mouseHandlerNames, function (name) {
  9314. instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
  9315. });
  9316. function makeMouseHandler(fn, instance) {
  9317. return function () {
  9318. if (instance._touching) {
  9319. return;
  9320. }
  9321. return fn.apply(instance, arguments);
  9322. };
  9323. }
  9324. }
  9325. function HandlerDomProxy(dom) {
  9326. Eventful.call(this);
  9327. this.dom = dom;
  9328. /**
  9329. * @private
  9330. * @type {boolean}
  9331. */
  9332. this._touching = false;
  9333. /**
  9334. * @private
  9335. * @type {number}
  9336. */
  9337. this._touchTimer;
  9338. this._handlers = {};
  9339. initDomHandler(this);
  9340. if (env$1.pointerEventsSupported) { // Only IE11+/Edge
  9341. // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
  9342. // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
  9343. // at the same time.
  9344. // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
  9345. // screen, which do not occurs in pointer event.
  9346. // So we use pointer event to both detect touch gesture and mouse behavior.
  9347. mountHandlers(pointerHandlerNames, this);
  9348. // FIXME
  9349. // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
  9350. // which does not prevent defuault behavior occasionally (which may cause view port
  9351. // zoomed in but use can not zoom it back). And event.preventDefault() does not work.
  9352. // So we have to not to use MSGesture and not to support touchmove and pinch on MS
  9353. // touch screen. And we only support click behavior on MS touch screen now.
  9354. // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
  9355. // We dont support touch on IE on win7.
  9356. // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
  9357. // if (typeof MSGesture === 'function') {
  9358. // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
  9359. // dom.addEventListener('MSGestureChange', onMSGestureChange);
  9360. // }
  9361. }
  9362. else {
  9363. if (env$1.touchEventsSupported) {
  9364. mountHandlers(touchHandlerNames, this);
  9365. // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
  9366. // addEventListener(root, 'mouseout', this._mouseoutHandler);
  9367. }
  9368. // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
  9369. // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
  9370. // mouse event can not be handle in those devices.
  9371. // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
  9372. // mouseevent after touch event triggered, see `setTouchTimer`.
  9373. mountHandlers(mouseHandlerNames, this);
  9374. }
  9375. function mountHandlers(handlerNames, instance) {
  9376. each$1(handlerNames, function (name) {
  9377. addEventListener(dom, eventNameFix(name), instance._handlers[name]);
  9378. }, instance);
  9379. }
  9380. }
  9381. var handlerDomProxyProto = HandlerDomProxy.prototype;
  9382. handlerDomProxyProto.dispose = function () {
  9383. var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
  9384. for (var i = 0; i < handlerNames.length; i++) {
  9385. var name = handlerNames[i];
  9386. removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
  9387. }
  9388. };
  9389. handlerDomProxyProto.setCursor = function (cursorStyle) {
  9390. this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');
  9391. };
  9392. mixin(HandlerDomProxy, Eventful);
  9393. /*!
  9394. * ZRender, a high performance 2d drawing library.
  9395. *
  9396. * Copyright (c) 2013, Baidu Inc.
  9397. * All rights reserved.
  9398. *
  9399. * LICENSE
  9400. * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
  9401. */
  9402. var useVML = !env$1.canvasSupported;
  9403. var painterCtors = {
  9404. canvas: Painter
  9405. };
  9406. var instances$1 = {}; // ZRender实例map索引
  9407. /**
  9408. * @type {string}
  9409. */
  9410. var version$1 = '4.0.7';
  9411. /**
  9412. * Initializing a zrender instance
  9413. * @param {HTMLElement} dom
  9414. * @param {Object} [opts]
  9415. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  9416. * @param {number} [opts.devicePixelRatio]
  9417. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  9418. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  9419. * @return {module:zrender/ZRender}
  9420. */
  9421. function init$1(dom, opts) {
  9422. var zr = new ZRender(guid(), dom, opts);
  9423. instances$1[zr.id] = zr;
  9424. return zr;
  9425. }
  9426. /**
  9427. * Dispose zrender instance
  9428. * @param {module:zrender/ZRender} zr
  9429. */
  9430. function dispose$1(zr) {
  9431. if (zr) {
  9432. zr.dispose();
  9433. }
  9434. else {
  9435. for (var key in instances$1) {
  9436. if (instances$1.hasOwnProperty(key)) {
  9437. instances$1[key].dispose();
  9438. }
  9439. }
  9440. instances$1 = {};
  9441. }
  9442. return this;
  9443. }
  9444. /**
  9445. * Get zrender instance by id
  9446. * @param {string} id zrender instance id
  9447. * @return {module:zrender/ZRender}
  9448. */
  9449. function getInstance(id) {
  9450. return instances$1[id];
  9451. }
  9452. function registerPainter(name, Ctor) {
  9453. painterCtors[name] = Ctor;
  9454. }
  9455. function delInstance(id) {
  9456. delete instances$1[id];
  9457. }
  9458. /**
  9459. * @module zrender/ZRender
  9460. */
  9461. /**
  9462. * @constructor
  9463. * @alias module:zrender/ZRender
  9464. * @param {string} id
  9465. * @param {HTMLElement} dom
  9466. * @param {Object} opts
  9467. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  9468. * @param {number} [opts.devicePixelRatio]
  9469. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  9470. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  9471. */
  9472. var ZRender = function (id, dom, opts) {
  9473. opts = opts || {};
  9474. /**
  9475. * @type {HTMLDomElement}
  9476. */
  9477. this.dom = dom;
  9478. /**
  9479. * @type {string}
  9480. */
  9481. this.id = id;
  9482. var self = this;
  9483. var storage = new Storage();
  9484. var rendererType = opts.renderer;
  9485. // TODO WebGL
  9486. if (useVML) {
  9487. if (!painterCtors.vml) {
  9488. throw new Error('You need to require \'zrender/vml/vml\' to support IE8');
  9489. }
  9490. rendererType = 'vml';
  9491. }
  9492. else if (!rendererType || !painterCtors[rendererType]) {
  9493. rendererType = 'canvas';
  9494. }
  9495. var painter = new painterCtors[rendererType](dom, storage, opts, id);
  9496. this.storage = storage;
  9497. this.painter = painter;
  9498. var handerProxy = (!env$1.node && !env$1.worker) ? new HandlerDomProxy(painter.getViewportRoot()) : null;
  9499. this.handler = new Handler(storage, painter, handerProxy, painter.root);
  9500. /**
  9501. * @type {module:zrender/animation/Animation}
  9502. */
  9503. this.animation = new Animation({
  9504. stage: {
  9505. update: bind(this.flush, this)
  9506. }
  9507. });
  9508. this.animation.start();
  9509. /**
  9510. * @type {boolean}
  9511. * @private
  9512. */
  9513. this._needsRefresh;
  9514. // 修改 storage.delFromStorage, 每次删除元素之前删除动画
  9515. // FIXME 有点ugly
  9516. var oldDelFromStorage = storage.delFromStorage;
  9517. var oldAddToStorage = storage.addToStorage;
  9518. storage.delFromStorage = function (el) {
  9519. oldDelFromStorage.call(storage, el);
  9520. el && el.removeSelfFromZr(self);
  9521. };
  9522. storage.addToStorage = function (el) {
  9523. oldAddToStorage.call(storage, el);
  9524. el.addSelfToZr(self);
  9525. };
  9526. };
  9527. ZRender.prototype = {
  9528. constructor: ZRender,
  9529. /**
  9530. * 获取实例唯一标识
  9531. * @return {string}
  9532. */
  9533. getId: function () {
  9534. return this.id;
  9535. },
  9536. /**
  9537. * 添加元素
  9538. * @param {module:zrender/Element} el
  9539. */
  9540. add: function (el) {
  9541. this.storage.addRoot(el);
  9542. this._needsRefresh = true;
  9543. },
  9544. /**
  9545. * 删除元素
  9546. * @param {module:zrender/Element} el
  9547. */
  9548. remove: function (el) {
  9549. this.storage.delRoot(el);
  9550. this._needsRefresh = true;
  9551. },
  9552. /**
  9553. * Change configuration of layer
  9554. * @param {string} zLevel
  9555. * @param {Object} config
  9556. * @param {string} [config.clearColor=0] Clear color
  9557. * @param {string} [config.motionBlur=false] If enable motion blur
  9558. * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
  9559. */
  9560. configLayer: function (zLevel, config) {
  9561. if (this.painter.configLayer) {
  9562. this.painter.configLayer(zLevel, config);
  9563. }
  9564. this._needsRefresh = true;
  9565. },
  9566. /**
  9567. * Set background color
  9568. * @param {string} backgroundColor
  9569. */
  9570. setBackgroundColor: function (backgroundColor) {
  9571. if (this.painter.setBackgroundColor) {
  9572. this.painter.setBackgroundColor(backgroundColor);
  9573. }
  9574. this._needsRefresh = true;
  9575. },
  9576. /**
  9577. * Repaint the canvas immediately
  9578. */
  9579. refreshImmediately: function () {
  9580. // var start = new Date();
  9581. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  9582. // Or it will cause zrender refreshes again and again.
  9583. this._needsRefresh = false;
  9584. this.painter.refresh();
  9585. /**
  9586. * Avoid trigger zr.refresh in Element#beforeUpdate hook
  9587. */
  9588. this._needsRefresh = false;
  9589. // var end = new Date();
  9590. // var log = document.getElementById('log');
  9591. // if (log) {
  9592. // log.innerHTML = log.innerHTML + '<br>' + (end - start);
  9593. // }
  9594. },
  9595. /**
  9596. * Mark and repaint the canvas in the next frame of browser
  9597. */
  9598. refresh: function () {
  9599. this._needsRefresh = true;
  9600. },
  9601. /**
  9602. * Perform all refresh
  9603. */
  9604. flush: function () {
  9605. var triggerRendered;
  9606. if (this._needsRefresh) {
  9607. triggerRendered = true;
  9608. this.refreshImmediately();
  9609. }
  9610. if (this._needsRefreshHover) {
  9611. triggerRendered = true;
  9612. this.refreshHoverImmediately();
  9613. }
  9614. triggerRendered && this.trigger('rendered');
  9615. },
  9616. /**
  9617. * Add element to hover layer
  9618. * @param {module:zrender/Element} el
  9619. * @param {Object} style
  9620. */
  9621. addHover: function (el, style) {
  9622. if (this.painter.addHover) {
  9623. var elMirror = this.painter.addHover(el, style);
  9624. this.refreshHover();
  9625. return elMirror;
  9626. }
  9627. },
  9628. /**
  9629. * Add element from hover layer
  9630. * @param {module:zrender/Element} el
  9631. */
  9632. removeHover: function (el) {
  9633. if (this.painter.removeHover) {
  9634. this.painter.removeHover(el);
  9635. this.refreshHover();
  9636. }
  9637. },
  9638. /**
  9639. * Clear all hover elements in hover layer
  9640. * @param {module:zrender/Element} el
  9641. */
  9642. clearHover: function () {
  9643. if (this.painter.clearHover) {
  9644. this.painter.clearHover();
  9645. this.refreshHover();
  9646. }
  9647. },
  9648. /**
  9649. * Refresh hover in next frame
  9650. */
  9651. refreshHover: function () {
  9652. this._needsRefreshHover = true;
  9653. },
  9654. /**
  9655. * Refresh hover immediately
  9656. */
  9657. refreshHoverImmediately: function () {
  9658. this._needsRefreshHover = false;
  9659. this.painter.refreshHover && this.painter.refreshHover();
  9660. },
  9661. /**
  9662. * Resize the canvas.
  9663. * Should be invoked when container size is changed
  9664. * @param {Object} [opts]
  9665. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  9666. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  9667. */
  9668. resize: function (opts) {
  9669. opts = opts || {};
  9670. this.painter.resize(opts.width, opts.height);
  9671. this.handler.resize();
  9672. },
  9673. /**
  9674. * Stop and clear all animation immediately
  9675. */
  9676. clearAnimation: function () {
  9677. this.animation.clear();
  9678. },
  9679. /**
  9680. * Get container width
  9681. */
  9682. getWidth: function () {
  9683. return this.painter.getWidth();
  9684. },
  9685. /**
  9686. * Get container height
  9687. */
  9688. getHeight: function () {
  9689. return this.painter.getHeight();
  9690. },
  9691. /**
  9692. * Export the canvas as Base64 URL
  9693. * @param {string} type
  9694. * @param {string} [backgroundColor='#fff']
  9695. * @return {string} Base64 URL
  9696. */
  9697. // toDataURL: function(type, backgroundColor) {
  9698. // return this.painter.getRenderedCanvas({
  9699. // backgroundColor: backgroundColor
  9700. // }).toDataURL(type);
  9701. // },
  9702. /**
  9703. * Converting a path to image.
  9704. * It has much better performance of drawing image rather than drawing a vector path.
  9705. * @param {module:zrender/graphic/Path} e
  9706. * @param {number} width
  9707. * @param {number} height
  9708. */
  9709. pathToImage: function (e, dpr) {
  9710. return this.painter.pathToImage(e, dpr);
  9711. },
  9712. /**
  9713. * Set default cursor
  9714. * @param {string} [cursorStyle='default'] 例如 crosshair
  9715. */
  9716. setCursorStyle: function (cursorStyle) {
  9717. this.handler.setCursorStyle(cursorStyle);
  9718. },
  9719. /**
  9720. * Find hovered element
  9721. * @param {number} x
  9722. * @param {number} y
  9723. * @return {Object} {target, topTarget}
  9724. */
  9725. findHover: function (x, y) {
  9726. return this.handler.findHover(x, y);
  9727. },
  9728. /**
  9729. * Bind event
  9730. *
  9731. * @param {string} eventName Event name
  9732. * @param {Function} eventHandler Handler function
  9733. * @param {Object} [context] Context object
  9734. */
  9735. on: function (eventName, eventHandler, context) {
  9736. this.handler.on(eventName, eventHandler, context);
  9737. },
  9738. /**
  9739. * Unbind event
  9740. * @param {string} eventName Event name
  9741. * @param {Function} [eventHandler] Handler function
  9742. */
  9743. off: function (eventName, eventHandler) {
  9744. this.handler.off(eventName, eventHandler);
  9745. },
  9746. /**
  9747. * Trigger event manually
  9748. *
  9749. * @param {string} eventName Event name
  9750. * @param {event=} event Event object
  9751. */
  9752. trigger: function (eventName, event) {
  9753. this.handler.trigger(eventName, event);
  9754. },
  9755. /**
  9756. * Clear all objects and the canvas.
  9757. */
  9758. clear: function () {
  9759. this.storage.delRoot();
  9760. this.painter.clear();
  9761. },
  9762. /**
  9763. * Dispose self.
  9764. */
  9765. dispose: function () {
  9766. this.animation.stop();
  9767. this.clear();
  9768. this.storage.dispose();
  9769. this.painter.dispose();
  9770. this.handler.dispose();
  9771. this.animation =
  9772. this.storage =
  9773. this.painter =
  9774. this.handler = null;
  9775. delInstance(this.id);
  9776. }
  9777. };
  9778. var zrender = (Object.freeze || Object)({
  9779. version: version$1,
  9780. init: init$1,
  9781. dispose: dispose$1,
  9782. getInstance: getInstance,
  9783. registerPainter: registerPainter
  9784. });
  9785. /*
  9786. * Licensed to the Apache Software Foundation (ASF) under one
  9787. * or more contributor license agreements. See the NOTICE file
  9788. * distributed with this work for additional information
  9789. * regarding copyright ownership. The ASF licenses this file
  9790. * to you under the Apache License, Version 2.0 (the
  9791. * "License"); you may not use this file except in compliance
  9792. * with the License. You may obtain a copy of the License at
  9793. *
  9794. * http://www.apache.org/licenses/LICENSE-2.0
  9795. *
  9796. * Unless required by applicable law or agreed to in writing,
  9797. * software distributed under the License is distributed on an
  9798. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  9799. * KIND, either express or implied. See the License for the
  9800. * specific language governing permissions and limitations
  9801. * under the License.
  9802. */
  9803. var each$2 = each$1;
  9804. var isObject$2 = isObject$1;
  9805. var isArray$1 = isArray;
  9806. /**
  9807. * Make the name displayable. But we should
  9808. * make sure it is not duplicated with user
  9809. * specified name, so use '\0';
  9810. */
  9811. var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
  9812. /**
  9813. * If value is not array, then translate it to array.
  9814. * @param {*} value
  9815. * @return {Array} [value] or value
  9816. */
  9817. function normalizeToArray(value) {
  9818. return value instanceof Array
  9819. ? value
  9820. : value == null
  9821. ? []
  9822. : [value];
  9823. }
  9824. /**
  9825. * Sync default option between normal and emphasis like `position` and `show`
  9826. * In case some one will write code like
  9827. * label: {
  9828. * show: false,
  9829. * position: 'outside',
  9830. * fontSize: 18
  9831. * },
  9832. * emphasis: {
  9833. * label: { show: true }
  9834. * }
  9835. * @param {Object} opt
  9836. * @param {string} key
  9837. * @param {Array.<string>} subOpts
  9838. */
  9839. function defaultEmphasis(opt, key, subOpts) {
  9840. // Caution: performance sensitive.
  9841. if (opt) {
  9842. opt[key] = opt[key] || {};
  9843. opt.emphasis = opt.emphasis || {};
  9844. opt.emphasis[key] = opt.emphasis[key] || {};
  9845. // Default emphasis option from normal
  9846. for (var i = 0, len = subOpts.length; i < len; i++) {
  9847. var subOptName = subOpts[i];
  9848. if (!opt.emphasis[key].hasOwnProperty(subOptName)
  9849. && opt[key].hasOwnProperty(subOptName)
  9850. ) {
  9851. opt.emphasis[key][subOptName] = opt[key][subOptName];
  9852. }
  9853. }
  9854. }
  9855. }
  9856. var TEXT_STYLE_OPTIONS = [
  9857. 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  9858. 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth',
  9859. 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline',
  9860. 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY',
  9861. 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY',
  9862. 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'
  9863. ];
  9864. // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
  9865. // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
  9866. // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  9867. // // FIXME: deprecated, check and remove it.
  9868. // 'textStyle'
  9869. // ]);
  9870. /**
  9871. * The method do not ensure performance.
  9872. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  9873. * This helper method retieves value from data.
  9874. * @param {string|number|Date|Array|Object} dataItem
  9875. * @return {number|string|Date|Array.<number|string|Date>}
  9876. */
  9877. function getDataItemValue(dataItem) {
  9878. return (isObject$2(dataItem) && !isArray$1(dataItem) && !(dataItem instanceof Date))
  9879. ? dataItem.value : dataItem;
  9880. }
  9881. /**
  9882. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  9883. * This helper method determine if dataItem has extra option besides value
  9884. * @param {string|number|Date|Array|Object} dataItem
  9885. */
  9886. function isDataItemOption(dataItem) {
  9887. return isObject$2(dataItem)
  9888. && !(dataItem instanceof Array);
  9889. // // markLine data can be array
  9890. // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
  9891. }
  9892. /**
  9893. * Mapping to exists for merge.
  9894. *
  9895. * @public
  9896. * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
  9897. * @param {Object|Array.<Object>} newCptOptions
  9898. * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  9899. * index of which is the same as exists.
  9900. */
  9901. function mappingToExists(exists, newCptOptions) {
  9902. // Mapping by the order by original option (but not order of
  9903. // new option) in merge mode. Because we should ensure
  9904. // some specified index (like xAxisIndex) is consistent with
  9905. // original option, which is easy to understand, espatially in
  9906. // media query. And in most case, merge option is used to
  9907. // update partial option but not be expected to change order.
  9908. newCptOptions = (newCptOptions || []).slice();
  9909. var result = map(exists || [], function (obj, index) {
  9910. return {exist: obj};
  9911. });
  9912. // Mapping by id or name if specified.
  9913. each$2(newCptOptions, function (cptOption, index) {
  9914. if (!isObject$2(cptOption)) {
  9915. return;
  9916. }
  9917. // id has highest priority.
  9918. for (var i = 0; i < result.length; i++) {
  9919. if (!result[i].option // Consider name: two map to one.
  9920. && cptOption.id != null
  9921. && result[i].exist.id === cptOption.id + ''
  9922. ) {
  9923. result[i].option = cptOption;
  9924. newCptOptions[index] = null;
  9925. return;
  9926. }
  9927. }
  9928. for (var i = 0; i < result.length; i++) {
  9929. var exist = result[i].exist;
  9930. if (!result[i].option // Consider name: two map to one.
  9931. // Can not match when both ids exist but different.
  9932. && (exist.id == null || cptOption.id == null)
  9933. && cptOption.name != null
  9934. && !isIdInner(cptOption)
  9935. && !isIdInner(exist)
  9936. && exist.name === cptOption.name + ''
  9937. ) {
  9938. result[i].option = cptOption;
  9939. newCptOptions[index] = null;
  9940. return;
  9941. }
  9942. }
  9943. });
  9944. // Otherwise mapping by index.
  9945. each$2(newCptOptions, function (cptOption, index) {
  9946. if (!isObject$2(cptOption)) {
  9947. return;
  9948. }
  9949. var i = 0;
  9950. for (; i < result.length; i++) {
  9951. var exist = result[i].exist;
  9952. if (!result[i].option
  9953. // Existing model that already has id should be able to
  9954. // mapped to (because after mapping performed model may
  9955. // be assigned with a id, whish should not affect next
  9956. // mapping), except those has inner id.
  9957. && !isIdInner(exist)
  9958. // Caution:
  9959. // Do not overwrite id. But name can be overwritten,
  9960. // because axis use name as 'show label text'.
  9961. // 'exist' always has id and name and we dont
  9962. // need to check it.
  9963. && cptOption.id == null
  9964. ) {
  9965. result[i].option = cptOption;
  9966. break;
  9967. }
  9968. }
  9969. if (i >= result.length) {
  9970. result.push({option: cptOption});
  9971. }
  9972. });
  9973. return result;
  9974. }
  9975. /**
  9976. * Make id and name for mapping result (result of mappingToExists)
  9977. * into `keyInfo` field.
  9978. *
  9979. * @public
  9980. * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  9981. * which order is the same as exists.
  9982. * @return {Array.<Object>} The input.
  9983. */
  9984. function makeIdAndName(mapResult) {
  9985. // We use this id to hash component models and view instances
  9986. // in echarts. id can be specified by user, or auto generated.
  9987. // The id generation rule ensures new view instance are able
  9988. // to mapped to old instance when setOption are called in
  9989. // no-merge mode. So we generate model id by name and plus
  9990. // type in view id.
  9991. // name can be duplicated among components, which is convenient
  9992. // to specify multi components (like series) by one name.
  9993. // Ensure that each id is distinct.
  9994. var idMap = createHashMap();
  9995. each$2(mapResult, function (item, index) {
  9996. var existCpt = item.exist;
  9997. existCpt && idMap.set(existCpt.id, item);
  9998. });
  9999. each$2(mapResult, function (item, index) {
  10000. var opt = item.option;
  10001. assert$1(
  10002. !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item,
  10003. 'id duplicates: ' + (opt && opt.id)
  10004. );
  10005. opt && opt.id != null && idMap.set(opt.id, item);
  10006. !item.keyInfo && (item.keyInfo = {});
  10007. });
  10008. // Make name and id.
  10009. each$2(mapResult, function (item, index) {
  10010. var existCpt = item.exist;
  10011. var opt = item.option;
  10012. var keyInfo = item.keyInfo;
  10013. if (!isObject$2(opt)) {
  10014. return;
  10015. }
  10016. // name can be overwitten. Consider case: axis.name = '20km'.
  10017. // But id generated by name will not be changed, which affect
  10018. // only in that case: setOption with 'not merge mode' and view
  10019. // instance will be recreated, which can be accepted.
  10020. keyInfo.name = opt.name != null
  10021. ? opt.name + ''
  10022. : existCpt
  10023. ? existCpt.name
  10024. // Avoid diffferent series has the same name,
  10025. // because name may be used like in color pallet.
  10026. : DUMMY_COMPONENT_NAME_PREFIX + index;
  10027. if (existCpt) {
  10028. keyInfo.id = existCpt.id;
  10029. }
  10030. else if (opt.id != null) {
  10031. keyInfo.id = opt.id + '';
  10032. }
  10033. else {
  10034. // Consider this situatoin:
  10035. // optionA: [{name: 'a'}, {name: 'a'}, {..}]
  10036. // optionB [{..}, {name: 'a'}, {name: 'a'}]
  10037. // Series with the same name between optionA and optionB
  10038. // should be mapped.
  10039. var idNum = 0;
  10040. do {
  10041. keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
  10042. }
  10043. while (idMap.get(keyInfo.id));
  10044. }
  10045. idMap.set(keyInfo.id, item);
  10046. });
  10047. }
  10048. function isNameSpecified(componentModel) {
  10049. var name = componentModel.name;
  10050. // Is specified when `indexOf` get -1 or > 0.
  10051. return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
  10052. }
  10053. /**
  10054. * @public
  10055. * @param {Object} cptOption
  10056. * @return {boolean}
  10057. */
  10058. function isIdInner(cptOption) {
  10059. return isObject$2(cptOption)
  10060. && cptOption.id
  10061. && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
  10062. }
  10063. /**
  10064. * A helper for removing duplicate items between batchA and batchB,
  10065. * and in themselves, and categorize by series.
  10066. *
  10067. * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  10068. * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  10069. * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
  10070. */
  10071. /**
  10072. * @param {module:echarts/data/List} data
  10073. * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
  10074. * each of which can be Array or primary type.
  10075. * @return {number|Array.<number>} dataIndex If not found, return undefined/null.
  10076. */
  10077. function queryDataIndex(data, payload) {
  10078. if (payload.dataIndexInside != null) {
  10079. return payload.dataIndexInside;
  10080. }
  10081. else if (payload.dataIndex != null) {
  10082. return isArray(payload.dataIndex)
  10083. ? map(payload.dataIndex, function (value) {
  10084. return data.indexOfRawIndex(value);
  10085. })
  10086. : data.indexOfRawIndex(payload.dataIndex);
  10087. }
  10088. else if (payload.name != null) {
  10089. return isArray(payload.name)
  10090. ? map(payload.name, function (value) {
  10091. return data.indexOfName(value);
  10092. })
  10093. : data.indexOfName(payload.name);
  10094. }
  10095. }
  10096. /**
  10097. * Enable property storage to any host object.
  10098. * Notice: Serialization is not supported.
  10099. *
  10100. * For example:
  10101. * var inner = zrUitl.makeInner();
  10102. *
  10103. * function some1(hostObj) {
  10104. * inner(hostObj).someProperty = 1212;
  10105. * ...
  10106. * }
  10107. * function some2() {
  10108. * var fields = inner(this);
  10109. * fields.someProperty1 = 1212;
  10110. * fields.someProperty2 = 'xx';
  10111. * ...
  10112. * }
  10113. *
  10114. * @return {Function}
  10115. */
  10116. function makeInner() {
  10117. // Consider different scope by es module import.
  10118. var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5);
  10119. return function (hostObj) {
  10120. return hostObj[key] || (hostObj[key] = {});
  10121. };
  10122. }
  10123. var innerUniqueIndex = 0;
  10124. /**
  10125. * @param {module:echarts/model/Global} ecModel
  10126. * @param {string|Object} finder
  10127. * If string, e.g., 'geo', means {geoIndex: 0}.
  10128. * If Object, could contain some of these properties below:
  10129. * {
  10130. * seriesIndex, seriesId, seriesName,
  10131. * geoIndex, geoId, geoName,
  10132. * bmapIndex, bmapId, bmapName,
  10133. * xAxisIndex, xAxisId, xAxisName,
  10134. * yAxisIndex, yAxisId, yAxisName,
  10135. * gridIndex, gridId, gridName,
  10136. * ... (can be extended)
  10137. * }
  10138. * Each properties can be number|string|Array.<number>|Array.<string>
  10139. * For example, a finder could be
  10140. * {
  10141. * seriesIndex: 3,
  10142. * geoId: ['aa', 'cc'],
  10143. * gridName: ['xx', 'rr']
  10144. * }
  10145. * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
  10146. * If nothing or null/undefined specified, return nothing.
  10147. * @param {Object} [opt]
  10148. * @param {string} [opt.defaultMainType]
  10149. * @param {Array.<string>} [opt.includeMainTypes]
  10150. * @return {Object} result like:
  10151. * {
  10152. * seriesModels: [seriesModel1, seriesModel2],
  10153. * seriesModel: seriesModel1, // The first model
  10154. * geoModels: [geoModel1, geoModel2],
  10155. * geoModel: geoModel1, // The first model
  10156. * ...
  10157. * }
  10158. */
  10159. function parseFinder(ecModel, finder, opt) {
  10160. if (isString(finder)) {
  10161. var obj = {};
  10162. obj[finder + 'Index'] = 0;
  10163. finder = obj;
  10164. }
  10165. var defaultMainType = opt && opt.defaultMainType;
  10166. if (defaultMainType
  10167. && !has(finder, defaultMainType + 'Index')
  10168. && !has(finder, defaultMainType + 'Id')
  10169. && !has(finder, defaultMainType + 'Name')
  10170. ) {
  10171. finder[defaultMainType + 'Index'] = 0;
  10172. }
  10173. var result = {};
  10174. each$2(finder, function (value, key) {
  10175. var value = finder[key];
  10176. // Exclude 'dataIndex' and other illgal keys.
  10177. if (key === 'dataIndex' || key === 'dataIndexInside') {
  10178. result[key] = value;
  10179. return;
  10180. }
  10181. var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
  10182. var mainType = parsedKey[1];
  10183. var queryType = (parsedKey[2] || '').toLowerCase();
  10184. if (!mainType
  10185. || !queryType
  10186. || value == null
  10187. || (queryType === 'index' && value === 'none')
  10188. || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0)
  10189. ) {
  10190. return;
  10191. }
  10192. var queryParam = {mainType: mainType};
  10193. if (queryType !== 'index' || value !== 'all') {
  10194. queryParam[queryType] = value;
  10195. }
  10196. var models = ecModel.queryComponents(queryParam);
  10197. result[mainType + 'Models'] = models;
  10198. result[mainType + 'Model'] = models[0];
  10199. });
  10200. return result;
  10201. }
  10202. function has(obj, prop) {
  10203. return obj && obj.hasOwnProperty(prop);
  10204. }
  10205. function setAttribute(dom, key, value) {
  10206. dom.setAttribute
  10207. ? dom.setAttribute(key, value)
  10208. : (dom[key] = value);
  10209. }
  10210. function getAttribute(dom, key) {
  10211. return dom.getAttribute
  10212. ? dom.getAttribute(key)
  10213. : dom[key];
  10214. }
  10215. function getTooltipRenderMode(renderModeOption) {
  10216. if (renderModeOption === 'auto') {
  10217. // Using html when `document` exists, use richText otherwise
  10218. return env$1.domSupported ? 'html' : 'richText';
  10219. }
  10220. else {
  10221. return renderModeOption || 'html';
  10222. }
  10223. }
  10224. /**
  10225. * Group a list by key.
  10226. *
  10227. * @param {Array} array
  10228. * @param {Function} getKey
  10229. * param {*} Array item
  10230. * return {string} key
  10231. * @return {Object} Result
  10232. * {Array}: keys,
  10233. * {module:zrender/core/util/HashMap} buckets: {key -> Array}
  10234. */
  10235. /*
  10236. * Licensed to the Apache Software Foundation (ASF) under one
  10237. * or more contributor license agreements. See the NOTICE file
  10238. * distributed with this work for additional information
  10239. * regarding copyright ownership. The ASF licenses this file
  10240. * to you under the Apache License, Version 2.0 (the
  10241. * "License"); you may not use this file except in compliance
  10242. * with the License. You may obtain a copy of the License at
  10243. *
  10244. * http://www.apache.org/licenses/LICENSE-2.0
  10245. *
  10246. * Unless required by applicable law or agreed to in writing,
  10247. * software distributed under the License is distributed on an
  10248. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  10249. * KIND, either express or implied. See the License for the
  10250. * specific language governing permissions and limitations
  10251. * under the License.
  10252. */
  10253. var TYPE_DELIMITER = '.';
  10254. var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
  10255. /**
  10256. * Notice, parseClassType('') should returns {main: '', sub: ''}
  10257. * @public
  10258. */
  10259. function parseClassType$1(componentType) {
  10260. var ret = {main: '', sub: ''};
  10261. if (componentType) {
  10262. componentType = componentType.split(TYPE_DELIMITER);
  10263. ret.main = componentType[0] || '';
  10264. ret.sub = componentType[1] || '';
  10265. }
  10266. return ret;
  10267. }
  10268. /**
  10269. * @public
  10270. */
  10271. function checkClassType(componentType) {
  10272. assert$1(
  10273. /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),
  10274. 'componentType "' + componentType + '" illegal'
  10275. );
  10276. }
  10277. /**
  10278. * @public
  10279. */
  10280. function enableClassExtend(RootClass, mandatoryMethods) {
  10281. RootClass.$constructor = RootClass;
  10282. RootClass.extend = function (proto) {
  10283. if (__DEV__) {
  10284. each$1(mandatoryMethods, function (method) {
  10285. if (!proto[method]) {
  10286. console.warn(
  10287. 'Method `' + method + '` should be implemented'
  10288. + (proto.type ? ' in ' + proto.type : '') + '.'
  10289. );
  10290. }
  10291. });
  10292. }
  10293. var superClass = this;
  10294. var ExtendedClass = function () {
  10295. if (!proto.$constructor) {
  10296. superClass.apply(this, arguments);
  10297. }
  10298. else {
  10299. proto.$constructor.apply(this, arguments);
  10300. }
  10301. };
  10302. extend(ExtendedClass.prototype, proto);
  10303. ExtendedClass.extend = this.extend;
  10304. ExtendedClass.superCall = superCall;
  10305. ExtendedClass.superApply = superApply;
  10306. inherits(ExtendedClass, this);
  10307. ExtendedClass.superClass = superClass;
  10308. return ExtendedClass;
  10309. };
  10310. }
  10311. var classBase = 0;
  10312. /**
  10313. * Can not use instanceof, consider different scope by
  10314. * cross domain or es module import in ec extensions.
  10315. * Mount a method "isInstance()" to Clz.
  10316. */
  10317. function enableClassCheck(Clz) {
  10318. var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_');
  10319. Clz.prototype[classAttr] = true;
  10320. if (__DEV__) {
  10321. assert$1(!Clz.isInstance, 'The method "is" can not be defined.');
  10322. }
  10323. Clz.isInstance = function (obj) {
  10324. return !!(obj && obj[classAttr]);
  10325. };
  10326. }
  10327. // superCall should have class info, which can not be fetch from 'this'.
  10328. // Consider this case:
  10329. // class A has method f,
  10330. // class B inherits class A, overrides method f, f call superApply('f'),
  10331. // class C inherits class B, do not overrides method f,
  10332. // then when method of class C is called, dead loop occured.
  10333. function superCall(context, methodName) {
  10334. var args = slice(arguments, 2);
  10335. return this.superClass.prototype[methodName].apply(context, args);
  10336. }
  10337. function superApply(context, methodName, args) {
  10338. return this.superClass.prototype[methodName].apply(context, args);
  10339. }
  10340. /**
  10341. * @param {Object} entity
  10342. * @param {Object} options
  10343. * @param {boolean} [options.registerWhenExtend]
  10344. * @public
  10345. */
  10346. function enableClassManagement(entity, options) {
  10347. options = options || {};
  10348. /**
  10349. * Component model classes
  10350. * key: componentType,
  10351. * value:
  10352. * componentClass, when componentType is 'xxx'
  10353. * or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
  10354. * @type {Object}
  10355. */
  10356. var storage = {};
  10357. entity.registerClass = function (Clazz, componentType) {
  10358. if (componentType) {
  10359. checkClassType(componentType);
  10360. componentType = parseClassType$1(componentType);
  10361. if (!componentType.sub) {
  10362. if (__DEV__) {
  10363. if (storage[componentType.main]) {
  10364. console.warn(componentType.main + ' exists.');
  10365. }
  10366. }
  10367. storage[componentType.main] = Clazz;
  10368. }
  10369. else if (componentType.sub !== IS_CONTAINER) {
  10370. var container = makeContainer(componentType);
  10371. container[componentType.sub] = Clazz;
  10372. }
  10373. }
  10374. return Clazz;
  10375. };
  10376. entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
  10377. var Clazz = storage[componentMainType];
  10378. if (Clazz && Clazz[IS_CONTAINER]) {
  10379. Clazz = subType ? Clazz[subType] : null;
  10380. }
  10381. if (throwWhenNotFound && !Clazz) {
  10382. throw new Error(
  10383. !subType
  10384. ? componentMainType + '.' + 'type should be specified.'
  10385. : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'
  10386. );
  10387. }
  10388. return Clazz;
  10389. };
  10390. entity.getClassesByMainType = function (componentType) {
  10391. componentType = parseClassType$1(componentType);
  10392. var result = [];
  10393. var obj = storage[componentType.main];
  10394. if (obj && obj[IS_CONTAINER]) {
  10395. each$1(obj, function (o, type) {
  10396. type !== IS_CONTAINER && result.push(o);
  10397. });
  10398. }
  10399. else {
  10400. result.push(obj);
  10401. }
  10402. return result;
  10403. };
  10404. entity.hasClass = function (componentType) {
  10405. // Just consider componentType.main.
  10406. componentType = parseClassType$1(componentType);
  10407. return !!storage[componentType.main];
  10408. };
  10409. /**
  10410. * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
  10411. */
  10412. entity.getAllClassMainTypes = function () {
  10413. var types = [];
  10414. each$1(storage, function (obj, type) {
  10415. types.push(type);
  10416. });
  10417. return types;
  10418. };
  10419. /**
  10420. * If a main type is container and has sub types
  10421. * @param {string} mainType
  10422. * @return {boolean}
  10423. */
  10424. entity.hasSubTypes = function (componentType) {
  10425. componentType = parseClassType$1(componentType);
  10426. var obj = storage[componentType.main];
  10427. return obj && obj[IS_CONTAINER];
  10428. };
  10429. entity.parseClassType = parseClassType$1;
  10430. function makeContainer(componentType) {
  10431. var container = storage[componentType.main];
  10432. if (!container || !container[IS_CONTAINER]) {
  10433. container = storage[componentType.main] = {};
  10434. container[IS_CONTAINER] = true;
  10435. }
  10436. return container;
  10437. }
  10438. if (options.registerWhenExtend) {
  10439. var originalExtend = entity.extend;
  10440. if (originalExtend) {
  10441. entity.extend = function (proto) {
  10442. var ExtendedClass = originalExtend.call(this, proto);
  10443. return entity.registerClass(ExtendedClass, proto.type);
  10444. };
  10445. }
  10446. }
  10447. return entity;
  10448. }
  10449. /**
  10450. * @param {string|Array.<string>} properties
  10451. */
  10452. /*
  10453. * Licensed to the Apache Software Foundation (ASF) under one
  10454. * or more contributor license agreements. See the NOTICE file
  10455. * distributed with this work for additional information
  10456. * regarding copyright ownership. The ASF licenses this file
  10457. * to you under the Apache License, Version 2.0 (the
  10458. * "License"); you may not use this file except in compliance
  10459. * with the License. You may obtain a copy of the License at
  10460. *
  10461. * http://www.apache.org/licenses/LICENSE-2.0
  10462. *
  10463. * Unless required by applicable law or agreed to in writing,
  10464. * software distributed under the License is distributed on an
  10465. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  10466. * KIND, either express or implied. See the License for the
  10467. * specific language governing permissions and limitations
  10468. * under the License.
  10469. */
  10470. // TODO Parse shadow style
  10471. // TODO Only shallow path support
  10472. var makeStyleMapper = function (properties) {
  10473. // Normalize
  10474. for (var i = 0; i < properties.length; i++) {
  10475. if (!properties[i][1]) {
  10476. properties[i][1] = properties[i][0];
  10477. }
  10478. }
  10479. return function (model, excludes, includes) {
  10480. var style = {};
  10481. for (var i = 0; i < properties.length; i++) {
  10482. var propName = properties[i][1];
  10483. if ((excludes && indexOf(excludes, propName) >= 0)
  10484. || (includes && indexOf(includes, propName) < 0)
  10485. ) {
  10486. continue;
  10487. }
  10488. var val = model.getShallow(propName);
  10489. if (val != null) {
  10490. style[properties[i][0]] = val;
  10491. }
  10492. }
  10493. return style;
  10494. };
  10495. };
  10496. /*
  10497. * Licensed to the Apache Software Foundation (ASF) under one
  10498. * or more contributor license agreements. See the NOTICE file
  10499. * distributed with this work for additional information
  10500. * regarding copyright ownership. The ASF licenses this file
  10501. * to you under the Apache License, Version 2.0 (the
  10502. * "License"); you may not use this file except in compliance
  10503. * with the License. You may obtain a copy of the License at
  10504. *
  10505. * http://www.apache.org/licenses/LICENSE-2.0
  10506. *
  10507. * Unless required by applicable law or agreed to in writing,
  10508. * software distributed under the License is distributed on an
  10509. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  10510. * KIND, either express or implied. See the License for the
  10511. * specific language governing permissions and limitations
  10512. * under the License.
  10513. */
  10514. var getLineStyle = makeStyleMapper(
  10515. [
  10516. ['lineWidth', 'width'],
  10517. ['stroke', 'color'],
  10518. ['opacity'],
  10519. ['shadowBlur'],
  10520. ['shadowOffsetX'],
  10521. ['shadowOffsetY'],
  10522. ['shadowColor']
  10523. ]
  10524. );
  10525. var lineStyleMixin = {
  10526. getLineStyle: function (excludes) {
  10527. var style = getLineStyle(this, excludes);
  10528. var lineDash = this.getLineDash(style.lineWidth);
  10529. lineDash && (style.lineDash = lineDash);
  10530. return style;
  10531. },
  10532. getLineDash: function (lineWidth) {
  10533. if (lineWidth == null) {
  10534. lineWidth = 1;
  10535. }
  10536. var lineType = this.get('type');
  10537. var dotSize = Math.max(lineWidth, 2);
  10538. var dashSize = lineWidth * 4;
  10539. return (lineType === 'solid' || lineType == null) ? null
  10540. : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);
  10541. }
  10542. };
  10543. /*
  10544. * Licensed to the Apache Software Foundation (ASF) under one
  10545. * or more contributor license agreements. See the NOTICE file
  10546. * distributed with this work for additional information
  10547. * regarding copyright ownership. The ASF licenses this file
  10548. * to you under the Apache License, Version 2.0 (the
  10549. * "License"); you may not use this file except in compliance
  10550. * with the License. You may obtain a copy of the License at
  10551. *
  10552. * http://www.apache.org/licenses/LICENSE-2.0
  10553. *
  10554. * Unless required by applicable law or agreed to in writing,
  10555. * software distributed under the License is distributed on an
  10556. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  10557. * KIND, either express or implied. See the License for the
  10558. * specific language governing permissions and limitations
  10559. * under the License.
  10560. */
  10561. var getAreaStyle = makeStyleMapper(
  10562. [
  10563. ['fill', 'color'],
  10564. ['shadowBlur'],
  10565. ['shadowOffsetX'],
  10566. ['shadowOffsetY'],
  10567. ['opacity'],
  10568. ['shadowColor']
  10569. ]
  10570. );
  10571. var areaStyleMixin = {
  10572. getAreaStyle: function (excludes, includes) {
  10573. return getAreaStyle(this, excludes, includes);
  10574. }
  10575. };
  10576. /**
  10577. * 曲线辅助模块
  10578. * @module zrender/core/curve
  10579. * @author pissang(https://www.github.com/pissang)
  10580. */
  10581. var mathPow = Math.pow;
  10582. var mathSqrt$2 = Math.sqrt;
  10583. var EPSILON$1 = 1e-8;
  10584. var EPSILON_NUMERIC = 1e-4;
  10585. var THREE_SQRT = mathSqrt$2(3);
  10586. var ONE_THIRD = 1 / 3;
  10587. // 临时变量
  10588. var _v0 = create();
  10589. var _v1 = create();
  10590. var _v2 = create();
  10591. function isAroundZero(val) {
  10592. return val > -EPSILON$1 && val < EPSILON$1;
  10593. }
  10594. function isNotAroundZero$1(val) {
  10595. return val > EPSILON$1 || val < -EPSILON$1;
  10596. }
  10597. /**
  10598. * 计算三次贝塞尔值
  10599. * @memberOf module:zrender/core/curve
  10600. * @param {number} p0
  10601. * @param {number} p1
  10602. * @param {number} p2
  10603. * @param {number} p3
  10604. * @param {number} t
  10605. * @return {number}
  10606. */
  10607. function cubicAt(p0, p1, p2, p3, t) {
  10608. var onet = 1 - t;
  10609. return onet * onet * (onet * p0 + 3 * t * p1)
  10610. + t * t * (t * p3 + 3 * onet * p2);
  10611. }
  10612. /**
  10613. * 计算三次贝塞尔导数值
  10614. * @memberOf module:zrender/core/curve
  10615. * @param {number} p0
  10616. * @param {number} p1
  10617. * @param {number} p2
  10618. * @param {number} p3
  10619. * @param {number} t
  10620. * @return {number}
  10621. */
  10622. function cubicDerivativeAt(p0, p1, p2, p3, t) {
  10623. var onet = 1 - t;
  10624. return 3 * (
  10625. ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet
  10626. + (p3 - p2) * t * t
  10627. );
  10628. }
  10629. /**
  10630. * 计算三次贝塞尔方程根,使用盛金公式
  10631. * @memberOf module:zrender/core/curve
  10632. * @param {number} p0
  10633. * @param {number} p1
  10634. * @param {number} p2
  10635. * @param {number} p3
  10636. * @param {number} val
  10637. * @param {Array.<number>} roots
  10638. * @return {number} 有效根数目
  10639. */
  10640. function cubicRootAt(p0, p1, p2, p3, val, roots) {
  10641. // Evaluate roots of cubic functions
  10642. var a = p3 + 3 * (p1 - p2) - p0;
  10643. var b = 3 * (p2 - p1 * 2 + p0);
  10644. var c = 3 * (p1 - p0);
  10645. var d = p0 - val;
  10646. var A = b * b - 3 * a * c;
  10647. var B = b * c - 9 * a * d;
  10648. var C = c * c - 3 * b * d;
  10649. var n = 0;
  10650. if (isAroundZero(A) && isAroundZero(B)) {
  10651. if (isAroundZero(b)) {
  10652. roots[0] = 0;
  10653. }
  10654. else {
  10655. var t1 = -c / b; //t1, t2, t3, b is not zero
  10656. if (t1 >= 0 && t1 <= 1) {
  10657. roots[n++] = t1;
  10658. }
  10659. }
  10660. }
  10661. else {
  10662. var disc = B * B - 4 * A * C;
  10663. if (isAroundZero(disc)) {
  10664. var K = B / A;
  10665. var t1 = -b / a + K; // t1, a is not zero
  10666. var t2 = -K / 2; // t2, t3
  10667. if (t1 >= 0 && t1 <= 1) {
  10668. roots[n++] = t1;
  10669. }
  10670. if (t2 >= 0 && t2 <= 1) {
  10671. roots[n++] = t2;
  10672. }
  10673. }
  10674. else if (disc > 0) {
  10675. var discSqrt = mathSqrt$2(disc);
  10676. var Y1 = A * b + 1.5 * a * (-B + discSqrt);
  10677. var Y2 = A * b + 1.5 * a * (-B - discSqrt);
  10678. if (Y1 < 0) {
  10679. Y1 = -mathPow(-Y1, ONE_THIRD);
  10680. }
  10681. else {
  10682. Y1 = mathPow(Y1, ONE_THIRD);
  10683. }
  10684. if (Y2 < 0) {
  10685. Y2 = -mathPow(-Y2, ONE_THIRD);
  10686. }
  10687. else {
  10688. Y2 = mathPow(Y2, ONE_THIRD);
  10689. }
  10690. var t1 = (-b - (Y1 + Y2)) / (3 * a);
  10691. if (t1 >= 0 && t1 <= 1) {
  10692. roots[n++] = t1;
  10693. }
  10694. }
  10695. else {
  10696. var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A));
  10697. var theta = Math.acos(T) / 3;
  10698. var ASqrt = mathSqrt$2(A);
  10699. var tmp = Math.cos(theta);
  10700. var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
  10701. var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
  10702. var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
  10703. if (t1 >= 0 && t1 <= 1) {
  10704. roots[n++] = t1;
  10705. }
  10706. if (t2 >= 0 && t2 <= 1) {
  10707. roots[n++] = t2;
  10708. }
  10709. if (t3 >= 0 && t3 <= 1) {
  10710. roots[n++] = t3;
  10711. }
  10712. }
  10713. }
  10714. return n;
  10715. }
  10716. /**
  10717. * 计算三次贝塞尔方程极限值的位置
  10718. * @memberOf module:zrender/core/curve
  10719. * @param {number} p0
  10720. * @param {number} p1
  10721. * @param {number} p2
  10722. * @param {number} p3
  10723. * @param {Array.<number>} extrema
  10724. * @return {number} 有效数目
  10725. */
  10726. function cubicExtrema(p0, p1, p2, p3, extrema) {
  10727. var b = 6 * p2 - 12 * p1 + 6 * p0;
  10728. var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
  10729. var c = 3 * p1 - 3 * p0;
  10730. var n = 0;
  10731. if (isAroundZero(a)) {
  10732. if (isNotAroundZero$1(b)) {
  10733. var t1 = -c / b;
  10734. if (t1 >= 0 && t1 <= 1) {
  10735. extrema[n++] = t1;
  10736. }
  10737. }
  10738. }
  10739. else {
  10740. var disc = b * b - 4 * a * c;
  10741. if (isAroundZero(disc)) {
  10742. extrema[0] = -b / (2 * a);
  10743. }
  10744. else if (disc > 0) {
  10745. var discSqrt = mathSqrt$2(disc);
  10746. var t1 = (-b + discSqrt) / (2 * a);
  10747. var t2 = (-b - discSqrt) / (2 * a);
  10748. if (t1 >= 0 && t1 <= 1) {
  10749. extrema[n++] = t1;
  10750. }
  10751. if (t2 >= 0 && t2 <= 1) {
  10752. extrema[n++] = t2;
  10753. }
  10754. }
  10755. }
  10756. return n;
  10757. }
  10758. /**
  10759. * 细分三次贝塞尔曲线
  10760. * @memberOf module:zrender/core/curve
  10761. * @param {number} p0
  10762. * @param {number} p1
  10763. * @param {number} p2
  10764. * @param {number} p3
  10765. * @param {number} t
  10766. * @param {Array.<number>} out
  10767. */
  10768. function cubicSubdivide(p0, p1, p2, p3, t, out) {
  10769. var p01 = (p1 - p0) * t + p0;
  10770. var p12 = (p2 - p1) * t + p1;
  10771. var p23 = (p3 - p2) * t + p2;
  10772. var p012 = (p12 - p01) * t + p01;
  10773. var p123 = (p23 - p12) * t + p12;
  10774. var p0123 = (p123 - p012) * t + p012;
  10775. // Seg0
  10776. out[0] = p0;
  10777. out[1] = p01;
  10778. out[2] = p012;
  10779. out[3] = p0123;
  10780. // Seg1
  10781. out[4] = p0123;
  10782. out[5] = p123;
  10783. out[6] = p23;
  10784. out[7] = p3;
  10785. }
  10786. /**
  10787. * 投射点到三次贝塞尔曲线上,返回投射距离。
  10788. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  10789. * @param {number} x0
  10790. * @param {number} y0
  10791. * @param {number} x1
  10792. * @param {number} y1
  10793. * @param {number} x2
  10794. * @param {number} y2
  10795. * @param {number} x3
  10796. * @param {number} y3
  10797. * @param {number} x
  10798. * @param {number} y
  10799. * @param {Array.<number>} [out] 投射点
  10800. * @return {number}
  10801. */
  10802. function cubicProjectPoint(
  10803. x0, y0, x1, y1, x2, y2, x3, y3,
  10804. x, y, out
  10805. ) {
  10806. // http://pomax.github.io/bezierinfo/#projections
  10807. var t;
  10808. var interval = 0.005;
  10809. var d = Infinity;
  10810. var prev;
  10811. var next;
  10812. var d1;
  10813. var d2;
  10814. _v0[0] = x;
  10815. _v0[1] = y;
  10816. // 先粗略估计一下可能的最小距离的 t 值
  10817. // PENDING
  10818. for (var _t = 0; _t < 1; _t += 0.05) {
  10819. _v1[0] = cubicAt(x0, x1, x2, x3, _t);
  10820. _v1[1] = cubicAt(y0, y1, y2, y3, _t);
  10821. d1 = distSquare(_v0, _v1);
  10822. if (d1 < d) {
  10823. t = _t;
  10824. d = d1;
  10825. }
  10826. }
  10827. d = Infinity;
  10828. // At most 32 iteration
  10829. for (var i = 0; i < 32; i++) {
  10830. if (interval < EPSILON_NUMERIC) {
  10831. break;
  10832. }
  10833. prev = t - interval;
  10834. next = t + interval;
  10835. // t - interval
  10836. _v1[0] = cubicAt(x0, x1, x2, x3, prev);
  10837. _v1[1] = cubicAt(y0, y1, y2, y3, prev);
  10838. d1 = distSquare(_v1, _v0);
  10839. if (prev >= 0 && d1 < d) {
  10840. t = prev;
  10841. d = d1;
  10842. }
  10843. else {
  10844. // t + interval
  10845. _v2[0] = cubicAt(x0, x1, x2, x3, next);
  10846. _v2[1] = cubicAt(y0, y1, y2, y3, next);
  10847. d2 = distSquare(_v2, _v0);
  10848. if (next <= 1 && d2 < d) {
  10849. t = next;
  10850. d = d2;
  10851. }
  10852. else {
  10853. interval *= 0.5;
  10854. }
  10855. }
  10856. }
  10857. // t
  10858. if (out) {
  10859. out[0] = cubicAt(x0, x1, x2, x3, t);
  10860. out[1] = cubicAt(y0, y1, y2, y3, t);
  10861. }
  10862. // console.log(interval, i);
  10863. return mathSqrt$2(d);
  10864. }
  10865. /**
  10866. * 计算二次方贝塞尔值
  10867. * @param {number} p0
  10868. * @param {number} p1
  10869. * @param {number} p2
  10870. * @param {number} t
  10871. * @return {number}
  10872. */
  10873. function quadraticAt(p0, p1, p2, t) {
  10874. var onet = 1 - t;
  10875. return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
  10876. }
  10877. /**
  10878. * 计算二次方贝塞尔导数值
  10879. * @param {number} p0
  10880. * @param {number} p1
  10881. * @param {number} p2
  10882. * @param {number} t
  10883. * @return {number}
  10884. */
  10885. function quadraticDerivativeAt(p0, p1, p2, t) {
  10886. return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
  10887. }
  10888. /**
  10889. * 计算二次方贝塞尔方程根
  10890. * @param {number} p0
  10891. * @param {number} p1
  10892. * @param {number} p2
  10893. * @param {number} t
  10894. * @param {Array.<number>} roots
  10895. * @return {number} 有效根数目
  10896. */
  10897. function quadraticRootAt(p0, p1, p2, val, roots) {
  10898. var a = p0 - 2 * p1 + p2;
  10899. var b = 2 * (p1 - p0);
  10900. var c = p0 - val;
  10901. var n = 0;
  10902. if (isAroundZero(a)) {
  10903. if (isNotAroundZero$1(b)) {
  10904. var t1 = -c / b;
  10905. if (t1 >= 0 && t1 <= 1) {
  10906. roots[n++] = t1;
  10907. }
  10908. }
  10909. }
  10910. else {
  10911. var disc = b * b - 4 * a * c;
  10912. if (isAroundZero(disc)) {
  10913. var t1 = -b / (2 * a);
  10914. if (t1 >= 0 && t1 <= 1) {
  10915. roots[n++] = t1;
  10916. }
  10917. }
  10918. else if (disc > 0) {
  10919. var discSqrt = mathSqrt$2(disc);
  10920. var t1 = (-b + discSqrt) / (2 * a);
  10921. var t2 = (-b - discSqrt) / (2 * a);
  10922. if (t1 >= 0 && t1 <= 1) {
  10923. roots[n++] = t1;
  10924. }
  10925. if (t2 >= 0 && t2 <= 1) {
  10926. roots[n++] = t2;
  10927. }
  10928. }
  10929. }
  10930. return n;
  10931. }
  10932. /**
  10933. * 计算二次贝塞尔方程极限值
  10934. * @memberOf module:zrender/core/curve
  10935. * @param {number} p0
  10936. * @param {number} p1
  10937. * @param {number} p2
  10938. * @return {number}
  10939. */
  10940. function quadraticExtremum(p0, p1, p2) {
  10941. var divider = p0 + p2 - 2 * p1;
  10942. if (divider === 0) {
  10943. // p1 is center of p0 and p2
  10944. return 0.5;
  10945. }
  10946. else {
  10947. return (p0 - p1) / divider;
  10948. }
  10949. }
  10950. /**
  10951. * 细分二次贝塞尔曲线
  10952. * @memberOf module:zrender/core/curve
  10953. * @param {number} p0
  10954. * @param {number} p1
  10955. * @param {number} p2
  10956. * @param {number} t
  10957. * @param {Array.<number>} out
  10958. */
  10959. function quadraticSubdivide(p0, p1, p2, t, out) {
  10960. var p01 = (p1 - p0) * t + p0;
  10961. var p12 = (p2 - p1) * t + p1;
  10962. var p012 = (p12 - p01) * t + p01;
  10963. // Seg0
  10964. out[0] = p0;
  10965. out[1] = p01;
  10966. out[2] = p012;
  10967. // Seg1
  10968. out[3] = p012;
  10969. out[4] = p12;
  10970. out[5] = p2;
  10971. }
  10972. /**
  10973. * 投射点到二次贝塞尔曲线上,返回投射距离。
  10974. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  10975. * @param {number} x0
  10976. * @param {number} y0
  10977. * @param {number} x1
  10978. * @param {number} y1
  10979. * @param {number} x2
  10980. * @param {number} y2
  10981. * @param {number} x
  10982. * @param {number} y
  10983. * @param {Array.<number>} out 投射点
  10984. * @return {number}
  10985. */
  10986. function quadraticProjectPoint(
  10987. x0, y0, x1, y1, x2, y2,
  10988. x, y, out
  10989. ) {
  10990. // http://pomax.github.io/bezierinfo/#projections
  10991. var t;
  10992. var interval = 0.005;
  10993. var d = Infinity;
  10994. _v0[0] = x;
  10995. _v0[1] = y;
  10996. // 先粗略估计一下可能的最小距离的 t 值
  10997. // PENDING
  10998. for (var _t = 0; _t < 1; _t += 0.05) {
  10999. _v1[0] = quadraticAt(x0, x1, x2, _t);
  11000. _v1[1] = quadraticAt(y0, y1, y2, _t);
  11001. var d1 = distSquare(_v0, _v1);
  11002. if (d1 < d) {
  11003. t = _t;
  11004. d = d1;
  11005. }
  11006. }
  11007. d = Infinity;
  11008. // At most 32 iteration
  11009. for (var i = 0; i < 32; i++) {
  11010. if (interval < EPSILON_NUMERIC) {
  11011. break;
  11012. }
  11013. var prev = t - interval;
  11014. var next = t + interval;
  11015. // t - interval
  11016. _v1[0] = quadraticAt(x0, x1, x2, prev);
  11017. _v1[1] = quadraticAt(y0, y1, y2, prev);
  11018. var d1 = distSquare(_v1, _v0);
  11019. if (prev >= 0 && d1 < d) {
  11020. t = prev;
  11021. d = d1;
  11022. }
  11023. else {
  11024. // t + interval
  11025. _v2[0] = quadraticAt(x0, x1, x2, next);
  11026. _v2[1] = quadraticAt(y0, y1, y2, next);
  11027. var d2 = distSquare(_v2, _v0);
  11028. if (next <= 1 && d2 < d) {
  11029. t = next;
  11030. d = d2;
  11031. }
  11032. else {
  11033. interval *= 0.5;
  11034. }
  11035. }
  11036. }
  11037. // t
  11038. if (out) {
  11039. out[0] = quadraticAt(x0, x1, x2, t);
  11040. out[1] = quadraticAt(y0, y1, y2, t);
  11041. }
  11042. // console.log(interval, i);
  11043. return mathSqrt$2(d);
  11044. }
  11045. /**
  11046. * @author Yi Shen(https://github.com/pissang)
  11047. */
  11048. var mathMin$3 = Math.min;
  11049. var mathMax$3 = Math.max;
  11050. var mathSin$2 = Math.sin;
  11051. var mathCos$2 = Math.cos;
  11052. var PI2 = Math.PI * 2;
  11053. var start = create();
  11054. var end = create();
  11055. var extremity = create();
  11056. /**
  11057. * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
  11058. * @module zrender/core/bbox
  11059. * @param {Array<Object>} points 顶点数组
  11060. * @param {number} min
  11061. * @param {number} max
  11062. */
  11063. function fromPoints(points, min$$1, max$$1) {
  11064. if (points.length === 0) {
  11065. return;
  11066. }
  11067. var p = points[0];
  11068. var left = p[0];
  11069. var right = p[0];
  11070. var top = p[1];
  11071. var bottom = p[1];
  11072. var i;
  11073. for (i = 1; i < points.length; i++) {
  11074. p = points[i];
  11075. left = mathMin$3(left, p[0]);
  11076. right = mathMax$3(right, p[0]);
  11077. top = mathMin$3(top, p[1]);
  11078. bottom = mathMax$3(bottom, p[1]);
  11079. }
  11080. min$$1[0] = left;
  11081. min$$1[1] = top;
  11082. max$$1[0] = right;
  11083. max$$1[1] = bottom;
  11084. }
  11085. /**
  11086. * @memberOf module:zrender/core/bbox
  11087. * @param {number} x0
  11088. * @param {number} y0
  11089. * @param {number} x1
  11090. * @param {number} y1
  11091. * @param {Array.<number>} min
  11092. * @param {Array.<number>} max
  11093. */
  11094. function fromLine(x0, y0, x1, y1, min$$1, max$$1) {
  11095. min$$1[0] = mathMin$3(x0, x1);
  11096. min$$1[1] = mathMin$3(y0, y1);
  11097. max$$1[0] = mathMax$3(x0, x1);
  11098. max$$1[1] = mathMax$3(y0, y1);
  11099. }
  11100. var xDim = [];
  11101. var yDim = [];
  11102. /**
  11103. * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
  11104. * @memberOf module:zrender/core/bbox
  11105. * @param {number} x0
  11106. * @param {number} y0
  11107. * @param {number} x1
  11108. * @param {number} y1
  11109. * @param {number} x2
  11110. * @param {number} y2
  11111. * @param {number} x3
  11112. * @param {number} y3
  11113. * @param {Array.<number>} min
  11114. * @param {Array.<number>} max
  11115. */
  11116. function fromCubic(
  11117. x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1
  11118. ) {
  11119. var cubicExtrema$$1 = cubicExtrema;
  11120. var cubicAt$$1 = cubicAt;
  11121. var i;
  11122. var n = cubicExtrema$$1(x0, x1, x2, x3, xDim);
  11123. min$$1[0] = Infinity;
  11124. min$$1[1] = Infinity;
  11125. max$$1[0] = -Infinity;
  11126. max$$1[1] = -Infinity;
  11127. for (i = 0; i < n; i++) {
  11128. var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]);
  11129. min$$1[0] = mathMin$3(x, min$$1[0]);
  11130. max$$1[0] = mathMax$3(x, max$$1[0]);
  11131. }
  11132. n = cubicExtrema$$1(y0, y1, y2, y3, yDim);
  11133. for (i = 0; i < n; i++) {
  11134. var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]);
  11135. min$$1[1] = mathMin$3(y, min$$1[1]);
  11136. max$$1[1] = mathMax$3(y, max$$1[1]);
  11137. }
  11138. min$$1[0] = mathMin$3(x0, min$$1[0]);
  11139. max$$1[0] = mathMax$3(x0, max$$1[0]);
  11140. min$$1[0] = mathMin$3(x3, min$$1[0]);
  11141. max$$1[0] = mathMax$3(x3, max$$1[0]);
  11142. min$$1[1] = mathMin$3(y0, min$$1[1]);
  11143. max$$1[1] = mathMax$3(y0, max$$1[1]);
  11144. min$$1[1] = mathMin$3(y3, min$$1[1]);
  11145. max$$1[1] = mathMax$3(y3, max$$1[1]);
  11146. }
  11147. /**
  11148. * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
  11149. * @memberOf module:zrender/core/bbox
  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 {Array.<number>} min
  11157. * @param {Array.<number>} max
  11158. */
  11159. function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) {
  11160. var quadraticExtremum$$1 = quadraticExtremum;
  11161. var quadraticAt$$1 = quadraticAt;
  11162. // Find extremities, where derivative in x dim or y dim is zero
  11163. var tx =
  11164. mathMax$3(
  11165. mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0
  11166. );
  11167. var ty =
  11168. mathMax$3(
  11169. mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0
  11170. );
  11171. var x = quadraticAt$$1(x0, x1, x2, tx);
  11172. var y = quadraticAt$$1(y0, y1, y2, ty);
  11173. min$$1[0] = mathMin$3(x0, x2, x);
  11174. min$$1[1] = mathMin$3(y0, y2, y);
  11175. max$$1[0] = mathMax$3(x0, x2, x);
  11176. max$$1[1] = mathMax$3(y0, y2, y);
  11177. }
  11178. /**
  11179. * 从圆弧中计算出最小包围盒,写入`min`和`max`中
  11180. * @method
  11181. * @memberOf module:zrender/core/bbox
  11182. * @param {number} x
  11183. * @param {number} y
  11184. * @param {number} rx
  11185. * @param {number} ry
  11186. * @param {number} startAngle
  11187. * @param {number} endAngle
  11188. * @param {number} anticlockwise
  11189. * @param {Array.<number>} min
  11190. * @param {Array.<number>} max
  11191. */
  11192. function fromArc(
  11193. x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1
  11194. ) {
  11195. var vec2Min = min;
  11196. var vec2Max = max;
  11197. var diff = Math.abs(startAngle - endAngle);
  11198. if (diff % PI2 < 1e-4 && diff > 1e-4) {
  11199. // Is a circle
  11200. min$$1[0] = x - rx;
  11201. min$$1[1] = y - ry;
  11202. max$$1[0] = x + rx;
  11203. max$$1[1] = y + ry;
  11204. return;
  11205. }
  11206. start[0] = mathCos$2(startAngle) * rx + x;
  11207. start[1] = mathSin$2(startAngle) * ry + y;
  11208. end[0] = mathCos$2(endAngle) * rx + x;
  11209. end[1] = mathSin$2(endAngle) * ry + y;
  11210. vec2Min(min$$1, start, end);
  11211. vec2Max(max$$1, start, end);
  11212. // Thresh to [0, Math.PI * 2]
  11213. startAngle = startAngle % (PI2);
  11214. if (startAngle < 0) {
  11215. startAngle = startAngle + PI2;
  11216. }
  11217. endAngle = endAngle % (PI2);
  11218. if (endAngle < 0) {
  11219. endAngle = endAngle + PI2;
  11220. }
  11221. if (startAngle > endAngle && !anticlockwise) {
  11222. endAngle += PI2;
  11223. }
  11224. else if (startAngle < endAngle && anticlockwise) {
  11225. startAngle += PI2;
  11226. }
  11227. if (anticlockwise) {
  11228. var tmp = endAngle;
  11229. endAngle = startAngle;
  11230. startAngle = tmp;
  11231. }
  11232. // var number = 0;
  11233. // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
  11234. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  11235. if (angle > startAngle) {
  11236. extremity[0] = mathCos$2(angle) * rx + x;
  11237. extremity[1] = mathSin$2(angle) * ry + y;
  11238. vec2Min(min$$1, extremity, min$$1);
  11239. vec2Max(max$$1, extremity, max$$1);
  11240. }
  11241. }
  11242. }
  11243. /**
  11244. * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
  11245. * 可以用于 isInsidePath 判断以及获取boundingRect
  11246. *
  11247. * @module zrender/core/PathProxy
  11248. * @author Yi Shen (http://www.github.com/pissang)
  11249. */
  11250. // TODO getTotalLength, getPointAtLength
  11251. var CMD = {
  11252. M: 1,
  11253. L: 2,
  11254. C: 3,
  11255. Q: 4,
  11256. A: 5,
  11257. Z: 6,
  11258. // Rect
  11259. R: 7
  11260. };
  11261. // var CMD_MEM_SIZE = {
  11262. // M: 3,
  11263. // L: 3,
  11264. // C: 7,
  11265. // Q: 5,
  11266. // A: 9,
  11267. // R: 5,
  11268. // Z: 1
  11269. // };
  11270. var min$1 = [];
  11271. var max$1 = [];
  11272. var min2 = [];
  11273. var max2 = [];
  11274. var mathMin$2 = Math.min;
  11275. var mathMax$2 = Math.max;
  11276. var mathCos$1 = Math.cos;
  11277. var mathSin$1 = Math.sin;
  11278. var mathSqrt$1 = Math.sqrt;
  11279. var mathAbs = Math.abs;
  11280. var hasTypedArray = typeof Float32Array !== 'undefined';
  11281. /**
  11282. * @alias module:zrender/core/PathProxy
  11283. * @constructor
  11284. */
  11285. var PathProxy = function (notSaveData) {
  11286. this._saveData = !(notSaveData || false);
  11287. if (this._saveData) {
  11288. /**
  11289. * Path data. Stored as flat array
  11290. * @type {Array.<Object>}
  11291. */
  11292. this.data = [];
  11293. }
  11294. this._ctx = null;
  11295. };
  11296. /**
  11297. * 快速计算Path包围盒(并不是最小包围盒)
  11298. * @return {Object}
  11299. */
  11300. PathProxy.prototype = {
  11301. constructor: PathProxy,
  11302. _xi: 0,
  11303. _yi: 0,
  11304. _x0: 0,
  11305. _y0: 0,
  11306. // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  11307. _ux: 0,
  11308. _uy: 0,
  11309. _len: 0,
  11310. _lineDash: null,
  11311. _dashOffset: 0,
  11312. _dashIdx: 0,
  11313. _dashSum: 0,
  11314. /**
  11315. * @readOnly
  11316. */
  11317. setScale: function (sx, sy) {
  11318. this._ux = mathAbs(1 / devicePixelRatio / sx) || 0;
  11319. this._uy = mathAbs(1 / devicePixelRatio / sy) || 0;
  11320. },
  11321. getContext: function () {
  11322. return this._ctx;
  11323. },
  11324. /**
  11325. * @param {CanvasRenderingContext2D} ctx
  11326. * @return {module:zrender/core/PathProxy}
  11327. */
  11328. beginPath: function (ctx) {
  11329. this._ctx = ctx;
  11330. ctx && ctx.beginPath();
  11331. ctx && (this.dpr = ctx.dpr);
  11332. // Reset
  11333. if (this._saveData) {
  11334. this._len = 0;
  11335. }
  11336. if (this._lineDash) {
  11337. this._lineDash = null;
  11338. this._dashOffset = 0;
  11339. }
  11340. return this;
  11341. },
  11342. /**
  11343. * @param {number} x
  11344. * @param {number} y
  11345. * @return {module:zrender/core/PathProxy}
  11346. */
  11347. moveTo: function (x, y) {
  11348. this.addData(CMD.M, x, y);
  11349. this._ctx && this._ctx.moveTo(x, y);
  11350. // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
  11351. // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
  11352. // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
  11353. // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
  11354. this._x0 = x;
  11355. this._y0 = y;
  11356. this._xi = x;
  11357. this._yi = y;
  11358. return this;
  11359. },
  11360. /**
  11361. * @param {number} x
  11362. * @param {number} y
  11363. * @return {module:zrender/core/PathProxy}
  11364. */
  11365. lineTo: function (x, y) {
  11366. var exceedUnit = mathAbs(x - this._xi) > this._ux
  11367. || mathAbs(y - this._yi) > this._uy
  11368. // Force draw the first segment
  11369. || this._len < 5;
  11370. this.addData(CMD.L, x, y);
  11371. if (this._ctx && exceedUnit) {
  11372. this._needsDash() ? this._dashedLineTo(x, y)
  11373. : this._ctx.lineTo(x, y);
  11374. }
  11375. if (exceedUnit) {
  11376. this._xi = x;
  11377. this._yi = y;
  11378. }
  11379. return this;
  11380. },
  11381. /**
  11382. * @param {number} x1
  11383. * @param {number} y1
  11384. * @param {number} x2
  11385. * @param {number} y2
  11386. * @param {number} x3
  11387. * @param {number} y3
  11388. * @return {module:zrender/core/PathProxy}
  11389. */
  11390. bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
  11391. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  11392. if (this._ctx) {
  11393. this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
  11394. : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  11395. }
  11396. this._xi = x3;
  11397. this._yi = y3;
  11398. return this;
  11399. },
  11400. /**
  11401. * @param {number} x1
  11402. * @param {number} y1
  11403. * @param {number} x2
  11404. * @param {number} y2
  11405. * @return {module:zrender/core/PathProxy}
  11406. */
  11407. quadraticCurveTo: function (x1, y1, x2, y2) {
  11408. this.addData(CMD.Q, x1, y1, x2, y2);
  11409. if (this._ctx) {
  11410. this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)
  11411. : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  11412. }
  11413. this._xi = x2;
  11414. this._yi = y2;
  11415. return this;
  11416. },
  11417. /**
  11418. * @param {number} cx
  11419. * @param {number} cy
  11420. * @param {number} r
  11421. * @param {number} startAngle
  11422. * @param {number} endAngle
  11423. * @param {boolean} anticlockwise
  11424. * @return {module:zrender/core/PathProxy}
  11425. */
  11426. arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  11427. this.addData(
  11428. CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1
  11429. );
  11430. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  11431. this._xi = mathCos$1(endAngle) * r + cx;
  11432. this._yi = mathSin$1(endAngle) * r + cy;
  11433. return this;
  11434. },
  11435. // TODO
  11436. arcTo: function (x1, y1, x2, y2, radius) {
  11437. if (this._ctx) {
  11438. this._ctx.arcTo(x1, y1, x2, y2, radius);
  11439. }
  11440. return this;
  11441. },
  11442. // TODO
  11443. rect: function (x, y, w, h) {
  11444. this._ctx && this._ctx.rect(x, y, w, h);
  11445. this.addData(CMD.R, x, y, w, h);
  11446. return this;
  11447. },
  11448. /**
  11449. * @return {module:zrender/core/PathProxy}
  11450. */
  11451. closePath: function () {
  11452. this.addData(CMD.Z);
  11453. var ctx = this._ctx;
  11454. var x0 = this._x0;
  11455. var y0 = this._y0;
  11456. if (ctx) {
  11457. this._needsDash() && this._dashedLineTo(x0, y0);
  11458. ctx.closePath();
  11459. }
  11460. this._xi = x0;
  11461. this._yi = y0;
  11462. return this;
  11463. },
  11464. /**
  11465. * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
  11466. * stroke 同样
  11467. * @param {CanvasRenderingContext2D} ctx
  11468. * @return {module:zrender/core/PathProxy}
  11469. */
  11470. fill: function (ctx) {
  11471. ctx && ctx.fill();
  11472. this.toStatic();
  11473. },
  11474. /**
  11475. * @param {CanvasRenderingContext2D} ctx
  11476. * @return {module:zrender/core/PathProxy}
  11477. */
  11478. stroke: function (ctx) {
  11479. ctx && ctx.stroke();
  11480. this.toStatic();
  11481. },
  11482. /**
  11483. * 必须在其它绘制命令前调用
  11484. * Must be invoked before all other path drawing methods
  11485. * @return {module:zrender/core/PathProxy}
  11486. */
  11487. setLineDash: function (lineDash) {
  11488. if (lineDash instanceof Array) {
  11489. this._lineDash = lineDash;
  11490. this._dashIdx = 0;
  11491. var lineDashSum = 0;
  11492. for (var i = 0; i < lineDash.length; i++) {
  11493. lineDashSum += lineDash[i];
  11494. }
  11495. this._dashSum = lineDashSum;
  11496. }
  11497. return this;
  11498. },
  11499. /**
  11500. * 必须在其它绘制命令前调用
  11501. * Must be invoked before all other path drawing methods
  11502. * @return {module:zrender/core/PathProxy}
  11503. */
  11504. setLineDashOffset: function (offset) {
  11505. this._dashOffset = offset;
  11506. return this;
  11507. },
  11508. /**
  11509. *
  11510. * @return {boolean}
  11511. */
  11512. len: function () {
  11513. return this._len;
  11514. },
  11515. /**
  11516. * 直接设置 Path 数据
  11517. */
  11518. setData: function (data) {
  11519. var len$$1 = data.length;
  11520. if (!(this.data && this.data.length === len$$1) && hasTypedArray) {
  11521. this.data = new Float32Array(len$$1);
  11522. }
  11523. for (var i = 0; i < len$$1; i++) {
  11524. this.data[i] = data[i];
  11525. }
  11526. this._len = len$$1;
  11527. },
  11528. /**
  11529. * 添加子路径
  11530. * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
  11531. */
  11532. appendPath: function (path) {
  11533. if (!(path instanceof Array)) {
  11534. path = [path];
  11535. }
  11536. var len$$1 = path.length;
  11537. var appendSize = 0;
  11538. var offset = this._len;
  11539. for (var i = 0; i < len$$1; i++) {
  11540. appendSize += path[i].len();
  11541. }
  11542. if (hasTypedArray && (this.data instanceof Float32Array)) {
  11543. this.data = new Float32Array(offset + appendSize);
  11544. }
  11545. for (var i = 0; i < len$$1; i++) {
  11546. var appendPathData = path[i].data;
  11547. for (var k = 0; k < appendPathData.length; k++) {
  11548. this.data[offset++] = appendPathData[k];
  11549. }
  11550. }
  11551. this._len = offset;
  11552. },
  11553. /**
  11554. * 填充 Path 数据。
  11555. * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
  11556. */
  11557. addData: function (cmd) {
  11558. if (!this._saveData) {
  11559. return;
  11560. }
  11561. var data = this.data;
  11562. if (this._len + arguments.length > data.length) {
  11563. // 因为之前的数组已经转换成静态的 Float32Array
  11564. // 所以不够用时需要扩展一个新的动态数组
  11565. this._expandData();
  11566. data = this.data;
  11567. }
  11568. for (var i = 0; i < arguments.length; i++) {
  11569. data[this._len++] = arguments[i];
  11570. }
  11571. this._prevCmd = cmd;
  11572. },
  11573. _expandData: function () {
  11574. // Only if data is Float32Array
  11575. if (!(this.data instanceof Array)) {
  11576. var newData = [];
  11577. for (var i = 0; i < this._len; i++) {
  11578. newData[i] = this.data[i];
  11579. }
  11580. this.data = newData;
  11581. }
  11582. },
  11583. /**
  11584. * If needs js implemented dashed line
  11585. * @return {boolean}
  11586. * @private
  11587. */
  11588. _needsDash: function () {
  11589. return this._lineDash;
  11590. },
  11591. _dashedLineTo: function (x1, y1) {
  11592. var dashSum = this._dashSum;
  11593. var offset = this._dashOffset;
  11594. var lineDash = this._lineDash;
  11595. var ctx = this._ctx;
  11596. var x0 = this._xi;
  11597. var y0 = this._yi;
  11598. var dx = x1 - x0;
  11599. var dy = y1 - y0;
  11600. var dist$$1 = mathSqrt$1(dx * dx + dy * dy);
  11601. var x = x0;
  11602. var y = y0;
  11603. var dash;
  11604. var nDash = lineDash.length;
  11605. var idx;
  11606. dx /= dist$$1;
  11607. dy /= dist$$1;
  11608. if (offset < 0) {
  11609. // Convert to positive offset
  11610. offset = dashSum + offset;
  11611. }
  11612. offset %= dashSum;
  11613. x -= offset * dx;
  11614. y -= offset * dy;
  11615. while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
  11616. || (dx === 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
  11617. idx = this._dashIdx;
  11618. dash = lineDash[idx];
  11619. x += dx * dash;
  11620. y += dy * dash;
  11621. this._dashIdx = (idx + 1) % nDash;
  11622. // Skip positive offset
  11623. if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
  11624. continue;
  11625. }
  11626. ctx[idx % 2 ? 'moveTo' : 'lineTo'](
  11627. dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1),
  11628. dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1)
  11629. );
  11630. }
  11631. // Offset for next lineTo
  11632. dx = x - x1;
  11633. dy = y - y1;
  11634. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  11635. },
  11636. // Not accurate dashed line to
  11637. _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
  11638. var dashSum = this._dashSum;
  11639. var offset = this._dashOffset;
  11640. var lineDash = this._lineDash;
  11641. var ctx = this._ctx;
  11642. var x0 = this._xi;
  11643. var y0 = this._yi;
  11644. var t;
  11645. var dx;
  11646. var dy;
  11647. var cubicAt$$1 = cubicAt;
  11648. var bezierLen = 0;
  11649. var idx = this._dashIdx;
  11650. var nDash = lineDash.length;
  11651. var x;
  11652. var y;
  11653. var tmpLen = 0;
  11654. if (offset < 0) {
  11655. // Convert to positive offset
  11656. offset = dashSum + offset;
  11657. }
  11658. offset %= dashSum;
  11659. // Bezier approx length
  11660. for (t = 0; t < 1; t += 0.1) {
  11661. dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1)
  11662. - cubicAt$$1(x0, x1, x2, x3, t);
  11663. dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1)
  11664. - cubicAt$$1(y0, y1, y2, y3, t);
  11665. bezierLen += mathSqrt$1(dx * dx + dy * dy);
  11666. }
  11667. // Find idx after add offset
  11668. for (; idx < nDash; idx++) {
  11669. tmpLen += lineDash[idx];
  11670. if (tmpLen > offset) {
  11671. break;
  11672. }
  11673. }
  11674. t = (tmpLen - offset) / bezierLen;
  11675. while (t <= 1) {
  11676. x = cubicAt$$1(x0, x1, x2, x3, t);
  11677. y = cubicAt$$1(y0, y1, y2, y3, t);
  11678. // Use line to approximate dashed bezier
  11679. // Bad result if dash is long
  11680. idx % 2 ? ctx.moveTo(x, y)
  11681. : ctx.lineTo(x, y);
  11682. t += lineDash[idx] / bezierLen;
  11683. idx = (idx + 1) % nDash;
  11684. }
  11685. // Finish the last segment and calculate the new offset
  11686. (idx % 2 !== 0) && ctx.lineTo(x3, y3);
  11687. dx = x3 - x;
  11688. dy = y3 - y;
  11689. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  11690. },
  11691. _dashedQuadraticTo: function (x1, y1, x2, y2) {
  11692. // Convert quadratic to cubic using degree elevation
  11693. var x3 = x2;
  11694. var y3 = y2;
  11695. x2 = (x2 + 2 * x1) / 3;
  11696. y2 = (y2 + 2 * y1) / 3;
  11697. x1 = (this._xi + 2 * x1) / 3;
  11698. y1 = (this._yi + 2 * y1) / 3;
  11699. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  11700. },
  11701. /**
  11702. * 转成静态的 Float32Array 减少堆内存占用
  11703. * Convert dynamic array to static Float32Array
  11704. */
  11705. toStatic: function () {
  11706. var data = this.data;
  11707. if (data instanceof Array) {
  11708. data.length = this._len;
  11709. if (hasTypedArray) {
  11710. this.data = new Float32Array(data);
  11711. }
  11712. }
  11713. },
  11714. /**
  11715. * @return {module:zrender/core/BoundingRect}
  11716. */
  11717. getBoundingRect: function () {
  11718. min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  11719. max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  11720. var data = this.data;
  11721. var xi = 0;
  11722. var yi = 0;
  11723. var x0 = 0;
  11724. var y0 = 0;
  11725. for (var i = 0; i < data.length;) {
  11726. var cmd = data[i++];
  11727. if (i === 1) {
  11728. // 如果第一个命令是 L, C, Q
  11729. // 则 previous point 同绘制命令的第一个 point
  11730. //
  11731. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11732. xi = data[i];
  11733. yi = data[i + 1];
  11734. x0 = xi;
  11735. y0 = yi;
  11736. }
  11737. switch (cmd) {
  11738. case CMD.M:
  11739. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  11740. // 在 closePath 的时候使用
  11741. x0 = data[i++];
  11742. y0 = data[i++];
  11743. xi = x0;
  11744. yi = y0;
  11745. min2[0] = x0;
  11746. min2[1] = y0;
  11747. max2[0] = x0;
  11748. max2[1] = y0;
  11749. break;
  11750. case CMD.L:
  11751. fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  11752. xi = data[i++];
  11753. yi = data[i++];
  11754. break;
  11755. case CMD.C:
  11756. fromCubic(
  11757. xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  11758. min2, max2
  11759. );
  11760. xi = data[i++];
  11761. yi = data[i++];
  11762. break;
  11763. case CMD.Q:
  11764. fromQuadratic(
  11765. xi, yi, data[i++], data[i++], data[i], data[i + 1],
  11766. min2, max2
  11767. );
  11768. xi = data[i++];
  11769. yi = data[i++];
  11770. break;
  11771. case CMD.A:
  11772. // TODO Arc 判断的开销比较大
  11773. var cx = data[i++];
  11774. var cy = data[i++];
  11775. var rx = data[i++];
  11776. var ry = data[i++];
  11777. var startAngle = data[i++];
  11778. var endAngle = data[i++] + startAngle;
  11779. // TODO Arc 旋转
  11780. i += 1;
  11781. var anticlockwise = 1 - data[i++];
  11782. if (i === 1) {
  11783. // 直接使用 arc 命令
  11784. // 第一个命令起点还未定义
  11785. x0 = mathCos$1(startAngle) * rx + cx;
  11786. y0 = mathSin$1(startAngle) * ry + cy;
  11787. }
  11788. fromArc(
  11789. cx, cy, rx, ry, startAngle, endAngle,
  11790. anticlockwise, min2, max2
  11791. );
  11792. xi = mathCos$1(endAngle) * rx + cx;
  11793. yi = mathSin$1(endAngle) * ry + cy;
  11794. break;
  11795. case CMD.R:
  11796. x0 = xi = data[i++];
  11797. y0 = yi = data[i++];
  11798. var width = data[i++];
  11799. var height = data[i++];
  11800. // Use fromLine
  11801. fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  11802. break;
  11803. case CMD.Z:
  11804. xi = x0;
  11805. yi = y0;
  11806. break;
  11807. }
  11808. // Union
  11809. min(min$1, min$1, min2);
  11810. max(max$1, max$1, max2);
  11811. }
  11812. // No data
  11813. if (i === 0) {
  11814. min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
  11815. }
  11816. return new BoundingRect(
  11817. min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]
  11818. );
  11819. },
  11820. /**
  11821. * Rebuild path from current data
  11822. * Rebuild path will not consider javascript implemented line dash.
  11823. * @param {CanvasRenderingContext2D} ctx
  11824. */
  11825. rebuildPath: function (ctx) {
  11826. var d = this.data;
  11827. var x0, y0;
  11828. var xi, yi;
  11829. var x, y;
  11830. var ux = this._ux;
  11831. var uy = this._uy;
  11832. var len$$1 = this._len;
  11833. for (var i = 0; i < len$$1;) {
  11834. var cmd = d[i++];
  11835. if (i === 1) {
  11836. // 如果第一个命令是 L, C, Q
  11837. // 则 previous point 同绘制命令的第一个 point
  11838. //
  11839. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11840. xi = d[i];
  11841. yi = d[i + 1];
  11842. x0 = xi;
  11843. y0 = yi;
  11844. }
  11845. switch (cmd) {
  11846. case CMD.M:
  11847. x0 = xi = d[i++];
  11848. y0 = yi = d[i++];
  11849. ctx.moveTo(xi, yi);
  11850. break;
  11851. case CMD.L:
  11852. x = d[i++];
  11853. y = d[i++];
  11854. // Not draw too small seg between
  11855. if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) {
  11856. ctx.lineTo(x, y);
  11857. xi = x;
  11858. yi = y;
  11859. }
  11860. break;
  11861. case CMD.C:
  11862. ctx.bezierCurveTo(
  11863. d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]
  11864. );
  11865. xi = d[i - 2];
  11866. yi = d[i - 1];
  11867. break;
  11868. case CMD.Q:
  11869. ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
  11870. xi = d[i - 2];
  11871. yi = d[i - 1];
  11872. break;
  11873. case CMD.A:
  11874. var cx = d[i++];
  11875. var cy = d[i++];
  11876. var rx = d[i++];
  11877. var ry = d[i++];
  11878. var theta = d[i++];
  11879. var dTheta = d[i++];
  11880. var psi = d[i++];
  11881. var fs = d[i++];
  11882. var r = (rx > ry) ? rx : ry;
  11883. var scaleX = (rx > ry) ? 1 : rx / ry;
  11884. var scaleY = (rx > ry) ? ry / rx : 1;
  11885. var isEllipse = Math.abs(rx - ry) > 1e-3;
  11886. var endAngle = theta + dTheta;
  11887. if (isEllipse) {
  11888. ctx.translate(cx, cy);
  11889. ctx.rotate(psi);
  11890. ctx.scale(scaleX, scaleY);
  11891. ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
  11892. ctx.scale(1 / scaleX, 1 / scaleY);
  11893. ctx.rotate(-psi);
  11894. ctx.translate(-cx, -cy);
  11895. }
  11896. else {
  11897. ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
  11898. }
  11899. if (i === 1) {
  11900. // 直接使用 arc 命令
  11901. // 第一个命令起点还未定义
  11902. x0 = mathCos$1(theta) * rx + cx;
  11903. y0 = mathSin$1(theta) * ry + cy;
  11904. }
  11905. xi = mathCos$1(endAngle) * rx + cx;
  11906. yi = mathSin$1(endAngle) * ry + cy;
  11907. break;
  11908. case CMD.R:
  11909. x0 = xi = d[i];
  11910. y0 = yi = d[i + 1];
  11911. ctx.rect(d[i++], d[i++], d[i++], d[i++]);
  11912. break;
  11913. case CMD.Z:
  11914. ctx.closePath();
  11915. xi = x0;
  11916. yi = y0;
  11917. }
  11918. }
  11919. }
  11920. };
  11921. PathProxy.CMD = CMD;
  11922. /**
  11923. * 线段包含判断
  11924. * @param {number} x0
  11925. * @param {number} y0
  11926. * @param {number} x1
  11927. * @param {number} y1
  11928. * @param {number} lineWidth
  11929. * @param {number} x
  11930. * @param {number} y
  11931. * @return {boolean}
  11932. */
  11933. function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) {
  11934. if (lineWidth === 0) {
  11935. return false;
  11936. }
  11937. var _l = lineWidth;
  11938. var _a = 0;
  11939. var _b = x0;
  11940. // Quick reject
  11941. if (
  11942. (y > y0 + _l && y > y1 + _l)
  11943. || (y < y0 - _l && y < y1 - _l)
  11944. || (x > x0 + _l && x > x1 + _l)
  11945. || (x < x0 - _l && x < x1 - _l)
  11946. ) {
  11947. return false;
  11948. }
  11949. if (x0 !== x1) {
  11950. _a = (y0 - y1) / (x0 - x1);
  11951. _b = (x0 * y1 - x1 * y0) / (x0 - x1);
  11952. }
  11953. else {
  11954. return Math.abs(x - x0) <= _l / 2;
  11955. }
  11956. var tmp = _a * x - y + _b;
  11957. var _s = tmp * tmp / (_a * _a + 1);
  11958. return _s <= _l / 2 * _l / 2;
  11959. }
  11960. /**
  11961. * 三次贝塞尔曲线描边包含判断
  11962. * @param {number} x0
  11963. * @param {number} y0
  11964. * @param {number} x1
  11965. * @param {number} y1
  11966. * @param {number} x2
  11967. * @param {number} y2
  11968. * @param {number} x3
  11969. * @param {number} y3
  11970. * @param {number} lineWidth
  11971. * @param {number} x
  11972. * @param {number} y
  11973. * @return {boolean}
  11974. */
  11975. function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
  11976. if (lineWidth === 0) {
  11977. return false;
  11978. }
  11979. var _l = lineWidth;
  11980. // Quick reject
  11981. if (
  11982. (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
  11983. || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
  11984. || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
  11985. || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
  11986. ) {
  11987. return false;
  11988. }
  11989. var d = cubicProjectPoint(
  11990. x0, y0, x1, y1, x2, y2, x3, y3,
  11991. x, y, null
  11992. );
  11993. return d <= _l / 2;
  11994. }
  11995. /**
  11996. * 二次贝塞尔曲线描边包含判断
  11997. * @param {number} x0
  11998. * @param {number} y0
  11999. * @param {number} x1
  12000. * @param {number} y1
  12001. * @param {number} x2
  12002. * @param {number} y2
  12003. * @param {number} lineWidth
  12004. * @param {number} x
  12005. * @param {number} y
  12006. * @return {boolean}
  12007. */
  12008. function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
  12009. if (lineWidth === 0) {
  12010. return false;
  12011. }
  12012. var _l = lineWidth;
  12013. // Quick reject
  12014. if (
  12015. (y > y0 + _l && y > y1 + _l && y > y2 + _l)
  12016. || (y < y0 - _l && y < y1 - _l && y < y2 - _l)
  12017. || (x > x0 + _l && x > x1 + _l && x > x2 + _l)
  12018. || (x < x0 - _l && x < x1 - _l && x < x2 - _l)
  12019. ) {
  12020. return false;
  12021. }
  12022. var d = quadraticProjectPoint(
  12023. x0, y0, x1, y1, x2, y2,
  12024. x, y, null
  12025. );
  12026. return d <= _l / 2;
  12027. }
  12028. var PI2$3 = Math.PI * 2;
  12029. function normalizeRadian(angle) {
  12030. angle %= PI2$3;
  12031. if (angle < 0) {
  12032. angle += PI2$3;
  12033. }
  12034. return angle;
  12035. }
  12036. var PI2$2 = Math.PI * 2;
  12037. /**
  12038. * 圆弧描边包含判断
  12039. * @param {number} cx
  12040. * @param {number} cy
  12041. * @param {number} r
  12042. * @param {number} startAngle
  12043. * @param {number} endAngle
  12044. * @param {boolean} anticlockwise
  12045. * @param {number} lineWidth
  12046. * @param {number} x
  12047. * @param {number} y
  12048. * @return {Boolean}
  12049. */
  12050. function containStroke$4(
  12051. cx, cy, r, startAngle, endAngle, anticlockwise,
  12052. lineWidth, x, y
  12053. ) {
  12054. if (lineWidth === 0) {
  12055. return false;
  12056. }
  12057. var _l = lineWidth;
  12058. x -= cx;
  12059. y -= cy;
  12060. var d = Math.sqrt(x * x + y * y);
  12061. if ((d - _l > r) || (d + _l < r)) {
  12062. return false;
  12063. }
  12064. if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) {
  12065. // Is a circle
  12066. return true;
  12067. }
  12068. if (anticlockwise) {
  12069. var tmp = startAngle;
  12070. startAngle = normalizeRadian(endAngle);
  12071. endAngle = normalizeRadian(tmp);
  12072. }
  12073. else {
  12074. startAngle = normalizeRadian(startAngle);
  12075. endAngle = normalizeRadian(endAngle);
  12076. }
  12077. if (startAngle > endAngle) {
  12078. endAngle += PI2$2;
  12079. }
  12080. var angle = Math.atan2(y, x);
  12081. if (angle < 0) {
  12082. angle += PI2$2;
  12083. }
  12084. return (angle >= startAngle && angle <= endAngle)
  12085. || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle);
  12086. }
  12087. function windingLine(x0, y0, x1, y1, x, y) {
  12088. if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
  12089. return 0;
  12090. }
  12091. // Ignore horizontal line
  12092. if (y1 === y0) {
  12093. return 0;
  12094. }
  12095. var dir = y1 < y0 ? 1 : -1;
  12096. var t = (y - y0) / (y1 - y0);
  12097. // Avoid winding error when intersection point is the connect point of two line of polygon
  12098. if (t === 1 || t === 0) {
  12099. dir = y1 < y0 ? 0.5 : -0.5;
  12100. }
  12101. var x_ = t * (x1 - x0) + x0;
  12102. // If (x, y) on the line, considered as "contain".
  12103. return x_ === x ? Infinity : x_ > x ? dir : 0;
  12104. }
  12105. var CMD$1 = PathProxy.CMD;
  12106. var PI2$1 = Math.PI * 2;
  12107. var EPSILON$2 = 1e-4;
  12108. function isAroundEqual(a, b) {
  12109. return Math.abs(a - b) < EPSILON$2;
  12110. }
  12111. // 临时数组
  12112. var roots = [-1, -1, -1];
  12113. var extrema = [-1, -1];
  12114. function swapExtrema() {
  12115. var tmp = extrema[0];
  12116. extrema[0] = extrema[1];
  12117. extrema[1] = tmp;
  12118. }
  12119. function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
  12120. // Quick reject
  12121. if (
  12122. (y > y0 && y > y1 && y > y2 && y > y3)
  12123. || (y < y0 && y < y1 && y < y2 && y < y3)
  12124. ) {
  12125. return 0;
  12126. }
  12127. var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
  12128. if (nRoots === 0) {
  12129. return 0;
  12130. }
  12131. else {
  12132. var w = 0;
  12133. var nExtrema = -1;
  12134. var y0_;
  12135. var y1_;
  12136. for (var i = 0; i < nRoots; i++) {
  12137. var t = roots[i];
  12138. // Avoid winding error when intersection point is the connect point of two line of polygon
  12139. var unit = (t === 0 || t === 1) ? 0.5 : 1;
  12140. var x_ = cubicAt(x0, x1, x2, x3, t);
  12141. if (x_ < x) { // Quick reject
  12142. continue;
  12143. }
  12144. if (nExtrema < 0) {
  12145. nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);
  12146. if (extrema[1] < extrema[0] && nExtrema > 1) {
  12147. swapExtrema();
  12148. }
  12149. y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);
  12150. if (nExtrema > 1) {
  12151. y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);
  12152. }
  12153. }
  12154. if (nExtrema === 2) {
  12155. // 分成三段单调函数
  12156. if (t < extrema[0]) {
  12157. w += y0_ < y0 ? unit : -unit;
  12158. }
  12159. else if (t < extrema[1]) {
  12160. w += y1_ < y0_ ? unit : -unit;
  12161. }
  12162. else {
  12163. w += y3 < y1_ ? unit : -unit;
  12164. }
  12165. }
  12166. else {
  12167. // 分成两段单调函数
  12168. if (t < extrema[0]) {
  12169. w += y0_ < y0 ? unit : -unit;
  12170. }
  12171. else {
  12172. w += y3 < y0_ ? unit : -unit;
  12173. }
  12174. }
  12175. }
  12176. return w;
  12177. }
  12178. }
  12179. function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
  12180. // Quick reject
  12181. if (
  12182. (y > y0 && y > y1 && y > y2)
  12183. || (y < y0 && y < y1 && y < y2)
  12184. ) {
  12185. return 0;
  12186. }
  12187. var nRoots = quadraticRootAt(y0, y1, y2, y, roots);
  12188. if (nRoots === 0) {
  12189. return 0;
  12190. }
  12191. else {
  12192. var t = quadraticExtremum(y0, y1, y2);
  12193. if (t >= 0 && t <= 1) {
  12194. var w = 0;
  12195. var y_ = quadraticAt(y0, y1, y2, t);
  12196. for (var i = 0; i < nRoots; i++) {
  12197. // Remove one endpoint.
  12198. var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
  12199. var x_ = quadraticAt(x0, x1, x2, roots[i]);
  12200. if (x_ < x) { // Quick reject
  12201. continue;
  12202. }
  12203. if (roots[i] < t) {
  12204. w += y_ < y0 ? unit : -unit;
  12205. }
  12206. else {
  12207. w += y2 < y_ ? unit : -unit;
  12208. }
  12209. }
  12210. return w;
  12211. }
  12212. else {
  12213. // Remove one endpoint.
  12214. var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
  12215. var x_ = quadraticAt(x0, x1, x2, roots[0]);
  12216. if (x_ < x) { // Quick reject
  12217. return 0;
  12218. }
  12219. return y2 < y0 ? unit : -unit;
  12220. }
  12221. }
  12222. }
  12223. // TODO
  12224. // Arc 旋转
  12225. function windingArc(
  12226. cx, cy, r, startAngle, endAngle, anticlockwise, x, y
  12227. ) {
  12228. y -= cy;
  12229. if (y > r || y < -r) {
  12230. return 0;
  12231. }
  12232. var tmp = Math.sqrt(r * r - y * y);
  12233. roots[0] = -tmp;
  12234. roots[1] = tmp;
  12235. var diff = Math.abs(startAngle - endAngle);
  12236. if (diff < 1e-4) {
  12237. return 0;
  12238. }
  12239. if (diff % PI2$1 < 1e-4) {
  12240. // Is a circle
  12241. startAngle = 0;
  12242. endAngle = PI2$1;
  12243. var dir = anticlockwise ? 1 : -1;
  12244. if (x >= roots[0] + cx && x <= roots[1] + cx) {
  12245. return dir;
  12246. }
  12247. else {
  12248. return 0;
  12249. }
  12250. }
  12251. if (anticlockwise) {
  12252. var tmp = startAngle;
  12253. startAngle = normalizeRadian(endAngle);
  12254. endAngle = normalizeRadian(tmp);
  12255. }
  12256. else {
  12257. startAngle = normalizeRadian(startAngle);
  12258. endAngle = normalizeRadian(endAngle);
  12259. }
  12260. if (startAngle > endAngle) {
  12261. endAngle += PI2$1;
  12262. }
  12263. var w = 0;
  12264. for (var i = 0; i < 2; i++) {
  12265. var x_ = roots[i];
  12266. if (x_ + cx > x) {
  12267. var angle = Math.atan2(y, x_);
  12268. var dir = anticlockwise ? 1 : -1;
  12269. if (angle < 0) {
  12270. angle = PI2$1 + angle;
  12271. }
  12272. if (
  12273. (angle >= startAngle && angle <= endAngle)
  12274. || (angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle)
  12275. ) {
  12276. if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
  12277. dir = -dir;
  12278. }
  12279. w += dir;
  12280. }
  12281. }
  12282. }
  12283. return w;
  12284. }
  12285. function containPath(data, lineWidth, isStroke, x, y) {
  12286. var w = 0;
  12287. var xi = 0;
  12288. var yi = 0;
  12289. var x0 = 0;
  12290. var y0 = 0;
  12291. for (var i = 0; i < data.length;) {
  12292. var cmd = data[i++];
  12293. // Begin a new subpath
  12294. if (cmd === CMD$1.M && i > 1) {
  12295. // Close previous subpath
  12296. if (!isStroke) {
  12297. w += windingLine(xi, yi, x0, y0, x, y);
  12298. }
  12299. // 如果被任何一个 subpath 包含
  12300. // if (w !== 0) {
  12301. // return true;
  12302. // }
  12303. }
  12304. if (i === 1) {
  12305. // 如果第一个命令是 L, C, Q
  12306. // 则 previous point 同绘制命令的第一个 point
  12307. //
  12308. // 第一个命令为 Arc 的情况下会在后面特殊处理
  12309. xi = data[i];
  12310. yi = data[i + 1];
  12311. x0 = xi;
  12312. y0 = yi;
  12313. }
  12314. switch (cmd) {
  12315. case CMD$1.M:
  12316. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  12317. // 在 closePath 的时候使用
  12318. x0 = data[i++];
  12319. y0 = data[i++];
  12320. xi = x0;
  12321. yi = y0;
  12322. break;
  12323. case CMD$1.L:
  12324. if (isStroke) {
  12325. if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
  12326. return true;
  12327. }
  12328. }
  12329. else {
  12330. // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
  12331. w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
  12332. }
  12333. xi = data[i++];
  12334. yi = data[i++];
  12335. break;
  12336. case CMD$1.C:
  12337. if (isStroke) {
  12338. if (containStroke$2(xi, yi,
  12339. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  12340. lineWidth, x, y
  12341. )) {
  12342. return true;
  12343. }
  12344. }
  12345. else {
  12346. w += windingCubic(
  12347. xi, yi,
  12348. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  12349. x, y
  12350. ) || 0;
  12351. }
  12352. xi = data[i++];
  12353. yi = data[i++];
  12354. break;
  12355. case CMD$1.Q:
  12356. if (isStroke) {
  12357. if (containStroke$3(xi, yi,
  12358. data[i++], data[i++], data[i], data[i + 1],
  12359. lineWidth, x, y
  12360. )) {
  12361. return true;
  12362. }
  12363. }
  12364. else {
  12365. w += windingQuadratic(
  12366. xi, yi,
  12367. data[i++], data[i++], data[i], data[i + 1],
  12368. x, y
  12369. ) || 0;
  12370. }
  12371. xi = data[i++];
  12372. yi = data[i++];
  12373. break;
  12374. case CMD$1.A:
  12375. // TODO Arc 判断的开销比较大
  12376. var cx = data[i++];
  12377. var cy = data[i++];
  12378. var rx = data[i++];
  12379. var ry = data[i++];
  12380. var theta = data[i++];
  12381. var dTheta = data[i++];
  12382. // TODO Arc 旋转
  12383. i += 1;
  12384. var anticlockwise = 1 - data[i++];
  12385. var x1 = Math.cos(theta) * rx + cx;
  12386. var y1 = Math.sin(theta) * ry + cy;
  12387. // 不是直接使用 arc 命令
  12388. if (i > 1) {
  12389. w += windingLine(xi, yi, x1, y1, x, y);
  12390. }
  12391. else {
  12392. // 第一个命令起点还未定义
  12393. x0 = x1;
  12394. y0 = y1;
  12395. }
  12396. // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
  12397. var _x = (x - cx) * ry / rx + cx;
  12398. if (isStroke) {
  12399. if (containStroke$4(
  12400. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  12401. lineWidth, _x, y
  12402. )) {
  12403. return true;
  12404. }
  12405. }
  12406. else {
  12407. w += windingArc(
  12408. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  12409. _x, y
  12410. );
  12411. }
  12412. xi = Math.cos(theta + dTheta) * rx + cx;
  12413. yi = Math.sin(theta + dTheta) * ry + cy;
  12414. break;
  12415. case CMD$1.R:
  12416. x0 = xi = data[i++];
  12417. y0 = yi = data[i++];
  12418. var width = data[i++];
  12419. var height = data[i++];
  12420. var x1 = x0 + width;
  12421. var y1 = y0 + height;
  12422. if (isStroke) {
  12423. if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y)
  12424. || containStroke$1(x1, y0, x1, y1, lineWidth, x, y)
  12425. || containStroke$1(x1, y1, x0, y1, lineWidth, x, y)
  12426. || containStroke$1(x0, y1, x0, y0, lineWidth, x, y)
  12427. ) {
  12428. return true;
  12429. }
  12430. }
  12431. else {
  12432. // FIXME Clockwise ?
  12433. w += windingLine(x1, y0, x1, y1, x, y);
  12434. w += windingLine(x0, y1, x0, y0, x, y);
  12435. }
  12436. break;
  12437. case CMD$1.Z:
  12438. if (isStroke) {
  12439. if (containStroke$1(
  12440. xi, yi, x0, y0, lineWidth, x, y
  12441. )) {
  12442. return true;
  12443. }
  12444. }
  12445. else {
  12446. // Close a subpath
  12447. w += windingLine(xi, yi, x0, y0, x, y);
  12448. // 如果被任何一个 subpath 包含
  12449. // FIXME subpaths may overlap
  12450. // if (w !== 0) {
  12451. // return true;
  12452. // }
  12453. }
  12454. xi = x0;
  12455. yi = y0;
  12456. break;
  12457. }
  12458. }
  12459. if (!isStroke && !isAroundEqual(yi, y0)) {
  12460. w += windingLine(xi, yi, x0, y0, x, y) || 0;
  12461. }
  12462. return w !== 0;
  12463. }
  12464. function contain(pathData, x, y) {
  12465. return containPath(pathData, 0, false, x, y);
  12466. }
  12467. function containStroke(pathData, lineWidth, x, y) {
  12468. return containPath(pathData, lineWidth, true, x, y);
  12469. }
  12470. var getCanvasPattern = Pattern.prototype.getCanvasPattern;
  12471. var abs = Math.abs;
  12472. var pathProxyForDraw = new PathProxy(true);
  12473. /**
  12474. * @alias module:zrender/graphic/Path
  12475. * @extends module:zrender/graphic/Displayable
  12476. * @constructor
  12477. * @param {Object} opts
  12478. */
  12479. function Path(opts) {
  12480. Displayable.call(this, opts);
  12481. /**
  12482. * @type {module:zrender/core/PathProxy}
  12483. * @readOnly
  12484. */
  12485. this.path = null;
  12486. }
  12487. Path.prototype = {
  12488. constructor: Path,
  12489. type: 'path',
  12490. __dirtyPath: true,
  12491. strokeContainThreshold: 5,
  12492. /**
  12493. * See `module:zrender/src/graphic/helper/subPixelOptimize`.
  12494. * @type {boolean}
  12495. */
  12496. subPixelOptimize: false,
  12497. brush: function (ctx, prevEl) {
  12498. var style = this.style;
  12499. var path = this.path || pathProxyForDraw;
  12500. var hasStroke = style.hasStroke();
  12501. var hasFill = style.hasFill();
  12502. var fill = style.fill;
  12503. var stroke = style.stroke;
  12504. var hasFillGradient = hasFill && !!(fill.colorStops);
  12505. var hasStrokeGradient = hasStroke && !!(stroke.colorStops);
  12506. var hasFillPattern = hasFill && !!(fill.image);
  12507. var hasStrokePattern = hasStroke && !!(stroke.image);
  12508. style.bind(ctx, this, prevEl);
  12509. this.setTransform(ctx);
  12510. if (this.__dirty) {
  12511. var rect;
  12512. // Update gradient because bounding rect may changed
  12513. if (hasFillGradient) {
  12514. rect = rect || this.getBoundingRect();
  12515. this._fillGradient = style.getGradient(ctx, fill, rect);
  12516. }
  12517. if (hasStrokeGradient) {
  12518. rect = rect || this.getBoundingRect();
  12519. this._strokeGradient = style.getGradient(ctx, stroke, rect);
  12520. }
  12521. }
  12522. // Use the gradient or pattern
  12523. if (hasFillGradient) {
  12524. // PENDING If may have affect the state
  12525. ctx.fillStyle = this._fillGradient;
  12526. }
  12527. else if (hasFillPattern) {
  12528. ctx.fillStyle = getCanvasPattern.call(fill, ctx);
  12529. }
  12530. if (hasStrokeGradient) {
  12531. ctx.strokeStyle = this._strokeGradient;
  12532. }
  12533. else if (hasStrokePattern) {
  12534. ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
  12535. }
  12536. var lineDash = style.lineDash;
  12537. var lineDashOffset = style.lineDashOffset;
  12538. var ctxLineDash = !!ctx.setLineDash;
  12539. // Update path sx, sy
  12540. var scale = this.getGlobalScale();
  12541. path.setScale(scale[0], scale[1]);
  12542. // Proxy context
  12543. // Rebuild path in following 2 cases
  12544. // 1. Path is dirty
  12545. // 2. Path needs javascript implemented lineDash stroking.
  12546. // In this case, lineDash information will not be saved in PathProxy
  12547. if (this.__dirtyPath
  12548. || (lineDash && !ctxLineDash && hasStroke)
  12549. ) {
  12550. path.beginPath(ctx);
  12551. // Setting line dash before build path
  12552. if (lineDash && !ctxLineDash) {
  12553. path.setLineDash(lineDash);
  12554. path.setLineDashOffset(lineDashOffset);
  12555. }
  12556. this.buildPath(path, this.shape, false);
  12557. // Clear path dirty flag
  12558. if (this.path) {
  12559. this.__dirtyPath = false;
  12560. }
  12561. }
  12562. else {
  12563. // Replay path building
  12564. ctx.beginPath();
  12565. this.path.rebuildPath(ctx);
  12566. }
  12567. if (hasFill) {
  12568. if (style.fillOpacity != null) {
  12569. var originalGlobalAlpha = ctx.globalAlpha;
  12570. ctx.globalAlpha = style.fillOpacity * style.opacity;
  12571. path.fill(ctx);
  12572. ctx.globalAlpha = originalGlobalAlpha;
  12573. }
  12574. else {
  12575. path.fill(ctx);
  12576. }
  12577. }
  12578. if (lineDash && ctxLineDash) {
  12579. ctx.setLineDash(lineDash);
  12580. ctx.lineDashOffset = lineDashOffset;
  12581. }
  12582. if (hasStroke) {
  12583. if (style.strokeOpacity != null) {
  12584. var originalGlobalAlpha = ctx.globalAlpha;
  12585. ctx.globalAlpha = style.strokeOpacity * style.opacity;
  12586. path.stroke(ctx);
  12587. ctx.globalAlpha = originalGlobalAlpha;
  12588. }
  12589. else {
  12590. path.stroke(ctx);
  12591. }
  12592. }
  12593. if (lineDash && ctxLineDash) {
  12594. // PENDING
  12595. // Remove lineDash
  12596. ctx.setLineDash([]);
  12597. }
  12598. // Draw rect text
  12599. if (style.text != null) {
  12600. // Only restore transform when needs draw text.
  12601. this.restoreTransform(ctx);
  12602. this.drawRectText(ctx, this.getBoundingRect());
  12603. }
  12604. },
  12605. // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
  12606. // Like in circle
  12607. buildPath: function (ctx, shapeCfg, inBundle) {},
  12608. createPathProxy: function () {
  12609. this.path = new PathProxy();
  12610. },
  12611. getBoundingRect: function () {
  12612. var rect = this._rect;
  12613. var style = this.style;
  12614. var needsUpdateRect = !rect;
  12615. if (needsUpdateRect) {
  12616. var path = this.path;
  12617. if (!path) {
  12618. // Create path on demand.
  12619. path = this.path = new PathProxy();
  12620. }
  12621. if (this.__dirtyPath) {
  12622. path.beginPath();
  12623. this.buildPath(path, this.shape, false);
  12624. }
  12625. rect = path.getBoundingRect();
  12626. }
  12627. this._rect = rect;
  12628. if (style.hasStroke()) {
  12629. // Needs update rect with stroke lineWidth when
  12630. // 1. Element changes scale or lineWidth
  12631. // 2. Shape is changed
  12632. var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
  12633. if (this.__dirty || needsUpdateRect) {
  12634. rectWithStroke.copy(rect);
  12635. // FIXME Must after updateTransform
  12636. var w = style.lineWidth;
  12637. // PENDING, Min line width is needed when line is horizontal or vertical
  12638. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  12639. // Only add extra hover lineWidth when there are no fill
  12640. if (!style.hasFill()) {
  12641. w = Math.max(w, this.strokeContainThreshold || 4);
  12642. }
  12643. // Consider line width
  12644. // Line scale can't be 0;
  12645. if (lineScale > 1e-10) {
  12646. rectWithStroke.width += w / lineScale;
  12647. rectWithStroke.height += w / lineScale;
  12648. rectWithStroke.x -= w / lineScale / 2;
  12649. rectWithStroke.y -= w / lineScale / 2;
  12650. }
  12651. }
  12652. // Return rect with stroke
  12653. return rectWithStroke;
  12654. }
  12655. return rect;
  12656. },
  12657. contain: function (x, y) {
  12658. var localPos = this.transformCoordToLocal(x, y);
  12659. var rect = this.getBoundingRect();
  12660. var style = this.style;
  12661. x = localPos[0];
  12662. y = localPos[1];
  12663. if (rect.contain(x, y)) {
  12664. var pathData = this.path.data;
  12665. if (style.hasStroke()) {
  12666. var lineWidth = style.lineWidth;
  12667. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  12668. // Line scale can't be 0;
  12669. if (lineScale > 1e-10) {
  12670. // Only add extra hover lineWidth when there are no fill
  12671. if (!style.hasFill()) {
  12672. lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
  12673. }
  12674. if (containStroke(
  12675. pathData, lineWidth / lineScale, x, y
  12676. )) {
  12677. return true;
  12678. }
  12679. }
  12680. }
  12681. if (style.hasFill()) {
  12682. return contain(pathData, x, y);
  12683. }
  12684. }
  12685. return false;
  12686. },
  12687. /**
  12688. * @param {boolean} dirtyPath
  12689. */
  12690. dirty: function (dirtyPath) {
  12691. if (dirtyPath == null) {
  12692. dirtyPath = true;
  12693. }
  12694. // Only mark dirty, not mark clean
  12695. if (dirtyPath) {
  12696. this.__dirtyPath = dirtyPath;
  12697. this._rect = null;
  12698. }
  12699. this.__dirty = this.__dirtyText = true;
  12700. this.__zr && this.__zr.refresh();
  12701. // Used as a clipping path
  12702. if (this.__clipTarget) {
  12703. this.__clipTarget.dirty();
  12704. }
  12705. },
  12706. /**
  12707. * Alias for animate('shape')
  12708. * @param {boolean} loop
  12709. */
  12710. animateShape: function (loop) {
  12711. return this.animate('shape', loop);
  12712. },
  12713. // Overwrite attrKV
  12714. attrKV: function (key, value) {
  12715. // FIXME
  12716. if (key === 'shape') {
  12717. this.setShape(value);
  12718. this.__dirtyPath = true;
  12719. this._rect = null;
  12720. }
  12721. else {
  12722. Displayable.prototype.attrKV.call(this, key, value);
  12723. }
  12724. },
  12725. /**
  12726. * @param {Object|string} key
  12727. * @param {*} value
  12728. */
  12729. setShape: function (key, value) {
  12730. var shape = this.shape;
  12731. // Path from string may not have shape
  12732. if (shape) {
  12733. if (isObject$1(key)) {
  12734. for (var name in key) {
  12735. if (key.hasOwnProperty(name)) {
  12736. shape[name] = key[name];
  12737. }
  12738. }
  12739. }
  12740. else {
  12741. shape[key] = value;
  12742. }
  12743. this.dirty(true);
  12744. }
  12745. return this;
  12746. },
  12747. getLineScale: function () {
  12748. var m = this.transform;
  12749. // Get the line scale.
  12750. // Determinant of `m` means how much the area is enlarged by the
  12751. // transformation. So its square root can be used as a scale factor
  12752. // for width.
  12753. return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10
  12754. ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
  12755. : 1;
  12756. }
  12757. };
  12758. /**
  12759. * 扩展一个 Path element, 比如星形,圆等。
  12760. * Extend a path element
  12761. * @param {Object} props
  12762. * @param {string} props.type Path type
  12763. * @param {Function} props.init Initialize
  12764. * @param {Function} props.buildPath Overwrite buildPath method
  12765. * @param {Object} [props.style] Extended default style config
  12766. * @param {Object} [props.shape] Extended default shape config
  12767. */
  12768. Path.extend = function (defaults$$1) {
  12769. var Sub = function (opts) {
  12770. Path.call(this, opts);
  12771. if (defaults$$1.style) {
  12772. // Extend default style
  12773. this.style.extendFrom(defaults$$1.style, false);
  12774. }
  12775. // Extend default shape
  12776. var defaultShape = defaults$$1.shape;
  12777. if (defaultShape) {
  12778. this.shape = this.shape || {};
  12779. var thisShape = this.shape;
  12780. for (var name in defaultShape) {
  12781. if (
  12782. !thisShape.hasOwnProperty(name)
  12783. && defaultShape.hasOwnProperty(name)
  12784. ) {
  12785. thisShape[name] = defaultShape[name];
  12786. }
  12787. }
  12788. }
  12789. defaults$$1.init && defaults$$1.init.call(this, opts);
  12790. };
  12791. inherits(Sub, Path);
  12792. // FIXME 不能 extend position, rotation 等引用对象
  12793. for (var name in defaults$$1) {
  12794. // Extending prototype values and methods
  12795. if (name !== 'style' && name !== 'shape') {
  12796. Sub.prototype[name] = defaults$$1[name];
  12797. }
  12798. }
  12799. return Sub;
  12800. };
  12801. inherits(Path, Displayable);
  12802. var CMD$2 = PathProxy.CMD;
  12803. var points = [[], [], []];
  12804. var mathSqrt$3 = Math.sqrt;
  12805. var mathAtan2 = Math.atan2;
  12806. var transformPath = function (path, m) {
  12807. var data = path.data;
  12808. var cmd;
  12809. var nPoint;
  12810. var i;
  12811. var j;
  12812. var k;
  12813. var p;
  12814. var M = CMD$2.M;
  12815. var C = CMD$2.C;
  12816. var L = CMD$2.L;
  12817. var R = CMD$2.R;
  12818. var A = CMD$2.A;
  12819. var Q = CMD$2.Q;
  12820. for (i = 0, j = 0; i < data.length;) {
  12821. cmd = data[i++];
  12822. j = i;
  12823. nPoint = 0;
  12824. switch (cmd) {
  12825. case M:
  12826. nPoint = 1;
  12827. break;
  12828. case L:
  12829. nPoint = 1;
  12830. break;
  12831. case C:
  12832. nPoint = 3;
  12833. break;
  12834. case Q:
  12835. nPoint = 2;
  12836. break;
  12837. case A:
  12838. var x = m[4];
  12839. var y = m[5];
  12840. var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]);
  12841. var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]);
  12842. var angle = mathAtan2(-m[1] / sy, m[0] / sx);
  12843. // cx
  12844. data[i] *= sx;
  12845. data[i++] += x;
  12846. // cy
  12847. data[i] *= sy;
  12848. data[i++] += y;
  12849. // Scale rx and ry
  12850. // FIXME Assume psi is 0 here
  12851. data[i++] *= sx;
  12852. data[i++] *= sy;
  12853. // Start angle
  12854. data[i++] += angle;
  12855. // end angle
  12856. data[i++] += angle;
  12857. // FIXME psi
  12858. i += 2;
  12859. j = i;
  12860. break;
  12861. case R:
  12862. // x0, y0
  12863. p[0] = data[i++];
  12864. p[1] = data[i++];
  12865. applyTransform(p, p, m);
  12866. data[j++] = p[0];
  12867. data[j++] = p[1];
  12868. // x1, y1
  12869. p[0] += data[i++];
  12870. p[1] += data[i++];
  12871. applyTransform(p, p, m);
  12872. data[j++] = p[0];
  12873. data[j++] = p[1];
  12874. }
  12875. for (k = 0; k < nPoint; k++) {
  12876. var p = points[k];
  12877. p[0] = data[i++];
  12878. p[1] = data[i++];
  12879. applyTransform(p, p, m);
  12880. // Write back
  12881. data[j++] = p[0];
  12882. data[j++] = p[1];
  12883. }
  12884. }
  12885. };
  12886. // command chars
  12887. // var cc = [
  12888. // 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',
  12889. // 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'
  12890. // ];
  12891. var mathSqrt = Math.sqrt;
  12892. var mathSin = Math.sin;
  12893. var mathCos = Math.cos;
  12894. var PI = Math.PI;
  12895. var vMag = function (v) {
  12896. return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  12897. };
  12898. var vRatio = function (u, v) {
  12899. return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
  12900. };
  12901. var vAngle = function (u, v) {
  12902. return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)
  12903. * Math.acos(vRatio(u, v));
  12904. };
  12905. function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
  12906. var psi = psiDeg * (PI / 180.0);
  12907. var xp = mathCos(psi) * (x1 - x2) / 2.0
  12908. + mathSin(psi) * (y1 - y2) / 2.0;
  12909. var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0
  12910. + mathCos(psi) * (y1 - y2) / 2.0;
  12911. var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
  12912. if (lambda > 1) {
  12913. rx *= mathSqrt(lambda);
  12914. ry *= mathSqrt(lambda);
  12915. }
  12916. var f = (fa === fs ? -1 : 1)
  12917. * mathSqrt((((rx * rx) * (ry * ry))
  12918. - ((rx * rx) * (yp * yp))
  12919. - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)
  12920. + (ry * ry) * (xp * xp))
  12921. ) || 0;
  12922. var cxp = f * rx * yp / ry;
  12923. var cyp = f * -ry * xp / rx;
  12924. var cx = (x1 + x2) / 2.0
  12925. + mathCos(psi) * cxp
  12926. - mathSin(psi) * cyp;
  12927. var cy = (y1 + y2) / 2.0
  12928. + mathSin(psi) * cxp
  12929. + mathCos(psi) * cyp;
  12930. var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);
  12931. var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];
  12932. var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];
  12933. var dTheta = vAngle(u, v);
  12934. if (vRatio(u, v) <= -1) {
  12935. dTheta = PI;
  12936. }
  12937. if (vRatio(u, v) >= 1) {
  12938. dTheta = 0;
  12939. }
  12940. if (fs === 0 && dTheta > 0) {
  12941. dTheta = dTheta - 2 * PI;
  12942. }
  12943. if (fs === 1 && dTheta < 0) {
  12944. dTheta = dTheta + 2 * PI;
  12945. }
  12946. path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
  12947. }
  12948. var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;
  12949. // Consider case:
  12950. // (1) delimiter can be comma or space, where continuous commas
  12951. // or spaces should be seen as one comma.
  12952. // (2) value can be like:
  12953. // '2e-4', 'l.5.9' (ignore 0), 'M-10-10', 'l-2.43e-1,34.9983',
  12954. // 'l-.5E1,54', '121-23-44-11' (no delimiter)
  12955. var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;
  12956. // var valueSplitReg = /[\s,]+/;
  12957. function createPathProxyFromString(data) {
  12958. if (!data) {
  12959. return new PathProxy();
  12960. }
  12961. // var data = data.replace(/-/g, ' -')
  12962. // .replace(/ /g, ' ')
  12963. // .replace(/ /g, ',')
  12964. // .replace(/,,/g, ',');
  12965. // var n;
  12966. // create pipes so that we can split the data
  12967. // for (n = 0; n < cc.length; n++) {
  12968. // cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
  12969. // }
  12970. // data = data.replace(/-/g, ',-');
  12971. // create array
  12972. // var arr = cs.split('|');
  12973. // init context point
  12974. var cpx = 0;
  12975. var cpy = 0;
  12976. var subpathX = cpx;
  12977. var subpathY = cpy;
  12978. var prevCmd;
  12979. var path = new PathProxy();
  12980. var CMD = PathProxy.CMD;
  12981. // commandReg.lastIndex = 0;
  12982. // var cmdResult;
  12983. // while ((cmdResult = commandReg.exec(data)) != null) {
  12984. // var cmdStr = cmdResult[1];
  12985. // var cmdContent = cmdResult[2];
  12986. var cmdList = data.match(commandReg);
  12987. for (var l = 0; l < cmdList.length; l++) {
  12988. var cmdText = cmdList[l];
  12989. var cmdStr = cmdText.charAt(0);
  12990. var cmd;
  12991. // String#split is faster a little bit than String#replace or RegExp#exec.
  12992. // var p = cmdContent.split(valueSplitReg);
  12993. // var pLen = 0;
  12994. // for (var i = 0; i < p.length; i++) {
  12995. // // '' and other invalid str => NaN
  12996. // var val = parseFloat(p[i]);
  12997. // !isNaN(val) && (p[pLen++] = val);
  12998. // }
  12999. var p = cmdText.match(numberReg) || [];
  13000. var pLen = p.length;
  13001. for (var i = 0; i < pLen; i++) {
  13002. p[i] = parseFloat(p[i]);
  13003. }
  13004. var off = 0;
  13005. while (off < pLen) {
  13006. var ctlPtx;
  13007. var ctlPty;
  13008. var rx;
  13009. var ry;
  13010. var psi;
  13011. var fa;
  13012. var fs;
  13013. var x1 = cpx;
  13014. var y1 = cpy;
  13015. // convert l, H, h, V, and v to L
  13016. switch (cmdStr) {
  13017. case 'l':
  13018. cpx += p[off++];
  13019. cpy += p[off++];
  13020. cmd = CMD.L;
  13021. path.addData(cmd, cpx, cpy);
  13022. break;
  13023. case 'L':
  13024. cpx = p[off++];
  13025. cpy = p[off++];
  13026. cmd = CMD.L;
  13027. path.addData(cmd, cpx, cpy);
  13028. break;
  13029. case 'm':
  13030. cpx += p[off++];
  13031. cpy += p[off++];
  13032. cmd = CMD.M;
  13033. path.addData(cmd, cpx, cpy);
  13034. subpathX = cpx;
  13035. subpathY = cpy;
  13036. cmdStr = 'l';
  13037. break;
  13038. case 'M':
  13039. cpx = p[off++];
  13040. cpy = p[off++];
  13041. cmd = CMD.M;
  13042. path.addData(cmd, cpx, cpy);
  13043. subpathX = cpx;
  13044. subpathY = cpy;
  13045. cmdStr = 'L';
  13046. break;
  13047. case 'h':
  13048. cpx += p[off++];
  13049. cmd = CMD.L;
  13050. path.addData(cmd, cpx, cpy);
  13051. break;
  13052. case 'H':
  13053. cpx = p[off++];
  13054. cmd = CMD.L;
  13055. path.addData(cmd, cpx, cpy);
  13056. break;
  13057. case 'v':
  13058. cpy += p[off++];
  13059. cmd = CMD.L;
  13060. path.addData(cmd, cpx, cpy);
  13061. break;
  13062. case 'V':
  13063. cpy = p[off++];
  13064. cmd = CMD.L;
  13065. path.addData(cmd, cpx, cpy);
  13066. break;
  13067. case 'C':
  13068. cmd = CMD.C;
  13069. path.addData(
  13070. cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]
  13071. );
  13072. cpx = p[off - 2];
  13073. cpy = p[off - 1];
  13074. break;
  13075. case 'c':
  13076. cmd = CMD.C;
  13077. path.addData(
  13078. cmd,
  13079. p[off++] + cpx, p[off++] + cpy,
  13080. p[off++] + cpx, p[off++] + cpy,
  13081. p[off++] + cpx, p[off++] + cpy
  13082. );
  13083. cpx += p[off - 2];
  13084. cpy += p[off - 1];
  13085. break;
  13086. case 'S':
  13087. ctlPtx = cpx;
  13088. ctlPty = cpy;
  13089. var len = path.len();
  13090. var pathData = path.data;
  13091. if (prevCmd === CMD.C) {
  13092. ctlPtx += cpx - pathData[len - 4];
  13093. ctlPty += cpy - pathData[len - 3];
  13094. }
  13095. cmd = CMD.C;
  13096. x1 = p[off++];
  13097. y1 = p[off++];
  13098. cpx = p[off++];
  13099. cpy = p[off++];
  13100. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  13101. break;
  13102. case 's':
  13103. ctlPtx = cpx;
  13104. ctlPty = cpy;
  13105. var len = path.len();
  13106. var pathData = path.data;
  13107. if (prevCmd === CMD.C) {
  13108. ctlPtx += cpx - pathData[len - 4];
  13109. ctlPty += cpy - pathData[len - 3];
  13110. }
  13111. cmd = CMD.C;
  13112. x1 = cpx + p[off++];
  13113. y1 = cpy + p[off++];
  13114. cpx += p[off++];
  13115. cpy += p[off++];
  13116. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  13117. break;
  13118. case 'Q':
  13119. x1 = p[off++];
  13120. y1 = p[off++];
  13121. cpx = p[off++];
  13122. cpy = p[off++];
  13123. cmd = CMD.Q;
  13124. path.addData(cmd, x1, y1, cpx, cpy);
  13125. break;
  13126. case 'q':
  13127. x1 = p[off++] + cpx;
  13128. y1 = p[off++] + cpy;
  13129. cpx += p[off++];
  13130. cpy += p[off++];
  13131. cmd = CMD.Q;
  13132. path.addData(cmd, x1, y1, cpx, cpy);
  13133. break;
  13134. case 'T':
  13135. ctlPtx = cpx;
  13136. ctlPty = cpy;
  13137. var len = path.len();
  13138. var pathData = path.data;
  13139. if (prevCmd === CMD.Q) {
  13140. ctlPtx += cpx - pathData[len - 4];
  13141. ctlPty += cpy - pathData[len - 3];
  13142. }
  13143. cpx = p[off++];
  13144. cpy = p[off++];
  13145. cmd = CMD.Q;
  13146. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  13147. break;
  13148. case 't':
  13149. ctlPtx = cpx;
  13150. ctlPty = cpy;
  13151. var len = path.len();
  13152. var pathData = path.data;
  13153. if (prevCmd === CMD.Q) {
  13154. ctlPtx += cpx - pathData[len - 4];
  13155. ctlPty += cpy - pathData[len - 3];
  13156. }
  13157. cpx += p[off++];
  13158. cpy += p[off++];
  13159. cmd = CMD.Q;
  13160. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  13161. break;
  13162. case 'A':
  13163. rx = p[off++];
  13164. ry = p[off++];
  13165. psi = p[off++];
  13166. fa = p[off++];
  13167. fs = p[off++];
  13168. x1 = cpx, y1 = cpy;
  13169. cpx = p[off++];
  13170. cpy = p[off++];
  13171. cmd = CMD.A;
  13172. processArc(
  13173. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  13174. );
  13175. break;
  13176. case 'a':
  13177. rx = p[off++];
  13178. ry = p[off++];
  13179. psi = p[off++];
  13180. fa = p[off++];
  13181. fs = p[off++];
  13182. x1 = cpx, y1 = cpy;
  13183. cpx += p[off++];
  13184. cpy += p[off++];
  13185. cmd = CMD.A;
  13186. processArc(
  13187. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  13188. );
  13189. break;
  13190. }
  13191. }
  13192. if (cmdStr === 'z' || cmdStr === 'Z') {
  13193. cmd = CMD.Z;
  13194. path.addData(cmd);
  13195. // z may be in the middle of the path.
  13196. cpx = subpathX;
  13197. cpy = subpathY;
  13198. }
  13199. prevCmd = cmd;
  13200. }
  13201. path.toStatic();
  13202. return path;
  13203. }
  13204. // TODO Optimize double memory cost problem
  13205. function createPathOptions(str, opts) {
  13206. var pathProxy = createPathProxyFromString(str);
  13207. opts = opts || {};
  13208. opts.buildPath = function (path) {
  13209. if (path.setData) {
  13210. path.setData(pathProxy.data);
  13211. // Svg and vml renderer don't have context
  13212. var ctx = path.getContext();
  13213. if (ctx) {
  13214. path.rebuildPath(ctx);
  13215. }
  13216. }
  13217. else {
  13218. var ctx = path;
  13219. pathProxy.rebuildPath(ctx);
  13220. }
  13221. };
  13222. opts.applyTransform = function (m) {
  13223. transformPath(pathProxy, m);
  13224. this.dirty(true);
  13225. };
  13226. return opts;
  13227. }
  13228. /**
  13229. * Create a Path object from path string data
  13230. * http://www.w3.org/TR/SVG/paths.html#PathData
  13231. * @param {Object} opts Other options
  13232. */
  13233. function createFromString(str, opts) {
  13234. return new Path(createPathOptions(str, opts));
  13235. }
  13236. /**
  13237. * Create a Path class from path string data
  13238. * @param {string} str
  13239. * @param {Object} opts Other options
  13240. */
  13241. function extendFromString(str, opts) {
  13242. return Path.extend(createPathOptions(str, opts));
  13243. }
  13244. /**
  13245. * Merge multiple paths
  13246. */
  13247. // TODO Apply transform
  13248. // TODO stroke dash
  13249. // TODO Optimize double memory cost problem
  13250. function mergePath$1(pathEls, opts) {
  13251. var pathList = [];
  13252. var len = pathEls.length;
  13253. for (var i = 0; i < len; i++) {
  13254. var pathEl = pathEls[i];
  13255. if (!pathEl.path) {
  13256. pathEl.createPathProxy();
  13257. }
  13258. if (pathEl.__dirtyPath) {
  13259. pathEl.buildPath(pathEl.path, pathEl.shape, true);
  13260. }
  13261. pathList.push(pathEl.path);
  13262. }
  13263. var pathBundle = new Path(opts);
  13264. // Need path proxy.
  13265. pathBundle.createPathProxy();
  13266. pathBundle.buildPath = function (path) {
  13267. path.appendPath(pathList);
  13268. // Svg and vml renderer don't have context
  13269. var ctx = path.getContext();
  13270. if (ctx) {
  13271. path.rebuildPath(ctx);
  13272. }
  13273. };
  13274. return pathBundle;
  13275. }
  13276. /**
  13277. * @alias zrender/graphic/Text
  13278. * @extends module:zrender/graphic/Displayable
  13279. * @constructor
  13280. * @param {Object} opts
  13281. */
  13282. var Text = function (opts) { // jshint ignore:line
  13283. Displayable.call(this, opts);
  13284. };
  13285. Text.prototype = {
  13286. constructor: Text,
  13287. type: 'text',
  13288. brush: function (ctx, prevEl) {
  13289. var style = this.style;
  13290. // Optimize, avoid normalize every time.
  13291. this.__dirty && normalizeTextStyle(style, true);
  13292. // Use props with prefix 'text'.
  13293. style.fill = style.stroke = style.shadowBlur = style.shadowColor =
  13294. style.shadowOffsetX = style.shadowOffsetY = null;
  13295. var text = style.text;
  13296. // Convert to string
  13297. text != null && (text += '');
  13298. // Do not apply style.bind in Text node. Because the real bind job
  13299. // is in textHelper.renderText, and performance of text render should
  13300. // be considered.
  13301. // style.bind(ctx, this, prevEl);
  13302. if (!needDrawText(text, style)) {
  13303. // The current el.style is not applied
  13304. // and should not be used as cache.
  13305. ctx.__attrCachedBy = ContextCachedBy.NONE;
  13306. return;
  13307. }
  13308. this.setTransform(ctx);
  13309. renderText(this, ctx, text, style, null, prevEl);
  13310. this.restoreTransform(ctx);
  13311. },
  13312. getBoundingRect: function () {
  13313. var style = this.style;
  13314. // Optimize, avoid normalize every time.
  13315. this.__dirty && normalizeTextStyle(style, true);
  13316. if (!this._rect) {
  13317. var text = style.text;
  13318. text != null ? (text += '') : (text = '');
  13319. var rect = getBoundingRect(
  13320. style.text + '',
  13321. style.font,
  13322. style.textAlign,
  13323. style.textVerticalAlign,
  13324. style.textPadding,
  13325. style.textLineHeight,
  13326. style.rich
  13327. );
  13328. rect.x += style.x || 0;
  13329. rect.y += style.y || 0;
  13330. if (getStroke(style.textStroke, style.textStrokeWidth)) {
  13331. var w = style.textStrokeWidth;
  13332. rect.x -= w / 2;
  13333. rect.y -= w / 2;
  13334. rect.width += w;
  13335. rect.height += w;
  13336. }
  13337. this._rect = rect;
  13338. }
  13339. return this._rect;
  13340. }
  13341. };
  13342. inherits(Text, Displayable);
  13343. /**
  13344. * 圆形
  13345. * @module zrender/shape/Circle
  13346. */
  13347. var Circle = Path.extend({
  13348. type: 'circle',
  13349. shape: {
  13350. cx: 0,
  13351. cy: 0,
  13352. r: 0
  13353. },
  13354. buildPath: function (ctx, shape, inBundle) {
  13355. // Better stroking in ShapeBundle
  13356. // Always do it may have performence issue ( fill may be 2x more cost)
  13357. if (inBundle) {
  13358. ctx.moveTo(shape.cx + shape.r, shape.cy);
  13359. }
  13360. // else {
  13361. // if (ctx.allocate && !ctx.data.length) {
  13362. // ctx.allocate(ctx.CMD_MEM_SIZE.A);
  13363. // }
  13364. // }
  13365. // Better stroking in ShapeBundle
  13366. // ctx.moveTo(shape.cx + shape.r, shape.cy);
  13367. ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
  13368. }
  13369. });
  13370. // Fix weird bug in some version of IE11 (like 11.0.9600.178**),
  13371. // where exception "unexpected call to method or property access"
  13372. // might be thrown when calling ctx.fill or ctx.stroke after a path
  13373. // whose area size is zero is drawn and ctx.clip() is called and
  13374. // shadowBlur is set. See #4572, #3112, #5777.
  13375. // (e.g.,
  13376. // ctx.moveTo(10, 10);
  13377. // ctx.lineTo(20, 10);
  13378. // ctx.closePath();
  13379. // ctx.clip();
  13380. // ctx.shadowBlur = 10;
  13381. // ...
  13382. // ctx.fill();
  13383. // )
  13384. var shadowTemp = [
  13385. ['shadowBlur', 0],
  13386. ['shadowColor', '#000'],
  13387. ['shadowOffsetX', 0],
  13388. ['shadowOffsetY', 0]
  13389. ];
  13390. var fixClipWithShadow = function (orignalBrush) {
  13391. // version string can be: '11.0'
  13392. return (env$1.browser.ie && env$1.browser.version >= 11)
  13393. ? function () {
  13394. var clipPaths = this.__clipPaths;
  13395. var style = this.style;
  13396. var modified;
  13397. if (clipPaths) {
  13398. for (var i = 0; i < clipPaths.length; i++) {
  13399. var clipPath = clipPaths[i];
  13400. var shape = clipPath && clipPath.shape;
  13401. var type = clipPath && clipPath.type;
  13402. if (shape && (
  13403. (type === 'sector' && shape.startAngle === shape.endAngle)
  13404. || (type === 'rect' && (!shape.width || !shape.height))
  13405. )) {
  13406. for (var j = 0; j < shadowTemp.length; j++) {
  13407. // It is save to put shadowTemp static, because shadowTemp
  13408. // will be all modified each item brush called.
  13409. shadowTemp[j][2] = style[shadowTemp[j][0]];
  13410. style[shadowTemp[j][0]] = shadowTemp[j][1];
  13411. }
  13412. modified = true;
  13413. break;
  13414. }
  13415. }
  13416. }
  13417. orignalBrush.apply(this, arguments);
  13418. if (modified) {
  13419. for (var j = 0; j < shadowTemp.length; j++) {
  13420. style[shadowTemp[j][0]] = shadowTemp[j][2];
  13421. }
  13422. }
  13423. }
  13424. : orignalBrush;
  13425. };
  13426. /**
  13427. * 扇形
  13428. * @module zrender/graphic/shape/Sector
  13429. */
  13430. var Sector = Path.extend({
  13431. type: 'sector',
  13432. shape: {
  13433. cx: 0,
  13434. cy: 0,
  13435. r0: 0,
  13436. r: 0,
  13437. startAngle: 0,
  13438. endAngle: Math.PI * 2,
  13439. clockwise: true
  13440. },
  13441. brush: fixClipWithShadow(Path.prototype.brush),
  13442. buildPath: function (ctx, shape) {
  13443. var x = shape.cx;
  13444. var y = shape.cy;
  13445. var r0 = Math.max(shape.r0 || 0, 0);
  13446. var r = Math.max(shape.r, 0);
  13447. var startAngle = shape.startAngle;
  13448. var endAngle = shape.endAngle;
  13449. var clockwise = shape.clockwise;
  13450. var unitX = Math.cos(startAngle);
  13451. var unitY = Math.sin(startAngle);
  13452. ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
  13453. ctx.lineTo(unitX * r + x, unitY * r + y);
  13454. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  13455. ctx.lineTo(
  13456. Math.cos(endAngle) * r0 + x,
  13457. Math.sin(endAngle) * r0 + y
  13458. );
  13459. if (r0 !== 0) {
  13460. ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
  13461. }
  13462. ctx.closePath();
  13463. }
  13464. });
  13465. /**
  13466. * 圆环
  13467. * @module zrender/graphic/shape/Ring
  13468. */
  13469. var Ring = Path.extend({
  13470. type: 'ring',
  13471. shape: {
  13472. cx: 0,
  13473. cy: 0,
  13474. r: 0,
  13475. r0: 0
  13476. },
  13477. buildPath: function (ctx, shape) {
  13478. var x = shape.cx;
  13479. var y = shape.cy;
  13480. var PI2 = Math.PI * 2;
  13481. ctx.moveTo(x + shape.r, y);
  13482. ctx.arc(x, y, shape.r, 0, PI2, false);
  13483. ctx.moveTo(x + shape.r0, y);
  13484. ctx.arc(x, y, shape.r0, 0, PI2, true);
  13485. }
  13486. });
  13487. /**
  13488. * Catmull-Rom spline 插值折线
  13489. * @module zrender/shape/util/smoothSpline
  13490. * @author pissang (https://www.github.com/pissang)
  13491. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  13492. * errorrik (errorrik@gmail.com)
  13493. */
  13494. /**
  13495. * @inner
  13496. */
  13497. function interpolate(p0, p1, p2, p3, t, t2, t3) {
  13498. var v0 = (p2 - p0) * 0.5;
  13499. var v1 = (p3 - p1) * 0.5;
  13500. return (2 * (p1 - p2) + v0 + v1) * t3
  13501. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  13502. + v0 * t + p1;
  13503. }
  13504. /**
  13505. * @alias module:zrender/shape/util/smoothSpline
  13506. * @param {Array} points 线段顶点数组
  13507. * @param {boolean} isLoop
  13508. * @return {Array}
  13509. */
  13510. var smoothSpline = function (points, isLoop) {
  13511. var len$$1 = points.length;
  13512. var ret = [];
  13513. var distance$$1 = 0;
  13514. for (var i = 1; i < len$$1; i++) {
  13515. distance$$1 += distance(points[i - 1], points[i]);
  13516. }
  13517. var segs = distance$$1 / 2;
  13518. segs = segs < len$$1 ? len$$1 : segs;
  13519. for (var i = 0; i < segs; i++) {
  13520. var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1);
  13521. var idx = Math.floor(pos);
  13522. var w = pos - idx;
  13523. var p0;
  13524. var p1 = points[idx % len$$1];
  13525. var p2;
  13526. var p3;
  13527. if (!isLoop) {
  13528. p0 = points[idx === 0 ? idx : idx - 1];
  13529. p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
  13530. p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2];
  13531. }
  13532. else {
  13533. p0 = points[(idx - 1 + len$$1) % len$$1];
  13534. p2 = points[(idx + 1) % len$$1];
  13535. p3 = points[(idx + 2) % len$$1];
  13536. }
  13537. var w2 = w * w;
  13538. var w3 = w * w2;
  13539. ret.push([
  13540. interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),
  13541. interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)
  13542. ]);
  13543. }
  13544. return ret;
  13545. };
  13546. /**
  13547. * 贝塞尔平滑曲线
  13548. * @module zrender/shape/util/smoothBezier
  13549. * @author pissang (https://www.github.com/pissang)
  13550. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  13551. * errorrik (errorrik@gmail.com)
  13552. */
  13553. /**
  13554. * 贝塞尔平滑曲线
  13555. * @alias module:zrender/shape/util/smoothBezier
  13556. * @param {Array} points 线段顶点数组
  13557. * @param {number} smooth 平滑等级, 0-1
  13558. * @param {boolean} isLoop
  13559. * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内
  13560. * 比如 [[0, 0], [100, 100]], 这个包围盒会与
  13561. * 整个折线的包围盒做一个并集用来约束控制点。
  13562. * @param {Array} 计算出来的控制点数组
  13563. */
  13564. var smoothBezier = function (points, smooth, isLoop, constraint) {
  13565. var cps = [];
  13566. var v = [];
  13567. var v1 = [];
  13568. var v2 = [];
  13569. var prevPoint;
  13570. var nextPoint;
  13571. var min$$1;
  13572. var max$$1;
  13573. if (constraint) {
  13574. min$$1 = [Infinity, Infinity];
  13575. max$$1 = [-Infinity, -Infinity];
  13576. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  13577. min(min$$1, min$$1, points[i]);
  13578. max(max$$1, max$$1, points[i]);
  13579. }
  13580. // 与指定的包围盒做并集
  13581. min(min$$1, min$$1, constraint[0]);
  13582. max(max$$1, max$$1, constraint[1]);
  13583. }
  13584. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  13585. var point = points[i];
  13586. if (isLoop) {
  13587. prevPoint = points[i ? i - 1 : len$$1 - 1];
  13588. nextPoint = points[(i + 1) % len$$1];
  13589. }
  13590. else {
  13591. if (i === 0 || i === len$$1 - 1) {
  13592. cps.push(clone$1(points[i]));
  13593. continue;
  13594. }
  13595. else {
  13596. prevPoint = points[i - 1];
  13597. nextPoint = points[i + 1];
  13598. }
  13599. }
  13600. sub(v, nextPoint, prevPoint);
  13601. // use degree to scale the handle length
  13602. scale(v, v, smooth);
  13603. var d0 = distance(point, prevPoint);
  13604. var d1 = distance(point, nextPoint);
  13605. var sum = d0 + d1;
  13606. if (sum !== 0) {
  13607. d0 /= sum;
  13608. d1 /= sum;
  13609. }
  13610. scale(v1, v, -d0);
  13611. scale(v2, v, d1);
  13612. var cp0 = add([], point, v1);
  13613. var cp1 = add([], point, v2);
  13614. if (constraint) {
  13615. max(cp0, cp0, min$$1);
  13616. min(cp0, cp0, max$$1);
  13617. max(cp1, cp1, min$$1);
  13618. min(cp1, cp1, max$$1);
  13619. }
  13620. cps.push(cp0);
  13621. cps.push(cp1);
  13622. }
  13623. if (isLoop) {
  13624. cps.push(cps.shift());
  13625. }
  13626. return cps;
  13627. };
  13628. function buildPath$1(ctx, shape, closePath) {
  13629. var points = shape.points;
  13630. var smooth = shape.smooth;
  13631. if (points && points.length >= 2) {
  13632. if (smooth && smooth !== 'spline') {
  13633. var controlPoints = smoothBezier(
  13634. points, smooth, closePath, shape.smoothConstraint
  13635. );
  13636. ctx.moveTo(points[0][0], points[0][1]);
  13637. var len = points.length;
  13638. for (var i = 0; i < (closePath ? len : len - 1); i++) {
  13639. var cp1 = controlPoints[i * 2];
  13640. var cp2 = controlPoints[i * 2 + 1];
  13641. var p = points[(i + 1) % len];
  13642. ctx.bezierCurveTo(
  13643. cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]
  13644. );
  13645. }
  13646. }
  13647. else {
  13648. if (smooth === 'spline') {
  13649. points = smoothSpline(points, closePath);
  13650. }
  13651. ctx.moveTo(points[0][0], points[0][1]);
  13652. for (var i = 1, l = points.length; i < l; i++) {
  13653. ctx.lineTo(points[i][0], points[i][1]);
  13654. }
  13655. }
  13656. closePath && ctx.closePath();
  13657. }
  13658. }
  13659. /**
  13660. * 多边形
  13661. * @module zrender/shape/Polygon
  13662. */
  13663. var Polygon = Path.extend({
  13664. type: 'polygon',
  13665. shape: {
  13666. points: null,
  13667. smooth: false,
  13668. smoothConstraint: null
  13669. },
  13670. buildPath: function (ctx, shape) {
  13671. buildPath$1(ctx, shape, true);
  13672. }
  13673. });
  13674. /**
  13675. * @module zrender/graphic/shape/Polyline
  13676. */
  13677. var Polyline = Path.extend({
  13678. type: 'polyline',
  13679. shape: {
  13680. points: null,
  13681. smooth: false,
  13682. smoothConstraint: null
  13683. },
  13684. style: {
  13685. stroke: '#000',
  13686. fill: null
  13687. },
  13688. buildPath: function (ctx, shape) {
  13689. buildPath$1(ctx, shape, false);
  13690. }
  13691. });
  13692. /**
  13693. * Sub-pixel optimize for canvas rendering, prevent from blur
  13694. * when rendering a thin vertical/horizontal line.
  13695. */
  13696. var round$1 = Math.round;
  13697. /**
  13698. * Sub pixel optimize line for canvas
  13699. *
  13700. * @param {Object} outputShape The modification will be performed on `outputShape`.
  13701. * `outputShape` and `inputShape` can be the same object.
  13702. * `outputShape` object can be used repeatly, because all of
  13703. * the `x1`, `x2`, `y1`, `y2` will be assigned in this method.
  13704. * @param {Object} [inputShape]
  13705. * @param {number} [inputShape.x1]
  13706. * @param {number} [inputShape.y1]
  13707. * @param {number} [inputShape.x2]
  13708. * @param {number} [inputShape.y2]
  13709. * @param {Object} [style]
  13710. * @param {number} [style.lineWidth]
  13711. */
  13712. function subPixelOptimizeLine$1(outputShape, inputShape, style) {
  13713. var lineWidth = style && style.lineWidth;
  13714. if (!inputShape || !lineWidth) {
  13715. return;
  13716. }
  13717. var x1 = inputShape.x1;
  13718. var x2 = inputShape.x2;
  13719. var y1 = inputShape.y1;
  13720. var y2 = inputShape.y2;
  13721. if (round$1(x1 * 2) === round$1(x2 * 2)) {
  13722. outputShape.x1 = outputShape.x2 = subPixelOptimize$1(x1, lineWidth, true);
  13723. }
  13724. else {
  13725. outputShape.x1 = x1;
  13726. outputShape.x2 = x2;
  13727. }
  13728. if (round$1(y1 * 2) === round$1(y2 * 2)) {
  13729. outputShape.y1 = outputShape.y2 = subPixelOptimize$1(y1, lineWidth, true);
  13730. }
  13731. else {
  13732. outputShape.y1 = y1;
  13733. outputShape.y2 = y2;
  13734. }
  13735. }
  13736. /**
  13737. * Sub pixel optimize rect for canvas
  13738. *
  13739. * @param {Object} outputShape The modification will be performed on `outputShape`.
  13740. * `outputShape` and `inputShape` can be the same object.
  13741. * `outputShape` object can be used repeatly, because all of
  13742. * the `x`, `y`, `width`, `height` will be assigned in this method.
  13743. * @param {Object} [inputShape]
  13744. * @param {number} [inputShape.x]
  13745. * @param {number} [inputShape.y]
  13746. * @param {number} [inputShape.width]
  13747. * @param {number} [inputShape.height]
  13748. * @param {Object} [style]
  13749. * @param {number} [style.lineWidth]
  13750. */
  13751. function subPixelOptimizeRect$1(outputShape, inputShape, style) {
  13752. var lineWidth = style && style.lineWidth;
  13753. if (!inputShape || !lineWidth) {
  13754. return;
  13755. }
  13756. var originX = inputShape.x;
  13757. var originY = inputShape.y;
  13758. var originWidth = inputShape.width;
  13759. var originHeight = inputShape.height;
  13760. outputShape.x = subPixelOptimize$1(originX, lineWidth, true);
  13761. outputShape.y = subPixelOptimize$1(originY, lineWidth, true);
  13762. outputShape.width = Math.max(
  13763. subPixelOptimize$1(originX + originWidth, lineWidth, false) - outputShape.x,
  13764. originWidth === 0 ? 0 : 1
  13765. );
  13766. outputShape.height = Math.max(
  13767. subPixelOptimize$1(originY + originHeight, lineWidth, false) - outputShape.y,
  13768. originHeight === 0 ? 0 : 1
  13769. );
  13770. }
  13771. /**
  13772. * Sub pixel optimize for canvas
  13773. *
  13774. * @param {number} position Coordinate, such as x, y
  13775. * @param {number} lineWidth Should be nonnegative integer.
  13776. * @param {boolean=} positiveOrNegative Default false (negative).
  13777. * @return {number} Optimized position.
  13778. */
  13779. function subPixelOptimize$1(position, lineWidth, positiveOrNegative) {
  13780. // Assure that (position + lineWidth / 2) is near integer edge,
  13781. // otherwise line will be fuzzy in canvas.
  13782. var doubledPosition = round$1(position * 2);
  13783. return (doubledPosition + round$1(lineWidth)) % 2 === 0
  13784. ? doubledPosition / 2
  13785. : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  13786. }
  13787. /**
  13788. * 矩形
  13789. * @module zrender/graphic/shape/Rect
  13790. */
  13791. // Avoid create repeatly.
  13792. var subPixelOptimizeOutputShape = {};
  13793. var Rect = Path.extend({
  13794. type: 'rect',
  13795. shape: {
  13796. // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
  13797. // r缩写为1 相当于 [1, 1, 1, 1]
  13798. // r缩写为[1] 相当于 [1, 1, 1, 1]
  13799. // r缩写为[1, 2] 相当于 [1, 2, 1, 2]
  13800. // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
  13801. r: 0,
  13802. x: 0,
  13803. y: 0,
  13804. width: 0,
  13805. height: 0
  13806. },
  13807. buildPath: function (ctx, shape) {
  13808. var x;
  13809. var y;
  13810. var width;
  13811. var height;
  13812. if (this.subPixelOptimize) {
  13813. subPixelOptimizeRect$1(subPixelOptimizeOutputShape, shape, this.style);
  13814. x = subPixelOptimizeOutputShape.x;
  13815. y = subPixelOptimizeOutputShape.y;
  13816. width = subPixelOptimizeOutputShape.width;
  13817. height = subPixelOptimizeOutputShape.height;
  13818. subPixelOptimizeOutputShape.r = shape.r;
  13819. shape = subPixelOptimizeOutputShape;
  13820. }
  13821. else {
  13822. x = shape.x;
  13823. y = shape.y;
  13824. width = shape.width;
  13825. height = shape.height;
  13826. }
  13827. if (!shape.r) {
  13828. ctx.rect(x, y, width, height);
  13829. }
  13830. else {
  13831. buildPath(ctx, shape);
  13832. }
  13833. ctx.closePath();
  13834. return;
  13835. }
  13836. });
  13837. /**
  13838. * 直线
  13839. * @module zrender/graphic/shape/Line
  13840. */
  13841. // Avoid create repeatly.
  13842. var subPixelOptimizeOutputShape$1 = {};
  13843. var Line = Path.extend({
  13844. type: 'line',
  13845. shape: {
  13846. // Start point
  13847. x1: 0,
  13848. y1: 0,
  13849. // End point
  13850. x2: 0,
  13851. y2: 0,
  13852. percent: 1
  13853. },
  13854. style: {
  13855. stroke: '#000',
  13856. fill: null
  13857. },
  13858. buildPath: function (ctx, shape) {
  13859. var x1;
  13860. var y1;
  13861. var x2;
  13862. var y2;
  13863. if (this.subPixelOptimize) {
  13864. subPixelOptimizeLine$1(subPixelOptimizeOutputShape$1, shape, this.style);
  13865. x1 = subPixelOptimizeOutputShape$1.x1;
  13866. y1 = subPixelOptimizeOutputShape$1.y1;
  13867. x2 = subPixelOptimizeOutputShape$1.x2;
  13868. y2 = subPixelOptimizeOutputShape$1.y2;
  13869. }
  13870. else {
  13871. x1 = shape.x1;
  13872. y1 = shape.y1;
  13873. x2 = shape.x2;
  13874. y2 = shape.y2;
  13875. }
  13876. var percent = shape.percent;
  13877. if (percent === 0) {
  13878. return;
  13879. }
  13880. ctx.moveTo(x1, y1);
  13881. if (percent < 1) {
  13882. x2 = x1 * (1 - percent) + x2 * percent;
  13883. y2 = y1 * (1 - percent) + y2 * percent;
  13884. }
  13885. ctx.lineTo(x2, y2);
  13886. },
  13887. /**
  13888. * Get point at percent
  13889. * @param {number} percent
  13890. * @return {Array.<number>}
  13891. */
  13892. pointAt: function (p) {
  13893. var shape = this.shape;
  13894. return [
  13895. shape.x1 * (1 - p) + shape.x2 * p,
  13896. shape.y1 * (1 - p) + shape.y2 * p
  13897. ];
  13898. }
  13899. });
  13900. /**
  13901. * 贝塞尔曲线
  13902. * @module zrender/shape/BezierCurve
  13903. */
  13904. var out = [];
  13905. function someVectorAt(shape, t, isTangent) {
  13906. var cpx2 = shape.cpx2;
  13907. var cpy2 = shape.cpy2;
  13908. if (cpx2 === null || cpy2 === null) {
  13909. return [
  13910. (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
  13911. (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
  13912. ];
  13913. }
  13914. else {
  13915. return [
  13916. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
  13917. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
  13918. ];
  13919. }
  13920. }
  13921. var BezierCurve = Path.extend({
  13922. type: 'bezier-curve',
  13923. shape: {
  13924. x1: 0,
  13925. y1: 0,
  13926. x2: 0,
  13927. y2: 0,
  13928. cpx1: 0,
  13929. cpy1: 0,
  13930. // cpx2: 0,
  13931. // cpy2: 0
  13932. // Curve show percent, for animating
  13933. percent: 1
  13934. },
  13935. style: {
  13936. stroke: '#000',
  13937. fill: null
  13938. },
  13939. buildPath: function (ctx, shape) {
  13940. var x1 = shape.x1;
  13941. var y1 = shape.y1;
  13942. var x2 = shape.x2;
  13943. var y2 = shape.y2;
  13944. var cpx1 = shape.cpx1;
  13945. var cpy1 = shape.cpy1;
  13946. var cpx2 = shape.cpx2;
  13947. var cpy2 = shape.cpy2;
  13948. var percent = shape.percent;
  13949. if (percent === 0) {
  13950. return;
  13951. }
  13952. ctx.moveTo(x1, y1);
  13953. if (cpx2 == null || cpy2 == null) {
  13954. if (percent < 1) {
  13955. quadraticSubdivide(
  13956. x1, cpx1, x2, percent, out
  13957. );
  13958. cpx1 = out[1];
  13959. x2 = out[2];
  13960. quadraticSubdivide(
  13961. y1, cpy1, y2, percent, out
  13962. );
  13963. cpy1 = out[1];
  13964. y2 = out[2];
  13965. }
  13966. ctx.quadraticCurveTo(
  13967. cpx1, cpy1,
  13968. x2, y2
  13969. );
  13970. }
  13971. else {
  13972. if (percent < 1) {
  13973. cubicSubdivide(
  13974. x1, cpx1, cpx2, x2, percent, out
  13975. );
  13976. cpx1 = out[1];
  13977. cpx2 = out[2];
  13978. x2 = out[3];
  13979. cubicSubdivide(
  13980. y1, cpy1, cpy2, y2, percent, out
  13981. );
  13982. cpy1 = out[1];
  13983. cpy2 = out[2];
  13984. y2 = out[3];
  13985. }
  13986. ctx.bezierCurveTo(
  13987. cpx1, cpy1,
  13988. cpx2, cpy2,
  13989. x2, y2
  13990. );
  13991. }
  13992. },
  13993. /**
  13994. * Get point at percent
  13995. * @param {number} t
  13996. * @return {Array.<number>}
  13997. */
  13998. pointAt: function (t) {
  13999. return someVectorAt(this.shape, t, false);
  14000. },
  14001. /**
  14002. * Get tangent at percent
  14003. * @param {number} t
  14004. * @return {Array.<number>}
  14005. */
  14006. tangentAt: function (t) {
  14007. var p = someVectorAt(this.shape, t, true);
  14008. return normalize(p, p);
  14009. }
  14010. });
  14011. /**
  14012. * 圆弧
  14013. * @module zrender/graphic/shape/Arc
  14014. */
  14015. var Arc = Path.extend({
  14016. type: 'arc',
  14017. shape: {
  14018. cx: 0,
  14019. cy: 0,
  14020. r: 0,
  14021. startAngle: 0,
  14022. endAngle: Math.PI * 2,
  14023. clockwise: true
  14024. },
  14025. style: {
  14026. stroke: '#000',
  14027. fill: null
  14028. },
  14029. buildPath: function (ctx, shape) {
  14030. var x = shape.cx;
  14031. var y = shape.cy;
  14032. var r = Math.max(shape.r, 0);
  14033. var startAngle = shape.startAngle;
  14034. var endAngle = shape.endAngle;
  14035. var clockwise = shape.clockwise;
  14036. var unitX = Math.cos(startAngle);
  14037. var unitY = Math.sin(startAngle);
  14038. ctx.moveTo(unitX * r + x, unitY * r + y);
  14039. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  14040. }
  14041. });
  14042. // CompoundPath to improve performance
  14043. var CompoundPath = Path.extend({
  14044. type: 'compound',
  14045. shape: {
  14046. paths: null
  14047. },
  14048. _updatePathDirty: function () {
  14049. var dirtyPath = this.__dirtyPath;
  14050. var paths = this.shape.paths;
  14051. for (var i = 0; i < paths.length; i++) {
  14052. // Mark as dirty if any subpath is dirty
  14053. dirtyPath = dirtyPath || paths[i].__dirtyPath;
  14054. }
  14055. this.__dirtyPath = dirtyPath;
  14056. this.__dirty = this.__dirty || dirtyPath;
  14057. },
  14058. beforeBrush: function () {
  14059. this._updatePathDirty();
  14060. var paths = this.shape.paths || [];
  14061. var scale = this.getGlobalScale();
  14062. // Update path scale
  14063. for (var i = 0; i < paths.length; i++) {
  14064. if (!paths[i].path) {
  14065. paths[i].createPathProxy();
  14066. }
  14067. paths[i].path.setScale(scale[0], scale[1]);
  14068. }
  14069. },
  14070. buildPath: function (ctx, shape) {
  14071. var paths = shape.paths || [];
  14072. for (var i = 0; i < paths.length; i++) {
  14073. paths[i].buildPath(ctx, paths[i].shape, true);
  14074. }
  14075. },
  14076. afterBrush: function () {
  14077. var paths = this.shape.paths || [];
  14078. for (var i = 0; i < paths.length; i++) {
  14079. paths[i].__dirtyPath = false;
  14080. }
  14081. },
  14082. getBoundingRect: function () {
  14083. this._updatePathDirty();
  14084. return Path.prototype.getBoundingRect.call(this);
  14085. }
  14086. });
  14087. /**
  14088. * @param {Array.<Object>} colorStops
  14089. */
  14090. var Gradient = function (colorStops) {
  14091. this.colorStops = colorStops || [];
  14092. };
  14093. Gradient.prototype = {
  14094. constructor: Gradient,
  14095. addColorStop: function (offset, color) {
  14096. this.colorStops.push({
  14097. offset: offset,
  14098. color: color
  14099. });
  14100. }
  14101. };
  14102. /**
  14103. * x, y, x2, y2 are all percent from 0 to 1
  14104. * @param {number} [x=0]
  14105. * @param {number} [y=0]
  14106. * @param {number} [x2=1]
  14107. * @param {number} [y2=0]
  14108. * @param {Array.<Object>} colorStops
  14109. * @param {boolean} [globalCoord=false]
  14110. */
  14111. var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
  14112. // Should do nothing more in this constructor. Because gradient can be
  14113. // declard by `color: {type: 'linear', colorStops: ...}`, where
  14114. // this constructor will not be called.
  14115. this.x = x == null ? 0 : x;
  14116. this.y = y == null ? 0 : y;
  14117. this.x2 = x2 == null ? 1 : x2;
  14118. this.y2 = y2 == null ? 0 : y2;
  14119. // Can be cloned
  14120. this.type = 'linear';
  14121. // If use global coord
  14122. this.global = globalCoord || false;
  14123. Gradient.call(this, colorStops);
  14124. };
  14125. LinearGradient.prototype = {
  14126. constructor: LinearGradient
  14127. };
  14128. inherits(LinearGradient, Gradient);
  14129. /**
  14130. * x, y, r are all percent from 0 to 1
  14131. * @param {number} [x=0.5]
  14132. * @param {number} [y=0.5]
  14133. * @param {number} [r=0.5]
  14134. * @param {Array.<Object>} [colorStops]
  14135. * @param {boolean} [globalCoord=false]
  14136. */
  14137. var RadialGradient = function (x, y, r, colorStops, globalCoord) {
  14138. // Should do nothing more in this constructor. Because gradient can be
  14139. // declard by `color: {type: 'radial', colorStops: ...}`, where
  14140. // this constructor will not be called.
  14141. this.x = x == null ? 0.5 : x;
  14142. this.y = y == null ? 0.5 : y;
  14143. this.r = r == null ? 0.5 : r;
  14144. // Can be cloned
  14145. this.type = 'radial';
  14146. // If use global coord
  14147. this.global = globalCoord || false;
  14148. Gradient.call(this, colorStops);
  14149. };
  14150. RadialGradient.prototype = {
  14151. constructor: RadialGradient
  14152. };
  14153. inherits(RadialGradient, Gradient);
  14154. /**
  14155. * Displayable for incremental rendering. It will be rendered in a separate layer
  14156. * IncrementalDisplay have two main methods. `clearDisplayables` and `addDisplayables`
  14157. * addDisplayables will render the added displayables incremetally.
  14158. *
  14159. * It use a not clearFlag to tell the painter don't clear the layer if it's the first element.
  14160. */
  14161. // TODO Style override ?
  14162. function IncrementalDisplayble(opts) {
  14163. Displayable.call(this, opts);
  14164. this._displayables = [];
  14165. this._temporaryDisplayables = [];
  14166. this._cursor = 0;
  14167. this.notClear = true;
  14168. }
  14169. IncrementalDisplayble.prototype.incremental = true;
  14170. IncrementalDisplayble.prototype.clearDisplaybles = function () {
  14171. this._displayables = [];
  14172. this._temporaryDisplayables = [];
  14173. this._cursor = 0;
  14174. this.dirty();
  14175. this.notClear = false;
  14176. };
  14177. IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) {
  14178. if (notPersistent) {
  14179. this._temporaryDisplayables.push(displayable);
  14180. }
  14181. else {
  14182. this._displayables.push(displayable);
  14183. }
  14184. this.dirty();
  14185. };
  14186. IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) {
  14187. notPersistent = notPersistent || false;
  14188. for (var i = 0; i < displayables.length; i++) {
  14189. this.addDisplayable(displayables[i], notPersistent);
  14190. }
  14191. };
  14192. IncrementalDisplayble.prototype.eachPendingDisplayable = function (cb) {
  14193. for (var i = this._cursor; i < this._displayables.length; i++) {
  14194. cb && cb(this._displayables[i]);
  14195. }
  14196. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  14197. cb && cb(this._temporaryDisplayables[i]);
  14198. }
  14199. };
  14200. IncrementalDisplayble.prototype.update = function () {
  14201. this.updateTransform();
  14202. for (var i = this._cursor; i < this._displayables.length; i++) {
  14203. var displayable = this._displayables[i];
  14204. // PENDING
  14205. displayable.parent = this;
  14206. displayable.update();
  14207. displayable.parent = null;
  14208. }
  14209. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  14210. var displayable = this._temporaryDisplayables[i];
  14211. // PENDING
  14212. displayable.parent = this;
  14213. displayable.update();
  14214. displayable.parent = null;
  14215. }
  14216. };
  14217. IncrementalDisplayble.prototype.brush = function (ctx, prevEl) {
  14218. // Render persistant displayables.
  14219. for (var i = this._cursor; i < this._displayables.length; i++) {
  14220. var displayable = this._displayables[i];
  14221. displayable.beforeBrush && displayable.beforeBrush(ctx);
  14222. displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]);
  14223. displayable.afterBrush && displayable.afterBrush(ctx);
  14224. }
  14225. this._cursor = i;
  14226. // Render temporary displayables.
  14227. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  14228. var displayable = this._temporaryDisplayables[i];
  14229. displayable.beforeBrush && displayable.beforeBrush(ctx);
  14230. displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]);
  14231. displayable.afterBrush && displayable.afterBrush(ctx);
  14232. }
  14233. this._temporaryDisplayables = [];
  14234. this.notClear = true;
  14235. };
  14236. var m = [];
  14237. IncrementalDisplayble.prototype.getBoundingRect = function () {
  14238. if (!this._rect) {
  14239. var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);
  14240. for (var i = 0; i < this._displayables.length; i++) {
  14241. var displayable = this._displayables[i];
  14242. var childRect = displayable.getBoundingRect().clone();
  14243. if (displayable.needLocalTransform()) {
  14244. childRect.applyTransform(displayable.getLocalTransform(m));
  14245. }
  14246. rect.union(childRect);
  14247. }
  14248. this._rect = rect;
  14249. }
  14250. return this._rect;
  14251. };
  14252. IncrementalDisplayble.prototype.contain = function (x, y) {
  14253. var localPos = this.transformCoordToLocal(x, y);
  14254. var rect = this.getBoundingRect();
  14255. if (rect.contain(localPos[0], localPos[1])) {
  14256. for (var i = 0; i < this._displayables.length; i++) {
  14257. var displayable = this._displayables[i];
  14258. if (displayable.contain(x, y)) {
  14259. return true;
  14260. }
  14261. }
  14262. }
  14263. return false;
  14264. };
  14265. inherits(IncrementalDisplayble, Displayable);
  14266. /*
  14267. * Licensed to the Apache Software Foundation (ASF) under one
  14268. * or more contributor license agreements. See the NOTICE file
  14269. * distributed with this work for additional information
  14270. * regarding copyright ownership. The ASF licenses this file
  14271. * to you under the Apache License, Version 2.0 (the
  14272. * "License"); you may not use this file except in compliance
  14273. * with the License. You may obtain a copy of the License at
  14274. *
  14275. * http://www.apache.org/licenses/LICENSE-2.0
  14276. *
  14277. * Unless required by applicable law or agreed to in writing,
  14278. * software distributed under the License is distributed on an
  14279. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14280. * KIND, either express or implied. See the License for the
  14281. * specific language governing permissions and limitations
  14282. * under the License.
  14283. */
  14284. var round = Math.round;
  14285. var mathMax$1 = Math.max;
  14286. var mathMin$1 = Math.min;
  14287. var EMPTY_OBJ = {};
  14288. var Z2_EMPHASIS_LIFT = 1;
  14289. /**
  14290. * Extend shape with parameters
  14291. */
  14292. function extendShape(opts) {
  14293. return Path.extend(opts);
  14294. }
  14295. /**
  14296. * Extend path
  14297. */
  14298. function extendPath(pathData, opts) {
  14299. return extendFromString(pathData, opts);
  14300. }
  14301. /**
  14302. * Create a path element from path data string
  14303. * @param {string} pathData
  14304. * @param {Object} opts
  14305. * @param {module:zrender/core/BoundingRect} rect
  14306. * @param {string} [layout=cover] 'center' or 'cover'
  14307. */
  14308. function makePath(pathData, opts, rect, layout) {
  14309. var path = createFromString(pathData, opts);
  14310. if (rect) {
  14311. if (layout === 'center') {
  14312. rect = centerGraphic(rect, path.getBoundingRect());
  14313. }
  14314. resizePath(path, rect);
  14315. }
  14316. return path;
  14317. }
  14318. /**
  14319. * Create a image element from image url
  14320. * @param {string} imageUrl image url
  14321. * @param {Object} opts options
  14322. * @param {module:zrender/core/BoundingRect} rect constrain rect
  14323. * @param {string} [layout=cover] 'center' or 'cover'
  14324. */
  14325. function makeImage(imageUrl, rect, layout) {
  14326. var path = new ZImage({
  14327. style: {
  14328. image: imageUrl,
  14329. x: rect.x,
  14330. y: rect.y,
  14331. width: rect.width,
  14332. height: rect.height
  14333. },
  14334. onload: function (img) {
  14335. if (layout === 'center') {
  14336. var boundingRect = {
  14337. width: img.width,
  14338. height: img.height
  14339. };
  14340. path.setStyle(centerGraphic(rect, boundingRect));
  14341. }
  14342. }
  14343. });
  14344. return path;
  14345. }
  14346. /**
  14347. * Get position of centered element in bounding box.
  14348. *
  14349. * @param {Object} rect element local bounding box
  14350. * @param {Object} boundingRect constraint bounding box
  14351. * @return {Object} element position containing x, y, width, and height
  14352. */
  14353. function centerGraphic(rect, boundingRect) {
  14354. // Set rect to center, keep width / height ratio.
  14355. var aspect = boundingRect.width / boundingRect.height;
  14356. var width = rect.height * aspect;
  14357. var height;
  14358. if (width <= rect.width) {
  14359. height = rect.height;
  14360. }
  14361. else {
  14362. width = rect.width;
  14363. height = width / aspect;
  14364. }
  14365. var cx = rect.x + rect.width / 2;
  14366. var cy = rect.y + rect.height / 2;
  14367. return {
  14368. x: cx - width / 2,
  14369. y: cy - height / 2,
  14370. width: width,
  14371. height: height
  14372. };
  14373. }
  14374. var mergePath = mergePath$1;
  14375. /**
  14376. * Resize a path to fit the rect
  14377. * @param {module:zrender/graphic/Path} path
  14378. * @param {Object} rect
  14379. */
  14380. function resizePath(path, rect) {
  14381. if (!path.applyTransform) {
  14382. return;
  14383. }
  14384. var pathRect = path.getBoundingRect();
  14385. var m = pathRect.calculateTransform(rect);
  14386. path.applyTransform(m);
  14387. }
  14388. /**
  14389. * Sub pixel optimize line for canvas
  14390. *
  14391. * @param {Object} param
  14392. * @param {Object} [param.shape]
  14393. * @param {number} [param.shape.x1]
  14394. * @param {number} [param.shape.y1]
  14395. * @param {number} [param.shape.x2]
  14396. * @param {number} [param.shape.y2]
  14397. * @param {Object} [param.style]
  14398. * @param {number} [param.style.lineWidth]
  14399. * @return {Object} Modified param
  14400. */
  14401. function subPixelOptimizeLine(param) {
  14402. var shape = param.shape;
  14403. var lineWidth = param.style.lineWidth;
  14404. if (round(shape.x1 * 2) === round(shape.x2 * 2)) {
  14405. shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
  14406. }
  14407. if (round(shape.y1 * 2) === round(shape.y2 * 2)) {
  14408. shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
  14409. }
  14410. return param;
  14411. }
  14412. /**
  14413. * Sub pixel optimize rect for canvas
  14414. *
  14415. * @param {Object} param
  14416. * @param {Object} [param.shape]
  14417. * @param {number} [param.shape.x]
  14418. * @param {number} [param.shape.y]
  14419. * @param {number} [param.shape.width]
  14420. * @param {number} [param.shape.height]
  14421. * @param {Object} [param.style]
  14422. * @param {number} [param.style.lineWidth]
  14423. * @return {Object} Modified param
  14424. */
  14425. function subPixelOptimizeRect(param) {
  14426. var shape = param.shape;
  14427. var lineWidth = param.style.lineWidth;
  14428. var originX = shape.x;
  14429. var originY = shape.y;
  14430. var originWidth = shape.width;
  14431. var originHeight = shape.height;
  14432. shape.x = subPixelOptimize(shape.x, lineWidth, true);
  14433. shape.y = subPixelOptimize(shape.y, lineWidth, true);
  14434. shape.width = Math.max(
  14435. subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,
  14436. originWidth === 0 ? 0 : 1
  14437. );
  14438. shape.height = Math.max(
  14439. subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,
  14440. originHeight === 0 ? 0 : 1
  14441. );
  14442. return param;
  14443. }
  14444. /**
  14445. * Sub pixel optimize for canvas
  14446. *
  14447. * @param {number} position Coordinate, such as x, y
  14448. * @param {number} lineWidth Should be nonnegative integer.
  14449. * @param {boolean=} positiveOrNegative Default false (negative).
  14450. * @return {number} Optimized position.
  14451. */
  14452. function subPixelOptimize(position, lineWidth, positiveOrNegative) {
  14453. // Assure that (position + lineWidth / 2) is near integer edge,
  14454. // otherwise line will be fuzzy in canvas.
  14455. var doubledPosition = round(position * 2);
  14456. return (doubledPosition + round(lineWidth)) % 2 === 0
  14457. ? doubledPosition / 2
  14458. : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  14459. }
  14460. function hasFillOrStroke(fillOrStroke) {
  14461. return fillOrStroke != null && fillOrStroke !== 'none';
  14462. }
  14463. // Most lifted color are duplicated.
  14464. var liftedColorMap = createHashMap();
  14465. var liftedColorCount = 0;
  14466. function liftColor(color) {
  14467. if (typeof color !== 'string') {
  14468. return color;
  14469. }
  14470. var liftedColor = liftedColorMap.get(color);
  14471. if (!liftedColor) {
  14472. liftedColor = lift(color, -0.1);
  14473. if (liftedColorCount < 10000) {
  14474. liftedColorMap.set(color, liftedColor);
  14475. liftedColorCount++;
  14476. }
  14477. }
  14478. return liftedColor;
  14479. }
  14480. function cacheElementStl(el) {
  14481. if (!el.__hoverStlDirty) {
  14482. return;
  14483. }
  14484. el.__hoverStlDirty = false;
  14485. var hoverStyle = el.__hoverStl;
  14486. if (!hoverStyle) {
  14487. el.__cachedNormalStl = el.__cachedNormalZ2 = null;
  14488. return;
  14489. }
  14490. var normalStyle = el.__cachedNormalStl = {};
  14491. el.__cachedNormalZ2 = el.z2;
  14492. var elStyle = el.style;
  14493. for (var name in hoverStyle) {
  14494. // See comment in `doSingleEnterHover`.
  14495. if (hoverStyle[name] != null) {
  14496. normalStyle[name] = elStyle[name];
  14497. }
  14498. }
  14499. // Always cache fill and stroke to normalStyle for lifting color.
  14500. normalStyle.fill = elStyle.fill;
  14501. normalStyle.stroke = elStyle.stroke;
  14502. }
  14503. function doSingleEnterHover(el) {
  14504. var hoverStl = el.__hoverStl;
  14505. if (!hoverStl || el.__highlighted) {
  14506. return;
  14507. }
  14508. var useHoverLayer = el.useHoverLayer;
  14509. el.__highlighted = useHoverLayer ? 'layer' : 'plain';
  14510. var zr = el.__zr;
  14511. if (!zr && useHoverLayer) {
  14512. return;
  14513. }
  14514. var elTarget = el;
  14515. var targetStyle = el.style;
  14516. if (useHoverLayer) {
  14517. elTarget = zr.addHover(el);
  14518. targetStyle = elTarget.style;
  14519. }
  14520. rollbackDefaultTextStyle(targetStyle);
  14521. if (!useHoverLayer) {
  14522. cacheElementStl(elTarget);
  14523. }
  14524. // styles can be:
  14525. // {
  14526. // label: {
  14527. // show: false,
  14528. // position: 'outside',
  14529. // fontSize: 18
  14530. // },
  14531. // emphasis: {
  14532. // label: {
  14533. // show: true
  14534. // }
  14535. // }
  14536. // },
  14537. // where properties of `emphasis` may not appear in `normal`. We previously use
  14538. // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
  14539. // But consider rich text and setOption in merge mode, it is impossible to cover
  14540. // all properties in merge. So we use merge mode when setting style here, where
  14541. // only properties that is not `null/undefined` can be set. The disadventage:
  14542. // null/undefined can not be used to remove style any more in `emphasis`.
  14543. targetStyle.extendFrom(hoverStl);
  14544. setDefaultHoverFillStroke(targetStyle, hoverStl, 'fill');
  14545. setDefaultHoverFillStroke(targetStyle, hoverStl, 'stroke');
  14546. applyDefaultTextStyle(targetStyle);
  14547. if (!useHoverLayer) {
  14548. el.dirty(false);
  14549. el.z2 += Z2_EMPHASIS_LIFT;
  14550. }
  14551. }
  14552. function setDefaultHoverFillStroke(targetStyle, hoverStyle, prop) {
  14553. if (!hasFillOrStroke(hoverStyle[prop]) && hasFillOrStroke(targetStyle[prop])) {
  14554. targetStyle[prop] = liftColor(targetStyle[prop]);
  14555. }
  14556. }
  14557. function doSingleLeaveHover(el) {
  14558. var highlighted = el.__highlighted;
  14559. if (!highlighted) {
  14560. return;
  14561. }
  14562. el.__highlighted = false;
  14563. if (highlighted === 'layer') {
  14564. el.__zr && el.__zr.removeHover(el);
  14565. }
  14566. else if (highlighted) {
  14567. var style = el.style;
  14568. var normalStl = el.__cachedNormalStl;
  14569. if (normalStl) {
  14570. rollbackDefaultTextStyle(style);
  14571. // Consider null/undefined value, should use
  14572. // `setStyle` but not `extendFrom(stl, true)`.
  14573. el.setStyle(normalStl);
  14574. applyDefaultTextStyle(style);
  14575. }
  14576. // `__cachedNormalZ2` will not be reset if calling `setElementHoverStyle`
  14577. // when `el` is on emphasis state. So here by comparing with 1, we try
  14578. // hard to make the bug case rare.
  14579. var normalZ2 = el.__cachedNormalZ2;
  14580. if (normalZ2 != null && el.z2 - normalZ2 === Z2_EMPHASIS_LIFT) {
  14581. el.z2 = normalZ2;
  14582. }
  14583. }
  14584. }
  14585. function traverseCall(el, method) {
  14586. el.isGroup
  14587. ? el.traverse(function (child) {
  14588. !child.isGroup && method(child);
  14589. })
  14590. : method(el);
  14591. }
  14592. /**
  14593. * Set hover style (namely "emphasis style") of element, based on the current
  14594. * style of the given `el`.
  14595. * This method should be called after all of the normal styles have been adopted
  14596. * to the `el`. See the reason on `setHoverStyle`.
  14597. *
  14598. * @param {module:zrender/Element} el Should not be `zrender/container/Group`.
  14599. * @param {Object|boolean} [hoverStl] The specified hover style.
  14600. * If set as `false`, disable the hover style.
  14601. * Similarly, The `el.hoverStyle` can alse be set
  14602. * as `false` to disable the hover style.
  14603. * Otherwise, use the default hover style if not provided.
  14604. * @param {Object} [opt]
  14605. * @param {boolean} [opt.hoverSilentOnTouch=false] See `graphic.setAsHoverStyleTrigger`
  14606. */
  14607. function setElementHoverStyle(el, hoverStl) {
  14608. // For performance consideration, it might be better to make the "hover style" only the
  14609. // difference properties from the "normal style", but not a entire copy of all styles.
  14610. hoverStl = el.__hoverStl = hoverStl !== false && (hoverStl || {});
  14611. el.__hoverStlDirty = true;
  14612. // FIXME
  14613. // It is not completely right to save "normal"/"emphasis" flag on elements.
  14614. // It probably should be saved on `data` of series. Consider the cases:
  14615. // (1) A highlighted elements are moved out of the view port and re-enter
  14616. // again by dataZoom.
  14617. // (2) call `setOption` and replace elements totally when they are highlighted.
  14618. if (el.__highlighted) {
  14619. // Consider the case:
  14620. // The styles of a highlighted `el` is being updated. The new "emphasis style"
  14621. // should be adapted to the `el`. Notice here new "normal styles" should have
  14622. // been set outside and the cached "normal style" is out of date.
  14623. el.__cachedNormalStl = null;
  14624. // Do not clear `__cachedNormalZ2` here, because setting `z2` is not a constraint
  14625. // of this method. In most cases, `z2` is not set and hover style should be able
  14626. // to rollback. Of course, that would bring bug, but only in a rare case, see
  14627. // `doSingleLeaveHover` for details.
  14628. doSingleLeaveHover(el);
  14629. doSingleEnterHover(el);
  14630. }
  14631. }
  14632. /**
  14633. * Emphasis (called by API) has higher priority than `mouseover`.
  14634. * When element has been called to be entered emphasis, mouse over
  14635. * should not trigger the highlight effect (for example, animation
  14636. * scale) again, and `mouseout` should not downplay the highlight
  14637. * effect. So the listener of `mouseover` and `mouseout` should
  14638. * check `isInEmphasis`.
  14639. *
  14640. * @param {module:zrender/Element} el
  14641. * @return {boolean}
  14642. */
  14643. function isInEmphasis(el) {
  14644. return el && el.__isEmphasisEntered;
  14645. }
  14646. function onElementMouseOver(e) {
  14647. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  14648. return;
  14649. }
  14650. // Only if element is not in emphasis status
  14651. !this.__isEmphasisEntered && traverseCall(this, doSingleEnterHover);
  14652. }
  14653. function onElementMouseOut(e) {
  14654. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  14655. return;
  14656. }
  14657. // Only if element is not in emphasis status
  14658. !this.__isEmphasisEntered && traverseCall(this, doSingleLeaveHover);
  14659. }
  14660. function enterEmphasis() {
  14661. this.__isEmphasisEntered = true;
  14662. traverseCall(this, doSingleEnterHover);
  14663. }
  14664. function leaveEmphasis() {
  14665. this.__isEmphasisEntered = false;
  14666. traverseCall(this, doSingleLeaveHover);
  14667. }
  14668. /**
  14669. * Set hover style (namely "emphasis style") of element,
  14670. * based on the current style of the given `el`.
  14671. *
  14672. * (1)
  14673. * **CONSTRAINTS** for this method:
  14674. * <A> This method MUST be called after all of the normal styles having been adopted
  14675. * to the `el`.
  14676. * <B> The input `hoverStyle` (that is, "emphasis style") MUST be the subset of the
  14677. * "normal style" having been set to the el.
  14678. * <C> `color` MUST be one of the "normal styles" (because color might be lifted as
  14679. * a default hover style).
  14680. *
  14681. * The reason: this method treat the current style of the `el` as the "normal style"
  14682. * and cache them when enter/update the "emphasis style". Consider the case: the `el`
  14683. * is in "emphasis" state and `setOption`/`dispatchAction` trigger the style updating
  14684. * logic, where the el should shift from the original emphasis style to the new
  14685. * "emphasis style" and should be able to "downplay" back to the new "normal style".
  14686. *
  14687. * Indeed, it is error-prone to make a interface has so many constraints, but I have
  14688. * not found a better solution yet to fit the backward compatibility, performance and
  14689. * the current programming style.
  14690. *
  14691. * (2)
  14692. * Call the method for a "root" element once. Do not call it for each descendants.
  14693. * If the descendants elemenets of a group has itself hover style different from the
  14694. * root group, we can simply mount the style on `el.hoverStyle` for them, but should
  14695. * not call this method for them.
  14696. *
  14697. * @param {module:zrender/Element} el
  14698. * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`.
  14699. * @param {Object} [opt]
  14700. * @param {boolean} [opt.hoverSilentOnTouch=false] See `graphic.setAsHoverStyleTrigger`.
  14701. */
  14702. function setHoverStyle(el, hoverStyle, opt) {
  14703. el.isGroup
  14704. ? el.traverse(function (child) {
  14705. // If element has sepcified hoverStyle, then use it instead of given hoverStyle
  14706. // Often used when item group has a label element and it's hoverStyle is different
  14707. !child.isGroup && setElementHoverStyle(child, child.hoverStyle || hoverStyle);
  14708. })
  14709. : setElementHoverStyle(el, el.hoverStyle || hoverStyle);
  14710. setAsHoverStyleTrigger(el, opt);
  14711. }
  14712. /**
  14713. * @param {Object|boolean} [opt] If `false`, means disable trigger.
  14714. * @param {boolean} [opt.hoverSilentOnTouch=false]
  14715. * In touch device, mouseover event will be trigger on touchstart event
  14716. * (see module:zrender/dom/HandlerProxy). By this mechanism, we can
  14717. * conveniently use hoverStyle when tap on touch screen without additional
  14718. * code for compatibility.
  14719. * But if the chart/component has select feature, which usually also use
  14720. * hoverStyle, there might be conflict between 'select-highlight' and
  14721. * 'hover-highlight' especially when roam is enabled (see geo for example).
  14722. * In this case, hoverSilentOnTouch should be used to disable hover-highlight
  14723. * on touch device.
  14724. */
  14725. function setAsHoverStyleTrigger(el, opt) {
  14726. var disable = opt === false;
  14727. el.__hoverSilentOnTouch = opt != null && opt.hoverSilentOnTouch;
  14728. // Simple optimize, since this method might be
  14729. // called for each elements of a group in some cases.
  14730. if (!disable || el.__hoverStyleTrigger) {
  14731. var method = disable ? 'off' : 'on';
  14732. // Duplicated function will be auto-ignored, see Eventful.js.
  14733. el[method]('mouseover', onElementMouseOver)[method]('mouseout', onElementMouseOut);
  14734. // Emphasis, normal can be triggered manually
  14735. el[method]('emphasis', enterEmphasis)[method]('normal', leaveEmphasis);
  14736. el.__hoverStyleTrigger = !disable;
  14737. }
  14738. }
  14739. /**
  14740. * See more info in `setTextStyleCommon`.
  14741. * @param {Object|module:zrender/graphic/Style} normalStyle
  14742. * @param {Object} emphasisStyle
  14743. * @param {module:echarts/model/Model} normalModel
  14744. * @param {module:echarts/model/Model} emphasisModel
  14745. * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
  14746. * @param {string|Function} [opt.defaultText]
  14747. * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
  14748. * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  14749. * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by
  14750. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  14751. * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by
  14752. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  14753. * @param {Object} [normalSpecified]
  14754. * @param {Object} [emphasisSpecified]
  14755. */
  14756. function setLabelStyle(
  14757. normalStyle, emphasisStyle,
  14758. normalModel, emphasisModel,
  14759. opt,
  14760. normalSpecified, emphasisSpecified
  14761. ) {
  14762. opt = opt || EMPTY_OBJ;
  14763. var labelFetcher = opt.labelFetcher;
  14764. var labelDataIndex = opt.labelDataIndex;
  14765. var labelDimIndex = opt.labelDimIndex;
  14766. // This scenario, `label.normal.show = true; label.emphasis.show = false`,
  14767. // is not supported util someone requests.
  14768. var showNormal = normalModel.getShallow('show');
  14769. var showEmphasis = emphasisModel.getShallow('show');
  14770. // Consider performance, only fetch label when necessary.
  14771. // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
  14772. // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
  14773. var baseText;
  14774. if (showNormal || showEmphasis) {
  14775. if (labelFetcher) {
  14776. baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex);
  14777. }
  14778. if (baseText == null) {
  14779. baseText = isFunction$1(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText;
  14780. }
  14781. }
  14782. var normalStyleText = showNormal ? baseText : null;
  14783. var emphasisStyleText = showEmphasis
  14784. ? retrieve2(
  14785. labelFetcher
  14786. ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex)
  14787. : null,
  14788. baseText
  14789. )
  14790. : null;
  14791. // Optimize: If style.text is null, text will not be drawn.
  14792. if (normalStyleText != null || emphasisStyleText != null) {
  14793. // Always set `textStyle` even if `normalStyle.text` is null, because default
  14794. // values have to be set on `normalStyle`.
  14795. // If we set default values on `emphasisStyle`, consider case:
  14796. // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
  14797. // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
  14798. // Then the 'red' will not work on emphasis.
  14799. setTextStyle(normalStyle, normalModel, normalSpecified, opt);
  14800. setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
  14801. }
  14802. normalStyle.text = normalStyleText;
  14803. emphasisStyle.text = emphasisStyleText;
  14804. }
  14805. /**
  14806. * Set basic textStyle properties.
  14807. * See more info in `setTextStyleCommon`.
  14808. * @param {Object|module:zrender/graphic/Style} textStyle
  14809. * @param {module:echarts/model/Model} model
  14810. * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
  14811. * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
  14812. * @param {boolean} [isEmphasis]
  14813. */
  14814. function setTextStyle(
  14815. textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis
  14816. ) {
  14817. setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
  14818. specifiedTextStyle && extend(textStyle, specifiedTextStyle);
  14819. // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  14820. return textStyle;
  14821. }
  14822. /**
  14823. * Set text option in the style.
  14824. * See more info in `setTextStyleCommon`.
  14825. * @deprecated
  14826. * @param {Object} textStyle
  14827. * @param {module:echarts/model/Model} labelModel
  14828. * @param {string|boolean} defaultColor Default text color.
  14829. * If set as false, it will be processed as a emphasis style.
  14830. */
  14831. function setText(textStyle, labelModel, defaultColor) {
  14832. var opt = {isRectText: true};
  14833. var isEmphasis;
  14834. if (defaultColor === false) {
  14835. isEmphasis = true;
  14836. }
  14837. else {
  14838. // Support setting color as 'auto' to get visual color.
  14839. opt.autoColor = defaultColor;
  14840. }
  14841. setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);
  14842. // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  14843. }
  14844. /**
  14845. * The uniform entry of set text style, that is, retrieve style definitions
  14846. * from `model` and set to `textStyle` object.
  14847. *
  14848. * Never in merge mode, but in overwrite mode, that is, all of the text style
  14849. * properties will be set. (Consider the states of normal and emphasis and
  14850. * default value can be adopted, merge would make the logic too complicated
  14851. * to manage.)
  14852. *
  14853. * The `textStyle` object can either be a plain object or an instance of
  14854. * `zrender/src/graphic/Style`, and either be the style of normal or emphasis.
  14855. * After this mothod called, the `textStyle` object can then be used in
  14856. * `el.setStyle(textStyle)` or `el.hoverStyle = textStyle`.
  14857. *
  14858. * Default value will be adopted and `insideRollbackOpt` will be created.
  14859. * See `applyDefaultTextStyle` `rollbackDefaultTextStyle` for more details.
  14860. *
  14861. * opt: {
  14862. * disableBox: boolean, Whether diable drawing box of block (outer most).
  14863. * isRectText: boolean,
  14864. * autoColor: string, specify a color when color is 'auto',
  14865. * for textFill, textStroke, textBackgroundColor, and textBorderColor.
  14866. * If autoColor specified, it is used as default textFill.
  14867. * useInsideStyle:
  14868. * `true`: Use inside style (textFill, textStroke, textStrokeWidth)
  14869. * if `textFill` is not specified.
  14870. * `false`: Do not use inside style.
  14871. * `null/undefined`: use inside style if `isRectText` is true and
  14872. * `textFill` is not specified and textPosition contains `'inside'`.
  14873. * forceRich: boolean
  14874. * }
  14875. */
  14876. function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
  14877. // Consider there will be abnormal when merge hover style to normal style if given default value.
  14878. opt = opt || EMPTY_OBJ;
  14879. if (opt.isRectText) {
  14880. var textPosition = textStyleModel.getShallow('position')
  14881. || (isEmphasis ? null : 'inside');
  14882. // 'outside' is not a valid zr textPostion value, but used
  14883. // in bar series, and magric type should be considered.
  14884. textPosition === 'outside' && (textPosition = 'top');
  14885. textStyle.textPosition = textPosition;
  14886. textStyle.textOffset = textStyleModel.getShallow('offset');
  14887. var labelRotate = textStyleModel.getShallow('rotate');
  14888. labelRotate != null && (labelRotate *= Math.PI / 180);
  14889. textStyle.textRotation = labelRotate;
  14890. textStyle.textDistance = retrieve2(
  14891. textStyleModel.getShallow('distance'), isEmphasis ? null : 5
  14892. );
  14893. }
  14894. var ecModel = textStyleModel.ecModel;
  14895. var globalTextStyle = ecModel && ecModel.option.textStyle;
  14896. // Consider case:
  14897. // {
  14898. // data: [{
  14899. // value: 12,
  14900. // label: {
  14901. // rich: {
  14902. // // no 'a' here but using parent 'a'.
  14903. // }
  14904. // }
  14905. // }],
  14906. // rich: {
  14907. // a: { ... }
  14908. // }
  14909. // }
  14910. var richItemNames = getRichItemNames(textStyleModel);
  14911. var richResult;
  14912. if (richItemNames) {
  14913. richResult = {};
  14914. for (var name in richItemNames) {
  14915. if (richItemNames.hasOwnProperty(name)) {
  14916. // Cascade is supported in rich.
  14917. var richTextStyle = textStyleModel.getModel(['rich', name]);
  14918. // In rich, never `disableBox`.
  14919. setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
  14920. }
  14921. }
  14922. }
  14923. textStyle.rich = richResult;
  14924. setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
  14925. if (opt.forceRich && !opt.textStyle) {
  14926. opt.textStyle = {};
  14927. }
  14928. return textStyle;
  14929. }
  14930. // Consider case:
  14931. // {
  14932. // data: [{
  14933. // value: 12,
  14934. // label: {
  14935. // rich: {
  14936. // // no 'a' here but using parent 'a'.
  14937. // }
  14938. // }
  14939. // }],
  14940. // rich: {
  14941. // a: { ... }
  14942. // }
  14943. // }
  14944. function getRichItemNames(textStyleModel) {
  14945. // Use object to remove duplicated names.
  14946. var richItemNameMap;
  14947. while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
  14948. var rich = (textStyleModel.option || EMPTY_OBJ).rich;
  14949. if (rich) {
  14950. richItemNameMap = richItemNameMap || {};
  14951. for (var name in rich) {
  14952. if (rich.hasOwnProperty(name)) {
  14953. richItemNameMap[name] = 1;
  14954. }
  14955. }
  14956. }
  14957. textStyleModel = textStyleModel.parentModel;
  14958. }
  14959. return richItemNameMap;
  14960. }
  14961. function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
  14962. // In merge mode, default value should not be given.
  14963. globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
  14964. textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt)
  14965. || globalTextStyle.color;
  14966. textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt)
  14967. || globalTextStyle.textBorderColor;
  14968. textStyle.textStrokeWidth = retrieve2(
  14969. textStyleModel.getShallow('textBorderWidth'),
  14970. globalTextStyle.textBorderWidth
  14971. );
  14972. // Save original textPosition, because style.textPosition will be repalced by
  14973. // real location (like [10, 30]) in zrender.
  14974. textStyle.insideRawTextPosition = textStyle.textPosition;
  14975. if (!isEmphasis) {
  14976. if (isBlock) {
  14977. textStyle.insideRollbackOpt = opt;
  14978. applyDefaultTextStyle(textStyle);
  14979. }
  14980. // Set default finally.
  14981. if (textStyle.textFill == null) {
  14982. textStyle.textFill = opt.autoColor;
  14983. }
  14984. }
  14985. // Do not use `getFont` here, because merge should be supported, where
  14986. // part of these properties may be changed in emphasis style, and the
  14987. // others should remain their original value got from normal style.
  14988. textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
  14989. textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
  14990. textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
  14991. textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
  14992. textStyle.textAlign = textStyleModel.getShallow('align');
  14993. textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign')
  14994. || textStyleModel.getShallow('baseline');
  14995. textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
  14996. textStyle.textWidth = textStyleModel.getShallow('width');
  14997. textStyle.textHeight = textStyleModel.getShallow('height');
  14998. textStyle.textTag = textStyleModel.getShallow('tag');
  14999. if (!isBlock || !opt.disableBox) {
  15000. textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
  15001. textStyle.textPadding = textStyleModel.getShallow('padding');
  15002. textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
  15003. textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
  15004. textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
  15005. textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
  15006. textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
  15007. textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
  15008. textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
  15009. }
  15010. textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor')
  15011. || globalTextStyle.textShadowColor;
  15012. textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur')
  15013. || globalTextStyle.textShadowBlur;
  15014. textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX')
  15015. || globalTextStyle.textShadowOffsetX;
  15016. textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY')
  15017. || globalTextStyle.textShadowOffsetY;
  15018. }
  15019. function getAutoColor(color, opt) {
  15020. return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null;
  15021. }
  15022. /**
  15023. * Give some default value to the input `textStyle` object, based on the current settings
  15024. * in this `textStyle` object.
  15025. *
  15026. * The Scenario:
  15027. * when text position is `inside` and `textFill` is not specified, we show
  15028. * text border by default for better view. But it should be considered that text position
  15029. * might be changed when hovering or being emphasis, where the `insideRollback` is used to
  15030. * restore the style.
  15031. *
  15032. * Usage (& NOTICE):
  15033. * When a style object (eithor plain object or instance of `zrender/src/graphic/Style`) is
  15034. * about to be modified on its text related properties, `rollbackDefaultTextStyle` should
  15035. * be called before the modification and `applyDefaultTextStyle` should be called after that.
  15036. * (For the case that all of the text related properties is reset, like `setTextStyleCommon`
  15037. * does, `rollbackDefaultTextStyle` is not needed to be called).
  15038. */
  15039. function applyDefaultTextStyle(textStyle) {
  15040. var opt = textStyle.insideRollbackOpt;
  15041. // Only `insideRollbackOpt` created (in `setTextStyleCommon`),
  15042. // applyDefaultTextStyle works.
  15043. if (!opt || textStyle.textFill != null) {
  15044. return;
  15045. }
  15046. var useInsideStyle = opt.useInsideStyle;
  15047. var textPosition = textStyle.insideRawTextPosition;
  15048. var insideRollback;
  15049. var autoColor = opt.autoColor;
  15050. if (useInsideStyle !== false
  15051. && (useInsideStyle === true
  15052. || (opt.isRectText
  15053. && textPosition
  15054. // textPosition can be [10, 30]
  15055. && typeof textPosition === 'string'
  15056. && textPosition.indexOf('inside') >= 0
  15057. )
  15058. )
  15059. ) {
  15060. insideRollback = {
  15061. textFill: null,
  15062. textStroke: textStyle.textStroke,
  15063. textStrokeWidth: textStyle.textStrokeWidth
  15064. };
  15065. textStyle.textFill = '#fff';
  15066. // Consider text with #fff overflow its container.
  15067. if (textStyle.textStroke == null) {
  15068. textStyle.textStroke = autoColor;
  15069. textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
  15070. }
  15071. }
  15072. else if (autoColor != null) {
  15073. insideRollback = {textFill: null};
  15074. textStyle.textFill = autoColor;
  15075. }
  15076. // Always set `insideRollback`, for clearing previous.
  15077. if (insideRollback) {
  15078. textStyle.insideRollback = insideRollback;
  15079. }
  15080. }
  15081. /**
  15082. * Consider the case: in a scatter,
  15083. * label: {
  15084. * normal: {position: 'inside'},
  15085. * emphasis: {position: 'top'}
  15086. * }
  15087. * In the normal state, the `textFill` will be set as '#fff' for pretty view (see
  15088. * `applyDefaultTextStyle`), but when switching to emphasis state, the `textFill`
  15089. * should be retured to 'autoColor', but not keep '#fff'.
  15090. */
  15091. function rollbackDefaultTextStyle(style) {
  15092. var insideRollback = style.insideRollback;
  15093. if (insideRollback) {
  15094. style.textFill = insideRollback.textFill;
  15095. style.textStroke = insideRollback.textStroke;
  15096. style.textStrokeWidth = insideRollback.textStrokeWidth;
  15097. style.insideRollback = null;
  15098. }
  15099. }
  15100. function getFont(opt, ecModel) {
  15101. // ecModel or default text style model.
  15102. var gTextStyleModel = ecModel || ecModel.getModel('textStyle');
  15103. return trim([
  15104. // FIXME in node-canvas fontWeight is before fontStyle
  15105. opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '',
  15106. opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '',
  15107. (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px',
  15108. opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'
  15109. ].join(' '));
  15110. }
  15111. function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
  15112. if (typeof dataIndex === 'function') {
  15113. cb = dataIndex;
  15114. dataIndex = null;
  15115. }
  15116. // Do not check 'animation' property directly here. Consider this case:
  15117. // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
  15118. // but its parent model (`seriesModel`) does.
  15119. var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
  15120. if (animationEnabled) {
  15121. var postfix = isUpdate ? 'Update' : '';
  15122. var duration = animatableModel.getShallow('animationDuration' + postfix);
  15123. var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
  15124. var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
  15125. if (typeof animationDelay === 'function') {
  15126. animationDelay = animationDelay(
  15127. dataIndex,
  15128. animatableModel.getAnimationDelayParams
  15129. ? animatableModel.getAnimationDelayParams(el, dataIndex)
  15130. : null
  15131. );
  15132. }
  15133. if (typeof duration === 'function') {
  15134. duration = duration(dataIndex);
  15135. }
  15136. duration > 0
  15137. ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb)
  15138. : (el.stopAnimation(), el.attr(props), cb && cb());
  15139. }
  15140. else {
  15141. el.stopAnimation();
  15142. el.attr(props);
  15143. cb && cb();
  15144. }
  15145. }
  15146. /**
  15147. * Update graphic element properties with or without animation according to the
  15148. * configuration in series.
  15149. *
  15150. * Caution: this method will stop previous animation.
  15151. * So if do not use this method to one element twice before
  15152. * animation starts, unless you know what you are doing.
  15153. *
  15154. * @param {module:zrender/Element} el
  15155. * @param {Object} props
  15156. * @param {module:echarts/model/Model} [animatableModel]
  15157. * @param {number} [dataIndex]
  15158. * @param {Function} [cb]
  15159. * @example
  15160. * graphic.updateProps(el, {
  15161. * position: [100, 100]
  15162. * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
  15163. * // Or
  15164. * graphic.updateProps(el, {
  15165. * position: [100, 100]
  15166. * }, seriesModel, function () { console.log('Animation done!'); });
  15167. */
  15168. function updateProps(el, props, animatableModel, dataIndex, cb) {
  15169. animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
  15170. }
  15171. /**
  15172. * Init graphic element properties with or without animation according to the
  15173. * configuration in series.
  15174. *
  15175. * Caution: this method will stop previous animation.
  15176. * So if do not use this method to one element twice before
  15177. * animation starts, unless you know what you are doing.
  15178. *
  15179. * @param {module:zrender/Element} el
  15180. * @param {Object} props
  15181. * @param {module:echarts/model/Model} [animatableModel]
  15182. * @param {number} [dataIndex]
  15183. * @param {Function} cb
  15184. */
  15185. function initProps(el, props, animatableModel, dataIndex, cb) {
  15186. animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
  15187. }
  15188. /**
  15189. * Get transform matrix of target (param target),
  15190. * in coordinate of its ancestor (param ancestor)
  15191. *
  15192. * @param {module:zrender/mixin/Transformable} target
  15193. * @param {module:zrender/mixin/Transformable} [ancestor]
  15194. */
  15195. function getTransform(target, ancestor) {
  15196. var mat = identity([]);
  15197. while (target && target !== ancestor) {
  15198. mul$1(mat, target.getLocalTransform(), mat);
  15199. target = target.parent;
  15200. }
  15201. return mat;
  15202. }
  15203. /**
  15204. * Apply transform to an vertex.
  15205. * @param {Array.<number>} target [x, y]
  15206. * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
  15207. * + Transform matrix: like [1, 0, 0, 1, 0, 0]
  15208. * + {position, rotation, scale}, the same as `zrender/Transformable`.
  15209. * @param {boolean=} invert Whether use invert matrix.
  15210. * @return {Array.<number>} [x, y]
  15211. */
  15212. function applyTransform$1(target, transform, invert$$1) {
  15213. if (transform && !isArrayLike(transform)) {
  15214. transform = Transformable.getLocalTransform(transform);
  15215. }
  15216. if (invert$$1) {
  15217. transform = invert([], transform);
  15218. }
  15219. return applyTransform([], target, transform);
  15220. }
  15221. /**
  15222. * @param {string} direction 'left' 'right' 'top' 'bottom'
  15223. * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
  15224. * @param {boolean=} invert Whether use invert matrix.
  15225. * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
  15226. */
  15227. function transformDirection(direction, transform, invert$$1) {
  15228. // Pick a base, ensure that transform result will not be (0, 0).
  15229. var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)
  15230. ? 1 : Math.abs(2 * transform[4] / transform[0]);
  15231. var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)
  15232. ? 1 : Math.abs(2 * transform[4] / transform[2]);
  15233. var vertex = [
  15234. direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,
  15235. direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0
  15236. ];
  15237. vertex = applyTransform$1(vertex, transform, invert$$1);
  15238. return Math.abs(vertex[0]) > Math.abs(vertex[1])
  15239. ? (vertex[0] > 0 ? 'right' : 'left')
  15240. : (vertex[1] > 0 ? 'bottom' : 'top');
  15241. }
  15242. /**
  15243. * Apply group transition animation from g1 to g2.
  15244. * If no animatableModel, no animation.
  15245. */
  15246. function groupTransition(g1, g2, animatableModel, cb) {
  15247. if (!g1 || !g2) {
  15248. return;
  15249. }
  15250. function getElMap(g) {
  15251. var elMap = {};
  15252. g.traverse(function (el) {
  15253. if (!el.isGroup && el.anid) {
  15254. elMap[el.anid] = el;
  15255. }
  15256. });
  15257. return elMap;
  15258. }
  15259. function getAnimatableProps(el) {
  15260. var obj = {
  15261. position: clone$1(el.position),
  15262. rotation: el.rotation
  15263. };
  15264. if (el.shape) {
  15265. obj.shape = extend({}, el.shape);
  15266. }
  15267. return obj;
  15268. }
  15269. var elMap1 = getElMap(g1);
  15270. g2.traverse(function (el) {
  15271. if (!el.isGroup && el.anid) {
  15272. var oldEl = elMap1[el.anid];
  15273. if (oldEl) {
  15274. var newProp = getAnimatableProps(el);
  15275. el.attr(getAnimatableProps(oldEl));
  15276. updateProps(el, newProp, animatableModel, el.dataIndex);
  15277. }
  15278. // else {
  15279. // if (el.previousProps) {
  15280. // graphic.updateProps
  15281. // }
  15282. // }
  15283. }
  15284. });
  15285. }
  15286. /**
  15287. * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
  15288. * @param {Object} rect {x, y, width, height}
  15289. * @return {Array.<Array.<number>>} A new clipped points.
  15290. */
  15291. function clipPointsByRect(points, rect) {
  15292. // FIXME: this way migth be incorrect when grpahic clipped by a corner.
  15293. // and when element have border.
  15294. return map(points, function (point) {
  15295. var x = point[0];
  15296. x = mathMax$1(x, rect.x);
  15297. x = mathMin$1(x, rect.x + rect.width);
  15298. var y = point[1];
  15299. y = mathMax$1(y, rect.y);
  15300. y = mathMin$1(y, rect.y + rect.height);
  15301. return [x, y];
  15302. });
  15303. }
  15304. /**
  15305. * @param {Object} targetRect {x, y, width, height}
  15306. * @param {Object} rect {x, y, width, height}
  15307. * @return {Object} A new clipped rect. If rect size are negative, return undefined.
  15308. */
  15309. function clipRectByRect(targetRect, rect) {
  15310. var x = mathMax$1(targetRect.x, rect.x);
  15311. var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width);
  15312. var y = mathMax$1(targetRect.y, rect.y);
  15313. var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height);
  15314. // If the total rect is cliped, nothing, including the border,
  15315. // should be painted. So return undefined.
  15316. if (x2 >= x && y2 >= y) {
  15317. return {
  15318. x: x,
  15319. y: y,
  15320. width: x2 - x,
  15321. height: y2 - y
  15322. };
  15323. }
  15324. }
  15325. /**
  15326. * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
  15327. * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
  15328. * @param {Object} [rect] {x, y, width, height}
  15329. * @return {module:zrender/Element} Icon path or image element.
  15330. */
  15331. function createIcon(iconStr, opt, rect) {
  15332. opt = extend({rectHover: true}, opt);
  15333. var style = opt.style = {strokeNoScale: true};
  15334. rect = rect || {x: -1, y: -1, width: 2, height: 2};
  15335. if (iconStr) {
  15336. return iconStr.indexOf('image://') === 0
  15337. ? (
  15338. style.image = iconStr.slice(8),
  15339. defaults(style, rect),
  15340. new ZImage(opt)
  15341. )
  15342. : (
  15343. makePath(
  15344. iconStr.replace('path://', ''),
  15345. opt,
  15346. rect,
  15347. 'center'
  15348. )
  15349. );
  15350. }
  15351. }
  15352. var graphic = (Object.freeze || Object)({
  15353. Z2_EMPHASIS_LIFT: Z2_EMPHASIS_LIFT,
  15354. extendShape: extendShape,
  15355. extendPath: extendPath,
  15356. makePath: makePath,
  15357. makeImage: makeImage,
  15358. mergePath: mergePath,
  15359. resizePath: resizePath,
  15360. subPixelOptimizeLine: subPixelOptimizeLine,
  15361. subPixelOptimizeRect: subPixelOptimizeRect,
  15362. subPixelOptimize: subPixelOptimize,
  15363. setElementHoverStyle: setElementHoverStyle,
  15364. isInEmphasis: isInEmphasis,
  15365. setHoverStyle: setHoverStyle,
  15366. setAsHoverStyleTrigger: setAsHoverStyleTrigger,
  15367. setLabelStyle: setLabelStyle,
  15368. setTextStyle: setTextStyle,
  15369. setText: setText,
  15370. getFont: getFont,
  15371. updateProps: updateProps,
  15372. initProps: initProps,
  15373. getTransform: getTransform,
  15374. applyTransform: applyTransform$1,
  15375. transformDirection: transformDirection,
  15376. groupTransition: groupTransition,
  15377. clipPointsByRect: clipPointsByRect,
  15378. clipRectByRect: clipRectByRect,
  15379. createIcon: createIcon,
  15380. Group: Group,
  15381. Image: ZImage,
  15382. Text: Text,
  15383. Circle: Circle,
  15384. Sector: Sector,
  15385. Ring: Ring,
  15386. Polygon: Polygon,
  15387. Polyline: Polyline,
  15388. Rect: Rect,
  15389. Line: Line,
  15390. BezierCurve: BezierCurve,
  15391. Arc: Arc,
  15392. IncrementalDisplayable: IncrementalDisplayble,
  15393. CompoundPath: CompoundPath,
  15394. LinearGradient: LinearGradient,
  15395. RadialGradient: RadialGradient,
  15396. BoundingRect: BoundingRect
  15397. });
  15398. /*
  15399. * Licensed to the Apache Software Foundation (ASF) under one
  15400. * or more contributor license agreements. See the NOTICE file
  15401. * distributed with this work for additional information
  15402. * regarding copyright ownership. The ASF licenses this file
  15403. * to you under the Apache License, Version 2.0 (the
  15404. * "License"); you may not use this file except in compliance
  15405. * with the License. You may obtain a copy of the License at
  15406. *
  15407. * http://www.apache.org/licenses/LICENSE-2.0
  15408. *
  15409. * Unless required by applicable law or agreed to in writing,
  15410. * software distributed under the License is distributed on an
  15411. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15412. * KIND, either express or implied. See the License for the
  15413. * specific language governing permissions and limitations
  15414. * under the License.
  15415. */
  15416. var PATH_COLOR = ['textStyle', 'color'];
  15417. var textStyleMixin = {
  15418. /**
  15419. * Get color property or get color from option.textStyle.color
  15420. * @param {boolean} [isEmphasis]
  15421. * @return {string}
  15422. */
  15423. getTextColor: function (isEmphasis) {
  15424. var ecModel = this.ecModel;
  15425. return this.getShallow('color')
  15426. || (
  15427. (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null
  15428. );
  15429. },
  15430. /**
  15431. * Create font string from fontStyle, fontWeight, fontSize, fontFamily
  15432. * @return {string}
  15433. */
  15434. getFont: function () {
  15435. return getFont({
  15436. fontStyle: this.getShallow('fontStyle'),
  15437. fontWeight: this.getShallow('fontWeight'),
  15438. fontSize: this.getShallow('fontSize'),
  15439. fontFamily: this.getShallow('fontFamily')
  15440. }, this.ecModel);
  15441. },
  15442. getTextRect: function (text) {
  15443. return getBoundingRect(
  15444. text,
  15445. this.getFont(),
  15446. this.getShallow('align'),
  15447. this.getShallow('verticalAlign') || this.getShallow('baseline'),
  15448. this.getShallow('padding'),
  15449. this.getShallow('lineHeight'),
  15450. this.getShallow('rich'),
  15451. this.getShallow('truncateText')
  15452. );
  15453. }
  15454. };
  15455. /*
  15456. * Licensed to the Apache Software Foundation (ASF) under one
  15457. * or more contributor license agreements. See the NOTICE file
  15458. * distributed with this work for additional information
  15459. * regarding copyright ownership. The ASF licenses this file
  15460. * to you under the Apache License, Version 2.0 (the
  15461. * "License"); you may not use this file except in compliance
  15462. * with the License. You may obtain a copy of the License at
  15463. *
  15464. * http://www.apache.org/licenses/LICENSE-2.0
  15465. *
  15466. * Unless required by applicable law or agreed to in writing,
  15467. * software distributed under the License is distributed on an
  15468. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15469. * KIND, either express or implied. See the License for the
  15470. * specific language governing permissions and limitations
  15471. * under the License.
  15472. */
  15473. var getItemStyle = makeStyleMapper(
  15474. [
  15475. ['fill', 'color'],
  15476. ['stroke', 'borderColor'],
  15477. ['lineWidth', 'borderWidth'],
  15478. ['opacity'],
  15479. ['shadowBlur'],
  15480. ['shadowOffsetX'],
  15481. ['shadowOffsetY'],
  15482. ['shadowColor'],
  15483. ['textPosition'],
  15484. ['textAlign']
  15485. ]
  15486. );
  15487. var itemStyleMixin = {
  15488. getItemStyle: function (excludes, includes) {
  15489. var style = getItemStyle(this, excludes, includes);
  15490. var lineDash = this.getBorderLineDash();
  15491. lineDash && (style.lineDash = lineDash);
  15492. return style;
  15493. },
  15494. getBorderLineDash: function () {
  15495. var lineType = this.get('borderType');
  15496. return (lineType === 'solid' || lineType == null) ? null
  15497. : (lineType === 'dashed' ? [5, 5] : [1, 1]);
  15498. }
  15499. };
  15500. /*
  15501. * Licensed to the Apache Software Foundation (ASF) under one
  15502. * or more contributor license agreements. See the NOTICE file
  15503. * distributed with this work for additional information
  15504. * regarding copyright ownership. The ASF licenses this file
  15505. * to you under the Apache License, Version 2.0 (the
  15506. * "License"); you may not use this file except in compliance
  15507. * with the License. You may obtain a copy of the License at
  15508. *
  15509. * http://www.apache.org/licenses/LICENSE-2.0
  15510. *
  15511. * Unless required by applicable law or agreed to in writing,
  15512. * software distributed under the License is distributed on an
  15513. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15514. * KIND, either express or implied. See the License for the
  15515. * specific language governing permissions and limitations
  15516. * under the License.
  15517. */
  15518. /**
  15519. * @module echarts/model/Model
  15520. */
  15521. var mixin$1 = mixin;
  15522. var inner = makeInner();
  15523. /**
  15524. * @alias module:echarts/model/Model
  15525. * @constructor
  15526. * @param {Object} [option]
  15527. * @param {module:echarts/model/Model} [parentModel]
  15528. * @param {module:echarts/model/Global} [ecModel]
  15529. */
  15530. function Model(option, parentModel, ecModel) {
  15531. /**
  15532. * @type {module:echarts/model/Model}
  15533. * @readOnly
  15534. */
  15535. this.parentModel = parentModel;
  15536. /**
  15537. * @type {module:echarts/model/Global}
  15538. * @readOnly
  15539. */
  15540. this.ecModel = ecModel;
  15541. /**
  15542. * @type {Object}
  15543. * @protected
  15544. */
  15545. this.option = option;
  15546. // Simple optimization
  15547. // if (this.init) {
  15548. // if (arguments.length <= 4) {
  15549. // this.init(option, parentModel, ecModel, extraOpt);
  15550. // }
  15551. // else {
  15552. // this.init.apply(this, arguments);
  15553. // }
  15554. // }
  15555. }
  15556. Model.prototype = {
  15557. constructor: Model,
  15558. /**
  15559. * Model 的初始化函数
  15560. * @param {Object} option
  15561. */
  15562. init: null,
  15563. /**
  15564. * 从新的 Option merge
  15565. */
  15566. mergeOption: function (option) {
  15567. merge(this.option, option, true);
  15568. },
  15569. /**
  15570. * @param {string|Array.<string>} path
  15571. * @param {boolean} [ignoreParent=false]
  15572. * @return {*}
  15573. */
  15574. get: function (path, ignoreParent) {
  15575. if (path == null) {
  15576. return this.option;
  15577. }
  15578. return doGet(
  15579. this.option,
  15580. this.parsePath(path),
  15581. !ignoreParent && getParent(this, path)
  15582. );
  15583. },
  15584. /**
  15585. * @param {string} key
  15586. * @param {boolean} [ignoreParent=false]
  15587. * @return {*}
  15588. */
  15589. getShallow: function (key, ignoreParent) {
  15590. var option = this.option;
  15591. var val = option == null ? option : option[key];
  15592. var parentModel = !ignoreParent && getParent(this, key);
  15593. if (val == null && parentModel) {
  15594. val = parentModel.getShallow(key);
  15595. }
  15596. return val;
  15597. },
  15598. /**
  15599. * @param {string|Array.<string>} [path]
  15600. * @param {module:echarts/model/Model} [parentModel]
  15601. * @return {module:echarts/model/Model}
  15602. */
  15603. getModel: function (path, parentModel) {
  15604. var obj = path == null
  15605. ? this.option
  15606. : doGet(this.option, path = this.parsePath(path));
  15607. var thisParentModel;
  15608. parentModel = parentModel || (
  15609. (thisParentModel = getParent(this, path))
  15610. && thisParentModel.getModel(path)
  15611. );
  15612. return new Model(obj, parentModel, this.ecModel);
  15613. },
  15614. /**
  15615. * If model has option
  15616. */
  15617. isEmpty: function () {
  15618. return this.option == null;
  15619. },
  15620. restoreData: function () {},
  15621. // Pending
  15622. clone: function () {
  15623. var Ctor = this.constructor;
  15624. return new Ctor(clone(this.option));
  15625. },
  15626. setReadOnly: function (properties) {
  15627. // clazzUtil.setReadOnly(this, properties);
  15628. },
  15629. // If path is null/undefined, return null/undefined.
  15630. parsePath: function (path) {
  15631. if (typeof path === 'string') {
  15632. path = path.split('.');
  15633. }
  15634. return path;
  15635. },
  15636. /**
  15637. * @param {Function} getParentMethod
  15638. * param {Array.<string>|string} path
  15639. * return {module:echarts/model/Model}
  15640. */
  15641. customizeGetParent: function (getParentMethod) {
  15642. inner(this).getParent = getParentMethod;
  15643. },
  15644. isAnimationEnabled: function () {
  15645. if (!env$1.node) {
  15646. if (this.option.animation != null) {
  15647. return !!this.option.animation;
  15648. }
  15649. else if (this.parentModel) {
  15650. return this.parentModel.isAnimationEnabled();
  15651. }
  15652. }
  15653. }
  15654. };
  15655. function doGet(obj, pathArr, parentModel) {
  15656. for (var i = 0; i < pathArr.length; i++) {
  15657. // Ignore empty
  15658. if (!pathArr[i]) {
  15659. continue;
  15660. }
  15661. // obj could be number/string/... (like 0)
  15662. obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;
  15663. if (obj == null) {
  15664. break;
  15665. }
  15666. }
  15667. if (obj == null && parentModel) {
  15668. obj = parentModel.get(pathArr);
  15669. }
  15670. return obj;
  15671. }
  15672. // `path` can be null/undefined
  15673. function getParent(model, path) {
  15674. var getParentMethod = inner(model).getParent;
  15675. return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
  15676. }
  15677. // Enable Model.extend.
  15678. enableClassExtend(Model);
  15679. enableClassCheck(Model);
  15680. mixin$1(Model, lineStyleMixin);
  15681. mixin$1(Model, areaStyleMixin);
  15682. mixin$1(Model, textStyleMixin);
  15683. mixin$1(Model, itemStyleMixin);
  15684. /*
  15685. * Licensed to the Apache Software Foundation (ASF) under one
  15686. * or more contributor license agreements. See the NOTICE file
  15687. * distributed with this work for additional information
  15688. * regarding copyright ownership. The ASF licenses this file
  15689. * to you under the Apache License, Version 2.0 (the
  15690. * "License"); you may not use this file except in compliance
  15691. * with the License. You may obtain a copy of the License at
  15692. *
  15693. * http://www.apache.org/licenses/LICENSE-2.0
  15694. *
  15695. * Unless required by applicable law or agreed to in writing,
  15696. * software distributed under the License is distributed on an
  15697. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15698. * KIND, either express or implied. See the License for the
  15699. * specific language governing permissions and limitations
  15700. * under the License.
  15701. */
  15702. var base = 0;
  15703. /**
  15704. * @public
  15705. * @param {string} type
  15706. * @return {string}
  15707. */
  15708. function getUID(type) {
  15709. // Considering the case of crossing js context,
  15710. // use Math.random to make id as unique as possible.
  15711. return [(type || ''), base++, Math.random().toFixed(5)].join('_');
  15712. }
  15713. /**
  15714. * @inner
  15715. */
  15716. function enableSubTypeDefaulter(entity) {
  15717. var subTypeDefaulters = {};
  15718. entity.registerSubTypeDefaulter = function (componentType, defaulter) {
  15719. componentType = parseClassType$1(componentType);
  15720. subTypeDefaulters[componentType.main] = defaulter;
  15721. };
  15722. entity.determineSubType = function (componentType, option) {
  15723. var type = option.type;
  15724. if (!type) {
  15725. var componentTypeMain = parseClassType$1(componentType).main;
  15726. if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
  15727. type = subTypeDefaulters[componentTypeMain](option);
  15728. }
  15729. }
  15730. return type;
  15731. };
  15732. return entity;
  15733. }
  15734. /**
  15735. * Topological travel on Activity Network (Activity On Vertices).
  15736. * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
  15737. *
  15738. * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
  15739. *
  15740. * If there is circle dependencey, Error will be thrown.
  15741. *
  15742. */
  15743. function enableTopologicalTravel(entity, dependencyGetter) {
  15744. /**
  15745. * @public
  15746. * @param {Array.<string>} targetNameList Target Component type list.
  15747. * Can be ['aa', 'bb', 'aa.xx']
  15748. * @param {Array.<string>} fullNameList By which we can build dependency graph.
  15749. * @param {Function} callback Params: componentType, dependencies.
  15750. * @param {Object} context Scope of callback.
  15751. */
  15752. entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
  15753. if (!targetNameList.length) {
  15754. return;
  15755. }
  15756. var result = makeDepndencyGraph(fullNameList);
  15757. var graph = result.graph;
  15758. var stack = result.noEntryList;
  15759. var targetNameSet = {};
  15760. each$1(targetNameList, function (name) {
  15761. targetNameSet[name] = true;
  15762. });
  15763. while (stack.length) {
  15764. var currComponentType = stack.pop();
  15765. var currVertex = graph[currComponentType];
  15766. var isInTargetNameSet = !!targetNameSet[currComponentType];
  15767. if (isInTargetNameSet) {
  15768. callback.call(context, currComponentType, currVertex.originalDeps.slice());
  15769. delete targetNameSet[currComponentType];
  15770. }
  15771. each$1(
  15772. currVertex.successor,
  15773. isInTargetNameSet ? removeEdgeAndAdd : removeEdge
  15774. );
  15775. }
  15776. each$1(targetNameSet, function () {
  15777. throw new Error('Circle dependency may exists');
  15778. });
  15779. function removeEdge(succComponentType) {
  15780. graph[succComponentType].entryCount--;
  15781. if (graph[succComponentType].entryCount === 0) {
  15782. stack.push(succComponentType);
  15783. }
  15784. }
  15785. // Consider this case: legend depends on series, and we call
  15786. // chart.setOption({series: [...]}), where only series is in option.
  15787. // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
  15788. // not be called, but only sereis.mergeOption is called. Thus legend
  15789. // have no chance to update its local record about series (like which
  15790. // name of series is available in legend).
  15791. function removeEdgeAndAdd(succComponentType) {
  15792. targetNameSet[succComponentType] = true;
  15793. removeEdge(succComponentType);
  15794. }
  15795. };
  15796. /**
  15797. * DepndencyGraph: {Object}
  15798. * key: conponentType,
  15799. * value: {
  15800. * successor: [conponentTypes...],
  15801. * originalDeps: [conponentTypes...],
  15802. * entryCount: {number}
  15803. * }
  15804. */
  15805. function makeDepndencyGraph(fullNameList) {
  15806. var graph = {};
  15807. var noEntryList = [];
  15808. each$1(fullNameList, function (name) {
  15809. var thisItem = createDependencyGraphItem(graph, name);
  15810. var originalDeps = thisItem.originalDeps = dependencyGetter(name);
  15811. var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
  15812. thisItem.entryCount = availableDeps.length;
  15813. if (thisItem.entryCount === 0) {
  15814. noEntryList.push(name);
  15815. }
  15816. each$1(availableDeps, function (dependentName) {
  15817. if (indexOf(thisItem.predecessor, dependentName) < 0) {
  15818. thisItem.predecessor.push(dependentName);
  15819. }
  15820. var thatItem = createDependencyGraphItem(graph, dependentName);
  15821. if (indexOf(thatItem.successor, dependentName) < 0) {
  15822. thatItem.successor.push(name);
  15823. }
  15824. });
  15825. });
  15826. return {graph: graph, noEntryList: noEntryList};
  15827. }
  15828. function createDependencyGraphItem(graph, name) {
  15829. if (!graph[name]) {
  15830. graph[name] = {predecessor: [], successor: []};
  15831. }
  15832. return graph[name];
  15833. }
  15834. function getAvailableDependencies(originalDeps, fullNameList) {
  15835. var availableDeps = [];
  15836. each$1(originalDeps, function (dep) {
  15837. indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
  15838. });
  15839. return availableDeps;
  15840. }
  15841. }
  15842. /*
  15843. * Licensed to the Apache Software Foundation (ASF) under one
  15844. * or more contributor license agreements. See the NOTICE file
  15845. * distributed with this work for additional information
  15846. * regarding copyright ownership. The ASF licenses this file
  15847. * to you under the Apache License, Version 2.0 (the
  15848. * "License"); you may not use this file except in compliance
  15849. * with the License. You may obtain a copy of the License at
  15850. *
  15851. * http://www.apache.org/licenses/LICENSE-2.0
  15852. *
  15853. * Unless required by applicable law or agreed to in writing,
  15854. * software distributed under the License is distributed on an
  15855. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15856. * KIND, either express or implied. See the License for the
  15857. * specific language governing permissions and limitations
  15858. * under the License.
  15859. */
  15860. /*
  15861. * A third-party license is embeded for some of the code in this file:
  15862. * The method "quantile" was copied from "d3.js".
  15863. * (See more details in the comment of the method below.)
  15864. * The use of the source code of this file is also subject to the terms
  15865. * and consitions of the license of "d3.js" (BSD-3Clause, see
  15866. * </licenses/LICENSE-d3>).
  15867. */
  15868. var RADIAN_EPSILON = 1e-4;
  15869. function _trim(str) {
  15870. return str.replace(/^\s+/, '').replace(/\s+$/, '');
  15871. }
  15872. /**
  15873. * Linear mapping a value from domain to range
  15874. * @memberOf module:echarts/util/number
  15875. * @param {(number|Array.<number>)} val
  15876. * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
  15877. * @param {Array.<number>} range Range extent range[0] can be bigger than range[1]
  15878. * @param {boolean} clamp
  15879. * @return {(number|Array.<number>}
  15880. */
  15881. function linearMap(val, domain, range, clamp) {
  15882. var subDomain = domain[1] - domain[0];
  15883. var subRange = range[1] - range[0];
  15884. if (subDomain === 0) {
  15885. return subRange === 0
  15886. ? range[0]
  15887. : (range[0] + range[1]) / 2;
  15888. }
  15889. // Avoid accuracy problem in edge, such as
  15890. // 146.39 - 62.83 === 83.55999999999999.
  15891. // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
  15892. // It is a little verbose for efficiency considering this method
  15893. // is a hotspot.
  15894. if (clamp) {
  15895. if (subDomain > 0) {
  15896. if (val <= domain[0]) {
  15897. return range[0];
  15898. }
  15899. else if (val >= domain[1]) {
  15900. return range[1];
  15901. }
  15902. }
  15903. else {
  15904. if (val >= domain[0]) {
  15905. return range[0];
  15906. }
  15907. else if (val <= domain[1]) {
  15908. return range[1];
  15909. }
  15910. }
  15911. }
  15912. else {
  15913. if (val === domain[0]) {
  15914. return range[0];
  15915. }
  15916. if (val === domain[1]) {
  15917. return range[1];
  15918. }
  15919. }
  15920. return (val - domain[0]) / subDomain * subRange + range[0];
  15921. }
  15922. /**
  15923. * Convert a percent string to absolute number.
  15924. * Returns NaN if percent is not a valid string or number
  15925. * @memberOf module:echarts/util/number
  15926. * @param {string|number} percent
  15927. * @param {number} all
  15928. * @return {number}
  15929. */
  15930. function parsePercent$1(percent, all) {
  15931. switch (percent) {
  15932. case 'center':
  15933. case 'middle':
  15934. percent = '50%';
  15935. break;
  15936. case 'left':
  15937. case 'top':
  15938. percent = '0%';
  15939. break;
  15940. case 'right':
  15941. case 'bottom':
  15942. percent = '100%';
  15943. break;
  15944. }
  15945. if (typeof percent === 'string') {
  15946. if (_trim(percent).match(/%$/)) {
  15947. return parseFloat(percent) / 100 * all;
  15948. }
  15949. return parseFloat(percent);
  15950. }
  15951. return percent == null ? NaN : +percent;
  15952. }
  15953. /**
  15954. * (1) Fix rounding error of float numbers.
  15955. * (2) Support return string to avoid scientific notation like '3.5e-7'.
  15956. *
  15957. * @param {number} x
  15958. * @param {number} [precision]
  15959. * @param {boolean} [returnStr]
  15960. * @return {number|string}
  15961. */
  15962. function round$2(x, precision, returnStr) {
  15963. if (precision == null) {
  15964. precision = 10;
  15965. }
  15966. // Avoid range error
  15967. precision = Math.min(Math.max(0, precision), 20);
  15968. x = (+x).toFixed(precision);
  15969. return returnStr ? x : +x;
  15970. }
  15971. function asc(arr) {
  15972. arr.sort(function (a, b) {
  15973. return a - b;
  15974. });
  15975. return arr;
  15976. }
  15977. /**
  15978. * Get precision
  15979. * @param {number} val
  15980. */
  15981. function getPrecision(val) {
  15982. val = +val;
  15983. if (isNaN(val)) {
  15984. return 0;
  15985. }
  15986. // It is much faster than methods converting number to string as follows
  15987. // var tmp = val.toString();
  15988. // return tmp.length - 1 - tmp.indexOf('.');
  15989. // especially when precision is low
  15990. var e = 1;
  15991. var count = 0;
  15992. while (Math.round(val * e) / e !== val) {
  15993. e *= 10;
  15994. count++;
  15995. }
  15996. return count;
  15997. }
  15998. /**
  15999. * @param {string|number} val
  16000. * @return {number}
  16001. */
  16002. function getPrecisionSafe(val) {
  16003. var str = val.toString();
  16004. // Consider scientific notation: '3.4e-12' '3.4e+12'
  16005. var eIndex = str.indexOf('e');
  16006. if (eIndex > 0) {
  16007. var precision = +str.slice(eIndex + 1);
  16008. return precision < 0 ? -precision : 0;
  16009. }
  16010. else {
  16011. var dotIndex = str.indexOf('.');
  16012. return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
  16013. }
  16014. }
  16015. /**
  16016. * Minimal dicernible data precisioin according to a single pixel.
  16017. *
  16018. * @param {Array.<number>} dataExtent
  16019. * @param {Array.<number>} pixelExtent
  16020. * @return {number} precision
  16021. */
  16022. function getPixelPrecision(dataExtent, pixelExtent) {
  16023. var log = Math.log;
  16024. var LN10 = Math.LN10;
  16025. var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
  16026. var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
  16027. // toFixed() digits argument must be between 0 and 20.
  16028. var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
  16029. return !isFinite(precision) ? 20 : precision;
  16030. }
  16031. /**
  16032. * Get a data of given precision, assuring the sum of percentages
  16033. * in valueList is 1.
  16034. * The largest remainer method is used.
  16035. * https://en.wikipedia.org/wiki/Largest_remainder_method
  16036. *
  16037. * @param {Array.<number>} valueList a list of all data
  16038. * @param {number} idx index of the data to be processed in valueList
  16039. * @param {number} precision integer number showing digits of precision
  16040. * @return {number} percent ranging from 0 to 100
  16041. */
  16042. function getPercentWithPrecision(valueList, idx, precision) {
  16043. if (!valueList[idx]) {
  16044. return 0;
  16045. }
  16046. var sum = reduce(valueList, function (acc, val) {
  16047. return acc + (isNaN(val) ? 0 : val);
  16048. }, 0);
  16049. if (sum === 0) {
  16050. return 0;
  16051. }
  16052. var digits = Math.pow(10, precision);
  16053. var votesPerQuota = map(valueList, function (val) {
  16054. return (isNaN(val) ? 0 : val) / sum * digits * 100;
  16055. });
  16056. var targetSeats = digits * 100;
  16057. var seats = map(votesPerQuota, function (votes) {
  16058. // Assign automatic seats.
  16059. return Math.floor(votes);
  16060. });
  16061. var currentSum = reduce(seats, function (acc, val) {
  16062. return acc + val;
  16063. }, 0);
  16064. var remainder = map(votesPerQuota, function (votes, idx) {
  16065. return votes - seats[idx];
  16066. });
  16067. // Has remainding votes.
  16068. while (currentSum < targetSeats) {
  16069. // Find next largest remainder.
  16070. var max = Number.NEGATIVE_INFINITY;
  16071. var maxId = null;
  16072. for (var i = 0, len = remainder.length; i < len; ++i) {
  16073. if (remainder[i] > max) {
  16074. max = remainder[i];
  16075. maxId = i;
  16076. }
  16077. }
  16078. // Add a vote to max remainder.
  16079. ++seats[maxId];
  16080. remainder[maxId] = 0;
  16081. ++currentSum;
  16082. }
  16083. return seats[idx] / digits;
  16084. }
  16085. // Number.MAX_SAFE_INTEGER, ie do not support.
  16086. var MAX_SAFE_INTEGER = 9007199254740991;
  16087. /**
  16088. * To 0 - 2 * PI, considering negative radian.
  16089. * @param {number} radian
  16090. * @return {number}
  16091. */
  16092. function remRadian(radian) {
  16093. var pi2 = Math.PI * 2;
  16094. return (radian % pi2 + pi2) % pi2;
  16095. }
  16096. /**
  16097. * @param {type} radian
  16098. * @return {boolean}
  16099. */
  16100. function isRadianAroundZero(val) {
  16101. return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
  16102. }
  16103. /* eslint-disable */
  16104. 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
  16105. /* eslint-enable */
  16106. /**
  16107. * @param {string|Date|number} value These values can be accepted:
  16108. * + An instance of Date, represent a time in its own time zone.
  16109. * + Or string in a subset of ISO 8601, only including:
  16110. * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
  16111. * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
  16112. * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
  16113. * all of which will be treated as local time if time zone is not specified
  16114. * (see <https://momentjs.com/>).
  16115. * + Or other string format, including (all of which will be treated as loacal time):
  16116. * '2012', '2012-3-1', '2012/3/1', '2012/03/01',
  16117. * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
  16118. * + a timestamp, which represent a time in UTC.
  16119. * @return {Date} date
  16120. */
  16121. function parseDate(value) {
  16122. if (value instanceof Date) {
  16123. return value;
  16124. }
  16125. else if (typeof value === 'string') {
  16126. // Different browsers parse date in different way, so we parse it manually.
  16127. // Some other issues:
  16128. // new Date('1970-01-01') is UTC,
  16129. // new Date('1970/01/01') and new Date('1970-1-01') is local.
  16130. // See issue #3623
  16131. var match = TIME_REG.exec(value);
  16132. if (!match) {
  16133. // return Invalid Date.
  16134. return new Date(NaN);
  16135. }
  16136. // Use local time when no timezone offset specifed.
  16137. if (!match[8]) {
  16138. // match[n] can only be string or undefined.
  16139. // But take care of '12' + 1 => '121'.
  16140. return new Date(
  16141. +match[1],
  16142. +(match[2] || 1) - 1,
  16143. +match[3] || 1,
  16144. +match[4] || 0,
  16145. +(match[5] || 0),
  16146. +match[6] || 0,
  16147. +match[7] || 0
  16148. );
  16149. }
  16150. // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
  16151. // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
  16152. // For example, system timezone is set as "Time Zone: America/Toronto",
  16153. // then these code will get different result:
  16154. // `new Date(1478411999999).getTimezoneOffset(); // get 240`
  16155. // `new Date(1478412000000).getTimezoneOffset(); // get 300`
  16156. // So we should not use `new Date`, but use `Date.UTC`.
  16157. else {
  16158. var hour = +match[4] || 0;
  16159. if (match[8].toUpperCase() !== 'Z') {
  16160. hour -= match[8].slice(0, 3);
  16161. }
  16162. return new Date(Date.UTC(
  16163. +match[1],
  16164. +(match[2] || 1) - 1,
  16165. +match[3] || 1,
  16166. hour,
  16167. +(match[5] || 0),
  16168. +match[6] || 0,
  16169. +match[7] || 0
  16170. ));
  16171. }
  16172. }
  16173. else if (value == null) {
  16174. return new Date(NaN);
  16175. }
  16176. return new Date(Math.round(value));
  16177. }
  16178. /**
  16179. * Quantity of a number. e.g. 0.1, 1, 10, 100
  16180. *
  16181. * @param {number} val
  16182. * @return {number}
  16183. */
  16184. function quantity(val) {
  16185. return Math.pow(10, quantityExponent(val));
  16186. }
  16187. function quantityExponent(val) {
  16188. return Math.floor(Math.log(val) / Math.LN10);
  16189. }
  16190. /**
  16191. * find a “nice” number approximately equal to x. Round the number if round = true,
  16192. * take ceiling if round = false. The primary observation is that the “nicest”
  16193. * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
  16194. *
  16195. * See "Nice Numbers for Graph Labels" of Graphic Gems.
  16196. *
  16197. * @param {number} val Non-negative value.
  16198. * @param {boolean} round
  16199. * @return {number}
  16200. */
  16201. function nice(val, round) {
  16202. var exponent = quantityExponent(val);
  16203. var exp10 = Math.pow(10, exponent);
  16204. var f = val / exp10; // 1 <= f < 10
  16205. var nf;
  16206. if (round) {
  16207. if (f < 1.5) {
  16208. nf = 1;
  16209. }
  16210. else if (f < 2.5) {
  16211. nf = 2;
  16212. }
  16213. else if (f < 4) {
  16214. nf = 3;
  16215. }
  16216. else if (f < 7) {
  16217. nf = 5;
  16218. }
  16219. else {
  16220. nf = 10;
  16221. }
  16222. }
  16223. else {
  16224. if (f < 1) {
  16225. nf = 1;
  16226. }
  16227. else if (f < 2) {
  16228. nf = 2;
  16229. }
  16230. else if (f < 3) {
  16231. nf = 3;
  16232. }
  16233. else if (f < 5) {
  16234. nf = 5;
  16235. }
  16236. else {
  16237. nf = 10;
  16238. }
  16239. }
  16240. val = nf * exp10;
  16241. // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
  16242. // 20 is the uppper bound of toFixed.
  16243. return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
  16244. }
  16245. /**
  16246. * This code was copied from "d3.js"
  16247. * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.
  16248. * See the license statement at the head of this file.
  16249. * @param {Array.<number>} ascArr
  16250. */
  16251. function quantile(ascArr, p) {
  16252. var H = (ascArr.length - 1) * p + 1;
  16253. var h = Math.floor(H);
  16254. var v = +ascArr[h - 1];
  16255. var e = H - h;
  16256. return e ? v + e * (ascArr[h] - v) : v;
  16257. }
  16258. /**
  16259. * Order intervals asc, and split them when overlap.
  16260. * expect(numberUtil.reformIntervals([
  16261. * {interval: [18, 62], close: [1, 1]},
  16262. * {interval: [-Infinity, -70], close: [0, 0]},
  16263. * {interval: [-70, -26], close: [1, 1]},
  16264. * {interval: [-26, 18], close: [1, 1]},
  16265. * {interval: [62, 150], close: [1, 1]},
  16266. * {interval: [106, 150], close: [1, 1]},
  16267. * {interval: [150, Infinity], close: [0, 0]}
  16268. * ])).toEqual([
  16269. * {interval: [-Infinity, -70], close: [0, 0]},
  16270. * {interval: [-70, -26], close: [1, 1]},
  16271. * {interval: [-26, 18], close: [0, 1]},
  16272. * {interval: [18, 62], close: [0, 1]},
  16273. * {interval: [62, 150], close: [0, 1]},
  16274. * {interval: [150, Infinity], close: [0, 0]}
  16275. * ]);
  16276. * @param {Array.<Object>} list, where `close` mean open or close
  16277. * of the interval, and Infinity can be used.
  16278. * @return {Array.<Object>} The origin list, which has been reformed.
  16279. */
  16280. function reformIntervals(list) {
  16281. list.sort(function (a, b) {
  16282. return littleThan(a, b, 0) ? -1 : 1;
  16283. });
  16284. var curr = -Infinity;
  16285. var currClose = 1;
  16286. for (var i = 0; i < list.length;) {
  16287. var interval = list[i].interval;
  16288. var close = list[i].close;
  16289. for (var lg = 0; lg < 2; lg++) {
  16290. if (interval[lg] <= curr) {
  16291. interval[lg] = curr;
  16292. close[lg] = !lg ? 1 - currClose : 1;
  16293. }
  16294. curr = interval[lg];
  16295. currClose = close[lg];
  16296. }
  16297. if (interval[0] === interval[1] && close[0] * close[1] !== 1) {
  16298. list.splice(i, 1);
  16299. }
  16300. else {
  16301. i++;
  16302. }
  16303. }
  16304. return list;
  16305. function littleThan(a, b, lg) {
  16306. return a.interval[lg] < b.interval[lg]
  16307. || (
  16308. a.interval[lg] === b.interval[lg]
  16309. && (
  16310. (a.close[lg] - b.close[lg] === (!lg ? 1 : -1))
  16311. || (!lg && littleThan(a, b, 1))
  16312. )
  16313. );
  16314. }
  16315. }
  16316. /**
  16317. * parseFloat NaNs numeric-cast false positives (null|true|false|"")
  16318. * ...but misinterprets leading-number strings, particularly hex literals ("0x...")
  16319. * subtraction forces infinities to NaN
  16320. *
  16321. * @param {*} v
  16322. * @return {boolean}
  16323. */
  16324. function isNumeric(v) {
  16325. return v - parseFloat(v) >= 0;
  16326. }
  16327. var number = (Object.freeze || Object)({
  16328. linearMap: linearMap,
  16329. parsePercent: parsePercent$1,
  16330. round: round$2,
  16331. asc: asc,
  16332. getPrecision: getPrecision,
  16333. getPrecisionSafe: getPrecisionSafe,
  16334. getPixelPrecision: getPixelPrecision,
  16335. getPercentWithPrecision: getPercentWithPrecision,
  16336. MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,
  16337. remRadian: remRadian,
  16338. isRadianAroundZero: isRadianAroundZero,
  16339. parseDate: parseDate,
  16340. quantity: quantity,
  16341. nice: nice,
  16342. quantile: quantile,
  16343. reformIntervals: reformIntervals,
  16344. isNumeric: isNumeric
  16345. });
  16346. /*
  16347. * Licensed to the Apache Software Foundation (ASF) under one
  16348. * or more contributor license agreements. See the NOTICE file
  16349. * distributed with this work for additional information
  16350. * regarding copyright ownership. The ASF licenses this file
  16351. * to you under the Apache License, Version 2.0 (the
  16352. * "License"); you may not use this file except in compliance
  16353. * with the License. You may obtain a copy of the License at
  16354. *
  16355. * http://www.apache.org/licenses/LICENSE-2.0
  16356. *
  16357. * Unless required by applicable law or agreed to in writing,
  16358. * software distributed under the License is distributed on an
  16359. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16360. * KIND, either express or implied. See the License for the
  16361. * specific language governing permissions and limitations
  16362. * under the License.
  16363. */
  16364. // import Text from 'zrender/src/graphic/Text';
  16365. /**
  16366. * 每三位默认加,格式化
  16367. * @param {string|number} x
  16368. * @return {string}
  16369. */
  16370. function addCommas(x) {
  16371. if (isNaN(x)) {
  16372. return '-';
  16373. }
  16374. x = (x + '').split('.');
  16375. return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,')
  16376. + (x.length > 1 ? ('.' + x[1]) : '');
  16377. }
  16378. /**
  16379. * @param {string} str
  16380. * @param {boolean} [upperCaseFirst=false]
  16381. * @return {string} str
  16382. */
  16383. function toCamelCase(str, upperCaseFirst) {
  16384. str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {
  16385. return group1.toUpperCase();
  16386. });
  16387. if (upperCaseFirst && str) {
  16388. str = str.charAt(0).toUpperCase() + str.slice(1);
  16389. }
  16390. return str;
  16391. }
  16392. var normalizeCssArray$1 = normalizeCssArray;
  16393. var replaceReg = /([&<>"'])/g;
  16394. var replaceMap = {
  16395. '&': '&amp;',
  16396. '<': '&lt;',
  16397. '>': '&gt;',
  16398. '"': '&quot;',
  16399. '\'': '&#39;'
  16400. };
  16401. function encodeHTML(source) {
  16402. return source == null
  16403. ? ''
  16404. : (source + '').replace(replaceReg, function (str, c) {
  16405. return replaceMap[c];
  16406. });
  16407. }
  16408. var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
  16409. var wrapVar = function (varName, seriesIdx) {
  16410. return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
  16411. };
  16412. /**
  16413. * Template formatter
  16414. * @param {string} tpl
  16415. * @param {Array.<Object>|Object} paramsList
  16416. * @param {boolean} [encode=false]
  16417. * @return {string}
  16418. */
  16419. function formatTpl(tpl, paramsList, encode) {
  16420. if (!isArray(paramsList)) {
  16421. paramsList = [paramsList];
  16422. }
  16423. var seriesLen = paramsList.length;
  16424. if (!seriesLen) {
  16425. return '';
  16426. }
  16427. var $vars = paramsList[0].$vars || [];
  16428. for (var i = 0; i < $vars.length; i++) {
  16429. var alias = TPL_VAR_ALIAS[i];
  16430. tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));
  16431. }
  16432. for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
  16433. for (var k = 0; k < $vars.length; k++) {
  16434. var val = paramsList[seriesIdx][$vars[k]];
  16435. tpl = tpl.replace(
  16436. wrapVar(TPL_VAR_ALIAS[k], seriesIdx),
  16437. encode ? encodeHTML(val) : val
  16438. );
  16439. }
  16440. }
  16441. return tpl;
  16442. }
  16443. /**
  16444. * simple Template formatter
  16445. *
  16446. * @param {string} tpl
  16447. * @param {Object} param
  16448. * @param {boolean} [encode=false]
  16449. * @return {string}
  16450. */
  16451. function formatTplSimple(tpl, param, encode) {
  16452. each$1(param, function (value, key) {
  16453. tpl = tpl.replace(
  16454. '{' + key + '}',
  16455. encode ? encodeHTML(value) : value
  16456. );
  16457. });
  16458. return tpl;
  16459. }
  16460. /**
  16461. * @param {Object|string} [opt] If string, means color.
  16462. * @param {string} [opt.color]
  16463. * @param {string} [opt.extraCssText]
  16464. * @param {string} [opt.type='item'] 'item' or 'subItem'
  16465. * @param {string} [opt.renderMode='html'] render mode of tooltip, 'html' or 'richText'
  16466. * @param {string} [opt.markerId='X'] id name for marker. If only one marker is in a rich text, this can be omitted.
  16467. * @return {string}
  16468. */
  16469. function getTooltipMarker(opt, extraCssText) {
  16470. opt = isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {});
  16471. var color = opt.color;
  16472. var type = opt.type;
  16473. var extraCssText = opt.extraCssText;
  16474. var renderMode = opt.renderMode || 'html';
  16475. var markerId = opt.markerId || 'X';
  16476. if (!color) {
  16477. return '';
  16478. }
  16479. if (renderMode === 'html') {
  16480. return type === 'subItem'
  16481. ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;'
  16482. + 'border-radius:4px;width:4px;height:4px;background-color:'
  16483. + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
  16484. : '<span style="display:inline-block;margin-right:5px;'
  16485. + 'border-radius:10px;width:10px;height:10px;background-color:'
  16486. + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
  16487. }
  16488. else {
  16489. // Space for rich element marker
  16490. return {
  16491. renderMode: renderMode,
  16492. content: '{marker' + markerId + '|} ',
  16493. style: {
  16494. color: color
  16495. }
  16496. };
  16497. }
  16498. }
  16499. function pad(str, len) {
  16500. str += '';
  16501. return '0000'.substr(0, len - str.length) + str;
  16502. }
  16503. /**
  16504. * ISO Date format
  16505. * @param {string} tpl
  16506. * @param {number} value
  16507. * @param {boolean} [isUTC=false] Default in local time.
  16508. * see `module:echarts/scale/Time`
  16509. * and `module:echarts/util/number#parseDate`.
  16510. * @inner
  16511. */
  16512. function formatTime(tpl, value, isUTC) {
  16513. if (tpl === 'week'
  16514. || tpl === 'month'
  16515. || tpl === 'quarter'
  16516. || tpl === 'half-year'
  16517. || tpl === 'year'
  16518. ) {
  16519. tpl = 'MM-dd\nyyyy';
  16520. }
  16521. var date = parseDate(value);
  16522. var utc = isUTC ? 'UTC' : '';
  16523. var y = date['get' + utc + 'FullYear']();
  16524. var M = date['get' + utc + 'Month']() + 1;
  16525. var d = date['get' + utc + 'Date']();
  16526. var h = date['get' + utc + 'Hours']();
  16527. var m = date['get' + utc + 'Minutes']();
  16528. var s = date['get' + utc + 'Seconds']();
  16529. var S = date['get' + utc + 'Milliseconds']();
  16530. tpl = tpl.replace('MM', pad(M, 2))
  16531. .replace('M', M)
  16532. .replace('yyyy', y)
  16533. .replace('yy', y % 100)
  16534. .replace('dd', pad(d, 2))
  16535. .replace('d', d)
  16536. .replace('hh', pad(h, 2))
  16537. .replace('h', h)
  16538. .replace('mm', pad(m, 2))
  16539. .replace('m', m)
  16540. .replace('ss', pad(s, 2))
  16541. .replace('s', s)
  16542. .replace('SSS', pad(S, 3));
  16543. return tpl;
  16544. }
  16545. /**
  16546. * Capital first
  16547. * @param {string} str
  16548. * @return {string}
  16549. */
  16550. function capitalFirst(str) {
  16551. return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;
  16552. }
  16553. var truncateText$1 = truncateText;
  16554. /**
  16555. * @public
  16556. * @param {Object} opt
  16557. * @param {string} opt.text
  16558. * @param {string} opt.font
  16559. * @param {string} [opt.textAlign='left']
  16560. * @param {string} [opt.textVerticalAlign='top']
  16561. * @param {Array.<number>} [opt.textPadding]
  16562. * @param {number} [opt.textLineHeight]
  16563. * @param {Object} [opt.rich]
  16564. * @param {Object} [opt.truncate]
  16565. * @return {Object} {x, y, width, height, lineHeight}
  16566. */
  16567. function getTextBoundingRect(opt) {
  16568. return getBoundingRect(
  16569. opt.text,
  16570. opt.font,
  16571. opt.textAlign,
  16572. opt.textVerticalAlign,
  16573. opt.textPadding,
  16574. opt.textLineHeight,
  16575. opt.rich,
  16576. opt.truncate
  16577. );
  16578. }
  16579. /**
  16580. * @deprecated
  16581. * the `textLineHeight` was added later.
  16582. * For backward compatiblility, put it as the last parameter.
  16583. * But deprecated this interface. Please use `getTextBoundingRect` instead.
  16584. */
  16585. function getTextRect(
  16586. text, font, textAlign, textVerticalAlign, textPadding, rich, truncate, textLineHeight
  16587. ) {
  16588. return getBoundingRect(
  16589. text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate
  16590. );
  16591. }
  16592. var format = (Object.freeze || Object)({
  16593. addCommas: addCommas,
  16594. toCamelCase: toCamelCase,
  16595. normalizeCssArray: normalizeCssArray$1,
  16596. encodeHTML: encodeHTML,
  16597. formatTpl: formatTpl,
  16598. formatTplSimple: formatTplSimple,
  16599. getTooltipMarker: getTooltipMarker,
  16600. formatTime: formatTime,
  16601. capitalFirst: capitalFirst,
  16602. truncateText: truncateText$1,
  16603. getTextBoundingRect: getTextBoundingRect,
  16604. getTextRect: getTextRect
  16605. });
  16606. /*
  16607. * Licensed to the Apache Software Foundation (ASF) under one
  16608. * or more contributor license agreements. See the NOTICE file
  16609. * distributed with this work for additional information
  16610. * regarding copyright ownership. The ASF licenses this file
  16611. * to you under the Apache License, Version 2.0 (the
  16612. * "License"); you may not use this file except in compliance
  16613. * with the License. You may obtain a copy of the License at
  16614. *
  16615. * http://www.apache.org/licenses/LICENSE-2.0
  16616. *
  16617. * Unless required by applicable law or agreed to in writing,
  16618. * software distributed under the License is distributed on an
  16619. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16620. * KIND, either express or implied. See the License for the
  16621. * specific language governing permissions and limitations
  16622. * under the License.
  16623. */
  16624. // Layout helpers for each component positioning
  16625. var each$3 = each$1;
  16626. /**
  16627. * @public
  16628. */
  16629. var LOCATION_PARAMS = [
  16630. 'left', 'right', 'top', 'bottom', 'width', 'height'
  16631. ];
  16632. /**
  16633. * @public
  16634. */
  16635. var HV_NAMES = [
  16636. ['width', 'left', 'right'],
  16637. ['height', 'top', 'bottom']
  16638. ];
  16639. function boxLayout(orient, group, gap, maxWidth, maxHeight) {
  16640. var x = 0;
  16641. var y = 0;
  16642. if (maxWidth == null) {
  16643. maxWidth = Infinity;
  16644. }
  16645. if (maxHeight == null) {
  16646. maxHeight = Infinity;
  16647. }
  16648. var currentLineMaxSize = 0;
  16649. group.eachChild(function (child, idx) {
  16650. var position = child.position;
  16651. var rect = child.getBoundingRect();
  16652. var nextChild = group.childAt(idx + 1);
  16653. var nextChildRect = nextChild && nextChild.getBoundingRect();
  16654. var nextX;
  16655. var nextY;
  16656. if (orient === 'horizontal') {
  16657. var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);
  16658. nextX = x + moveX;
  16659. // Wrap when width exceeds maxWidth or meet a `newline` group
  16660. // FIXME compare before adding gap?
  16661. if (nextX > maxWidth || child.newline) {
  16662. x = 0;
  16663. nextX = moveX;
  16664. y += currentLineMaxSize + gap;
  16665. currentLineMaxSize = rect.height;
  16666. }
  16667. else {
  16668. // FIXME: consider rect.y is not `0`?
  16669. currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
  16670. }
  16671. }
  16672. else {
  16673. var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);
  16674. nextY = y + moveY;
  16675. // Wrap when width exceeds maxHeight or meet a `newline` group
  16676. if (nextY > maxHeight || child.newline) {
  16677. x += currentLineMaxSize + gap;
  16678. y = 0;
  16679. nextY = moveY;
  16680. currentLineMaxSize = rect.width;
  16681. }
  16682. else {
  16683. currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
  16684. }
  16685. }
  16686. if (child.newline) {
  16687. return;
  16688. }
  16689. position[0] = x;
  16690. position[1] = y;
  16691. orient === 'horizontal'
  16692. ? (x = nextX + gap)
  16693. : (y = nextY + gap);
  16694. });
  16695. }
  16696. /**
  16697. * VBox or HBox layouting
  16698. * @param {string} orient
  16699. * @param {module:zrender/container/Group} group
  16700. * @param {number} gap
  16701. * @param {number} [width=Infinity]
  16702. * @param {number} [height=Infinity]
  16703. */
  16704. var box = boxLayout;
  16705. /**
  16706. * VBox layouting
  16707. * @param {module:zrender/container/Group} group
  16708. * @param {number} gap
  16709. * @param {number} [width=Infinity]
  16710. * @param {number} [height=Infinity]
  16711. */
  16712. var vbox = curry(boxLayout, 'vertical');
  16713. /**
  16714. * HBox layouting
  16715. * @param {module:zrender/container/Group} group
  16716. * @param {number} gap
  16717. * @param {number} [width=Infinity]
  16718. * @param {number} [height=Infinity]
  16719. */
  16720. var hbox = curry(boxLayout, 'horizontal');
  16721. /**
  16722. * If x or x2 is not specified or 'center' 'left' 'right',
  16723. * the width would be as long as possible.
  16724. * If y or y2 is not specified or 'middle' 'top' 'bottom',
  16725. * the height would be as long as possible.
  16726. *
  16727. * @param {Object} positionInfo
  16728. * @param {number|string} [positionInfo.x]
  16729. * @param {number|string} [positionInfo.y]
  16730. * @param {number|string} [positionInfo.x2]
  16731. * @param {number|string} [positionInfo.y2]
  16732. * @param {Object} containerRect {width, height}
  16733. * @param {string|number} margin
  16734. * @return {Object} {width, height}
  16735. */
  16736. /**
  16737. * Parse position info.
  16738. *
  16739. * @param {Object} positionInfo
  16740. * @param {number|string} [positionInfo.left]
  16741. * @param {number|string} [positionInfo.top]
  16742. * @param {number|string} [positionInfo.right]
  16743. * @param {number|string} [positionInfo.bottom]
  16744. * @param {number|string} [positionInfo.width]
  16745. * @param {number|string} [positionInfo.height]
  16746. * @param {number|string} [positionInfo.aspect] Aspect is width / height
  16747. * @param {Object} containerRect
  16748. * @param {string|number} [margin]
  16749. *
  16750. * @return {module:zrender/core/BoundingRect}
  16751. */
  16752. function getLayoutRect(
  16753. positionInfo, containerRect, margin
  16754. ) {
  16755. margin = normalizeCssArray$1(margin || 0);
  16756. var containerWidth = containerRect.width;
  16757. var containerHeight = containerRect.height;
  16758. var left = parsePercent$1(positionInfo.left, containerWidth);
  16759. var top = parsePercent$1(positionInfo.top, containerHeight);
  16760. var right = parsePercent$1(positionInfo.right, containerWidth);
  16761. var bottom = parsePercent$1(positionInfo.bottom, containerHeight);
  16762. var width = parsePercent$1(positionInfo.width, containerWidth);
  16763. var height = parsePercent$1(positionInfo.height, containerHeight);
  16764. var verticalMargin = margin[2] + margin[0];
  16765. var horizontalMargin = margin[1] + margin[3];
  16766. var aspect = positionInfo.aspect;
  16767. // If width is not specified, calculate width from left and right
  16768. if (isNaN(width)) {
  16769. width = containerWidth - right - horizontalMargin - left;
  16770. }
  16771. if (isNaN(height)) {
  16772. height = containerHeight - bottom - verticalMargin - top;
  16773. }
  16774. if (aspect != null) {
  16775. // If width and height are not given
  16776. // 1. Graph should not exceeds the container
  16777. // 2. Aspect must be keeped
  16778. // 3. Graph should take the space as more as possible
  16779. // FIXME
  16780. // Margin is not considered, because there is no case that both
  16781. // using margin and aspect so far.
  16782. if (isNaN(width) && isNaN(height)) {
  16783. if (aspect > containerWidth / containerHeight) {
  16784. width = containerWidth * 0.8;
  16785. }
  16786. else {
  16787. height = containerHeight * 0.8;
  16788. }
  16789. }
  16790. // Calculate width or height with given aspect
  16791. if (isNaN(width)) {
  16792. width = aspect * height;
  16793. }
  16794. if (isNaN(height)) {
  16795. height = width / aspect;
  16796. }
  16797. }
  16798. // If left is not specified, calculate left from right and width
  16799. if (isNaN(left)) {
  16800. left = containerWidth - right - width - horizontalMargin;
  16801. }
  16802. if (isNaN(top)) {
  16803. top = containerHeight - bottom - height - verticalMargin;
  16804. }
  16805. // Align left and top
  16806. switch (positionInfo.left || positionInfo.right) {
  16807. case 'center':
  16808. left = containerWidth / 2 - width / 2 - margin[3];
  16809. break;
  16810. case 'right':
  16811. left = containerWidth - width - horizontalMargin;
  16812. break;
  16813. }
  16814. switch (positionInfo.top || positionInfo.bottom) {
  16815. case 'middle':
  16816. case 'center':
  16817. top = containerHeight / 2 - height / 2 - margin[0];
  16818. break;
  16819. case 'bottom':
  16820. top = containerHeight - height - verticalMargin;
  16821. break;
  16822. }
  16823. // If something is wrong and left, top, width, height are calculated as NaN
  16824. left = left || 0;
  16825. top = top || 0;
  16826. if (isNaN(width)) {
  16827. // Width may be NaN if only one value is given except width
  16828. width = containerWidth - horizontalMargin - left - (right || 0);
  16829. }
  16830. if (isNaN(height)) {
  16831. // Height may be NaN if only one value is given except height
  16832. height = containerHeight - verticalMargin - top - (bottom || 0);
  16833. }
  16834. var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
  16835. rect.margin = margin;
  16836. return rect;
  16837. }
  16838. /**
  16839. * Position a zr element in viewport
  16840. * Group position is specified by either
  16841. * {left, top}, {right, bottom}
  16842. * If all properties exists, right and bottom will be igonred.
  16843. *
  16844. * Logic:
  16845. * 1. Scale (against origin point in parent coord)
  16846. * 2. Rotate (against origin point in parent coord)
  16847. * 3. Traslate (with el.position by this method)
  16848. * So this method only fixes the last step 'Traslate', which does not affect
  16849. * scaling and rotating.
  16850. *
  16851. * If be called repeatly with the same input el, the same result will be gotten.
  16852. *
  16853. * @param {module:zrender/Element} el Should have `getBoundingRect` method.
  16854. * @param {Object} positionInfo
  16855. * @param {number|string} [positionInfo.left]
  16856. * @param {number|string} [positionInfo.top]
  16857. * @param {number|string} [positionInfo.right]
  16858. * @param {number|string} [positionInfo.bottom]
  16859. * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'
  16860. * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'
  16861. * @param {Object} containerRect
  16862. * @param {string|number} margin
  16863. * @param {Object} [opt]
  16864. * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
  16865. * @param {Array.<number>} [opt.boundingMode='all']
  16866. * Specify how to calculate boundingRect when locating.
  16867. * 'all': Position the boundingRect that is transformed and uioned
  16868. * both itself and its descendants.
  16869. * This mode simplies confine the elements in the bounding
  16870. * of their container (e.g., using 'right: 0').
  16871. * 'raw': Position the boundingRect that is not transformed and only itself.
  16872. * This mode is useful when you want a element can overflow its
  16873. * container. (Consider a rotated circle needs to be located in a corner.)
  16874. * In this mode positionInfo.width/height can only be number.
  16875. */
  16876. function positionElement(el, positionInfo, containerRect, margin, opt) {
  16877. var h = !opt || !opt.hv || opt.hv[0];
  16878. var v = !opt || !opt.hv || opt.hv[1];
  16879. var boundingMode = opt && opt.boundingMode || 'all';
  16880. if (!h && !v) {
  16881. return;
  16882. }
  16883. var rect;
  16884. if (boundingMode === 'raw') {
  16885. rect = el.type === 'group'
  16886. ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0)
  16887. : el.getBoundingRect();
  16888. }
  16889. else {
  16890. rect = el.getBoundingRect();
  16891. if (el.needLocalTransform()) {
  16892. var transform = el.getLocalTransform();
  16893. // Notice: raw rect may be inner object of el,
  16894. // which should not be modified.
  16895. rect = rect.clone();
  16896. rect.applyTransform(transform);
  16897. }
  16898. }
  16899. // The real width and height can not be specified but calculated by the given el.
  16900. positionInfo = getLayoutRect(
  16901. defaults(
  16902. {width: rect.width, height: rect.height},
  16903. positionInfo
  16904. ),
  16905. containerRect,
  16906. margin
  16907. );
  16908. // Because 'tranlate' is the last step in transform
  16909. // (see zrender/core/Transformable#getLocalTransform),
  16910. // we can just only modify el.position to get final result.
  16911. var elPos = el.position;
  16912. var dx = h ? positionInfo.x - rect.x : 0;
  16913. var dy = v ? positionInfo.y - rect.y : 0;
  16914. el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy]);
  16915. }
  16916. /**
  16917. * @param {Object} option Contains some of the properties in HV_NAMES.
  16918. * @param {number} hvIdx 0: horizontal; 1: vertical.
  16919. */
  16920. /**
  16921. * Consider Case:
  16922. * When defulat option has {left: 0, width: 100}, and we set {right: 0}
  16923. * through setOption or media query, using normal zrUtil.merge will cause
  16924. * {right: 0} does not take effect.
  16925. *
  16926. * @example
  16927. * ComponentModel.extend({
  16928. * init: function () {
  16929. * ...
  16930. * var inputPositionParams = layout.getLayoutParams(option);
  16931. * this.mergeOption(inputPositionParams);
  16932. * },
  16933. * mergeOption: function (newOption) {
  16934. * newOption && zrUtil.merge(thisOption, newOption, true);
  16935. * layout.mergeLayoutParam(thisOption, newOption);
  16936. * }
  16937. * });
  16938. *
  16939. * @param {Object} targetOption
  16940. * @param {Object} newOption
  16941. * @param {Object|string} [opt]
  16942. * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components
  16943. * that width (or height) should not be calculated by left and right (or top and bottom).
  16944. */
  16945. function mergeLayoutParam(targetOption, newOption, opt) {
  16946. !isObject$1(opt) && (opt = {});
  16947. var ignoreSize = opt.ignoreSize;
  16948. !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
  16949. var hResult = merge$$1(HV_NAMES[0], 0);
  16950. var vResult = merge$$1(HV_NAMES[1], 1);
  16951. copy(HV_NAMES[0], targetOption, hResult);
  16952. copy(HV_NAMES[1], targetOption, vResult);
  16953. function merge$$1(names, hvIdx) {
  16954. var newParams = {};
  16955. var newValueCount = 0;
  16956. var merged = {};
  16957. var mergedValueCount = 0;
  16958. var enoughParamNumber = 2;
  16959. each$3(names, function (name) {
  16960. merged[name] = targetOption[name];
  16961. });
  16962. each$3(names, function (name) {
  16963. // Consider case: newOption.width is null, which is
  16964. // set by user for removing width setting.
  16965. hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
  16966. hasValue(newParams, name) && newValueCount++;
  16967. hasValue(merged, name) && mergedValueCount++;
  16968. });
  16969. if (ignoreSize[hvIdx]) {
  16970. // Only one of left/right is premitted to exist.
  16971. if (hasValue(newOption, names[1])) {
  16972. merged[names[2]] = null;
  16973. }
  16974. else if (hasValue(newOption, names[2])) {
  16975. merged[names[1]] = null;
  16976. }
  16977. return merged;
  16978. }
  16979. // Case: newOption: {width: ..., right: ...},
  16980. // or targetOption: {right: ...} and newOption: {width: ...},
  16981. // There is no conflict when merged only has params count
  16982. // little than enoughParamNumber.
  16983. if (mergedValueCount === enoughParamNumber || !newValueCount) {
  16984. return merged;
  16985. }
  16986. // Case: newOption: {width: ..., right: ...},
  16987. // Than we can make sure user only want those two, and ignore
  16988. // all origin params in targetOption.
  16989. else if (newValueCount >= enoughParamNumber) {
  16990. return newParams;
  16991. }
  16992. else {
  16993. // Chose another param from targetOption by priority.
  16994. for (var i = 0; i < names.length; i++) {
  16995. var name = names[i];
  16996. if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
  16997. newParams[name] = targetOption[name];
  16998. break;
  16999. }
  17000. }
  17001. return newParams;
  17002. }
  17003. }
  17004. function hasProp(obj, name) {
  17005. return obj.hasOwnProperty(name);
  17006. }
  17007. function hasValue(obj, name) {
  17008. return obj[name] != null && obj[name] !== 'auto';
  17009. }
  17010. function copy(names, target, source) {
  17011. each$3(names, function (name) {
  17012. target[name] = source[name];
  17013. });
  17014. }
  17015. }
  17016. /**
  17017. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  17018. * @param {Object} source
  17019. * @return {Object} Result contains those props.
  17020. */
  17021. function getLayoutParams(source) {
  17022. return copyLayoutParams({}, source);
  17023. }
  17024. /**
  17025. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  17026. * @param {Object} source
  17027. * @return {Object} Result contains those props.
  17028. */
  17029. function copyLayoutParams(target, source) {
  17030. source && target && each$3(LOCATION_PARAMS, function (name) {
  17031. source.hasOwnProperty(name) && (target[name] = source[name]);
  17032. });
  17033. return target;
  17034. }
  17035. /*
  17036. * Licensed to the Apache Software Foundation (ASF) under one
  17037. * or more contributor license agreements. See the NOTICE file
  17038. * distributed with this work for additional information
  17039. * regarding copyright ownership. The ASF licenses this file
  17040. * to you under the Apache License, Version 2.0 (the
  17041. * "License"); you may not use this file except in compliance
  17042. * with the License. You may obtain a copy of the License at
  17043. *
  17044. * http://www.apache.org/licenses/LICENSE-2.0
  17045. *
  17046. * Unless required by applicable law or agreed to in writing,
  17047. * software distributed under the License is distributed on an
  17048. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17049. * KIND, either express or implied. See the License for the
  17050. * specific language governing permissions and limitations
  17051. * under the License.
  17052. */
  17053. var boxLayoutMixin = {
  17054. getBoxLayoutParams: function () {
  17055. return {
  17056. left: this.get('left'),
  17057. top: this.get('top'),
  17058. right: this.get('right'),
  17059. bottom: this.get('bottom'),
  17060. width: this.get('width'),
  17061. height: this.get('height')
  17062. };
  17063. }
  17064. };
  17065. /*
  17066. * Licensed to the Apache Software Foundation (ASF) under one
  17067. * or more contributor license agreements. See the NOTICE file
  17068. * distributed with this work for additional information
  17069. * regarding copyright ownership. The ASF licenses this file
  17070. * to you under the Apache License, Version 2.0 (the
  17071. * "License"); you may not use this file except in compliance
  17072. * with the License. You may obtain a copy of the License at
  17073. *
  17074. * http://www.apache.org/licenses/LICENSE-2.0
  17075. *
  17076. * Unless required by applicable law or agreed to in writing,
  17077. * software distributed under the License is distributed on an
  17078. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17079. * KIND, either express or implied. See the License for the
  17080. * specific language governing permissions and limitations
  17081. * under the License.
  17082. */
  17083. /**
  17084. * Component model
  17085. *
  17086. * @module echarts/model/Component
  17087. */
  17088. var inner$1 = makeInner();
  17089. /**
  17090. * @alias module:echarts/model/Component
  17091. * @constructor
  17092. * @param {Object} option
  17093. * @param {module:echarts/model/Model} parentModel
  17094. * @param {module:echarts/model/Model} ecModel
  17095. */
  17096. var ComponentModel = Model.extend({
  17097. type: 'component',
  17098. /**
  17099. * @readOnly
  17100. * @type {string}
  17101. */
  17102. id: '',
  17103. /**
  17104. * Because simplified concept is probably better, series.name (or component.name)
  17105. * has been having too many resposibilities:
  17106. * (1) Generating id (which requires name in option should not be modified).
  17107. * (2) As an index to mapping series when merging option or calling API (a name
  17108. * can refer to more then one components, which is convinient is some case).
  17109. * (3) Display.
  17110. * @readOnly
  17111. */
  17112. name: '',
  17113. /**
  17114. * @readOnly
  17115. * @type {string}
  17116. */
  17117. mainType: '',
  17118. /**
  17119. * @readOnly
  17120. * @type {string}
  17121. */
  17122. subType: '',
  17123. /**
  17124. * @readOnly
  17125. * @type {number}
  17126. */
  17127. componentIndex: 0,
  17128. /**
  17129. * @type {Object}
  17130. * @protected
  17131. */
  17132. defaultOption: null,
  17133. /**
  17134. * @type {module:echarts/model/Global}
  17135. * @readOnly
  17136. */
  17137. ecModel: null,
  17138. /**
  17139. * key: componentType
  17140. * value: Component model list, can not be null.
  17141. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  17142. * @readOnly
  17143. */
  17144. dependentModels: [],
  17145. /**
  17146. * @type {string}
  17147. * @readOnly
  17148. */
  17149. uid: null,
  17150. /**
  17151. * Support merge layout params.
  17152. * Only support 'box' now (left/right/top/bottom/width/height).
  17153. * @type {string|Object} Object can be {ignoreSize: true}
  17154. * @readOnly
  17155. */
  17156. layoutMode: null,
  17157. $constructor: function (option, parentModel, ecModel, extraOpt) {
  17158. Model.call(this, option, parentModel, ecModel, extraOpt);
  17159. this.uid = getUID('ec_cpt_model');
  17160. },
  17161. init: function (option, parentModel, ecModel, extraOpt) {
  17162. this.mergeDefaultAndTheme(option, ecModel);
  17163. },
  17164. mergeDefaultAndTheme: function (option, ecModel) {
  17165. var layoutMode = this.layoutMode;
  17166. var inputPositionParams = layoutMode
  17167. ? getLayoutParams(option) : {};
  17168. var themeModel = ecModel.getTheme();
  17169. merge(option, themeModel.get(this.mainType));
  17170. merge(option, this.getDefaultOption());
  17171. if (layoutMode) {
  17172. mergeLayoutParam(option, inputPositionParams, layoutMode);
  17173. }
  17174. },
  17175. mergeOption: function (option, extraOpt) {
  17176. merge(this.option, option, true);
  17177. var layoutMode = this.layoutMode;
  17178. if (layoutMode) {
  17179. mergeLayoutParam(this.option, option, layoutMode);
  17180. }
  17181. },
  17182. // Hooker after init or mergeOption
  17183. optionUpdated: function (newCptOption, isInit) {},
  17184. getDefaultOption: function () {
  17185. var fields = inner$1(this);
  17186. if (!fields.defaultOption) {
  17187. var optList = [];
  17188. var Class = this.constructor;
  17189. while (Class) {
  17190. var opt = Class.prototype.defaultOption;
  17191. opt && optList.push(opt);
  17192. Class = Class.superClass;
  17193. }
  17194. var defaultOption = {};
  17195. for (var i = optList.length - 1; i >= 0; i--) {
  17196. defaultOption = merge(defaultOption, optList[i], true);
  17197. }
  17198. fields.defaultOption = defaultOption;
  17199. }
  17200. return fields.defaultOption;
  17201. },
  17202. getReferringComponents: function (mainType) {
  17203. return this.ecModel.queryComponents({
  17204. mainType: mainType,
  17205. index: this.get(mainType + 'Index', true),
  17206. id: this.get(mainType + 'Id', true)
  17207. });
  17208. }
  17209. });
  17210. // Reset ComponentModel.extend, add preConstruct.
  17211. // clazzUtil.enableClassExtend(
  17212. // ComponentModel,
  17213. // function (option, parentModel, ecModel, extraOpt) {
  17214. // // Set dependentModels, componentIndex, name, id, mainType, subType.
  17215. // zrUtil.extend(this, extraOpt);
  17216. // this.uid = componentUtil.getUID('componentModel');
  17217. // // this.setReadOnly([
  17218. // // 'type', 'id', 'uid', 'name', 'mainType', 'subType',
  17219. // // 'dependentModels', 'componentIndex'
  17220. // // ]);
  17221. // }
  17222. // );
  17223. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  17224. enableClassManagement(
  17225. ComponentModel, {registerWhenExtend: true}
  17226. );
  17227. enableSubTypeDefaulter(ComponentModel);
  17228. // Add capability of ComponentModel.topologicalTravel.
  17229. enableTopologicalTravel(ComponentModel, getDependencies);
  17230. function getDependencies(componentType) {
  17231. var deps = [];
  17232. each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
  17233. deps = deps.concat(Clazz.prototype.dependencies || []);
  17234. });
  17235. // Ensure main type.
  17236. deps = map(deps, function (type) {
  17237. return parseClassType$1(type).main;
  17238. });
  17239. // Hack dataset for convenience.
  17240. if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {
  17241. deps.unshift('dataset');
  17242. }
  17243. return deps;
  17244. }
  17245. mixin(ComponentModel, boxLayoutMixin);
  17246. /*
  17247. * Licensed to the Apache Software Foundation (ASF) under one
  17248. * or more contributor license agreements. See the NOTICE file
  17249. * distributed with this work for additional information
  17250. * regarding copyright ownership. The ASF licenses this file
  17251. * to you under the Apache License, Version 2.0 (the
  17252. * "License"); you may not use this file except in compliance
  17253. * with the License. You may obtain a copy of the License at
  17254. *
  17255. * http://www.apache.org/licenses/LICENSE-2.0
  17256. *
  17257. * Unless required by applicable law or agreed to in writing,
  17258. * software distributed under the License is distributed on an
  17259. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17260. * KIND, either express or implied. See the License for the
  17261. * specific language governing permissions and limitations
  17262. * under the License.
  17263. */
  17264. var platform = '';
  17265. // Navigator not exists in node
  17266. if (typeof navigator !== 'undefined') {
  17267. platform = navigator.platform || '';
  17268. }
  17269. var globalDefault = {
  17270. // backgroundColor: 'rgba(0,0,0,0)',
  17271. // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
  17272. // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
  17273. // Light colors:
  17274. // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
  17275. // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
  17276. // Dark colors:
  17277. color: [
  17278. '#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83',
  17279. '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'
  17280. ],
  17281. gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
  17282. // If xAxis and yAxis declared, grid is created by default.
  17283. // grid: {},
  17284. textStyle: {
  17285. // color: '#000',
  17286. // decoration: 'none',
  17287. // PENDING
  17288. fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
  17289. // fontFamily: 'Arial, Verdana, sans-serif',
  17290. fontSize: 12,
  17291. fontStyle: 'normal',
  17292. fontWeight: 'normal'
  17293. },
  17294. // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
  17295. // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  17296. // Default is source-over
  17297. blendMode: null,
  17298. animation: 'auto',
  17299. animationDuration: 1000,
  17300. animationDurationUpdate: 300,
  17301. animationEasing: 'exponentialOut',
  17302. animationEasingUpdate: 'cubicOut',
  17303. animationThreshold: 2000,
  17304. // Configuration for progressive/incremental rendering
  17305. progressiveThreshold: 3000,
  17306. progressive: 400,
  17307. // Threshold of if use single hover layer to optimize.
  17308. // It is recommended that `hoverLayerThreshold` is equivalent to or less than
  17309. // `progressiveThreshold`, otherwise hover will cause restart of progressive,
  17310. // which is unexpected.
  17311. // see example <echarts/test/heatmap-large.html>.
  17312. hoverLayerThreshold: 3000,
  17313. // See: module:echarts/scale/Time
  17314. useUTC: false
  17315. };
  17316. /*
  17317. * Licensed to the Apache Software Foundation (ASF) under one
  17318. * or more contributor license agreements. See the NOTICE file
  17319. * distributed with this work for additional information
  17320. * regarding copyright ownership. The ASF licenses this file
  17321. * to you under the Apache License, Version 2.0 (the
  17322. * "License"); you may not use this file except in compliance
  17323. * with the License. You may obtain a copy of the License at
  17324. *
  17325. * http://www.apache.org/licenses/LICENSE-2.0
  17326. *
  17327. * Unless required by applicable law or agreed to in writing,
  17328. * software distributed under the License is distributed on an
  17329. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17330. * KIND, either express or implied. See the License for the
  17331. * specific language governing permissions and limitations
  17332. * under the License.
  17333. */
  17334. var inner$2 = makeInner();
  17335. function getNearestColorPalette(colors, requestColorNum) {
  17336. var paletteNum = colors.length;
  17337. // TODO colors must be in order
  17338. for (var i = 0; i < paletteNum; i++) {
  17339. if (colors[i].length > requestColorNum) {
  17340. return colors[i];
  17341. }
  17342. }
  17343. return colors[paletteNum - 1];
  17344. }
  17345. var colorPaletteMixin = {
  17346. clearColorPalette: function () {
  17347. inner$2(this).colorIdx = 0;
  17348. inner$2(this).colorNameMap = {};
  17349. },
  17350. /**
  17351. * @param {string} name MUST NOT be null/undefined. Otherwise call this function
  17352. * twise with the same parameters will get different result.
  17353. * @param {Object} [scope=this]
  17354. * @param {Object} [requestColorNum]
  17355. * @return {string} color string.
  17356. */
  17357. getColorFromPalette: function (name, scope, requestColorNum) {
  17358. scope = scope || this;
  17359. var scopeFields = inner$2(scope);
  17360. var colorIdx = scopeFields.colorIdx || 0;
  17361. var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {};
  17362. // Use `hasOwnProperty` to avoid conflict with Object.prototype.
  17363. if (colorNameMap.hasOwnProperty(name)) {
  17364. return colorNameMap[name];
  17365. }
  17366. var defaultColorPalette = normalizeToArray(this.get('color', true));
  17367. var layeredColorPalette = this.get('colorLayer', true);
  17368. var colorPalette = ((requestColorNum == null || !layeredColorPalette)
  17369. ? defaultColorPalette : getNearestColorPalette(layeredColorPalette, requestColorNum));
  17370. // In case can't find in layered color palette.
  17371. colorPalette = colorPalette || defaultColorPalette;
  17372. if (!colorPalette || !colorPalette.length) {
  17373. return;
  17374. }
  17375. var color = colorPalette[colorIdx];
  17376. if (name) {
  17377. colorNameMap[name] = color;
  17378. }
  17379. scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length;
  17380. return color;
  17381. }
  17382. };
  17383. /*
  17384. * Licensed to the Apache Software Foundation (ASF) under one
  17385. * or more contributor license agreements. See the NOTICE file
  17386. * distributed with this work for additional information
  17387. * regarding copyright ownership. The ASF licenses this file
  17388. * to you under the Apache License, Version 2.0 (the
  17389. * "License"); you may not use this file except in compliance
  17390. * with the License. You may obtain a copy of the License at
  17391. *
  17392. * http://www.apache.org/licenses/LICENSE-2.0
  17393. *
  17394. * Unless required by applicable law or agreed to in writing,
  17395. * software distributed under the License is distributed on an
  17396. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17397. * KIND, either express or implied. See the License for the
  17398. * specific language governing permissions and limitations
  17399. * under the License.
  17400. */
  17401. /**
  17402. * Helper for model references.
  17403. * There are many manners to refer axis/coordSys.
  17404. */
  17405. // TODO
  17406. // merge relevant logic to this file?
  17407. // check: "modelHelper" of tooltip and "BrushTargetManager".
  17408. /**
  17409. * @return {Object} For example:
  17410. * {
  17411. * coordSysName: 'cartesian2d',
  17412. * coordSysDims: ['x', 'y', ...],
  17413. * axisMap: HashMap({
  17414. * x: xAxisModel,
  17415. * y: yAxisModel
  17416. * }),
  17417. * categoryAxisMap: HashMap({
  17418. * x: xAxisModel,
  17419. * y: undefined
  17420. * }),
  17421. * // It also indicate that whether there is category axis.
  17422. * firstCategoryDimIndex: 1,
  17423. * // To replace user specified encode.
  17424. * }
  17425. */
  17426. function getCoordSysDefineBySeries(seriesModel) {
  17427. var coordSysName = seriesModel.get('coordinateSystem');
  17428. var result = {
  17429. coordSysName: coordSysName,
  17430. coordSysDims: [],
  17431. axisMap: createHashMap(),
  17432. categoryAxisMap: createHashMap()
  17433. };
  17434. var fetch = fetchers[coordSysName];
  17435. if (fetch) {
  17436. fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);
  17437. return result;
  17438. }
  17439. }
  17440. var fetchers = {
  17441. cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {
  17442. var xAxisModel = seriesModel.getReferringComponents('xAxis')[0];
  17443. var yAxisModel = seriesModel.getReferringComponents('yAxis')[0];
  17444. if (__DEV__) {
  17445. if (!xAxisModel) {
  17446. throw new Error('xAxis "' + retrieve(
  17447. seriesModel.get('xAxisIndex'),
  17448. seriesModel.get('xAxisId'),
  17449. 0
  17450. ) + '" not found');
  17451. }
  17452. if (!yAxisModel) {
  17453. throw new Error('yAxis "' + retrieve(
  17454. seriesModel.get('xAxisIndex'),
  17455. seriesModel.get('yAxisId'),
  17456. 0
  17457. ) + '" not found');
  17458. }
  17459. }
  17460. result.coordSysDims = ['x', 'y'];
  17461. axisMap.set('x', xAxisModel);
  17462. axisMap.set('y', yAxisModel);
  17463. if (isCategory(xAxisModel)) {
  17464. categoryAxisMap.set('x', xAxisModel);
  17465. result.firstCategoryDimIndex = 0;
  17466. }
  17467. if (isCategory(yAxisModel)) {
  17468. categoryAxisMap.set('y', yAxisModel);
  17469. result.firstCategoryDimIndex = 1;
  17470. }
  17471. },
  17472. singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {
  17473. var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0];
  17474. if (__DEV__) {
  17475. if (!singleAxisModel) {
  17476. throw new Error('singleAxis should be specified.');
  17477. }
  17478. }
  17479. result.coordSysDims = ['single'];
  17480. axisMap.set('single', singleAxisModel);
  17481. if (isCategory(singleAxisModel)) {
  17482. categoryAxisMap.set('single', singleAxisModel);
  17483. result.firstCategoryDimIndex = 0;
  17484. }
  17485. },
  17486. polar: function (seriesModel, result, axisMap, categoryAxisMap) {
  17487. var polarModel = seriesModel.getReferringComponents('polar')[0];
  17488. var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
  17489. var angleAxisModel = polarModel.findAxisModel('angleAxis');
  17490. if (__DEV__) {
  17491. if (!angleAxisModel) {
  17492. throw new Error('angleAxis option not found');
  17493. }
  17494. if (!radiusAxisModel) {
  17495. throw new Error('radiusAxis option not found');
  17496. }
  17497. }
  17498. result.coordSysDims = ['radius', 'angle'];
  17499. axisMap.set('radius', radiusAxisModel);
  17500. axisMap.set('angle', angleAxisModel);
  17501. if (isCategory(radiusAxisModel)) {
  17502. categoryAxisMap.set('radius', radiusAxisModel);
  17503. result.firstCategoryDimIndex = 0;
  17504. }
  17505. if (isCategory(angleAxisModel)) {
  17506. categoryAxisMap.set('angle', angleAxisModel);
  17507. result.firstCategoryDimIndex = 1;
  17508. }
  17509. },
  17510. geo: function (seriesModel, result, axisMap, categoryAxisMap) {
  17511. result.coordSysDims = ['lng', 'lat'];
  17512. },
  17513. parallel: function (seriesModel, result, axisMap, categoryAxisMap) {
  17514. var ecModel = seriesModel.ecModel;
  17515. var parallelModel = ecModel.getComponent(
  17516. 'parallel', seriesModel.get('parallelIndex')
  17517. );
  17518. var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();
  17519. each$1(parallelModel.parallelAxisIndex, function (axisIndex, index) {
  17520. var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
  17521. var axisDim = coordSysDims[index];
  17522. axisMap.set(axisDim, axisModel);
  17523. if (isCategory(axisModel) && result.firstCategoryDimIndex == null) {
  17524. categoryAxisMap.set(axisDim, axisModel);
  17525. result.firstCategoryDimIndex = index;
  17526. }
  17527. });
  17528. }
  17529. };
  17530. function isCategory(axisModel) {
  17531. return axisModel.get('type') === 'category';
  17532. }
  17533. /*
  17534. * Licensed to the Apache Software Foundation (ASF) under one
  17535. * or more contributor license agreements. See the NOTICE file
  17536. * distributed with this work for additional information
  17537. * regarding copyright ownership. The ASF licenses this file
  17538. * to you under the Apache License, Version 2.0 (the
  17539. * "License"); you may not use this file except in compliance
  17540. * with the License. You may obtain a copy of the License at
  17541. *
  17542. * http://www.apache.org/licenses/LICENSE-2.0
  17543. *
  17544. * Unless required by applicable law or agreed to in writing,
  17545. * software distributed under the License is distributed on an
  17546. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17547. * KIND, either express or implied. See the License for the
  17548. * specific language governing permissions and limitations
  17549. * under the License.
  17550. */
  17551. // Avoid typo.
  17552. var SOURCE_FORMAT_ORIGINAL = 'original';
  17553. var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
  17554. var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
  17555. var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';
  17556. var SOURCE_FORMAT_UNKNOWN = 'unknown';
  17557. // ??? CHANGE A NAME
  17558. var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';
  17559. var SERIES_LAYOUT_BY_COLUMN = 'column';
  17560. var SERIES_LAYOUT_BY_ROW = 'row';
  17561. /*
  17562. * Licensed to the Apache Software Foundation (ASF) under one
  17563. * or more contributor license agreements. See the NOTICE file
  17564. * distributed with this work for additional information
  17565. * regarding copyright ownership. The ASF licenses this file
  17566. * to you under the Apache License, Version 2.0 (the
  17567. * "License"); you may not use this file except in compliance
  17568. * with the License. You may obtain a copy of the License at
  17569. *
  17570. * http://www.apache.org/licenses/LICENSE-2.0
  17571. *
  17572. * Unless required by applicable law or agreed to in writing,
  17573. * software distributed under the License is distributed on an
  17574. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17575. * KIND, either express or implied. See the License for the
  17576. * specific language governing permissions and limitations
  17577. * under the License.
  17578. */
  17579. /**
  17580. * [sourceFormat]
  17581. *
  17582. * + "original":
  17583. * This format is only used in series.data, where
  17584. * itemStyle can be specified in data item.
  17585. *
  17586. * + "arrayRows":
  17587. * [
  17588. * ['product', 'score', 'amount'],
  17589. * ['Matcha Latte', 89.3, 95.8],
  17590. * ['Milk Tea', 92.1, 89.4],
  17591. * ['Cheese Cocoa', 94.4, 91.2],
  17592. * ['Walnut Brownie', 85.4, 76.9]
  17593. * ]
  17594. *
  17595. * + "objectRows":
  17596. * [
  17597. * {product: 'Matcha Latte', score: 89.3, amount: 95.8},
  17598. * {product: 'Milk Tea', score: 92.1, amount: 89.4},
  17599. * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2},
  17600. * {product: 'Walnut Brownie', score: 85.4, amount: 76.9}
  17601. * ]
  17602. *
  17603. * + "keyedColumns":
  17604. * {
  17605. * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],
  17606. * 'count': [823, 235, 1042, 988],
  17607. * 'score': [95.8, 81.4, 91.2, 76.9]
  17608. * }
  17609. *
  17610. * + "typedArray"
  17611. *
  17612. * + "unknown"
  17613. */
  17614. /**
  17615. * @constructor
  17616. * @param {Object} fields
  17617. * @param {string} fields.sourceFormat
  17618. * @param {Array|Object} fields.fromDataset
  17619. * @param {Array|Object} [fields.data]
  17620. * @param {string} [seriesLayoutBy='column']
  17621. * @param {Array.<Object|string>} [dimensionsDefine]
  17622. * @param {Objet|HashMap} [encodeDefine]
  17623. * @param {number} [startIndex=0]
  17624. * @param {number} [dimensionsDetectCount]
  17625. */
  17626. function Source(fields) {
  17627. /**
  17628. * @type {boolean}
  17629. */
  17630. this.fromDataset = fields.fromDataset;
  17631. /**
  17632. * Not null/undefined.
  17633. * @type {Array|Object}
  17634. */
  17635. this.data = fields.data || (
  17636. fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []
  17637. );
  17638. /**
  17639. * See also "detectSourceFormat".
  17640. * Not null/undefined.
  17641. * @type {string}
  17642. */
  17643. this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
  17644. /**
  17645. * 'row' or 'column'
  17646. * Not null/undefined.
  17647. * @type {string} seriesLayoutBy
  17648. */
  17649. this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
  17650. /**
  17651. * dimensions definition in option.
  17652. * can be null/undefined.
  17653. * @type {Array.<Object|string>}
  17654. */
  17655. this.dimensionsDefine = fields.dimensionsDefine;
  17656. /**
  17657. * encode definition in option.
  17658. * can be null/undefined.
  17659. * @type {Objet|HashMap}
  17660. */
  17661. this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
  17662. /**
  17663. * Not null/undefined, uint.
  17664. * @type {number}
  17665. */
  17666. this.startIndex = fields.startIndex || 0;
  17667. /**
  17668. * Can be null/undefined (when unknown), uint.
  17669. * @type {number}
  17670. */
  17671. this.dimensionsDetectCount = fields.dimensionsDetectCount;
  17672. }
  17673. /**
  17674. * Wrap original series data for some compatibility cases.
  17675. */
  17676. Source.seriesDataToSource = function (data) {
  17677. return new Source({
  17678. data: data,
  17679. sourceFormat: isTypedArray(data)
  17680. ? SOURCE_FORMAT_TYPED_ARRAY
  17681. : SOURCE_FORMAT_ORIGINAL,
  17682. fromDataset: false
  17683. });
  17684. };
  17685. enableClassCheck(Source);
  17686. /*
  17687. * Licensed to the Apache Software Foundation (ASF) under one
  17688. * or more contributor license agreements. See the NOTICE file
  17689. * distributed with this work for additional information
  17690. * regarding copyright ownership. The ASF licenses this file
  17691. * to you under the Apache License, Version 2.0 (the
  17692. * "License"); you may not use this file except in compliance
  17693. * with the License. You may obtain a copy of the License at
  17694. *
  17695. * http://www.apache.org/licenses/LICENSE-2.0
  17696. *
  17697. * Unless required by applicable law or agreed to in writing,
  17698. * software distributed under the License is distributed on an
  17699. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17700. * KIND, either express or implied. See the License for the
  17701. * specific language governing permissions and limitations
  17702. * under the License.
  17703. */
  17704. var inner$3 = makeInner();
  17705. /**
  17706. * @see {module:echarts/data/Source}
  17707. * @param {module:echarts/component/dataset/DatasetModel} datasetModel
  17708. * @return {string} sourceFormat
  17709. */
  17710. function detectSourceFormat(datasetModel) {
  17711. var data = datasetModel.option.source;
  17712. var sourceFormat = SOURCE_FORMAT_UNKNOWN;
  17713. if (isTypedArray(data)) {
  17714. sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;
  17715. }
  17716. else if (isArray(data)) {
  17717. // FIXME Whether tolerate null in top level array?
  17718. if (data.length === 0) {
  17719. sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
  17720. }
  17721. for (var i = 0, len = data.length; i < len; i++) {
  17722. var item = data[i];
  17723. if (item == null) {
  17724. continue;
  17725. }
  17726. else if (isArray(item)) {
  17727. sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
  17728. break;
  17729. }
  17730. else if (isObject$1(item)) {
  17731. sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;
  17732. break;
  17733. }
  17734. }
  17735. }
  17736. else if (isObject$1(data)) {
  17737. for (var key in data) {
  17738. if (data.hasOwnProperty(key) && isArrayLike(data[key])) {
  17739. sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;
  17740. break;
  17741. }
  17742. }
  17743. }
  17744. else if (data != null) {
  17745. throw new Error('Invalid data');
  17746. }
  17747. inner$3(datasetModel).sourceFormat = sourceFormat;
  17748. }
  17749. /**
  17750. * [Scenarios]:
  17751. * (1) Provide source data directly:
  17752. * series: {
  17753. * encode: {...},
  17754. * dimensions: [...]
  17755. * seriesLayoutBy: 'row',
  17756. * data: [[...]]
  17757. * }
  17758. * (2) Refer to datasetModel.
  17759. * series: [{
  17760. * encode: {...}
  17761. * // Ignore datasetIndex means `datasetIndex: 0`
  17762. * // and the dimensions defination in dataset is used
  17763. * }, {
  17764. * encode: {...},
  17765. * seriesLayoutBy: 'column',
  17766. * datasetIndex: 1
  17767. * }]
  17768. *
  17769. * Get data from series itself or datset.
  17770. * @return {module:echarts/data/Source} source
  17771. */
  17772. function getSource(seriesModel) {
  17773. return inner$3(seriesModel).source;
  17774. }
  17775. /**
  17776. * MUST be called before mergeOption of all series.
  17777. * @param {module:echarts/model/Global} ecModel
  17778. */
  17779. function resetSourceDefaulter(ecModel) {
  17780. // `datasetMap` is used to make default encode.
  17781. inner$3(ecModel).datasetMap = createHashMap();
  17782. }
  17783. /**
  17784. * [Caution]:
  17785. * MUST be called after series option merged and
  17786. * before "series.getInitailData()" called.
  17787. *
  17788. * [The rule of making default encode]:
  17789. * Category axis (if exists) alway map to the first dimension.
  17790. * Each other axis occupies a subsequent dimension.
  17791. *
  17792. * [Why make default encode]:
  17793. * Simplify the typing of encode in option, avoiding the case like that:
  17794. * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}],
  17795. * where the "y" have to be manually typed as "1, 2, 3, ...".
  17796. *
  17797. * @param {module:echarts/model/Series} seriesModel
  17798. */
  17799. function prepareSource(seriesModel) {
  17800. var seriesOption = seriesModel.option;
  17801. var data = seriesOption.data;
  17802. var sourceFormat = isTypedArray(data)
  17803. ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
  17804. var fromDataset = false;
  17805. var seriesLayoutBy = seriesOption.seriesLayoutBy;
  17806. var sourceHeader = seriesOption.sourceHeader;
  17807. var dimensionsDefine = seriesOption.dimensions;
  17808. var datasetModel = getDatasetModel(seriesModel);
  17809. if (datasetModel) {
  17810. var datasetOption = datasetModel.option;
  17811. data = datasetOption.source;
  17812. sourceFormat = inner$3(datasetModel).sourceFormat;
  17813. fromDataset = true;
  17814. // These settings from series has higher priority.
  17815. seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy;
  17816. sourceHeader == null && (sourceHeader = datasetOption.sourceHeader);
  17817. dimensionsDefine = dimensionsDefine || datasetOption.dimensions;
  17818. }
  17819. var completeResult = completeBySourceData(
  17820. data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine
  17821. );
  17822. // Note: dataset option does not have `encode`.
  17823. var encodeDefine = seriesOption.encode;
  17824. if (!encodeDefine && datasetModel) {
  17825. encodeDefine = makeDefaultEncode(
  17826. seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
  17827. );
  17828. }
  17829. inner$3(seriesModel).source = new Source({
  17830. data: data,
  17831. fromDataset: fromDataset,
  17832. seriesLayoutBy: seriesLayoutBy,
  17833. sourceFormat: sourceFormat,
  17834. dimensionsDefine: completeResult.dimensionsDefine,
  17835. startIndex: completeResult.startIndex,
  17836. dimensionsDetectCount: completeResult.dimensionsDetectCount,
  17837. encodeDefine: encodeDefine
  17838. });
  17839. }
  17840. // return {startIndex, dimensionsDefine, dimensionsCount}
  17841. function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) {
  17842. if (!data) {
  17843. return {dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)};
  17844. }
  17845. var dimensionsDetectCount;
  17846. var startIndex;
  17847. var findPotentialName;
  17848. if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
  17849. // Rule: Most of the first line are string: it is header.
  17850. // Caution: consider a line with 5 string and 1 number,
  17851. // it still can not be sure it is a head, because the
  17852. // 5 string may be 5 values of category columns.
  17853. if (sourceHeader === 'auto' || sourceHeader == null) {
  17854. arrayRowsTravelFirst(function (val) {
  17855. // '-' is regarded as null/undefined.
  17856. if (val != null && val !== '-') {
  17857. if (isString(val)) {
  17858. startIndex == null && (startIndex = 1);
  17859. }
  17860. else {
  17861. startIndex = 0;
  17862. }
  17863. }
  17864. // 10 is an experience number, avoid long loop.
  17865. }, seriesLayoutBy, data, 10);
  17866. }
  17867. else {
  17868. startIndex = sourceHeader ? 1 : 0;
  17869. }
  17870. if (!dimensionsDefine && startIndex === 1) {
  17871. dimensionsDefine = [];
  17872. arrayRowsTravelFirst(function (val, index) {
  17873. dimensionsDefine[index] = val != null ? val : '';
  17874. }, seriesLayoutBy, data);
  17875. }
  17876. dimensionsDetectCount = dimensionsDefine
  17877. ? dimensionsDefine.length
  17878. : seriesLayoutBy === SERIES_LAYOUT_BY_ROW
  17879. ? data.length
  17880. : data[0]
  17881. ? data[0].length
  17882. : null;
  17883. }
  17884. else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
  17885. if (!dimensionsDefine) {
  17886. dimensionsDefine = objectRowsCollectDimensions(data);
  17887. findPotentialName = true;
  17888. }
  17889. }
  17890. else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
  17891. if (!dimensionsDefine) {
  17892. dimensionsDefine = [];
  17893. findPotentialName = true;
  17894. each$1(data, function (colArr, key) {
  17895. dimensionsDefine.push(key);
  17896. });
  17897. }
  17898. }
  17899. else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  17900. var value0 = getDataItemValue(data[0]);
  17901. dimensionsDetectCount = isArray(value0) && value0.length || 1;
  17902. }
  17903. else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
  17904. if (__DEV__) {
  17905. assert$1(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');
  17906. }
  17907. }
  17908. var potentialNameDimIndex;
  17909. if (findPotentialName) {
  17910. each$1(dimensionsDefine, function (dim, idx) {
  17911. if ((isObject$1(dim) ? dim.name : dim) === 'name') {
  17912. potentialNameDimIndex = idx;
  17913. }
  17914. });
  17915. }
  17916. return {
  17917. startIndex: startIndex,
  17918. dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine),
  17919. dimensionsDetectCount: dimensionsDetectCount,
  17920. potentialNameDimIndex: potentialNameDimIndex
  17921. // TODO: potentialIdDimIdx
  17922. };
  17923. }
  17924. // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],
  17925. // which is reasonable. But dimension name is duplicated.
  17926. // Returns undefined or an array contains only object without null/undefiend or string.
  17927. function normalizeDimensionsDefine(dimensionsDefine) {
  17928. if (!dimensionsDefine) {
  17929. // The meaning of null/undefined is different from empty array.
  17930. return;
  17931. }
  17932. var nameMap = createHashMap();
  17933. return map(dimensionsDefine, function (item, index) {
  17934. item = extend({}, isObject$1(item) ? item : {name: item});
  17935. // User can set null in dimensions.
  17936. // We dont auto specify name, othewise a given name may
  17937. // cause it be refered unexpectedly.
  17938. if (item.name == null) {
  17939. return item;
  17940. }
  17941. // Also consider number form like 2012.
  17942. item.name += '';
  17943. // User may also specify displayName.
  17944. // displayName will always exists except user not
  17945. // specified or dim name is not specified or detected.
  17946. // (A auto generated dim name will not be used as
  17947. // displayName).
  17948. if (item.displayName == null) {
  17949. item.displayName = item.name;
  17950. }
  17951. var exist = nameMap.get(item.name);
  17952. if (!exist) {
  17953. nameMap.set(item.name, {count: 1});
  17954. }
  17955. else {
  17956. item.name += '-' + exist.count++;
  17957. }
  17958. return item;
  17959. });
  17960. }
  17961. function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {
  17962. maxLoop == null && (maxLoop = Infinity);
  17963. if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
  17964. for (var i = 0; i < data.length && i < maxLoop; i++) {
  17965. cb(data[i] ? data[i][0] : null, i);
  17966. }
  17967. }
  17968. else {
  17969. var value0 = data[0] || [];
  17970. for (var i = 0; i < value0.length && i < maxLoop; i++) {
  17971. cb(value0[i], i);
  17972. }
  17973. }
  17974. }
  17975. function objectRowsCollectDimensions(data) {
  17976. var firstIndex = 0;
  17977. var obj;
  17978. while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line
  17979. if (obj) {
  17980. var dimensions = [];
  17981. each$1(obj, function (value, key) {
  17982. dimensions.push(key);
  17983. });
  17984. return dimensions;
  17985. }
  17986. }
  17987. // ??? TODO merge to completedimensions, where also has
  17988. // default encode making logic. And the default rule
  17989. // should depends on series? consider 'map'.
  17990. function makeDefaultEncode(
  17991. seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
  17992. ) {
  17993. var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
  17994. var encode = {};
  17995. // var encodeTooltip = [];
  17996. // var encodeLabel = [];
  17997. var encodeItemName = [];
  17998. var encodeSeriesName = [];
  17999. var seriesType = seriesModel.subType;
  18000. // ??? TODO refactor: provide by series itself.
  18001. // Consider the case: 'map' series is based on geo coordSys,
  18002. // 'graph', 'heatmap' can be based on cartesian. But can not
  18003. // give default rule simply here.
  18004. var nSeriesMap = createHashMap(['pie', 'map', 'funnel']);
  18005. var cSeriesMap = createHashMap([
  18006. 'line', 'bar', 'pictorialBar', 'scatter', 'effectScatter', 'candlestick', 'boxplot'
  18007. ]);
  18008. // Usually in this case series will use the first data
  18009. // dimension as the "value" dimension, or other default
  18010. // processes respectively.
  18011. if (coordSysDefine && cSeriesMap.get(seriesType) != null) {
  18012. var ecModel = seriesModel.ecModel;
  18013. var datasetMap = inner$3(ecModel).datasetMap;
  18014. var key = datasetModel.uid + '_' + seriesLayoutBy;
  18015. var datasetRecord = datasetMap.get(key)
  18016. || datasetMap.set(key, {categoryWayDim: 1, valueWayDim: 0});
  18017. // TODO
  18018. // Auto detect first time axis and do arrangement.
  18019. each$1(coordSysDefine.coordSysDims, function (coordDim) {
  18020. // In value way.
  18021. if (coordSysDefine.firstCategoryDimIndex == null) {
  18022. var dataDim = datasetRecord.valueWayDim++;
  18023. encode[coordDim] = dataDim;
  18024. // ??? TODO give a better default series name rule?
  18025. // especially when encode x y specified.
  18026. // consider: when mutiple series share one dimension
  18027. // category axis, series name should better use
  18028. // the other dimsion name. On the other hand, use
  18029. // both dimensions name.
  18030. encodeSeriesName.push(dataDim);
  18031. // encodeTooltip.push(dataDim);
  18032. // encodeLabel.push(dataDim);
  18033. }
  18034. // In category way, category axis.
  18035. else if (coordSysDefine.categoryAxisMap.get(coordDim)) {
  18036. encode[coordDim] = 0;
  18037. encodeItemName.push(0);
  18038. }
  18039. // In category way, non-category axis.
  18040. else {
  18041. var dataDim = datasetRecord.categoryWayDim++;
  18042. encode[coordDim] = dataDim;
  18043. // encodeTooltip.push(dataDim);
  18044. // encodeLabel.push(dataDim);
  18045. encodeSeriesName.push(dataDim);
  18046. }
  18047. });
  18048. }
  18049. // Do not make a complex rule! Hard to code maintain and not necessary.
  18050. // ??? TODO refactor: provide by series itself.
  18051. // [{name: ..., value: ...}, ...] like:
  18052. else if (nSeriesMap.get(seriesType) != null) {
  18053. // Find the first not ordinal. (5 is an experience value)
  18054. var firstNotOrdinal;
  18055. for (var i = 0; i < 5 && firstNotOrdinal == null; i++) {
  18056. if (!doGuessOrdinal(
  18057. data, sourceFormat, seriesLayoutBy,
  18058. completeResult.dimensionsDefine, completeResult.startIndex, i
  18059. )) {
  18060. firstNotOrdinal = i;
  18061. }
  18062. }
  18063. if (firstNotOrdinal != null) {
  18064. encode.value = firstNotOrdinal;
  18065. var nameDimIndex = completeResult.potentialNameDimIndex
  18066. || Math.max(firstNotOrdinal - 1, 0);
  18067. // By default, label use itemName in charts.
  18068. // So we dont set encodeLabel here.
  18069. encodeSeriesName.push(nameDimIndex);
  18070. encodeItemName.push(nameDimIndex);
  18071. // encodeTooltip.push(firstNotOrdinal);
  18072. }
  18073. }
  18074. // encodeTooltip.length && (encode.tooltip = encodeTooltip);
  18075. // encodeLabel.length && (encode.label = encodeLabel);
  18076. encodeItemName.length && (encode.itemName = encodeItemName);
  18077. encodeSeriesName.length && (encode.seriesName = encodeSeriesName);
  18078. return encode;
  18079. }
  18080. /**
  18081. * If return null/undefined, indicate that should not use datasetModel.
  18082. */
  18083. function getDatasetModel(seriesModel) {
  18084. var option = seriesModel.option;
  18085. // Caution: consider the scenario:
  18086. // A dataset is declared and a series is not expected to use the dataset,
  18087. // and at the beginning `setOption({series: { noData })` (just prepare other
  18088. // option but no data), then `setOption({series: {data: [...]}); In this case,
  18089. // the user should set an empty array to avoid that dataset is used by default.
  18090. var thisData = option.data;
  18091. if (!thisData) {
  18092. return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0);
  18093. }
  18094. }
  18095. /**
  18096. * The rule should not be complex, otherwise user might not
  18097. * be able to known where the data is wrong.
  18098. * The code is ugly, but how to make it neat?
  18099. *
  18100. * @param {module:echars/data/Source} source
  18101. * @param {number} dimIndex
  18102. * @return {boolean} Whether ordinal.
  18103. */
  18104. function guessOrdinal(source, dimIndex) {
  18105. return doGuessOrdinal(
  18106. source.data,
  18107. source.sourceFormat,
  18108. source.seriesLayoutBy,
  18109. source.dimensionsDefine,
  18110. source.startIndex,
  18111. dimIndex
  18112. );
  18113. }
  18114. // dimIndex may be overflow source data.
  18115. function doGuessOrdinal(
  18116. data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex
  18117. ) {
  18118. var result;
  18119. // Experience value.
  18120. var maxLoop = 5;
  18121. if (isTypedArray(data)) {
  18122. return false;
  18123. }
  18124. // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine
  18125. // always exists in source.
  18126. var dimName;
  18127. if (dimensionsDefine) {
  18128. dimName = dimensionsDefine[dimIndex];
  18129. dimName = isObject$1(dimName) ? dimName.name : dimName;
  18130. }
  18131. if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
  18132. if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
  18133. var sample = data[dimIndex];
  18134. for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {
  18135. if ((result = detectValue(sample[startIndex + i])) != null) {
  18136. return result;
  18137. }
  18138. }
  18139. }
  18140. else {
  18141. for (var i = 0; i < data.length && i < maxLoop; i++) {
  18142. var row = data[startIndex + i];
  18143. if (row && (result = detectValue(row[dimIndex])) != null) {
  18144. return result;
  18145. }
  18146. }
  18147. }
  18148. }
  18149. else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
  18150. if (!dimName) {
  18151. return;
  18152. }
  18153. for (var i = 0; i < data.length && i < maxLoop; i++) {
  18154. var item = data[i];
  18155. if (item && (result = detectValue(item[dimName])) != null) {
  18156. return result;
  18157. }
  18158. }
  18159. }
  18160. else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
  18161. if (!dimName) {
  18162. return;
  18163. }
  18164. var sample = data[dimName];
  18165. if (!sample || isTypedArray(sample)) {
  18166. return false;
  18167. }
  18168. for (var i = 0; i < sample.length && i < maxLoop; i++) {
  18169. if ((result = detectValue(sample[i])) != null) {
  18170. return result;
  18171. }
  18172. }
  18173. }
  18174. else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  18175. for (var i = 0; i < data.length && i < maxLoop; i++) {
  18176. var item = data[i];
  18177. var val = getDataItemValue(item);
  18178. if (!isArray(val)) {
  18179. return false;
  18180. }
  18181. if ((result = detectValue(val[dimIndex])) != null) {
  18182. return result;
  18183. }
  18184. }
  18185. }
  18186. function detectValue(val) {
  18187. // Consider usage convenience, '1', '2' will be treated as "number".
  18188. // `isFinit('')` get `true`.
  18189. if (val != null && isFinite(val) && val !== '') {
  18190. return false;
  18191. }
  18192. else if (isString(val) && val !== '-') {
  18193. return true;
  18194. }
  18195. }
  18196. return false;
  18197. }
  18198. /*
  18199. * Licensed to the Apache Software Foundation (ASF) under one
  18200. * or more contributor license agreements. See the NOTICE file
  18201. * distributed with this work for additional information
  18202. * regarding copyright ownership. The ASF licenses this file
  18203. * to you under the Apache License, Version 2.0 (the
  18204. * "License"); you may not use this file except in compliance
  18205. * with the License. You may obtain a copy of the License at
  18206. *
  18207. * http://www.apache.org/licenses/LICENSE-2.0
  18208. *
  18209. * Unless required by applicable law or agreed to in writing,
  18210. * software distributed under the License is distributed on an
  18211. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18212. * KIND, either express or implied. See the License for the
  18213. * specific language governing permissions and limitations
  18214. * under the License.
  18215. */
  18216. /**
  18217. * ECharts global model
  18218. *
  18219. * @module {echarts/model/Global}
  18220. */
  18221. /**
  18222. * Caution: If the mechanism should be changed some day, these cases
  18223. * should be considered:
  18224. *
  18225. * (1) In `merge option` mode, if using the same option to call `setOption`
  18226. * many times, the result should be the same (try our best to ensure that).
  18227. * (2) In `merge option` mode, if a component has no id/name specified, it
  18228. * will be merged by index, and the result sequence of the components is
  18229. * consistent to the original sequence.
  18230. * (3) `reset` feature (in toolbox). Find detailed info in comments about
  18231. * `mergeOption` in module:echarts/model/OptionManager.
  18232. */
  18233. var OPTION_INNER_KEY = '\0_ec_inner';
  18234. /**
  18235. * @alias module:echarts/model/Global
  18236. *
  18237. * @param {Object} option
  18238. * @param {module:echarts/model/Model} parentModel
  18239. * @param {Object} theme
  18240. */
  18241. var GlobalModel = Model.extend({
  18242. init: function (option, parentModel, theme, optionManager) {
  18243. theme = theme || {};
  18244. this.option = null; // Mark as not initialized.
  18245. /**
  18246. * @type {module:echarts/model/Model}
  18247. * @private
  18248. */
  18249. this._theme = new Model(theme);
  18250. /**
  18251. * @type {module:echarts/model/OptionManager}
  18252. */
  18253. this._optionManager = optionManager;
  18254. },
  18255. setOption: function (option, optionPreprocessorFuncs) {
  18256. assert$1(
  18257. !(OPTION_INNER_KEY in option),
  18258. 'please use chart.getOption()'
  18259. );
  18260. this._optionManager.setOption(option, optionPreprocessorFuncs);
  18261. this.resetOption(null);
  18262. },
  18263. /**
  18264. * @param {string} type null/undefined: reset all.
  18265. * 'recreate': force recreate all.
  18266. * 'timeline': only reset timeline option
  18267. * 'media': only reset media query option
  18268. * @return {boolean} Whether option changed.
  18269. */
  18270. resetOption: function (type) {
  18271. var optionChanged = false;
  18272. var optionManager = this._optionManager;
  18273. if (!type || type === 'recreate') {
  18274. var baseOption = optionManager.mountOption(type === 'recreate');
  18275. if (!this.option || type === 'recreate') {
  18276. initBase.call(this, baseOption);
  18277. }
  18278. else {
  18279. this.restoreData();
  18280. this.mergeOption(baseOption);
  18281. }
  18282. optionChanged = true;
  18283. }
  18284. if (type === 'timeline' || type === 'media') {
  18285. this.restoreData();
  18286. }
  18287. if (!type || type === 'recreate' || type === 'timeline') {
  18288. var timelineOption = optionManager.getTimelineOption(this);
  18289. timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
  18290. }
  18291. if (!type || type === 'recreate' || type === 'media') {
  18292. var mediaOptions = optionManager.getMediaOption(this, this._api);
  18293. if (mediaOptions.length) {
  18294. each$1(mediaOptions, function (mediaOption) {
  18295. this.mergeOption(mediaOption, optionChanged = true);
  18296. }, this);
  18297. }
  18298. }
  18299. return optionChanged;
  18300. },
  18301. /**
  18302. * @protected
  18303. */
  18304. mergeOption: function (newOption) {
  18305. var option = this.option;
  18306. var componentsMap = this._componentsMap;
  18307. var newCptTypes = [];
  18308. resetSourceDefaulter(this);
  18309. // If no component class, merge directly.
  18310. // For example: color, animaiton options, etc.
  18311. each$1(newOption, function (componentOption, mainType) {
  18312. if (componentOption == null) {
  18313. return;
  18314. }
  18315. if (!ComponentModel.hasClass(mainType)) {
  18316. // globalSettingTask.dirty();
  18317. option[mainType] = option[mainType] == null
  18318. ? clone(componentOption)
  18319. : merge(option[mainType], componentOption, true);
  18320. }
  18321. else if (mainType) {
  18322. newCptTypes.push(mainType);
  18323. }
  18324. });
  18325. ComponentModel.topologicalTravel(
  18326. newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
  18327. );
  18328. function visitComponent(mainType, dependencies) {
  18329. var newCptOptionList = normalizeToArray(newOption[mainType]);
  18330. var mapResult = mappingToExists(
  18331. componentsMap.get(mainType), newCptOptionList
  18332. );
  18333. makeIdAndName(mapResult);
  18334. // Set mainType and complete subType.
  18335. each$1(mapResult, function (item, index) {
  18336. var opt = item.option;
  18337. if (isObject$1(opt)) {
  18338. item.keyInfo.mainType = mainType;
  18339. item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
  18340. }
  18341. });
  18342. var dependentModels = getComponentsByTypes(
  18343. componentsMap, dependencies
  18344. );
  18345. option[mainType] = [];
  18346. componentsMap.set(mainType, []);
  18347. each$1(mapResult, function (resultItem, index) {
  18348. var componentModel = resultItem.exist;
  18349. var newCptOption = resultItem.option;
  18350. assert$1(
  18351. isObject$1(newCptOption) || componentModel,
  18352. 'Empty component definition'
  18353. );
  18354. // Consider where is no new option and should be merged using {},
  18355. // see removeEdgeAndAdd in topologicalTravel and
  18356. // ComponentModel.getAllClassMainTypes.
  18357. if (!newCptOption) {
  18358. componentModel.mergeOption({}, this);
  18359. componentModel.optionUpdated({}, false);
  18360. }
  18361. else {
  18362. var ComponentModelClass = ComponentModel.getClass(
  18363. mainType, resultItem.keyInfo.subType, true
  18364. );
  18365. if (componentModel && componentModel instanceof ComponentModelClass) {
  18366. componentModel.name = resultItem.keyInfo.name;
  18367. // componentModel.settingTask && componentModel.settingTask.dirty();
  18368. componentModel.mergeOption(newCptOption, this);
  18369. componentModel.optionUpdated(newCptOption, false);
  18370. }
  18371. else {
  18372. // PENDING Global as parent ?
  18373. var extraOpt = extend(
  18374. {
  18375. dependentModels: dependentModels,
  18376. componentIndex: index
  18377. },
  18378. resultItem.keyInfo
  18379. );
  18380. componentModel = new ComponentModelClass(
  18381. newCptOption, this, this, extraOpt
  18382. );
  18383. extend(componentModel, extraOpt);
  18384. componentModel.init(newCptOption, this, this, extraOpt);
  18385. // Call optionUpdated after init.
  18386. // newCptOption has been used as componentModel.option
  18387. // and may be merged with theme and default, so pass null
  18388. // to avoid confusion.
  18389. componentModel.optionUpdated(null, true);
  18390. }
  18391. }
  18392. componentsMap.get(mainType)[index] = componentModel;
  18393. option[mainType][index] = componentModel.option;
  18394. }, this);
  18395. // Backup series for filtering.
  18396. if (mainType === 'series') {
  18397. createSeriesIndices(this, componentsMap.get('series'));
  18398. }
  18399. }
  18400. this._seriesIndicesMap = createHashMap(
  18401. this._seriesIndices = this._seriesIndices || []
  18402. );
  18403. },
  18404. /**
  18405. * Get option for output (cloned option and inner info removed)
  18406. * @public
  18407. * @return {Object}
  18408. */
  18409. getOption: function () {
  18410. var option = clone(this.option);
  18411. each$1(option, function (opts, mainType) {
  18412. if (ComponentModel.hasClass(mainType)) {
  18413. var opts = normalizeToArray(opts);
  18414. for (var i = opts.length - 1; i >= 0; i--) {
  18415. // Remove options with inner id.
  18416. if (isIdInner(opts[i])) {
  18417. opts.splice(i, 1);
  18418. }
  18419. }
  18420. option[mainType] = opts;
  18421. }
  18422. });
  18423. delete option[OPTION_INNER_KEY];
  18424. return option;
  18425. },
  18426. /**
  18427. * @return {module:echarts/model/Model}
  18428. */
  18429. getTheme: function () {
  18430. return this._theme;
  18431. },
  18432. /**
  18433. * @param {string} mainType
  18434. * @param {number} [idx=0]
  18435. * @return {module:echarts/model/Component}
  18436. */
  18437. getComponent: function (mainType, idx) {
  18438. var list = this._componentsMap.get(mainType);
  18439. if (list) {
  18440. return list[idx || 0];
  18441. }
  18442. },
  18443. /**
  18444. * If none of index and id and name used, return all components with mainType.
  18445. * @param {Object} condition
  18446. * @param {string} condition.mainType
  18447. * @param {string} [condition.subType] If ignore, only query by mainType
  18448. * @param {number|Array.<number>} [condition.index] Either input index or id or name.
  18449. * @param {string|Array.<string>} [condition.id] Either input index or id or name.
  18450. * @param {string|Array.<string>} [condition.name] Either input index or id or name.
  18451. * @return {Array.<module:echarts/model/Component>}
  18452. */
  18453. queryComponents: function (condition) {
  18454. var mainType = condition.mainType;
  18455. if (!mainType) {
  18456. return [];
  18457. }
  18458. var index = condition.index;
  18459. var id = condition.id;
  18460. var name = condition.name;
  18461. var cpts = this._componentsMap.get(mainType);
  18462. if (!cpts || !cpts.length) {
  18463. return [];
  18464. }
  18465. var result;
  18466. if (index != null) {
  18467. if (!isArray(index)) {
  18468. index = [index];
  18469. }
  18470. result = filter(map(index, function (idx) {
  18471. return cpts[idx];
  18472. }), function (val) {
  18473. return !!val;
  18474. });
  18475. }
  18476. else if (id != null) {
  18477. var isIdArray = isArray(id);
  18478. result = filter(cpts, function (cpt) {
  18479. return (isIdArray && indexOf(id, cpt.id) >= 0)
  18480. || (!isIdArray && cpt.id === id);
  18481. });
  18482. }
  18483. else if (name != null) {
  18484. var isNameArray = isArray(name);
  18485. result = filter(cpts, function (cpt) {
  18486. return (isNameArray && indexOf(name, cpt.name) >= 0)
  18487. || (!isNameArray && cpt.name === name);
  18488. });
  18489. }
  18490. else {
  18491. // Return all components with mainType
  18492. result = cpts.slice();
  18493. }
  18494. return filterBySubType(result, condition);
  18495. },
  18496. /**
  18497. * The interface is different from queryComponents,
  18498. * which is convenient for inner usage.
  18499. *
  18500. * @usage
  18501. * var result = findComponents(
  18502. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
  18503. * );
  18504. * var result = findComponents(
  18505. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
  18506. * );
  18507. * var result = findComponents(
  18508. * {mainType: 'series'},
  18509. * function (model, index) {...}
  18510. * );
  18511. * // result like [component0, componnet1, ...]
  18512. *
  18513. * @param {Object} condition
  18514. * @param {string} condition.mainType Mandatory.
  18515. * @param {string} [condition.subType] Optional.
  18516. * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
  18517. * where xxx is mainType.
  18518. * If query attribute is null/undefined or has no index/id/name,
  18519. * do not filtering by query conditions, which is convenient for
  18520. * no-payload situations or when target of action is global.
  18521. * @param {Function} [condition.filter] parameter: component, return boolean.
  18522. * @return {Array.<module:echarts/model/Component>}
  18523. */
  18524. findComponents: function (condition) {
  18525. var query = condition.query;
  18526. var mainType = condition.mainType;
  18527. var queryCond = getQueryCond(query);
  18528. var result = queryCond
  18529. ? this.queryComponents(queryCond)
  18530. : this._componentsMap.get(mainType);
  18531. return doFilter(filterBySubType(result, condition));
  18532. function getQueryCond(q) {
  18533. var indexAttr = mainType + 'Index';
  18534. var idAttr = mainType + 'Id';
  18535. var nameAttr = mainType + 'Name';
  18536. return q && (
  18537. q[indexAttr] != null
  18538. || q[idAttr] != null
  18539. || q[nameAttr] != null
  18540. )
  18541. ? {
  18542. mainType: mainType,
  18543. // subType will be filtered finally.
  18544. index: q[indexAttr],
  18545. id: q[idAttr],
  18546. name: q[nameAttr]
  18547. }
  18548. : null;
  18549. }
  18550. function doFilter(res) {
  18551. return condition.filter
  18552. ? filter(res, condition.filter)
  18553. : res;
  18554. }
  18555. },
  18556. /**
  18557. * @usage
  18558. * eachComponent('legend', function (legendModel, index) {
  18559. * ...
  18560. * });
  18561. * eachComponent(function (componentType, model, index) {
  18562. * // componentType does not include subType
  18563. * // (componentType is 'xxx' but not 'xxx.aa')
  18564. * });
  18565. * eachComponent(
  18566. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
  18567. * function (model, index) {...}
  18568. * );
  18569. * eachComponent(
  18570. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
  18571. * function (model, index) {...}
  18572. * );
  18573. *
  18574. * @param {string|Object=} mainType When mainType is object, the definition
  18575. * is the same as the method 'findComponents'.
  18576. * @param {Function} cb
  18577. * @param {*} context
  18578. */
  18579. eachComponent: function (mainType, cb, context) {
  18580. var componentsMap = this._componentsMap;
  18581. if (typeof mainType === 'function') {
  18582. context = cb;
  18583. cb = mainType;
  18584. componentsMap.each(function (components, componentType) {
  18585. each$1(components, function (component, index) {
  18586. cb.call(context, componentType, component, index);
  18587. });
  18588. });
  18589. }
  18590. else if (isString(mainType)) {
  18591. each$1(componentsMap.get(mainType), cb, context);
  18592. }
  18593. else if (isObject$1(mainType)) {
  18594. var queryResult = this.findComponents(mainType);
  18595. each$1(queryResult, cb, context);
  18596. }
  18597. },
  18598. /**
  18599. * @param {string} name
  18600. * @return {Array.<module:echarts/model/Series>}
  18601. */
  18602. getSeriesByName: function (name) {
  18603. var series = this._componentsMap.get('series');
  18604. return filter(series, function (oneSeries) {
  18605. return oneSeries.name === name;
  18606. });
  18607. },
  18608. /**
  18609. * @param {number} seriesIndex
  18610. * @return {module:echarts/model/Series}
  18611. */
  18612. getSeriesByIndex: function (seriesIndex) {
  18613. return this._componentsMap.get('series')[seriesIndex];
  18614. },
  18615. /**
  18616. * Get series list before filtered by type.
  18617. * FIXME: rename to getRawSeriesByType?
  18618. *
  18619. * @param {string} subType
  18620. * @return {Array.<module:echarts/model/Series>}
  18621. */
  18622. getSeriesByType: function (subType) {
  18623. var series = this._componentsMap.get('series');
  18624. return filter(series, function (oneSeries) {
  18625. return oneSeries.subType === subType;
  18626. });
  18627. },
  18628. /**
  18629. * @return {Array.<module:echarts/model/Series>}
  18630. */
  18631. getSeries: function () {
  18632. return this._componentsMap.get('series').slice();
  18633. },
  18634. /**
  18635. * @return {number}
  18636. */
  18637. getSeriesCount: function () {
  18638. return this._componentsMap.get('series').length;
  18639. },
  18640. /**
  18641. * After filtering, series may be different
  18642. * frome raw series.
  18643. *
  18644. * @param {Function} cb
  18645. * @param {*} context
  18646. */
  18647. eachSeries: function (cb, context) {
  18648. assertSeriesInitialized(this);
  18649. each$1(this._seriesIndices, function (rawSeriesIndex) {
  18650. var series = this._componentsMap.get('series')[rawSeriesIndex];
  18651. cb.call(context, series, rawSeriesIndex);
  18652. }, this);
  18653. },
  18654. /**
  18655. * Iterate raw series before filtered.
  18656. *
  18657. * @param {Function} cb
  18658. * @param {*} context
  18659. */
  18660. eachRawSeries: function (cb, context) {
  18661. each$1(this._componentsMap.get('series'), cb, context);
  18662. },
  18663. /**
  18664. * After filtering, series may be different.
  18665. * frome raw series.
  18666. *
  18667. * @parma {string} subType
  18668. * @param {Function} cb
  18669. * @param {*} context
  18670. */
  18671. eachSeriesByType: function (subType, cb, context) {
  18672. assertSeriesInitialized(this);
  18673. each$1(this._seriesIndices, function (rawSeriesIndex) {
  18674. var series = this._componentsMap.get('series')[rawSeriesIndex];
  18675. if (series.subType === subType) {
  18676. cb.call(context, series, rawSeriesIndex);
  18677. }
  18678. }, this);
  18679. },
  18680. /**
  18681. * Iterate raw series before filtered of given type.
  18682. *
  18683. * @parma {string} subType
  18684. * @param {Function} cb
  18685. * @param {*} context
  18686. */
  18687. eachRawSeriesByType: function (subType, cb, context) {
  18688. return each$1(this.getSeriesByType(subType), cb, context);
  18689. },
  18690. /**
  18691. * @param {module:echarts/model/Series} seriesModel
  18692. */
  18693. isSeriesFiltered: function (seriesModel) {
  18694. assertSeriesInitialized(this);
  18695. return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;
  18696. },
  18697. /**
  18698. * @return {Array.<number>}
  18699. */
  18700. getCurrentSeriesIndices: function () {
  18701. return (this._seriesIndices || []).slice();
  18702. },
  18703. /**
  18704. * @param {Function} cb
  18705. * @param {*} context
  18706. */
  18707. filterSeries: function (cb, context) {
  18708. assertSeriesInitialized(this);
  18709. var filteredSeries = filter(
  18710. this._componentsMap.get('series'), cb, context
  18711. );
  18712. createSeriesIndices(this, filteredSeries);
  18713. },
  18714. restoreData: function (payload) {
  18715. var componentsMap = this._componentsMap;
  18716. createSeriesIndices(this, componentsMap.get('series'));
  18717. var componentTypes = [];
  18718. componentsMap.each(function (components, componentType) {
  18719. componentTypes.push(componentType);
  18720. });
  18721. ComponentModel.topologicalTravel(
  18722. componentTypes,
  18723. ComponentModel.getAllClassMainTypes(),
  18724. function (componentType, dependencies) {
  18725. each$1(componentsMap.get(componentType), function (component) {
  18726. (componentType !== 'series' || !isNotTargetSeries(component, payload))
  18727. && component.restoreData();
  18728. });
  18729. }
  18730. );
  18731. }
  18732. });
  18733. function isNotTargetSeries(seriesModel, payload) {
  18734. if (payload) {
  18735. var index = payload.seiresIndex;
  18736. var id = payload.seriesId;
  18737. var name = payload.seriesName;
  18738. return (index != null && seriesModel.componentIndex !== index)
  18739. || (id != null && seriesModel.id !== id)
  18740. || (name != null && seriesModel.name !== name);
  18741. }
  18742. }
  18743. /**
  18744. * @inner
  18745. */
  18746. function mergeTheme(option, theme) {
  18747. // PENDING
  18748. // NOT use `colorLayer` in theme if option has `color`
  18749. var notMergeColorLayer = option.color && !option.colorLayer;
  18750. each$1(theme, function (themeItem, name) {
  18751. if (name === 'colorLayer' && notMergeColorLayer) {
  18752. return;
  18753. }
  18754. // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
  18755. if (!ComponentModel.hasClass(name)) {
  18756. if (typeof themeItem === 'object') {
  18757. option[name] = !option[name]
  18758. ? clone(themeItem)
  18759. : merge(option[name], themeItem, false);
  18760. }
  18761. else {
  18762. if (option[name] == null) {
  18763. option[name] = themeItem;
  18764. }
  18765. }
  18766. }
  18767. });
  18768. }
  18769. function initBase(baseOption) {
  18770. baseOption = baseOption;
  18771. // Using OPTION_INNER_KEY to mark that this option can not be used outside,
  18772. // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
  18773. this.option = {};
  18774. this.option[OPTION_INNER_KEY] = 1;
  18775. /**
  18776. * Init with series: [], in case of calling findSeries method
  18777. * before series initialized.
  18778. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  18779. * @private
  18780. */
  18781. this._componentsMap = createHashMap({series: []});
  18782. /**
  18783. * Mapping between filtered series list and raw series list.
  18784. * key: filtered series indices, value: raw series indices.
  18785. * @type {Array.<nubmer>}
  18786. * @private
  18787. */
  18788. this._seriesIndices;
  18789. this._seriesIndicesMap;
  18790. mergeTheme(baseOption, this._theme.option);
  18791. // TODO Needs clone when merging to the unexisted property
  18792. merge(baseOption, globalDefault, false);
  18793. this.mergeOption(baseOption);
  18794. }
  18795. /**
  18796. * @inner
  18797. * @param {Array.<string>|string} types model types
  18798. * @return {Object} key: {string} type, value: {Array.<Object>} models
  18799. */
  18800. function getComponentsByTypes(componentsMap, types) {
  18801. if (!isArray(types)) {
  18802. types = types ? [types] : [];
  18803. }
  18804. var ret = {};
  18805. each$1(types, function (type) {
  18806. ret[type] = (componentsMap.get(type) || []).slice();
  18807. });
  18808. return ret;
  18809. }
  18810. /**
  18811. * @inner
  18812. */
  18813. function determineSubType(mainType, newCptOption, existComponent) {
  18814. var subType = newCptOption.type
  18815. ? newCptOption.type
  18816. : existComponent
  18817. ? existComponent.subType
  18818. // Use determineSubType only when there is no existComponent.
  18819. : ComponentModel.determineSubType(mainType, newCptOption);
  18820. // tooltip, markline, markpoint may always has no subType
  18821. return subType;
  18822. }
  18823. /**
  18824. * @inner
  18825. */
  18826. function createSeriesIndices(ecModel, seriesModels) {
  18827. ecModel._seriesIndicesMap = createHashMap(
  18828. ecModel._seriesIndices = map(seriesModels, function (series) {
  18829. return series.componentIndex;
  18830. }) || []
  18831. );
  18832. }
  18833. /**
  18834. * @inner
  18835. */
  18836. function filterBySubType(components, condition) {
  18837. // Using hasOwnProperty for restrict. Consider
  18838. // subType is undefined in user payload.
  18839. return condition.hasOwnProperty('subType')
  18840. ? filter(components, function (cpt) {
  18841. return cpt.subType === condition.subType;
  18842. })
  18843. : components;
  18844. }
  18845. /**
  18846. * @inner
  18847. */
  18848. function assertSeriesInitialized(ecModel) {
  18849. // Components that use _seriesIndices should depends on series component,
  18850. // which make sure that their initialization is after series.
  18851. if (__DEV__) {
  18852. if (!ecModel._seriesIndices) {
  18853. throw new Error('Option should contains series.');
  18854. }
  18855. }
  18856. }
  18857. mixin(GlobalModel, colorPaletteMixin);
  18858. /*
  18859. * Licensed to the Apache Software Foundation (ASF) under one
  18860. * or more contributor license agreements. See the NOTICE file
  18861. * distributed with this work for additional information
  18862. * regarding copyright ownership. The ASF licenses this file
  18863. * to you under the Apache License, Version 2.0 (the
  18864. * "License"); you may not use this file except in compliance
  18865. * with the License. You may obtain a copy of the License at
  18866. *
  18867. * http://www.apache.org/licenses/LICENSE-2.0
  18868. *
  18869. * Unless required by applicable law or agreed to in writing,
  18870. * software distributed under the License is distributed on an
  18871. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18872. * KIND, either express or implied. See the License for the
  18873. * specific language governing permissions and limitations
  18874. * under the License.
  18875. */
  18876. var echartsAPIList = [
  18877. 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
  18878. 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
  18879. 'getViewOfComponentModel', 'getViewOfSeriesModel'
  18880. ];
  18881. // And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
  18882. function ExtensionAPI(chartInstance) {
  18883. each$1(echartsAPIList, function (name) {
  18884. this[name] = bind(chartInstance[name], chartInstance);
  18885. }, this);
  18886. }
  18887. /*
  18888. * Licensed to the Apache Software Foundation (ASF) under one
  18889. * or more contributor license agreements. See the NOTICE file
  18890. * distributed with this work for additional information
  18891. * regarding copyright ownership. The ASF licenses this file
  18892. * to you under the Apache License, Version 2.0 (the
  18893. * "License"); you may not use this file except in compliance
  18894. * with the License. You may obtain a copy of the License at
  18895. *
  18896. * http://www.apache.org/licenses/LICENSE-2.0
  18897. *
  18898. * Unless required by applicable law or agreed to in writing,
  18899. * software distributed under the License is distributed on an
  18900. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18901. * KIND, either express or implied. See the License for the
  18902. * specific language governing permissions and limitations
  18903. * under the License.
  18904. */
  18905. var coordinateSystemCreators = {};
  18906. function CoordinateSystemManager() {
  18907. this._coordinateSystems = [];
  18908. }
  18909. CoordinateSystemManager.prototype = {
  18910. constructor: CoordinateSystemManager,
  18911. create: function (ecModel, api) {
  18912. var coordinateSystems = [];
  18913. each$1(coordinateSystemCreators, function (creater, type) {
  18914. var list = creater.create(ecModel, api);
  18915. coordinateSystems = coordinateSystems.concat(list || []);
  18916. });
  18917. this._coordinateSystems = coordinateSystems;
  18918. },
  18919. update: function (ecModel, api) {
  18920. each$1(this._coordinateSystems, function (coordSys) {
  18921. coordSys.update && coordSys.update(ecModel, api);
  18922. });
  18923. },
  18924. getCoordinateSystems: function () {
  18925. return this._coordinateSystems.slice();
  18926. }
  18927. };
  18928. CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
  18929. coordinateSystemCreators[type] = coordinateSystemCreator;
  18930. };
  18931. CoordinateSystemManager.get = function (type) {
  18932. return coordinateSystemCreators[type];
  18933. };
  18934. /*
  18935. * Licensed to the Apache Software Foundation (ASF) under one
  18936. * or more contributor license agreements. See the NOTICE file
  18937. * distributed with this work for additional information
  18938. * regarding copyright ownership. The ASF licenses this file
  18939. * to you under the Apache License, Version 2.0 (the
  18940. * "License"); you may not use this file except in compliance
  18941. * with the License. You may obtain a copy of the License at
  18942. *
  18943. * http://www.apache.org/licenses/LICENSE-2.0
  18944. *
  18945. * Unless required by applicable law or agreed to in writing,
  18946. * software distributed under the License is distributed on an
  18947. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18948. * KIND, either express or implied. See the License for the
  18949. * specific language governing permissions and limitations
  18950. * under the License.
  18951. */
  18952. /**
  18953. * ECharts option manager
  18954. *
  18955. * @module {echarts/model/OptionManager}
  18956. */
  18957. var each$4 = each$1;
  18958. var clone$3 = clone;
  18959. var map$1 = map;
  18960. var merge$1 = merge;
  18961. var QUERY_REG = /^(min|max)?(.+)$/;
  18962. /**
  18963. * TERM EXPLANATIONS:
  18964. *
  18965. * [option]:
  18966. *
  18967. * An object that contains definitions of components. For example:
  18968. * var option = {
  18969. * title: {...},
  18970. * legend: {...},
  18971. * visualMap: {...},
  18972. * series: [
  18973. * {data: [...]},
  18974. * {data: [...]},
  18975. * ...
  18976. * ]
  18977. * };
  18978. *
  18979. * [rawOption]:
  18980. *
  18981. * An object input to echarts.setOption. 'rawOption' may be an
  18982. * 'option', or may be an object contains multi-options. For example:
  18983. * var option = {
  18984. * baseOption: {
  18985. * title: {...},
  18986. * legend: {...},
  18987. * series: [
  18988. * {data: [...]},
  18989. * {data: [...]},
  18990. * ...
  18991. * ]
  18992. * },
  18993. * timeline: {...},
  18994. * options: [
  18995. * {title: {...}, series: {data: [...]}},
  18996. * {title: {...}, series: {data: [...]}},
  18997. * ...
  18998. * ],
  18999. * media: [
  19000. * {
  19001. * query: {maxWidth: 320},
  19002. * option: {series: {x: 20}, visualMap: {show: false}}
  19003. * },
  19004. * {
  19005. * query: {minWidth: 320, maxWidth: 720},
  19006. * option: {series: {x: 500}, visualMap: {show: true}}
  19007. * },
  19008. * {
  19009. * option: {series: {x: 1200}, visualMap: {show: true}}
  19010. * }
  19011. * ]
  19012. * };
  19013. *
  19014. * @alias module:echarts/model/OptionManager
  19015. * @param {module:echarts/ExtensionAPI} api
  19016. */
  19017. function OptionManager(api) {
  19018. /**
  19019. * @private
  19020. * @type {module:echarts/ExtensionAPI}
  19021. */
  19022. this._api = api;
  19023. /**
  19024. * @private
  19025. * @type {Array.<number>}
  19026. */
  19027. this._timelineOptions = [];
  19028. /**
  19029. * @private
  19030. * @type {Array.<Object>}
  19031. */
  19032. this._mediaList = [];
  19033. /**
  19034. * @private
  19035. * @type {Object}
  19036. */
  19037. this._mediaDefault;
  19038. /**
  19039. * -1, means default.
  19040. * empty means no media.
  19041. * @private
  19042. * @type {Array.<number>}
  19043. */
  19044. this._currentMediaIndices = [];
  19045. /**
  19046. * @private
  19047. * @type {Object}
  19048. */
  19049. this._optionBackup;
  19050. /**
  19051. * @private
  19052. * @type {Object}
  19053. */
  19054. this._newBaseOption;
  19055. }
  19056. // timeline.notMerge is not supported in ec3. Firstly there is rearly
  19057. // case that notMerge is needed. Secondly supporting 'notMerge' requires
  19058. // rawOption cloned and backuped when timeline changed, which does no
  19059. // good to performance. What's more, that both timeline and setOption
  19060. // method supply 'notMerge' brings complex and some problems.
  19061. // Consider this case:
  19062. // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
  19063. // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
  19064. OptionManager.prototype = {
  19065. constructor: OptionManager,
  19066. /**
  19067. * @public
  19068. * @param {Object} rawOption Raw option.
  19069. * @param {module:echarts/model/Global} ecModel
  19070. * @param {Array.<Function>} optionPreprocessorFuncs
  19071. * @return {Object} Init option
  19072. */
  19073. setOption: function (rawOption, optionPreprocessorFuncs) {
  19074. if (rawOption) {
  19075. // That set dat primitive is dangerous if user reuse the data when setOption again.
  19076. each$1(normalizeToArray(rawOption.series), function (series) {
  19077. series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);
  19078. });
  19079. }
  19080. // Caution: some series modify option data, if do not clone,
  19081. // it should ensure that the repeat modify correctly
  19082. // (create a new object when modify itself).
  19083. rawOption = clone$3(rawOption, true);
  19084. // FIXME
  19085. // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
  19086. var oldOptionBackup = this._optionBackup;
  19087. var newParsedOption = parseRawOption.call(
  19088. this, rawOption, optionPreprocessorFuncs, !oldOptionBackup
  19089. );
  19090. this._newBaseOption = newParsedOption.baseOption;
  19091. // For setOption at second time (using merge mode);
  19092. if (oldOptionBackup) {
  19093. // Only baseOption can be merged.
  19094. mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);
  19095. // For simplicity, timeline options and media options do not support merge,
  19096. // that is, if you `setOption` twice and both has timeline options, the latter
  19097. // timeline opitons will not be merged to the formers, but just substitude them.
  19098. if (newParsedOption.timelineOptions.length) {
  19099. oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
  19100. }
  19101. if (newParsedOption.mediaList.length) {
  19102. oldOptionBackup.mediaList = newParsedOption.mediaList;
  19103. }
  19104. if (newParsedOption.mediaDefault) {
  19105. oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
  19106. }
  19107. }
  19108. else {
  19109. this._optionBackup = newParsedOption;
  19110. }
  19111. },
  19112. /**
  19113. * @param {boolean} isRecreate
  19114. * @return {Object}
  19115. */
  19116. mountOption: function (isRecreate) {
  19117. var optionBackup = this._optionBackup;
  19118. // TODO
  19119. // 如果没有reset功能则不clone。
  19120. this._timelineOptions = map$1(optionBackup.timelineOptions, clone$3);
  19121. this._mediaList = map$1(optionBackup.mediaList, clone$3);
  19122. this._mediaDefault = clone$3(optionBackup.mediaDefault);
  19123. this._currentMediaIndices = [];
  19124. return clone$3(isRecreate
  19125. // this._optionBackup.baseOption, which is created at the first `setOption`
  19126. // called, and is merged into every new option by inner method `mergeOption`
  19127. // each time `setOption` called, can be only used in `isRecreate`, because
  19128. // its reliability is under suspicion. In other cases option merge is
  19129. // performed by `model.mergeOption`.
  19130. ? optionBackup.baseOption : this._newBaseOption
  19131. );
  19132. },
  19133. /**
  19134. * @param {module:echarts/model/Global} ecModel
  19135. * @return {Object}
  19136. */
  19137. getTimelineOption: function (ecModel) {
  19138. var option;
  19139. var timelineOptions = this._timelineOptions;
  19140. if (timelineOptions.length) {
  19141. // getTimelineOption can only be called after ecModel inited,
  19142. // so we can get currentIndex from timelineModel.
  19143. var timelineModel = ecModel.getComponent('timeline');
  19144. if (timelineModel) {
  19145. option = clone$3(
  19146. timelineOptions[timelineModel.getCurrentIndex()],
  19147. true
  19148. );
  19149. }
  19150. }
  19151. return option;
  19152. },
  19153. /**
  19154. * @param {module:echarts/model/Global} ecModel
  19155. * @return {Array.<Object>}
  19156. */
  19157. getMediaOption: function (ecModel) {
  19158. var ecWidth = this._api.getWidth();
  19159. var ecHeight = this._api.getHeight();
  19160. var mediaList = this._mediaList;
  19161. var mediaDefault = this._mediaDefault;
  19162. var indices = [];
  19163. var result = [];
  19164. // No media defined.
  19165. if (!mediaList.length && !mediaDefault) {
  19166. return result;
  19167. }
  19168. // Multi media may be applied, the latter defined media has higher priority.
  19169. for (var i = 0, len = mediaList.length; i < len; i++) {
  19170. if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
  19171. indices.push(i);
  19172. }
  19173. }
  19174. // FIXME
  19175. // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。
  19176. if (!indices.length && mediaDefault) {
  19177. indices = [-1];
  19178. }
  19179. if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
  19180. result = map$1(indices, function (index) {
  19181. return clone$3(
  19182. index === -1 ? mediaDefault.option : mediaList[index].option
  19183. );
  19184. });
  19185. }
  19186. // Otherwise return nothing.
  19187. this._currentMediaIndices = indices;
  19188. return result;
  19189. }
  19190. };
  19191. function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
  19192. var timelineOptions = [];
  19193. var mediaList = [];
  19194. var mediaDefault;
  19195. var baseOption;
  19196. // Compatible with ec2.
  19197. var timelineOpt = rawOption.timeline;
  19198. if (rawOption.baseOption) {
  19199. baseOption = rawOption.baseOption;
  19200. }
  19201. // For timeline
  19202. if (timelineOpt || rawOption.options) {
  19203. baseOption = baseOption || {};
  19204. timelineOptions = (rawOption.options || []).slice();
  19205. }
  19206. // For media query
  19207. if (rawOption.media) {
  19208. baseOption = baseOption || {};
  19209. var media = rawOption.media;
  19210. each$4(media, function (singleMedia) {
  19211. if (singleMedia && singleMedia.option) {
  19212. if (singleMedia.query) {
  19213. mediaList.push(singleMedia);
  19214. }
  19215. else if (!mediaDefault) {
  19216. // Use the first media default.
  19217. mediaDefault = singleMedia;
  19218. }
  19219. }
  19220. });
  19221. }
  19222. // For normal option
  19223. if (!baseOption) {
  19224. baseOption = rawOption;
  19225. }
  19226. // Set timelineOpt to baseOption in ec3,
  19227. // which is convenient for merge option.
  19228. if (!baseOption.timeline) {
  19229. baseOption.timeline = timelineOpt;
  19230. }
  19231. // Preprocess.
  19232. each$4([baseOption].concat(timelineOptions)
  19233. .concat(map(mediaList, function (media) {
  19234. return media.option;
  19235. })),
  19236. function (option) {
  19237. each$4(optionPreprocessorFuncs, function (preProcess) {
  19238. preProcess(option, isNew);
  19239. });
  19240. }
  19241. );
  19242. return {
  19243. baseOption: baseOption,
  19244. timelineOptions: timelineOptions,
  19245. mediaDefault: mediaDefault,
  19246. mediaList: mediaList
  19247. };
  19248. }
  19249. /**
  19250. * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
  19251. * Support: width, height, aspectRatio
  19252. * Can use max or min as prefix.
  19253. */
  19254. function applyMediaQuery(query, ecWidth, ecHeight) {
  19255. var realMap = {
  19256. width: ecWidth,
  19257. height: ecHeight,
  19258. aspectratio: ecWidth / ecHeight // lowser case for convenientce.
  19259. };
  19260. var applicatable = true;
  19261. each$1(query, function (value, attr) {
  19262. var matched = attr.match(QUERY_REG);
  19263. if (!matched || !matched[1] || !matched[2]) {
  19264. return;
  19265. }
  19266. var operator = matched[1];
  19267. var realAttr = matched[2].toLowerCase();
  19268. if (!compare(realMap[realAttr], value, operator)) {
  19269. applicatable = false;
  19270. }
  19271. });
  19272. return applicatable;
  19273. }
  19274. function compare(real, expect, operator) {
  19275. if (operator === 'min') {
  19276. return real >= expect;
  19277. }
  19278. else if (operator === 'max') {
  19279. return real <= expect;
  19280. }
  19281. else { // Equals
  19282. return real === expect;
  19283. }
  19284. }
  19285. function indicesEquals(indices1, indices2) {
  19286. // indices is always order by asc and has only finite number.
  19287. return indices1.join(',') === indices2.join(',');
  19288. }
  19289. /**
  19290. * Consider case:
  19291. * `chart.setOption(opt1);`
  19292. * Then user do some interaction like dataZoom, dataView changing.
  19293. * `chart.setOption(opt2);`
  19294. * Then user press 'reset button' in toolbox.
  19295. *
  19296. * After doing that all of the interaction effects should be reset, the
  19297. * chart should be the same as the result of invoke
  19298. * `chart.setOption(opt1); chart.setOption(opt2);`.
  19299. *
  19300. * Although it is not able ensure that
  19301. * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
  19302. * `chart.setOption(merge(opt1, opt2));` exactly,
  19303. * this might be the only simple way to implement that feature.
  19304. *
  19305. * MEMO: We've considered some other approaches:
  19306. * 1. Each model handle its self restoration but not uniform treatment.
  19307. * (Too complex in logic and error-prone)
  19308. * 2. Use a shadow ecModel. (Performace expensive)
  19309. */
  19310. function mergeOption(oldOption, newOption) {
  19311. newOption = newOption || {};
  19312. each$4(newOption, function (newCptOpt, mainType) {
  19313. if (newCptOpt == null) {
  19314. return;
  19315. }
  19316. var oldCptOpt = oldOption[mainType];
  19317. if (!ComponentModel.hasClass(mainType)) {
  19318. oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true);
  19319. }
  19320. else {
  19321. newCptOpt = normalizeToArray(newCptOpt);
  19322. oldCptOpt = normalizeToArray(oldCptOpt);
  19323. var mapResult = mappingToExists(oldCptOpt, newCptOpt);
  19324. oldOption[mainType] = map$1(mapResult, function (item) {
  19325. return (item.option && item.exist)
  19326. ? merge$1(item.exist, item.option, true)
  19327. : (item.exist || item.option);
  19328. });
  19329. }
  19330. });
  19331. }
  19332. /*
  19333. * Licensed to the Apache Software Foundation (ASF) under one
  19334. * or more contributor license agreements. See the NOTICE file
  19335. * distributed with this work for additional information
  19336. * regarding copyright ownership. The ASF licenses this file
  19337. * to you under the Apache License, Version 2.0 (the
  19338. * "License"); you may not use this file except in compliance
  19339. * with the License. You may obtain a copy of the License at
  19340. *
  19341. * http://www.apache.org/licenses/LICENSE-2.0
  19342. *
  19343. * Unless required by applicable law or agreed to in writing,
  19344. * software distributed under the License is distributed on an
  19345. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19346. * KIND, either express or implied. See the License for the
  19347. * specific language governing permissions and limitations
  19348. * under the License.
  19349. */
  19350. var each$5 = each$1;
  19351. var isObject$3 = isObject$1;
  19352. var POSSIBLE_STYLES = [
  19353. 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',
  19354. 'chordStyle', 'label', 'labelLine'
  19355. ];
  19356. function compatEC2ItemStyle(opt) {
  19357. var itemStyleOpt = opt && opt.itemStyle;
  19358. if (!itemStyleOpt) {
  19359. return;
  19360. }
  19361. for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {
  19362. var styleName = POSSIBLE_STYLES[i];
  19363. var normalItemStyleOpt = itemStyleOpt.normal;
  19364. var emphasisItemStyleOpt = itemStyleOpt.emphasis;
  19365. if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
  19366. opt[styleName] = opt[styleName] || {};
  19367. if (!opt[styleName].normal) {
  19368. opt[styleName].normal = normalItemStyleOpt[styleName];
  19369. }
  19370. else {
  19371. merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
  19372. }
  19373. normalItemStyleOpt[styleName] = null;
  19374. }
  19375. if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
  19376. opt[styleName] = opt[styleName] || {};
  19377. if (!opt[styleName].emphasis) {
  19378. opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
  19379. }
  19380. else {
  19381. merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
  19382. }
  19383. emphasisItemStyleOpt[styleName] = null;
  19384. }
  19385. }
  19386. }
  19387. function convertNormalEmphasis(opt, optType, useExtend) {
  19388. if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {
  19389. var normalOpt = opt[optType].normal;
  19390. var emphasisOpt = opt[optType].emphasis;
  19391. if (normalOpt) {
  19392. // Timeline controlStyle has other properties besides normal and emphasis
  19393. if (useExtend) {
  19394. opt[optType].normal = opt[optType].emphasis = null;
  19395. defaults(opt[optType], normalOpt);
  19396. }
  19397. else {
  19398. opt[optType] = normalOpt;
  19399. }
  19400. }
  19401. if (emphasisOpt) {
  19402. opt.emphasis = opt.emphasis || {};
  19403. opt.emphasis[optType] = emphasisOpt;
  19404. }
  19405. }
  19406. }
  19407. function removeEC3NormalStatus(opt) {
  19408. convertNormalEmphasis(opt, 'itemStyle');
  19409. convertNormalEmphasis(opt, 'lineStyle');
  19410. convertNormalEmphasis(opt, 'areaStyle');
  19411. convertNormalEmphasis(opt, 'label');
  19412. convertNormalEmphasis(opt, 'labelLine');
  19413. // treemap
  19414. convertNormalEmphasis(opt, 'upperLabel');
  19415. // graph
  19416. convertNormalEmphasis(opt, 'edgeLabel');
  19417. }
  19418. function compatTextStyle(opt, propName) {
  19419. // Check whether is not object (string\null\undefined ...)
  19420. var labelOptSingle = isObject$3(opt) && opt[propName];
  19421. var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle;
  19422. if (textStyle) {
  19423. for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {
  19424. var propName = TEXT_STYLE_OPTIONS[i];
  19425. if (textStyle.hasOwnProperty(propName)) {
  19426. labelOptSingle[propName] = textStyle[propName];
  19427. }
  19428. }
  19429. }
  19430. }
  19431. function compatEC3CommonStyles(opt) {
  19432. if (opt) {
  19433. removeEC3NormalStatus(opt);
  19434. compatTextStyle(opt, 'label');
  19435. opt.emphasis && compatTextStyle(opt.emphasis, 'label');
  19436. }
  19437. }
  19438. function processSeries(seriesOpt) {
  19439. if (!isObject$3(seriesOpt)) {
  19440. return;
  19441. }
  19442. compatEC2ItemStyle(seriesOpt);
  19443. removeEC3NormalStatus(seriesOpt);
  19444. compatTextStyle(seriesOpt, 'label');
  19445. // treemap
  19446. compatTextStyle(seriesOpt, 'upperLabel');
  19447. // graph
  19448. compatTextStyle(seriesOpt, 'edgeLabel');
  19449. if (seriesOpt.emphasis) {
  19450. compatTextStyle(seriesOpt.emphasis, 'label');
  19451. // treemap
  19452. compatTextStyle(seriesOpt.emphasis, 'upperLabel');
  19453. // graph
  19454. compatTextStyle(seriesOpt.emphasis, 'edgeLabel');
  19455. }
  19456. var markPoint = seriesOpt.markPoint;
  19457. if (markPoint) {
  19458. compatEC2ItemStyle(markPoint);
  19459. compatEC3CommonStyles(markPoint);
  19460. }
  19461. var markLine = seriesOpt.markLine;
  19462. if (markLine) {
  19463. compatEC2ItemStyle(markLine);
  19464. compatEC3CommonStyles(markLine);
  19465. }
  19466. var markArea = seriesOpt.markArea;
  19467. if (markArea) {
  19468. compatEC3CommonStyles(markArea);
  19469. }
  19470. var data = seriesOpt.data;
  19471. // Break with ec3: if `setOption` again, there may be no `type` in option,
  19472. // then the backward compat based on option type will not be performed.
  19473. if (seriesOpt.type === 'graph') {
  19474. data = data || seriesOpt.nodes;
  19475. var edgeData = seriesOpt.links || seriesOpt.edges;
  19476. if (edgeData && !isTypedArray(edgeData)) {
  19477. for (var i = 0; i < edgeData.length; i++) {
  19478. compatEC3CommonStyles(edgeData[i]);
  19479. }
  19480. }
  19481. each$1(seriesOpt.categories, function (opt) {
  19482. removeEC3NormalStatus(opt);
  19483. });
  19484. }
  19485. if (data && !isTypedArray(data)) {
  19486. for (var i = 0; i < data.length; i++) {
  19487. compatEC3CommonStyles(data[i]);
  19488. }
  19489. }
  19490. // mark point data
  19491. var markPoint = seriesOpt.markPoint;
  19492. if (markPoint && markPoint.data) {
  19493. var mpData = markPoint.data;
  19494. for (var i = 0; i < mpData.length; i++) {
  19495. compatEC3CommonStyles(mpData[i]);
  19496. }
  19497. }
  19498. // mark line data
  19499. var markLine = seriesOpt.markLine;
  19500. if (markLine && markLine.data) {
  19501. var mlData = markLine.data;
  19502. for (var i = 0; i < mlData.length; i++) {
  19503. if (isArray(mlData[i])) {
  19504. compatEC3CommonStyles(mlData[i][0]);
  19505. compatEC3CommonStyles(mlData[i][1]);
  19506. }
  19507. else {
  19508. compatEC3CommonStyles(mlData[i]);
  19509. }
  19510. }
  19511. }
  19512. // Series
  19513. if (seriesOpt.type === 'gauge') {
  19514. compatTextStyle(seriesOpt, 'axisLabel');
  19515. compatTextStyle(seriesOpt, 'title');
  19516. compatTextStyle(seriesOpt, 'detail');
  19517. }
  19518. else if (seriesOpt.type === 'treemap') {
  19519. convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');
  19520. each$1(seriesOpt.levels, function (opt) {
  19521. removeEC3NormalStatus(opt);
  19522. });
  19523. }
  19524. else if (seriesOpt.type === 'tree') {
  19525. removeEC3NormalStatus(seriesOpt.leaves);
  19526. }
  19527. // sunburst starts from ec4, so it does not need to compat levels.
  19528. }
  19529. function toArr(o) {
  19530. return isArray(o) ? o : o ? [o] : [];
  19531. }
  19532. function toObj(o) {
  19533. return (isArray(o) ? o[0] : o) || {};
  19534. }
  19535. var compatStyle = function (option, isTheme) {
  19536. each$5(toArr(option.series), function (seriesOpt) {
  19537. isObject$3(seriesOpt) && processSeries(seriesOpt);
  19538. });
  19539. var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];
  19540. isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');
  19541. each$5(
  19542. axes,
  19543. function (axisName) {
  19544. each$5(toArr(option[axisName]), function (axisOpt) {
  19545. if (axisOpt) {
  19546. compatTextStyle(axisOpt, 'axisLabel');
  19547. compatTextStyle(axisOpt.axisPointer, 'label');
  19548. }
  19549. });
  19550. }
  19551. );
  19552. each$5(toArr(option.parallel), function (parallelOpt) {
  19553. var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;
  19554. compatTextStyle(parallelAxisDefault, 'axisLabel');
  19555. compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');
  19556. });
  19557. each$5(toArr(option.calendar), function (calendarOpt) {
  19558. convertNormalEmphasis(calendarOpt, 'itemStyle');
  19559. compatTextStyle(calendarOpt, 'dayLabel');
  19560. compatTextStyle(calendarOpt, 'monthLabel');
  19561. compatTextStyle(calendarOpt, 'yearLabel');
  19562. });
  19563. // radar.name.textStyle
  19564. each$5(toArr(option.radar), function (radarOpt) {
  19565. compatTextStyle(radarOpt, 'name');
  19566. });
  19567. each$5(toArr(option.geo), function (geoOpt) {
  19568. if (isObject$3(geoOpt)) {
  19569. compatEC3CommonStyles(geoOpt);
  19570. each$5(toArr(geoOpt.regions), function (regionObj) {
  19571. compatEC3CommonStyles(regionObj);
  19572. });
  19573. }
  19574. });
  19575. each$5(toArr(option.timeline), function (timelineOpt) {
  19576. compatEC3CommonStyles(timelineOpt);
  19577. convertNormalEmphasis(timelineOpt, 'label');
  19578. convertNormalEmphasis(timelineOpt, 'itemStyle');
  19579. convertNormalEmphasis(timelineOpt, 'controlStyle', true);
  19580. var data = timelineOpt.data;
  19581. isArray(data) && each$1(data, function (item) {
  19582. if (isObject$1(item)) {
  19583. convertNormalEmphasis(item, 'label');
  19584. convertNormalEmphasis(item, 'itemStyle');
  19585. }
  19586. });
  19587. });
  19588. each$5(toArr(option.toolbox), function (toolboxOpt) {
  19589. convertNormalEmphasis(toolboxOpt, 'iconStyle');
  19590. each$5(toolboxOpt.feature, function (featureOpt) {
  19591. convertNormalEmphasis(featureOpt, 'iconStyle');
  19592. });
  19593. });
  19594. compatTextStyle(toObj(option.axisPointer), 'label');
  19595. compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
  19596. };
  19597. /*
  19598. * Licensed to the Apache Software Foundation (ASF) under one
  19599. * or more contributor license agreements. See the NOTICE file
  19600. * distributed with this work for additional information
  19601. * regarding copyright ownership. The ASF licenses this file
  19602. * to you under the Apache License, Version 2.0 (the
  19603. * "License"); you may not use this file except in compliance
  19604. * with the License. You may obtain a copy of the License at
  19605. *
  19606. * http://www.apache.org/licenses/LICENSE-2.0
  19607. *
  19608. * Unless required by applicable law or agreed to in writing,
  19609. * software distributed under the License is distributed on an
  19610. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19611. * KIND, either express or implied. See the License for the
  19612. * specific language governing permissions and limitations
  19613. * under the License.
  19614. */
  19615. // Compatitable with 2.0
  19616. function get(opt, path) {
  19617. path = path.split(',');
  19618. var obj = opt;
  19619. for (var i = 0; i < path.length; i++) {
  19620. obj = obj && obj[path[i]];
  19621. if (obj == null) {
  19622. break;
  19623. }
  19624. }
  19625. return obj;
  19626. }
  19627. function set$1(opt, path, val, overwrite) {
  19628. path = path.split(',');
  19629. var obj = opt;
  19630. var key;
  19631. for (var i = 0; i < path.length - 1; i++) {
  19632. key = path[i];
  19633. if (obj[key] == null) {
  19634. obj[key] = {};
  19635. }
  19636. obj = obj[key];
  19637. }
  19638. if (overwrite || obj[path[i]] == null) {
  19639. obj[path[i]] = val;
  19640. }
  19641. }
  19642. function compatLayoutProperties(option) {
  19643. each$1(LAYOUT_PROPERTIES, function (prop) {
  19644. if (prop[0] in option && !(prop[1] in option)) {
  19645. option[prop[1]] = option[prop[0]];
  19646. }
  19647. });
  19648. }
  19649. var LAYOUT_PROPERTIES = [
  19650. ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']
  19651. ];
  19652. var COMPATITABLE_COMPONENTS = [
  19653. 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'
  19654. ];
  19655. var backwardCompat = function (option, isTheme) {
  19656. compatStyle(option, isTheme);
  19657. // Make sure series array for model initialization.
  19658. option.series = normalizeToArray(option.series);
  19659. each$1(option.series, function (seriesOpt) {
  19660. if (!isObject$1(seriesOpt)) {
  19661. return;
  19662. }
  19663. var seriesType = seriesOpt.type;
  19664. if (seriesType === 'pie' || seriesType === 'gauge') {
  19665. if (seriesOpt.clockWise != null) {
  19666. seriesOpt.clockwise = seriesOpt.clockWise;
  19667. }
  19668. }
  19669. if (seriesType === 'gauge') {
  19670. var pointerColor = get(seriesOpt, 'pointer.color');
  19671. pointerColor != null
  19672. && set$1(seriesOpt, 'itemStyle.normal.color', pointerColor);
  19673. }
  19674. compatLayoutProperties(seriesOpt);
  19675. });
  19676. // dataRange has changed to visualMap
  19677. if (option.dataRange) {
  19678. option.visualMap = option.dataRange;
  19679. }
  19680. each$1(COMPATITABLE_COMPONENTS, function (componentName) {
  19681. var options = option[componentName];
  19682. if (options) {
  19683. if (!isArray(options)) {
  19684. options = [options];
  19685. }
  19686. each$1(options, function (option) {
  19687. compatLayoutProperties(option);
  19688. });
  19689. }
  19690. });
  19691. };
  19692. /*
  19693. * Licensed to the Apache Software Foundation (ASF) under one
  19694. * or more contributor license agreements. See the NOTICE file
  19695. * distributed with this work for additional information
  19696. * regarding copyright ownership. The ASF licenses this file
  19697. * to you under the Apache License, Version 2.0 (the
  19698. * "License"); you may not use this file except in compliance
  19699. * with the License. You may obtain a copy of the License at
  19700. *
  19701. * http://www.apache.org/licenses/LICENSE-2.0
  19702. *
  19703. * Unless required by applicable law or agreed to in writing,
  19704. * software distributed under the License is distributed on an
  19705. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19706. * KIND, either express or implied. See the License for the
  19707. * specific language governing permissions and limitations
  19708. * under the License.
  19709. */
  19710. // (1) [Caution]: the logic is correct based on the premises:
  19711. // data processing stage is blocked in stream.
  19712. // See <module:echarts/stream/Scheduler#performDataProcessorTasks>
  19713. // (2) Only register once when import repeatly.
  19714. // Should be executed before after series filtered and before stack calculation.
  19715. var dataStack = function (ecModel) {
  19716. var stackInfoMap = createHashMap();
  19717. ecModel.eachSeries(function (seriesModel) {
  19718. var stack = seriesModel.get('stack');
  19719. // Compatibal: when `stack` is set as '', do not stack.
  19720. if (stack) {
  19721. var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);
  19722. var data = seriesModel.getData();
  19723. var stackInfo = {
  19724. // Used for calculate axis extent automatically.
  19725. stackResultDimension: data.getCalculationInfo('stackResultDimension'),
  19726. stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),
  19727. stackedDimension: data.getCalculationInfo('stackedDimension'),
  19728. stackedByDimension: data.getCalculationInfo('stackedByDimension'),
  19729. isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),
  19730. data: data,
  19731. seriesModel: seriesModel
  19732. };
  19733. // If stacked on axis that do not support data stack.
  19734. if (!stackInfo.stackedDimension
  19735. || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)
  19736. ) {
  19737. return;
  19738. }
  19739. stackInfoList.length && data.setCalculationInfo(
  19740. 'stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel
  19741. );
  19742. stackInfoList.push(stackInfo);
  19743. }
  19744. });
  19745. stackInfoMap.each(calculateStack);
  19746. };
  19747. function calculateStack(stackInfoList) {
  19748. each$1(stackInfoList, function (targetStackInfo, idxInStack) {
  19749. var resultVal = [];
  19750. var resultNaN = [NaN, NaN];
  19751. var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];
  19752. var targetData = targetStackInfo.data;
  19753. var isStackedByIndex = targetStackInfo.isStackedByIndex;
  19754. // Should not write on raw data, because stack series model list changes
  19755. // depending on legend selection.
  19756. var newData = targetData.map(dims, function (v0, v1, dataIndex) {
  19757. var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex);
  19758. // Consider `connectNulls` of line area, if value is NaN, stackedOver
  19759. // should also be NaN, to draw a appropriate belt area.
  19760. if (isNaN(sum)) {
  19761. return resultNaN;
  19762. }
  19763. var byValue;
  19764. var stackedDataRawIndex;
  19765. if (isStackedByIndex) {
  19766. stackedDataRawIndex = targetData.getRawIndex(dataIndex);
  19767. }
  19768. else {
  19769. byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);
  19770. }
  19771. // If stackOver is NaN, chart view will render point on value start.
  19772. var stackedOver = NaN;
  19773. for (var j = idxInStack - 1; j >= 0; j--) {
  19774. var stackInfo = stackInfoList[j];
  19775. // Has been optimized by inverted indices on `stackedByDimension`.
  19776. if (!isStackedByIndex) {
  19777. stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);
  19778. }
  19779. if (stackedDataRawIndex >= 0) {
  19780. var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex);
  19781. // Considering positive stack, negative stack and empty data
  19782. if ((sum >= 0 && val > 0) // Positive stack
  19783. || (sum <= 0 && val < 0) // Negative stack
  19784. ) {
  19785. sum += val;
  19786. stackedOver = val;
  19787. break;
  19788. }
  19789. }
  19790. }
  19791. resultVal[0] = sum;
  19792. resultVal[1] = stackedOver;
  19793. return resultVal;
  19794. });
  19795. targetData.hostModel.setData(newData);
  19796. // Update for consequent calculation
  19797. targetStackInfo.data = newData;
  19798. });
  19799. }
  19800. /*
  19801. * Licensed to the Apache Software Foundation (ASF) under one
  19802. * or more contributor license agreements. See the NOTICE file
  19803. * distributed with this work for additional information
  19804. * regarding copyright ownership. The ASF licenses this file
  19805. * to you under the Apache License, Version 2.0 (the
  19806. * "License"); you may not use this file except in compliance
  19807. * with the License. You may obtain a copy of the License at
  19808. *
  19809. * http://www.apache.org/licenses/LICENSE-2.0
  19810. *
  19811. * Unless required by applicable law or agreed to in writing,
  19812. * software distributed under the License is distributed on an
  19813. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19814. * KIND, either express or implied. See the License for the
  19815. * specific language governing permissions and limitations
  19816. * under the License.
  19817. */
  19818. // TODO
  19819. // ??? refactor? check the outer usage of data provider.
  19820. // merge with defaultDimValueGetter?
  19821. /**
  19822. * If normal array used, mutable chunk size is supported.
  19823. * If typed array used, chunk size must be fixed.
  19824. */
  19825. function DefaultDataProvider(source, dimSize) {
  19826. if (!Source.isInstance(source)) {
  19827. source = Source.seriesDataToSource(source);
  19828. }
  19829. this._source = source;
  19830. var data = this._data = source.data;
  19831. var sourceFormat = source.sourceFormat;
  19832. // Typed array. TODO IE10+?
  19833. if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
  19834. if (__DEV__) {
  19835. if (dimSize == null) {
  19836. throw new Error('Typed array data must specify dimension size');
  19837. }
  19838. }
  19839. this._offset = 0;
  19840. this._dimSize = dimSize;
  19841. this._data = data;
  19842. }
  19843. var methods = providerMethods[
  19844. sourceFormat === SOURCE_FORMAT_ARRAY_ROWS
  19845. ? sourceFormat + '_' + source.seriesLayoutBy
  19846. : sourceFormat
  19847. ];
  19848. if (__DEV__) {
  19849. assert$1(methods, 'Invalide sourceFormat: ' + sourceFormat);
  19850. }
  19851. extend(this, methods);
  19852. }
  19853. var providerProto = DefaultDataProvider.prototype;
  19854. // If data is pure without style configuration
  19855. providerProto.pure = false;
  19856. // If data is persistent and will not be released after use.
  19857. providerProto.persistent = true;
  19858. // ???! FIXME legacy data provider do not has method getSource
  19859. providerProto.getSource = function () {
  19860. return this._source;
  19861. };
  19862. var providerMethods = {
  19863. 'arrayRows_column': {
  19864. pure: true,
  19865. count: function () {
  19866. return Math.max(0, this._data.length - this._source.startIndex);
  19867. },
  19868. getItem: function (idx) {
  19869. return this._data[idx + this._source.startIndex];
  19870. },
  19871. appendData: appendDataSimply
  19872. },
  19873. 'arrayRows_row': {
  19874. pure: true,
  19875. count: function () {
  19876. var row = this._data[0];
  19877. return row ? Math.max(0, row.length - this._source.startIndex) : 0;
  19878. },
  19879. getItem: function (idx) {
  19880. idx += this._source.startIndex;
  19881. var item = [];
  19882. var data = this._data;
  19883. for (var i = 0; i < data.length; i++) {
  19884. var row = data[i];
  19885. item.push(row ? row[idx] : null);
  19886. }
  19887. return item;
  19888. },
  19889. appendData: function () {
  19890. throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
  19891. }
  19892. },
  19893. 'objectRows': {
  19894. pure: true,
  19895. count: countSimply,
  19896. getItem: getItemSimply,
  19897. appendData: appendDataSimply
  19898. },
  19899. 'keyedColumns': {
  19900. pure: true,
  19901. count: function () {
  19902. var dimName = this._source.dimensionsDefine[0].name;
  19903. var col = this._data[dimName];
  19904. return col ? col.length : 0;
  19905. },
  19906. getItem: function (idx) {
  19907. var item = [];
  19908. var dims = this._source.dimensionsDefine;
  19909. for (var i = 0; i < dims.length; i++) {
  19910. var col = this._data[dims[i].name];
  19911. item.push(col ? col[idx] : null);
  19912. }
  19913. return item;
  19914. },
  19915. appendData: function (newData) {
  19916. var data = this._data;
  19917. each$1(newData, function (newCol, key) {
  19918. var oldCol = data[key] || (data[key] = []);
  19919. for (var i = 0; i < (newCol || []).length; i++) {
  19920. oldCol.push(newCol[i]);
  19921. }
  19922. });
  19923. }
  19924. },
  19925. 'original': {
  19926. count: countSimply,
  19927. getItem: getItemSimply,
  19928. appendData: appendDataSimply
  19929. },
  19930. 'typedArray': {
  19931. persistent: false,
  19932. pure: true,
  19933. count: function () {
  19934. return this._data ? (this._data.length / this._dimSize) : 0;
  19935. },
  19936. getItem: function (idx, out) {
  19937. idx = idx - this._offset;
  19938. out = out || [];
  19939. var offset = this._dimSize * idx;
  19940. for (var i = 0; i < this._dimSize; i++) {
  19941. out[i] = this._data[offset + i];
  19942. }
  19943. return out;
  19944. },
  19945. appendData: function (newData) {
  19946. if (__DEV__) {
  19947. assert$1(
  19948. isTypedArray(newData),
  19949. 'Added data must be TypedArray if data in initialization is TypedArray'
  19950. );
  19951. }
  19952. this._data = newData;
  19953. },
  19954. // Clean self if data is already used.
  19955. clean: function () {
  19956. // PENDING
  19957. this._offset += this.count();
  19958. this._data = null;
  19959. }
  19960. }
  19961. };
  19962. function countSimply() {
  19963. return this._data.length;
  19964. }
  19965. function getItemSimply(idx) {
  19966. return this._data[idx];
  19967. }
  19968. function appendDataSimply(newData) {
  19969. for (var i = 0; i < newData.length; i++) {
  19970. this._data.push(newData[i]);
  19971. }
  19972. }
  19973. var rawValueGetters = {
  19974. arrayRows: getRawValueSimply,
  19975. objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
  19976. return dimIndex != null ? dataItem[dimName] : dataItem;
  19977. },
  19978. keyedColumns: getRawValueSimply,
  19979. original: function (dataItem, dataIndex, dimIndex, dimName) {
  19980. // FIXME
  19981. // In some case (markpoint in geo (geo-map.html)), dataItem
  19982. // is {coord: [...]}
  19983. var value = getDataItemValue(dataItem);
  19984. return (dimIndex == null || !(value instanceof Array))
  19985. ? value
  19986. : value[dimIndex];
  19987. },
  19988. typedArray: getRawValueSimply
  19989. };
  19990. function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
  19991. return dimIndex != null ? dataItem[dimIndex] : dataItem;
  19992. }
  19993. var defaultDimValueGetters = {
  19994. arrayRows: getDimValueSimply,
  19995. objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
  19996. return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
  19997. },
  19998. keyedColumns: getDimValueSimply,
  19999. original: function (dataItem, dimName, dataIndex, dimIndex) {
  20000. // Performance sensitive, do not use modelUtil.getDataItemValue.
  20001. // If dataItem is an plain object with no value field, the var `value`
  20002. // will be assigned with the object, but it will be tread correctly
  20003. // in the `convertDataValue`.
  20004. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  20005. // If any dataItem is like { value: 10 }
  20006. if (!this._rawData.pure && isDataItemOption(dataItem)) {
  20007. this.hasItemOption = true;
  20008. }
  20009. return converDataValue(
  20010. (value instanceof Array)
  20011. ? value[dimIndex]
  20012. // If value is a single number or something else not array.
  20013. : value,
  20014. this._dimensionInfos[dimName]
  20015. );
  20016. },
  20017. typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
  20018. return dataItem[dimIndex];
  20019. }
  20020. };
  20021. function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
  20022. return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
  20023. }
  20024. /**
  20025. * This helper method convert value in data.
  20026. * @param {string|number|Date} value
  20027. * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
  20028. * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
  20029. */
  20030. function converDataValue(value, dimInfo) {
  20031. // Performance sensitive.
  20032. var dimType = dimInfo && dimInfo.type;
  20033. if (dimType === 'ordinal') {
  20034. // If given value is a category string
  20035. var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
  20036. return ordinalMeta
  20037. ? ordinalMeta.parseAndCollect(value)
  20038. : value;
  20039. }
  20040. if (dimType === 'time'
  20041. // spead up when using timestamp
  20042. && typeof value !== 'number'
  20043. && value != null
  20044. && value !== '-'
  20045. ) {
  20046. value = +parseDate(value);
  20047. }
  20048. // dimType defaults 'number'.
  20049. // If dimType is not ordinal and value is null or undefined or NaN or '-',
  20050. // parse to NaN.
  20051. return (value == null || value === '')
  20052. ? NaN
  20053. // If string (like '-'), using '+' parse to NaN
  20054. // If object, also parse to NaN
  20055. : +value;
  20056. }
  20057. // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
  20058. // Consider persistent.
  20059. // Caution: why use raw value to display on label or tooltip?
  20060. // A reason is to avoid format. For example time value we do not know
  20061. // how to format is expected. More over, if stack is used, calculated
  20062. // value may be 0.91000000001, which have brings trouble to display.
  20063. // TODO: consider how to treat null/undefined/NaN when display?
  20064. /**
  20065. * @param {module:echarts/data/List} data
  20066. * @param {number} dataIndex
  20067. * @param {string|number} [dim] dimName or dimIndex
  20068. * @return {Array.<number>|string|number} can be null/undefined.
  20069. */
  20070. function retrieveRawValue(data, dataIndex, dim) {
  20071. if (!data) {
  20072. return;
  20073. }
  20074. // Consider data may be not persistent.
  20075. var dataItem = data.getRawDataItem(dataIndex);
  20076. if (dataItem == null) {
  20077. return;
  20078. }
  20079. var sourceFormat = data.getProvider().getSource().sourceFormat;
  20080. var dimName;
  20081. var dimIndex;
  20082. var dimInfo = data.getDimensionInfo(dim);
  20083. if (dimInfo) {
  20084. dimName = dimInfo.name;
  20085. dimIndex = dimInfo.index;
  20086. }
  20087. return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName);
  20088. }
  20089. /**
  20090. * Compatible with some cases (in pie, map) like:
  20091. * data: [{name: 'xx', value: 5, selected: true}, ...]
  20092. * where only sourceFormat is 'original' and 'objectRows' supported.
  20093. *
  20094. * ??? TODO
  20095. * Supported detail options in data item when using 'arrayRows'.
  20096. *
  20097. * @param {module:echarts/data/List} data
  20098. * @param {number} dataIndex
  20099. * @param {string} attr like 'selected'
  20100. */
  20101. function retrieveRawAttr(data, dataIndex, attr) {
  20102. if (!data) {
  20103. return;
  20104. }
  20105. var sourceFormat = data.getProvider().getSource().sourceFormat;
  20106. if (sourceFormat !== SOURCE_FORMAT_ORIGINAL
  20107. && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
  20108. ) {
  20109. return;
  20110. }
  20111. var dataItem = data.getRawDataItem(dataIndex);
  20112. if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject$1(dataItem)) {
  20113. dataItem = null;
  20114. }
  20115. if (dataItem) {
  20116. return dataItem[attr];
  20117. }
  20118. }
  20119. /*
  20120. * Licensed to the Apache Software Foundation (ASF) under one
  20121. * or more contributor license agreements. See the NOTICE file
  20122. * distributed with this work for additional information
  20123. * regarding copyright ownership. The ASF licenses this file
  20124. * to you under the Apache License, Version 2.0 (the
  20125. * "License"); you may not use this file except in compliance
  20126. * with the License. You may obtain a copy of the License at
  20127. *
  20128. * http://www.apache.org/licenses/LICENSE-2.0
  20129. *
  20130. * Unless required by applicable law or agreed to in writing,
  20131. * software distributed under the License is distributed on an
  20132. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20133. * KIND, either express or implied. See the License for the
  20134. * specific language governing permissions and limitations
  20135. * under the License.
  20136. */
  20137. var DIMENSION_LABEL_REG = /\{@(.+?)\}/g;
  20138. // PENDING A little ugly
  20139. var dataFormatMixin = {
  20140. /**
  20141. * Get params for formatter
  20142. * @param {number} dataIndex
  20143. * @param {string} [dataType]
  20144. * @return {Object}
  20145. */
  20146. getDataParams: function (dataIndex, dataType) {
  20147. var data = this.getData(dataType);
  20148. var rawValue = this.getRawValue(dataIndex, dataType);
  20149. var rawDataIndex = data.getRawIndex(dataIndex);
  20150. var name = data.getName(dataIndex);
  20151. var itemOpt = data.getRawDataItem(dataIndex);
  20152. var color = data.getItemVisual(dataIndex, 'color');
  20153. var tooltipModel = this.ecModel.getComponent('tooltip');
  20154. var renderModeOption = tooltipModel && tooltipModel.get('renderMode');
  20155. var renderMode = getTooltipRenderMode(renderModeOption);
  20156. var mainType = this.mainType;
  20157. var isSeries = mainType === 'series';
  20158. return {
  20159. componentType: mainType,
  20160. componentSubType: this.subType,
  20161. componentIndex: this.componentIndex,
  20162. seriesType: isSeries ? this.subType : null,
  20163. seriesIndex: this.seriesIndex,
  20164. seriesId: isSeries ? this.id : null,
  20165. seriesName: isSeries ? this.name : null,
  20166. name: name,
  20167. dataIndex: rawDataIndex,
  20168. data: itemOpt,
  20169. dataType: dataType,
  20170. value: rawValue,
  20171. color: color,
  20172. marker: getTooltipMarker({
  20173. color: color,
  20174. renderMode: renderMode
  20175. }),
  20176. // Param name list for mapping `a`, `b`, `c`, `d`, `e`
  20177. $vars: ['seriesName', 'name', 'value']
  20178. };
  20179. },
  20180. /**
  20181. * Format label
  20182. * @param {number} dataIndex
  20183. * @param {string} [status='normal'] 'normal' or 'emphasis'
  20184. * @param {string} [dataType]
  20185. * @param {number} [dimIndex]
  20186. * @param {string} [labelProp='label']
  20187. * @return {string} If not formatter, return null/undefined
  20188. */
  20189. getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {
  20190. status = status || 'normal';
  20191. var data = this.getData(dataType);
  20192. var itemModel = data.getItemModel(dataIndex);
  20193. var params = this.getDataParams(dataIndex, dataType);
  20194. if (dimIndex != null && (params.value instanceof Array)) {
  20195. params.value = params.value[dimIndex];
  20196. }
  20197. var formatter = itemModel.get(
  20198. status === 'normal'
  20199. ? [labelProp || 'label', 'formatter']
  20200. : [status, labelProp || 'label', 'formatter']
  20201. );
  20202. if (typeof formatter === 'function') {
  20203. params.status = status;
  20204. return formatter(params);
  20205. }
  20206. else if (typeof formatter === 'string') {
  20207. var str = formatTpl(formatter, params);
  20208. // Support 'aaa{@[3]}bbb{@product}ccc'.
  20209. // Do not support '}' in dim name util have to.
  20210. return str.replace(DIMENSION_LABEL_REG, function (origin, dim) {
  20211. var len = dim.length;
  20212. if (dim.charAt(0) === '[' && dim.charAt(len - 1) === ']') {
  20213. dim = +dim.slice(1, len - 1); // Also: '[]' => 0
  20214. }
  20215. return retrieveRawValue(data, dataIndex, dim);
  20216. });
  20217. }
  20218. },
  20219. /**
  20220. * Get raw value in option
  20221. * @param {number} idx
  20222. * @param {string} [dataType]
  20223. * @return {Array|number|string}
  20224. */
  20225. getRawValue: function (idx, dataType) {
  20226. return retrieveRawValue(this.getData(dataType), idx);
  20227. },
  20228. /**
  20229. * Should be implemented.
  20230. * @param {number} dataIndex
  20231. * @param {boolean} [multipleSeries=false]
  20232. * @param {number} [dataType]
  20233. * @return {string} tooltip string
  20234. */
  20235. formatTooltip: function () {
  20236. // Empty function
  20237. }
  20238. };
  20239. /*
  20240. * Licensed to the Apache Software Foundation (ASF) under one
  20241. * or more contributor license agreements. See the NOTICE file
  20242. * distributed with this work for additional information
  20243. * regarding copyright ownership. The ASF licenses this file
  20244. * to you under the Apache License, Version 2.0 (the
  20245. * "License"); you may not use this file except in compliance
  20246. * with the License. You may obtain a copy of the License at
  20247. *
  20248. * http://www.apache.org/licenses/LICENSE-2.0
  20249. *
  20250. * Unless required by applicable law or agreed to in writing,
  20251. * software distributed under the License is distributed on an
  20252. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20253. * KIND, either express or implied. See the License for the
  20254. * specific language governing permissions and limitations
  20255. * under the License.
  20256. */
  20257. /**
  20258. * @param {Object} define
  20259. * @return See the return of `createTask`.
  20260. */
  20261. function createTask(define) {
  20262. return new Task(define);
  20263. }
  20264. /**
  20265. * @constructor
  20266. * @param {Object} define
  20267. * @param {Function} define.reset Custom reset
  20268. * @param {Function} [define.plan] Returns 'reset' indicate reset immediately.
  20269. * @param {Function} [define.count] count is used to determin data task.
  20270. * @param {Function} [define.onDirty] count is used to determin data task.
  20271. */
  20272. function Task(define) {
  20273. define = define || {};
  20274. this._reset = define.reset;
  20275. this._plan = define.plan;
  20276. this._count = define.count;
  20277. this._onDirty = define.onDirty;
  20278. this._dirty = true;
  20279. // Context must be specified implicitly, to
  20280. // avoid miss update context when model changed.
  20281. this.context;
  20282. }
  20283. var taskProto = Task.prototype;
  20284. /**
  20285. * @param {Object} performArgs
  20286. * @param {number} [performArgs.step] Specified step.
  20287. * @param {number} [performArgs.skip] Skip customer perform call.
  20288. * @param {number} [performArgs.modBy] Sampling window size.
  20289. * @param {number} [performArgs.modDataCount] Sampling count.
  20290. */
  20291. taskProto.perform = function (performArgs) {
  20292. var upTask = this._upstream;
  20293. var skip = performArgs && performArgs.skip;
  20294. // TODO some refactor.
  20295. // Pull data. Must pull data each time, because context.data
  20296. // may be updated by Series.setData.
  20297. if (this._dirty && upTask) {
  20298. var context = this.context;
  20299. context.data = context.outputData = upTask.context.outputData;
  20300. }
  20301. if (this.__pipeline) {
  20302. this.__pipeline.currentTask = this;
  20303. }
  20304. var planResult;
  20305. if (this._plan && !skip) {
  20306. planResult = this._plan(this.context);
  20307. }
  20308. // Support sharding by mod, which changes the render sequence and makes the rendered graphic
  20309. // elements uniformed distributed when progress, especially when moving or zooming.
  20310. var lastModBy = normalizeModBy(this._modBy);
  20311. var lastModDataCount = this._modDataCount || 0;
  20312. var modBy = normalizeModBy(performArgs && performArgs.modBy);
  20313. var modDataCount = performArgs && performArgs.modDataCount || 0;
  20314. if (lastModBy !== modBy || lastModDataCount !== modDataCount) {
  20315. planResult = 'reset';
  20316. }
  20317. function normalizeModBy(val) {
  20318. !(val >= 1) && (val = 1); // jshint ignore:line
  20319. return val;
  20320. }
  20321. var forceFirstProgress;
  20322. if (this._dirty || planResult === 'reset') {
  20323. this._dirty = false;
  20324. forceFirstProgress = reset(this, skip);
  20325. }
  20326. this._modBy = modBy;
  20327. this._modDataCount = modDataCount;
  20328. var step = performArgs && performArgs.step;
  20329. if (upTask) {
  20330. if (__DEV__) {
  20331. assert$1(upTask._outputDueEnd != null);
  20332. }
  20333. this._dueEnd = upTask._outputDueEnd;
  20334. }
  20335. // DataTask or overallTask
  20336. else {
  20337. if (__DEV__) {
  20338. assert$1(!this._progress || this._count);
  20339. }
  20340. this._dueEnd = this._count ? this._count(this.context) : Infinity;
  20341. }
  20342. // Note: Stubs, that its host overall task let it has progress, has progress.
  20343. // If no progress, pass index from upstream to downstream each time plan called.
  20344. if (this._progress) {
  20345. var start = this._dueIndex;
  20346. var end = Math.min(
  20347. step != null ? this._dueIndex + step : Infinity,
  20348. this._dueEnd
  20349. );
  20350. if (!skip && (forceFirstProgress || start < end)) {
  20351. var progress = this._progress;
  20352. if (isArray(progress)) {
  20353. for (var i = 0; i < progress.length; i++) {
  20354. doProgress(this, progress[i], start, end, modBy, modDataCount);
  20355. }
  20356. }
  20357. else {
  20358. doProgress(this, progress, start, end, modBy, modDataCount);
  20359. }
  20360. }
  20361. this._dueIndex = end;
  20362. // If no `outputDueEnd`, assume that output data and
  20363. // input data is the same, so use `dueIndex` as `outputDueEnd`.
  20364. var outputDueEnd = this._settedOutputEnd != null
  20365. ? this._settedOutputEnd : end;
  20366. if (__DEV__) {
  20367. // ??? Can not rollback.
  20368. assert$1(outputDueEnd >= this._outputDueEnd);
  20369. }
  20370. this._outputDueEnd = outputDueEnd;
  20371. }
  20372. else {
  20373. // (1) Some overall task has no progress.
  20374. // (2) Stubs, that its host overall task do not let it has progress, has no progress.
  20375. // This should always be performed so it can be passed to downstream.
  20376. this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null
  20377. ? this._settedOutputEnd : this._dueEnd;
  20378. }
  20379. return this.unfinished();
  20380. };
  20381. var iterator = (function () {
  20382. var end;
  20383. var current;
  20384. var modBy;
  20385. var modDataCount;
  20386. var winCount;
  20387. var it = {
  20388. reset: function (s, e, sStep, sCount) {
  20389. current = s;
  20390. end = e;
  20391. modBy = sStep;
  20392. modDataCount = sCount;
  20393. winCount = Math.ceil(modDataCount / modBy);
  20394. it.next = (modBy > 1 && modDataCount > 0) ? modNext : sequentialNext;
  20395. }
  20396. };
  20397. return it;
  20398. function sequentialNext() {
  20399. return current < end ? current++ : null;
  20400. }
  20401. function modNext() {
  20402. var dataIndex = (current % winCount) * modBy + Math.ceil(current / winCount);
  20403. var result = current >= end
  20404. ? null
  20405. : dataIndex < modDataCount
  20406. ? dataIndex
  20407. // If modDataCount is smaller than data.count() (consider `appendData` case),
  20408. // Use normal linear rendering mode.
  20409. : current;
  20410. current++;
  20411. return result;
  20412. }
  20413. })();
  20414. taskProto.dirty = function () {
  20415. this._dirty = true;
  20416. this._onDirty && this._onDirty(this.context);
  20417. };
  20418. function doProgress(taskIns, progress, start, end, modBy, modDataCount) {
  20419. iterator.reset(start, end, modBy, modDataCount);
  20420. taskIns._callingProgress = progress;
  20421. taskIns._callingProgress({
  20422. start: start, end: end, count: end - start, next: iterator.next
  20423. }, taskIns.context);
  20424. }
  20425. function reset(taskIns, skip) {
  20426. taskIns._dueIndex = taskIns._outputDueEnd = taskIns._dueEnd = 0;
  20427. taskIns._settedOutputEnd = null;
  20428. var progress;
  20429. var forceFirstProgress;
  20430. if (!skip && taskIns._reset) {
  20431. progress = taskIns._reset(taskIns.context);
  20432. if (progress && progress.progress) {
  20433. forceFirstProgress = progress.forceFirstProgress;
  20434. progress = progress.progress;
  20435. }
  20436. // To simplify no progress checking, array must has item.
  20437. if (isArray(progress) && !progress.length) {
  20438. progress = null;
  20439. }
  20440. }
  20441. taskIns._progress = progress;
  20442. taskIns._modBy = taskIns._modDataCount = null;
  20443. var downstream = taskIns._downstream;
  20444. downstream && downstream.dirty();
  20445. return forceFirstProgress;
  20446. }
  20447. /**
  20448. * @return {boolean}
  20449. */
  20450. taskProto.unfinished = function () {
  20451. return this._progress && this._dueIndex < this._dueEnd;
  20452. };
  20453. /**
  20454. * @param {Object} downTask The downstream task.
  20455. * @return {Object} The downstream task.
  20456. */
  20457. taskProto.pipe = function (downTask) {
  20458. if (__DEV__) {
  20459. assert$1(downTask && !downTask._disposed && downTask !== this);
  20460. }
  20461. // If already downstream, do not dirty downTask.
  20462. if (this._downstream !== downTask || this._dirty) {
  20463. this._downstream = downTask;
  20464. downTask._upstream = this;
  20465. downTask.dirty();
  20466. }
  20467. };
  20468. taskProto.dispose = function () {
  20469. if (this._disposed) {
  20470. return;
  20471. }
  20472. this._upstream && (this._upstream._downstream = null);
  20473. this._downstream && (this._downstream._upstream = null);
  20474. this._dirty = false;
  20475. this._disposed = true;
  20476. };
  20477. taskProto.getUpstream = function () {
  20478. return this._upstream;
  20479. };
  20480. taskProto.getDownstream = function () {
  20481. return this._downstream;
  20482. };
  20483. taskProto.setOutputEnd = function (end) {
  20484. // This only happend in dataTask, dataZoom, map, currently.
  20485. // where dataZoom do not set end each time, but only set
  20486. // when reset. So we should record the setted end, in case
  20487. // that the stub of dataZoom perform again and earse the
  20488. // setted end by upstream.
  20489. this._outputDueEnd = this._settedOutputEnd = end;
  20490. };
  20491. ///////////////////////////////////////////////////////////
  20492. // For stream debug (Should be commented out after used!)
  20493. // Usage: printTask(this, 'begin');
  20494. // Usage: printTask(this, null, {someExtraProp});
  20495. // function printTask(task, prefix, extra) {
  20496. // window.ecTaskUID == null && (window.ecTaskUID = 0);
  20497. // task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);
  20498. // task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);
  20499. // var props = [];
  20500. // if (task.__pipeline) {
  20501. // var val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;
  20502. // props.push({text: 'idx', value: val});
  20503. // } else {
  20504. // var stubCount = 0;
  20505. // task.agentStubMap.each(() => stubCount++);
  20506. // props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});
  20507. // }
  20508. // props.push({text: 'uid', value: task.uidDebug});
  20509. // if (task.__pipeline) {
  20510. // props.push({text: 'pid', value: task.__pipeline.id});
  20511. // task.agent && props.push(
  20512. // {text: 'stubFor', value: task.agent.uidDebug}
  20513. // );
  20514. // }
  20515. // props.push(
  20516. // {text: 'dirty', value: task._dirty},
  20517. // {text: 'dueIndex', value: task._dueIndex},
  20518. // {text: 'dueEnd', value: task._dueEnd},
  20519. // {text: 'outputDueEnd', value: task._outputDueEnd}
  20520. // );
  20521. // if (extra) {
  20522. // Object.keys(extra).forEach(key => {
  20523. // props.push({text: key, value: extra[key]});
  20524. // });
  20525. // }
  20526. // var args = ['color: blue'];
  20527. // var msg = `%c[${prefix || 'T'}] %c` + props.map(item => (
  20528. // args.push('color: black', 'color: red'),
  20529. // `${item.text}: %c${item.value}`
  20530. // )).join('%c, ');
  20531. // console.log.apply(console, [msg].concat(args));
  20532. // // console.log(this);
  20533. // }
  20534. /*
  20535. * Licensed to the Apache Software Foundation (ASF) under one
  20536. * or more contributor license agreements. See the NOTICE file
  20537. * distributed with this work for additional information
  20538. * regarding copyright ownership. The ASF licenses this file
  20539. * to you under the Apache License, Version 2.0 (the
  20540. * "License"); you may not use this file except in compliance
  20541. * with the License. You may obtain a copy of the License at
  20542. *
  20543. * http://www.apache.org/licenses/LICENSE-2.0
  20544. *
  20545. * Unless required by applicable law or agreed to in writing,
  20546. * software distributed under the License is distributed on an
  20547. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20548. * KIND, either express or implied. See the License for the
  20549. * specific language governing permissions and limitations
  20550. * under the License.
  20551. */
  20552. var inner$4 = makeInner();
  20553. var SeriesModel = ComponentModel.extend({
  20554. type: 'series.__base__',
  20555. /**
  20556. * @readOnly
  20557. */
  20558. seriesIndex: 0,
  20559. // coodinateSystem will be injected in the echarts/CoordinateSystem
  20560. coordinateSystem: null,
  20561. /**
  20562. * @type {Object}
  20563. * @protected
  20564. */
  20565. defaultOption: null,
  20566. /**
  20567. * Data provided for legend
  20568. * @type {Function}
  20569. */
  20570. // PENDING
  20571. legendDataProvider: null,
  20572. /**
  20573. * Access path of color for visual
  20574. */
  20575. visualColorAccessPath: 'itemStyle.color',
  20576. /**
  20577. * Support merge layout params.
  20578. * Only support 'box' now (left/right/top/bottom/width/height).
  20579. * @type {string|Object} Object can be {ignoreSize: true}
  20580. * @readOnly
  20581. */
  20582. layoutMode: null,
  20583. init: function (option, parentModel, ecModel, extraOpt) {
  20584. /**
  20585. * @type {number}
  20586. * @readOnly
  20587. */
  20588. this.seriesIndex = this.componentIndex;
  20589. this.dataTask = createTask({
  20590. count: dataTaskCount,
  20591. reset: dataTaskReset
  20592. });
  20593. this.dataTask.context = {model: this};
  20594. this.mergeDefaultAndTheme(option, ecModel);
  20595. prepareSource(this);
  20596. var data = this.getInitialData(option, ecModel);
  20597. wrapData(data, this);
  20598. this.dataTask.context.data = data;
  20599. if (__DEV__) {
  20600. assert$1(data, 'getInitialData returned invalid data.');
  20601. }
  20602. /**
  20603. * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
  20604. * @private
  20605. */
  20606. inner$4(this).dataBeforeProcessed = data;
  20607. // If we reverse the order (make data firstly, and then make
  20608. // dataBeforeProcessed by cloneShallow), cloneShallow will
  20609. // cause data.graph.data !== data when using
  20610. // module:echarts/data/Graph or module:echarts/data/Tree.
  20611. // See module:echarts/data/helper/linkList
  20612. // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model
  20613. // init or merge stage, because the data can be restored. So we do not `restoreData`
  20614. // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.
  20615. // Call `seriesModel.getRawData()` instead.
  20616. // this.restoreData();
  20617. autoSeriesName(this);
  20618. },
  20619. /**
  20620. * Util for merge default and theme to option
  20621. * @param {Object} option
  20622. * @param {module:echarts/model/Global} ecModel
  20623. */
  20624. mergeDefaultAndTheme: function (option, ecModel) {
  20625. var layoutMode = this.layoutMode;
  20626. var inputPositionParams = layoutMode
  20627. ? getLayoutParams(option) : {};
  20628. // Backward compat: using subType on theme.
  20629. // But if name duplicate between series subType
  20630. // (for example: parallel) add component mainType,
  20631. // add suffix 'Series'.
  20632. var themeSubType = this.subType;
  20633. if (ComponentModel.hasClass(themeSubType)) {
  20634. themeSubType += 'Series';
  20635. }
  20636. merge(
  20637. option,
  20638. ecModel.getTheme().get(this.subType)
  20639. );
  20640. merge(option, this.getDefaultOption());
  20641. // Default label emphasis `show`
  20642. defaultEmphasis(option, 'label', ['show']);
  20643. this.fillDataTextStyle(option.data);
  20644. if (layoutMode) {
  20645. mergeLayoutParam(option, inputPositionParams, layoutMode);
  20646. }
  20647. },
  20648. mergeOption: function (newSeriesOption, ecModel) {
  20649. // this.settingTask.dirty();
  20650. newSeriesOption = merge(this.option, newSeriesOption, true);
  20651. this.fillDataTextStyle(newSeriesOption.data);
  20652. var layoutMode = this.layoutMode;
  20653. if (layoutMode) {
  20654. mergeLayoutParam(this.option, newSeriesOption, layoutMode);
  20655. }
  20656. prepareSource(this);
  20657. var data = this.getInitialData(newSeriesOption, ecModel);
  20658. wrapData(data, this);
  20659. this.dataTask.dirty();
  20660. this.dataTask.context.data = data;
  20661. inner$4(this).dataBeforeProcessed = data;
  20662. autoSeriesName(this);
  20663. },
  20664. fillDataTextStyle: function (data) {
  20665. // Default data label emphasis `show`
  20666. // FIXME Tree structure data ?
  20667. // FIXME Performance ?
  20668. if (data && !isTypedArray(data)) {
  20669. var props = ['show'];
  20670. for (var i = 0; i < data.length; i++) {
  20671. if (data[i] && data[i].label) {
  20672. defaultEmphasis(data[i], 'label', props);
  20673. }
  20674. }
  20675. }
  20676. },
  20677. /**
  20678. * Init a data structure from data related option in series
  20679. * Must be overwritten
  20680. */
  20681. getInitialData: function () {},
  20682. /**
  20683. * Append data to list
  20684. * @param {Object} params
  20685. * @param {Array|TypedArray} params.data
  20686. */
  20687. appendData: function (params) {
  20688. // FIXME ???
  20689. // (1) If data from dataset, forbidden append.
  20690. // (2) support append data of dataset.
  20691. var data = this.getRawData();
  20692. data.appendData(params.data);
  20693. },
  20694. /**
  20695. * Consider some method like `filter`, `map` need make new data,
  20696. * We should make sure that `seriesModel.getData()` get correct
  20697. * data in the stream procedure. So we fetch data from upstream
  20698. * each time `task.perform` called.
  20699. * @param {string} [dataType]
  20700. * @return {module:echarts/data/List}
  20701. */
  20702. getData: function (dataType) {
  20703. var task = getCurrentTask(this);
  20704. if (task) {
  20705. var data = task.context.data;
  20706. return dataType == null ? data : data.getLinkedData(dataType);
  20707. }
  20708. else {
  20709. // When series is not alive (that may happen when click toolbox
  20710. // restore or setOption with not merge mode), series data may
  20711. // be still need to judge animation or something when graphic
  20712. // elements want to know whether fade out.
  20713. return inner$4(this).data;
  20714. }
  20715. },
  20716. /**
  20717. * @param {module:echarts/data/List} data
  20718. */
  20719. setData: function (data) {
  20720. var task = getCurrentTask(this);
  20721. if (task) {
  20722. var context = task.context;
  20723. // Consider case: filter, data sample.
  20724. if (context.data !== data && task.modifyOutputEnd) {
  20725. task.setOutputEnd(data.count());
  20726. }
  20727. context.outputData = data;
  20728. // Caution: setData should update context.data,
  20729. // Because getData may be called multiply in a
  20730. // single stage and expect to get the data just
  20731. // set. (For example, AxisProxy, x y both call
  20732. // getData and setDate sequentially).
  20733. // So the context.data should be fetched from
  20734. // upstream each time when a stage starts to be
  20735. // performed.
  20736. if (task !== this.dataTask) {
  20737. context.data = data;
  20738. }
  20739. }
  20740. inner$4(this).data = data;
  20741. },
  20742. /**
  20743. * @see {module:echarts/data/helper/sourceHelper#getSource}
  20744. * @return {module:echarts/data/Source} source
  20745. */
  20746. getSource: function () {
  20747. return getSource(this);
  20748. },
  20749. /**
  20750. * Get data before processed
  20751. * @return {module:echarts/data/List}
  20752. */
  20753. getRawData: function () {
  20754. return inner$4(this).dataBeforeProcessed;
  20755. },
  20756. /**
  20757. * Get base axis if has coordinate system and has axis.
  20758. * By default use coordSys.getBaseAxis();
  20759. * Can be overrided for some chart.
  20760. * @return {type} description
  20761. */
  20762. getBaseAxis: function () {
  20763. var coordSys = this.coordinateSystem;
  20764. return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
  20765. },
  20766. // FIXME
  20767. /**
  20768. * Default tooltip formatter
  20769. *
  20770. * @param {number} dataIndex
  20771. * @param {boolean} [multipleSeries=false]
  20772. * @param {number} [dataType]
  20773. * @param {string} [renderMode='html'] valid values: 'html' and 'richText'.
  20774. * 'html' is used for rendering tooltip in extra DOM form, and the result
  20775. * string is used as DOM HTML content.
  20776. * 'richText' is used for rendering tooltip in rich text form, for those where
  20777. * DOM operation is not supported.
  20778. * @return {Object} formatted tooltip with `html` and `markers`
  20779. */
  20780. formatTooltip: function (dataIndex, multipleSeries, dataType, renderMode) {
  20781. var series = this;
  20782. renderMode = renderMode || 'html';
  20783. var newLine = renderMode === 'html' ? '<br/>' : '\n';
  20784. var isRichText = renderMode === 'richText';
  20785. var markers = {};
  20786. var markerId = 0;
  20787. function formatArrayValue(value) {
  20788. // ??? TODO refactor these logic.
  20789. // check: category-no-encode-has-axis-data in dataset.html
  20790. var vertially = reduce(value, function (vertially, val, idx) {
  20791. var dimItem = data.getDimensionInfo(idx);
  20792. return vertially |= dimItem && dimItem.tooltip !== false && dimItem.displayName != null;
  20793. }, 0);
  20794. var result = [];
  20795. tooltipDims.length
  20796. ? each$1(tooltipDims, function (dim) {
  20797. setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
  20798. })
  20799. // By default, all dims is used on tooltip.
  20800. : each$1(value, setEachItem);
  20801. function setEachItem(val, dim) {
  20802. var dimInfo = data.getDimensionInfo(dim);
  20803. // If `dimInfo.tooltip` is not set, show tooltip.
  20804. if (!dimInfo || dimInfo.otherDims.tooltip === false) {
  20805. return;
  20806. }
  20807. var dimType = dimInfo.type;
  20808. var markName = 'sub' + series.seriesIndex + 'at' + markerId;
  20809. var dimHead = getTooltipMarker({
  20810. color: color,
  20811. type: 'subItem',
  20812. renderMode: renderMode,
  20813. markerId: markName
  20814. });
  20815. var dimHeadStr = typeof dimHead === 'string' ? dimHead : dimHead.content;
  20816. var valStr = (vertially
  20817. ? dimHeadStr + encodeHTML(dimInfo.displayName || '-') + ': '
  20818. : ''
  20819. )
  20820. // FIXME should not format time for raw data?
  20821. + encodeHTML(dimType === 'ordinal'
  20822. ? val + ''
  20823. : dimType === 'time'
  20824. ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val))
  20825. : addCommas(val)
  20826. );
  20827. valStr && result.push(valStr);
  20828. if (isRichText) {
  20829. markers[markName] = color;
  20830. ++markerId;
  20831. }
  20832. }
  20833. var newLine = vertially ? (isRichText ? '\n' : '<br/>') : '';
  20834. var content = newLine + result.join(newLine || ', ');
  20835. return {
  20836. renderMode: renderMode,
  20837. content: content,
  20838. style: markers
  20839. };
  20840. }
  20841. function formatSingleValue(val) {
  20842. // return encodeHTML(addCommas(val));
  20843. return {
  20844. renderMode: renderMode,
  20845. content: encodeHTML(addCommas(val)),
  20846. style: markers
  20847. };
  20848. }
  20849. var data = this.getData();
  20850. var tooltipDims = data.mapDimension('defaultedTooltip', true);
  20851. var tooltipDimLen = tooltipDims.length;
  20852. var value = this.getRawValue(dataIndex);
  20853. var isValueArr = isArray(value);
  20854. var color = data.getItemVisual(dataIndex, 'color');
  20855. if (isObject$1(color) && color.colorStops) {
  20856. color = (color.colorStops[0] || {}).color;
  20857. }
  20858. color = color || 'transparent';
  20859. // Complicated rule for pretty tooltip.
  20860. var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen))
  20861. ? formatArrayValue(value)
  20862. : tooltipDimLen
  20863. ? formatSingleValue(retrieveRawValue(data, dataIndex, tooltipDims[0]))
  20864. : formatSingleValue(isValueArr ? value[0] : value);
  20865. var content = formattedValue.content;
  20866. var markName = series.seriesIndex + 'at' + markerId;
  20867. var colorEl = getTooltipMarker({
  20868. color: color,
  20869. type: 'item',
  20870. renderMode: renderMode,
  20871. markerId: markName
  20872. });
  20873. markers[markName] = color;
  20874. ++markerId;
  20875. var name = data.getName(dataIndex);
  20876. var seriesName = this.name;
  20877. if (!isNameSpecified(this)) {
  20878. seriesName = '';
  20879. }
  20880. seriesName = seriesName
  20881. ? encodeHTML(seriesName) + (!multipleSeries ? newLine : ': ')
  20882. : '';
  20883. var colorStr = typeof colorEl === 'string' ? colorEl : colorEl.content;
  20884. var html = !multipleSeries
  20885. ? seriesName + colorStr
  20886. + (name
  20887. ? encodeHTML(name) + ': ' + content
  20888. : content
  20889. )
  20890. : colorStr + seriesName + content;
  20891. return {
  20892. html: html,
  20893. markers: markers
  20894. };
  20895. },
  20896. /**
  20897. * @return {boolean}
  20898. */
  20899. isAnimationEnabled: function () {
  20900. if (env$1.node) {
  20901. return false;
  20902. }
  20903. var animationEnabled = this.getShallow('animation');
  20904. if (animationEnabled) {
  20905. if (this.getData().count() > this.getShallow('animationThreshold')) {
  20906. animationEnabled = false;
  20907. }
  20908. }
  20909. return animationEnabled;
  20910. },
  20911. restoreData: function () {
  20912. this.dataTask.dirty();
  20913. },
  20914. getColorFromPalette: function (name, scope, requestColorNum) {
  20915. var ecModel = this.ecModel;
  20916. // PENDING
  20917. var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope, requestColorNum);
  20918. if (!color) {
  20919. color = ecModel.getColorFromPalette(name, scope, requestColorNum);
  20920. }
  20921. return color;
  20922. },
  20923. /**
  20924. * Use `data.mapDimension(coordDim, true)` instead.
  20925. * @deprecated
  20926. */
  20927. coordDimToDataDim: function (coordDim) {
  20928. return this.getRawData().mapDimension(coordDim, true);
  20929. },
  20930. /**
  20931. * Get progressive rendering count each step
  20932. * @return {number}
  20933. */
  20934. getProgressive: function () {
  20935. return this.get('progressive');
  20936. },
  20937. /**
  20938. * Get progressive rendering count each step
  20939. * @return {number}
  20940. */
  20941. getProgressiveThreshold: function () {
  20942. return this.get('progressiveThreshold');
  20943. },
  20944. /**
  20945. * Get data indices for show tooltip content. See tooltip.
  20946. * @abstract
  20947. * @param {Array.<string>|string} dim
  20948. * @param {Array.<number>} value
  20949. * @param {module:echarts/coord/single/SingleAxis} baseAxis
  20950. * @return {Object} {dataIndices, nestestValue}.
  20951. */
  20952. getAxisTooltipData: null,
  20953. /**
  20954. * See tooltip.
  20955. * @abstract
  20956. * @param {number} dataIndex
  20957. * @return {Array.<number>} Point of tooltip. null/undefined can be returned.
  20958. */
  20959. getTooltipPosition: null,
  20960. /**
  20961. * @see {module:echarts/stream/Scheduler}
  20962. */
  20963. pipeTask: null,
  20964. /**
  20965. * Convinient for override in extended class.
  20966. * @protected
  20967. * @type {Function}
  20968. */
  20969. preventIncremental: null,
  20970. /**
  20971. * @public
  20972. * @readOnly
  20973. * @type {Object}
  20974. */
  20975. pipelineContext: null
  20976. });
  20977. mixin(SeriesModel, dataFormatMixin);
  20978. mixin(SeriesModel, colorPaletteMixin);
  20979. /**
  20980. * MUST be called after `prepareSource` called
  20981. * Here we need to make auto series, especially for auto legend. But we
  20982. * do not modify series.name in option to avoid side effects.
  20983. */
  20984. function autoSeriesName(seriesModel) {
  20985. // User specified name has higher priority, otherwise it may cause
  20986. // series can not be queried unexpectedly.
  20987. var name = seriesModel.name;
  20988. if (!isNameSpecified(seriesModel)) {
  20989. seriesModel.name = getSeriesAutoName(seriesModel) || name;
  20990. }
  20991. }
  20992. function getSeriesAutoName(seriesModel) {
  20993. var data = seriesModel.getRawData();
  20994. var dataDims = data.mapDimension('seriesName', true);
  20995. var nameArr = [];
  20996. each$1(dataDims, function (dataDim) {
  20997. var dimInfo = data.getDimensionInfo(dataDim);
  20998. dimInfo.displayName && nameArr.push(dimInfo.displayName);
  20999. });
  21000. return nameArr.join(' ');
  21001. }
  21002. function dataTaskCount(context) {
  21003. return context.model.getRawData().count();
  21004. }
  21005. function dataTaskReset(context) {
  21006. var seriesModel = context.model;
  21007. seriesModel.setData(seriesModel.getRawData().cloneShallow());
  21008. return dataTaskProgress;
  21009. }
  21010. function dataTaskProgress(param, context) {
  21011. // Avoid repead cloneShallow when data just created in reset.
  21012. if (param.end > context.outputData.count()) {
  21013. context.model.getRawData().cloneShallow(context.outputData);
  21014. }
  21015. }
  21016. // TODO refactor
  21017. function wrapData(data, seriesModel) {
  21018. each$1(data.CHANGABLE_METHODS, function (methodName) {
  21019. data.wrapMethod(methodName, curry(onDataSelfChange, seriesModel));
  21020. });
  21021. }
  21022. function onDataSelfChange(seriesModel) {
  21023. var task = getCurrentTask(seriesModel);
  21024. if (task) {
  21025. // Consider case: filter, selectRange
  21026. task.setOutputEnd(this.count());
  21027. }
  21028. }
  21029. function getCurrentTask(seriesModel) {
  21030. var scheduler = (seriesModel.ecModel || {}).scheduler;
  21031. var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
  21032. if (pipeline) {
  21033. // When pipline finished, the currrentTask keep the last
  21034. // task (renderTask).
  21035. var task = pipeline.currentTask;
  21036. if (task) {
  21037. var agentStubMap = task.agentStubMap;
  21038. if (agentStubMap) {
  21039. task = agentStubMap.get(seriesModel.uid);
  21040. }
  21041. }
  21042. return task;
  21043. }
  21044. }
  21045. /*
  21046. * Licensed to the Apache Software Foundation (ASF) under one
  21047. * or more contributor license agreements. See the NOTICE file
  21048. * distributed with this work for additional information
  21049. * regarding copyright ownership. The ASF licenses this file
  21050. * to you under the Apache License, Version 2.0 (the
  21051. * "License"); you may not use this file except in compliance
  21052. * with the License. You may obtain a copy of the License at
  21053. *
  21054. * http://www.apache.org/licenses/LICENSE-2.0
  21055. *
  21056. * Unless required by applicable law or agreed to in writing,
  21057. * software distributed under the License is distributed on an
  21058. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21059. * KIND, either express or implied. See the License for the
  21060. * specific language governing permissions and limitations
  21061. * under the License.
  21062. */
  21063. var Component$1 = function () {
  21064. /**
  21065. * @type {module:zrender/container/Group}
  21066. * @readOnly
  21067. */
  21068. this.group = new Group();
  21069. /**
  21070. * @type {string}
  21071. * @readOnly
  21072. */
  21073. this.uid = getUID('viewComponent');
  21074. };
  21075. Component$1.prototype = {
  21076. constructor: Component$1,
  21077. init: function (ecModel, api) {},
  21078. render: function (componentModel, ecModel, api, payload) {},
  21079. dispose: function () {},
  21080. /**
  21081. * @param {string} eventType
  21082. * @param {Object} query
  21083. * @param {module:zrender/Element} targetEl
  21084. * @param {Object} packedEvent
  21085. * @return {boolen} Pass only when return `true`.
  21086. */
  21087. filterForExposedEvent: null
  21088. };
  21089. var componentProto = Component$1.prototype;
  21090. componentProto.updateView
  21091. = componentProto.updateLayout
  21092. = componentProto.updateVisual
  21093. = function (seriesModel, ecModel, api, payload) {
  21094. // Do nothing;
  21095. };
  21096. // Enable Component.extend.
  21097. enableClassExtend(Component$1);
  21098. // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  21099. enableClassManagement(Component$1, {registerWhenExtend: true});
  21100. /*
  21101. * Licensed to the Apache Software Foundation (ASF) under one
  21102. * or more contributor license agreements. See the NOTICE file
  21103. * distributed with this work for additional information
  21104. * regarding copyright ownership. The ASF licenses this file
  21105. * to you under the Apache License, Version 2.0 (the
  21106. * "License"); you may not use this file except in compliance
  21107. * with the License. You may obtain a copy of the License at
  21108. *
  21109. * http://www.apache.org/licenses/LICENSE-2.0
  21110. *
  21111. * Unless required by applicable law or agreed to in writing,
  21112. * software distributed under the License is distributed on an
  21113. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21114. * KIND, either express or implied. See the License for the
  21115. * specific language governing permissions and limitations
  21116. * under the License.
  21117. */
  21118. /**
  21119. * @return {string} If large mode changed, return string 'reset';
  21120. */
  21121. var createRenderPlanner = function () {
  21122. var inner = makeInner();
  21123. return function (seriesModel) {
  21124. var fields = inner(seriesModel);
  21125. var pipelineContext = seriesModel.pipelineContext;
  21126. var originalLarge = fields.large;
  21127. var originalProgressive = fields.progressiveRender;
  21128. var large = fields.large = pipelineContext.large;
  21129. var progressive = fields.progressiveRender = pipelineContext.progressiveRender;
  21130. return !!((originalLarge ^ large) || (originalProgressive ^ progressive)) && 'reset';
  21131. };
  21132. };
  21133. /*
  21134. * Licensed to the Apache Software Foundation (ASF) under one
  21135. * or more contributor license agreements. See the NOTICE file
  21136. * distributed with this work for additional information
  21137. * regarding copyright ownership. The ASF licenses this file
  21138. * to you under the Apache License, Version 2.0 (the
  21139. * "License"); you may not use this file except in compliance
  21140. * with the License. You may obtain a copy of the License at
  21141. *
  21142. * http://www.apache.org/licenses/LICENSE-2.0
  21143. *
  21144. * Unless required by applicable law or agreed to in writing,
  21145. * software distributed under the License is distributed on an
  21146. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21147. * KIND, either express or implied. See the License for the
  21148. * specific language governing permissions and limitations
  21149. * under the License.
  21150. */
  21151. var inner$5 = makeInner();
  21152. var renderPlanner = createRenderPlanner();
  21153. function Chart() {
  21154. /**
  21155. * @type {module:zrender/container/Group}
  21156. * @readOnly
  21157. */
  21158. this.group = new Group();
  21159. /**
  21160. * @type {string}
  21161. * @readOnly
  21162. */
  21163. this.uid = getUID('viewChart');
  21164. this.renderTask = createTask({
  21165. plan: renderTaskPlan,
  21166. reset: renderTaskReset
  21167. });
  21168. this.renderTask.context = {view: this};
  21169. }
  21170. Chart.prototype = {
  21171. type: 'chart',
  21172. /**
  21173. * Init the chart.
  21174. * @param {module:echarts/model/Global} ecModel
  21175. * @param {module:echarts/ExtensionAPI} api
  21176. */
  21177. init: function (ecModel, api) {},
  21178. /**
  21179. * Render the chart.
  21180. * @param {module:echarts/model/Series} seriesModel
  21181. * @param {module:echarts/model/Global} ecModel
  21182. * @param {module:echarts/ExtensionAPI} api
  21183. * @param {Object} payload
  21184. */
  21185. render: function (seriesModel, ecModel, api, payload) {},
  21186. /**
  21187. * Highlight series or specified data item.
  21188. * @param {module:echarts/model/Series} seriesModel
  21189. * @param {module:echarts/model/Global} ecModel
  21190. * @param {module:echarts/ExtensionAPI} api
  21191. * @param {Object} payload
  21192. */
  21193. highlight: function (seriesModel, ecModel, api, payload) {
  21194. toggleHighlight(seriesModel.getData(), payload, 'emphasis');
  21195. },
  21196. /**
  21197. * Downplay series or specified data item.
  21198. * @param {module:echarts/model/Series} seriesModel
  21199. * @param {module:echarts/model/Global} ecModel
  21200. * @param {module:echarts/ExtensionAPI} api
  21201. * @param {Object} payload
  21202. */
  21203. downplay: function (seriesModel, ecModel, api, payload) {
  21204. toggleHighlight(seriesModel.getData(), payload, 'normal');
  21205. },
  21206. /**
  21207. * Remove self.
  21208. * @param {module:echarts/model/Global} ecModel
  21209. * @param {module:echarts/ExtensionAPI} api
  21210. */
  21211. remove: function (ecModel, api) {
  21212. this.group.removeAll();
  21213. },
  21214. /**
  21215. * Dispose self.
  21216. * @param {module:echarts/model/Global} ecModel
  21217. * @param {module:echarts/ExtensionAPI} api
  21218. */
  21219. dispose: function () {},
  21220. /**
  21221. * Rendering preparation in progressive mode.
  21222. * @param {module:echarts/model/Series} seriesModel
  21223. * @param {module:echarts/model/Global} ecModel
  21224. * @param {module:echarts/ExtensionAPI} api
  21225. * @param {Object} payload
  21226. */
  21227. incrementalPrepareRender: null,
  21228. /**
  21229. * Render in progressive mode.
  21230. * @param {Object} params See taskParams in `stream/task.js`
  21231. * @param {module:echarts/model/Series} seriesModel
  21232. * @param {module:echarts/model/Global} ecModel
  21233. * @param {module:echarts/ExtensionAPI} api
  21234. * @param {Object} payload
  21235. */
  21236. incrementalRender: null,
  21237. /**
  21238. * Update transform directly.
  21239. * @param {module:echarts/model/Series} seriesModel
  21240. * @param {module:echarts/model/Global} ecModel
  21241. * @param {module:echarts/ExtensionAPI} api
  21242. * @param {Object} payload
  21243. * @return {Object} {update: true}
  21244. */
  21245. updateTransform: null,
  21246. /**
  21247. * The view contains the given point.
  21248. * @interface
  21249. * @param {Array.<number>} point
  21250. * @return {boolean}
  21251. */
  21252. // containPoint: function () {}
  21253. /**
  21254. * @param {string} eventType
  21255. * @param {Object} query
  21256. * @param {module:zrender/Element} targetEl
  21257. * @param {Object} packedEvent
  21258. * @return {boolen} Pass only when return `true`.
  21259. */
  21260. filterForExposedEvent: null
  21261. };
  21262. var chartProto = Chart.prototype;
  21263. chartProto.updateView
  21264. = chartProto.updateLayout
  21265. = chartProto.updateVisual
  21266. = function (seriesModel, ecModel, api, payload) {
  21267. this.render(seriesModel, ecModel, api, payload);
  21268. };
  21269. /**
  21270. * Set state of single element
  21271. * @param {module:zrender/Element} el
  21272. * @param {string} state
  21273. */
  21274. function elSetState(el, state) {
  21275. if (el) {
  21276. el.trigger(state);
  21277. if (el.type === 'group') {
  21278. for (var i = 0; i < el.childCount(); i++) {
  21279. elSetState(el.childAt(i), state);
  21280. }
  21281. }
  21282. }
  21283. }
  21284. /**
  21285. * @param {module:echarts/data/List} data
  21286. * @param {Object} payload
  21287. * @param {string} state 'normal'|'emphasis'
  21288. */
  21289. function toggleHighlight(data, payload, state) {
  21290. var dataIndex = queryDataIndex(data, payload);
  21291. if (dataIndex != null) {
  21292. each$1(normalizeToArray(dataIndex), function (dataIdx) {
  21293. elSetState(data.getItemGraphicEl(dataIdx), state);
  21294. });
  21295. }
  21296. else {
  21297. data.eachItemGraphicEl(function (el) {
  21298. elSetState(el, state);
  21299. });
  21300. }
  21301. }
  21302. // Enable Chart.extend.
  21303. enableClassExtend(Chart, ['dispose']);
  21304. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  21305. enableClassManagement(Chart, {registerWhenExtend: true});
  21306. Chart.markUpdateMethod = function (payload, methodName) {
  21307. inner$5(payload).updateMethod = methodName;
  21308. };
  21309. function renderTaskPlan(context) {
  21310. return renderPlanner(context.model);
  21311. }
  21312. function renderTaskReset(context) {
  21313. var seriesModel = context.model;
  21314. var ecModel = context.ecModel;
  21315. var api = context.api;
  21316. var payload = context.payload;
  21317. // ???! remove updateView updateVisual
  21318. var progressiveRender = seriesModel.pipelineContext.progressiveRender;
  21319. var view = context.view;
  21320. var updateMethod = payload && inner$5(payload).updateMethod;
  21321. var methodName = progressiveRender
  21322. ? 'incrementalPrepareRender'
  21323. : (updateMethod && view[updateMethod])
  21324. ? updateMethod
  21325. // `appendData` is also supported when data amount
  21326. // is less than progressive threshold.
  21327. : 'render';
  21328. if (methodName !== 'render') {
  21329. view[methodName](seriesModel, ecModel, api, payload);
  21330. }
  21331. return progressMethodMap[methodName];
  21332. }
  21333. var progressMethodMap = {
  21334. incrementalPrepareRender: {
  21335. progress: function (params, context) {
  21336. context.view.incrementalRender(
  21337. params, context.model, context.ecModel, context.api, context.payload
  21338. );
  21339. }
  21340. },
  21341. render: {
  21342. // Put view.render in `progress` to support appendData. But in this case
  21343. // view.render should not be called in reset, otherwise it will be called
  21344. // twise. Use `forceFirstProgress` to make sure that view.render is called
  21345. // in any cases.
  21346. forceFirstProgress: true,
  21347. progress: function (params, context) {
  21348. context.view.render(
  21349. context.model, context.ecModel, context.api, context.payload
  21350. );
  21351. }
  21352. }
  21353. };
  21354. /*
  21355. * Licensed to the Apache Software Foundation (ASF) under one
  21356. * or more contributor license agreements. See the NOTICE file
  21357. * distributed with this work for additional information
  21358. * regarding copyright ownership. The ASF licenses this file
  21359. * to you under the Apache License, Version 2.0 (the
  21360. * "License"); you may not use this file except in compliance
  21361. * with the License. You may obtain a copy of the License at
  21362. *
  21363. * http://www.apache.org/licenses/LICENSE-2.0
  21364. *
  21365. * Unless required by applicable law or agreed to in writing,
  21366. * software distributed under the License is distributed on an
  21367. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21368. * KIND, either express or implied. See the License for the
  21369. * specific language governing permissions and limitations
  21370. * under the License.
  21371. */
  21372. var ORIGIN_METHOD = '\0__throttleOriginMethod';
  21373. var RATE = '\0__throttleRate';
  21374. var THROTTLE_TYPE = '\0__throttleType';
  21375. /**
  21376. * @public
  21377. * @param {(Function)} fn
  21378. * @param {number} [delay=0] Unit: ms.
  21379. * @param {boolean} [debounce=false]
  21380. * true: If call interval less than `delay`, only the last call works.
  21381. * false: If call interval less than `delay, call works on fixed rate.
  21382. * @return {(Function)} throttled fn.
  21383. */
  21384. function throttle(fn, delay, debounce) {
  21385. var currCall;
  21386. var lastCall = 0;
  21387. var lastExec = 0;
  21388. var timer = null;
  21389. var diff;
  21390. var scope;
  21391. var args;
  21392. var debounceNextCall;
  21393. delay = delay || 0;
  21394. function exec() {
  21395. lastExec = (new Date()).getTime();
  21396. timer = null;
  21397. fn.apply(scope, args || []);
  21398. }
  21399. var cb = function () {
  21400. currCall = (new Date()).getTime();
  21401. scope = this;
  21402. args = arguments;
  21403. var thisDelay = debounceNextCall || delay;
  21404. var thisDebounce = debounceNextCall || debounce;
  21405. debounceNextCall = null;
  21406. diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
  21407. clearTimeout(timer);
  21408. // Here we should make sure that: the `exec` SHOULD NOT be called later
  21409. // than a new call of `cb`, that is, preserving the command order. Consider
  21410. // calculating "scale rate" when roaming as an example. When a call of `cb`
  21411. // happens, either the `exec` is called dierectly, or the call is delayed.
  21412. // But the delayed call should never be later than next call of `cb`. Under
  21413. // this assurance, we can simply update view state each time `dispatchAction`
  21414. // triggered by user roaming, but not need to add extra code to avoid the
  21415. // state being "rolled-back".
  21416. if (thisDebounce) {
  21417. timer = setTimeout(exec, thisDelay);
  21418. }
  21419. else {
  21420. if (diff >= 0) {
  21421. exec();
  21422. }
  21423. else {
  21424. timer = setTimeout(exec, -diff);
  21425. }
  21426. }
  21427. lastCall = currCall;
  21428. };
  21429. /**
  21430. * Clear throttle.
  21431. * @public
  21432. */
  21433. cb.clear = function () {
  21434. if (timer) {
  21435. clearTimeout(timer);
  21436. timer = null;
  21437. }
  21438. };
  21439. /**
  21440. * Enable debounce once.
  21441. */
  21442. cb.debounceNextCall = function (debounceDelay) {
  21443. debounceNextCall = debounceDelay;
  21444. };
  21445. return cb;
  21446. }
  21447. /**
  21448. * Create throttle method or update throttle rate.
  21449. *
  21450. * @example
  21451. * ComponentView.prototype.render = function () {
  21452. * ...
  21453. * throttle.createOrUpdate(
  21454. * this,
  21455. * '_dispatchAction',
  21456. * this.model.get('throttle'),
  21457. * 'fixRate'
  21458. * );
  21459. * };
  21460. * ComponentView.prototype.remove = function () {
  21461. * throttle.clear(this, '_dispatchAction');
  21462. * };
  21463. * ComponentView.prototype.dispose = function () {
  21464. * throttle.clear(this, '_dispatchAction');
  21465. * };
  21466. *
  21467. * @public
  21468. * @param {Object} obj
  21469. * @param {string} fnAttr
  21470. * @param {number} [rate]
  21471. * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
  21472. * @return {Function} throttled function.
  21473. */
  21474. function createOrUpdate(obj, fnAttr, rate, throttleType) {
  21475. var fn = obj[fnAttr];
  21476. if (!fn) {
  21477. return;
  21478. }
  21479. var originFn = fn[ORIGIN_METHOD] || fn;
  21480. var lastThrottleType = fn[THROTTLE_TYPE];
  21481. var lastRate = fn[RATE];
  21482. if (lastRate !== rate || lastThrottleType !== throttleType) {
  21483. if (rate == null || !throttleType) {
  21484. return (obj[fnAttr] = originFn);
  21485. }
  21486. fn = obj[fnAttr] = throttle(
  21487. originFn, rate, throttleType === 'debounce'
  21488. );
  21489. fn[ORIGIN_METHOD] = originFn;
  21490. fn[THROTTLE_TYPE] = throttleType;
  21491. fn[RATE] = rate;
  21492. }
  21493. return fn;
  21494. }
  21495. /**
  21496. * Clear throttle. Example see throttle.createOrUpdate.
  21497. *
  21498. * @public
  21499. * @param {Object} obj
  21500. * @param {string} fnAttr
  21501. */
  21502. function clear(obj, fnAttr) {
  21503. var fn = obj[fnAttr];
  21504. if (fn && fn[ORIGIN_METHOD]) {
  21505. obj[fnAttr] = fn[ORIGIN_METHOD];
  21506. }
  21507. }
  21508. /*
  21509. * Licensed to the Apache Software Foundation (ASF) under one
  21510. * or more contributor license agreements. See the NOTICE file
  21511. * distributed with this work for additional information
  21512. * regarding copyright ownership. The ASF licenses this file
  21513. * to you under the Apache License, Version 2.0 (the
  21514. * "License"); you may not use this file except in compliance
  21515. * with the License. You may obtain a copy of the License at
  21516. *
  21517. * http://www.apache.org/licenses/LICENSE-2.0
  21518. *
  21519. * Unless required by applicable law or agreed to in writing,
  21520. * software distributed under the License is distributed on an
  21521. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21522. * KIND, either express or implied. See the License for the
  21523. * specific language governing permissions and limitations
  21524. * under the License.
  21525. */
  21526. var seriesColor = {
  21527. createOnAllSeries: true,
  21528. performRawSeries: true,
  21529. reset: function (seriesModel, ecModel) {
  21530. var data = seriesModel.getData();
  21531. var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.color').split('.');
  21532. var color = seriesModel.get(colorAccessPath) // Set in itemStyle
  21533. || seriesModel.getColorFromPalette(
  21534. // TODO series count changed.
  21535. seriesModel.name, null, ecModel.getSeriesCount()
  21536. ); // Default color
  21537. // FIXME Set color function or use the platte color
  21538. data.setVisual('color', color);
  21539. // Only visible series has each data be visual encoded
  21540. if (!ecModel.isSeriesFiltered(seriesModel)) {
  21541. if (typeof color === 'function' && !(color instanceof Gradient)) {
  21542. data.each(function (idx) {
  21543. data.setItemVisual(
  21544. idx, 'color', color(seriesModel.getDataParams(idx))
  21545. );
  21546. });
  21547. }
  21548. // itemStyle in each data item
  21549. var dataEach = function (data, idx) {
  21550. var itemModel = data.getItemModel(idx);
  21551. var color = itemModel.get(colorAccessPath, true);
  21552. if (color != null) {
  21553. data.setItemVisual(idx, 'color', color);
  21554. }
  21555. };
  21556. return { dataEach: data.hasItemOption ? dataEach : null };
  21557. }
  21558. }
  21559. };
  21560. /*
  21561. * Licensed to the Apache Software Foundation (ASF) under one
  21562. * or more contributor license agreements. See the NOTICE file
  21563. * distributed with this work for additional information
  21564. * regarding copyright ownership. The ASF licenses this file
  21565. * to you under the Apache License, Version 2.0 (the
  21566. * "License"); you may not use this file except in compliance
  21567. * with the License. You may obtain a copy of the License at
  21568. *
  21569. * http://www.apache.org/licenses/LICENSE-2.0
  21570. *
  21571. * Unless required by applicable law or agreed to in writing,
  21572. * software distributed under the License is distributed on an
  21573. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21574. * KIND, either express or implied. See the License for the
  21575. * specific language governing permissions and limitations
  21576. * under the License.
  21577. */
  21578. var lang = {
  21579. toolbox: {
  21580. brush: {
  21581. title: {
  21582. rect: 'Box Select',
  21583. polygon: 'Lasso Select',
  21584. lineX: 'Horizontally Select',
  21585. lineY: 'Vertically Select',
  21586. keep: 'Keep Selections',
  21587. clear: 'Clear Selections'
  21588. }
  21589. },
  21590. dataView: {
  21591. title: 'Data View',
  21592. lang: ['Data View', 'Close', 'Refresh']
  21593. },
  21594. dataZoom: {
  21595. title: {
  21596. zoom: 'Zoom',
  21597. back: 'Zoom Reset'
  21598. }
  21599. },
  21600. magicType: {
  21601. title: {
  21602. line: 'Switch to Line Chart',
  21603. bar: 'Switch to Bar Chart',
  21604. stack: 'Stack',
  21605. tiled: 'Tile'
  21606. }
  21607. },
  21608. restore: {
  21609. title: 'Restore'
  21610. },
  21611. saveAsImage: {
  21612. title: 'Save as Image',
  21613. lang: ['Right Click to Save Image']
  21614. }
  21615. }
  21616. };
  21617. /*
  21618. * Licensed to the Apache Software Foundation (ASF) under one
  21619. * or more contributor license agreements. See the NOTICE file
  21620. * distributed with this work for additional information
  21621. * regarding copyright ownership. The ASF licenses this file
  21622. * to you under the Apache License, Version 2.0 (the
  21623. * "License"); you may not use this file except in compliance
  21624. * with the License. You may obtain a copy of the License at
  21625. *
  21626. * http://www.apache.org/licenses/LICENSE-2.0
  21627. *
  21628. * Unless required by applicable law or agreed to in writing,
  21629. * software distributed under the License is distributed on an
  21630. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21631. * KIND, either express or implied. See the License for the
  21632. * specific language governing permissions and limitations
  21633. * under the License.
  21634. */
  21635. var aria = function (dom, ecModel) {
  21636. var ariaModel = ecModel.getModel('aria');
  21637. if (!ariaModel.get('show')) {
  21638. return;
  21639. }
  21640. else if (ariaModel.get('description')) {
  21641. dom.setAttribute('aria-label', ariaModel.get('description'));
  21642. return;
  21643. }
  21644. var seriesCnt = 0;
  21645. ecModel.eachSeries(function (seriesModel, idx) {
  21646. ++seriesCnt;
  21647. }, this);
  21648. var maxDataCnt = ariaModel.get('data.maxCount') || 10;
  21649. var maxSeriesCnt = ariaModel.get('series.maxCount') || 10;
  21650. var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);
  21651. var ariaLabel;
  21652. if (seriesCnt < 1) {
  21653. // No series, no aria label
  21654. return;
  21655. }
  21656. else {
  21657. var title = getTitle();
  21658. if (title) {
  21659. ariaLabel = replace(getConfig('general.withTitle'), {
  21660. title: title
  21661. });
  21662. }
  21663. else {
  21664. ariaLabel = getConfig('general.withoutTitle');
  21665. }
  21666. var seriesLabels = [];
  21667. var prefix = seriesCnt > 1
  21668. ? 'series.multiple.prefix'
  21669. : 'series.single.prefix';
  21670. ariaLabel += replace(getConfig(prefix), { seriesCount: seriesCnt });
  21671. ecModel.eachSeries(function (seriesModel, idx) {
  21672. if (idx < displaySeriesCnt) {
  21673. var seriesLabel;
  21674. var seriesName = seriesModel.get('name');
  21675. var seriesTpl = 'series.'
  21676. + (seriesCnt > 1 ? 'multiple' : 'single') + '.';
  21677. seriesLabel = getConfig(seriesName
  21678. ? seriesTpl + 'withName'
  21679. : seriesTpl + 'withoutName');
  21680. seriesLabel = replace(seriesLabel, {
  21681. seriesId: seriesModel.seriesIndex,
  21682. seriesName: seriesModel.get('name'),
  21683. seriesType: getSeriesTypeName(seriesModel.subType)
  21684. });
  21685. var data = seriesModel.getData();
  21686. window.data = data;
  21687. if (data.count() > maxDataCnt) {
  21688. // Show part of data
  21689. seriesLabel += replace(getConfig('data.partialData'), {
  21690. displayCnt: maxDataCnt
  21691. });
  21692. }
  21693. else {
  21694. seriesLabel += getConfig('data.allData');
  21695. }
  21696. var dataLabels = [];
  21697. for (var i = 0; i < data.count(); i++) {
  21698. if (i < maxDataCnt) {
  21699. var name = data.getName(i);
  21700. var value = retrieveRawValue(data, i);
  21701. dataLabels.push(
  21702. replace(
  21703. name
  21704. ? getConfig('data.withName')
  21705. : getConfig('data.withoutName'),
  21706. {
  21707. name: name,
  21708. value: value
  21709. }
  21710. )
  21711. );
  21712. }
  21713. }
  21714. seriesLabel += dataLabels
  21715. .join(getConfig('data.separator.middle'))
  21716. + getConfig('data.separator.end');
  21717. seriesLabels.push(seriesLabel);
  21718. }
  21719. });
  21720. ariaLabel += seriesLabels
  21721. .join(getConfig('series.multiple.separator.middle'))
  21722. + getConfig('series.multiple.separator.end');
  21723. dom.setAttribute('aria-label', ariaLabel);
  21724. }
  21725. function replace(str, keyValues) {
  21726. if (typeof str !== 'string') {
  21727. return str;
  21728. }
  21729. var result = str;
  21730. each$1(keyValues, function (value, key) {
  21731. result = result.replace(
  21732. new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'),
  21733. value
  21734. );
  21735. });
  21736. return result;
  21737. }
  21738. function getConfig(path) {
  21739. var userConfig = ariaModel.get(path);
  21740. if (userConfig == null) {
  21741. var pathArr = path.split('.');
  21742. var result = lang.aria;
  21743. for (var i = 0; i < pathArr.length; ++i) {
  21744. result = result[pathArr[i]];
  21745. }
  21746. return result;
  21747. }
  21748. else {
  21749. return userConfig;
  21750. }
  21751. }
  21752. function getTitle() {
  21753. var title = ecModel.getModel('title').option;
  21754. if (title && title.length) {
  21755. title = title[0];
  21756. }
  21757. return title && title.text;
  21758. }
  21759. function getSeriesTypeName(type) {
  21760. return lang.series.typeNames[type] || '自定义图';
  21761. }
  21762. };
  21763. /*
  21764. * Licensed to the Apache Software Foundation (ASF) under one
  21765. * or more contributor license agreements. See the NOTICE file
  21766. * distributed with this work for additional information
  21767. * regarding copyright ownership. The ASF licenses this file
  21768. * to you under the Apache License, Version 2.0 (the
  21769. * "License"); you may not use this file except in compliance
  21770. * with the License. You may obtain a copy of the License at
  21771. *
  21772. * http://www.apache.org/licenses/LICENSE-2.0
  21773. *
  21774. * Unless required by applicable law or agreed to in writing,
  21775. * software distributed under the License is distributed on an
  21776. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21777. * KIND, either express or implied. See the License for the
  21778. * specific language governing permissions and limitations
  21779. * under the License.
  21780. */
  21781. var PI$1 = Math.PI;
  21782. /**
  21783. * @param {module:echarts/ExtensionAPI} api
  21784. * @param {Object} [opts]
  21785. * @param {string} [opts.text]
  21786. * @param {string} [opts.color]
  21787. * @param {string} [opts.textColor]
  21788. * @return {module:zrender/Element}
  21789. */
  21790. var loadingDefault = function (api, opts) {
  21791. opts = opts || {};
  21792. defaults(opts, {
  21793. text: 'loading',
  21794. color: '#c23531',
  21795. textColor: '#000',
  21796. maskColor: 'rgba(255, 255, 255, 0.8)',
  21797. zlevel: 0
  21798. });
  21799. var mask = new Rect({
  21800. style: {
  21801. fill: opts.maskColor
  21802. },
  21803. zlevel: opts.zlevel,
  21804. z: 10000
  21805. });
  21806. var arc = new Arc({
  21807. shape: {
  21808. startAngle: -PI$1 / 2,
  21809. endAngle: -PI$1 / 2 + 0.1,
  21810. r: 10
  21811. },
  21812. style: {
  21813. stroke: opts.color,
  21814. lineCap: 'round',
  21815. lineWidth: 5
  21816. },
  21817. zlevel: opts.zlevel,
  21818. z: 10001
  21819. });
  21820. var labelRect = new Rect({
  21821. style: {
  21822. fill: 'none',
  21823. text: opts.text,
  21824. textPosition: 'right',
  21825. textDistance: 10,
  21826. textFill: opts.textColor
  21827. },
  21828. zlevel: opts.zlevel,
  21829. z: 10001
  21830. });
  21831. arc.animateShape(true)
  21832. .when(1000, {
  21833. endAngle: PI$1 * 3 / 2
  21834. })
  21835. .start('circularInOut');
  21836. arc.animateShape(true)
  21837. .when(1000, {
  21838. startAngle: PI$1 * 3 / 2
  21839. })
  21840. .delay(300)
  21841. .start('circularInOut');
  21842. var group = new Group();
  21843. group.add(arc);
  21844. group.add(labelRect);
  21845. group.add(mask);
  21846. // Inject resize
  21847. group.resize = function () {
  21848. var cx = api.getWidth() / 2;
  21849. var cy = api.getHeight() / 2;
  21850. arc.setShape({
  21851. cx: cx,
  21852. cy: cy
  21853. });
  21854. var r = arc.shape.r;
  21855. labelRect.setShape({
  21856. x: cx - r,
  21857. y: cy - r,
  21858. width: r * 2,
  21859. height: r * 2
  21860. });
  21861. mask.setShape({
  21862. x: 0,
  21863. y: 0,
  21864. width: api.getWidth(),
  21865. height: api.getHeight()
  21866. });
  21867. };
  21868. group.resize();
  21869. return group;
  21870. };
  21871. /*
  21872. * Licensed to the Apache Software Foundation (ASF) under one
  21873. * or more contributor license agreements. See the NOTICE file
  21874. * distributed with this work for additional information
  21875. * regarding copyright ownership. The ASF licenses this file
  21876. * to you under the Apache License, Version 2.0 (the
  21877. * "License"); you may not use this file except in compliance
  21878. * with the License. You may obtain a copy of the License at
  21879. *
  21880. * http://www.apache.org/licenses/LICENSE-2.0
  21881. *
  21882. * Unless required by applicable law or agreed to in writing,
  21883. * software distributed under the License is distributed on an
  21884. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  21885. * KIND, either express or implied. See the License for the
  21886. * specific language governing permissions and limitations
  21887. * under the License.
  21888. */
  21889. /**
  21890. * @module echarts/stream/Scheduler
  21891. */
  21892. /**
  21893. * @constructor
  21894. */
  21895. function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {
  21896. this.ecInstance = ecInstance;
  21897. this.api = api;
  21898. this.unfinished;
  21899. // Fix current processors in case that in some rear cases that
  21900. // processors might be registered after echarts instance created.
  21901. // Register processors incrementally for a echarts instance is
  21902. // not supported by this stream architecture.
  21903. var dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();
  21904. var visualHandlers = this._visualHandlers = visualHandlers.slice();
  21905. this._allHandlers = dataProcessorHandlers.concat(visualHandlers);
  21906. /**
  21907. * @private
  21908. * @type {
  21909. * [handlerUID: string]: {
  21910. * seriesTaskMap?: {
  21911. * [seriesUID: string]: Task
  21912. * },
  21913. * overallTask?: Task
  21914. * }
  21915. * }
  21916. */
  21917. this._stageTaskMap = createHashMap();
  21918. }
  21919. var proto = Scheduler.prototype;
  21920. /**
  21921. * @param {module:echarts/model/Global} ecModel
  21922. * @param {Object} payload
  21923. */
  21924. proto.restoreData = function (ecModel, payload) {
  21925. // TODO: Only restroe needed series and components, but not all components.
  21926. // Currently `restoreData` of all of the series and component will be called.
  21927. // But some independent components like `title`, `legend`, `graphic`, `toolbox`,
  21928. // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,
  21929. // and some components like coordinate system, axes, dataZoom, visualMap only
  21930. // need their target series refresh.
  21931. // (1) If we are implementing this feature some day, we should consider these cases:
  21932. // if a data processor depends on a component (e.g., dataZoomProcessor depends
  21933. // on the settings of `dataZoom`), it should be re-performed if the component
  21934. // is modified by `setOption`.
  21935. // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,
  21936. // it should be re-performed when the result array of `getTargetSeries` changed.
  21937. // We use `dependencies` to cover these issues.
  21938. // (3) How to update target series when coordinate system related components modified.
  21939. // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,
  21940. // and this case all of the tasks will be set as dirty.
  21941. ecModel.restoreData(payload);
  21942. // Theoretically an overall task not only depends on each of its target series, but also
  21943. // depends on all of the series.
  21944. // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks
  21945. // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure
  21946. // that the overall task is set as dirty and to be performed, otherwise it probably cause
  21947. // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it
  21948. // probably cause state chaos (consider `dataZoomProcessor`).
  21949. this._stageTaskMap.each(function (taskRecord) {
  21950. var overallTask = taskRecord.overallTask;
  21951. overallTask && overallTask.dirty();
  21952. });
  21953. };
  21954. // If seriesModel provided, incremental threshold is check by series data.
  21955. proto.getPerformArgs = function (task, isBlock) {
  21956. // For overall task
  21957. if (!task.__pipeline) {
  21958. return;
  21959. }
  21960. var pipeline = this._pipelineMap.get(task.__pipeline.id);
  21961. var pCtx = pipeline.context;
  21962. var incremental = !isBlock
  21963. && pipeline.progressiveEnabled
  21964. && (!pCtx || pCtx.progressiveRender)
  21965. && task.__idxInPipeline > pipeline.blockIndex;
  21966. var step = incremental ? pipeline.step : null;
  21967. var modDataCount = pCtx && pCtx.modDataCount;
  21968. var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;
  21969. return {step: step, modBy: modBy, modDataCount: modDataCount};
  21970. };
  21971. proto.getPipeline = function (pipelineId) {
  21972. return this._pipelineMap.get(pipelineId);
  21973. };
  21974. /**
  21975. * Current, progressive rendering starts from visual and layout.
  21976. * Always detect render mode in the same stage, avoiding that incorrect
  21977. * detection caused by data filtering.
  21978. * Caution:
  21979. * `updateStreamModes` use `seriesModel.getData()`.
  21980. */
  21981. proto.updateStreamModes = function (seriesModel, view) {
  21982. var pipeline = this._pipelineMap.get(seriesModel.uid);
  21983. var data = seriesModel.getData();
  21984. var dataLen = data.count();
  21985. // `progressiveRender` means that can render progressively in each
  21986. // animation frame. Note that some types of series do not provide
  21987. // `view.incrementalPrepareRender` but support `chart.appendData`. We
  21988. // use the term `incremental` but not `progressive` to describe the
  21989. // case that `chart.appendData`.
  21990. var progressiveRender = pipeline.progressiveEnabled
  21991. && view.incrementalPrepareRender
  21992. && dataLen >= pipeline.threshold;
  21993. var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold');
  21994. // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
  21995. // see `test/candlestick-large3.html`
  21996. var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
  21997. seriesModel.pipelineContext = pipeline.context = {
  21998. progressiveRender: progressiveRender,
  21999. modDataCount: modDataCount,
  22000. large: large
  22001. };
  22002. };
  22003. proto.restorePipelines = function (ecModel) {
  22004. var scheduler = this;
  22005. var pipelineMap = scheduler._pipelineMap = createHashMap();
  22006. ecModel.eachSeries(function (seriesModel) {
  22007. var progressive = seriesModel.getProgressive();
  22008. var pipelineId = seriesModel.uid;
  22009. pipelineMap.set(pipelineId, {
  22010. id: pipelineId,
  22011. head: null,
  22012. tail: null,
  22013. threshold: seriesModel.getProgressiveThreshold(),
  22014. progressiveEnabled: progressive
  22015. && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
  22016. blockIndex: -1,
  22017. step: Math.round(progressive || 700),
  22018. count: 0
  22019. });
  22020. pipe(scheduler, seriesModel, seriesModel.dataTask);
  22021. });
  22022. };
  22023. proto.prepareStageTasks = function () {
  22024. var stageTaskMap = this._stageTaskMap;
  22025. var ecModel = this.ecInstance.getModel();
  22026. var api = this.api;
  22027. each$1(this._allHandlers, function (handler) {
  22028. var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, []);
  22029. handler.reset && createSeriesStageTask(this, handler, record, ecModel, api);
  22030. handler.overallReset && createOverallStageTask(this, handler, record, ecModel, api);
  22031. }, this);
  22032. };
  22033. proto.prepareView = function (view, model, ecModel, api) {
  22034. var renderTask = view.renderTask;
  22035. var context = renderTask.context;
  22036. context.model = model;
  22037. context.ecModel = ecModel;
  22038. context.api = api;
  22039. renderTask.__block = !view.incrementalPrepareRender;
  22040. pipe(this, model, renderTask);
  22041. };
  22042. proto.performDataProcessorTasks = function (ecModel, payload) {
  22043. // If we do not use `block` here, it should be considered when to update modes.
  22044. performStageTasks(this, this._dataProcessorHandlers, ecModel, payload, {block: true});
  22045. };
  22046. // opt
  22047. // opt.visualType: 'visual' or 'layout'
  22048. // opt.setDirty
  22049. proto.performVisualTasks = function (ecModel, payload, opt) {
  22050. performStageTasks(this, this._visualHandlers, ecModel, payload, opt);
  22051. };
  22052. function performStageTasks(scheduler, stageHandlers, ecModel, payload, opt) {
  22053. opt = opt || {};
  22054. var unfinished;
  22055. each$1(stageHandlers, function (stageHandler, idx) {
  22056. if (opt.visualType && opt.visualType !== stageHandler.visualType) {
  22057. return;
  22058. }
  22059. var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);
  22060. var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
  22061. var overallTask = stageHandlerRecord.overallTask;
  22062. if (overallTask) {
  22063. var overallNeedDirty;
  22064. var agentStubMap = overallTask.agentStubMap;
  22065. agentStubMap.each(function (stub) {
  22066. if (needSetDirty(opt, stub)) {
  22067. stub.dirty();
  22068. overallNeedDirty = true;
  22069. }
  22070. });
  22071. overallNeedDirty && overallTask.dirty();
  22072. updatePayload(overallTask, payload);
  22073. var performArgs = scheduler.getPerformArgs(overallTask, opt.block);
  22074. // Execute stubs firstly, which may set the overall task dirty,
  22075. // then execute the overall task. And stub will call seriesModel.setData,
  22076. // which ensures that in the overallTask seriesModel.getData() will not
  22077. // return incorrect data.
  22078. agentStubMap.each(function (stub) {
  22079. stub.perform(performArgs);
  22080. });
  22081. unfinished |= overallTask.perform(performArgs);
  22082. }
  22083. else if (seriesTaskMap) {
  22084. seriesTaskMap.each(function (task, pipelineId) {
  22085. if (needSetDirty(opt, task)) {
  22086. task.dirty();
  22087. }
  22088. var performArgs = scheduler.getPerformArgs(task, opt.block);
  22089. performArgs.skip = !stageHandler.performRawSeries
  22090. && ecModel.isSeriesFiltered(task.context.model);
  22091. updatePayload(task, payload);
  22092. unfinished |= task.perform(performArgs);
  22093. });
  22094. }
  22095. });
  22096. function needSetDirty(opt, task) {
  22097. return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
  22098. }
  22099. scheduler.unfinished |= unfinished;
  22100. }
  22101. proto.performSeriesTasks = function (ecModel) {
  22102. var unfinished;
  22103. ecModel.eachSeries(function (seriesModel) {
  22104. // Progress to the end for dataInit and dataRestore.
  22105. unfinished |= seriesModel.dataTask.perform();
  22106. });
  22107. this.unfinished |= unfinished;
  22108. };
  22109. proto.plan = function () {
  22110. // Travel pipelines, check block.
  22111. this._pipelineMap.each(function (pipeline) {
  22112. var task = pipeline.tail;
  22113. do {
  22114. if (task.__block) {
  22115. pipeline.blockIndex = task.__idxInPipeline;
  22116. break;
  22117. }
  22118. task = task.getUpstream();
  22119. }
  22120. while (task);
  22121. });
  22122. };
  22123. var updatePayload = proto.updatePayload = function (task, payload) {
  22124. payload !== 'remain' && (task.context.payload = payload);
  22125. };
  22126. function createSeriesStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
  22127. var seriesTaskMap = stageHandlerRecord.seriesTaskMap
  22128. || (stageHandlerRecord.seriesTaskMap = createHashMap());
  22129. var seriesType = stageHandler.seriesType;
  22130. var getTargetSeries = stageHandler.getTargetSeries;
  22131. // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,
  22132. // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,
  22133. // it works but it may cause other irrelevant charts blocked.
  22134. if (stageHandler.createOnAllSeries) {
  22135. ecModel.eachRawSeries(create);
  22136. }
  22137. else if (seriesType) {
  22138. ecModel.eachRawSeriesByType(seriesType, create);
  22139. }
  22140. else if (getTargetSeries) {
  22141. getTargetSeries(ecModel, api).each(create);
  22142. }
  22143. function create(seriesModel) {
  22144. var pipelineId = seriesModel.uid;
  22145. // Init tasks for each seriesModel only once.
  22146. // Reuse original task instance.
  22147. var task = seriesTaskMap.get(pipelineId)
  22148. || seriesTaskMap.set(pipelineId, createTask({
  22149. plan: seriesTaskPlan,
  22150. reset: seriesTaskReset,
  22151. count: seriesTaskCount
  22152. }));
  22153. task.context = {
  22154. model: seriesModel,
  22155. ecModel: ecModel,
  22156. api: api,
  22157. useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
  22158. plan: stageHandler.plan,
  22159. reset: stageHandler.reset,
  22160. scheduler: scheduler
  22161. };
  22162. pipe(scheduler, seriesModel, task);
  22163. }
  22164. // Clear unused series tasks.
  22165. var pipelineMap = scheduler._pipelineMap;
  22166. seriesTaskMap.each(function (task, pipelineId) {
  22167. if (!pipelineMap.get(pipelineId)) {
  22168. task.dispose();
  22169. seriesTaskMap.removeKey(pipelineId);
  22170. }
  22171. });
  22172. }
  22173. function createOverallStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
  22174. var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask
  22175. // For overall task, the function only be called on reset stage.
  22176. || createTask({reset: overallTaskReset});
  22177. overallTask.context = {
  22178. ecModel: ecModel,
  22179. api: api,
  22180. overallReset: stageHandler.overallReset,
  22181. scheduler: scheduler
  22182. };
  22183. // Reuse orignal stubs.
  22184. var agentStubMap = overallTask.agentStubMap = overallTask.agentStubMap || createHashMap();
  22185. var seriesType = stageHandler.seriesType;
  22186. var getTargetSeries = stageHandler.getTargetSeries;
  22187. var overallProgress = true;
  22188. var modifyOutputEnd = stageHandler.modifyOutputEnd;
  22189. // An overall task with seriesType detected or has `getTargetSeries`, we add
  22190. // stub in each pipelines, it will set the overall task dirty when the pipeline
  22191. // progress. Moreover, to avoid call the overall task each frame (too frequent),
  22192. // we set the pipeline block.
  22193. if (seriesType) {
  22194. ecModel.eachRawSeriesByType(seriesType, createStub);
  22195. }
  22196. else if (getTargetSeries) {
  22197. getTargetSeries(ecModel, api).each(createStub);
  22198. }
  22199. // Otherwise, (usually it is legancy case), the overall task will only be
  22200. // executed when upstream dirty. Otherwise the progressive rendering of all
  22201. // pipelines will be disabled unexpectedly. But it still needs stubs to receive
  22202. // dirty info from upsteam.
  22203. else {
  22204. overallProgress = false;
  22205. each$1(ecModel.getSeries(), createStub);
  22206. }
  22207. function createStub(seriesModel) {
  22208. var pipelineId = seriesModel.uid;
  22209. var stub = agentStubMap.get(pipelineId);
  22210. if (!stub) {
  22211. stub = agentStubMap.set(pipelineId, createTask(
  22212. {reset: stubReset, onDirty: stubOnDirty}
  22213. ));
  22214. // When the result of `getTargetSeries` changed, the overallTask
  22215. // should be set as dirty and re-performed.
  22216. overallTask.dirty();
  22217. }
  22218. stub.context = {
  22219. model: seriesModel,
  22220. overallProgress: overallProgress,
  22221. modifyOutputEnd: modifyOutputEnd
  22222. };
  22223. stub.agent = overallTask;
  22224. stub.__block = overallProgress;
  22225. pipe(scheduler, seriesModel, stub);
  22226. }
  22227. // Clear unused stubs.
  22228. var pipelineMap = scheduler._pipelineMap;
  22229. agentStubMap.each(function (stub, pipelineId) {
  22230. if (!pipelineMap.get(pipelineId)) {
  22231. stub.dispose();
  22232. // When the result of `getTargetSeries` changed, the overallTask
  22233. // should be set as dirty and re-performed.
  22234. overallTask.dirty();
  22235. agentStubMap.removeKey(pipelineId);
  22236. }
  22237. });
  22238. }
  22239. function overallTaskReset(context) {
  22240. context.overallReset(
  22241. context.ecModel, context.api, context.payload
  22242. );
  22243. }
  22244. function stubReset(context, upstreamContext) {
  22245. return context.overallProgress && stubProgress;
  22246. }
  22247. function stubProgress() {
  22248. this.agent.dirty();
  22249. this.getDownstream().dirty();
  22250. }
  22251. function stubOnDirty() {
  22252. this.agent && this.agent.dirty();
  22253. }
  22254. function seriesTaskPlan(context) {
  22255. return context.plan && context.plan(
  22256. context.model, context.ecModel, context.api, context.payload
  22257. );
  22258. }
  22259. function seriesTaskReset(context) {
  22260. if (context.useClearVisual) {
  22261. context.data.clearAllVisual();
  22262. }
  22263. var resetDefines = context.resetDefines = normalizeToArray(context.reset(
  22264. context.model, context.ecModel, context.api, context.payload
  22265. ));
  22266. return resetDefines.length > 1
  22267. ? map(resetDefines, function (v, idx) {
  22268. return makeSeriesTaskProgress(idx);
  22269. })
  22270. : singleSeriesTaskProgress;
  22271. }
  22272. var singleSeriesTaskProgress = makeSeriesTaskProgress(0);
  22273. function makeSeriesTaskProgress(resetDefineIdx) {
  22274. return function (params, context) {
  22275. var data = context.data;
  22276. var resetDefine = context.resetDefines[resetDefineIdx];
  22277. if (resetDefine && resetDefine.dataEach) {
  22278. for (var i = params.start; i < params.end; i++) {
  22279. resetDefine.dataEach(data, i);
  22280. }
  22281. }
  22282. else if (resetDefine && resetDefine.progress) {
  22283. resetDefine.progress(params, data);
  22284. }
  22285. };
  22286. }
  22287. function seriesTaskCount(context) {
  22288. return context.data.count();
  22289. }
  22290. function pipe(scheduler, seriesModel, task) {
  22291. var pipelineId = seriesModel.uid;
  22292. var pipeline = scheduler._pipelineMap.get(pipelineId);
  22293. !pipeline.head && (pipeline.head = task);
  22294. pipeline.tail && pipeline.tail.pipe(task);
  22295. pipeline.tail = task;
  22296. task.__idxInPipeline = pipeline.count++;
  22297. task.__pipeline = pipeline;
  22298. }
  22299. Scheduler.wrapStageHandler = function (stageHandler, visualType) {
  22300. if (isFunction$1(stageHandler)) {
  22301. stageHandler = {
  22302. overallReset: stageHandler,
  22303. seriesType: detectSeriseType(stageHandler)
  22304. };
  22305. }
  22306. stageHandler.uid = getUID('stageHandler');
  22307. visualType && (stageHandler.visualType = visualType);
  22308. return stageHandler;
  22309. };
  22310. /**
  22311. * Only some legacy stage handlers (usually in echarts extensions) are pure function.
  22312. * To ensure that they can work normally, they should work in block mode, that is,
  22313. * they should not be started util the previous tasks finished. So they cause the
  22314. * progressive rendering disabled. We try to detect the series type, to narrow down
  22315. * the block range to only the series type they concern, but not all series.
  22316. */
  22317. function detectSeriseType(legacyFunc) {
  22318. seriesType = null;
  22319. try {
  22320. // Assume there is no async when calling `eachSeriesByType`.
  22321. legacyFunc(ecModelMock, apiMock);
  22322. }
  22323. catch (e) {
  22324. }
  22325. return seriesType;
  22326. }
  22327. var ecModelMock = {};
  22328. var apiMock = {};
  22329. var seriesType;
  22330. mockMethods(ecModelMock, GlobalModel);
  22331. mockMethods(apiMock, ExtensionAPI);
  22332. ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
  22333. seriesType = type;
  22334. };
  22335. ecModelMock.eachComponent = function (cond) {
  22336. if (cond.mainType === 'series' && cond.subType) {
  22337. seriesType = cond.subType;
  22338. }
  22339. };
  22340. function mockMethods(target, Clz) {
  22341. /* eslint-disable */
  22342. for (var name in Clz.prototype) {
  22343. // Do not use hasOwnProperty
  22344. target[name] = noop;
  22345. }
  22346. /* eslint-enable */
  22347. }
  22348. /*
  22349. * Licensed to the Apache Software Foundation (ASF) under one
  22350. * or more contributor license agreements. See the NOTICE file
  22351. * distributed with this work for additional information
  22352. * regarding copyright ownership. The ASF licenses this file
  22353. * to you under the Apache License, Version 2.0 (the
  22354. * "License"); you may not use this file except in compliance
  22355. * with the License. You may obtain a copy of the License at
  22356. *
  22357. * http://www.apache.org/licenses/LICENSE-2.0
  22358. *
  22359. * Unless required by applicable law or agreed to in writing,
  22360. * software distributed under the License is distributed on an
  22361. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  22362. * KIND, either express or implied. See the License for the
  22363. * specific language governing permissions and limitations
  22364. * under the License.
  22365. */
  22366. var colorAll = [
  22367. '#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f',
  22368. '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'
  22369. ];
  22370. var lightTheme = {
  22371. color: colorAll,
  22372. colorLayer: [
  22373. ['#37A2DA', '#ffd85c', '#fd7b5f'],
  22374. ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'],
  22375. ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'],
  22376. colorAll
  22377. ]
  22378. };
  22379. /*
  22380. * Licensed to the Apache Software Foundation (ASF) under one
  22381. * or more contributor license agreements. See the NOTICE file
  22382. * distributed with this work for additional information
  22383. * regarding copyright ownership. The ASF licenses this file
  22384. * to you under the Apache License, Version 2.0 (the
  22385. * "License"); you may not use this file except in compliance
  22386. * with the License. You may obtain a copy of the License at
  22387. *
  22388. * http://www.apache.org/licenses/LICENSE-2.0
  22389. *
  22390. * Unless required by applicable law or agreed to in writing,
  22391. * software distributed under the License is distributed on an
  22392. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  22393. * KIND, either express or implied. See the License for the
  22394. * specific language governing permissions and limitations
  22395. * under the License.
  22396. */
  22397. var contrastColor = '#eee';
  22398. var axisCommon = function () {
  22399. return {
  22400. axisLine: {
  22401. lineStyle: {
  22402. color: contrastColor
  22403. }
  22404. },
  22405. axisTick: {
  22406. lineStyle: {
  22407. color: contrastColor
  22408. }
  22409. },
  22410. axisLabel: {
  22411. textStyle: {
  22412. color: contrastColor
  22413. }
  22414. },
  22415. splitLine: {
  22416. lineStyle: {
  22417. type: 'dashed',
  22418. color: '#aaa'
  22419. }
  22420. },
  22421. splitArea: {
  22422. areaStyle: {
  22423. color: contrastColor
  22424. }
  22425. }
  22426. };
  22427. };
  22428. var colorPalette = [
  22429. '#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53',
  22430. '#eedd78', '#73a373', '#73b9bc', '#7289ab', '#91ca8c', '#f49f42'
  22431. ];
  22432. var theme = {
  22433. color: colorPalette,
  22434. backgroundColor: '#333',
  22435. tooltip: {
  22436. axisPointer: {
  22437. lineStyle: {
  22438. color: contrastColor
  22439. },
  22440. crossStyle: {
  22441. color: contrastColor
  22442. }
  22443. }
  22444. },
  22445. legend: {
  22446. textStyle: {
  22447. color: contrastColor
  22448. }
  22449. },
  22450. textStyle: {
  22451. color: contrastColor
  22452. },
  22453. title: {
  22454. textStyle: {
  22455. color: contrastColor
  22456. }
  22457. },
  22458. toolbox: {
  22459. iconStyle: {
  22460. normal: {
  22461. borderColor: contrastColor
  22462. }
  22463. }
  22464. },
  22465. dataZoom: {
  22466. textStyle: {
  22467. color: contrastColor
  22468. }
  22469. },
  22470. visualMap: {
  22471. textStyle: {
  22472. color: contrastColor
  22473. }
  22474. },
  22475. timeline: {
  22476. lineStyle: {
  22477. color: contrastColor
  22478. },
  22479. itemStyle: {
  22480. normal: {
  22481. color: colorPalette[1]
  22482. }
  22483. },
  22484. label: {
  22485. normal: {
  22486. textStyle: {
  22487. color: contrastColor
  22488. }
  22489. }
  22490. },
  22491. controlStyle: {
  22492. normal: {
  22493. color: contrastColor,
  22494. borderColor: contrastColor
  22495. }
  22496. }
  22497. },
  22498. timeAxis: axisCommon(),
  22499. logAxis: axisCommon(),
  22500. valueAxis: axisCommon(),
  22501. categoryAxis: axisCommon(),
  22502. line: {
  22503. symbol: 'circle'
  22504. },
  22505. graph: {
  22506. color: colorPalette
  22507. },
  22508. gauge: {
  22509. title: {
  22510. textStyle: {
  22511. color: contrastColor
  22512. }
  22513. }
  22514. },
  22515. candlestick: {
  22516. itemStyle: {
  22517. normal: {
  22518. color: '#FD1050',
  22519. color0: '#0CF49B',
  22520. borderColor: '#FD1050',
  22521. borderColor0: '#0CF49B'
  22522. }
  22523. }
  22524. }
  22525. };
  22526. theme.categoryAxis.splitLine.show = false;
  22527. /*
  22528. * Licensed to the Apache Software Foundation (ASF) under one
  22529. * or more contributor license agreements. See the NOTICE file
  22530. * distributed with this work for additional information
  22531. * regarding copyright ownership. The ASF licenses this file
  22532. * to you under the Apache License, Version 2.0 (the
  22533. * "License"); you may not use this file except in compliance
  22534. * with the License. You may obtain a copy of the License at
  22535. *
  22536. * http://www.apache.org/licenses/LICENSE-2.0
  22537. *
  22538. * Unless required by applicable law or agreed to in writing,
  22539. * software distributed under the License is distributed on an
  22540. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  22541. * KIND, either express or implied. See the License for the
  22542. * specific language governing permissions and limitations
  22543. * under the License.
  22544. */
  22545. /**
  22546. * This module is imported by echarts directly.
  22547. *
  22548. * Notice:
  22549. * Always keep this file exists for backward compatibility.
  22550. * Because before 4.1.0, dataset is an optional component,
  22551. * some users may import this module manually.
  22552. */
  22553. ComponentModel.extend({
  22554. type: 'dataset',
  22555. /**
  22556. * @protected
  22557. */
  22558. defaultOption: {
  22559. // 'row', 'column'
  22560. seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
  22561. // null/'auto': auto detect header, see "module:echarts/data/helper/sourceHelper"
  22562. sourceHeader: null,
  22563. dimensions: null,
  22564. source: null
  22565. },
  22566. optionUpdated: function () {
  22567. detectSourceFormat(this);
  22568. }
  22569. });
  22570. Component$1.extend({
  22571. type: 'dataset'
  22572. });
  22573. /**
  22574. * 椭圆形状
  22575. * @module zrender/graphic/shape/Ellipse
  22576. */
  22577. var Ellipse = Path.extend({
  22578. type: 'ellipse',
  22579. shape: {
  22580. cx: 0, cy: 0,
  22581. rx: 0, ry: 0
  22582. },
  22583. buildPath: function (ctx, shape) {
  22584. var k = 0.5522848;
  22585. var x = shape.cx;
  22586. var y = shape.cy;
  22587. var a = shape.rx;
  22588. var b = shape.ry;
  22589. var ox = a * k; // 水平控制点偏移量
  22590. var oy = b * k; // 垂直控制点偏移量
  22591. // 从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
  22592. ctx.moveTo(x - a, y);
  22593. ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);
  22594. ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);
  22595. ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
  22596. ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
  22597. ctx.closePath();
  22598. }
  22599. });
  22600. // import RadialGradient from '../graphic/RadialGradient';
  22601. // import Pattern from '../graphic/Pattern';
  22602. // import * as vector from '../core/vector';
  22603. // Most of the values can be separated by comma and/or white space.
  22604. var DILIMITER_REG = /[\s,]+/;
  22605. /**
  22606. * For big svg string, this method might be time consuming.
  22607. *
  22608. * @param {string} svg xml string
  22609. * @return {Object} xml root.
  22610. */
  22611. function parseXML(svg) {
  22612. if (isString(svg)) {
  22613. var parser = new DOMParser();
  22614. svg = parser.parseFromString(svg, 'text/xml');
  22615. }
  22616. // Document node. If using $.get, doc node may be input.
  22617. if (svg.nodeType === 9) {
  22618. svg = svg.firstChild;
  22619. }
  22620. // nodeName of <!DOCTYPE svg> is also 'svg'.
  22621. while (svg.nodeName.toLowerCase() !== 'svg' || svg.nodeType !== 1) {
  22622. svg = svg.nextSibling;
  22623. }
  22624. return svg;
  22625. }
  22626. function SVGParser() {
  22627. this._defs = {};
  22628. this._root = null;
  22629. this._isDefine = false;
  22630. this._isText = false;
  22631. }
  22632. SVGParser.prototype.parse = function (xml, opt) {
  22633. opt = opt || {};
  22634. var svg = parseXML(xml);
  22635. if (!svg) {
  22636. throw new Error('Illegal svg');
  22637. }
  22638. var root = new Group();
  22639. this._root = root;
  22640. // parse view port
  22641. var viewBox = svg.getAttribute('viewBox') || '';
  22642. // If width/height not specified, means "100%" of `opt.width/height`.
  22643. // TODO: Other percent value not supported yet.
  22644. var width = parseFloat(svg.getAttribute('width') || opt.width);
  22645. var height = parseFloat(svg.getAttribute('height') || opt.height);
  22646. // If width/height not specified, set as null for output.
  22647. isNaN(width) && (width = null);
  22648. isNaN(height) && (height = null);
  22649. // Apply inline style on svg element.
  22650. parseAttributes(svg, root, null, true);
  22651. var child = svg.firstChild;
  22652. while (child) {
  22653. this._parseNode(child, root);
  22654. child = child.nextSibling;
  22655. }
  22656. var viewBoxRect;
  22657. var viewBoxTransform;
  22658. if (viewBox) {
  22659. var viewBoxArr = trim(viewBox).split(DILIMITER_REG);
  22660. // Some invalid case like viewBox: 'none'.
  22661. if (viewBoxArr.length >= 4) {
  22662. viewBoxRect = {
  22663. x: parseFloat(viewBoxArr[0] || 0),
  22664. y: parseFloat(viewBoxArr[1] || 0),
  22665. width: parseFloat(viewBoxArr[2]),
  22666. height: parseFloat(viewBoxArr[3])
  22667. };
  22668. }
  22669. }
  22670. if (viewBoxRect && width != null && height != null) {
  22671. viewBoxTransform = makeViewBoxTransform(viewBoxRect, width, height);
  22672. if (!opt.ignoreViewBox) {
  22673. // If set transform on the output group, it probably bring trouble when
  22674. // some users only intend to show the clipped content inside the viewBox,
  22675. // but not intend to transform the output group. So we keep the output
  22676. // group no transform. If the user intend to use the viewBox as a
  22677. // camera, just set `opt.ignoreViewBox` as `true` and set transfrom
  22678. // manually according to the viewBox info in the output of this method.
  22679. var elRoot = root;
  22680. root = new Group();
  22681. root.add(elRoot);
  22682. elRoot.scale = viewBoxTransform.scale.slice();
  22683. elRoot.position = viewBoxTransform.position.slice();
  22684. }
  22685. }
  22686. // Some shapes might be overflow the viewport, which should be
  22687. // clipped despite whether the viewBox is used, as the SVG does.
  22688. if (!opt.ignoreRootClip && width != null && height != null) {
  22689. root.setClipPath(new Rect({
  22690. shape: {x: 0, y: 0, width: width, height: height}
  22691. }));
  22692. }
  22693. // Set width/height on group just for output the viewport size.
  22694. return {
  22695. root: root,
  22696. width: width,
  22697. height: height,
  22698. viewBoxRect: viewBoxRect,
  22699. viewBoxTransform: viewBoxTransform
  22700. };
  22701. };
  22702. SVGParser.prototype._parseNode = function (xmlNode, parentGroup) {
  22703. var nodeName = xmlNode.nodeName.toLowerCase();
  22704. // TODO
  22705. // support <style>...</style> in svg, where nodeName is 'style',
  22706. // CSS classes is defined globally wherever the style tags are declared.
  22707. if (nodeName === 'defs') {
  22708. // define flag
  22709. this._isDefine = true;
  22710. }
  22711. else if (nodeName === 'text') {
  22712. this._isText = true;
  22713. }
  22714. var el;
  22715. if (this._isDefine) {
  22716. var parser = defineParsers[nodeName];
  22717. if (parser) {
  22718. var def = parser.call(this, xmlNode);
  22719. var id = xmlNode.getAttribute('id');
  22720. if (id) {
  22721. this._defs[id] = def;
  22722. }
  22723. }
  22724. }
  22725. else {
  22726. var parser = nodeParsers[nodeName];
  22727. if (parser) {
  22728. el = parser.call(this, xmlNode, parentGroup);
  22729. parentGroup.add(el);
  22730. }
  22731. }
  22732. var child = xmlNode.firstChild;
  22733. while (child) {
  22734. if (child.nodeType === 1) {
  22735. this._parseNode(child, el);
  22736. }
  22737. // Is text
  22738. if (child.nodeType === 3 && this._isText) {
  22739. this._parseText(child, el);
  22740. }
  22741. child = child.nextSibling;
  22742. }
  22743. // Quit define
  22744. if (nodeName === 'defs') {
  22745. this._isDefine = false;
  22746. }
  22747. else if (nodeName === 'text') {
  22748. this._isText = false;
  22749. }
  22750. };
  22751. SVGParser.prototype._parseText = function (xmlNode, parentGroup) {
  22752. if (xmlNode.nodeType === 1) {
  22753. var dx = xmlNode.getAttribute('dx') || 0;
  22754. var dy = xmlNode.getAttribute('dy') || 0;
  22755. this._textX += parseFloat(dx);
  22756. this._textY += parseFloat(dy);
  22757. }
  22758. var text = new Text({
  22759. style: {
  22760. text: xmlNode.textContent,
  22761. transformText: true
  22762. },
  22763. position: [this._textX || 0, this._textY || 0]
  22764. });
  22765. inheritStyle(parentGroup, text);
  22766. parseAttributes(xmlNode, text, this._defs);
  22767. var fontSize = text.style.fontSize;
  22768. if (fontSize && fontSize < 9) {
  22769. // PENDING
  22770. text.style.fontSize = 9;
  22771. text.scale = text.scale || [1, 1];
  22772. text.scale[0] *= fontSize / 9;
  22773. text.scale[1] *= fontSize / 9;
  22774. }
  22775. var rect = text.getBoundingRect();
  22776. this._textX += rect.width;
  22777. parentGroup.add(text);
  22778. return text;
  22779. };
  22780. var nodeParsers = {
  22781. 'g': function (xmlNode, parentGroup) {
  22782. var g = new Group();
  22783. inheritStyle(parentGroup, g);
  22784. parseAttributes(xmlNode, g, this._defs);
  22785. return g;
  22786. },
  22787. 'rect': function (xmlNode, parentGroup) {
  22788. var rect = new Rect();
  22789. inheritStyle(parentGroup, rect);
  22790. parseAttributes(xmlNode, rect, this._defs);
  22791. rect.setShape({
  22792. x: parseFloat(xmlNode.getAttribute('x') || 0),
  22793. y: parseFloat(xmlNode.getAttribute('y') || 0),
  22794. width: parseFloat(xmlNode.getAttribute('width') || 0),
  22795. height: parseFloat(xmlNode.getAttribute('height') || 0)
  22796. });
  22797. // console.log(xmlNode.getAttribute('transform'));
  22798. // console.log(rect.transform);
  22799. return rect;
  22800. },
  22801. 'circle': function (xmlNode, parentGroup) {
  22802. var circle = new Circle();
  22803. inheritStyle(parentGroup, circle);
  22804. parseAttributes(xmlNode, circle, this._defs);
  22805. circle.setShape({
  22806. cx: parseFloat(xmlNode.getAttribute('cx') || 0),
  22807. cy: parseFloat(xmlNode.getAttribute('cy') || 0),
  22808. r: parseFloat(xmlNode.getAttribute('r') || 0)
  22809. });
  22810. return circle;
  22811. },
  22812. 'line': function (xmlNode, parentGroup) {
  22813. var line = new Line();
  22814. inheritStyle(parentGroup, line);
  22815. parseAttributes(xmlNode, line, this._defs);
  22816. line.setShape({
  22817. x1: parseFloat(xmlNode.getAttribute('x1') || 0),
  22818. y1: parseFloat(xmlNode.getAttribute('y1') || 0),
  22819. x2: parseFloat(xmlNode.getAttribute('x2') || 0),
  22820. y2: parseFloat(xmlNode.getAttribute('y2') || 0)
  22821. });
  22822. return line;
  22823. },
  22824. 'ellipse': function (xmlNode, parentGroup) {
  22825. var ellipse = new Ellipse();
  22826. inheritStyle(parentGroup, ellipse);
  22827. parseAttributes(xmlNode, ellipse, this._defs);
  22828. ellipse.setShape({
  22829. cx: parseFloat(xmlNode.getAttribute('cx') || 0),
  22830. cy: parseFloat(xmlNode.getAttribute('cy') || 0),
  22831. rx: parseFloat(xmlNode.getAttribute('rx') || 0),
  22832. ry: parseFloat(xmlNode.getAttribute('ry') || 0)
  22833. });
  22834. return ellipse;
  22835. },
  22836. 'polygon': function (xmlNode, parentGroup) {
  22837. var points = xmlNode.getAttribute('points');
  22838. if (points) {
  22839. points = parsePoints(points);
  22840. }
  22841. var polygon = new Polygon({
  22842. shape: {
  22843. points: points || []
  22844. }
  22845. });
  22846. inheritStyle(parentGroup, polygon);
  22847. parseAttributes(xmlNode, polygon, this._defs);
  22848. return polygon;
  22849. },
  22850. 'polyline': function (xmlNode, parentGroup) {
  22851. var path = new Path();
  22852. inheritStyle(parentGroup, path);
  22853. parseAttributes(xmlNode, path, this._defs);
  22854. var points = xmlNode.getAttribute('points');
  22855. if (points) {
  22856. points = parsePoints(points);
  22857. }
  22858. var polyline = new Polyline({
  22859. shape: {
  22860. points: points || []
  22861. }
  22862. });
  22863. return polyline;
  22864. },
  22865. 'image': function (xmlNode, parentGroup) {
  22866. var img = new ZImage();
  22867. inheritStyle(parentGroup, img);
  22868. parseAttributes(xmlNode, img, this._defs);
  22869. img.setStyle({
  22870. image: xmlNode.getAttribute('xlink:href'),
  22871. x: xmlNode.getAttribute('x'),
  22872. y: xmlNode.getAttribute('y'),
  22873. width: xmlNode.getAttribute('width'),
  22874. height: xmlNode.getAttribute('height')
  22875. });
  22876. return img;
  22877. },
  22878. 'text': function (xmlNode, parentGroup) {
  22879. var x = xmlNode.getAttribute('x') || 0;
  22880. var y = xmlNode.getAttribute('y') || 0;
  22881. var dx = xmlNode.getAttribute('dx') || 0;
  22882. var dy = xmlNode.getAttribute('dy') || 0;
  22883. this._textX = parseFloat(x) + parseFloat(dx);
  22884. this._textY = parseFloat(y) + parseFloat(dy);
  22885. var g = new Group();
  22886. inheritStyle(parentGroup, g);
  22887. parseAttributes(xmlNode, g, this._defs);
  22888. return g;
  22889. },
  22890. 'tspan': function (xmlNode, parentGroup) {
  22891. var x = xmlNode.getAttribute('x');
  22892. var y = xmlNode.getAttribute('y');
  22893. if (x != null) {
  22894. // new offset x
  22895. this._textX = parseFloat(x);
  22896. }
  22897. if (y != null) {
  22898. // new offset y
  22899. this._textY = parseFloat(y);
  22900. }
  22901. var dx = xmlNode.getAttribute('dx') || 0;
  22902. var dy = xmlNode.getAttribute('dy') || 0;
  22903. var g = new Group();
  22904. inheritStyle(parentGroup, g);
  22905. parseAttributes(xmlNode, g, this._defs);
  22906. this._textX += dx;
  22907. this._textY += dy;
  22908. return g;
  22909. },
  22910. 'path': function (xmlNode, parentGroup) {
  22911. // TODO svg fill rule
  22912. // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule
  22913. // path.style.globalCompositeOperation = 'xor';
  22914. var d = xmlNode.getAttribute('d') || '';
  22915. // Performance sensitive.
  22916. var path = createFromString(d);
  22917. inheritStyle(parentGroup, path);
  22918. parseAttributes(xmlNode, path, this._defs);
  22919. return path;
  22920. }
  22921. };
  22922. var defineParsers = {
  22923. 'lineargradient': function (xmlNode) {
  22924. var x1 = parseInt(xmlNode.getAttribute('x1') || 0, 10);
  22925. var y1 = parseInt(xmlNode.getAttribute('y1') || 0, 10);
  22926. var x2 = parseInt(xmlNode.getAttribute('x2') || 10, 10);
  22927. var y2 = parseInt(xmlNode.getAttribute('y2') || 0, 10);
  22928. var gradient = new LinearGradient(x1, y1, x2, y2);
  22929. _parseGradientColorStops(xmlNode, gradient);
  22930. return gradient;
  22931. },
  22932. 'radialgradient': function (xmlNode) {
  22933. }
  22934. };
  22935. function _parseGradientColorStops(xmlNode, gradient) {
  22936. var stop = xmlNode.firstChild;
  22937. while (stop) {
  22938. if (stop.nodeType === 1) {
  22939. var offset = stop.getAttribute('offset');
  22940. if (offset.indexOf('%') > 0) { // percentage
  22941. offset = parseInt(offset, 10) / 100;
  22942. }
  22943. else if (offset) { // number from 0 to 1
  22944. offset = parseFloat(offset);
  22945. }
  22946. else {
  22947. offset = 0;
  22948. }
  22949. var stopColor = stop.getAttribute('stop-color') || '#000000';
  22950. gradient.addColorStop(offset, stopColor);
  22951. }
  22952. stop = stop.nextSibling;
  22953. }
  22954. }
  22955. function inheritStyle(parent, child) {
  22956. if (parent && parent.__inheritedStyle) {
  22957. if (!child.__inheritedStyle) {
  22958. child.__inheritedStyle = {};
  22959. }
  22960. defaults(child.__inheritedStyle, parent.__inheritedStyle);
  22961. }
  22962. }
  22963. function parsePoints(pointsString) {
  22964. var list = trim(pointsString).split(DILIMITER_REG);
  22965. var points = [];
  22966. for (var i = 0; i < list.length; i += 2) {
  22967. var x = parseFloat(list[i]);
  22968. var y = parseFloat(list[i + 1]);
  22969. points.push([x, y]);
  22970. }
  22971. return points;
  22972. }
  22973. var attributesMap = {
  22974. 'fill': 'fill',
  22975. 'stroke': 'stroke',
  22976. 'stroke-width': 'lineWidth',
  22977. 'opacity': 'opacity',
  22978. 'fill-opacity': 'fillOpacity',
  22979. 'stroke-opacity': 'strokeOpacity',
  22980. 'stroke-dasharray': 'lineDash',
  22981. 'stroke-dashoffset': 'lineDashOffset',
  22982. 'stroke-linecap': 'lineCap',
  22983. 'stroke-linejoin': 'lineJoin',
  22984. 'stroke-miterlimit': 'miterLimit',
  22985. 'font-family': 'fontFamily',
  22986. 'font-size': 'fontSize',
  22987. 'font-style': 'fontStyle',
  22988. 'font-weight': 'fontWeight',
  22989. 'text-align': 'textAlign',
  22990. 'alignment-baseline': 'textBaseline'
  22991. };
  22992. function parseAttributes(xmlNode, el, defs, onlyInlineStyle) {
  22993. var zrStyle = el.__inheritedStyle || {};
  22994. var isTextEl = el.type === 'text';
  22995. // TODO Shadow
  22996. if (xmlNode.nodeType === 1) {
  22997. parseTransformAttribute(xmlNode, el);
  22998. extend(zrStyle, parseStyleAttribute(xmlNode));
  22999. if (!onlyInlineStyle) {
  23000. for (var svgAttrName in attributesMap) {
  23001. if (attributesMap.hasOwnProperty(svgAttrName)) {
  23002. var attrValue = xmlNode.getAttribute(svgAttrName);
  23003. if (attrValue != null) {
  23004. zrStyle[attributesMap[svgAttrName]] = attrValue;
  23005. }
  23006. }
  23007. }
  23008. }
  23009. }
  23010. var elFillProp = isTextEl ? 'textFill' : 'fill';
  23011. var elStrokeProp = isTextEl ? 'textStroke' : 'stroke';
  23012. el.style = el.style || new Style();
  23013. var elStyle = el.style;
  23014. zrStyle.fill != null && elStyle.set(elFillProp, getPaint(zrStyle.fill, defs));
  23015. zrStyle.stroke != null && elStyle.set(elStrokeProp, getPaint(zrStyle.stroke, defs));
  23016. each$1([
  23017. 'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'
  23018. ], function (propName) {
  23019. var elPropName = (propName === 'lineWidth' && isTextEl) ? 'textStrokeWidth' : propName;
  23020. zrStyle[propName] != null && elStyle.set(elPropName, parseFloat(zrStyle[propName]));
  23021. });
  23022. if (!zrStyle.textBaseline || zrStyle.textBaseline === 'auto') {
  23023. zrStyle.textBaseline = 'alphabetic';
  23024. }
  23025. if (zrStyle.textBaseline === 'alphabetic') {
  23026. zrStyle.textBaseline = 'bottom';
  23027. }
  23028. if (zrStyle.textAlign === 'start') {
  23029. zrStyle.textAlign = 'left';
  23030. }
  23031. if (zrStyle.textAlign === 'end') {
  23032. zrStyle.textAlign = 'right';
  23033. }
  23034. each$1(['lineDashOffset', 'lineCap', 'lineJoin',
  23035. 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign', 'textBaseline'
  23036. ], function (propName) {
  23037. zrStyle[propName] != null && elStyle.set(propName, zrStyle[propName]);
  23038. });
  23039. if (zrStyle.lineDash) {
  23040. el.style.lineDash = trim(zrStyle.lineDash).split(DILIMITER_REG);
  23041. }
  23042. if (elStyle[elStrokeProp] && elStyle[elStrokeProp] !== 'none') {
  23043. // enable stroke
  23044. el[elStrokeProp] = true;
  23045. }
  23046. el.__inheritedStyle = zrStyle;
  23047. }
  23048. var urlRegex = /url\(\s*#(.*?)\)/;
  23049. function getPaint(str, defs) {
  23050. // if (str === 'none') {
  23051. // return;
  23052. // }
  23053. var urlMatch = defs && str && str.match(urlRegex);
  23054. if (urlMatch) {
  23055. var url = trim(urlMatch[1]);
  23056. var def = defs[url];
  23057. return def;
  23058. }
  23059. return str;
  23060. }
  23061. var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.e,]*)\)/g;
  23062. function parseTransformAttribute(xmlNode, node) {
  23063. var transform = xmlNode.getAttribute('transform');
  23064. if (transform) {
  23065. transform = transform.replace(/,/g, ' ');
  23066. var m = null;
  23067. var transformOps = [];
  23068. transform.replace(transformRegex, function (str, type, value) {
  23069. transformOps.push(type, value);
  23070. });
  23071. for (var i = transformOps.length - 1; i > 0; i -= 2) {
  23072. var value = transformOps[i];
  23073. var type = transformOps[i - 1];
  23074. m = m || create$1();
  23075. switch (type) {
  23076. case 'translate':
  23077. value = trim(value).split(DILIMITER_REG);
  23078. translate(m, m, [parseFloat(value[0]), parseFloat(value[1] || 0)]);
  23079. break;
  23080. case 'scale':
  23081. value = trim(value).split(DILIMITER_REG);
  23082. scale$1(m, m, [parseFloat(value[0]), parseFloat(value[1] || value[0])]);
  23083. break;
  23084. case 'rotate':
  23085. value = trim(value).split(DILIMITER_REG);
  23086. rotate(m, m, parseFloat(value[0]));
  23087. break;
  23088. case 'skew':
  23089. value = trim(value).split(DILIMITER_REG);
  23090. console.warn('Skew transform is not supported yet');
  23091. break;
  23092. case 'matrix':
  23093. var value = trim(value).split(DILIMITER_REG);
  23094. m[0] = parseFloat(value[0]);
  23095. m[1] = parseFloat(value[1]);
  23096. m[2] = parseFloat(value[2]);
  23097. m[3] = parseFloat(value[3]);
  23098. m[4] = parseFloat(value[4]);
  23099. m[5] = parseFloat(value[5]);
  23100. break;
  23101. }
  23102. }
  23103. node.setLocalTransform(m);
  23104. }
  23105. }
  23106. // Value may contain space.
  23107. var styleRegex = /([^\s:;]+)\s*:\s*([^:;]+)/g;
  23108. function parseStyleAttribute(xmlNode) {
  23109. var style = xmlNode.getAttribute('style');
  23110. var result = {};
  23111. if (!style) {
  23112. return result;
  23113. }
  23114. var styleList = {};
  23115. styleRegex.lastIndex = 0;
  23116. var styleRegResult;
  23117. while ((styleRegResult = styleRegex.exec(style)) != null) {
  23118. styleList[styleRegResult[1]] = styleRegResult[2];
  23119. }
  23120. for (var svgAttrName in attributesMap) {
  23121. if (attributesMap.hasOwnProperty(svgAttrName) && styleList[svgAttrName] != null) {
  23122. result[attributesMap[svgAttrName]] = styleList[svgAttrName];
  23123. }
  23124. }
  23125. return result;
  23126. }
  23127. /**
  23128. * @param {Array.<number>} viewBoxRect
  23129. * @param {number} width
  23130. * @param {number} height
  23131. * @return {Object} {scale, position}
  23132. */
  23133. function makeViewBoxTransform(viewBoxRect, width, height) {
  23134. var scaleX = width / viewBoxRect.width;
  23135. var scaleY = height / viewBoxRect.height;
  23136. var scale = Math.min(scaleX, scaleY);
  23137. // preserveAspectRatio 'xMidYMid'
  23138. var viewBoxScale = [scale, scale];
  23139. var viewBoxPosition = [
  23140. -(viewBoxRect.x + viewBoxRect.width / 2) * scale + width / 2,
  23141. -(viewBoxRect.y + viewBoxRect.height / 2) * scale + height / 2
  23142. ];
  23143. return {
  23144. scale: viewBoxScale,
  23145. position: viewBoxPosition
  23146. };
  23147. }
  23148. /**
  23149. * @param {string|XMLElement} xml
  23150. * @param {Object} [opt]
  23151. * @param {number} [opt.width] Default width if svg width not specified or is a percent value.
  23152. * @param {number} [opt.height] Default height if svg height not specified or is a percent value.
  23153. * @param {boolean} [opt.ignoreViewBox]
  23154. * @param {boolean} [opt.ignoreRootClip]
  23155. * @return {Object} result:
  23156. * {
  23157. * root: Group, The root of the the result tree of zrender shapes,
  23158. * width: number, the viewport width of the SVG,
  23159. * height: number, the viewport height of the SVG,
  23160. * viewBoxRect: {x, y, width, height}, the declared viewBox rect of the SVG, if exists,
  23161. * viewBoxTransform: the {scale, position} calculated by viewBox and viewport, is exists.
  23162. * }
  23163. */
  23164. /*
  23165. * Licensed to the Apache Software Foundation (ASF) under one
  23166. * or more contributor license agreements. See the NOTICE file
  23167. * distributed with this work for additional information
  23168. * regarding copyright ownership. The ASF licenses this file
  23169. * to you under the Apache License, Version 2.0 (the
  23170. * "License"); you may not use this file except in compliance
  23171. * with the License. You may obtain a copy of the License at
  23172. *
  23173. * http://www.apache.org/licenses/LICENSE-2.0
  23174. *
  23175. * Unless required by applicable law or agreed to in writing,
  23176. * software distributed under the License is distributed on an
  23177. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  23178. * KIND, either express or implied. See the License for the
  23179. * specific language governing permissions and limitations
  23180. * under the License.
  23181. */
  23182. var storage = createHashMap();
  23183. // For minimize the code size of common echarts package,
  23184. // do not put too much logic in this module.
  23185. var mapDataStorage = {
  23186. // The format of record: see `echarts.registerMap`.
  23187. // Compatible with previous `echarts.registerMap`.
  23188. registerMap: function (mapName, rawGeoJson, rawSpecialAreas) {
  23189. var records;
  23190. if (isArray(rawGeoJson)) {
  23191. records = rawGeoJson;
  23192. }
  23193. else if (rawGeoJson.svg) {
  23194. records = [{
  23195. type: 'svg',
  23196. source: rawGeoJson.svg,
  23197. specialAreas: rawGeoJson.specialAreas
  23198. }];
  23199. }
  23200. else {
  23201. // Backward compatibility.
  23202. if (rawGeoJson.geoJson && !rawGeoJson.features) {
  23203. rawSpecialAreas = rawGeoJson.specialAreas;
  23204. rawGeoJson = rawGeoJson.geoJson;
  23205. }
  23206. records = [{
  23207. type: 'geoJSON',
  23208. source: rawGeoJson,
  23209. specialAreas: rawSpecialAreas
  23210. }];
  23211. }
  23212. each$1(records, function (record) {
  23213. var type = record.type;
  23214. type === 'geoJson' && (type = record.type = 'geoJSON');
  23215. var parse = parsers[type];
  23216. if (__DEV__) {
  23217. assert$1(parse, 'Illegal map type: ' + type);
  23218. }
  23219. parse(record);
  23220. });
  23221. return storage.set(mapName, records);
  23222. },
  23223. retrieveMap: function (mapName) {
  23224. return storage.get(mapName);
  23225. }
  23226. };
  23227. var parsers = {
  23228. geoJSON: function (record) {
  23229. var source = record.source;
  23230. record.geoJSON = !isString(source)
  23231. ? source
  23232. : (typeof JSON !== 'undefined' && JSON.parse)
  23233. ? JSON.parse(source)
  23234. : (new Function('return (' + source + ');'))();
  23235. },
  23236. // Only perform parse to XML object here, which might be time
  23237. // consiming for large SVG.
  23238. // Although convert XML to zrender element is also time consiming,
  23239. // if we do it here, the clone of zrender elements has to be
  23240. // required. So we do it once for each geo instance, util real
  23241. // performance issues call for optimizing it.
  23242. svg: function (record) {
  23243. record.svgXML = parseXML(record.source);
  23244. }
  23245. };
  23246. /*
  23247. * Licensed to the Apache Software Foundation (ASF) under one
  23248. * or more contributor license agreements. See the NOTICE file
  23249. * distributed with this work for additional information
  23250. * regarding copyright ownership. The ASF licenses this file
  23251. * to you under the Apache License, Version 2.0 (the
  23252. * "License"); you may not use this file except in compliance
  23253. * with the License. You may obtain a copy of the License at
  23254. *
  23255. * http://www.apache.org/licenses/LICENSE-2.0
  23256. *
  23257. * Unless required by applicable law or agreed to in writing,
  23258. * software distributed under the License is distributed on an
  23259. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  23260. * KIND, either express or implied. See the License for the
  23261. * specific language governing permissions and limitations
  23262. * under the License.
  23263. */
  23264. var assert = assert$1;
  23265. var each = each$1;
  23266. var isFunction = isFunction$1;
  23267. var isObject = isObject$1;
  23268. var parseClassType = ComponentModel.parseClassType;
  23269. var version = '4.2.1';
  23270. var dependencies = {
  23271. zrender: '4.0.6'
  23272. };
  23273. var TEST_FRAME_REMAIN_TIME = 1;
  23274. var PRIORITY_PROCESSOR_FILTER = 1000;
  23275. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  23276. var PRIORITY_VISUAL_LAYOUT = 1000;
  23277. var PRIORITY_VISUAL_GLOBAL = 2000;
  23278. var PRIORITY_VISUAL_CHART = 3000;
  23279. var PRIORITY_VISUAL_COMPONENT = 4000;
  23280. // FIXME
  23281. // necessary?
  23282. var PRIORITY_VISUAL_BRUSH = 5000;
  23283. var PRIORITY = {
  23284. PROCESSOR: {
  23285. FILTER: PRIORITY_PROCESSOR_FILTER,
  23286. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  23287. },
  23288. VISUAL: {
  23289. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  23290. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  23291. CHART: PRIORITY_VISUAL_CHART,
  23292. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  23293. BRUSH: PRIORITY_VISUAL_BRUSH
  23294. }
  23295. };
  23296. // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  23297. // where they must not be invoked nestedly, except the only case: invoke
  23298. // dispatchAction with updateMethod "none" in main process.
  23299. // This flag is used to carry out this rule.
  23300. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  23301. var IN_MAIN_PROCESS = '__flagInMainProcess';
  23302. var OPTION_UPDATED = '__optionUpdated';
  23303. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  23304. function createRegisterEventWithLowercaseName(method) {
  23305. return function (eventName, handler, context) {
  23306. // Event name is all lowercase
  23307. eventName = eventName && eventName.toLowerCase();
  23308. Eventful.prototype[method].call(this, eventName, handler, context);
  23309. };
  23310. }
  23311. /**
  23312. * @module echarts~MessageCenter
  23313. */
  23314. function MessageCenter() {
  23315. Eventful.call(this);
  23316. }
  23317. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
  23318. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
  23319. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
  23320. mixin(MessageCenter, Eventful);
  23321. /**
  23322. * @module echarts~ECharts
  23323. */
  23324. function ECharts(dom, theme$$1, opts) {
  23325. opts = opts || {};
  23326. // Get theme by name
  23327. if (typeof theme$$1 === 'string') {
  23328. theme$$1 = themeStorage[theme$$1];
  23329. }
  23330. /**
  23331. * @type {string}
  23332. */
  23333. this.id;
  23334. /**
  23335. * Group id
  23336. * @type {string}
  23337. */
  23338. this.group;
  23339. /**
  23340. * @type {HTMLElement}
  23341. * @private
  23342. */
  23343. this._dom = dom;
  23344. var defaultRenderer = 'canvas';
  23345. if (__DEV__) {
  23346. defaultRenderer = (
  23347. typeof window === 'undefined' ? global : window
  23348. ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
  23349. }
  23350. /**
  23351. * @type {module:zrender/ZRender}
  23352. * @private
  23353. */
  23354. var zr = this._zr = init$1(dom, {
  23355. renderer: opts.renderer || defaultRenderer,
  23356. devicePixelRatio: opts.devicePixelRatio,
  23357. width: opts.width,
  23358. height: opts.height
  23359. });
  23360. /**
  23361. * Expect 60 pfs.
  23362. * @type {Function}
  23363. * @private
  23364. */
  23365. this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
  23366. var theme$$1 = clone(theme$$1);
  23367. theme$$1 && backwardCompat(theme$$1, true);
  23368. /**
  23369. * @type {Object}
  23370. * @private
  23371. */
  23372. this._theme = theme$$1;
  23373. /**
  23374. * @type {Array.<module:echarts/view/Chart>}
  23375. * @private
  23376. */
  23377. this._chartsViews = [];
  23378. /**
  23379. * @type {Object.<string, module:echarts/view/Chart>}
  23380. * @private
  23381. */
  23382. this._chartsMap = {};
  23383. /**
  23384. * @type {Array.<module:echarts/view/Component>}
  23385. * @private
  23386. */
  23387. this._componentsViews = [];
  23388. /**
  23389. * @type {Object.<string, module:echarts/view/Component>}
  23390. * @private
  23391. */
  23392. this._componentsMap = {};
  23393. /**
  23394. * @type {module:echarts/CoordinateSystem}
  23395. * @private
  23396. */
  23397. this._coordSysMgr = new CoordinateSystemManager();
  23398. /**
  23399. * @type {module:echarts/ExtensionAPI}
  23400. * @private
  23401. */
  23402. var api = this._api = createExtensionAPI(this);
  23403. // Sort on demand
  23404. function prioritySortFunc(a, b) {
  23405. return a.__prio - b.__prio;
  23406. }
  23407. sort(visualFuncs, prioritySortFunc);
  23408. sort(dataProcessorFuncs, prioritySortFunc);
  23409. /**
  23410. * @type {module:echarts/stream/Scheduler}
  23411. */
  23412. this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
  23413. Eventful.call(this, this._ecEventProcessor = new EventProcessor());
  23414. /**
  23415. * @type {module:echarts~MessageCenter}
  23416. * @private
  23417. */
  23418. this._messageCenter = new MessageCenter();
  23419. // Init mouse events
  23420. this._initEvents();
  23421. // In case some people write `window.onresize = chart.resize`
  23422. this.resize = bind(this.resize, this);
  23423. // Can't dispatch action during rendering procedure
  23424. this._pendingActions = [];
  23425. zr.animation.on('frame', this._onframe, this);
  23426. bindRenderedEvent(zr, this);
  23427. // ECharts instance can be used as value.
  23428. setAsPrimitive(this);
  23429. }
  23430. var echartsProto = ECharts.prototype;
  23431. echartsProto._onframe = function () {
  23432. if (this._disposed) {
  23433. return;
  23434. }
  23435. var scheduler = this._scheduler;
  23436. // Lazy update
  23437. if (this[OPTION_UPDATED]) {
  23438. var silent = this[OPTION_UPDATED].silent;
  23439. this[IN_MAIN_PROCESS] = true;
  23440. prepare(this);
  23441. updateMethods.update.call(this);
  23442. this[IN_MAIN_PROCESS] = false;
  23443. this[OPTION_UPDATED] = false;
  23444. flushPendingActions.call(this, silent);
  23445. triggerUpdatedEvent.call(this, silent);
  23446. }
  23447. // Avoid do both lazy update and progress in one frame.
  23448. else if (scheduler.unfinished) {
  23449. // Stream progress.
  23450. var remainTime = TEST_FRAME_REMAIN_TIME;
  23451. var ecModel = this._model;
  23452. var api = this._api;
  23453. scheduler.unfinished = false;
  23454. do {
  23455. var startTime = +new Date();
  23456. scheduler.performSeriesTasks(ecModel);
  23457. // Currently dataProcessorFuncs do not check threshold.
  23458. scheduler.performDataProcessorTasks(ecModel);
  23459. updateStreamModes(this, ecModel);
  23460. // Do not update coordinate system here. Because that coord system update in
  23461. // each frame is not a good user experience. So we follow the rule that
  23462. // the extent of the coordinate system is determin in the first frame (the
  23463. // frame is executed immedietely after task reset.
  23464. // this._coordSysMgr.update(ecModel, api);
  23465. // console.log('--- ec frame visual ---', remainTime);
  23466. scheduler.performVisualTasks(ecModel);
  23467. renderSeries(this, this._model, api, 'remain');
  23468. remainTime -= (+new Date() - startTime);
  23469. }
  23470. while (remainTime > 0 && scheduler.unfinished);
  23471. // Call flush explicitly for trigger finished event.
  23472. if (!scheduler.unfinished) {
  23473. this._zr.flush();
  23474. }
  23475. // Else, zr flushing be ensue within the same frame,
  23476. // because zr flushing is after onframe event.
  23477. }
  23478. };
  23479. /**
  23480. * @return {HTMLElement}
  23481. */
  23482. echartsProto.getDom = function () {
  23483. return this._dom;
  23484. };
  23485. /**
  23486. * @return {module:zrender~ZRender}
  23487. */
  23488. echartsProto.getZr = function () {
  23489. return this._zr;
  23490. };
  23491. /**
  23492. * Usage:
  23493. * chart.setOption(option, notMerge, lazyUpdate);
  23494. * chart.setOption(option, {
  23495. * notMerge: ...,
  23496. * lazyUpdate: ...,
  23497. * silent: ...
  23498. * });
  23499. *
  23500. * @param {Object} option
  23501. * @param {Object|boolean} [opts] opts or notMerge.
  23502. * @param {boolean} [opts.notMerge=false]
  23503. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  23504. */
  23505. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  23506. if (__DEV__) {
  23507. assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
  23508. }
  23509. var silent;
  23510. if (isObject(notMerge)) {
  23511. lazyUpdate = notMerge.lazyUpdate;
  23512. silent = notMerge.silent;
  23513. notMerge = notMerge.notMerge;
  23514. }
  23515. this[IN_MAIN_PROCESS] = true;
  23516. if (!this._model || notMerge) {
  23517. var optionManager = new OptionManager(this._api);
  23518. var theme$$1 = this._theme;
  23519. var ecModel = this._model = new GlobalModel(null, null, theme$$1, optionManager);
  23520. ecModel.scheduler = this._scheduler;
  23521. ecModel.init(null, null, theme$$1, optionManager);
  23522. }
  23523. this._model.setOption(option, optionPreprocessorFuncs);
  23524. if (lazyUpdate) {
  23525. this[OPTION_UPDATED] = {silent: silent};
  23526. this[IN_MAIN_PROCESS] = false;
  23527. }
  23528. else {
  23529. prepare(this);
  23530. updateMethods.update.call(this);
  23531. // Ensure zr refresh sychronously, and then pixel in canvas can be
  23532. // fetched after `setOption`.
  23533. this._zr.flush();
  23534. this[OPTION_UPDATED] = false;
  23535. this[IN_MAIN_PROCESS] = false;
  23536. flushPendingActions.call(this, silent);
  23537. triggerUpdatedEvent.call(this, silent);
  23538. }
  23539. };
  23540. /**
  23541. * @DEPRECATED
  23542. */
  23543. echartsProto.setTheme = function () {
  23544. console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  23545. };
  23546. /**
  23547. * @return {module:echarts/model/Global}
  23548. */
  23549. echartsProto.getModel = function () {
  23550. return this._model;
  23551. };
  23552. /**
  23553. * @return {Object}
  23554. */
  23555. echartsProto.getOption = function () {
  23556. return this._model && this._model.getOption();
  23557. };
  23558. /**
  23559. * @return {number}
  23560. */
  23561. echartsProto.getWidth = function () {
  23562. return this._zr.getWidth();
  23563. };
  23564. /**
  23565. * @return {number}
  23566. */
  23567. echartsProto.getHeight = function () {
  23568. return this._zr.getHeight();
  23569. };
  23570. /**
  23571. * @return {number}
  23572. */
  23573. echartsProto.getDevicePixelRatio = function () {
  23574. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  23575. };
  23576. /**
  23577. * Get canvas which has all thing rendered
  23578. * @param {Object} opts
  23579. * @param {string} [opts.backgroundColor]
  23580. * @return {string}
  23581. */
  23582. echartsProto.getRenderedCanvas = function (opts) {
  23583. if (!env$1.canvasSupported) {
  23584. return;
  23585. }
  23586. opts = opts || {};
  23587. opts.pixelRatio = opts.pixelRatio || 1;
  23588. opts.backgroundColor = opts.backgroundColor
  23589. || this._model.get('backgroundColor');
  23590. var zr = this._zr;
  23591. // var list = zr.storage.getDisplayList();
  23592. // Stop animations
  23593. // Never works before in init animation, so remove it.
  23594. // zrUtil.each(list, function (el) {
  23595. // el.stopAnimation(true);
  23596. // });
  23597. return zr.painter.getRenderedCanvas(opts);
  23598. };
  23599. /**
  23600. * Get svg data url
  23601. * @return {string}
  23602. */
  23603. echartsProto.getSvgDataUrl = function () {
  23604. if (!env$1.svgSupported) {
  23605. return;
  23606. }
  23607. var zr = this._zr;
  23608. var list = zr.storage.getDisplayList();
  23609. // Stop animations
  23610. each$1(list, function (el) {
  23611. el.stopAnimation(true);
  23612. });
  23613. return zr.painter.pathToDataUrl();
  23614. };
  23615. /**
  23616. * @return {string}
  23617. * @param {Object} opts
  23618. * @param {string} [opts.type='png']
  23619. * @param {string} [opts.pixelRatio=1]
  23620. * @param {string} [opts.backgroundColor]
  23621. * @param {string} [opts.excludeComponents]
  23622. */
  23623. echartsProto.getDataURL = function (opts) {
  23624. opts = opts || {};
  23625. var excludeComponents = opts.excludeComponents;
  23626. var ecModel = this._model;
  23627. var excludesComponentViews = [];
  23628. var self = this;
  23629. each(excludeComponents, function (componentType) {
  23630. ecModel.eachComponent({
  23631. mainType: componentType
  23632. }, function (component) {
  23633. var view = self._componentsMap[component.__viewId];
  23634. if (!view.group.ignore) {
  23635. excludesComponentViews.push(view);
  23636. view.group.ignore = true;
  23637. }
  23638. });
  23639. });
  23640. var url = this._zr.painter.getType() === 'svg'
  23641. ? this.getSvgDataUrl()
  23642. : this.getRenderedCanvas(opts).toDataURL(
  23643. 'image/' + (opts && opts.type || 'png')
  23644. );
  23645. each(excludesComponentViews, function (view) {
  23646. view.group.ignore = false;
  23647. });
  23648. return url;
  23649. };
  23650. /**
  23651. * @return {string}
  23652. * @param {Object} opts
  23653. * @param {string} [opts.type='png']
  23654. * @param {string} [opts.pixelRatio=1]
  23655. * @param {string} [opts.backgroundColor]
  23656. */
  23657. echartsProto.getConnectedDataURL = function (opts) {
  23658. if (!env$1.canvasSupported) {
  23659. return;
  23660. }
  23661. var groupId = this.group;
  23662. var mathMin = Math.min;
  23663. var mathMax = Math.max;
  23664. var MAX_NUMBER = Infinity;
  23665. if (connectedGroups[groupId]) {
  23666. var left = MAX_NUMBER;
  23667. var top = MAX_NUMBER;
  23668. var right = -MAX_NUMBER;
  23669. var bottom = -MAX_NUMBER;
  23670. var canvasList = [];
  23671. var dpr = (opts && opts.pixelRatio) || 1;
  23672. each$1(instances, function (chart, id) {
  23673. if (chart.group === groupId) {
  23674. var canvas = chart.getRenderedCanvas(
  23675. clone(opts)
  23676. );
  23677. var boundingRect = chart.getDom().getBoundingClientRect();
  23678. left = mathMin(boundingRect.left, left);
  23679. top = mathMin(boundingRect.top, top);
  23680. right = mathMax(boundingRect.right, right);
  23681. bottom = mathMax(boundingRect.bottom, bottom);
  23682. canvasList.push({
  23683. dom: canvas,
  23684. left: boundingRect.left,
  23685. top: boundingRect.top
  23686. });
  23687. }
  23688. });
  23689. left *= dpr;
  23690. top *= dpr;
  23691. right *= dpr;
  23692. bottom *= dpr;
  23693. var width = right - left;
  23694. var height = bottom - top;
  23695. var targetCanvas = createCanvas();
  23696. targetCanvas.width = width;
  23697. targetCanvas.height = height;
  23698. var zr = init$1(targetCanvas);
  23699. each(canvasList, function (item) {
  23700. var img = new ZImage({
  23701. style: {
  23702. x: item.left * dpr - left,
  23703. y: item.top * dpr - top,
  23704. image: item.dom
  23705. }
  23706. });
  23707. zr.add(img);
  23708. });
  23709. zr.refreshImmediately();
  23710. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  23711. }
  23712. else {
  23713. return this.getDataURL(opts);
  23714. }
  23715. };
  23716. /**
  23717. * Convert from logical coordinate system to pixel coordinate system.
  23718. * See CoordinateSystem#convertToPixel.
  23719. * @param {string|Object} finder
  23720. * If string, e.g., 'geo', means {geoIndex: 0}.
  23721. * If Object, could contain some of these properties below:
  23722. * {
  23723. * seriesIndex / seriesId / seriesName,
  23724. * geoIndex / geoId, geoName,
  23725. * bmapIndex / bmapId / bmapName,
  23726. * xAxisIndex / xAxisId / xAxisName,
  23727. * yAxisIndex / yAxisId / yAxisName,
  23728. * gridIndex / gridId / gridName,
  23729. * ... (can be extended)
  23730. * }
  23731. * @param {Array|number} value
  23732. * @return {Array|number} result
  23733. */
  23734. echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel');
  23735. /**
  23736. * Convert from pixel coordinate system to logical coordinate system.
  23737. * See CoordinateSystem#convertFromPixel.
  23738. * @param {string|Object} finder
  23739. * If string, e.g., 'geo', means {geoIndex: 0}.
  23740. * If Object, could contain some of these properties below:
  23741. * {
  23742. * seriesIndex / seriesId / seriesName,
  23743. * geoIndex / geoId / geoName,
  23744. * bmapIndex / bmapId / bmapName,
  23745. * xAxisIndex / xAxisId / xAxisName,
  23746. * yAxisIndex / yAxisId / yAxisName
  23747. * gridIndex / gridId / gridName,
  23748. * ... (can be extended)
  23749. * }
  23750. * @param {Array|number} value
  23751. * @return {Array|number} result
  23752. */
  23753. echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel');
  23754. function doConvertPixel(methodName, finder, value) {
  23755. var ecModel = this._model;
  23756. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  23757. var result;
  23758. finder = parseFinder(ecModel, finder);
  23759. for (var i = 0; i < coordSysList.length; i++) {
  23760. var coordSys = coordSysList[i];
  23761. if (coordSys[methodName]
  23762. && (result = coordSys[methodName](ecModel, finder, value)) != null
  23763. ) {
  23764. return result;
  23765. }
  23766. }
  23767. if (__DEV__) {
  23768. console.warn(
  23769. 'No coordinate system that supports ' + methodName + ' found by the given finder.'
  23770. );
  23771. }
  23772. }
  23773. /**
  23774. * Is the specified coordinate systems or components contain the given pixel point.
  23775. * @param {string|Object} finder
  23776. * If string, e.g., 'geo', means {geoIndex: 0}.
  23777. * If Object, could contain some of these properties below:
  23778. * {
  23779. * seriesIndex / seriesId / seriesName,
  23780. * geoIndex / geoId / geoName,
  23781. * bmapIndex / bmapId / bmapName,
  23782. * xAxisIndex / xAxisId / xAxisName,
  23783. * yAxisIndex / yAxisId / yAxisName,
  23784. * gridIndex / gridId / gridName,
  23785. * ... (can be extended)
  23786. * }
  23787. * @param {Array|number} value
  23788. * @return {boolean} result
  23789. */
  23790. echartsProto.containPixel = function (finder, value) {
  23791. var ecModel = this._model;
  23792. var result;
  23793. finder = parseFinder(ecModel, finder);
  23794. each$1(finder, function (models, key) {
  23795. key.indexOf('Models') >= 0 && each$1(models, function (model) {
  23796. var coordSys = model.coordinateSystem;
  23797. if (coordSys && coordSys.containPoint) {
  23798. result |= !!coordSys.containPoint(value);
  23799. }
  23800. else if (key === 'seriesModels') {
  23801. var view = this._chartsMap[model.__viewId];
  23802. if (view && view.containPoint) {
  23803. result |= view.containPoint(value, model);
  23804. }
  23805. else {
  23806. if (__DEV__) {
  23807. console.warn(key + ': ' + (view
  23808. ? 'The found component do not support containPoint.'
  23809. : 'No view mapping to the found component.'
  23810. ));
  23811. }
  23812. }
  23813. }
  23814. else {
  23815. if (__DEV__) {
  23816. console.warn(key + ': containPoint is not supported');
  23817. }
  23818. }
  23819. }, this);
  23820. }, this);
  23821. return !!result;
  23822. };
  23823. /**
  23824. * Get visual from series or data.
  23825. * @param {string|Object} finder
  23826. * If string, e.g., 'series', means {seriesIndex: 0}.
  23827. * If Object, could contain some of these properties below:
  23828. * {
  23829. * seriesIndex / seriesId / seriesName,
  23830. * dataIndex / dataIndexInside
  23831. * }
  23832. * If dataIndex is not specified, series visual will be fetched,
  23833. * but not data item visual.
  23834. * If all of seriesIndex, seriesId, seriesName are not specified,
  23835. * visual will be fetched from first series.
  23836. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  23837. */
  23838. echartsProto.getVisual = function (finder, visualType) {
  23839. var ecModel = this._model;
  23840. finder = parseFinder(ecModel, finder, {defaultMainType: 'series'});
  23841. var seriesModel = finder.seriesModel;
  23842. if (__DEV__) {
  23843. if (!seriesModel) {
  23844. console.warn('There is no specified seires model');
  23845. }
  23846. }
  23847. var data = seriesModel.getData();
  23848. var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
  23849. ? finder.dataIndexInside
  23850. : finder.hasOwnProperty('dataIndex')
  23851. ? data.indexOfRawIndex(finder.dataIndex)
  23852. : null;
  23853. return dataIndexInside != null
  23854. ? data.getItemVisual(dataIndexInside, visualType)
  23855. : data.getVisual(visualType);
  23856. };
  23857. /**
  23858. * Get view of corresponding component model
  23859. * @param {module:echarts/model/Component} componentModel
  23860. * @return {module:echarts/view/Component}
  23861. */
  23862. echartsProto.getViewOfComponentModel = function (componentModel) {
  23863. return this._componentsMap[componentModel.__viewId];
  23864. };
  23865. /**
  23866. * Get view of corresponding series model
  23867. * @param {module:echarts/model/Series} seriesModel
  23868. * @return {module:echarts/view/Chart}
  23869. */
  23870. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  23871. return this._chartsMap[seriesModel.__viewId];
  23872. };
  23873. var updateMethods = {
  23874. prepareAndUpdate: function (payload) {
  23875. prepare(this);
  23876. updateMethods.update.call(this, payload);
  23877. },
  23878. /**
  23879. * @param {Object} payload
  23880. * @private
  23881. */
  23882. update: function (payload) {
  23883. // console.profile && console.profile('update');
  23884. var ecModel = this._model;
  23885. var api = this._api;
  23886. var zr = this._zr;
  23887. var coordSysMgr = this._coordSysMgr;
  23888. var scheduler = this._scheduler;
  23889. // update before setOption
  23890. if (!ecModel) {
  23891. return;
  23892. }
  23893. scheduler.restoreData(ecModel, payload);
  23894. scheduler.performSeriesTasks(ecModel);
  23895. // TODO
  23896. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  23897. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  23898. // Create new coordinate system each update
  23899. // In LineView may save the old coordinate system and use it to get the orignal point
  23900. coordSysMgr.create(ecModel, api);
  23901. scheduler.performDataProcessorTasks(ecModel, payload);
  23902. // Current stream render is not supported in data process. So we can update
  23903. // stream modes after data processing, where the filtered data is used to
  23904. // deteming whether use progressive rendering.
  23905. updateStreamModes(this, ecModel);
  23906. // We update stream modes before coordinate system updated, then the modes info
  23907. // can be fetched when coord sys updating (consider the barGrid extent fix). But
  23908. // the drawback is the full coord info can not be fetched. Fortunately this full
  23909. // coord is not requied in stream mode updater currently.
  23910. coordSysMgr.update(ecModel, api);
  23911. clearColorPalette(ecModel);
  23912. scheduler.performVisualTasks(ecModel, payload);
  23913. render(this, ecModel, api, payload);
  23914. // Set background
  23915. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  23916. // In IE8
  23917. if (!env$1.canvasSupported) {
  23918. var colorArr = parse(backgroundColor);
  23919. backgroundColor = stringify(colorArr, 'rgb');
  23920. if (colorArr[3] === 0) {
  23921. backgroundColor = 'transparent';
  23922. }
  23923. }
  23924. else {
  23925. zr.setBackgroundColor(backgroundColor);
  23926. }
  23927. performPostUpdateFuncs(ecModel, api);
  23928. // console.profile && console.profileEnd('update');
  23929. },
  23930. /**
  23931. * @param {Object} payload
  23932. * @private
  23933. */
  23934. updateTransform: function (payload) {
  23935. var ecModel = this._model;
  23936. var ecIns = this;
  23937. var api = this._api;
  23938. // update before setOption
  23939. if (!ecModel) {
  23940. return;
  23941. }
  23942. // ChartView.markUpdateMethod(payload, 'updateTransform');
  23943. var componentDirtyList = [];
  23944. ecModel.eachComponent(function (componentType, componentModel) {
  23945. var componentView = ecIns.getViewOfComponentModel(componentModel);
  23946. if (componentView && componentView.__alive) {
  23947. if (componentView.updateTransform) {
  23948. var result = componentView.updateTransform(componentModel, ecModel, api, payload);
  23949. result && result.update && componentDirtyList.push(componentView);
  23950. }
  23951. else {
  23952. componentDirtyList.push(componentView);
  23953. }
  23954. }
  23955. });
  23956. var seriesDirtyMap = createHashMap();
  23957. ecModel.eachSeries(function (seriesModel) {
  23958. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  23959. if (chartView.updateTransform) {
  23960. var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
  23961. result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
  23962. }
  23963. else {
  23964. seriesDirtyMap.set(seriesModel.uid, 1);
  23965. }
  23966. });
  23967. clearColorPalette(ecModel);
  23968. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  23969. // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
  23970. this._scheduler.performVisualTasks(
  23971. ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap}
  23972. );
  23973. // Currently, not call render of components. Geo render cost a lot.
  23974. // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
  23975. renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap);
  23976. performPostUpdateFuncs(ecModel, this._api);
  23977. },
  23978. /**
  23979. * @param {Object} payload
  23980. * @private
  23981. */
  23982. updateView: function (payload) {
  23983. var ecModel = this._model;
  23984. // update before setOption
  23985. if (!ecModel) {
  23986. return;
  23987. }
  23988. Chart.markUpdateMethod(payload, 'updateView');
  23989. clearColorPalette(ecModel);
  23990. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  23991. this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
  23992. render(this, this._model, this._api, payload);
  23993. performPostUpdateFuncs(ecModel, this._api);
  23994. },
  23995. /**
  23996. * @param {Object} payload
  23997. * @private
  23998. */
  23999. updateVisual: function (payload) {
  24000. updateMethods.update.call(this, payload);
  24001. // var ecModel = this._model;
  24002. // // update before setOption
  24003. // if (!ecModel) {
  24004. // return;
  24005. // }
  24006. // ChartView.markUpdateMethod(payload, 'updateVisual');
  24007. // clearColorPalette(ecModel);
  24008. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  24009. // this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true});
  24010. // render(this, this._model, this._api, payload);
  24011. // performPostUpdateFuncs(ecModel, this._api);
  24012. },
  24013. /**
  24014. * @param {Object} payload
  24015. * @private
  24016. */
  24017. updateLayout: function (payload) {
  24018. updateMethods.update.call(this, payload);
  24019. // var ecModel = this._model;
  24020. // // update before setOption
  24021. // if (!ecModel) {
  24022. // return;
  24023. // }
  24024. // ChartView.markUpdateMethod(payload, 'updateLayout');
  24025. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  24026. // // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
  24027. // this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
  24028. // render(this, this._model, this._api, payload);
  24029. // performPostUpdateFuncs(ecModel, this._api);
  24030. }
  24031. };
  24032. function prepare(ecIns) {
  24033. var ecModel = ecIns._model;
  24034. var scheduler = ecIns._scheduler;
  24035. scheduler.restorePipelines(ecModel);
  24036. scheduler.prepareStageTasks();
  24037. prepareView(ecIns, 'component', ecModel, scheduler);
  24038. prepareView(ecIns, 'chart', ecModel, scheduler);
  24039. scheduler.plan();
  24040. }
  24041. /**
  24042. * @private
  24043. */
  24044. function updateDirectly(ecIns, method, payload, mainType, subType) {
  24045. var ecModel = ecIns._model;
  24046. // broadcast
  24047. if (!mainType) {
  24048. // FIXME
  24049. // Chart will not be update directly here, except set dirty.
  24050. // But there is no such scenario now.
  24051. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  24052. return;
  24053. }
  24054. var query = {};
  24055. query[mainType + 'Id'] = payload[mainType + 'Id'];
  24056. query[mainType + 'Index'] = payload[mainType + 'Index'];
  24057. query[mainType + 'Name'] = payload[mainType + 'Name'];
  24058. var condition = {mainType: mainType, query: query};
  24059. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  24060. var excludeSeriesId = payload.excludeSeriesId;
  24061. if (excludeSeriesId != null) {
  24062. excludeSeriesId = createHashMap(normalizeToArray(excludeSeriesId));
  24063. }
  24064. // If dispatchAction before setOption, do nothing.
  24065. ecModel && ecModel.eachComponent(condition, function (model) {
  24066. if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) {
  24067. callView(ecIns[
  24068. mainType === 'series' ? '_chartsMap' : '_componentsMap'
  24069. ][model.__viewId]);
  24070. }
  24071. }, ecIns);
  24072. function callView(view) {
  24073. view && view.__alive && view[method] && view[method](
  24074. view.__model, ecModel, ecIns._api, payload
  24075. );
  24076. }
  24077. }
  24078. /**
  24079. * Resize the chart
  24080. * @param {Object} opts
  24081. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  24082. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  24083. * @param {boolean} [opts.silent=false]
  24084. */
  24085. echartsProto.resize = function (opts) {
  24086. if (__DEV__) {
  24087. assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
  24088. }
  24089. this._zr.resize(opts);
  24090. var ecModel = this._model;
  24091. // Resize loading effect
  24092. this._loadingFX && this._loadingFX.resize();
  24093. if (!ecModel) {
  24094. return;
  24095. }
  24096. var optionChanged = ecModel.resetOption('media');
  24097. var silent = opts && opts.silent;
  24098. this[IN_MAIN_PROCESS] = true;
  24099. optionChanged && prepare(this);
  24100. updateMethods.update.call(this);
  24101. this[IN_MAIN_PROCESS] = false;
  24102. flushPendingActions.call(this, silent);
  24103. triggerUpdatedEvent.call(this, silent);
  24104. };
  24105. function updateStreamModes(ecIns, ecModel) {
  24106. var chartsMap = ecIns._chartsMap;
  24107. var scheduler = ecIns._scheduler;
  24108. ecModel.eachSeries(function (seriesModel) {
  24109. scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
  24110. });
  24111. }
  24112. /**
  24113. * Show loading effect
  24114. * @param {string} [name='default']
  24115. * @param {Object} [cfg]
  24116. */
  24117. echartsProto.showLoading = function (name, cfg) {
  24118. if (isObject(name)) {
  24119. cfg = name;
  24120. name = '';
  24121. }
  24122. name = name || 'default';
  24123. this.hideLoading();
  24124. if (!loadingEffects[name]) {
  24125. if (__DEV__) {
  24126. console.warn('Loading effects ' + name + ' not exists.');
  24127. }
  24128. return;
  24129. }
  24130. var el = loadingEffects[name](this._api, cfg);
  24131. var zr = this._zr;
  24132. this._loadingFX = el;
  24133. zr.add(el);
  24134. };
  24135. /**
  24136. * Hide loading effect
  24137. */
  24138. echartsProto.hideLoading = function () {
  24139. this._loadingFX && this._zr.remove(this._loadingFX);
  24140. this._loadingFX = null;
  24141. };
  24142. /**
  24143. * @param {Object} eventObj
  24144. * @return {Object}
  24145. */
  24146. echartsProto.makeActionFromEvent = function (eventObj) {
  24147. var payload = extend({}, eventObj);
  24148. payload.type = eventActionMap[eventObj.type];
  24149. return payload;
  24150. };
  24151. /**
  24152. * @pubilc
  24153. * @param {Object} payload
  24154. * @param {string} [payload.type] Action type
  24155. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  24156. * @param {boolean} [opt.silent=false] Whether trigger events.
  24157. * @param {boolean} [opt.flush=undefined]
  24158. * true: Flush immediately, and then pixel in canvas can be fetched
  24159. * immediately. Caution: it might affect performance.
  24160. * false: Not not flush.
  24161. * undefined: Auto decide whether perform flush.
  24162. */
  24163. echartsProto.dispatchAction = function (payload, opt) {
  24164. if (!isObject(opt)) {
  24165. opt = {silent: !!opt};
  24166. }
  24167. if (!actions[payload.type]) {
  24168. return;
  24169. }
  24170. // Avoid dispatch action before setOption. Especially in `connect`.
  24171. if (!this._model) {
  24172. return;
  24173. }
  24174. // May dispatchAction in rendering procedure
  24175. if (this[IN_MAIN_PROCESS]) {
  24176. this._pendingActions.push(payload);
  24177. return;
  24178. }
  24179. doDispatchAction.call(this, payload, opt.silent);
  24180. if (opt.flush) {
  24181. this._zr.flush(true);
  24182. }
  24183. else if (opt.flush !== false && env$1.browser.weChat) {
  24184. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  24185. // hang when sliding page (on touch event), which cause that zr does not
  24186. // refresh util user interaction finished, which is not expected.
  24187. // But `dispatchAction` may be called too frequently when pan on touch
  24188. // screen, which impacts performance if do not throttle them.
  24189. this._throttledZrFlush();
  24190. }
  24191. flushPendingActions.call(this, opt.silent);
  24192. triggerUpdatedEvent.call(this, opt.silent);
  24193. };
  24194. function doDispatchAction(payload, silent) {
  24195. var payloadType = payload.type;
  24196. var escapeConnect = payload.escapeConnect;
  24197. var actionWrap = actions[payloadType];
  24198. var actionInfo = actionWrap.actionInfo;
  24199. var cptType = (actionInfo.update || 'update').split(':');
  24200. var updateMethod = cptType.pop();
  24201. cptType = cptType[0] != null && parseClassType(cptType[0]);
  24202. this[IN_MAIN_PROCESS] = true;
  24203. var payloads = [payload];
  24204. var batched = false;
  24205. // Batch action
  24206. if (payload.batch) {
  24207. batched = true;
  24208. payloads = map(payload.batch, function (item) {
  24209. item = defaults(extend({}, item), payload);
  24210. item.batch = null;
  24211. return item;
  24212. });
  24213. }
  24214. var eventObjBatch = [];
  24215. var eventObj;
  24216. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  24217. each(payloads, function (batchItem) {
  24218. // Action can specify the event by return it.
  24219. eventObj = actionWrap.action(batchItem, this._model, this._api);
  24220. // Emit event outside
  24221. eventObj = eventObj || extend({}, batchItem);
  24222. // Convert type to eventType
  24223. eventObj.type = actionInfo.event || eventObj.type;
  24224. eventObjBatch.push(eventObj);
  24225. // light update does not perform data process, layout and visual.
  24226. if (isHighDown) {
  24227. // method, payload, mainType, subType
  24228. updateDirectly(this, updateMethod, batchItem, 'series');
  24229. }
  24230. else if (cptType) {
  24231. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  24232. }
  24233. }, this);
  24234. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  24235. // Still dirty
  24236. if (this[OPTION_UPDATED]) {
  24237. // FIXME Pass payload ?
  24238. prepare(this);
  24239. updateMethods.update.call(this, payload);
  24240. this[OPTION_UPDATED] = false;
  24241. }
  24242. else {
  24243. updateMethods[updateMethod].call(this, payload);
  24244. }
  24245. }
  24246. // Follow the rule of action batch
  24247. if (batched) {
  24248. eventObj = {
  24249. type: actionInfo.event || payloadType,
  24250. escapeConnect: escapeConnect,
  24251. batch: eventObjBatch
  24252. };
  24253. }
  24254. else {
  24255. eventObj = eventObjBatch[0];
  24256. }
  24257. this[IN_MAIN_PROCESS] = false;
  24258. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  24259. }
  24260. function flushPendingActions(silent) {
  24261. var pendingActions = this._pendingActions;
  24262. while (pendingActions.length) {
  24263. var payload = pendingActions.shift();
  24264. doDispatchAction.call(this, payload, silent);
  24265. }
  24266. }
  24267. function triggerUpdatedEvent(silent) {
  24268. !silent && this.trigger('updated');
  24269. }
  24270. /**
  24271. * Event `rendered` is triggered when zr
  24272. * rendered. It is useful for realtime
  24273. * snapshot (reflect animation).
  24274. *
  24275. * Event `finished` is triggered when:
  24276. * (1) zrender rendering finished.
  24277. * (2) initial animation finished.
  24278. * (3) progressive rendering finished.
  24279. * (4) no pending action.
  24280. * (5) no delayed setOption needs to be processed.
  24281. */
  24282. function bindRenderedEvent(zr, ecIns) {
  24283. zr.on('rendered', function () {
  24284. ecIns.trigger('rendered');
  24285. // The `finished` event should not be triggered repeatly,
  24286. // so it should only be triggered when rendering indeed happend
  24287. // in zrender. (Consider the case that dipatchAction is keep
  24288. // triggering when mouse move).
  24289. if (
  24290. // Although zr is dirty if initial animation is not finished
  24291. // and this checking is called on frame, we also check
  24292. // animation finished for robustness.
  24293. zr.animation.isFinished()
  24294. && !ecIns[OPTION_UPDATED]
  24295. && !ecIns._scheduler.unfinished
  24296. && !ecIns._pendingActions.length
  24297. ) {
  24298. ecIns.trigger('finished');
  24299. }
  24300. });
  24301. }
  24302. /**
  24303. * @param {Object} params
  24304. * @param {number} params.seriesIndex
  24305. * @param {Array|TypedArray} params.data
  24306. */
  24307. echartsProto.appendData = function (params) {
  24308. var seriesIndex = params.seriesIndex;
  24309. var ecModel = this.getModel();
  24310. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  24311. if (__DEV__) {
  24312. assert(params.data && seriesModel);
  24313. }
  24314. seriesModel.appendData(params);
  24315. // Note: `appendData` does not support that update extent of coordinate
  24316. // system, util some scenario require that. In the expected usage of
  24317. // `appendData`, the initial extent of coordinate system should better
  24318. // be fixed by axis `min`/`max` setting or initial data, otherwise if
  24319. // the extent changed while `appendData`, the location of the painted
  24320. // graphic elements have to be changed, which make the usage of
  24321. // `appendData` meaningless.
  24322. this._scheduler.unfinished = true;
  24323. };
  24324. /**
  24325. * Register event
  24326. * @method
  24327. */
  24328. echartsProto.on = createRegisterEventWithLowercaseName('on');
  24329. echartsProto.off = createRegisterEventWithLowercaseName('off');
  24330. echartsProto.one = createRegisterEventWithLowercaseName('one');
  24331. /**
  24332. * Prepare view instances of charts and components
  24333. * @param {module:echarts/model/Global} ecModel
  24334. * @private
  24335. */
  24336. function prepareView(ecIns, type, ecModel, scheduler) {
  24337. var isComponent = type === 'component';
  24338. var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
  24339. var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
  24340. var zr = ecIns._zr;
  24341. var api = ecIns._api;
  24342. for (var i = 0; i < viewList.length; i++) {
  24343. viewList[i].__alive = false;
  24344. }
  24345. isComponent
  24346. ? ecModel.eachComponent(function (componentType, model) {
  24347. componentType !== 'series' && doPrepare(model);
  24348. })
  24349. : ecModel.eachSeries(doPrepare);
  24350. function doPrepare(model) {
  24351. // Consider: id same and type changed.
  24352. var viewId = '_ec_' + model.id + '_' + model.type;
  24353. var view = viewMap[viewId];
  24354. if (!view) {
  24355. var classType = parseClassType(model.type);
  24356. var Clazz = isComponent
  24357. ? Component$1.getClass(classType.main, classType.sub)
  24358. : Chart.getClass(classType.sub);
  24359. if (__DEV__) {
  24360. assert(Clazz, classType.sub + ' does not exist.');
  24361. }
  24362. view = new Clazz();
  24363. view.init(ecModel, api);
  24364. viewMap[viewId] = view;
  24365. viewList.push(view);
  24366. zr.add(view.group);
  24367. }
  24368. model.__viewId = view.__id = viewId;
  24369. view.__alive = true;
  24370. view.__model = model;
  24371. view.group.__ecComponentInfo = {
  24372. mainType: model.mainType,
  24373. index: model.componentIndex
  24374. };
  24375. !isComponent && scheduler.prepareView(view, model, ecModel, api);
  24376. }
  24377. for (var i = 0; i < viewList.length;) {
  24378. var view = viewList[i];
  24379. if (!view.__alive) {
  24380. !isComponent && view.renderTask.dispose();
  24381. zr.remove(view.group);
  24382. view.dispose(ecModel, api);
  24383. viewList.splice(i, 1);
  24384. delete viewMap[view.__id];
  24385. view.__id = view.group.__ecComponentInfo = null;
  24386. }
  24387. else {
  24388. i++;
  24389. }
  24390. }
  24391. }
  24392. // /**
  24393. // * Encode visual infomation from data after data processing
  24394. // *
  24395. // * @param {module:echarts/model/Global} ecModel
  24396. // * @param {object} layout
  24397. // * @param {boolean} [layoutFilter] `true`: only layout,
  24398. // * `false`: only not layout,
  24399. // * `null`/`undefined`: all.
  24400. // * @param {string} taskBaseTag
  24401. // * @private
  24402. // */
  24403. // function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
  24404. // each(visualFuncs, function (visual, index) {
  24405. // var isLayout = visual.isLayout;
  24406. // if (layoutFilter == null
  24407. // || (layoutFilter === false && !isLayout)
  24408. // || (layoutFilter === true && isLayout)
  24409. // ) {
  24410. // visual.func(ecModel, api, payload);
  24411. // }
  24412. // });
  24413. // }
  24414. function clearColorPalette(ecModel) {
  24415. ecModel.clearColorPalette();
  24416. ecModel.eachSeries(function (seriesModel) {
  24417. seriesModel.clearColorPalette();
  24418. });
  24419. }
  24420. function render(ecIns, ecModel, api, payload) {
  24421. renderComponents(ecIns, ecModel, api, payload);
  24422. each(ecIns._chartsViews, function (chart) {
  24423. chart.__alive = false;
  24424. });
  24425. renderSeries(ecIns, ecModel, api, payload);
  24426. // Remove groups of unrendered charts
  24427. each(ecIns._chartsViews, function (chart) {
  24428. if (!chart.__alive) {
  24429. chart.remove(ecModel, api);
  24430. }
  24431. });
  24432. }
  24433. function renderComponents(ecIns, ecModel, api, payload, dirtyList) {
  24434. each(dirtyList || ecIns._componentsViews, function (componentView) {
  24435. var componentModel = componentView.__model;
  24436. componentView.render(componentModel, ecModel, api, payload);
  24437. updateZ(componentModel, componentView);
  24438. });
  24439. }
  24440. /**
  24441. * Render each chart and component
  24442. * @private
  24443. */
  24444. function renderSeries(ecIns, ecModel, api, payload, dirtyMap) {
  24445. // Render all charts
  24446. var scheduler = ecIns._scheduler;
  24447. var unfinished;
  24448. ecModel.eachSeries(function (seriesModel) {
  24449. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  24450. chartView.__alive = true;
  24451. var renderTask = chartView.renderTask;
  24452. scheduler.updatePayload(renderTask, payload);
  24453. if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
  24454. renderTask.dirty();
  24455. }
  24456. unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask));
  24457. chartView.group.silent = !!seriesModel.get('silent');
  24458. updateZ(seriesModel, chartView);
  24459. updateBlend(seriesModel, chartView);
  24460. });
  24461. scheduler.unfinished |= unfinished;
  24462. // If use hover layer
  24463. updateHoverLayerStatus(ecIns._zr, ecModel);
  24464. // Add aria
  24465. aria(ecIns._zr.dom, ecModel);
  24466. }
  24467. function performPostUpdateFuncs(ecModel, api) {
  24468. each(postUpdateFuncs, function (func) {
  24469. func(ecModel, api);
  24470. });
  24471. }
  24472. var MOUSE_EVENT_NAMES = [
  24473. 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
  24474. 'mousedown', 'mouseup', 'globalout', 'contextmenu'
  24475. ];
  24476. /**
  24477. * @private
  24478. */
  24479. echartsProto._initEvents = function () {
  24480. each(MOUSE_EVENT_NAMES, function (eveName) {
  24481. var handler = function (e) {
  24482. var ecModel = this.getModel();
  24483. var el = e.target;
  24484. var params;
  24485. var isGlobalOut = eveName === 'globalout';
  24486. // no e.target when 'globalout'.
  24487. if (isGlobalOut) {
  24488. params = {};
  24489. }
  24490. else if (el && el.dataIndex != null) {
  24491. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  24492. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType, el) || {};
  24493. }
  24494. // If element has custom eventData of components
  24495. else if (el && el.eventData) {
  24496. params = extend({}, el.eventData);
  24497. }
  24498. // Contract: if params prepared in mouse event,
  24499. // these properties must be specified:
  24500. // {
  24501. // componentType: string (component main type)
  24502. // componentIndex: number
  24503. // }
  24504. // Otherwise event query can not work.
  24505. if (params) {
  24506. var componentType = params.componentType;
  24507. var componentIndex = params.componentIndex;
  24508. // Special handling for historic reason: when trigger by
  24509. // markLine/markPoint/markArea, the componentType is
  24510. // 'markLine'/'markPoint'/'markArea', but we should better
  24511. // enable them to be queried by seriesIndex, since their
  24512. // option is set in each series.
  24513. if (componentType === 'markLine'
  24514. || componentType === 'markPoint'
  24515. || componentType === 'markArea'
  24516. ) {
  24517. componentType = 'series';
  24518. componentIndex = params.seriesIndex;
  24519. }
  24520. var model = componentType && componentIndex != null
  24521. && ecModel.getComponent(componentType, componentIndex);
  24522. var view = model && this[
  24523. model.mainType === 'series' ? '_chartsMap' : '_componentsMap'
  24524. ][model.__viewId];
  24525. if (__DEV__) {
  24526. // `event.componentType` and `event[componentTpype + 'Index']` must not
  24527. // be missed, otherwise there is no way to distinguish source component.
  24528. // See `dataFormat.getDataParams`.
  24529. if (!isGlobalOut && !(model && view)) {
  24530. console.warn('model or view can not be found by params');
  24531. }
  24532. }
  24533. params.event = e;
  24534. params.type = eveName;
  24535. this._ecEventProcessor.eventInfo = {
  24536. targetEl: el,
  24537. packedEvent: params,
  24538. model: model,
  24539. view: view
  24540. };
  24541. this.trigger(eveName, params);
  24542. }
  24543. };
  24544. // Consider that some component (like tooltip, brush, ...)
  24545. // register zr event handler, but user event handler might
  24546. // do anything, such as call `setOption` or `dispatchAction`,
  24547. // which probably update any of the content and probably
  24548. // cause problem if it is called previous other inner handlers.
  24549. handler.zrEventfulCallAtLast = true;
  24550. this._zr.on(eveName, handler, this);
  24551. }, this);
  24552. each(eventActionMap, function (actionType, eventType) {
  24553. this._messageCenter.on(eventType, function (event) {
  24554. this.trigger(eventType, event);
  24555. }, this);
  24556. }, this);
  24557. };
  24558. /**
  24559. * @return {boolean}
  24560. */
  24561. echartsProto.isDisposed = function () {
  24562. return this._disposed;
  24563. };
  24564. /**
  24565. * Clear
  24566. */
  24567. echartsProto.clear = function () {
  24568. this.setOption({ series: [] }, true);
  24569. };
  24570. /**
  24571. * Dispose instance
  24572. */
  24573. echartsProto.dispose = function () {
  24574. if (this._disposed) {
  24575. if (__DEV__) {
  24576. console.warn('Instance ' + this.id + ' has been disposed');
  24577. }
  24578. return;
  24579. }
  24580. this._disposed = true;
  24581. setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
  24582. var api = this._api;
  24583. var ecModel = this._model;
  24584. each(this._componentsViews, function (component) {
  24585. component.dispose(ecModel, api);
  24586. });
  24587. each(this._chartsViews, function (chart) {
  24588. chart.dispose(ecModel, api);
  24589. });
  24590. // Dispose after all views disposed
  24591. this._zr.dispose();
  24592. delete instances[this.id];
  24593. };
  24594. mixin(ECharts, Eventful);
  24595. function updateHoverLayerStatus(zr, ecModel) {
  24596. var storage = zr.storage;
  24597. var elCount = 0;
  24598. storage.traverse(function (el) {
  24599. if (!el.isGroup) {
  24600. elCount++;
  24601. }
  24602. });
  24603. if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) {
  24604. storage.traverse(function (el) {
  24605. if (!el.isGroup) {
  24606. // Don't switch back.
  24607. el.useHoverLayer = true;
  24608. }
  24609. });
  24610. }
  24611. }
  24612. /**
  24613. * Update chart progressive and blend.
  24614. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  24615. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  24616. */
  24617. function updateBlend(seriesModel, chartView) {
  24618. var blendMode = seriesModel.get('blendMode') || null;
  24619. if (__DEV__) {
  24620. if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') {
  24621. console.warn('Only canvas support blendMode');
  24622. }
  24623. }
  24624. chartView.group.traverse(function (el) {
  24625. // FIXME marker and other components
  24626. if (!el.isGroup) {
  24627. // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
  24628. if (el.style.blend !== blendMode) {
  24629. el.setStyle('blend', blendMode);
  24630. }
  24631. }
  24632. if (el.eachPendingDisplayable) {
  24633. el.eachPendingDisplayable(function (displayable) {
  24634. displayable.setStyle('blend', blendMode);
  24635. });
  24636. }
  24637. });
  24638. }
  24639. /**
  24640. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  24641. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  24642. */
  24643. function updateZ(model, view) {
  24644. var z = model.get('z');
  24645. var zlevel = model.get('zlevel');
  24646. // Set z and zlevel
  24647. view.group.traverse(function (el) {
  24648. if (el.type !== 'group') {
  24649. z != null && (el.z = z);
  24650. zlevel != null && (el.zlevel = zlevel);
  24651. }
  24652. });
  24653. }
  24654. function createExtensionAPI(ecInstance) {
  24655. var coordSysMgr = ecInstance._coordSysMgr;
  24656. return extend(new ExtensionAPI(ecInstance), {
  24657. // Inject methods
  24658. getCoordinateSystems: bind(
  24659. coordSysMgr.getCoordinateSystems, coordSysMgr
  24660. ),
  24661. getComponentByElement: function (el) {
  24662. while (el) {
  24663. var modelInfo = el.__ecComponentInfo;
  24664. if (modelInfo != null) {
  24665. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  24666. }
  24667. el = el.parent;
  24668. }
  24669. }
  24670. });
  24671. }
  24672. /**
  24673. * @class
  24674. * Usage of query:
  24675. * `chart.on('click', query, handler);`
  24676. * The `query` can be:
  24677. * + The component type query string, only `mainType` or `mainType.subType`,
  24678. * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
  24679. * + The component query object, like:
  24680. * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
  24681. * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
  24682. * + The data query object, like:
  24683. * `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.
  24684. * + The other query object (cmponent customized query), like:
  24685. * `{element: 'some'}` (only available in custom series).
  24686. *
  24687. * Caveat: If a prop in the `query` object is `null/undefined`, it is the
  24688. * same as there is no such prop in the `query` object.
  24689. */
  24690. function EventProcessor() {
  24691. // These info required: targetEl, packedEvent, model, view
  24692. this.eventInfo;
  24693. }
  24694. EventProcessor.prototype = {
  24695. constructor: EventProcessor,
  24696. normalizeQuery: function (query) {
  24697. var cptQuery = {};
  24698. var dataQuery = {};
  24699. var otherQuery = {};
  24700. // `query` is `mainType` or `mainType.subType` of component.
  24701. if (isString(query)) {
  24702. var condCptType = parseClassType(query);
  24703. // `.main` and `.sub` may be ''.
  24704. cptQuery.mainType = condCptType.main || null;
  24705. cptQuery.subType = condCptType.sub || null;
  24706. }
  24707. // `query` is an object, convert to {mainType, index, name, id}.
  24708. else {
  24709. // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
  24710. // can not be used in `compomentModel.filterForExposedEvent`.
  24711. var suffixes = ['Index', 'Name', 'Id'];
  24712. var dataKeys = {name: 1, dataIndex: 1, dataType: 1};
  24713. each$1(query, function (val, key) {
  24714. var reserved = false;
  24715. for (var i = 0; i < suffixes.length; i++) {
  24716. var propSuffix = suffixes[i];
  24717. var suffixPos = key.lastIndexOf(propSuffix);
  24718. if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
  24719. var mainType = key.slice(0, suffixPos);
  24720. // Consider `dataIndex`.
  24721. if (mainType !== 'data') {
  24722. cptQuery.mainType = mainType;
  24723. cptQuery[propSuffix.toLowerCase()] = val;
  24724. reserved = true;
  24725. }
  24726. }
  24727. }
  24728. if (dataKeys.hasOwnProperty(key)) {
  24729. dataQuery[key] = val;
  24730. reserved = true;
  24731. }
  24732. if (!reserved) {
  24733. otherQuery[key] = val;
  24734. }
  24735. });
  24736. }
  24737. return {
  24738. cptQuery: cptQuery,
  24739. dataQuery: dataQuery,
  24740. otherQuery: otherQuery
  24741. };
  24742. },
  24743. filter: function (eventType, query, args) {
  24744. // They should be assigned before each trigger call.
  24745. var eventInfo = this.eventInfo;
  24746. if (!eventInfo) {
  24747. return true;
  24748. }
  24749. var targetEl = eventInfo.targetEl;
  24750. var packedEvent = eventInfo.packedEvent;
  24751. var model = eventInfo.model;
  24752. var view = eventInfo.view;
  24753. // For event like 'globalout'.
  24754. if (!model || !view) {
  24755. return true;
  24756. }
  24757. var cptQuery = query.cptQuery;
  24758. var dataQuery = query.dataQuery;
  24759. return check(cptQuery, model, 'mainType')
  24760. && check(cptQuery, model, 'subType')
  24761. && check(cptQuery, model, 'index', 'componentIndex')
  24762. && check(cptQuery, model, 'name')
  24763. && check(cptQuery, model, 'id')
  24764. && check(dataQuery, packedEvent, 'name')
  24765. && check(dataQuery, packedEvent, 'dataIndex')
  24766. && check(dataQuery, packedEvent, 'dataType')
  24767. && (!view.filterForExposedEvent || view.filterForExposedEvent(
  24768. eventType, query.otherQuery, targetEl, packedEvent
  24769. ));
  24770. function check(query, host, prop, propOnHost) {
  24771. return query[prop] == null || host[propOnHost || prop] === query[prop];
  24772. }
  24773. },
  24774. afterTrigger: function () {
  24775. // Make sure the eventInfo wont be used in next trigger.
  24776. this.eventInfo = null;
  24777. }
  24778. };
  24779. /**
  24780. * @type {Object} key: actionType.
  24781. * @inner
  24782. */
  24783. var actions = {};
  24784. /**
  24785. * Map eventType to actionType
  24786. * @type {Object}
  24787. */
  24788. var eventActionMap = {};
  24789. /**
  24790. * Data processor functions of each stage
  24791. * @type {Array.<Object.<string, Function>>}
  24792. * @inner
  24793. */
  24794. var dataProcessorFuncs = [];
  24795. /**
  24796. * @type {Array.<Function>}
  24797. * @inner
  24798. */
  24799. var optionPreprocessorFuncs = [];
  24800. /**
  24801. * @type {Array.<Function>}
  24802. * @inner
  24803. */
  24804. var postUpdateFuncs = [];
  24805. /**
  24806. * Visual encoding functions of each stage
  24807. * @type {Array.<Object.<string, Function>>}
  24808. */
  24809. var visualFuncs = [];
  24810. /**
  24811. * Theme storage
  24812. * @type {Object.<key, Object>}
  24813. */
  24814. var themeStorage = {};
  24815. /**
  24816. * Loading effects
  24817. */
  24818. var loadingEffects = {};
  24819. var instances = {};
  24820. var connectedGroups = {};
  24821. var idBase = new Date() - 0;
  24822. var groupIdBase = new Date() - 0;
  24823. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  24824. function enableConnect(chart) {
  24825. var STATUS_PENDING = 0;
  24826. var STATUS_UPDATING = 1;
  24827. var STATUS_UPDATED = 2;
  24828. var STATUS_KEY = '__connectUpdateStatus';
  24829. function updateConnectedChartsStatus(charts, status) {
  24830. for (var i = 0; i < charts.length; i++) {
  24831. var otherChart = charts[i];
  24832. otherChart[STATUS_KEY] = status;
  24833. }
  24834. }
  24835. each(eventActionMap, function (actionType, eventType) {
  24836. chart._messageCenter.on(eventType, function (event) {
  24837. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  24838. if (event && event.escapeConnect) {
  24839. return;
  24840. }
  24841. var action = chart.makeActionFromEvent(event);
  24842. var otherCharts = [];
  24843. each(instances, function (otherChart) {
  24844. if (otherChart !== chart && otherChart.group === chart.group) {
  24845. otherCharts.push(otherChart);
  24846. }
  24847. });
  24848. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  24849. each(otherCharts, function (otherChart) {
  24850. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  24851. otherChart.dispatchAction(action);
  24852. }
  24853. });
  24854. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  24855. }
  24856. });
  24857. });
  24858. }
  24859. /**
  24860. * @param {HTMLElement} dom
  24861. * @param {Object} [theme]
  24862. * @param {Object} opts
  24863. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  24864. * @param {string} [opts.renderer] Currently only 'canvas' is supported.
  24865. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  24866. * Can be 'auto' (the same as null/undefined)
  24867. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  24868. * Can be 'auto' (the same as null/undefined)
  24869. */
  24870. function init(dom, theme$$1, opts) {
  24871. if (__DEV__) {
  24872. // Check version
  24873. if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
  24874. throw new Error(
  24875. 'zrender/src ' + version$1
  24876. + ' is too old for ECharts ' + version
  24877. + '. Current version need ZRender '
  24878. + dependencies.zrender + '+'
  24879. );
  24880. }
  24881. if (!dom) {
  24882. throw new Error('Initialize failed: invalid dom.');
  24883. }
  24884. }
  24885. var existInstance = getInstanceByDom(dom);
  24886. if (existInstance) {
  24887. if (__DEV__) {
  24888. console.warn('There is a chart instance already initialized on the dom.');
  24889. }
  24890. return existInstance;
  24891. }
  24892. if (__DEV__) {
  24893. if (isDom(dom)
  24894. && dom.nodeName.toUpperCase() !== 'CANVAS'
  24895. && (
  24896. (!dom.clientWidth && (!opts || opts.width == null))
  24897. || (!dom.clientHeight && (!opts || opts.height == null))
  24898. )
  24899. ) {
  24900. console.warn('Can\'t get dom width or height');
  24901. }
  24902. }
  24903. var chart = new ECharts(dom, theme$$1, opts);
  24904. chart.id = 'ec_' + idBase++;
  24905. instances[chart.id] = chart;
  24906. setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
  24907. enableConnect(chart);
  24908. return chart;
  24909. }
  24910. /**
  24911. * @return {string|Array.<module:echarts~ECharts>} groupId
  24912. */
  24913. function connect(groupId) {
  24914. // Is array of charts
  24915. if (isArray(groupId)) {
  24916. var charts = groupId;
  24917. groupId = null;
  24918. // If any chart has group
  24919. each(charts, function (chart) {
  24920. if (chart.group != null) {
  24921. groupId = chart.group;
  24922. }
  24923. });
  24924. groupId = groupId || ('g_' + groupIdBase++);
  24925. each(charts, function (chart) {
  24926. chart.group = groupId;
  24927. });
  24928. }
  24929. connectedGroups[groupId] = true;
  24930. return groupId;
  24931. }
  24932. /**
  24933. * @DEPRECATED
  24934. * @return {string} groupId
  24935. */
  24936. function disConnect(groupId) {
  24937. connectedGroups[groupId] = false;
  24938. }
  24939. /**
  24940. * @return {string} groupId
  24941. */
  24942. var disconnect = disConnect;
  24943. /**
  24944. * Dispose a chart instance
  24945. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  24946. */
  24947. function dispose(chart) {
  24948. if (typeof chart === 'string') {
  24949. chart = instances[chart];
  24950. }
  24951. else if (!(chart instanceof ECharts)) {
  24952. // Try to treat as dom
  24953. chart = getInstanceByDom(chart);
  24954. }
  24955. if ((chart instanceof ECharts) && !chart.isDisposed()) {
  24956. chart.dispose();
  24957. }
  24958. }
  24959. /**
  24960. * @param {HTMLElement} dom
  24961. * @return {echarts~ECharts}
  24962. */
  24963. function getInstanceByDom(dom) {
  24964. return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)];
  24965. }
  24966. /**
  24967. * @param {string} key
  24968. * @return {echarts~ECharts}
  24969. */
  24970. function getInstanceById(key) {
  24971. return instances[key];
  24972. }
  24973. /**
  24974. * Register theme
  24975. */
  24976. function registerTheme(name, theme$$1) {
  24977. themeStorage[name] = theme$$1;
  24978. }
  24979. /**
  24980. * Register option preprocessor
  24981. * @param {Function} preprocessorFunc
  24982. */
  24983. function registerPreprocessor(preprocessorFunc) {
  24984. optionPreprocessorFuncs.push(preprocessorFunc);
  24985. }
  24986. /**
  24987. * @param {number} [priority=1000]
  24988. * @param {Object|Function} processor
  24989. */
  24990. function registerProcessor(priority, processor) {
  24991. normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER);
  24992. }
  24993. /**
  24994. * Register postUpdater
  24995. * @param {Function} postUpdateFunc
  24996. */
  24997. function registerPostUpdate(postUpdateFunc) {
  24998. postUpdateFuncs.push(postUpdateFunc);
  24999. }
  25000. /**
  25001. * Usage:
  25002. * registerAction('someAction', 'someEvent', function () { ... });
  25003. * registerAction('someAction', function () { ... });
  25004. * registerAction(
  25005. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  25006. * function () { ... }
  25007. * );
  25008. *
  25009. * @param {(string|Object)} actionInfo
  25010. * @param {string} actionInfo.type
  25011. * @param {string} [actionInfo.event]
  25012. * @param {string} [actionInfo.update]
  25013. * @param {string} [eventName]
  25014. * @param {Function} action
  25015. */
  25016. function registerAction(actionInfo, eventName, action) {
  25017. if (typeof eventName === 'function') {
  25018. action = eventName;
  25019. eventName = '';
  25020. }
  25021. var actionType = isObject(actionInfo)
  25022. ? actionInfo.type
  25023. : ([actionInfo, actionInfo = {
  25024. event: eventName
  25025. }][0]);
  25026. // Event name is all lowercase
  25027. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  25028. eventName = actionInfo.event;
  25029. // Validate action type and event name.
  25030. assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  25031. if (!actions[actionType]) {
  25032. actions[actionType] = {action: action, actionInfo: actionInfo};
  25033. }
  25034. eventActionMap[eventName] = actionType;
  25035. }
  25036. /**
  25037. * @param {string} type
  25038. * @param {*} CoordinateSystem
  25039. */
  25040. function registerCoordinateSystem(type, CoordinateSystem$$1) {
  25041. CoordinateSystemManager.register(type, CoordinateSystem$$1);
  25042. }
  25043. /**
  25044. * Get dimensions of specified coordinate system.
  25045. * @param {string} type
  25046. * @return {Array.<string|Object>}
  25047. */
  25048. function getCoordinateSystemDimensions(type) {
  25049. var coordSysCreator = CoordinateSystemManager.get(type);
  25050. if (coordSysCreator) {
  25051. return coordSysCreator.getDimensionsInfo
  25052. ? coordSysCreator.getDimensionsInfo()
  25053. : coordSysCreator.dimensions.slice();
  25054. }
  25055. }
  25056. /**
  25057. * Layout is a special stage of visual encoding
  25058. * Most visual encoding like color are common for different chart
  25059. * But each chart has it's own layout algorithm
  25060. *
  25061. * @param {number} [priority=1000]
  25062. * @param {Function} layoutTask
  25063. */
  25064. function registerLayout(priority, layoutTask) {
  25065. normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');
  25066. }
  25067. /**
  25068. * @param {number} [priority=3000]
  25069. * @param {module:echarts/stream/Task} visualTask
  25070. */
  25071. function registerVisual(priority, visualTask) {
  25072. normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');
  25073. }
  25074. /**
  25075. * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset}
  25076. */
  25077. function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {
  25078. if (isFunction(priority) || isObject(priority)) {
  25079. fn = priority;
  25080. priority = defaultPriority;
  25081. }
  25082. if (__DEV__) {
  25083. if (isNaN(priority) || priority == null) {
  25084. throw new Error('Illegal priority');
  25085. }
  25086. // Check duplicate
  25087. each(targetList, function (wrap) {
  25088. assert(wrap.__raw !== fn);
  25089. });
  25090. }
  25091. var stageHandler = Scheduler.wrapStageHandler(fn, visualType);
  25092. stageHandler.__prio = priority;
  25093. stageHandler.__raw = fn;
  25094. targetList.push(stageHandler);
  25095. return stageHandler;
  25096. }
  25097. /**
  25098. * @param {string} name
  25099. */
  25100. function registerLoading(name, loadingFx) {
  25101. loadingEffects[name] = loadingFx;
  25102. }
  25103. /**
  25104. * @param {Object} opts
  25105. * @param {string} [superClass]
  25106. */
  25107. function extendComponentModel(opts/*, superClass*/) {
  25108. // var Clazz = ComponentModel;
  25109. // if (superClass) {
  25110. // var classType = parseClassType(superClass);
  25111. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  25112. // }
  25113. return ComponentModel.extend(opts);
  25114. }
  25115. /**
  25116. * @param {Object} opts
  25117. * @param {string} [superClass]
  25118. */
  25119. function extendComponentView(opts/*, superClass*/) {
  25120. // var Clazz = ComponentView;
  25121. // if (superClass) {
  25122. // var classType = parseClassType(superClass);
  25123. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  25124. // }
  25125. return Component$1.extend(opts);
  25126. }
  25127. /**
  25128. * @param {Object} opts
  25129. * @param {string} [superClass]
  25130. */
  25131. function extendSeriesModel(opts/*, superClass*/) {
  25132. // var Clazz = SeriesModel;
  25133. // if (superClass) {
  25134. // superClass = 'series.' + superClass.replace('series.', '');
  25135. // var classType = parseClassType(superClass);
  25136. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  25137. // }
  25138. return SeriesModel.extend(opts);
  25139. }
  25140. /**
  25141. * @param {Object} opts
  25142. * @param {string} [superClass]
  25143. */
  25144. function extendChartView(opts/*, superClass*/) {
  25145. // var Clazz = ChartView;
  25146. // if (superClass) {
  25147. // superClass = superClass.replace('series.', '');
  25148. // var classType = parseClassType(superClass);
  25149. // Clazz = ChartView.getClass(classType.main, true);
  25150. // }
  25151. return Chart.extend(opts);
  25152. }
  25153. /**
  25154. * ZRender need a canvas context to do measureText.
  25155. * But in node environment canvas may be created by node-canvas.
  25156. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  25157. *
  25158. * Be careful of using it in the browser.
  25159. *
  25160. * @param {Function} creator
  25161. * @example
  25162. * var Canvas = require('canvas');
  25163. * var echarts = require('echarts');
  25164. * echarts.setCanvasCreator(function () {
  25165. * // Small size is enough.
  25166. * return new Canvas(32, 32);
  25167. * });
  25168. */
  25169. function setCanvasCreator(creator) {
  25170. $override('createCanvas', creator);
  25171. }
  25172. /**
  25173. * @param {string} mapName
  25174. * @param {Array.<Object>|Object|string} geoJson
  25175. * @param {Object} [specialAreas]
  25176. *
  25177. * @example GeoJSON
  25178. * $.get('USA.json', function (geoJson) {
  25179. * echarts.registerMap('USA', geoJson);
  25180. * // Or
  25181. * echarts.registerMap('USA', {
  25182. * geoJson: geoJson,
  25183. * specialAreas: {}
  25184. * })
  25185. * });
  25186. *
  25187. * $.get('airport.svg', function (svg) {
  25188. * echarts.registerMap('airport', {
  25189. * svg: svg
  25190. * }
  25191. * });
  25192. *
  25193. * echarts.registerMap('eu', [
  25194. * {svg: eu-topographic.svg},
  25195. * {geoJSON: eu.json}
  25196. * ])
  25197. */
  25198. function registerMap(mapName, geoJson, specialAreas) {
  25199. mapDataStorage.registerMap(mapName, geoJson, specialAreas);
  25200. }
  25201. /**
  25202. * @param {string} mapName
  25203. * @return {Object}
  25204. */
  25205. function getMap(mapName) {
  25206. // For backward compatibility, only return the first one.
  25207. var records = mapDataStorage.retrieveMap(mapName);
  25208. return records && records[0] && {
  25209. geoJson: records[0].geoJSON,
  25210. specialAreas: records[0].specialAreas
  25211. };
  25212. }
  25213. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  25214. registerPreprocessor(backwardCompat);
  25215. registerProcessor(PRIORITY_PROCESSOR_STATISTIC, dataStack);
  25216. registerLoading('default', loadingDefault);
  25217. // Default actions
  25218. registerAction({
  25219. type: 'highlight',
  25220. event: 'highlight',
  25221. update: 'highlight'
  25222. }, noop);
  25223. registerAction({
  25224. type: 'downplay',
  25225. event: 'downplay',
  25226. update: 'downplay'
  25227. }, noop);
  25228. // Default theme
  25229. registerTheme('light', lightTheme);
  25230. registerTheme('dark', theme);
  25231. // For backward compatibility, where the namespace `dataTool` will
  25232. // be mounted on `echarts` is the extension `dataTool` is imported.
  25233. var dataTool = {};
  25234. /*
  25235. * Licensed to the Apache Software Foundation (ASF) under one
  25236. * or more contributor license agreements. See the NOTICE file
  25237. * distributed with this work for additional information
  25238. * regarding copyright ownership. The ASF licenses this file
  25239. * to you under the Apache License, Version 2.0 (the
  25240. * "License"); you may not use this file except in compliance
  25241. * with the License. You may obtain a copy of the License at
  25242. *
  25243. * http://www.apache.org/licenses/LICENSE-2.0
  25244. *
  25245. * Unless required by applicable law or agreed to in writing,
  25246. * software distributed under the License is distributed on an
  25247. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  25248. * KIND, either express or implied. See the License for the
  25249. * specific language governing permissions and limitations
  25250. * under the License.
  25251. */
  25252. function defaultKeyGetter(item) {
  25253. return item;
  25254. }
  25255. /**
  25256. * @param {Array} oldArr
  25257. * @param {Array} newArr
  25258. * @param {Function} oldKeyGetter
  25259. * @param {Function} newKeyGetter
  25260. * @param {Object} [context] Can be visited by this.context in callback.
  25261. */
  25262. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  25263. this._old = oldArr;
  25264. this._new = newArr;
  25265. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  25266. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  25267. this.context = context;
  25268. }
  25269. DataDiffer.prototype = {
  25270. constructor: DataDiffer,
  25271. /**
  25272. * Callback function when add a data
  25273. */
  25274. add: function (func) {
  25275. this._add = func;
  25276. return this;
  25277. },
  25278. /**
  25279. * Callback function when update a data
  25280. */
  25281. update: function (func) {
  25282. this._update = func;
  25283. return this;
  25284. },
  25285. /**
  25286. * Callback function when remove a data
  25287. */
  25288. remove: function (func) {
  25289. this._remove = func;
  25290. return this;
  25291. },
  25292. execute: function () {
  25293. var oldArr = this._old;
  25294. var newArr = this._new;
  25295. var oldDataIndexMap = {};
  25296. var newDataIndexMap = {};
  25297. var oldDataKeyArr = [];
  25298. var newDataKeyArr = [];
  25299. var i;
  25300. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  25301. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
  25302. // Travel by inverted order to make sure order consistency
  25303. // when duplicate keys exists (consider newDataIndex.pop() below).
  25304. // For performance consideration, these code below do not look neat.
  25305. for (i = 0; i < oldArr.length; i++) {
  25306. var key = oldDataKeyArr[i];
  25307. var idx = newDataIndexMap[key];
  25308. // idx can never be empty array here. see 'set null' logic below.
  25309. if (idx != null) {
  25310. // Consider there is duplicate key (for example, use dataItem.name as key).
  25311. // We should make sure every item in newArr and oldArr can be visited.
  25312. var len = idx.length;
  25313. if (len) {
  25314. len === 1 && (newDataIndexMap[key] = null);
  25315. idx = idx.unshift();
  25316. }
  25317. else {
  25318. newDataIndexMap[key] = null;
  25319. }
  25320. this._update && this._update(idx, i);
  25321. }
  25322. else {
  25323. this._remove && this._remove(i);
  25324. }
  25325. }
  25326. for (var i = 0; i < newDataKeyArr.length; i++) {
  25327. var key = newDataKeyArr[i];
  25328. if (newDataIndexMap.hasOwnProperty(key)) {
  25329. var idx = newDataIndexMap[key];
  25330. if (idx == null) {
  25331. continue;
  25332. }
  25333. // idx can never be empty array here. see 'set null' logic above.
  25334. if (!idx.length) {
  25335. this._add && this._add(idx);
  25336. }
  25337. else {
  25338. for (var j = 0, len = idx.length; j < len; j++) {
  25339. this._add && this._add(idx[j]);
  25340. }
  25341. }
  25342. }
  25343. }
  25344. }
  25345. };
  25346. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  25347. for (var i = 0; i < arr.length; i++) {
  25348. // Add prefix to avoid conflict with Object.prototype.
  25349. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  25350. var existence = map[key];
  25351. if (existence == null) {
  25352. keyArr.push(key);
  25353. map[key] = i;
  25354. }
  25355. else {
  25356. if (!existence.length) {
  25357. map[key] = existence = [existence];
  25358. }
  25359. existence.push(i);
  25360. }
  25361. }
  25362. }
  25363. /*
  25364. * Licensed to the Apache Software Foundation (ASF) under one
  25365. * or more contributor license agreements. See the NOTICE file
  25366. * distributed with this work for additional information
  25367. * regarding copyright ownership. The ASF licenses this file
  25368. * to you under the Apache License, Version 2.0 (the
  25369. * "License"); you may not use this file except in compliance
  25370. * with the License. You may obtain a copy of the License at
  25371. *
  25372. * http://www.apache.org/licenses/LICENSE-2.0
  25373. *
  25374. * Unless required by applicable law or agreed to in writing,
  25375. * software distributed under the License is distributed on an
  25376. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  25377. * KIND, either express or implied. See the License for the
  25378. * specific language governing permissions and limitations
  25379. * under the License.
  25380. */
  25381. var OTHER_DIMENSIONS = createHashMap([
  25382. 'tooltip', 'label', 'itemName', 'itemId', 'seriesName'
  25383. ]);
  25384. function summarizeDimensions(data) {
  25385. var summary = {};
  25386. var encode = summary.encode = {};
  25387. var notExtraCoordDimMap = createHashMap();
  25388. var defaultedLabel = [];
  25389. var defaultedTooltip = [];
  25390. each$1(data.dimensions, function (dimName) {
  25391. var dimItem = data.getDimensionInfo(dimName);
  25392. var coordDim = dimItem.coordDim;
  25393. if (coordDim) {
  25394. if (__DEV__) {
  25395. assert$1(OTHER_DIMENSIONS.get(coordDim) == null);
  25396. }
  25397. var coordDimArr = encode[coordDim];
  25398. if (!encode.hasOwnProperty(coordDim)) {
  25399. coordDimArr = encode[coordDim] = [];
  25400. }
  25401. coordDimArr[dimItem.coordDimIndex] = dimName;
  25402. if (!dimItem.isExtraCoord) {
  25403. notExtraCoordDimMap.set(coordDim, 1);
  25404. // Use the last coord dim (and label friendly) as default label,
  25405. // because when dataset is used, it is hard to guess which dimension
  25406. // can be value dimension. If both show x, y on label is not look good,
  25407. // and conventionally y axis is focused more.
  25408. if (mayLabelDimType(dimItem.type)) {
  25409. defaultedLabel[0] = dimName;
  25410. }
  25411. }
  25412. if (dimItem.defaultTooltip) {
  25413. defaultedTooltip.push(dimName);
  25414. }
  25415. }
  25416. OTHER_DIMENSIONS.each(function (v, otherDim) {
  25417. var otherDimArr = encode[otherDim];
  25418. if (!encode.hasOwnProperty(otherDim)) {
  25419. otherDimArr = encode[otherDim] = [];
  25420. }
  25421. var dimIndex = dimItem.otherDims[otherDim];
  25422. if (dimIndex != null && dimIndex !== false) {
  25423. otherDimArr[dimIndex] = dimItem.name;
  25424. }
  25425. });
  25426. });
  25427. var dataDimsOnCoord = [];
  25428. var encodeFirstDimNotExtra = {};
  25429. notExtraCoordDimMap.each(function (v, coordDim) {
  25430. var dimArr = encode[coordDim];
  25431. // ??? FIXME extra coord should not be set in dataDimsOnCoord.
  25432. // But should fix the case that radar axes: simplify the logic
  25433. // of `completeDimension`, remove `extraPrefix`.
  25434. encodeFirstDimNotExtra[coordDim] = dimArr[0];
  25435. // Not necessary to remove duplicate, because a data
  25436. // dim canot on more than one coordDim.
  25437. dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);
  25438. });
  25439. summary.dataDimsOnCoord = dataDimsOnCoord;
  25440. summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;
  25441. var encodeLabel = encode.label;
  25442. // FIXME `encode.label` is not recommanded, because formatter can not be set
  25443. // in this way. Use label.formatter instead. May be remove this approach someday.
  25444. if (encodeLabel && encodeLabel.length) {
  25445. defaultedLabel = encodeLabel.slice();
  25446. }
  25447. var encodeTooltip = encode.tooltip;
  25448. if (encodeTooltip && encodeTooltip.length) {
  25449. defaultedTooltip = encodeTooltip.slice();
  25450. }
  25451. else if (!defaultedTooltip.length) {
  25452. defaultedTooltip = defaultedLabel.slice();
  25453. }
  25454. encode.defaultedLabel = defaultedLabel;
  25455. encode.defaultedTooltip = defaultedTooltip;
  25456. return summary;
  25457. }
  25458. function getDimensionTypeByAxis(axisType) {
  25459. return axisType === 'category'
  25460. ? 'ordinal'
  25461. : axisType === 'time'
  25462. ? 'time'
  25463. : 'float';
  25464. }
  25465. function mayLabelDimType(dimType) {
  25466. // In most cases, ordinal and time do not suitable for label.
  25467. // Ordinal info can be displayed on axis. Time is too long.
  25468. return !(dimType === 'ordinal' || dimType === 'time');
  25469. }
  25470. // function findTheLastDimMayLabel(data) {
  25471. // // Get last value dim
  25472. // var dimensions = data.dimensions.slice();
  25473. // var valueType;
  25474. // var valueDim;
  25475. // while (dimensions.length && (
  25476. // valueDim = dimensions.pop(),
  25477. // valueType = data.getDimensionInfo(valueDim).type,
  25478. // valueType === 'ordinal' || valueType === 'time'
  25479. // )) {} // jshint ignore:line
  25480. // return valueDim;
  25481. // }
  25482. /*
  25483. * Licensed to the Apache Software Foundation (ASF) under one
  25484. * or more contributor license agreements. See the NOTICE file
  25485. * distributed with this work for additional information
  25486. * regarding copyright ownership. The ASF licenses this file
  25487. * to you under the Apache License, Version 2.0 (the
  25488. * "License"); you may not use this file except in compliance
  25489. * with the License. You may obtain a copy of the License at
  25490. *
  25491. * http://www.apache.org/licenses/LICENSE-2.0
  25492. *
  25493. * Unless required by applicable law or agreed to in writing,
  25494. * software distributed under the License is distributed on an
  25495. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  25496. * KIND, either express or implied. See the License for the
  25497. * specific language governing permissions and limitations
  25498. * under the License.
  25499. */
  25500. /* global Float64Array, Int32Array, Uint32Array, Uint16Array */
  25501. /**
  25502. * List for data storage
  25503. * @module echarts/data/List
  25504. */
  25505. var isObject$4 = isObject$1;
  25506. var UNDEFINED = 'undefined';
  25507. var INDEX_NOT_FOUND = -1;
  25508. // Use prefix to avoid index to be the same as otherIdList[idx],
  25509. // which will cause weird udpate animation.
  25510. var ID_PREFIX = 'e\0\0';
  25511. var dataCtors = {
  25512. 'float': typeof Float64Array === UNDEFINED
  25513. ? Array : Float64Array,
  25514. 'int': typeof Int32Array === UNDEFINED
  25515. ? Array : Int32Array,
  25516. // Ordinal data type can be string or int
  25517. 'ordinal': Array,
  25518. 'number': Array,
  25519. 'time': Array
  25520. };
  25521. // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
  25522. // different from the Ctor of typed array.
  25523. var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
  25524. var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
  25525. var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
  25526. function getIndicesCtor(list) {
  25527. // The possible max value in this._indicies is always this._rawCount despite of filtering.
  25528. return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
  25529. }
  25530. function cloneChunk(originalChunk) {
  25531. var Ctor = originalChunk.constructor;
  25532. // Only shallow clone is enough when Array.
  25533. return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
  25534. }
  25535. var TRANSFERABLE_PROPERTIES = [
  25536. 'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
  25537. '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter',
  25538. '_count', '_rawCount', '_nameDimIdx', '_idDimIdx'
  25539. ];
  25540. var CLONE_PROPERTIES = [
  25541. '_extent', '_approximateExtent', '_rawExtent'
  25542. ];
  25543. function transferProperties(target, source) {
  25544. each$1(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {
  25545. if (source.hasOwnProperty(propName)) {
  25546. target[propName] = source[propName];
  25547. }
  25548. });
  25549. target.__wrappedMethods = source.__wrappedMethods;
  25550. each$1(CLONE_PROPERTIES, function (propName) {
  25551. target[propName] = clone(source[propName]);
  25552. });
  25553. target._calculationInfo = extend(source._calculationInfo);
  25554. }
  25555. /**
  25556. * @constructor
  25557. * @alias module:echarts/data/List
  25558. *
  25559. * @param {Array.<string|Object>} dimensions
  25560. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  25561. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  25562. * Spetial fields: {
  25563. * ordinalMeta: <module:echarts/data/OrdinalMeta>
  25564. * createInvertedIndices: <boolean>
  25565. * }
  25566. * @param {module:echarts/model/Model} hostModel
  25567. */
  25568. var List = function (dimensions, hostModel) {
  25569. dimensions = dimensions || ['x', 'y'];
  25570. var dimensionInfos = {};
  25571. var dimensionNames = [];
  25572. var invertedIndicesMap = {};
  25573. for (var i = 0; i < dimensions.length; i++) {
  25574. // Use the original dimensions[i], where other flag props may exists.
  25575. var dimensionInfo = dimensions[i];
  25576. if (isString(dimensionInfo)) {
  25577. dimensionInfo = {name: dimensionInfo};
  25578. }
  25579. var dimensionName = dimensionInfo.name;
  25580. dimensionInfo.type = dimensionInfo.type || 'float';
  25581. if (!dimensionInfo.coordDim) {
  25582. dimensionInfo.coordDim = dimensionName;
  25583. dimensionInfo.coordDimIndex = 0;
  25584. }
  25585. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  25586. dimensionNames.push(dimensionName);
  25587. dimensionInfos[dimensionName] = dimensionInfo;
  25588. dimensionInfo.index = i;
  25589. if (dimensionInfo.createInvertedIndices) {
  25590. invertedIndicesMap[dimensionName] = [];
  25591. }
  25592. }
  25593. /**
  25594. * @readOnly
  25595. * @type {Array.<string>}
  25596. */
  25597. this.dimensions = dimensionNames;
  25598. /**
  25599. * Infomation of each data dimension, like data type.
  25600. * @type {Object}
  25601. */
  25602. this._dimensionInfos = dimensionInfos;
  25603. /**
  25604. * @type {module:echarts/model/Model}
  25605. */
  25606. this.hostModel = hostModel;
  25607. /**
  25608. * @type {module:echarts/model/Model}
  25609. */
  25610. this.dataType;
  25611. /**
  25612. * Indices stores the indices of data subset after filtered.
  25613. * This data subset will be used in chart.
  25614. * @type {Array.<number>}
  25615. * @readOnly
  25616. */
  25617. this._indices = null;
  25618. this._count = 0;
  25619. this._rawCount = 0;
  25620. /**
  25621. * Data storage
  25622. * @type {Object.<key, Array.<TypedArray|Array>>}
  25623. * @private
  25624. */
  25625. this._storage = {};
  25626. /**
  25627. * @type {Array.<string>}
  25628. */
  25629. this._nameList = [];
  25630. /**
  25631. * @type {Array.<string>}
  25632. */
  25633. this._idList = [];
  25634. /**
  25635. * Models of data option is stored sparse for optimizing memory cost
  25636. * @type {Array.<module:echarts/model/Model>}
  25637. * @private
  25638. */
  25639. this._optionModels = [];
  25640. /**
  25641. * Global visual properties after visual coding
  25642. * @type {Object}
  25643. * @private
  25644. */
  25645. this._visual = {};
  25646. /**
  25647. * Globel layout properties.
  25648. * @type {Object}
  25649. * @private
  25650. */
  25651. this._layout = {};
  25652. /**
  25653. * Item visual properties after visual coding
  25654. * @type {Array.<Object>}
  25655. * @private
  25656. */
  25657. this._itemVisuals = [];
  25658. /**
  25659. * Key: visual type, Value: boolean
  25660. * @type {Object}
  25661. * @readOnly
  25662. */
  25663. this.hasItemVisual = {};
  25664. /**
  25665. * Item layout properties after layout
  25666. * @type {Array.<Object>}
  25667. * @private
  25668. */
  25669. this._itemLayouts = [];
  25670. /**
  25671. * Graphic elemnents
  25672. * @type {Array.<module:zrender/Element>}
  25673. * @private
  25674. */
  25675. this._graphicEls = [];
  25676. /**
  25677. * Max size of each chunk.
  25678. * @type {number}
  25679. * @private
  25680. */
  25681. this._chunkSize = 1e5;
  25682. /**
  25683. * @type {number}
  25684. * @private
  25685. */
  25686. this._chunkCount = 0;
  25687. /**
  25688. * @type {Array.<Array|Object>}
  25689. * @private
  25690. */
  25691. this._rawData;
  25692. /**
  25693. * Raw extent will not be cloned, but only transfered.
  25694. * It will not be calculated util needed.
  25695. * key: dim,
  25696. * value: {end: number, extent: Array.<number>}
  25697. * @type {Object}
  25698. * @private
  25699. */
  25700. this._rawExtent = {};
  25701. /**
  25702. * @type {Object}
  25703. * @private
  25704. */
  25705. this._extent = {};
  25706. /**
  25707. * key: dim
  25708. * value: extent
  25709. * @type {Object}
  25710. * @private
  25711. */
  25712. this._approximateExtent = {};
  25713. /**
  25714. * Cache summary info for fast visit. See "dimensionHelper".
  25715. * @type {Object}
  25716. * @private
  25717. */
  25718. this._dimensionsSummary = summarizeDimensions(this);
  25719. /**
  25720. * @type {Object.<Array|TypedArray>}
  25721. * @private
  25722. */
  25723. this._invertedIndicesMap = invertedIndicesMap;
  25724. /**
  25725. * @type {Object}
  25726. * @private
  25727. */
  25728. this._calculationInfo = {};
  25729. };
  25730. var listProto = List.prototype;
  25731. listProto.type = 'list';
  25732. /**
  25733. * If each data item has it's own option
  25734. * @type {boolean}
  25735. */
  25736. listProto.hasItemOption = true;
  25737. /**
  25738. * Get dimension name
  25739. * @param {string|number} dim
  25740. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  25741. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  25742. * @return {string} Concrete dim name.
  25743. */
  25744. listProto.getDimension = function (dim) {
  25745. if (!isNaN(dim)) {
  25746. dim = this.dimensions[dim] || dim;
  25747. }
  25748. return dim;
  25749. };
  25750. /**
  25751. * Get type and calculation info of particular dimension
  25752. * @param {string|number} dim
  25753. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  25754. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  25755. */
  25756. listProto.getDimensionInfo = function (dim) {
  25757. // Do not clone, because there may be categories in dimInfo.
  25758. return this._dimensionInfos[this.getDimension(dim)];
  25759. };
  25760. /**
  25761. * @return {Array.<string>} concrete dimension name list on coord.
  25762. */
  25763. listProto.getDimensionsOnCoord = function () {
  25764. return this._dimensionsSummary.dataDimsOnCoord.slice();
  25765. };
  25766. /**
  25767. * @param {string} coordDim
  25768. * @param {number} [idx] A coordDim may map to more than one data dim.
  25769. * If idx is `true`, return a array of all mapped dims.
  25770. * If idx is not specified, return the first dim not extra.
  25771. * @return {string|Array.<string>} concrete data dim.
  25772. * If idx is number, and not found, return null/undefined.
  25773. * If idx is `true`, and not found, return empty array (always return array).
  25774. */
  25775. listProto.mapDimension = function (coordDim, idx) {
  25776. var dimensionsSummary = this._dimensionsSummary;
  25777. if (idx == null) {
  25778. return dimensionsSummary.encodeFirstDimNotExtra[coordDim];
  25779. }
  25780. var dims = dimensionsSummary.encode[coordDim];
  25781. return idx === true
  25782. // always return array if idx is `true`
  25783. ? (dims || []).slice()
  25784. : (dims && dims[idx]);
  25785. };
  25786. /**
  25787. * Initialize from data
  25788. * @param {Array.<Object|number|Array>} data source or data or data provider.
  25789. * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and
  25790. * defualt label/tooltip.
  25791. * A name can be specified in encode.itemName,
  25792. * or dataItem.name (only for series option data),
  25793. * or provided in nameList from outside.
  25794. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  25795. */
  25796. listProto.initData = function (data, nameList, dimValueGetter) {
  25797. var notProvider = Source.isInstance(data) || isArrayLike(data);
  25798. if (notProvider) {
  25799. data = new DefaultDataProvider(data, this.dimensions.length);
  25800. }
  25801. if (__DEV__) {
  25802. if (!notProvider && (typeof data.getItem !== 'function' || typeof data.count !== 'function')) {
  25803. throw new Error('Inavlid data provider.');
  25804. }
  25805. }
  25806. this._rawData = data;
  25807. // Clear
  25808. this._storage = {};
  25809. this._indices = null;
  25810. this._nameList = nameList || [];
  25811. this._idList = [];
  25812. this._nameRepeatCount = {};
  25813. if (!dimValueGetter) {
  25814. this.hasItemOption = false;
  25815. }
  25816. /**
  25817. * @readOnly
  25818. */
  25819. this.defaultDimValueGetter = defaultDimValueGetters[
  25820. this._rawData.getSource().sourceFormat
  25821. ];
  25822. // Default dim value getter
  25823. this._dimValueGetter = dimValueGetter = dimValueGetter
  25824. || this.defaultDimValueGetter;
  25825. this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows;
  25826. // Reset raw extent.
  25827. this._rawExtent = {};
  25828. this._initDataFromProvider(0, data.count());
  25829. // If data has no item option.
  25830. if (data.pure) {
  25831. this.hasItemOption = false;
  25832. }
  25833. };
  25834. listProto.getProvider = function () {
  25835. return this._rawData;
  25836. };
  25837. /**
  25838. * Caution: Can be only called on raw data (before `this._indices` created).
  25839. */
  25840. listProto.appendData = function (data) {
  25841. if (__DEV__) {
  25842. assert$1(!this._indices, 'appendData can only be called on raw data.');
  25843. }
  25844. var rawData = this._rawData;
  25845. var start = this.count();
  25846. rawData.appendData(data);
  25847. var end = rawData.count();
  25848. if (!rawData.persistent) {
  25849. end += start;
  25850. }
  25851. this._initDataFromProvider(start, end);
  25852. };
  25853. /**
  25854. * Caution: Can be only called on raw data (before `this._indices` created).
  25855. * This method does not modify `rawData` (`dataProvider`), but only
  25856. * add values to storage.
  25857. *
  25858. * The final count will be increased by `Math.max(values.length, names.length)`.
  25859. *
  25860. * @param {Array.<Array.<*>>} values That is the SourceType: 'arrayRows', like
  25861. * [
  25862. * [12, 33, 44],
  25863. * [NaN, 43, 1],
  25864. * ['-', 'asdf', 0]
  25865. * ]
  25866. * Each item is exaclty cooresponding to a dimension.
  25867. * @param {Array.<string>} [names]
  25868. */
  25869. listProto.appendValues = function (values, names) {
  25870. var chunkSize = this._chunkSize;
  25871. var storage = this._storage;
  25872. var dimensions = this.dimensions;
  25873. var dimLen = dimensions.length;
  25874. var rawExtent = this._rawExtent;
  25875. var start = this.count();
  25876. var end = start + Math.max(values.length, names ? names.length : 0);
  25877. var originalChunkCount = this._chunkCount;
  25878. for (var i = 0; i < dimLen; i++) {
  25879. var dim = dimensions[i];
  25880. if (!rawExtent[dim]) {
  25881. rawExtent[dim] = getInitialExtent();
  25882. }
  25883. if (!storage[dim]) {
  25884. storage[dim] = [];
  25885. }
  25886. prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
  25887. this._chunkCount = storage[dim].length;
  25888. }
  25889. var emptyDataItem = new Array(dimLen);
  25890. for (var idx = start; idx < end; idx++) {
  25891. var sourceIdx = idx - start;
  25892. var chunkIndex = Math.floor(idx / chunkSize);
  25893. var chunkOffset = idx % chunkSize;
  25894. // Store the data by dimensions
  25895. for (var k = 0; k < dimLen; k++) {
  25896. var dim = dimensions[k];
  25897. var val = this._dimValueGetterArrayRows(
  25898. values[sourceIdx] || emptyDataItem, dim, sourceIdx, k
  25899. );
  25900. storage[dim][chunkIndex][chunkOffset] = val;
  25901. var dimRawExtent = rawExtent[dim];
  25902. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  25903. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  25904. }
  25905. if (names) {
  25906. this._nameList[idx] = names[sourceIdx];
  25907. }
  25908. }
  25909. this._rawCount = this._count = end;
  25910. // Reset data extent
  25911. this._extent = {};
  25912. prepareInvertedIndex(this);
  25913. };
  25914. listProto._initDataFromProvider = function (start, end) {
  25915. // Optimize.
  25916. if (start >= end) {
  25917. return;
  25918. }
  25919. var chunkSize = this._chunkSize;
  25920. var rawData = this._rawData;
  25921. var storage = this._storage;
  25922. var dimensions = this.dimensions;
  25923. var dimLen = dimensions.length;
  25924. var dimensionInfoMap = this._dimensionInfos;
  25925. var nameList = this._nameList;
  25926. var idList = this._idList;
  25927. var rawExtent = this._rawExtent;
  25928. var nameRepeatCount = this._nameRepeatCount = {};
  25929. var nameDimIdx;
  25930. var originalChunkCount = this._chunkCount;
  25931. for (var i = 0; i < dimLen; i++) {
  25932. var dim = dimensions[i];
  25933. if (!rawExtent[dim]) {
  25934. rawExtent[dim] = getInitialExtent();
  25935. }
  25936. var dimInfo = dimensionInfoMap[dim];
  25937. if (dimInfo.otherDims.itemName === 0) {
  25938. nameDimIdx = this._nameDimIdx = i;
  25939. }
  25940. if (dimInfo.otherDims.itemId === 0) {
  25941. this._idDimIdx = i;
  25942. }
  25943. if (!storage[dim]) {
  25944. storage[dim] = [];
  25945. }
  25946. prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
  25947. this._chunkCount = storage[dim].length;
  25948. }
  25949. var dataItem = new Array(dimLen);
  25950. for (var idx = start; idx < end; idx++) {
  25951. // NOTICE: Try not to write things into dataItem
  25952. dataItem = rawData.getItem(idx, dataItem);
  25953. // Each data item is value
  25954. // [1, 2]
  25955. // 2
  25956. // Bar chart, line chart which uses category axis
  25957. // only gives the 'y' value. 'x' value is the indices of category
  25958. // Use a tempValue to normalize the value to be a (x, y) value
  25959. var chunkIndex = Math.floor(idx / chunkSize);
  25960. var chunkOffset = idx % chunkSize;
  25961. // Store the data by dimensions
  25962. for (var k = 0; k < dimLen; k++) {
  25963. var dim = dimensions[k];
  25964. var dimStorage = storage[dim][chunkIndex];
  25965. // PENDING NULL is empty or zero
  25966. var val = this._dimValueGetter(dataItem, dim, idx, k);
  25967. dimStorage[chunkOffset] = val;
  25968. var dimRawExtent = rawExtent[dim];
  25969. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  25970. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  25971. }
  25972. // ??? FIXME not check by pure but sourceFormat?
  25973. // TODO refactor these logic.
  25974. if (!rawData.pure) {
  25975. var name = nameList[idx];
  25976. if (dataItem && name == null) {
  25977. // If dataItem is {name: ...}, it has highest priority.
  25978. // That is appropriate for many common cases.
  25979. if (dataItem.name != null) {
  25980. // There is no other place to persistent dataItem.name,
  25981. // so save it to nameList.
  25982. nameList[idx] = name = dataItem.name;
  25983. }
  25984. else if (nameDimIdx != null) {
  25985. var nameDim = dimensions[nameDimIdx];
  25986. var nameDimChunk = storage[nameDim][chunkIndex];
  25987. if (nameDimChunk) {
  25988. name = nameDimChunk[chunkOffset];
  25989. var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
  25990. if (ordinalMeta && ordinalMeta.categories.length) {
  25991. name = ordinalMeta.categories[name];
  25992. }
  25993. }
  25994. }
  25995. }
  25996. // Try using the id in option
  25997. // id or name is used on dynamical data, mapping old and new items.
  25998. var id = dataItem == null ? null : dataItem.id;
  25999. if (id == null && name != null) {
  26000. // Use name as id and add counter to avoid same name
  26001. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  26002. id = name;
  26003. if (nameRepeatCount[name] > 0) {
  26004. id += '__ec__' + nameRepeatCount[name];
  26005. }
  26006. nameRepeatCount[name]++;
  26007. }
  26008. id != null && (idList[idx] = id);
  26009. }
  26010. }
  26011. if (!rawData.persistent && rawData.clean) {
  26012. // Clean unused data if data source is typed array.
  26013. rawData.clean();
  26014. }
  26015. this._rawCount = this._count = end;
  26016. // Reset data extent
  26017. this._extent = {};
  26018. prepareInvertedIndex(this);
  26019. };
  26020. function prepareChunks(storage, dimInfo, chunkSize, chunkCount, end) {
  26021. var DataCtor = dataCtors[dimInfo.type];
  26022. var lastChunkIndex = chunkCount - 1;
  26023. var dim = dimInfo.name;
  26024. var resizeChunkArray = storage[dim][lastChunkIndex];
  26025. if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
  26026. var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
  26027. // The cost of the copy is probably inconsiderable
  26028. // within the initial chunkSize.
  26029. for (var j = 0; j < resizeChunkArray.length; j++) {
  26030. newStore[j] = resizeChunkArray[j];
  26031. }
  26032. storage[dim][lastChunkIndex] = newStore;
  26033. }
  26034. // Create new chunks.
  26035. for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
  26036. storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
  26037. }
  26038. }
  26039. function prepareInvertedIndex(list) {
  26040. var invertedIndicesMap = list._invertedIndicesMap;
  26041. each$1(invertedIndicesMap, function (invertedIndices, dim) {
  26042. var dimInfo = list._dimensionInfos[dim];
  26043. // Currently, only dimensions that has ordinalMeta can create inverted indices.
  26044. var ordinalMeta = dimInfo.ordinalMeta;
  26045. if (ordinalMeta) {
  26046. invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
  26047. ordinalMeta.categories.length
  26048. );
  26049. // The default value of TypedArray is 0. To avoid miss
  26050. // mapping to 0, we should set it as INDEX_NOT_FOUND.
  26051. for (var i = 0; i < invertedIndices.length; i++) {
  26052. invertedIndices[i] = INDEX_NOT_FOUND;
  26053. }
  26054. for (var i = 0; i < list._count; i++) {
  26055. // Only support the case that all values are distinct.
  26056. invertedIndices[list.get(dim, i)] = i;
  26057. }
  26058. }
  26059. });
  26060. }
  26061. function getRawValueFromStore(list, dimIndex, rawIndex) {
  26062. var val;
  26063. if (dimIndex != null) {
  26064. var chunkSize = list._chunkSize;
  26065. var chunkIndex = Math.floor(rawIndex / chunkSize);
  26066. var chunkOffset = rawIndex % chunkSize;
  26067. var dim = list.dimensions[dimIndex];
  26068. var chunk = list._storage[dim][chunkIndex];
  26069. if (chunk) {
  26070. val = chunk[chunkOffset];
  26071. var ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
  26072. if (ordinalMeta && ordinalMeta.categories.length) {
  26073. val = ordinalMeta.categories[val];
  26074. }
  26075. }
  26076. }
  26077. return val;
  26078. }
  26079. /**
  26080. * @return {number}
  26081. */
  26082. listProto.count = function () {
  26083. return this._count;
  26084. };
  26085. listProto.getIndices = function () {
  26086. var newIndices;
  26087. var indices = this._indices;
  26088. if (indices) {
  26089. var Ctor = indices.constructor;
  26090. var thisCount = this._count;
  26091. // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
  26092. if (Ctor === Array) {
  26093. newIndices = new Ctor(thisCount);
  26094. for (var i = 0; i < thisCount; i++) {
  26095. newIndices[i] = indices[i];
  26096. }
  26097. }
  26098. else {
  26099. newIndices = new Ctor(indices.buffer, 0, thisCount);
  26100. }
  26101. }
  26102. else {
  26103. var Ctor = getIndicesCtor(this);
  26104. var newIndices = new Ctor(this.count());
  26105. for (var i = 0; i < newIndices.length; i++) {
  26106. newIndices[i] = i;
  26107. }
  26108. }
  26109. return newIndices;
  26110. };
  26111. /**
  26112. * Get value. Return NaN if idx is out of range.
  26113. * @param {string} dim Dim must be concrete name.
  26114. * @param {number} idx
  26115. * @param {boolean} stack
  26116. * @return {number}
  26117. */
  26118. listProto.get = function (dim, idx /*, stack */) {
  26119. if (!(idx >= 0 && idx < this._count)) {
  26120. return NaN;
  26121. }
  26122. var storage = this._storage;
  26123. if (!storage[dim]) {
  26124. // TODO Warn ?
  26125. return NaN;
  26126. }
  26127. idx = this.getRawIndex(idx);
  26128. var chunkIndex = Math.floor(idx / this._chunkSize);
  26129. var chunkOffset = idx % this._chunkSize;
  26130. var chunkStore = storage[dim][chunkIndex];
  26131. var value = chunkStore[chunkOffset];
  26132. // FIXME ordinal data type is not stackable
  26133. // if (stack) {
  26134. // var dimensionInfo = this._dimensionInfos[dim];
  26135. // if (dimensionInfo && dimensionInfo.stackable) {
  26136. // var stackedOn = this.stackedOn;
  26137. // while (stackedOn) {
  26138. // // Get no stacked data of stacked on
  26139. // var stackedValue = stackedOn.get(dim, idx);
  26140. // // Considering positive stack, negative stack and empty data
  26141. // if ((value >= 0 && stackedValue > 0) // Positive stack
  26142. // || (value <= 0 && stackedValue < 0) // Negative stack
  26143. // ) {
  26144. // value += stackedValue;
  26145. // }
  26146. // stackedOn = stackedOn.stackedOn;
  26147. // }
  26148. // }
  26149. // }
  26150. return value;
  26151. };
  26152. /**
  26153. * @param {string} dim concrete dim
  26154. * @param {number} rawIndex
  26155. * @return {number|string}
  26156. */
  26157. listProto.getByRawIndex = function (dim, rawIdx) {
  26158. if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
  26159. return NaN;
  26160. }
  26161. var dimStore = this._storage[dim];
  26162. if (!dimStore) {
  26163. // TODO Warn ?
  26164. return NaN;
  26165. }
  26166. var chunkIndex = Math.floor(rawIdx / this._chunkSize);
  26167. var chunkOffset = rawIdx % this._chunkSize;
  26168. var chunkStore = dimStore[chunkIndex];
  26169. return chunkStore[chunkOffset];
  26170. };
  26171. /**
  26172. * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
  26173. * Hack a much simpler _getFast
  26174. * @private
  26175. */
  26176. listProto._getFast = function (dim, rawIdx) {
  26177. var chunkIndex = Math.floor(rawIdx / this._chunkSize);
  26178. var chunkOffset = rawIdx % this._chunkSize;
  26179. var chunkStore = this._storage[dim][chunkIndex];
  26180. return chunkStore[chunkOffset];
  26181. };
  26182. /**
  26183. * Get value for multi dimensions.
  26184. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  26185. * @param {number} idx
  26186. * @return {number}
  26187. */
  26188. listProto.getValues = function (dimensions, idx /*, stack */) {
  26189. var values = [];
  26190. if (!isArray(dimensions)) {
  26191. // stack = idx;
  26192. idx = dimensions;
  26193. dimensions = this.dimensions;
  26194. }
  26195. for (var i = 0, len = dimensions.length; i < len; i++) {
  26196. values.push(this.get(dimensions[i], idx /*, stack */));
  26197. }
  26198. return values;
  26199. };
  26200. /**
  26201. * If value is NaN. Inlcuding '-'
  26202. * Only check the coord dimensions.
  26203. * @param {string} dim
  26204. * @param {number} idx
  26205. * @return {number}
  26206. */
  26207. listProto.hasValue = function (idx) {
  26208. var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
  26209. var dimensionInfos = this._dimensionInfos;
  26210. for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
  26211. if (
  26212. // Ordinal type can be string or number
  26213. dimensionInfos[dataDimsOnCoord[i]].type !== 'ordinal'
  26214. // FIXME check ordinal when using index?
  26215. && isNaN(this.get(dataDimsOnCoord[i], idx))
  26216. ) {
  26217. return false;
  26218. }
  26219. }
  26220. return true;
  26221. };
  26222. /**
  26223. * Get extent of data in one dimension
  26224. * @param {string} dim
  26225. * @param {boolean} stack
  26226. */
  26227. listProto.getDataExtent = function (dim /*, stack */) {
  26228. // Make sure use concrete dim as cache name.
  26229. dim = this.getDimension(dim);
  26230. var dimData = this._storage[dim];
  26231. var initialExtent = getInitialExtent();
  26232. // stack = !!((stack || false) && this.getCalculationInfo(dim));
  26233. if (!dimData) {
  26234. return initialExtent;
  26235. }
  26236. // Make more strict checkings to ensure hitting cache.
  26237. var currEnd = this.count();
  26238. // var cacheName = [dim, !!stack].join('_');
  26239. // var cacheName = dim;
  26240. // Consider the most cases when using data zoom, `getDataExtent`
  26241. // happened before filtering. We cache raw extent, which is not
  26242. // necessary to be cleared and recalculated when restore data.
  26243. var useRaw = !this._indices; // && !stack;
  26244. var dimExtent;
  26245. if (useRaw) {
  26246. return this._rawExtent[dim].slice();
  26247. }
  26248. dimExtent = this._extent[dim];
  26249. if (dimExtent) {
  26250. return dimExtent.slice();
  26251. }
  26252. dimExtent = initialExtent;
  26253. var min = dimExtent[0];
  26254. var max = dimExtent[1];
  26255. for (var i = 0; i < currEnd; i++) {
  26256. // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
  26257. var value = this._getFast(dim, this.getRawIndex(i));
  26258. value < min && (min = value);
  26259. value > max && (max = value);
  26260. }
  26261. dimExtent = [min, max];
  26262. this._extent[dim] = dimExtent;
  26263. return dimExtent;
  26264. };
  26265. /**
  26266. * Optimize for the scenario that data is filtered by a given extent.
  26267. * Consider that if data amount is more than hundreds of thousand,
  26268. * extent calculation will cost more than 10ms and the cache will
  26269. * be erased because of the filtering.
  26270. */
  26271. listProto.getApproximateExtent = function (dim /*, stack */) {
  26272. dim = this.getDimension(dim);
  26273. return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */);
  26274. };
  26275. listProto.setApproximateExtent = function (extent, dim /*, stack */) {
  26276. dim = this.getDimension(dim);
  26277. this._approximateExtent[dim] = extent.slice();
  26278. };
  26279. /**
  26280. * @param {string} key
  26281. * @return {*}
  26282. */
  26283. listProto.getCalculationInfo = function (key) {
  26284. return this._calculationInfo[key];
  26285. };
  26286. /**
  26287. * @param {string|Object} key or k-v object
  26288. * @param {*} [value]
  26289. */
  26290. listProto.setCalculationInfo = function (key, value) {
  26291. isObject$4(key)
  26292. ? extend(this._calculationInfo, key)
  26293. : (this._calculationInfo[key] = value);
  26294. };
  26295. /**
  26296. * Get sum of data in one dimension
  26297. * @param {string} dim
  26298. */
  26299. listProto.getSum = function (dim /*, stack */) {
  26300. var dimData = this._storage[dim];
  26301. var sum = 0;
  26302. if (dimData) {
  26303. for (var i = 0, len = this.count(); i < len; i++) {
  26304. var value = this.get(dim, i /*, stack */);
  26305. if (!isNaN(value)) {
  26306. sum += value;
  26307. }
  26308. }
  26309. }
  26310. return sum;
  26311. };
  26312. /**
  26313. * Get median of data in one dimension
  26314. * @param {string} dim
  26315. */
  26316. listProto.getMedian = function (dim /*, stack */) {
  26317. var dimDataArray = [];
  26318. // map all data of one dimension
  26319. this.each(dim, function (val, idx) {
  26320. if (!isNaN(val)) {
  26321. dimDataArray.push(val);
  26322. }
  26323. });
  26324. // TODO
  26325. // Use quick select?
  26326. // immutability & sort
  26327. var sortedDimDataArray = [].concat(dimDataArray).sort(function (a, b) {
  26328. return a - b;
  26329. });
  26330. var len = this.count();
  26331. // calculate median
  26332. return len === 0
  26333. ? 0
  26334. : len % 2 === 1
  26335. ? sortedDimDataArray[(len - 1) / 2]
  26336. : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
  26337. };
  26338. // /**
  26339. // * Retreive the index with given value
  26340. // * @param {string} dim Concrete dimension.
  26341. // * @param {number} value
  26342. // * @return {number}
  26343. // */
  26344. // Currently incorrect: should return dataIndex but not rawIndex.
  26345. // Do not fix it until this method is to be used somewhere.
  26346. // FIXME Precision of float value
  26347. // listProto.indexOf = function (dim, value) {
  26348. // var storage = this._storage;
  26349. // var dimData = storage[dim];
  26350. // var chunkSize = this._chunkSize;
  26351. // if (dimData) {
  26352. // for (var i = 0, len = this.count(); i < len; i++) {
  26353. // var chunkIndex = Math.floor(i / chunkSize);
  26354. // var chunkOffset = i % chunkSize;
  26355. // if (dimData[chunkIndex][chunkOffset] === value) {
  26356. // return i;
  26357. // }
  26358. // }
  26359. // }
  26360. // return -1;
  26361. // };
  26362. /**
  26363. * Only support the dimension which inverted index created.
  26364. * Do not support other cases until required.
  26365. * @param {string} concrete dim
  26366. * @param {number|string} value
  26367. * @return {number} rawIndex
  26368. */
  26369. listProto.rawIndexOf = function (dim, value) {
  26370. var invertedIndices = dim && this._invertedIndicesMap[dim];
  26371. if (__DEV__) {
  26372. if (!invertedIndices) {
  26373. throw new Error('Do not supported yet');
  26374. }
  26375. }
  26376. var rawIndex = invertedIndices[value];
  26377. if (rawIndex == null || isNaN(rawIndex)) {
  26378. return INDEX_NOT_FOUND;
  26379. }
  26380. return rawIndex;
  26381. };
  26382. /**
  26383. * Retreive the index with given name
  26384. * @param {number} idx
  26385. * @param {number} name
  26386. * @return {number}
  26387. */
  26388. listProto.indexOfName = function (name) {
  26389. for (var i = 0, len = this.count(); i < len; i++) {
  26390. if (this.getName(i) === name) {
  26391. return i;
  26392. }
  26393. }
  26394. return -1;
  26395. };
  26396. /**
  26397. * Retreive the index with given raw data index
  26398. * @param {number} idx
  26399. * @param {number} name
  26400. * @return {number}
  26401. */
  26402. listProto.indexOfRawIndex = function (rawIndex) {
  26403. if (!this._indices) {
  26404. return rawIndex;
  26405. }
  26406. if (rawIndex >= this._rawCount || rawIndex < 0) {
  26407. return -1;
  26408. }
  26409. // Indices are ascending
  26410. var indices = this._indices;
  26411. // If rawIndex === dataIndex
  26412. var rawDataIndex = indices[rawIndex];
  26413. if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
  26414. return rawIndex;
  26415. }
  26416. var left = 0;
  26417. var right = this._count - 1;
  26418. while (left <= right) {
  26419. var mid = (left + right) / 2 | 0;
  26420. if (indices[mid] < rawIndex) {
  26421. left = mid + 1;
  26422. }
  26423. else if (indices[mid] > rawIndex) {
  26424. right = mid - 1;
  26425. }
  26426. else {
  26427. return mid;
  26428. }
  26429. }
  26430. return -1;
  26431. };
  26432. /**
  26433. * Retreive the index of nearest value
  26434. * @param {string} dim
  26435. * @param {number} value
  26436. * @param {number} [maxDistance=Infinity]
  26437. * @return {Array.<number>} Considere multiple points has the same value.
  26438. */
  26439. listProto.indicesOfNearest = function (dim, value, maxDistance) {
  26440. var storage = this._storage;
  26441. var dimData = storage[dim];
  26442. var nearestIndices = [];
  26443. if (!dimData) {
  26444. return nearestIndices;
  26445. }
  26446. if (maxDistance == null) {
  26447. maxDistance = Infinity;
  26448. }
  26449. var minDist = Number.MAX_VALUE;
  26450. var minDiff = -1;
  26451. for (var i = 0, len = this.count(); i < len; i++) {
  26452. var diff = value - this.get(dim, i /*, stack */);
  26453. var dist = Math.abs(diff);
  26454. if (diff <= maxDistance && dist <= minDist) {
  26455. // For the case of two data are same on xAxis, which has sequence data.
  26456. // Show the nearest index
  26457. // https://github.com/ecomfe/echarts/issues/2869
  26458. if (dist < minDist || (diff >= 0 && minDiff < 0)) {
  26459. minDist = dist;
  26460. minDiff = diff;
  26461. nearestIndices.length = 0;
  26462. }
  26463. nearestIndices.push(i);
  26464. }
  26465. }
  26466. return nearestIndices;
  26467. };
  26468. /**
  26469. * Get raw data index
  26470. * @param {number} idx
  26471. * @return {number}
  26472. */
  26473. listProto.getRawIndex = getRawIndexWithoutIndices;
  26474. function getRawIndexWithoutIndices(idx) {
  26475. return idx;
  26476. }
  26477. function getRawIndexWithIndices(idx) {
  26478. if (idx < this._count && idx >= 0) {
  26479. return this._indices[idx];
  26480. }
  26481. return -1;
  26482. }
  26483. /**
  26484. * Get raw data item
  26485. * @param {number} idx
  26486. * @return {number}
  26487. */
  26488. listProto.getRawDataItem = function (idx) {
  26489. if (!this._rawData.persistent) {
  26490. var val = [];
  26491. for (var i = 0; i < this.dimensions.length; i++) {
  26492. var dim = this.dimensions[i];
  26493. val.push(this.get(dim, idx));
  26494. }
  26495. return val;
  26496. }
  26497. else {
  26498. return this._rawData.getItem(this.getRawIndex(idx));
  26499. }
  26500. };
  26501. /**
  26502. * @param {number} idx
  26503. * @param {boolean} [notDefaultIdx=false]
  26504. * @return {string}
  26505. */
  26506. listProto.getName = function (idx) {
  26507. var rawIndex = this.getRawIndex(idx);
  26508. return this._nameList[rawIndex]
  26509. || getRawValueFromStore(this, this._nameDimIdx, rawIndex)
  26510. || '';
  26511. };
  26512. /**
  26513. * @param {number} idx
  26514. * @param {boolean} [notDefaultIdx=false]
  26515. * @return {string}
  26516. */
  26517. listProto.getId = function (idx) {
  26518. return getId(this, this.getRawIndex(idx));
  26519. };
  26520. function getId(list, rawIndex) {
  26521. var id = list._idList[rawIndex];
  26522. if (id == null) {
  26523. id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
  26524. }
  26525. if (id == null) {
  26526. // FIXME Check the usage in graph, should not use prefix.
  26527. id = ID_PREFIX + rawIndex;
  26528. }
  26529. return id;
  26530. }
  26531. function normalizeDimensions(dimensions) {
  26532. if (!isArray(dimensions)) {
  26533. dimensions = [dimensions];
  26534. }
  26535. return dimensions;
  26536. }
  26537. function validateDimensions(list, dims) {
  26538. for (var i = 0; i < dims.length; i++) {
  26539. // stroage may be empty when no data, so use
  26540. // dimensionInfos to check.
  26541. if (!list._dimensionInfos[dims[i]]) {
  26542. console.error('Unkown dimension ' + dims[i]);
  26543. }
  26544. }
  26545. }
  26546. /**
  26547. * Data iteration
  26548. * @param {string|Array.<string>}
  26549. * @param {Function} cb
  26550. * @param {*} [context=this]
  26551. *
  26552. * @example
  26553. * list.each('x', function (x, idx) {});
  26554. * list.each(['x', 'y'], function (x, y, idx) {});
  26555. * list.each(function (idx) {})
  26556. */
  26557. listProto.each = function (dims, cb, context, contextCompat) {
  26558. 'use strict';
  26559. if (!this._count) {
  26560. return;
  26561. }
  26562. if (typeof dims === 'function') {
  26563. contextCompat = context;
  26564. context = cb;
  26565. cb = dims;
  26566. dims = [];
  26567. }
  26568. // contextCompat just for compat echarts3
  26569. context = context || contextCompat || this;
  26570. dims = map(normalizeDimensions(dims), this.getDimension, this);
  26571. if (__DEV__) {
  26572. validateDimensions(this, dims);
  26573. }
  26574. var dimSize = dims.length;
  26575. for (var i = 0; i < this.count(); i++) {
  26576. // Simple optimization
  26577. switch (dimSize) {
  26578. case 0:
  26579. cb.call(context, i);
  26580. break;
  26581. case 1:
  26582. cb.call(context, this.get(dims[0], i), i);
  26583. break;
  26584. case 2:
  26585. cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i);
  26586. break;
  26587. default:
  26588. var k = 0;
  26589. var value = [];
  26590. for (; k < dimSize; k++) {
  26591. value[k] = this.get(dims[k], i);
  26592. }
  26593. // Index
  26594. value[k] = i;
  26595. cb.apply(context, value);
  26596. }
  26597. }
  26598. };
  26599. /**
  26600. * Data filter
  26601. * @param {string|Array.<string>}
  26602. * @param {Function} cb
  26603. * @param {*} [context=this]
  26604. */
  26605. listProto.filterSelf = function (dimensions, cb, context, contextCompat) {
  26606. 'use strict';
  26607. if (!this._count) {
  26608. return;
  26609. }
  26610. if (typeof dimensions === 'function') {
  26611. contextCompat = context;
  26612. context = cb;
  26613. cb = dimensions;
  26614. dimensions = [];
  26615. }
  26616. // contextCompat just for compat echarts3
  26617. context = context || contextCompat || this;
  26618. dimensions = map(
  26619. normalizeDimensions(dimensions), this.getDimension, this
  26620. );
  26621. if (__DEV__) {
  26622. validateDimensions(this, dimensions);
  26623. }
  26624. var count = this.count();
  26625. var Ctor = getIndicesCtor(this);
  26626. var newIndices = new Ctor(count);
  26627. var value = [];
  26628. var dimSize = dimensions.length;
  26629. var offset = 0;
  26630. var dim0 = dimensions[0];
  26631. for (var i = 0; i < count; i++) {
  26632. var keep;
  26633. var rawIdx = this.getRawIndex(i);
  26634. // Simple optimization
  26635. if (dimSize === 0) {
  26636. keep = cb.call(context, i);
  26637. }
  26638. else if (dimSize === 1) {
  26639. var val = this._getFast(dim0, rawIdx);
  26640. keep = cb.call(context, val, i);
  26641. }
  26642. else {
  26643. for (var k = 0; k < dimSize; k++) {
  26644. value[k] = this._getFast(dim0, rawIdx);
  26645. }
  26646. value[k] = i;
  26647. keep = cb.apply(context, value);
  26648. }
  26649. if (keep) {
  26650. newIndices[offset++] = rawIdx;
  26651. }
  26652. }
  26653. // Set indices after filtered.
  26654. if (offset < count) {
  26655. this._indices = newIndices;
  26656. }
  26657. this._count = offset;
  26658. // Reset data extent
  26659. this._extent = {};
  26660. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  26661. return this;
  26662. };
  26663. /**
  26664. * Select data in range. (For optimization of filter)
  26665. * (Manually inline code, support 5 million data filtering in data zoom.)
  26666. */
  26667. listProto.selectRange = function (range) {
  26668. 'use strict';
  26669. if (!this._count) {
  26670. return;
  26671. }
  26672. var dimensions = [];
  26673. for (var dim in range) {
  26674. if (range.hasOwnProperty(dim)) {
  26675. dimensions.push(dim);
  26676. }
  26677. }
  26678. if (__DEV__) {
  26679. validateDimensions(this, dimensions);
  26680. }
  26681. var dimSize = dimensions.length;
  26682. if (!dimSize) {
  26683. return;
  26684. }
  26685. var originalCount = this.count();
  26686. var Ctor = getIndicesCtor(this);
  26687. var newIndices = new Ctor(originalCount);
  26688. var offset = 0;
  26689. var dim0 = dimensions[0];
  26690. var min = range[dim0][0];
  26691. var max = range[dim0][1];
  26692. var quickFinished = false;
  26693. if (!this._indices) {
  26694. // Extreme optimization for common case. About 2x faster in chrome.
  26695. var idx = 0;
  26696. if (dimSize === 1) {
  26697. var dimStorage = this._storage[dimensions[0]];
  26698. for (var k = 0; k < this._chunkCount; k++) {
  26699. var chunkStorage = dimStorage[k];
  26700. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  26701. for (var i = 0; i < len; i++) {
  26702. var val = chunkStorage[i];
  26703. // NaN will not be filtered. Consider the case, in line chart, empty
  26704. // value indicates the line should be broken. But for the case like
  26705. // scatter plot, a data item with empty value will not be rendered,
  26706. // but the axis extent may be effected if some other dim of the data
  26707. // item has value. Fortunately it is not a significant negative effect.
  26708. if (
  26709. (val >= min && val <= max) || isNaN(val)
  26710. ) {
  26711. newIndices[offset++] = idx;
  26712. }
  26713. idx++;
  26714. }
  26715. }
  26716. quickFinished = true;
  26717. }
  26718. else if (dimSize === 2) {
  26719. var dimStorage = this._storage[dim0];
  26720. var dimStorage2 = this._storage[dimensions[1]];
  26721. var min2 = range[dimensions[1]][0];
  26722. var max2 = range[dimensions[1]][1];
  26723. for (var k = 0; k < this._chunkCount; k++) {
  26724. var chunkStorage = dimStorage[k];
  26725. var chunkStorage2 = dimStorage2[k];
  26726. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  26727. for (var i = 0; i < len; i++) {
  26728. var val = chunkStorage[i];
  26729. var val2 = chunkStorage2[i];
  26730. // Do not filter NaN, see comment above.
  26731. if ((
  26732. (val >= min && val <= max) || isNaN(val)
  26733. )
  26734. && (
  26735. (val2 >= min2 && val2 <= max2) || isNaN(val2)
  26736. )
  26737. ) {
  26738. newIndices[offset++] = idx;
  26739. }
  26740. idx++;
  26741. }
  26742. }
  26743. quickFinished = true;
  26744. }
  26745. }
  26746. if (!quickFinished) {
  26747. if (dimSize === 1) {
  26748. for (var i = 0; i < originalCount; i++) {
  26749. var rawIndex = this.getRawIndex(i);
  26750. var val = this._getFast(dim0, rawIndex);
  26751. // Do not filter NaN, see comment above.
  26752. if (
  26753. (val >= min && val <= max) || isNaN(val)
  26754. ) {
  26755. newIndices[offset++] = rawIndex;
  26756. }
  26757. }
  26758. }
  26759. else {
  26760. for (var i = 0; i < originalCount; i++) {
  26761. var keep = true;
  26762. var rawIndex = this.getRawIndex(i);
  26763. for (var k = 0; k < dimSize; k++) {
  26764. var dimk = dimensions[k];
  26765. var val = this._getFast(dim, rawIndex);
  26766. // Do not filter NaN, see comment above.
  26767. if (val < range[dimk][0] || val > range[dimk][1]) {
  26768. keep = false;
  26769. }
  26770. }
  26771. if (keep) {
  26772. newIndices[offset++] = this.getRawIndex(i);
  26773. }
  26774. }
  26775. }
  26776. }
  26777. // Set indices after filtered.
  26778. if (offset < originalCount) {
  26779. this._indices = newIndices;
  26780. }
  26781. this._count = offset;
  26782. // Reset data extent
  26783. this._extent = {};
  26784. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  26785. return this;
  26786. };
  26787. /**
  26788. * Data mapping to a plain array
  26789. * @param {string|Array.<string>} [dimensions]
  26790. * @param {Function} cb
  26791. * @param {*} [context=this]
  26792. * @return {Array}
  26793. */
  26794. listProto.mapArray = function (dimensions, cb, context, contextCompat) {
  26795. 'use strict';
  26796. if (typeof dimensions === 'function') {
  26797. contextCompat = context;
  26798. context = cb;
  26799. cb = dimensions;
  26800. dimensions = [];
  26801. }
  26802. // contextCompat just for compat echarts3
  26803. context = context || contextCompat || this;
  26804. var result = [];
  26805. this.each(dimensions, function () {
  26806. result.push(cb && cb.apply(this, arguments));
  26807. }, context);
  26808. return result;
  26809. };
  26810. // Data in excludeDimensions is copied, otherwise transfered.
  26811. function cloneListForMapAndSample(original, excludeDimensions) {
  26812. var allDimensions = original.dimensions;
  26813. var list = new List(
  26814. map(allDimensions, original.getDimensionInfo, original),
  26815. original.hostModel
  26816. );
  26817. // FIXME If needs stackedOn, value may already been stacked
  26818. transferProperties(list, original);
  26819. var storage = list._storage = {};
  26820. var originalStorage = original._storage;
  26821. // Init storage
  26822. for (var i = 0; i < allDimensions.length; i++) {
  26823. var dim = allDimensions[i];
  26824. if (originalStorage[dim]) {
  26825. // Notice that we do not reset invertedIndicesMap here, becuase
  26826. // there is no scenario of mapping or sampling ordinal dimension.
  26827. if (indexOf(excludeDimensions, dim) >= 0) {
  26828. storage[dim] = cloneDimStore(originalStorage[dim]);
  26829. list._rawExtent[dim] = getInitialExtent();
  26830. list._extent[dim] = null;
  26831. }
  26832. else {
  26833. // Direct reference for other dimensions
  26834. storage[dim] = originalStorage[dim];
  26835. }
  26836. }
  26837. }
  26838. return list;
  26839. }
  26840. function cloneDimStore(originalDimStore) {
  26841. var newDimStore = new Array(originalDimStore.length);
  26842. for (var j = 0; j < originalDimStore.length; j++) {
  26843. newDimStore[j] = cloneChunk(originalDimStore[j]);
  26844. }
  26845. return newDimStore;
  26846. }
  26847. function getInitialExtent() {
  26848. return [Infinity, -Infinity];
  26849. }
  26850. /**
  26851. * Data mapping to a new List with given dimensions
  26852. * @param {string|Array.<string>} dimensions
  26853. * @param {Function} cb
  26854. * @param {*} [context=this]
  26855. * @return {Array}
  26856. */
  26857. listProto.map = function (dimensions, cb, context, contextCompat) {
  26858. 'use strict';
  26859. // contextCompat just for compat echarts3
  26860. context = context || contextCompat || this;
  26861. dimensions = map(
  26862. normalizeDimensions(dimensions), this.getDimension, this
  26863. );
  26864. if (__DEV__) {
  26865. validateDimensions(this, dimensions);
  26866. }
  26867. var list = cloneListForMapAndSample(this, dimensions);
  26868. // Following properties are all immutable.
  26869. // So we can reference to the same value
  26870. list._indices = this._indices;
  26871. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  26872. var storage = list._storage;
  26873. var tmpRetValue = [];
  26874. var chunkSize = this._chunkSize;
  26875. var dimSize = dimensions.length;
  26876. var dataCount = this.count();
  26877. var values = [];
  26878. var rawExtent = list._rawExtent;
  26879. for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
  26880. for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
  26881. values[dimIndex] = this.get(dimensions[dimIndex], dataIndex /*, stack */);
  26882. }
  26883. values[dimSize] = dataIndex;
  26884. var retValue = cb && cb.apply(context, values);
  26885. if (retValue != null) {
  26886. // a number or string (in oridinal dimension)?
  26887. if (typeof retValue !== 'object') {
  26888. tmpRetValue[0] = retValue;
  26889. retValue = tmpRetValue;
  26890. }
  26891. var rawIndex = this.getRawIndex(dataIndex);
  26892. var chunkIndex = Math.floor(rawIndex / chunkSize);
  26893. var chunkOffset = rawIndex % chunkSize;
  26894. for (var i = 0; i < retValue.length; i++) {
  26895. var dim = dimensions[i];
  26896. var val = retValue[i];
  26897. var rawExtentOnDim = rawExtent[dim];
  26898. var dimStore = storage[dim];
  26899. if (dimStore) {
  26900. dimStore[chunkIndex][chunkOffset] = val;
  26901. }
  26902. if (val < rawExtentOnDim[0]) {
  26903. rawExtentOnDim[0] = val;
  26904. }
  26905. if (val > rawExtentOnDim[1]) {
  26906. rawExtentOnDim[1] = val;
  26907. }
  26908. }
  26909. }
  26910. }
  26911. return list;
  26912. };
  26913. /**
  26914. * Large data down sampling on given dimension
  26915. * @param {string} dimension
  26916. * @param {number} rate
  26917. * @param {Function} sampleValue
  26918. * @param {Function} sampleIndex Sample index for name and id
  26919. */
  26920. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  26921. var list = cloneListForMapAndSample(this, [dimension]);
  26922. var targetStorage = list._storage;
  26923. var frameValues = [];
  26924. var frameSize = Math.floor(1 / rate);
  26925. var dimStore = targetStorage[dimension];
  26926. var len = this.count();
  26927. var chunkSize = this._chunkSize;
  26928. var rawExtentOnDim = list._rawExtent[dimension];
  26929. var newIndices = new (getIndicesCtor(this))(len);
  26930. var offset = 0;
  26931. for (var i = 0; i < len; i += frameSize) {
  26932. // Last frame
  26933. if (frameSize > len - i) {
  26934. frameSize = len - i;
  26935. frameValues.length = frameSize;
  26936. }
  26937. for (var k = 0; k < frameSize; k++) {
  26938. var dataIdx = this.getRawIndex(i + k);
  26939. var originalChunkIndex = Math.floor(dataIdx / chunkSize);
  26940. var originalChunkOffset = dataIdx % chunkSize;
  26941. frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
  26942. }
  26943. var value = sampleValue(frameValues);
  26944. var sampleFrameIdx = this.getRawIndex(
  26945. Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
  26946. );
  26947. var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
  26948. var sampleChunkOffset = sampleFrameIdx % chunkSize;
  26949. // Only write value on the filtered data
  26950. dimStore[sampleChunkIndex][sampleChunkOffset] = value;
  26951. if (value < rawExtentOnDim[0]) {
  26952. rawExtentOnDim[0] = value;
  26953. }
  26954. if (value > rawExtentOnDim[1]) {
  26955. rawExtentOnDim[1] = value;
  26956. }
  26957. newIndices[offset++] = sampleFrameIdx;
  26958. }
  26959. list._count = offset;
  26960. list._indices = newIndices;
  26961. list.getRawIndex = getRawIndexWithIndices;
  26962. return list;
  26963. };
  26964. /**
  26965. * Get model of one data item.
  26966. *
  26967. * @param {number} idx
  26968. */
  26969. // FIXME Model proxy ?
  26970. listProto.getItemModel = function (idx) {
  26971. var hostModel = this.hostModel;
  26972. return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
  26973. };
  26974. /**
  26975. * Create a data differ
  26976. * @param {module:echarts/data/List} otherList
  26977. * @return {module:echarts/data/DataDiffer}
  26978. */
  26979. listProto.diff = function (otherList) {
  26980. var thisList = this;
  26981. return new DataDiffer(
  26982. otherList ? otherList.getIndices() : [],
  26983. this.getIndices(),
  26984. function (idx) {
  26985. return getId(otherList, idx);
  26986. },
  26987. function (idx) {
  26988. return getId(thisList, idx);
  26989. }
  26990. );
  26991. };
  26992. /**
  26993. * Get visual property.
  26994. * @param {string} key
  26995. */
  26996. listProto.getVisual = function (key) {
  26997. var visual = this._visual;
  26998. return visual && visual[key];
  26999. };
  27000. /**
  27001. * Set visual property
  27002. * @param {string|Object} key
  27003. * @param {*} [value]
  27004. *
  27005. * @example
  27006. * setVisual('color', color);
  27007. * setVisual({
  27008. * 'color': color
  27009. * });
  27010. */
  27011. listProto.setVisual = function (key, val) {
  27012. if (isObject$4(key)) {
  27013. for (var name in key) {
  27014. if (key.hasOwnProperty(name)) {
  27015. this.setVisual(name, key[name]);
  27016. }
  27017. }
  27018. return;
  27019. }
  27020. this._visual = this._visual || {};
  27021. this._visual[key] = val;
  27022. };
  27023. /**
  27024. * Set layout property.
  27025. * @param {string|Object} key
  27026. * @param {*} [val]
  27027. */
  27028. listProto.setLayout = function (key, val) {
  27029. if (isObject$4(key)) {
  27030. for (var name in key) {
  27031. if (key.hasOwnProperty(name)) {
  27032. this.setLayout(name, key[name]);
  27033. }
  27034. }
  27035. return;
  27036. }
  27037. this._layout[key] = val;
  27038. };
  27039. /**
  27040. * Get layout property.
  27041. * @param {string} key.
  27042. * @return {*}
  27043. */
  27044. listProto.getLayout = function (key) {
  27045. return this._layout[key];
  27046. };
  27047. /**
  27048. * Get layout of single data item
  27049. * @param {number} idx
  27050. */
  27051. listProto.getItemLayout = function (idx) {
  27052. return this._itemLayouts[idx];
  27053. };
  27054. /**
  27055. * Set layout of single data item
  27056. * @param {number} idx
  27057. * @param {Object} layout
  27058. * @param {boolean=} [merge=false]
  27059. */
  27060. listProto.setItemLayout = function (idx, layout, merge$$1) {
  27061. this._itemLayouts[idx] = merge$$1
  27062. ? extend(this._itemLayouts[idx] || {}, layout)
  27063. : layout;
  27064. };
  27065. /**
  27066. * Clear all layout of single data item
  27067. */
  27068. listProto.clearItemLayouts = function () {
  27069. this._itemLayouts.length = 0;
  27070. };
  27071. /**
  27072. * Get visual property of single data item
  27073. * @param {number} idx
  27074. * @param {string} key
  27075. * @param {boolean} [ignoreParent=false]
  27076. */
  27077. listProto.getItemVisual = function (idx, key, ignoreParent) {
  27078. var itemVisual = this._itemVisuals[idx];
  27079. var val = itemVisual && itemVisual[key];
  27080. if (val == null && !ignoreParent) {
  27081. // Use global visual property
  27082. return this.getVisual(key);
  27083. }
  27084. return val;
  27085. };
  27086. /**
  27087. * Set visual property of single data item
  27088. *
  27089. * @param {number} idx
  27090. * @param {string|Object} key
  27091. * @param {*} [value]
  27092. *
  27093. * @example
  27094. * setItemVisual(0, 'color', color);
  27095. * setItemVisual(0, {
  27096. * 'color': color
  27097. * });
  27098. */
  27099. listProto.setItemVisual = function (idx, key, value) {
  27100. var itemVisual = this._itemVisuals[idx] || {};
  27101. var hasItemVisual = this.hasItemVisual;
  27102. this._itemVisuals[idx] = itemVisual;
  27103. if (isObject$4(key)) {
  27104. for (var name in key) {
  27105. if (key.hasOwnProperty(name)) {
  27106. itemVisual[name] = key[name];
  27107. hasItemVisual[name] = true;
  27108. }
  27109. }
  27110. return;
  27111. }
  27112. itemVisual[key] = value;
  27113. hasItemVisual[key] = true;
  27114. };
  27115. /**
  27116. * Clear itemVisuals and list visual.
  27117. */
  27118. listProto.clearAllVisual = function () {
  27119. this._visual = {};
  27120. this._itemVisuals = [];
  27121. this.hasItemVisual = {};
  27122. };
  27123. var setItemDataAndSeriesIndex = function (child) {
  27124. child.seriesIndex = this.seriesIndex;
  27125. child.dataIndex = this.dataIndex;
  27126. child.dataType = this.dataType;
  27127. };
  27128. /**
  27129. * Set graphic element relative to data. It can be set as null
  27130. * @param {number} idx
  27131. * @param {module:zrender/Element} [el]
  27132. */
  27133. listProto.setItemGraphicEl = function (idx, el) {
  27134. var hostModel = this.hostModel;
  27135. if (el) {
  27136. // Add data index and series index for indexing the data by element
  27137. // Useful in tooltip
  27138. el.dataIndex = idx;
  27139. el.dataType = this.dataType;
  27140. el.seriesIndex = hostModel && hostModel.seriesIndex;
  27141. if (el.type === 'group') {
  27142. el.traverse(setItemDataAndSeriesIndex, el);
  27143. }
  27144. }
  27145. this._graphicEls[idx] = el;
  27146. };
  27147. /**
  27148. * @param {number} idx
  27149. * @return {module:zrender/Element}
  27150. */
  27151. listProto.getItemGraphicEl = function (idx) {
  27152. return this._graphicEls[idx];
  27153. };
  27154. /**
  27155. * @param {Function} cb
  27156. * @param {*} context
  27157. */
  27158. listProto.eachItemGraphicEl = function (cb, context) {
  27159. each$1(this._graphicEls, function (el, idx) {
  27160. if (el) {
  27161. cb && cb.call(context, el, idx);
  27162. }
  27163. });
  27164. };
  27165. /**
  27166. * Shallow clone a new list except visual and layout properties, and graph elements.
  27167. * New list only change the indices.
  27168. */
  27169. listProto.cloneShallow = function (list) {
  27170. if (!list) {
  27171. var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
  27172. list = new List(dimensionInfoList, this.hostModel);
  27173. }
  27174. // FIXME
  27175. list._storage = this._storage;
  27176. transferProperties(list, this);
  27177. // Clone will not change the data extent and indices
  27178. if (this._indices) {
  27179. var Ctor = this._indices.constructor;
  27180. list._indices = new Ctor(this._indices);
  27181. }
  27182. else {
  27183. list._indices = null;
  27184. }
  27185. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  27186. return list;
  27187. };
  27188. /**
  27189. * Wrap some method to add more feature
  27190. * @param {string} methodName
  27191. * @param {Function} injectFunction
  27192. */
  27193. listProto.wrapMethod = function (methodName, injectFunction) {
  27194. var originalMethod = this[methodName];
  27195. if (typeof originalMethod !== 'function') {
  27196. return;
  27197. }
  27198. this.__wrappedMethods = this.__wrappedMethods || [];
  27199. this.__wrappedMethods.push(methodName);
  27200. this[methodName] = function () {
  27201. var res = originalMethod.apply(this, arguments);
  27202. return injectFunction.apply(this, [res].concat(slice(arguments)));
  27203. };
  27204. };
  27205. // Methods that create a new list based on this list should be listed here.
  27206. // Notice that those method should `RETURN` the new list.
  27207. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
  27208. // Methods that change indices of this list should be listed here.
  27209. listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
  27210. /*
  27211. * Licensed to the Apache Software Foundation (ASF) under one
  27212. * or more contributor license agreements. See the NOTICE file
  27213. * distributed with this work for additional information
  27214. * regarding copyright ownership. The ASF licenses this file
  27215. * to you under the Apache License, Version 2.0 (the
  27216. * "License"); you may not use this file except in compliance
  27217. * with the License. You may obtain a copy of the License at
  27218. *
  27219. * http://www.apache.org/licenses/LICENSE-2.0
  27220. *
  27221. * Unless required by applicable law or agreed to in writing,
  27222. * software distributed under the License is distributed on an
  27223. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27224. * KIND, either express or implied. See the License for the
  27225. * specific language governing permissions and limitations
  27226. * under the License.
  27227. */
  27228. /**
  27229. * @deprecated
  27230. * Use `echarts/data/helper/createDimensions` instead.
  27231. */
  27232. /**
  27233. * @see {module:echarts/test/ut/spec/data/completeDimensions}
  27234. *
  27235. * Complete the dimensions array, by user defined `dimension` and `encode`,
  27236. * and guessing from the data structure.
  27237. * If no 'value' dimension specified, the first no-named dimension will be
  27238. * named as 'value'.
  27239. *
  27240. * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
  27241. * provides not only dim template, but also default order.
  27242. * properties: 'name', 'type', 'displayName'.
  27243. * `name` of each item provides default coord name.
  27244. * [{dimsDef: [string|Object, ...]}, ...] dimsDef of sysDim item provides default dim name, and
  27245. * provide dims count that the sysDim required.
  27246. * [{ordinalMeta}] can be specified.
  27247. * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious)
  27248. * @param {Object} [opt]
  27249. * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
  27250. * For example: ['asdf', {name, type}, ...].
  27251. * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
  27252. * @param {string} [opt.generateCoord] Generate coord dim with the given name.
  27253. * If not specified, extra dim names will be:
  27254. * 'value', 'value0', 'value1', ...
  27255. * @param {number} [opt.generateCoordCount] By default, the generated dim name is `generateCoord`.
  27256. * If `generateCoordCount` specified, the generated dim names will be:
  27257. * `generateCoord` + 0, `generateCoord` + 1, ...
  27258. * can be Infinity, indicate that use all of the remain columns.
  27259. * @param {number} [opt.dimCount] If not specified, guess by the first data item.
  27260. * @param {number} [opt.encodeDefaulter] If not specified, auto find the next available data dim.
  27261. * @return {Array.<Object>} [{
  27262. * name: string mandatory,
  27263. * displayName: string, the origin name in dimsDef, see source helper.
  27264. * If displayName given, the tooltip will displayed vertically.
  27265. * coordDim: string mandatory,
  27266. * coordDimIndex: number mandatory,
  27267. * type: string optional,
  27268. * otherDims: { never null/undefined
  27269. * tooltip: number optional,
  27270. * label: number optional,
  27271. * itemName: number optional,
  27272. * seriesName: number optional,
  27273. * },
  27274. * isExtraCoord: boolean true if coord is generated
  27275. * (not specified in encode and not series specified)
  27276. * other props ...
  27277. * }]
  27278. */
  27279. function completeDimensions(sysDims, source, opt) {
  27280. if (!Source.isInstance(source)) {
  27281. source = Source.seriesDataToSource(source);
  27282. }
  27283. opt = opt || {};
  27284. sysDims = (sysDims || []).slice();
  27285. var dimsDef = (opt.dimsDef || []).slice();
  27286. var encodeDef = createHashMap(opt.encodeDef);
  27287. var dataDimNameMap = createHashMap();
  27288. var coordDimNameMap = createHashMap();
  27289. // var valueCandidate;
  27290. var result = [];
  27291. var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount);
  27292. // Apply user defined dims (`name` and `type`) and init result.
  27293. for (var i = 0; i < dimCount; i++) {
  27294. var dimDefItem = dimsDef[i] = extend(
  27295. {}, isObject$1(dimsDef[i]) ? dimsDef[i] : {name: dimsDef[i]}
  27296. );
  27297. var userDimName = dimDefItem.name;
  27298. var resultItem = result[i] = {otherDims: {}};
  27299. // Name will be applied later for avoiding duplication.
  27300. if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
  27301. // Only if `series.dimensions` is defined in option
  27302. // displayName, will be set, and dimension will be diplayed vertically in
  27303. // tooltip by default.
  27304. resultItem.name = resultItem.displayName = userDimName;
  27305. dataDimNameMap.set(userDimName, i);
  27306. }
  27307. dimDefItem.type != null && (resultItem.type = dimDefItem.type);
  27308. dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
  27309. }
  27310. // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
  27311. encodeDef.each(function (dataDims, coordDim) {
  27312. dataDims = normalizeToArray(dataDims).slice();
  27313. // Note: It is allowed that `dataDims.length` is `0`, e.g., options is
  27314. // `{encode: {x: -1, y: 1}}`. Should not filter anything in
  27315. // this case.
  27316. if (dataDims.length === 1 && dataDims[0] < 0) {
  27317. encodeDef.set(coordDim, false);
  27318. return;
  27319. }
  27320. var validDataDims = encodeDef.set(coordDim, []);
  27321. each$1(dataDims, function (resultDimIdx, idx) {
  27322. // The input resultDimIdx can be dim name or index.
  27323. isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
  27324. if (resultDimIdx != null && resultDimIdx < dimCount) {
  27325. validDataDims[idx] = resultDimIdx;
  27326. applyDim(result[resultDimIdx], coordDim, idx);
  27327. }
  27328. });
  27329. });
  27330. // Apply templetes and default order from `sysDims`.
  27331. var availDimIdx = 0;
  27332. each$1(sysDims, function (sysDimItem, sysDimIndex) {
  27333. var coordDim;
  27334. var sysDimItem;
  27335. var sysDimItemDimsDef;
  27336. var sysDimItemOtherDims;
  27337. if (isString(sysDimItem)) {
  27338. coordDim = sysDimItem;
  27339. sysDimItem = {};
  27340. }
  27341. else {
  27342. coordDim = sysDimItem.name;
  27343. var ordinalMeta = sysDimItem.ordinalMeta;
  27344. sysDimItem.ordinalMeta = null;
  27345. sysDimItem = clone(sysDimItem);
  27346. sysDimItem.ordinalMeta = ordinalMeta;
  27347. // `coordDimIndex` should not be set directly.
  27348. sysDimItemDimsDef = sysDimItem.dimsDef;
  27349. sysDimItemOtherDims = sysDimItem.otherDims;
  27350. sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex
  27351. = sysDimItem.dimsDef = sysDimItem.otherDims = null;
  27352. }
  27353. var dataDims = encodeDef.get(coordDim);
  27354. // negative resultDimIdx means no need to mapping.
  27355. if (dataDims === false) {
  27356. return;
  27357. }
  27358. var dataDims = normalizeToArray(dataDims);
  27359. // dimensions provides default dim sequences.
  27360. if (!dataDims.length) {
  27361. for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
  27362. while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
  27363. availDimIdx++;
  27364. }
  27365. availDimIdx < result.length && dataDims.push(availDimIdx++);
  27366. }
  27367. }
  27368. // Apply templates.
  27369. each$1(dataDims, function (resultDimIdx, coordDimIndex) {
  27370. var resultItem = result[resultDimIdx];
  27371. applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
  27372. if (resultItem.name == null && sysDimItemDimsDef) {
  27373. var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];
  27374. !isObject$1(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {name: sysDimItemDimsDefItem});
  27375. resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;
  27376. resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;
  27377. }
  27378. // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
  27379. sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
  27380. });
  27381. });
  27382. function applyDim(resultItem, coordDim, coordDimIndex) {
  27383. if (OTHER_DIMENSIONS.get(coordDim) != null) {
  27384. resultItem.otherDims[coordDim] = coordDimIndex;
  27385. }
  27386. else {
  27387. resultItem.coordDim = coordDim;
  27388. resultItem.coordDimIndex = coordDimIndex;
  27389. coordDimNameMap.set(coordDim, true);
  27390. }
  27391. }
  27392. // Make sure the first extra dim is 'value'.
  27393. var generateCoord = opt.generateCoord;
  27394. var generateCoordCount = opt.generateCoordCount;
  27395. var fromZero = generateCoordCount != null;
  27396. generateCoordCount = generateCoord ? (generateCoordCount || 1) : 0;
  27397. var extra = generateCoord || 'value';
  27398. // Set dim `name` and other `coordDim` and other props.
  27399. for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
  27400. var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};
  27401. var coordDim = resultItem.coordDim;
  27402. if (coordDim == null) {
  27403. resultItem.coordDim = genName(
  27404. extra, coordDimNameMap, fromZero
  27405. );
  27406. resultItem.coordDimIndex = 0;
  27407. if (!generateCoord || generateCoordCount <= 0) {
  27408. resultItem.isExtraCoord = true;
  27409. }
  27410. generateCoordCount--;
  27411. }
  27412. resultItem.name == null && (resultItem.name = genName(
  27413. resultItem.coordDim,
  27414. dataDimNameMap
  27415. ));
  27416. if (resultItem.type == null && guessOrdinal(source, resultDimIdx, resultItem.name)) {
  27417. resultItem.type = 'ordinal';
  27418. }
  27419. }
  27420. return result;
  27421. }
  27422. // ??? TODO
  27423. // Originally detect dimCount by data[0]. Should we
  27424. // optimize it to only by sysDims and dimensions and encode.
  27425. // So only necessary dims will be initialized.
  27426. // But
  27427. // (1) custom series should be considered. where other dims
  27428. // may be visited.
  27429. // (2) sometimes user need to calcualte bubble size or use visualMap
  27430. // on other dimensions besides coordSys needed.
  27431. // So, dims that is not used by system, should be shared in storage?
  27432. function getDimCount(source, sysDims, dimsDef, optDimCount) {
  27433. // Note that the result dimCount should not small than columns count
  27434. // of data, otherwise `dataDimNameMap` checking will be incorrect.
  27435. var dimCount = Math.max(
  27436. source.dimensionsDetectCount || 1,
  27437. sysDims.length,
  27438. dimsDef.length,
  27439. optDimCount || 0
  27440. );
  27441. each$1(sysDims, function (sysDimItem) {
  27442. var sysDimItemDimsDef = sysDimItem.dimsDef;
  27443. sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
  27444. });
  27445. return dimCount;
  27446. }
  27447. function genName(name, map$$1, fromZero) {
  27448. if (fromZero || map$$1.get(name) != null) {
  27449. var i = 0;
  27450. while (map$$1.get(name + i) != null) {
  27451. i++;
  27452. }
  27453. name += i;
  27454. }
  27455. map$$1.set(name, true);
  27456. return name;
  27457. }
  27458. /*
  27459. * Licensed to the Apache Software Foundation (ASF) under one
  27460. * or more contributor license agreements. See the NOTICE file
  27461. * distributed with this work for additional information
  27462. * regarding copyright ownership. The ASF licenses this file
  27463. * to you under the Apache License, Version 2.0 (the
  27464. * "License"); you may not use this file except in compliance
  27465. * with the License. You may obtain a copy of the License at
  27466. *
  27467. * http://www.apache.org/licenses/LICENSE-2.0
  27468. *
  27469. * Unless required by applicable law or agreed to in writing,
  27470. * software distributed under the License is distributed on an
  27471. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27472. * KIND, either express or implied. See the License for the
  27473. * specific language governing permissions and limitations
  27474. * under the License.
  27475. */
  27476. /**
  27477. * Substitute `completeDimensions`.
  27478. * `completeDimensions` is to be deprecated.
  27479. */
  27480. /**
  27481. * @param {module:echarts/data/Source|module:echarts/data/List} source or data.
  27482. * @param {Object|Array} [opt]
  27483. * @param {Array.<string|Object>} [opt.coordDimensions=[]]
  27484. * @param {number} [opt.dimensionsCount]
  27485. * @param {string} [opt.generateCoord]
  27486. * @param {string} [opt.generateCoordCount]
  27487. * @param {Array.<string|Object>} [opt.dimensionsDefine=source.dimensionsDefine] Overwrite source define.
  27488. * @param {Object|HashMap} [opt.encodeDefine=source.encodeDefine] Overwrite source define.
  27489. * @return {Array.<Object>} dimensionsInfo
  27490. */
  27491. var createDimensions = function (source, opt) {
  27492. opt = opt || {};
  27493. return completeDimensions(opt.coordDimensions || [], source, {
  27494. dimsDef: opt.dimensionsDefine || source.dimensionsDefine,
  27495. encodeDef: opt.encodeDefine || source.encodeDefine,
  27496. dimCount: opt.dimensionsCount,
  27497. generateCoord: opt.generateCoord,
  27498. generateCoordCount: opt.generateCoordCount
  27499. });
  27500. };
  27501. /*
  27502. * Licensed to the Apache Software Foundation (ASF) under one
  27503. * or more contributor license agreements. See the NOTICE file
  27504. * distributed with this work for additional information
  27505. * regarding copyright ownership. The ASF licenses this file
  27506. * to you under the Apache License, Version 2.0 (the
  27507. * "License"); you may not use this file except in compliance
  27508. * with the License. You may obtain a copy of the License at
  27509. *
  27510. * http://www.apache.org/licenses/LICENSE-2.0
  27511. *
  27512. * Unless required by applicable law or agreed to in writing,
  27513. * software distributed under the License is distributed on an
  27514. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27515. * KIND, either express or implied. See the License for the
  27516. * specific language governing permissions and limitations
  27517. * under the License.
  27518. */
  27519. /**
  27520. * Note that it is too complicated to support 3d stack by value
  27521. * (have to create two-dimension inverted index), so in 3d case
  27522. * we just support that stacked by index.
  27523. *
  27524. * @param {module:echarts/model/Series} seriesModel
  27525. * @param {Array.<string|Object>} dimensionInfoList The same as the input of <module:echarts/data/List>.
  27526. * The input dimensionInfoList will be modified.
  27527. * @param {Object} [opt]
  27528. * @param {boolean} [opt.stackedCoordDimension=''] Specify a coord dimension if needed.
  27529. * @param {boolean} [opt.byIndex=false]
  27530. * @return {Object} calculationInfo
  27531. * {
  27532. * stackedDimension: string
  27533. * stackedByDimension: string
  27534. * isStackedByIndex: boolean
  27535. * stackedOverDimension: string
  27536. * stackResultDimension: string
  27537. * }
  27538. */
  27539. function enableDataStack(seriesModel, dimensionInfoList, opt) {
  27540. opt = opt || {};
  27541. var byIndex = opt.byIndex;
  27542. var stackedCoordDimension = opt.stackedCoordDimension;
  27543. // Compatibal: when `stack` is set as '', do not stack.
  27544. var mayStack = !!(seriesModel && seriesModel.get('stack'));
  27545. var stackedByDimInfo;
  27546. var stackedDimInfo;
  27547. var stackResultDimension;
  27548. var stackedOverDimension;
  27549. each$1(dimensionInfoList, function (dimensionInfo, index) {
  27550. if (isString(dimensionInfo)) {
  27551. dimensionInfoList[index] = dimensionInfo = {name: dimensionInfo};
  27552. }
  27553. if (mayStack && !dimensionInfo.isExtraCoord) {
  27554. // Find the first ordinal dimension as the stackedByDimInfo.
  27555. if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {
  27556. stackedByDimInfo = dimensionInfo;
  27557. }
  27558. // Find the first stackable dimension as the stackedDimInfo.
  27559. if (!stackedDimInfo
  27560. && dimensionInfo.type !== 'ordinal'
  27561. && dimensionInfo.type !== 'time'
  27562. && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)
  27563. ) {
  27564. stackedDimInfo = dimensionInfo;
  27565. }
  27566. }
  27567. });
  27568. if (stackedDimInfo && !byIndex && !stackedByDimInfo) {
  27569. // Compatible with previous design, value axis (time axis) only stack by index.
  27570. // It may make sense if the user provides elaborately constructed data.
  27571. byIndex = true;
  27572. }
  27573. // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.
  27574. // That put stack logic in List is for using conveniently in echarts extensions, but it
  27575. // might not be a good way.
  27576. if (stackedDimInfo) {
  27577. // Use a weird name that not duplicated with other names.
  27578. stackResultDimension = '__\0ecstackresult';
  27579. stackedOverDimension = '__\0ecstackedover';
  27580. // Create inverted index to fast query index by value.
  27581. if (stackedByDimInfo) {
  27582. stackedByDimInfo.createInvertedIndices = true;
  27583. }
  27584. var stackedDimCoordDim = stackedDimInfo.coordDim;
  27585. var stackedDimType = stackedDimInfo.type;
  27586. var stackedDimCoordIndex = 0;
  27587. each$1(dimensionInfoList, function (dimensionInfo) {
  27588. if (dimensionInfo.coordDim === stackedDimCoordDim) {
  27589. stackedDimCoordIndex++;
  27590. }
  27591. });
  27592. dimensionInfoList.push({
  27593. name: stackResultDimension,
  27594. coordDim: stackedDimCoordDim,
  27595. coordDimIndex: stackedDimCoordIndex,
  27596. type: stackedDimType,
  27597. isExtraCoord: true,
  27598. isCalculationCoord: true
  27599. });
  27600. stackedDimCoordIndex++;
  27601. dimensionInfoList.push({
  27602. name: stackedOverDimension,
  27603. // This dimension contains stack base (generally, 0), so do not set it as
  27604. // `stackedDimCoordDim` to avoid extent calculation, consider log scale.
  27605. coordDim: stackedOverDimension,
  27606. coordDimIndex: stackedDimCoordIndex,
  27607. type: stackedDimType,
  27608. isExtraCoord: true,
  27609. isCalculationCoord: true
  27610. });
  27611. }
  27612. return {
  27613. stackedDimension: stackedDimInfo && stackedDimInfo.name,
  27614. stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,
  27615. isStackedByIndex: byIndex,
  27616. stackedOverDimension: stackedOverDimension,
  27617. stackResultDimension: stackResultDimension
  27618. };
  27619. }
  27620. /**
  27621. * @param {module:echarts/data/List} data
  27622. * @param {string} stackedDim
  27623. */
  27624. function isDimensionStacked(data, stackedDim /*, stackedByDim*/) {
  27625. // Each single series only maps to one pair of axis. So we do not need to
  27626. // check stackByDim, whatever stacked by a dimension or stacked by index.
  27627. return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');
  27628. // && (
  27629. // stackedByDim != null
  27630. // ? stackedByDim === data.getCalculationInfo('stackedByDimension')
  27631. // : data.getCalculationInfo('isStackedByIndex')
  27632. // );
  27633. }
  27634. /**
  27635. * @param {module:echarts/data/List} data
  27636. * @param {string} targetDim
  27637. * @param {string} [stackedByDim] If not input this parameter, check whether
  27638. * stacked by index.
  27639. * @return {string} dimension
  27640. */
  27641. function getStackedDimension(data, targetDim) {
  27642. return isDimensionStacked(data, targetDim)
  27643. ? data.getCalculationInfo('stackResultDimension')
  27644. : targetDim;
  27645. }
  27646. /*
  27647. * Licensed to the Apache Software Foundation (ASF) under one
  27648. * or more contributor license agreements. See the NOTICE file
  27649. * distributed with this work for additional information
  27650. * regarding copyright ownership. The ASF licenses this file
  27651. * to you under the Apache License, Version 2.0 (the
  27652. * "License"); you may not use this file except in compliance
  27653. * with the License. You may obtain a copy of the License at
  27654. *
  27655. * http://www.apache.org/licenses/LICENSE-2.0
  27656. *
  27657. * Unless required by applicable law or agreed to in writing,
  27658. * software distributed under the License is distributed on an
  27659. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27660. * KIND, either express or implied. See the License for the
  27661. * specific language governing permissions and limitations
  27662. * under the License.
  27663. */
  27664. /**
  27665. * @param {module:echarts/data/Source|Array} source Or raw data.
  27666. * @param {module:echarts/model/Series} seriesModel
  27667. * @param {Object} [opt]
  27668. * @param {string} [opt.generateCoord]
  27669. */
  27670. function createListFromArray(source, seriesModel, opt) {
  27671. opt = opt || {};
  27672. if (!Source.isInstance(source)) {
  27673. source = Source.seriesDataToSource(source);
  27674. }
  27675. var coordSysName = seriesModel.get('coordinateSystem');
  27676. var registeredCoordSys = CoordinateSystemManager.get(coordSysName);
  27677. var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
  27678. var coordSysDimDefs;
  27679. if (coordSysDefine) {
  27680. coordSysDimDefs = map(coordSysDefine.coordSysDims, function (dim) {
  27681. var dimInfo = {name: dim};
  27682. var axisModel = coordSysDefine.axisMap.get(dim);
  27683. if (axisModel) {
  27684. var axisType = axisModel.get('type');
  27685. dimInfo.type = getDimensionTypeByAxis(axisType);
  27686. // dimInfo.stackable = isStackable(axisType);
  27687. }
  27688. return dimInfo;
  27689. });
  27690. }
  27691. if (!coordSysDimDefs) {
  27692. // Get dimensions from registered coordinate system
  27693. coordSysDimDefs = (registeredCoordSys && (
  27694. registeredCoordSys.getDimensionsInfo
  27695. ? registeredCoordSys.getDimensionsInfo()
  27696. : registeredCoordSys.dimensions.slice()
  27697. )) || ['x', 'y'];
  27698. }
  27699. var dimInfoList = createDimensions(source, {
  27700. coordDimensions: coordSysDimDefs,
  27701. generateCoord: opt.generateCoord
  27702. });
  27703. var firstCategoryDimIndex;
  27704. var hasNameEncode;
  27705. coordSysDefine && each$1(dimInfoList, function (dimInfo, dimIndex) {
  27706. var coordDim = dimInfo.coordDim;
  27707. var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim);
  27708. if (categoryAxisModel) {
  27709. if (firstCategoryDimIndex == null) {
  27710. firstCategoryDimIndex = dimIndex;
  27711. }
  27712. dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();
  27713. }
  27714. if (dimInfo.otherDims.itemName != null) {
  27715. hasNameEncode = true;
  27716. }
  27717. });
  27718. if (!hasNameEncode && firstCategoryDimIndex != null) {
  27719. dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;
  27720. }
  27721. var stackCalculationInfo = enableDataStack(seriesModel, dimInfoList);
  27722. var list = new List(dimInfoList, seriesModel);
  27723. list.setCalculationInfo(stackCalculationInfo);
  27724. var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source))
  27725. ? function (itemOpt, dimName, dataIndex, dimIndex) {
  27726. // Use dataIndex as ordinal value in categoryAxis
  27727. return dimIndex === firstCategoryDimIndex
  27728. ? dataIndex
  27729. : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);
  27730. }
  27731. : null;
  27732. list.hasItemOption = false;
  27733. list.initData(source, null, dimValueGetter);
  27734. return list;
  27735. }
  27736. function isNeedCompleteOrdinalData(source) {
  27737. if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  27738. var sampleItem = firstDataNotNull(source.data || []);
  27739. return sampleItem != null
  27740. && !isArray(getDataItemValue(sampleItem));
  27741. }
  27742. }
  27743. function firstDataNotNull(data) {
  27744. var i = 0;
  27745. while (i < data.length && data[i] == null) {
  27746. i++;
  27747. }
  27748. return data[i];
  27749. }
  27750. /*
  27751. * Licensed to the Apache Software Foundation (ASF) under one
  27752. * or more contributor license agreements. See the NOTICE file
  27753. * distributed with this work for additional information
  27754. * regarding copyright ownership. The ASF licenses this file
  27755. * to you under the Apache License, Version 2.0 (the
  27756. * "License"); you may not use this file except in compliance
  27757. * with the License. You may obtain a copy of the License at
  27758. *
  27759. * http://www.apache.org/licenses/LICENSE-2.0
  27760. *
  27761. * Unless required by applicable law or agreed to in writing,
  27762. * software distributed under the License is distributed on an
  27763. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27764. * KIND, either express or implied. See the License for the
  27765. * specific language governing permissions and limitations
  27766. * under the License.
  27767. */
  27768. /**
  27769. * // Scale class management
  27770. * @module echarts/scale/Scale
  27771. */
  27772. /**
  27773. * @param {Object} [setting]
  27774. */
  27775. function Scale(setting) {
  27776. this._setting = setting || {};
  27777. /**
  27778. * Extent
  27779. * @type {Array.<number>}
  27780. * @protected
  27781. */
  27782. this._extent = [Infinity, -Infinity];
  27783. /**
  27784. * Step is calculated in adjustExtent
  27785. * @type {Array.<number>}
  27786. * @protected
  27787. */
  27788. this._interval = 0;
  27789. this.init && this.init.apply(this, arguments);
  27790. }
  27791. /**
  27792. * Parse input val to valid inner number.
  27793. * @param {*} val
  27794. * @return {number}
  27795. */
  27796. Scale.prototype.parse = function (val) {
  27797. // Notice: This would be a trap here, If the implementation
  27798. // of this method depends on extent, and this method is used
  27799. // before extent set (like in dataZoom), it would be wrong.
  27800. // Nevertheless, parse does not depend on extent generally.
  27801. return val;
  27802. };
  27803. Scale.prototype.getSetting = function (name) {
  27804. return this._setting[name];
  27805. };
  27806. Scale.prototype.contain = function (val) {
  27807. var extent = this._extent;
  27808. return val >= extent[0] && val <= extent[1];
  27809. };
  27810. /**
  27811. * Normalize value to linear [0, 1], return 0.5 if extent span is 0
  27812. * @param {number} val
  27813. * @return {number}
  27814. */
  27815. Scale.prototype.normalize = function (val) {
  27816. var extent = this._extent;
  27817. if (extent[1] === extent[0]) {
  27818. return 0.5;
  27819. }
  27820. return (val - extent[0]) / (extent[1] - extent[0]);
  27821. };
  27822. /**
  27823. * Scale normalized value
  27824. * @param {number} val
  27825. * @return {number}
  27826. */
  27827. Scale.prototype.scale = function (val) {
  27828. var extent = this._extent;
  27829. return val * (extent[1] - extent[0]) + extent[0];
  27830. };
  27831. /**
  27832. * Set extent from data
  27833. * @param {Array.<number>} other
  27834. */
  27835. Scale.prototype.unionExtent = function (other) {
  27836. var extent = this._extent;
  27837. other[0] < extent[0] && (extent[0] = other[0]);
  27838. other[1] > extent[1] && (extent[1] = other[1]);
  27839. // not setExtent because in log axis it may transformed to power
  27840. // this.setExtent(extent[0], extent[1]);
  27841. };
  27842. /**
  27843. * Set extent from data
  27844. * @param {module:echarts/data/List} data
  27845. * @param {string} dim
  27846. */
  27847. Scale.prototype.unionExtentFromData = function (data, dim) {
  27848. this.unionExtent(data.getApproximateExtent(dim));
  27849. };
  27850. /**
  27851. * Get extent
  27852. * @return {Array.<number>}
  27853. */
  27854. Scale.prototype.getExtent = function () {
  27855. return this._extent.slice();
  27856. };
  27857. /**
  27858. * Set extent
  27859. * @param {number} start
  27860. * @param {number} end
  27861. */
  27862. Scale.prototype.setExtent = function (start, end) {
  27863. var thisExtent = this._extent;
  27864. if (!isNaN(start)) {
  27865. thisExtent[0] = start;
  27866. }
  27867. if (!isNaN(end)) {
  27868. thisExtent[1] = end;
  27869. }
  27870. };
  27871. /**
  27872. * When axis extent depends on data and no data exists,
  27873. * axis ticks should not be drawn, which is named 'blank'.
  27874. */
  27875. Scale.prototype.isBlank = function () {
  27876. return this._isBlank;
  27877. },
  27878. /**
  27879. * When axis extent depends on data and no data exists,
  27880. * axis ticks should not be drawn, which is named 'blank'.
  27881. */
  27882. Scale.prototype.setBlank = function (isBlank) {
  27883. this._isBlank = isBlank;
  27884. };
  27885. /**
  27886. * @abstract
  27887. * @param {*} tick
  27888. * @return {string} label of the tick.
  27889. */
  27890. Scale.prototype.getLabel = null;
  27891. enableClassExtend(Scale);
  27892. enableClassManagement(Scale, {
  27893. registerWhenExtend: true
  27894. });
  27895. /*
  27896. * Licensed to the Apache Software Foundation (ASF) under one
  27897. * or more contributor license agreements. See the NOTICE file
  27898. * distributed with this work for additional information
  27899. * regarding copyright ownership. The ASF licenses this file
  27900. * to you under the Apache License, Version 2.0 (the
  27901. * "License"); you may not use this file except in compliance
  27902. * with the License. You may obtain a copy of the License at
  27903. *
  27904. * http://www.apache.org/licenses/LICENSE-2.0
  27905. *
  27906. * Unless required by applicable law or agreed to in writing,
  27907. * software distributed under the License is distributed on an
  27908. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  27909. * KIND, either express or implied. See the License for the
  27910. * specific language governing permissions and limitations
  27911. * under the License.
  27912. */
  27913. /**
  27914. * @constructor
  27915. * @param {Object} [opt]
  27916. * @param {Object} [opt.categories=[]]
  27917. * @param {Object} [opt.needCollect=false]
  27918. * @param {Object} [opt.deduplication=false]
  27919. */
  27920. function OrdinalMeta(opt) {
  27921. /**
  27922. * @readOnly
  27923. * @type {Array.<string>}
  27924. */
  27925. this.categories = opt.categories || [];
  27926. /**
  27927. * @private
  27928. * @type {boolean}
  27929. */
  27930. this._needCollect = opt.needCollect;
  27931. /**
  27932. * @private
  27933. * @type {boolean}
  27934. */
  27935. this._deduplication = opt.deduplication;
  27936. /**
  27937. * @private
  27938. * @type {boolean}
  27939. */
  27940. this._map;
  27941. }
  27942. /**
  27943. * @param {module:echarts/model/Model} axisModel
  27944. * @return {module:echarts/data/OrdinalMeta}
  27945. */
  27946. OrdinalMeta.createByAxisModel = function (axisModel) {
  27947. var option = axisModel.option;
  27948. var data = option.data;
  27949. var categories = data && map(data, getName);
  27950. return new OrdinalMeta({
  27951. categories: categories,
  27952. needCollect: !categories,
  27953. // deduplication is default in axis.
  27954. deduplication: option.dedplication !== false
  27955. });
  27956. };
  27957. var proto$1 = OrdinalMeta.prototype;
  27958. /**
  27959. * @param {string} category
  27960. * @return {number} ordinal
  27961. */
  27962. proto$1.getOrdinal = function (category) {
  27963. return getOrCreateMap(this).get(category);
  27964. };
  27965. /**
  27966. * @param {*} category
  27967. * @return {number} The ordinal. If not found, return NaN.
  27968. */
  27969. proto$1.parseAndCollect = function (category) {
  27970. var index;
  27971. var needCollect = this._needCollect;
  27972. // The value of category dim can be the index of the given category set.
  27973. // This feature is only supported when !needCollect, because we should
  27974. // consider a common case: a value is 2017, which is a number but is
  27975. // expected to be tread as a category. This case usually happen in dataset,
  27976. // where it happent to be no need of the index feature.
  27977. if (typeof category !== 'string' && !needCollect) {
  27978. return category;
  27979. }
  27980. // Optimize for the scenario:
  27981. // category is ['2012-01-01', '2012-01-02', ...], where the input
  27982. // data has been ensured not duplicate and is large data.
  27983. // Notice, if a dataset dimension provide categroies, usually echarts
  27984. // should remove duplication except user tell echarts dont do that
  27985. // (set axis.deduplication = false), because echarts do not know whether
  27986. // the values in the category dimension has duplication (consider the
  27987. // parallel-aqi example)
  27988. if (needCollect && !this._deduplication) {
  27989. index = this.categories.length;
  27990. this.categories[index] = category;
  27991. return index;
  27992. }
  27993. var map$$1 = getOrCreateMap(this);
  27994. index = map$$1.get(category);
  27995. if (index == null) {
  27996. if (needCollect) {
  27997. index = this.categories.length;
  27998. this.categories[index] = category;
  27999. map$$1.set(category, index);
  28000. }
  28001. else {
  28002. index = NaN;
  28003. }
  28004. }
  28005. return index;
  28006. };
  28007. // Consider big data, do not create map until needed.
  28008. function getOrCreateMap(ordinalMeta) {
  28009. return ordinalMeta._map || (
  28010. ordinalMeta._map = createHashMap(ordinalMeta.categories)
  28011. );
  28012. }
  28013. function getName(obj) {
  28014. if (isObject$1(obj) && obj.value != null) {
  28015. return obj.value;
  28016. }
  28017. else {
  28018. return obj + '';
  28019. }
  28020. }
  28021. /*
  28022. * Licensed to the Apache Software Foundation (ASF) under one
  28023. * or more contributor license agreements. See the NOTICE file
  28024. * distributed with this work for additional information
  28025. * regarding copyright ownership. The ASF licenses this file
  28026. * to you under the Apache License, Version 2.0 (the
  28027. * "License"); you may not use this file except in compliance
  28028. * with the License. You may obtain a copy of the License at
  28029. *
  28030. * http://www.apache.org/licenses/LICENSE-2.0
  28031. *
  28032. * Unless required by applicable law or agreed to in writing,
  28033. * software distributed under the License is distributed on an
  28034. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28035. * KIND, either express or implied. See the License for the
  28036. * specific language governing permissions and limitations
  28037. * under the License.
  28038. */
  28039. /**
  28040. * Linear continuous scale
  28041. * @module echarts/coord/scale/Ordinal
  28042. *
  28043. * http://en.wikipedia.org/wiki/Level_of_measurement
  28044. */
  28045. // FIXME only one data
  28046. var scaleProto = Scale.prototype;
  28047. var OrdinalScale = Scale.extend({
  28048. type: 'ordinal',
  28049. /**
  28050. * @param {module:echarts/data/OrdianlMeta|Array.<string>} ordinalMeta
  28051. */
  28052. init: function (ordinalMeta, extent) {
  28053. // Caution: Should not use instanceof, consider ec-extensions using
  28054. // import approach to get OrdinalMeta class.
  28055. if (!ordinalMeta || isArray(ordinalMeta)) {
  28056. ordinalMeta = new OrdinalMeta({categories: ordinalMeta});
  28057. }
  28058. this._ordinalMeta = ordinalMeta;
  28059. this._extent = extent || [0, ordinalMeta.categories.length - 1];
  28060. },
  28061. parse: function (val) {
  28062. return typeof val === 'string'
  28063. ? this._ordinalMeta.getOrdinal(val)
  28064. // val might be float.
  28065. : Math.round(val);
  28066. },
  28067. contain: function (rank) {
  28068. rank = this.parse(rank);
  28069. return scaleProto.contain.call(this, rank)
  28070. && this._ordinalMeta.categories[rank] != null;
  28071. },
  28072. /**
  28073. * Normalize given rank or name to linear [0, 1]
  28074. * @param {number|string} [val]
  28075. * @return {number}
  28076. */
  28077. normalize: function (val) {
  28078. return scaleProto.normalize.call(this, this.parse(val));
  28079. },
  28080. scale: function (val) {
  28081. return Math.round(scaleProto.scale.call(this, val));
  28082. },
  28083. /**
  28084. * @return {Array}
  28085. */
  28086. getTicks: function () {
  28087. var ticks = [];
  28088. var extent = this._extent;
  28089. var rank = extent[0];
  28090. while (rank <= extent[1]) {
  28091. ticks.push(rank);
  28092. rank++;
  28093. }
  28094. return ticks;
  28095. },
  28096. /**
  28097. * Get item on rank n
  28098. * @param {number} n
  28099. * @return {string}
  28100. */
  28101. getLabel: function (n) {
  28102. if (!this.isBlank()) {
  28103. // Note that if no data, ordinalMeta.categories is an empty array.
  28104. return this._ordinalMeta.categories[n];
  28105. }
  28106. },
  28107. /**
  28108. * @return {number}
  28109. */
  28110. count: function () {
  28111. return this._extent[1] - this._extent[0] + 1;
  28112. },
  28113. /**
  28114. * @override
  28115. */
  28116. unionExtentFromData: function (data, dim) {
  28117. this.unionExtent(data.getApproximateExtent(dim));
  28118. },
  28119. getOrdinalMeta: function () {
  28120. return this._ordinalMeta;
  28121. },
  28122. niceTicks: noop,
  28123. niceExtent: noop
  28124. });
  28125. /**
  28126. * @return {module:echarts/scale/Time}
  28127. */
  28128. OrdinalScale.create = function () {
  28129. return new OrdinalScale();
  28130. };
  28131. /*
  28132. * Licensed to the Apache Software Foundation (ASF) under one
  28133. * or more contributor license agreements. See the NOTICE file
  28134. * distributed with this work for additional information
  28135. * regarding copyright ownership. The ASF licenses this file
  28136. * to you under the Apache License, Version 2.0 (the
  28137. * "License"); you may not use this file except in compliance
  28138. * with the License. You may obtain a copy of the License at
  28139. *
  28140. * http://www.apache.org/licenses/LICENSE-2.0
  28141. *
  28142. * Unless required by applicable law or agreed to in writing,
  28143. * software distributed under the License is distributed on an
  28144. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28145. * KIND, either express or implied. See the License for the
  28146. * specific language governing permissions and limitations
  28147. * under the License.
  28148. */
  28149. /**
  28150. * For testable.
  28151. */
  28152. var roundNumber$1 = round$2;
  28153. /**
  28154. * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
  28155. * Should be extent[0] < extent[1].
  28156. * @param {number} splitNumber splitNumber should be >= 1.
  28157. * @param {number} [minInterval]
  28158. * @param {number} [maxInterval]
  28159. * @return {Object} {interval, intervalPrecision, niceTickExtent}
  28160. */
  28161. function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {
  28162. var result = {};
  28163. var span = extent[1] - extent[0];
  28164. var interval = result.interval = nice(span / splitNumber, true);
  28165. if (minInterval != null && interval < minInterval) {
  28166. interval = result.interval = minInterval;
  28167. }
  28168. if (maxInterval != null && interval > maxInterval) {
  28169. interval = result.interval = maxInterval;
  28170. }
  28171. // Tow more digital for tick.
  28172. var precision = result.intervalPrecision = getIntervalPrecision(interval);
  28173. // Niced extent inside original extent
  28174. var niceTickExtent = result.niceTickExtent = [
  28175. roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision),
  28176. roundNumber$1(Math.floor(extent[1] / interval) * interval, precision)
  28177. ];
  28178. fixExtent(niceTickExtent, extent);
  28179. return result;
  28180. }
  28181. /**
  28182. * @param {number} interval
  28183. * @return {number} interval precision
  28184. */
  28185. function getIntervalPrecision(interval) {
  28186. // Tow more digital for tick.
  28187. return getPrecisionSafe(interval) + 2;
  28188. }
  28189. function clamp(niceTickExtent, idx, extent) {
  28190. niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
  28191. }
  28192. // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
  28193. function fixExtent(niceTickExtent, extent) {
  28194. !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
  28195. !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
  28196. clamp(niceTickExtent, 0, extent);
  28197. clamp(niceTickExtent, 1, extent);
  28198. if (niceTickExtent[0] > niceTickExtent[1]) {
  28199. niceTickExtent[0] = niceTickExtent[1];
  28200. }
  28201. }
  28202. function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {
  28203. var ticks = [];
  28204. // If interval is 0, return [];
  28205. if (!interval) {
  28206. return ticks;
  28207. }
  28208. // Consider this case: using dataZoom toolbox, zoom and zoom.
  28209. var safeLimit = 10000;
  28210. if (extent[0] < niceTickExtent[0]) {
  28211. ticks.push(extent[0]);
  28212. }
  28213. var tick = niceTickExtent[0];
  28214. while (tick <= niceTickExtent[1]) {
  28215. ticks.push(tick);
  28216. // Avoid rounding error
  28217. tick = roundNumber$1(tick + interval, intervalPrecision);
  28218. if (tick === ticks[ticks.length - 1]) {
  28219. // Consider out of safe float point, e.g.,
  28220. // -3711126.9907707 + 2e-10 === -3711126.9907707
  28221. break;
  28222. }
  28223. if (ticks.length > safeLimit) {
  28224. return [];
  28225. }
  28226. }
  28227. // Consider this case: the last item of ticks is smaller
  28228. // than niceTickExtent[1] and niceTickExtent[1] === extent[1].
  28229. if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
  28230. ticks.push(extent[1]);
  28231. }
  28232. return ticks;
  28233. }
  28234. /*
  28235. * Licensed to the Apache Software Foundation (ASF) under one
  28236. * or more contributor license agreements. See the NOTICE file
  28237. * distributed with this work for additional information
  28238. * regarding copyright ownership. The ASF licenses this file
  28239. * to you under the Apache License, Version 2.0 (the
  28240. * "License"); you may not use this file except in compliance
  28241. * with the License. You may obtain a copy of the License at
  28242. *
  28243. * http://www.apache.org/licenses/LICENSE-2.0
  28244. *
  28245. * Unless required by applicable law or agreed to in writing,
  28246. * software distributed under the License is distributed on an
  28247. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28248. * KIND, either express or implied. See the License for the
  28249. * specific language governing permissions and limitations
  28250. * under the License.
  28251. */
  28252. /**
  28253. * Interval scale
  28254. * @module echarts/scale/Interval
  28255. */
  28256. var roundNumber = round$2;
  28257. /**
  28258. * @alias module:echarts/coord/scale/Interval
  28259. * @constructor
  28260. */
  28261. var IntervalScale = Scale.extend({
  28262. type: 'interval',
  28263. _interval: 0,
  28264. _intervalPrecision: 2,
  28265. setExtent: function (start, end) {
  28266. var thisExtent = this._extent;
  28267. //start,end may be a Number like '25',so...
  28268. if (!isNaN(start)) {
  28269. thisExtent[0] = parseFloat(start);
  28270. }
  28271. if (!isNaN(end)) {
  28272. thisExtent[1] = parseFloat(end);
  28273. }
  28274. },
  28275. unionExtent: function (other) {
  28276. var extent = this._extent;
  28277. other[0] < extent[0] && (extent[0] = other[0]);
  28278. other[1] > extent[1] && (extent[1] = other[1]);
  28279. // unionExtent may called by it's sub classes
  28280. IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
  28281. },
  28282. /**
  28283. * Get interval
  28284. */
  28285. getInterval: function () {
  28286. return this._interval;
  28287. },
  28288. /**
  28289. * Set interval
  28290. */
  28291. setInterval: function (interval) {
  28292. this._interval = interval;
  28293. // Dropped auto calculated niceExtent and use user setted extent
  28294. // We assume user wan't to set both interval, min, max to get a better result
  28295. this._niceExtent = this._extent.slice();
  28296. this._intervalPrecision = getIntervalPrecision(interval);
  28297. },
  28298. /**
  28299. * @return {Array.<number>}
  28300. */
  28301. getTicks: function () {
  28302. return intervalScaleGetTicks(
  28303. this._interval, this._extent, this._niceExtent, this._intervalPrecision
  28304. );
  28305. },
  28306. /**
  28307. * @param {number} data
  28308. * @param {Object} [opt]
  28309. * @param {number|string} [opt.precision] If 'auto', use nice presision.
  28310. * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
  28311. * @return {string}
  28312. */
  28313. getLabel: function (data, opt) {
  28314. if (data == null) {
  28315. return '';
  28316. }
  28317. var precision = opt && opt.precision;
  28318. if (precision == null) {
  28319. precision = getPrecisionSafe(data) || 0;
  28320. }
  28321. else if (precision === 'auto') {
  28322. // Should be more precise then tick.
  28323. precision = this._intervalPrecision;
  28324. }
  28325. // (1) If `precision` is set, 12.005 should be display as '12.00500'.
  28326. // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
  28327. data = roundNumber(data, precision, true);
  28328. return addCommas(data);
  28329. },
  28330. /**
  28331. * Update interval and extent of intervals for nice ticks
  28332. *
  28333. * @param {number} [splitNumber = 5] Desired number of ticks
  28334. * @param {number} [minInterval]
  28335. * @param {number} [maxInterval]
  28336. */
  28337. niceTicks: function (splitNumber, minInterval, maxInterval) {
  28338. splitNumber = splitNumber || 5;
  28339. var extent = this._extent;
  28340. var span = extent[1] - extent[0];
  28341. if (!isFinite(span)) {
  28342. return;
  28343. }
  28344. // User may set axis min 0 and data are all negative
  28345. // FIXME If it needs to reverse ?
  28346. if (span < 0) {
  28347. span = -span;
  28348. extent.reverse();
  28349. }
  28350. var result = intervalScaleNiceTicks(
  28351. extent, splitNumber, minInterval, maxInterval
  28352. );
  28353. this._intervalPrecision = result.intervalPrecision;
  28354. this._interval = result.interval;
  28355. this._niceExtent = result.niceTickExtent;
  28356. },
  28357. /**
  28358. * Nice extent.
  28359. * @param {Object} opt
  28360. * @param {number} [opt.splitNumber = 5] Given approx tick number
  28361. * @param {boolean} [opt.fixMin=false]
  28362. * @param {boolean} [opt.fixMax=false]
  28363. * @param {boolean} [opt.minInterval]
  28364. * @param {boolean} [opt.maxInterval]
  28365. */
  28366. niceExtent: function (opt) {
  28367. var extent = this._extent;
  28368. // If extent start and end are same, expand them
  28369. if (extent[0] === extent[1]) {
  28370. if (extent[0] !== 0) {
  28371. // Expand extent
  28372. var expandSize = extent[0];
  28373. // In the fowllowing case
  28374. // Axis has been fixed max 100
  28375. // Plus data are all 100 and axis extent are [100, 100].
  28376. // Extend to the both side will cause expanded max is larger than fixed max.
  28377. // So only expand to the smaller side.
  28378. if (!opt.fixMax) {
  28379. extent[1] += expandSize / 2;
  28380. extent[0] -= expandSize / 2;
  28381. }
  28382. else {
  28383. extent[0] -= expandSize / 2;
  28384. }
  28385. }
  28386. else {
  28387. extent[1] = 1;
  28388. }
  28389. }
  28390. var span = extent[1] - extent[0];
  28391. // If there are no data and extent are [Infinity, -Infinity]
  28392. if (!isFinite(span)) {
  28393. extent[0] = 0;
  28394. extent[1] = 1;
  28395. }
  28396. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  28397. // var extent = this._extent;
  28398. var interval = this._interval;
  28399. if (!opt.fixMin) {
  28400. extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
  28401. }
  28402. if (!opt.fixMax) {
  28403. extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
  28404. }
  28405. }
  28406. });
  28407. /**
  28408. * @return {module:echarts/scale/Time}
  28409. */
  28410. IntervalScale.create = function () {
  28411. return new IntervalScale();
  28412. };
  28413. /*
  28414. * Licensed to the Apache Software Foundation (ASF) under one
  28415. * or more contributor license agreements. See the NOTICE file
  28416. * distributed with this work for additional information
  28417. * regarding copyright ownership. The ASF licenses this file
  28418. * to you under the Apache License, Version 2.0 (the
  28419. * "License"); you may not use this file except in compliance
  28420. * with the License. You may obtain a copy of the License at
  28421. *
  28422. * http://www.apache.org/licenses/LICENSE-2.0
  28423. *
  28424. * Unless required by applicable law or agreed to in writing,
  28425. * software distributed under the License is distributed on an
  28426. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28427. * KIND, either express or implied. See the License for the
  28428. * specific language governing permissions and limitations
  28429. * under the License.
  28430. */
  28431. /* global Float32Array */
  28432. var STACK_PREFIX = '__ec_stack_';
  28433. var LARGE_BAR_MIN_WIDTH = 0.5;
  28434. var LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array;
  28435. function getSeriesStackId(seriesModel) {
  28436. return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;
  28437. }
  28438. function getAxisKey(axis) {
  28439. return axis.dim + axis.index;
  28440. }
  28441. /**
  28442. * @param {Object} opt
  28443. * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently.
  28444. * @param {number} opt.count Positive interger.
  28445. * @param {number} [opt.barWidth]
  28446. * @param {number} [opt.barMaxWidth]
  28447. * @param {number} [opt.barGap]
  28448. * @param {number} [opt.barCategoryGap]
  28449. * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.
  28450. */
  28451. function prepareLayoutBarSeries(seriesType, ecModel) {
  28452. var seriesModels = [];
  28453. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  28454. // Check series coordinate, do layout for cartesian2d only
  28455. if (isOnCartesian(seriesModel) && !isInLargeMode(seriesModel)) {
  28456. seriesModels.push(seriesModel);
  28457. }
  28458. });
  28459. return seriesModels;
  28460. }
  28461. function makeColumnLayout(barSeries) {
  28462. var seriesInfoList = [];
  28463. each$1(barSeries, function (seriesModel) {
  28464. var data = seriesModel.getData();
  28465. var cartesian = seriesModel.coordinateSystem;
  28466. var baseAxis = cartesian.getBaseAxis();
  28467. var axisExtent = baseAxis.getExtent();
  28468. var bandWidth = baseAxis.type === 'category'
  28469. ? baseAxis.getBandWidth()
  28470. : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
  28471. var barWidth = parsePercent$1(
  28472. seriesModel.get('barWidth'), bandWidth
  28473. );
  28474. var barMaxWidth = parsePercent$1(
  28475. seriesModel.get('barMaxWidth'), bandWidth
  28476. );
  28477. var barGap = seriesModel.get('barGap');
  28478. var barCategoryGap = seriesModel.get('barCategoryGap');
  28479. seriesInfoList.push({
  28480. bandWidth: bandWidth,
  28481. barWidth: barWidth,
  28482. barMaxWidth: barMaxWidth,
  28483. barGap: barGap,
  28484. barCategoryGap: barCategoryGap,
  28485. axisKey: getAxisKey(baseAxis),
  28486. stackId: getSeriesStackId(seriesModel)
  28487. });
  28488. });
  28489. return doCalBarWidthAndOffset(seriesInfoList);
  28490. }
  28491. function doCalBarWidthAndOffset(seriesInfoList) {
  28492. // Columns info on each category axis. Key is cartesian name
  28493. var columnsMap = {};
  28494. each$1(seriesInfoList, function (seriesInfo, idx) {
  28495. var axisKey = seriesInfo.axisKey;
  28496. var bandWidth = seriesInfo.bandWidth;
  28497. var columnsOnAxis = columnsMap[axisKey] || {
  28498. bandWidth: bandWidth,
  28499. remainedWidth: bandWidth,
  28500. autoWidthCount: 0,
  28501. categoryGap: '20%',
  28502. gap: '30%',
  28503. stacks: {}
  28504. };
  28505. var stacks = columnsOnAxis.stacks;
  28506. columnsMap[axisKey] = columnsOnAxis;
  28507. var stackId = seriesInfo.stackId;
  28508. if (!stacks[stackId]) {
  28509. columnsOnAxis.autoWidthCount++;
  28510. }
  28511. stacks[stackId] = stacks[stackId] || {
  28512. width: 0,
  28513. maxWidth: 0
  28514. };
  28515. // Caution: In a single coordinate system, these barGrid attributes
  28516. // will be shared by series. Consider that they have default values,
  28517. // only the attributes set on the last series will work.
  28518. // Do not change this fact unless there will be a break change.
  28519. // TODO
  28520. var barWidth = seriesInfo.barWidth;
  28521. if (barWidth && !stacks[stackId].width) {
  28522. // See #6312, do not restrict width.
  28523. stacks[stackId].width = barWidth;
  28524. barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
  28525. columnsOnAxis.remainedWidth -= barWidth;
  28526. }
  28527. var barMaxWidth = seriesInfo.barMaxWidth;
  28528. barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
  28529. var barGap = seriesInfo.barGap;
  28530. (barGap != null) && (columnsOnAxis.gap = barGap);
  28531. var barCategoryGap = seriesInfo.barCategoryGap;
  28532. (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
  28533. });
  28534. var result = {};
  28535. each$1(columnsMap, function (columnsOnAxis, coordSysName) {
  28536. result[coordSysName] = {};
  28537. var stacks = columnsOnAxis.stacks;
  28538. var bandWidth = columnsOnAxis.bandWidth;
  28539. var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);
  28540. var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);
  28541. var remainedWidth = columnsOnAxis.remainedWidth;
  28542. var autoWidthCount = columnsOnAxis.autoWidthCount;
  28543. var autoWidth = (remainedWidth - categoryGap)
  28544. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  28545. autoWidth = Math.max(autoWidth, 0);
  28546. // Find if any auto calculated bar exceeded maxBarWidth
  28547. each$1(stacks, function (column, stack) {
  28548. var maxWidth = column.maxWidth;
  28549. if (maxWidth && maxWidth < autoWidth) {
  28550. maxWidth = Math.min(maxWidth, remainedWidth);
  28551. if (column.width) {
  28552. maxWidth = Math.min(maxWidth, column.width);
  28553. }
  28554. remainedWidth -= maxWidth;
  28555. column.width = maxWidth;
  28556. autoWidthCount--;
  28557. }
  28558. });
  28559. // Recalculate width again
  28560. autoWidth = (remainedWidth - categoryGap)
  28561. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  28562. autoWidth = Math.max(autoWidth, 0);
  28563. var widthSum = 0;
  28564. var lastColumn;
  28565. each$1(stacks, function (column, idx) {
  28566. if (!column.width) {
  28567. column.width = autoWidth;
  28568. }
  28569. lastColumn = column;
  28570. widthSum += column.width * (1 + barGapPercent);
  28571. });
  28572. if (lastColumn) {
  28573. widthSum -= lastColumn.width * barGapPercent;
  28574. }
  28575. var offset = -widthSum / 2;
  28576. each$1(stacks, function (column, stackId) {
  28577. result[coordSysName][stackId] = result[coordSysName][stackId] || {
  28578. offset: offset,
  28579. width: column.width
  28580. };
  28581. offset += column.width * (1 + barGapPercent);
  28582. });
  28583. });
  28584. return result;
  28585. }
  28586. /**
  28587. * @param {Object} barWidthAndOffset The result of makeColumnLayout
  28588. * @param {module:echarts/coord/Axis} axis
  28589. * @param {module:echarts/model/Series} [seriesModel] If not provided, return all.
  28590. * @return {Object} {stackId: {offset, width}} or {offset, width} if seriesModel provided.
  28591. */
  28592. function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {
  28593. if (barWidthAndOffset && axis) {
  28594. var result = barWidthAndOffset[getAxisKey(axis)];
  28595. if (result != null && seriesModel != null) {
  28596. result = result[getSeriesStackId(seriesModel)];
  28597. }
  28598. return result;
  28599. }
  28600. }
  28601. /**
  28602. * @param {string} seriesType
  28603. * @param {module:echarts/model/Global} ecModel
  28604. */
  28605. function layout(seriesType, ecModel) {
  28606. var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);
  28607. var barWidthAndOffset = makeColumnLayout(seriesModels);
  28608. var lastStackCoords = {};
  28609. each$1(seriesModels, function (seriesModel) {
  28610. var data = seriesModel.getData();
  28611. var cartesian = seriesModel.coordinateSystem;
  28612. var baseAxis = cartesian.getBaseAxis();
  28613. var stackId = getSeriesStackId(seriesModel);
  28614. var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];
  28615. var columnOffset = columnLayoutInfo.offset;
  28616. var columnWidth = columnLayoutInfo.width;
  28617. var valueAxis = cartesian.getOtherAxis(baseAxis);
  28618. var barMinHeight = seriesModel.get('barMinHeight') || 0;
  28619. lastStackCoords[stackId] = lastStackCoords[stackId] || [];
  28620. data.setLayout({
  28621. offset: columnOffset,
  28622. size: columnWidth
  28623. });
  28624. var valueDim = data.mapDimension(valueAxis.dim);
  28625. var baseDim = data.mapDimension(baseAxis.dim);
  28626. var stacked = isDimensionStacked(data, valueDim /*, baseDim*/);
  28627. var isValueAxisH = valueAxis.isHorizontal();
  28628. var valueAxisStart = getValueAxisStart(baseAxis, valueAxis, stacked);
  28629. for (var idx = 0, len = data.count(); idx < len; idx++) {
  28630. var value = data.get(valueDim, idx);
  28631. var baseValue = data.get(baseDim, idx);
  28632. if (isNaN(value)) {
  28633. continue;
  28634. }
  28635. var sign = value >= 0 ? 'p' : 'n';
  28636. var baseCoord = valueAxisStart;
  28637. // Because of the barMinHeight, we can not use the value in
  28638. // stackResultDimension directly.
  28639. if (stacked) {
  28640. // Only ordinal axis can be stacked.
  28641. if (!lastStackCoords[stackId][baseValue]) {
  28642. lastStackCoords[stackId][baseValue] = {
  28643. p: valueAxisStart, // Positive stack
  28644. n: valueAxisStart // Negative stack
  28645. };
  28646. }
  28647. // Should also consider #4243
  28648. baseCoord = lastStackCoords[stackId][baseValue][sign];
  28649. }
  28650. var x;
  28651. var y;
  28652. var width;
  28653. var height;
  28654. if (isValueAxisH) {
  28655. var coord = cartesian.dataToPoint([value, baseValue]);
  28656. x = baseCoord;
  28657. y = coord[1] + columnOffset;
  28658. width = coord[0] - valueAxisStart;
  28659. height = columnWidth;
  28660. if (Math.abs(width) < barMinHeight) {
  28661. width = (width < 0 ? -1 : 1) * barMinHeight;
  28662. }
  28663. stacked && (lastStackCoords[stackId][baseValue][sign] += width);
  28664. }
  28665. else {
  28666. var coord = cartesian.dataToPoint([baseValue, value]);
  28667. x = coord[0] + columnOffset;
  28668. y = baseCoord;
  28669. width = columnWidth;
  28670. height = coord[1] - valueAxisStart;
  28671. if (Math.abs(height) < barMinHeight) {
  28672. // Include zero to has a positive bar
  28673. height = (height <= 0 ? -1 : 1) * barMinHeight;
  28674. }
  28675. stacked && (lastStackCoords[stackId][baseValue][sign] += height);
  28676. }
  28677. data.setItemLayout(idx, {
  28678. x: x,
  28679. y: y,
  28680. width: width,
  28681. height: height
  28682. });
  28683. }
  28684. }, this);
  28685. }
  28686. // TODO: Do not support stack in large mode yet.
  28687. var largeLayout = {
  28688. seriesType: 'bar',
  28689. plan: createRenderPlanner(),
  28690. reset: function (seriesModel) {
  28691. if (!isOnCartesian(seriesModel) || !isInLargeMode(seriesModel)) {
  28692. return;
  28693. }
  28694. var data = seriesModel.getData();
  28695. var cartesian = seriesModel.coordinateSystem;
  28696. var baseAxis = cartesian.getBaseAxis();
  28697. var valueAxis = cartesian.getOtherAxis(baseAxis);
  28698. var valueDim = data.mapDimension(valueAxis.dim);
  28699. var baseDim = data.mapDimension(baseAxis.dim);
  28700. var valueAxisHorizontal = valueAxis.isHorizontal();
  28701. var valueDimIdx = valueAxisHorizontal ? 0 : 1;
  28702. var barWidth = retrieveColumnLayout(
  28703. makeColumnLayout([seriesModel]), baseAxis, seriesModel
  28704. ).width;
  28705. if (!(barWidth > LARGE_BAR_MIN_WIDTH)) { // jshint ignore:line
  28706. barWidth = LARGE_BAR_MIN_WIDTH;
  28707. }
  28708. return {progress: progress};
  28709. function progress(params, data) {
  28710. var largePoints = new LargeArr(params.count * 2);
  28711. var dataIndex;
  28712. var coord = [];
  28713. var valuePair = [];
  28714. var offset = 0;
  28715. while ((dataIndex = params.next()) != null) {
  28716. valuePair[valueDimIdx] = data.get(valueDim, dataIndex);
  28717. valuePair[1 - valueDimIdx] = data.get(baseDim, dataIndex);
  28718. coord = cartesian.dataToPoint(valuePair, null, coord);
  28719. largePoints[offset++] = coord[0];
  28720. largePoints[offset++] = coord[1];
  28721. }
  28722. data.setLayout({
  28723. largePoints: largePoints,
  28724. barWidth: barWidth,
  28725. valueAxisStart: getValueAxisStart(baseAxis, valueAxis, false),
  28726. valueAxisHorizontal: valueAxisHorizontal
  28727. });
  28728. }
  28729. }
  28730. };
  28731. function isOnCartesian(seriesModel) {
  28732. return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';
  28733. }
  28734. function isInLargeMode(seriesModel) {
  28735. return seriesModel.pipelineContext && seriesModel.pipelineContext.large;
  28736. }
  28737. // See cases in `test/bar-start.html` and `#7412`, `#8747`.
  28738. function getValueAxisStart(baseAxis, valueAxis, stacked) {
  28739. var extent = valueAxis.getGlobalExtent();
  28740. var min;
  28741. var max;
  28742. if (extent[0] > extent[1]) {
  28743. min = extent[1];
  28744. max = extent[0];
  28745. }
  28746. else {
  28747. min = extent[0];
  28748. max = extent[1];
  28749. }
  28750. var valueStart = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0));
  28751. valueStart < min && (valueStart = min);
  28752. valueStart > max && (valueStart = max);
  28753. return valueStart;
  28754. }
  28755. /*
  28756. * Licensed to the Apache Software Foundation (ASF) under one
  28757. * or more contributor license agreements. See the NOTICE file
  28758. * distributed with this work for additional information
  28759. * regarding copyright ownership. The ASF licenses this file
  28760. * to you under the Apache License, Version 2.0 (the
  28761. * "License"); you may not use this file except in compliance
  28762. * with the License. You may obtain a copy of the License at
  28763. *
  28764. * http://www.apache.org/licenses/LICENSE-2.0
  28765. *
  28766. * Unless required by applicable law or agreed to in writing,
  28767. * software distributed under the License is distributed on an
  28768. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28769. * KIND, either express or implied. See the License for the
  28770. * specific language governing permissions and limitations
  28771. * under the License.
  28772. */
  28773. /*
  28774. * A third-party license is embeded for some of the code in this file:
  28775. * The "scaleLevels" was originally copied from "d3.js" with some
  28776. * modifications made for this project.
  28777. * (See more details in the comment on the definition of "scaleLevels" below.)
  28778. * The use of the source code of this file is also subject to the terms
  28779. * and consitions of the license of "d3.js" (BSD-3Clause, see
  28780. * </licenses/LICENSE-d3>).
  28781. */
  28782. // [About UTC and local time zone]:
  28783. // In most cases, `number.parseDate` will treat input data string as local time
  28784. // (except time zone is specified in time string). And `format.formateTime` returns
  28785. // local time by default. option.useUTC is false by default. This design have
  28786. // concidered these common case:
  28787. // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
  28788. // in local time by default.
  28789. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  28790. // as its original time, without any time difference.
  28791. var intervalScaleProto = IntervalScale.prototype;
  28792. var mathCeil = Math.ceil;
  28793. var mathFloor = Math.floor;
  28794. var ONE_SECOND = 1000;
  28795. var ONE_MINUTE = ONE_SECOND * 60;
  28796. var ONE_HOUR = ONE_MINUTE * 60;
  28797. var ONE_DAY = ONE_HOUR * 24;
  28798. // FIXME 公用?
  28799. var bisect = function (a, x, lo, hi) {
  28800. while (lo < hi) {
  28801. var mid = lo + hi >>> 1;
  28802. if (a[mid][1] < x) {
  28803. lo = mid + 1;
  28804. }
  28805. else {
  28806. hi = mid;
  28807. }
  28808. }
  28809. return lo;
  28810. };
  28811. /**
  28812. * @alias module:echarts/coord/scale/Time
  28813. * @constructor
  28814. */
  28815. var TimeScale = IntervalScale.extend({
  28816. type: 'time',
  28817. /**
  28818. * @override
  28819. */
  28820. getLabel: function (val) {
  28821. var stepLvl = this._stepLvl;
  28822. var date = new Date(val);
  28823. return formatTime(stepLvl[0], date, this.getSetting('useUTC'));
  28824. },
  28825. /**
  28826. * @override
  28827. */
  28828. niceExtent: function (opt) {
  28829. var extent = this._extent;
  28830. // If extent start and end are same, expand them
  28831. if (extent[0] === extent[1]) {
  28832. // Expand extent
  28833. extent[0] -= ONE_DAY;
  28834. extent[1] += ONE_DAY;
  28835. }
  28836. // If there are no data and extent are [Infinity, -Infinity]
  28837. if (extent[1] === -Infinity && extent[0] === Infinity) {
  28838. var d = new Date();
  28839. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  28840. extent[0] = extent[1] - ONE_DAY;
  28841. }
  28842. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  28843. // var extent = this._extent;
  28844. var interval = this._interval;
  28845. if (!opt.fixMin) {
  28846. extent[0] = round$2(mathFloor(extent[0] / interval) * interval);
  28847. }
  28848. if (!opt.fixMax) {
  28849. extent[1] = round$2(mathCeil(extent[1] / interval) * interval);
  28850. }
  28851. },
  28852. /**
  28853. * @override
  28854. */
  28855. niceTicks: function (approxTickNum, minInterval, maxInterval) {
  28856. approxTickNum = approxTickNum || 10;
  28857. var extent = this._extent;
  28858. var span = extent[1] - extent[0];
  28859. var approxInterval = span / approxTickNum;
  28860. if (minInterval != null && approxInterval < minInterval) {
  28861. approxInterval = minInterval;
  28862. }
  28863. if (maxInterval != null && approxInterval > maxInterval) {
  28864. approxInterval = maxInterval;
  28865. }
  28866. var scaleLevelsLen = scaleLevels.length;
  28867. var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
  28868. var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
  28869. var interval = level[1];
  28870. // Same with interval scale if span is much larger than 1 year
  28871. if (level[0] === 'year') {
  28872. var yearSpan = span / interval;
  28873. // From "Nice Numbers for Graph Labels" of Graphic Gems
  28874. // var niceYearSpan = numberUtil.nice(yearSpan, false);
  28875. var yearStep = nice(yearSpan / approxTickNum, true);
  28876. interval *= yearStep;
  28877. }
  28878. var timezoneOffset = this.getSetting('useUTC')
  28879. ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;
  28880. var niceExtent = [
  28881. Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
  28882. Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)
  28883. ];
  28884. fixExtent(niceExtent, extent);
  28885. this._stepLvl = level;
  28886. // Interval will be used in getTicks
  28887. this._interval = interval;
  28888. this._niceExtent = niceExtent;
  28889. },
  28890. parse: function (val) {
  28891. // val might be float.
  28892. return +parseDate(val);
  28893. }
  28894. });
  28895. each$1(['contain', 'normalize'], function (methodName) {
  28896. TimeScale.prototype[methodName] = function (val) {
  28897. return intervalScaleProto[methodName].call(this, this.parse(val));
  28898. };
  28899. });
  28900. /**
  28901. * This implementation was originally copied from "d3.js"
  28902. * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js>
  28903. * with some modifications made for this program.
  28904. * See the license statement at the head of this file.
  28905. */
  28906. var scaleLevels = [
  28907. // Format interval
  28908. ['hh:mm:ss', ONE_SECOND], // 1s
  28909. ['hh:mm:ss', ONE_SECOND * 5], // 5s
  28910. ['hh:mm:ss', ONE_SECOND * 10], // 10s
  28911. ['hh:mm:ss', ONE_SECOND * 15], // 15s
  28912. ['hh:mm:ss', ONE_SECOND * 30], // 30s
  28913. ['hh:mm\nMM-dd', ONE_MINUTE], // 1m
  28914. ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
  28915. ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
  28916. ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
  28917. ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
  28918. ['hh:mm\nMM-dd', ONE_HOUR], // 1h
  28919. ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
  28920. ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
  28921. ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
  28922. ['MM-dd\nyyyy', ONE_DAY], // 1d
  28923. ['MM-dd\nyyyy', ONE_DAY * 2], // 2d
  28924. ['MM-dd\nyyyy', ONE_DAY * 3], // 3d
  28925. ['MM-dd\nyyyy', ONE_DAY * 4], // 4d
  28926. ['MM-dd\nyyyy', ONE_DAY * 5], // 5d
  28927. ['MM-dd\nyyyy', ONE_DAY * 6], // 6d
  28928. ['week', ONE_DAY * 7], // 7d
  28929. ['MM-dd\nyyyy', ONE_DAY * 10], // 10d
  28930. ['week', ONE_DAY * 14], // 2w
  28931. ['week', ONE_DAY * 21], // 3w
  28932. ['month', ONE_DAY * 31], // 1M
  28933. ['week', ONE_DAY * 42], // 6w
  28934. ['month', ONE_DAY * 62], // 2M
  28935. ['week', ONE_DAY * 70], // 10w
  28936. ['quarter', ONE_DAY * 95], // 3M
  28937. ['month', ONE_DAY * 31 * 4], // 4M
  28938. ['month', ONE_DAY * 31 * 5], // 5M
  28939. ['half-year', ONE_DAY * 380 / 2], // 6M
  28940. ['month', ONE_DAY * 31 * 8], // 8M
  28941. ['month', ONE_DAY * 31 * 10], // 10M
  28942. ['year', ONE_DAY * 380] // 1Y
  28943. ];
  28944. /**
  28945. * @param {module:echarts/model/Model}
  28946. * @return {module:echarts/scale/Time}
  28947. */
  28948. TimeScale.create = function (model) {
  28949. return new TimeScale({useUTC: model.ecModel.get('useUTC')});
  28950. };
  28951. /*
  28952. * Licensed to the Apache Software Foundation (ASF) under one
  28953. * or more contributor license agreements. See the NOTICE file
  28954. * distributed with this work for additional information
  28955. * regarding copyright ownership. The ASF licenses this file
  28956. * to you under the Apache License, Version 2.0 (the
  28957. * "License"); you may not use this file except in compliance
  28958. * with the License. You may obtain a copy of the License at
  28959. *
  28960. * http://www.apache.org/licenses/LICENSE-2.0
  28961. *
  28962. * Unless required by applicable law or agreed to in writing,
  28963. * software distributed under the License is distributed on an
  28964. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  28965. * KIND, either express or implied. See the License for the
  28966. * specific language governing permissions and limitations
  28967. * under the License.
  28968. */
  28969. /**
  28970. * Log scale
  28971. * @module echarts/scale/Log
  28972. */
  28973. // Use some method of IntervalScale
  28974. var scaleProto$1 = Scale.prototype;
  28975. var intervalScaleProto$1 = IntervalScale.prototype;
  28976. var getPrecisionSafe$1 = getPrecisionSafe;
  28977. var roundingErrorFix = round$2;
  28978. var mathFloor$1 = Math.floor;
  28979. var mathCeil$1 = Math.ceil;
  28980. var mathPow$1 = Math.pow;
  28981. var mathLog = Math.log;
  28982. var LogScale = Scale.extend({
  28983. type: 'log',
  28984. base: 10,
  28985. $constructor: function () {
  28986. Scale.apply(this, arguments);
  28987. this._originalScale = new IntervalScale();
  28988. },
  28989. /**
  28990. * @return {Array.<number>}
  28991. */
  28992. getTicks: function () {
  28993. var originalScale = this._originalScale;
  28994. var extent = this._extent;
  28995. var originalExtent = originalScale.getExtent();
  28996. return map(intervalScaleProto$1.getTicks.call(this), function (val) {
  28997. var powVal = round$2(mathPow$1(this.base, val));
  28998. // Fix #4158
  28999. powVal = (val === extent[0] && originalScale.__fixMin)
  29000. ? fixRoundingError(powVal, originalExtent[0])
  29001. : powVal;
  29002. powVal = (val === extent[1] && originalScale.__fixMax)
  29003. ? fixRoundingError(powVal, originalExtent[1])
  29004. : powVal;
  29005. return powVal;
  29006. }, this);
  29007. },
  29008. /**
  29009. * @param {number} val
  29010. * @return {string}
  29011. */
  29012. getLabel: intervalScaleProto$1.getLabel,
  29013. /**
  29014. * @param {number} val
  29015. * @return {number}
  29016. */
  29017. scale: function (val) {
  29018. val = scaleProto$1.scale.call(this, val);
  29019. return mathPow$1(this.base, val);
  29020. },
  29021. /**
  29022. * @param {number} start
  29023. * @param {number} end
  29024. */
  29025. setExtent: function (start, end) {
  29026. var base = this.base;
  29027. start = mathLog(start) / mathLog(base);
  29028. end = mathLog(end) / mathLog(base);
  29029. intervalScaleProto$1.setExtent.call(this, start, end);
  29030. },
  29031. /**
  29032. * @return {number} end
  29033. */
  29034. getExtent: function () {
  29035. var base = this.base;
  29036. var extent = scaleProto$1.getExtent.call(this);
  29037. extent[0] = mathPow$1(base, extent[0]);
  29038. extent[1] = mathPow$1(base, extent[1]);
  29039. // Fix #4158
  29040. var originalScale = this._originalScale;
  29041. var originalExtent = originalScale.getExtent();
  29042. originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
  29043. originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
  29044. return extent;
  29045. },
  29046. /**
  29047. * @param {Array.<number>} extent
  29048. */
  29049. unionExtent: function (extent) {
  29050. this._originalScale.unionExtent(extent);
  29051. var base = this.base;
  29052. extent[0] = mathLog(extent[0]) / mathLog(base);
  29053. extent[1] = mathLog(extent[1]) / mathLog(base);
  29054. scaleProto$1.unionExtent.call(this, extent);
  29055. },
  29056. /**
  29057. * @override
  29058. */
  29059. unionExtentFromData: function (data, dim) {
  29060. // TODO
  29061. // filter value that <= 0
  29062. this.unionExtent(data.getApproximateExtent(dim));
  29063. },
  29064. /**
  29065. * Update interval and extent of intervals for nice ticks
  29066. * @param {number} [approxTickNum = 10] Given approx tick number
  29067. */
  29068. niceTicks: function (approxTickNum) {
  29069. approxTickNum = approxTickNum || 10;
  29070. var extent = this._extent;
  29071. var span = extent[1] - extent[0];
  29072. if (span === Infinity || span <= 0) {
  29073. return;
  29074. }
  29075. var interval = quantity(span);
  29076. var err = approxTickNum / span * interval;
  29077. // Filter ticks to get closer to the desired count.
  29078. if (err <= 0.5) {
  29079. interval *= 10;
  29080. }
  29081. // Interval should be integer
  29082. while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
  29083. interval *= 10;
  29084. }
  29085. var niceExtent = [
  29086. round$2(mathCeil$1(extent[0] / interval) * interval),
  29087. round$2(mathFloor$1(extent[1] / interval) * interval)
  29088. ];
  29089. this._interval = interval;
  29090. this._niceExtent = niceExtent;
  29091. },
  29092. /**
  29093. * Nice extent.
  29094. * @override
  29095. */
  29096. niceExtent: function (opt) {
  29097. intervalScaleProto$1.niceExtent.call(this, opt);
  29098. var originalScale = this._originalScale;
  29099. originalScale.__fixMin = opt.fixMin;
  29100. originalScale.__fixMax = opt.fixMax;
  29101. }
  29102. });
  29103. each$1(['contain', 'normalize'], function (methodName) {
  29104. LogScale.prototype[methodName] = function (val) {
  29105. val = mathLog(val) / mathLog(this.base);
  29106. return scaleProto$1[methodName].call(this, val);
  29107. };
  29108. });
  29109. LogScale.create = function () {
  29110. return new LogScale();
  29111. };
  29112. function fixRoundingError(val, originalVal) {
  29113. return roundingErrorFix(val, getPrecisionSafe$1(originalVal));
  29114. }
  29115. /*
  29116. * Licensed to the Apache Software Foundation (ASF) under one
  29117. * or more contributor license agreements. See the NOTICE file
  29118. * distributed with this work for additional information
  29119. * regarding copyright ownership. The ASF licenses this file
  29120. * to you under the Apache License, Version 2.0 (the
  29121. * "License"); you may not use this file except in compliance
  29122. * with the License. You may obtain a copy of the License at
  29123. *
  29124. * http://www.apache.org/licenses/LICENSE-2.0
  29125. *
  29126. * Unless required by applicable law or agreed to in writing,
  29127. * software distributed under the License is distributed on an
  29128. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  29129. * KIND, either express or implied. See the License for the
  29130. * specific language governing permissions and limitations
  29131. * under the License.
  29132. */
  29133. /**
  29134. * Get axis scale extent before niced.
  29135. * Item of returned array can only be number (including Infinity and NaN).
  29136. */
  29137. function getScaleExtent(scale, model) {
  29138. var scaleType = scale.type;
  29139. var min = model.getMin();
  29140. var max = model.getMax();
  29141. var fixMin = min != null;
  29142. var fixMax = max != null;
  29143. var originalExtent = scale.getExtent();
  29144. var axisDataLen;
  29145. var boundaryGap;
  29146. var span;
  29147. if (scaleType === 'ordinal') {
  29148. axisDataLen = model.getCategories().length;
  29149. }
  29150. else {
  29151. boundaryGap = model.get('boundaryGap');
  29152. if (!isArray(boundaryGap)) {
  29153. boundaryGap = [boundaryGap || 0, boundaryGap || 0];
  29154. }
  29155. if (typeof boundaryGap[0] === 'boolean') {
  29156. if (__DEV__) {
  29157. console.warn('Boolean type for boundaryGap is only '
  29158. + 'allowed for ordinal axis. Please use string in '
  29159. + 'percentage instead, e.g., "20%". Currently, '
  29160. + 'boundaryGap is set to be 0.');
  29161. }
  29162. boundaryGap = [0, 0];
  29163. }
  29164. boundaryGap[0] = parsePercent$1(boundaryGap[0], 1);
  29165. boundaryGap[1] = parsePercent$1(boundaryGap[1], 1);
  29166. span = (originalExtent[1] - originalExtent[0])
  29167. || Math.abs(originalExtent[0]);
  29168. }
  29169. // Notice: When min/max is not set (that is, when there are null/undefined,
  29170. // which is the most common case), these cases should be ensured:
  29171. // (1) For 'ordinal', show all axis.data.
  29172. // (2) For others:
  29173. // + `boundaryGap` is applied (if min/max set, boundaryGap is
  29174. // disabled).
  29175. // + If `needCrossZero`, min/max should be zero, otherwise, min/max should
  29176. // be the result that originalExtent enlarged by boundaryGap.
  29177. // (3) If no data, it should be ensured that `scale.setBlank` is set.
  29178. // FIXME
  29179. // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
  29180. // (2) When `needCrossZero` and all data is positive/negative, should it be ensured
  29181. // that the results processed by boundaryGap are positive/negative?
  29182. if (min == null) {
  29183. min = scaleType === 'ordinal'
  29184. ? (axisDataLen ? 0 : NaN)
  29185. : originalExtent[0] - boundaryGap[0] * span;
  29186. }
  29187. if (max == null) {
  29188. max = scaleType === 'ordinal'
  29189. ? (axisDataLen ? axisDataLen - 1 : NaN)
  29190. : originalExtent[1] + boundaryGap[1] * span;
  29191. }
  29192. if (min === 'dataMin') {
  29193. min = originalExtent[0];
  29194. }
  29195. else if (typeof min === 'function') {
  29196. min = min({
  29197. min: originalExtent[0],
  29198. max: originalExtent[1]
  29199. });
  29200. }
  29201. if (max === 'dataMax') {
  29202. max = originalExtent[1];
  29203. }
  29204. else if (typeof max === 'function') {
  29205. max = max({
  29206. min: originalExtent[0],
  29207. max: originalExtent[1]
  29208. });
  29209. }
  29210. (min == null || !isFinite(min)) && (min = NaN);
  29211. (max == null || !isFinite(max)) && (max = NaN);
  29212. scale.setBlank(
  29213. eqNaN(min)
  29214. || eqNaN(max)
  29215. || (scaleType === 'ordinal' && !scale.getOrdinalMeta().categories.length)
  29216. );
  29217. // Evaluate if axis needs cross zero
  29218. if (model.getNeedCrossZero()) {
  29219. // Axis is over zero and min is not set
  29220. if (min > 0 && max > 0 && !fixMin) {
  29221. min = 0;
  29222. }
  29223. // Axis is under zero and max is not set
  29224. if (min < 0 && max < 0 && !fixMax) {
  29225. max = 0;
  29226. }
  29227. }
  29228. // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis
  29229. // is base axis
  29230. // FIXME
  29231. // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.
  29232. // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?
  29233. // Should not depend on series type `bar`?
  29234. // (3) Fix that might overlap when using dataZoom.
  29235. // (4) Consider other chart types using `barGrid`?
  29236. // See #6728, #4862, `test/bar-overflow-time-plot.html`
  29237. var ecModel = model.ecModel;
  29238. if (ecModel && (scaleType === 'time' /*|| scaleType === 'interval' */)) {
  29239. var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);
  29240. var isBaseAxisAndHasBarSeries;
  29241. each$1(barSeriesModels, function (seriesModel) {
  29242. isBaseAxisAndHasBarSeries |= seriesModel.getBaseAxis() === model.axis;
  29243. });
  29244. if (isBaseAxisAndHasBarSeries) {
  29245. // Calculate placement of bars on axis
  29246. var barWidthAndOffset = makeColumnLayout(barSeriesModels);
  29247. // Adjust axis min and max to account for overflow
  29248. var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);
  29249. min = adjustedScale.min;
  29250. max = adjustedScale.max;
  29251. }
  29252. }
  29253. return [min, max];
  29254. }
  29255. function adjustScaleForOverflow(min, max, model, barWidthAndOffset) {
  29256. // Get Axis Length
  29257. var axisExtent = model.axis.getExtent();
  29258. var axisLength = axisExtent[1] - axisExtent[0];
  29259. // Get bars on current base axis and calculate min and max overflow
  29260. var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);
  29261. if (barsOnCurrentAxis === undefined) {
  29262. return {min: min, max: max};
  29263. }
  29264. var minOverflow = Infinity;
  29265. each$1(barsOnCurrentAxis, function (item) {
  29266. minOverflow = Math.min(item.offset, minOverflow);
  29267. });
  29268. var maxOverflow = -Infinity;
  29269. each$1(barsOnCurrentAxis, function (item) {
  29270. maxOverflow = Math.max(item.offset + item.width, maxOverflow);
  29271. });
  29272. minOverflow = Math.abs(minOverflow);
  29273. maxOverflow = Math.abs(maxOverflow);
  29274. var totalOverFlow = minOverflow + maxOverflow;
  29275. // Calulate required buffer based on old range and overflow
  29276. var oldRange = max - min;
  29277. var oldRangePercentOfNew = (1 - (minOverflow + maxOverflow) / axisLength);
  29278. var overflowBuffer = ((oldRange / oldRangePercentOfNew) - oldRange);
  29279. max += overflowBuffer * (maxOverflow / totalOverFlow);
  29280. min -= overflowBuffer * (minOverflow / totalOverFlow);
  29281. return {min: min, max: max};
  29282. }
  29283. function niceScaleExtent(scale, model) {
  29284. var extent = getScaleExtent(scale, model);
  29285. var fixMin = model.getMin() != null;
  29286. var fixMax = model.getMax() != null;
  29287. var splitNumber = model.get('splitNumber');
  29288. if (scale.type === 'log') {
  29289. scale.base = model.get('logBase');
  29290. }
  29291. var scaleType = scale.type;
  29292. scale.setExtent(extent[0], extent[1]);
  29293. scale.niceExtent({
  29294. splitNumber: splitNumber,
  29295. fixMin: fixMin,
  29296. fixMax: fixMax,
  29297. minInterval: (scaleType === 'interval' || scaleType === 'time')
  29298. ? model.get('minInterval') : null,
  29299. maxInterval: (scaleType === 'interval' || scaleType === 'time')
  29300. ? model.get('maxInterval') : null
  29301. });
  29302. // If some one specified the min, max. And the default calculated interval
  29303. // is not good enough. He can specify the interval. It is often appeared
  29304. // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
  29305. // to be 60.
  29306. // FIXME
  29307. var interval = model.get('interval');
  29308. if (interval != null) {
  29309. scale.setInterval && scale.setInterval(interval);
  29310. }
  29311. }
  29312. /**
  29313. * @param {module:echarts/model/Model} model
  29314. * @param {string} [axisType] Default retrieve from model.type
  29315. * @return {module:echarts/scale/*}
  29316. */
  29317. function createScaleByModel(model, axisType) {
  29318. axisType = axisType || model.get('type');
  29319. if (axisType) {
  29320. switch (axisType) {
  29321. // Buildin scale
  29322. case 'category':
  29323. return new OrdinalScale(
  29324. model.getOrdinalMeta
  29325. ? model.getOrdinalMeta()
  29326. : model.getCategories(),
  29327. [Infinity, -Infinity]
  29328. );
  29329. case 'value':
  29330. return new IntervalScale();
  29331. // Extended scale, like time and log
  29332. default:
  29333. return (Scale.getClass(axisType) || IntervalScale).create(model);
  29334. }
  29335. }
  29336. }
  29337. /**
  29338. * Check if the axis corss 0
  29339. */
  29340. function ifAxisCrossZero(axis) {
  29341. var dataExtent = axis.scale.getExtent();
  29342. var min = dataExtent[0];
  29343. var max = dataExtent[1];
  29344. return !((min > 0 && max > 0) || (min < 0 && max < 0));
  29345. }
  29346. /**
  29347. * @param {module:echarts/coord/Axis} axis
  29348. * @return {Function} Label formatter function.
  29349. * param: {number} tickValue,
  29350. * param: {number} idx, the index in all ticks.
  29351. * If category axis, this param is not requied.
  29352. * return: {string} label string.
  29353. */
  29354. function makeLabelFormatter(axis) {
  29355. var labelFormatter = axis.getLabelModel().get('formatter');
  29356. var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;
  29357. if (typeof labelFormatter === 'string') {
  29358. labelFormatter = (function (tpl) {
  29359. return function (val) {
  29360. // For category axis, get raw value; for numeric axis,
  29361. // get foramtted label like '1,333,444'.
  29362. val = axis.scale.getLabel(val);
  29363. return tpl.replace('{value}', val != null ? val : '');
  29364. };
  29365. })(labelFormatter);
  29366. // Consider empty array
  29367. return labelFormatter;
  29368. }
  29369. else if (typeof labelFormatter === 'function') {
  29370. return function (tickValue, idx) {
  29371. // The original intention of `idx` is "the index of the tick in all ticks".
  29372. // But the previous implementation of category axis do not consider the
  29373. // `axisLabel.interval`, which cause that, for example, the `interval` is
  29374. // `1`, then the ticks "name5", "name7", "name9" are displayed, where the
  29375. // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep
  29376. // the definition here for back compatibility.
  29377. if (categoryTickStart != null) {
  29378. idx = tickValue - categoryTickStart;
  29379. }
  29380. return labelFormatter(getAxisRawValue(axis, tickValue), idx);
  29381. };
  29382. }
  29383. else {
  29384. return function (tick) {
  29385. return axis.scale.getLabel(tick);
  29386. };
  29387. }
  29388. }
  29389. function getAxisRawValue(axis, value) {
  29390. // In category axis with data zoom, tick is not the original
  29391. // index of axis.data. So tick should not be exposed to user
  29392. // in category axis.
  29393. return axis.type === 'category' ? axis.scale.getLabel(value) : value;
  29394. }
  29395. /**
  29396. * @param {module:echarts/coord/Axis} axis
  29397. * @return {module:zrender/core/BoundingRect} Be null/undefined if no labels.
  29398. */
  29399. function estimateLabelUnionRect(axis) {
  29400. var axisModel = axis.model;
  29401. var scale = axis.scale;
  29402. if (!axisModel.get('axisLabel.show') || scale.isBlank()) {
  29403. return;
  29404. }
  29405. var isCategory = axis.type === 'category';
  29406. var realNumberScaleTicks;
  29407. var tickCount;
  29408. var categoryScaleExtent = scale.getExtent();
  29409. // Optimize for large category data, avoid call `getTicks()`.
  29410. if (isCategory) {
  29411. tickCount = scale.count();
  29412. }
  29413. else {
  29414. realNumberScaleTicks = scale.getTicks();
  29415. tickCount = realNumberScaleTicks.length;
  29416. }
  29417. var axisLabelModel = axis.getLabelModel();
  29418. var labelFormatter = makeLabelFormatter(axis);
  29419. var rect;
  29420. var step = 1;
  29421. // Simple optimization for large amount of labels
  29422. if (tickCount > 40) {
  29423. step = Math.ceil(tickCount / 40);
  29424. }
  29425. for (var i = 0; i < tickCount; i += step) {
  29426. var tickValue = realNumberScaleTicks ? realNumberScaleTicks[i] : categoryScaleExtent[0] + i;
  29427. var label = labelFormatter(tickValue);
  29428. var unrotatedSingleRect = axisLabelModel.getTextRect(label);
  29429. var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
  29430. rect ? rect.union(singleRect) : (rect = singleRect);
  29431. }
  29432. return rect;
  29433. }
  29434. function rotateTextRect(textRect, rotate) {
  29435. var rotateRadians = rotate * Math.PI / 180;
  29436. var boundingBox = textRect.plain();
  29437. var beforeWidth = boundingBox.width;
  29438. var beforeHeight = boundingBox.height;
  29439. var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
  29440. var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
  29441. var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
  29442. return rotatedRect;
  29443. }
  29444. /**
  29445. * @param {module:echarts/src/model/Model} model axisLabelModel or axisTickModel
  29446. * @return {number|String} Can be null|'auto'|number|function
  29447. */
  29448. function getOptionCategoryInterval(model) {
  29449. var interval = model.get('interval');
  29450. return interval == null ? 'auto' : interval;
  29451. }
  29452. /**
  29453. * Set `categoryInterval` as 0 implicitly indicates that
  29454. * show all labels reguardless of overlap.
  29455. * @param {Object} axis axisModel.axis
  29456. * @return {boolean}
  29457. */
  29458. function shouldShowAllLabels(axis) {
  29459. return axis.type === 'category'
  29460. && getOptionCategoryInterval(axis.getLabelModel()) === 0;
  29461. }
  29462. /*
  29463. * Licensed to the Apache Software Foundation (ASF) under one
  29464. * or more contributor license agreements. See the NOTICE file
  29465. * distributed with this work for additional information
  29466. * regarding copyright ownership. The ASF licenses this file
  29467. * to you under the Apache License, Version 2.0 (the
  29468. * "License"); you may not use this file except in compliance
  29469. * with the License. You may obtain a copy of the License at
  29470. *
  29471. * http://www.apache.org/licenses/LICENSE-2.0
  29472. *
  29473. * Unless required by applicable law or agreed to in writing,
  29474. * software distributed under the License is distributed on an
  29475. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  29476. * KIND, either express or implied. See the License for the
  29477. * specific language governing permissions and limitations
  29478. * under the License.
  29479. */
  29480. // import * as axisHelper from './axisHelper';
  29481. var axisModelCommonMixin = {
  29482. /**
  29483. * @param {boolean} origin
  29484. * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
  29485. */
  29486. getMin: function (origin) {
  29487. var option = this.option;
  29488. var min = (!origin && option.rangeStart != null)
  29489. ? option.rangeStart : option.min;
  29490. if (this.axis
  29491. && min != null
  29492. && min !== 'dataMin'
  29493. && typeof min !== 'function'
  29494. && !eqNaN(min)
  29495. ) {
  29496. min = this.axis.scale.parse(min);
  29497. }
  29498. return min;
  29499. },
  29500. /**
  29501. * @param {boolean} origin
  29502. * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
  29503. */
  29504. getMax: function (origin) {
  29505. var option = this.option;
  29506. var max = (!origin && option.rangeEnd != null)
  29507. ? option.rangeEnd : option.max;
  29508. if (this.axis
  29509. && max != null
  29510. && max !== 'dataMax'
  29511. && typeof max !== 'function'
  29512. && !eqNaN(max)
  29513. ) {
  29514. max = this.axis.scale.parse(max);
  29515. }
  29516. return max;
  29517. },
  29518. /**
  29519. * @return {boolean}
  29520. */
  29521. getNeedCrossZero: function () {
  29522. var option = this.option;
  29523. return (option.rangeStart != null || option.rangeEnd != null)
  29524. ? false : !option.scale;
  29525. },
  29526. /**
  29527. * Should be implemented by each axis model if necessary.
  29528. * @return {module:echarts/model/Component} coordinate system model
  29529. */
  29530. getCoordSysModel: noop,
  29531. /**
  29532. * @param {number} rangeStart Can only be finite number or null/undefined or NaN.
  29533. * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
  29534. */
  29535. setRange: function (rangeStart, rangeEnd) {
  29536. this.option.rangeStart = rangeStart;
  29537. this.option.rangeEnd = rangeEnd;
  29538. },
  29539. /**
  29540. * Reset range
  29541. */
  29542. resetRange: function () {
  29543. // rangeStart and rangeEnd is readonly.
  29544. this.option.rangeStart = this.option.rangeEnd = null;
  29545. }
  29546. };
  29547. /*
  29548. * Licensed to the Apache Software Foundation (ASF) under one
  29549. * or more contributor license agreements. See the NOTICE file
  29550. * distributed with this work for additional information
  29551. * regarding copyright ownership. The ASF licenses this file
  29552. * to you under the Apache License, Version 2.0 (the
  29553. * "License"); you may not use this file except in compliance
  29554. * with the License. You may obtain a copy of the License at
  29555. *
  29556. * http://www.apache.org/licenses/LICENSE-2.0
  29557. *
  29558. * Unless required by applicable law or agreed to in writing,
  29559. * software distributed under the License is distributed on an
  29560. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  29561. * KIND, either express or implied. See the License for the
  29562. * specific language governing permissions and limitations
  29563. * under the License.
  29564. */
  29565. // Symbol factory
  29566. /**
  29567. * Triangle shape
  29568. * @inner
  29569. */
  29570. var Triangle = extendShape({
  29571. type: 'triangle',
  29572. shape: {
  29573. cx: 0,
  29574. cy: 0,
  29575. width: 0,
  29576. height: 0
  29577. },
  29578. buildPath: function (path, shape) {
  29579. var cx = shape.cx;
  29580. var cy = shape.cy;
  29581. var width = shape.width / 2;
  29582. var height = shape.height / 2;
  29583. path.moveTo(cx, cy - height);
  29584. path.lineTo(cx + width, cy + height);
  29585. path.lineTo(cx - width, cy + height);
  29586. path.closePath();
  29587. }
  29588. });
  29589. /**
  29590. * Diamond shape
  29591. * @inner
  29592. */
  29593. var Diamond = extendShape({
  29594. type: 'diamond',
  29595. shape: {
  29596. cx: 0,
  29597. cy: 0,
  29598. width: 0,
  29599. height: 0
  29600. },
  29601. buildPath: function (path, shape) {
  29602. var cx = shape.cx;
  29603. var cy = shape.cy;
  29604. var width = shape.width / 2;
  29605. var height = shape.height / 2;
  29606. path.moveTo(cx, cy - height);
  29607. path.lineTo(cx + width, cy);
  29608. path.lineTo(cx, cy + height);
  29609. path.lineTo(cx - width, cy);
  29610. path.closePath();
  29611. }
  29612. });
  29613. /**
  29614. * Pin shape
  29615. * @inner
  29616. */
  29617. var Pin = extendShape({
  29618. type: 'pin',
  29619. shape: {
  29620. // x, y on the cusp
  29621. x: 0,
  29622. y: 0,
  29623. width: 0,
  29624. height: 0
  29625. },
  29626. buildPath: function (path, shape) {
  29627. var x = shape.x;
  29628. var y = shape.y;
  29629. var w = shape.width / 5 * 3;
  29630. // Height must be larger than width
  29631. var h = Math.max(w, shape.height);
  29632. var r = w / 2;
  29633. // Dist on y with tangent point and circle center
  29634. var dy = r * r / (h - r);
  29635. var cy = y - h + r + dy;
  29636. var angle = Math.asin(dy / r);
  29637. // Dist on x with tangent point and circle center
  29638. var dx = Math.cos(angle) * r;
  29639. var tanX = Math.sin(angle);
  29640. var tanY = Math.cos(angle);
  29641. var cpLen = r * 0.6;
  29642. var cpLen2 = r * 0.7;
  29643. path.moveTo(x - dx, cy + dy);
  29644. path.arc(
  29645. x, cy, r,
  29646. Math.PI - angle,
  29647. Math.PI * 2 + angle
  29648. );
  29649. path.bezierCurveTo(
  29650. x + dx - tanX * cpLen, cy + dy + tanY * cpLen,
  29651. x, y - cpLen2,
  29652. x, y
  29653. );
  29654. path.bezierCurveTo(
  29655. x, y - cpLen2,
  29656. x - dx + tanX * cpLen, cy + dy + tanY * cpLen,
  29657. x - dx, cy + dy
  29658. );
  29659. path.closePath();
  29660. }
  29661. });
  29662. /**
  29663. * Arrow shape
  29664. * @inner
  29665. */
  29666. var Arrow = extendShape({
  29667. type: 'arrow',
  29668. shape: {
  29669. x: 0,
  29670. y: 0,
  29671. width: 0,
  29672. height: 0
  29673. },
  29674. buildPath: function (ctx, shape) {
  29675. var height = shape.height;
  29676. var width = shape.width;
  29677. var x = shape.x;
  29678. var y = shape.y;
  29679. var dx = width / 3 * 2;
  29680. ctx.moveTo(x, y);
  29681. ctx.lineTo(x + dx, y + height);
  29682. ctx.lineTo(x, y + height / 4 * 3);
  29683. ctx.lineTo(x - dx, y + height);
  29684. ctx.lineTo(x, y);
  29685. ctx.closePath();
  29686. }
  29687. });
  29688. /**
  29689. * Map of path contructors
  29690. * @type {Object.<string, module:zrender/graphic/Path>}
  29691. */
  29692. var symbolCtors = {
  29693. line: Line,
  29694. rect: Rect,
  29695. roundRect: Rect,
  29696. square: Rect,
  29697. circle: Circle,
  29698. diamond: Diamond,
  29699. pin: Pin,
  29700. arrow: Arrow,
  29701. triangle: Triangle
  29702. };
  29703. var symbolShapeMakers = {
  29704. line: function (x, y, w, h, shape) {
  29705. // FIXME
  29706. shape.x1 = x;
  29707. shape.y1 = y + h / 2;
  29708. shape.x2 = x + w;
  29709. shape.y2 = y + h / 2;
  29710. },
  29711. rect: function (x, y, w, h, shape) {
  29712. shape.x = x;
  29713. shape.y = y;
  29714. shape.width = w;
  29715. shape.height = h;
  29716. },
  29717. roundRect: function (x, y, w, h, shape) {
  29718. shape.x = x;
  29719. shape.y = y;
  29720. shape.width = w;
  29721. shape.height = h;
  29722. shape.r = Math.min(w, h) / 4;
  29723. },
  29724. square: function (x, y, w, h, shape) {
  29725. var size = Math.min(w, h);
  29726. shape.x = x;
  29727. shape.y = y;
  29728. shape.width = size;
  29729. shape.height = size;
  29730. },
  29731. circle: function (x, y, w, h, shape) {
  29732. // Put circle in the center of square
  29733. shape.cx = x + w / 2;
  29734. shape.cy = y + h / 2;
  29735. shape.r = Math.min(w, h) / 2;
  29736. },
  29737. diamond: function (x, y, w, h, shape) {
  29738. shape.cx = x + w / 2;
  29739. shape.cy = y + h / 2;
  29740. shape.width = w;
  29741. shape.height = h;
  29742. },
  29743. pin: function (x, y, w, h, shape) {
  29744. shape.x = x + w / 2;
  29745. shape.y = y + h / 2;
  29746. shape.width = w;
  29747. shape.height = h;
  29748. },
  29749. arrow: function (x, y, w, h, shape) {
  29750. shape.x = x + w / 2;
  29751. shape.y = y + h / 2;
  29752. shape.width = w;
  29753. shape.height = h;
  29754. },
  29755. triangle: function (x, y, w, h, shape) {
  29756. shape.cx = x + w / 2;
  29757. shape.cy = y + h / 2;
  29758. shape.width = w;
  29759. shape.height = h;
  29760. }
  29761. };
  29762. var symbolBuildProxies = {};
  29763. each$1(symbolCtors, function (Ctor, name) {
  29764. symbolBuildProxies[name] = new Ctor();
  29765. });
  29766. var SymbolClz = extendShape({
  29767. type: 'symbol',
  29768. shape: {
  29769. symbolType: '',
  29770. x: 0,
  29771. y: 0,
  29772. width: 0,
  29773. height: 0
  29774. },
  29775. beforeBrush: function () {
  29776. var style = this.style;
  29777. var shape = this.shape;
  29778. // FIXME
  29779. if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
  29780. style.textPosition = ['50%', '40%'];
  29781. style.textAlign = 'center';
  29782. style.textVerticalAlign = 'middle';
  29783. }
  29784. },
  29785. buildPath: function (ctx, shape, inBundle) {
  29786. var symbolType = shape.symbolType;
  29787. var proxySymbol = symbolBuildProxies[symbolType];
  29788. if (shape.symbolType !== 'none') {
  29789. if (!proxySymbol) {
  29790. // Default rect
  29791. symbolType = 'rect';
  29792. proxySymbol = symbolBuildProxies[symbolType];
  29793. }
  29794. symbolShapeMakers[symbolType](
  29795. shape.x, shape.y, shape.width, shape.height, proxySymbol.shape
  29796. );
  29797. proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
  29798. }
  29799. }
  29800. });
  29801. // Provide setColor helper method to avoid determine if set the fill or stroke outside
  29802. function symbolPathSetColor(color, innerColor) {
  29803. if (this.type !== 'image') {
  29804. var symbolStyle = this.style;
  29805. var symbolShape = this.shape;
  29806. if (symbolShape && symbolShape.symbolType === 'line') {
  29807. symbolStyle.stroke = color;
  29808. }
  29809. else if (this.__isEmptyBrush) {
  29810. symbolStyle.stroke = color;
  29811. symbolStyle.fill = innerColor || '#fff';
  29812. }
  29813. else {
  29814. // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
  29815. symbolStyle.fill && (symbolStyle.fill = color);
  29816. symbolStyle.stroke && (symbolStyle.stroke = color);
  29817. }
  29818. this.dirty(false);
  29819. }
  29820. }
  29821. /**
  29822. * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
  29823. * @param {string} symbolType
  29824. * @param {number} x
  29825. * @param {number} y
  29826. * @param {number} w
  29827. * @param {number} h
  29828. * @param {string} color
  29829. * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
  29830. * for path and image only.
  29831. */
  29832. function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
  29833. // TODO Support image object, DynamicImage.
  29834. var isEmpty = symbolType.indexOf('empty') === 0;
  29835. if (isEmpty) {
  29836. symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
  29837. }
  29838. var symbolPath;
  29839. if (symbolType.indexOf('image://') === 0) {
  29840. symbolPath = makeImage(
  29841. symbolType.slice(8),
  29842. new BoundingRect(x, y, w, h),
  29843. keepAspect ? 'center' : 'cover'
  29844. );
  29845. }
  29846. else if (symbolType.indexOf('path://') === 0) {
  29847. symbolPath = makePath(
  29848. symbolType.slice(7),
  29849. {},
  29850. new BoundingRect(x, y, w, h),
  29851. keepAspect ? 'center' : 'cover'
  29852. );
  29853. }
  29854. else {
  29855. symbolPath = new SymbolClz({
  29856. shape: {
  29857. symbolType: symbolType,
  29858. x: x,
  29859. y: y,
  29860. width: w,
  29861. height: h
  29862. }
  29863. });
  29864. }
  29865. symbolPath.__isEmptyBrush = isEmpty;
  29866. symbolPath.setColor = symbolPathSetColor;
  29867. symbolPath.setColor(color);
  29868. return symbolPath;
  29869. }
  29870. /*
  29871. * Licensed to the Apache Software Foundation (ASF) under one
  29872. * or more contributor license agreements. See the NOTICE file
  29873. * distributed with this work for additional information
  29874. * regarding copyright ownership. The ASF licenses this file
  29875. * to you under the Apache License, Version 2.0 (the
  29876. * "License"); you may not use this file except in compliance
  29877. * with the License. You may obtain a copy of the License at
  29878. *
  29879. * http://www.apache.org/licenses/LICENSE-2.0
  29880. *
  29881. * Unless required by applicable law or agreed to in writing,
  29882. * software distributed under the License is distributed on an
  29883. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  29884. * KIND, either express or implied. See the License for the
  29885. * specific language governing permissions and limitations
  29886. * under the License.
  29887. */
  29888. // import createGraphFromNodeEdge from './chart/helper/createGraphFromNodeEdge';
  29889. /**
  29890. * Create a muti dimension List structure from seriesModel.
  29891. * @param {module:echarts/model/Model} seriesModel
  29892. * @return {module:echarts/data/List} list
  29893. */
  29894. function createList(seriesModel) {
  29895. return createListFromArray(seriesModel.getSource(), seriesModel);
  29896. }
  29897. var dataStack$1 = {
  29898. isDimensionStacked: isDimensionStacked,
  29899. enableDataStack: enableDataStack,
  29900. getStackedDimension: getStackedDimension
  29901. };
  29902. /**
  29903. * Create scale
  29904. * @param {Array.<number>} dataExtent
  29905. * @param {Object|module:echarts/Model} option
  29906. */
  29907. function createScale(dataExtent, option) {
  29908. var axisModel = option;
  29909. if (!Model.isInstance(option)) {
  29910. axisModel = new Model(option);
  29911. mixin(axisModel, axisModelCommonMixin);
  29912. }
  29913. var scale = createScaleByModel(axisModel);
  29914. scale.setExtent(dataExtent[0], dataExtent[1]);
  29915. niceScaleExtent(scale, axisModel);
  29916. return scale;
  29917. }
  29918. /**
  29919. * Mixin common methods to axis model,
  29920. *
  29921. * Inlcude methods
  29922. * `getFormattedLabels() => Array.<string>`
  29923. * `getCategories() => Array.<string>`
  29924. * `getMin(origin: boolean) => number`
  29925. * `getMax(origin: boolean) => number`
  29926. * `getNeedCrossZero() => boolean`
  29927. * `setRange(start: number, end: number)`
  29928. * `resetRange()`
  29929. */
  29930. function mixinAxisModelCommonMethods(Model$$1) {
  29931. mixin(Model$$1, axisModelCommonMixin);
  29932. }
  29933. var helper = (Object.freeze || Object)({
  29934. createList: createList,
  29935. getLayoutRect: getLayoutRect,
  29936. dataStack: dataStack$1,
  29937. createScale: createScale,
  29938. mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,
  29939. completeDimensions: completeDimensions,
  29940. createDimensions: createDimensions,
  29941. createSymbol: createSymbol
  29942. });
  29943. var EPSILON$3 = 1e-8;
  29944. function isAroundEqual$1(a, b) {
  29945. return Math.abs(a - b) < EPSILON$3;
  29946. }
  29947. function contain$1(points, x, y) {
  29948. var w = 0;
  29949. var p = points[0];
  29950. if (!p) {
  29951. return false;
  29952. }
  29953. for (var i = 1; i < points.length; i++) {
  29954. var p2 = points[i];
  29955. w += windingLine(p[0], p[1], p2[0], p2[1], x, y);
  29956. p = p2;
  29957. }
  29958. // Close polygon
  29959. var p0 = points[0];
  29960. if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {
  29961. w += windingLine(p[0], p[1], p0[0], p0[1], x, y);
  29962. }
  29963. return w !== 0;
  29964. }
  29965. /*
  29966. * Licensed to the Apache Software Foundation (ASF) under one
  29967. * or more contributor license agreements. See the NOTICE file
  29968. * distributed with this work for additional information
  29969. * regarding copyright ownership. The ASF licenses this file
  29970. * to you under the Apache License, Version 2.0 (the
  29971. * "License"); you may not use this file except in compliance
  29972. * with the License. You may obtain a copy of the License at
  29973. *
  29974. * http://www.apache.org/licenses/LICENSE-2.0
  29975. *
  29976. * Unless required by applicable law or agreed to in writing,
  29977. * software distributed under the License is distributed on an
  29978. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  29979. * KIND, either express or implied. See the License for the
  29980. * specific language governing permissions and limitations
  29981. * under the License.
  29982. */
  29983. /**
  29984. * @module echarts/coord/geo/Region
  29985. */
  29986. /**
  29987. * @param {string|Region} name
  29988. * @param {Array} geometries
  29989. * @param {Array.<number>} cp
  29990. */
  29991. function Region(name, geometries, cp) {
  29992. /**
  29993. * @type {string}
  29994. * @readOnly
  29995. */
  29996. this.name = name;
  29997. /**
  29998. * @type {Array.<Array>}
  29999. * @readOnly
  30000. */
  30001. this.geometries = geometries;
  30002. if (!cp) {
  30003. var rect = this.getBoundingRect();
  30004. cp = [
  30005. rect.x + rect.width / 2,
  30006. rect.y + rect.height / 2
  30007. ];
  30008. }
  30009. else {
  30010. cp = [cp[0], cp[1]];
  30011. }
  30012. /**
  30013. * @type {Array.<number>}
  30014. */
  30015. this.center = cp;
  30016. }
  30017. Region.prototype = {
  30018. constructor: Region,
  30019. properties: null,
  30020. /**
  30021. * @return {module:zrender/core/BoundingRect}
  30022. */
  30023. getBoundingRect: function () {
  30024. var rect = this._rect;
  30025. if (rect) {
  30026. return rect;
  30027. }
  30028. var MAX_NUMBER = Number.MAX_VALUE;
  30029. var min$$1 = [MAX_NUMBER, MAX_NUMBER];
  30030. var max$$1 = [-MAX_NUMBER, -MAX_NUMBER];
  30031. var min2 = [];
  30032. var max2 = [];
  30033. var geometries = this.geometries;
  30034. for (var i = 0; i < geometries.length; i++) {
  30035. // Only support polygon
  30036. if (geometries[i].type !== 'polygon') {
  30037. continue;
  30038. }
  30039. // Doesn't consider hole
  30040. var exterior = geometries[i].exterior;
  30041. fromPoints(exterior, min2, max2);
  30042. min(min$$1, min$$1, min2);
  30043. max(max$$1, max$$1, max2);
  30044. }
  30045. // No data
  30046. if (i === 0) {
  30047. min$$1[0] = min$$1[1] = max$$1[0] = max$$1[1] = 0;
  30048. }
  30049. return (this._rect = new BoundingRect(
  30050. min$$1[0], min$$1[1], max$$1[0] - min$$1[0], max$$1[1] - min$$1[1]
  30051. ));
  30052. },
  30053. /**
  30054. * @param {<Array.<number>} coord
  30055. * @return {boolean}
  30056. */
  30057. contain: function (coord) {
  30058. var rect = this.getBoundingRect();
  30059. var geometries = this.geometries;
  30060. if (!rect.contain(coord[0], coord[1])) {
  30061. return false;
  30062. }
  30063. loopGeo: for (var i = 0, len$$1 = geometries.length; i < len$$1; i++) {
  30064. // Only support polygon.
  30065. if (geometries[i].type !== 'polygon') {
  30066. continue;
  30067. }
  30068. var exterior = geometries[i].exterior;
  30069. var interiors = geometries[i].interiors;
  30070. if (contain$1(exterior, coord[0], coord[1])) {
  30071. // Not in the region if point is in the hole.
  30072. for (var k = 0; k < (interiors ? interiors.length : 0); k++) {
  30073. if (contain$1(interiors[k])) {
  30074. continue loopGeo;
  30075. }
  30076. }
  30077. return true;
  30078. }
  30079. }
  30080. return false;
  30081. },
  30082. transformTo: function (x, y, width, height) {
  30083. var rect = this.getBoundingRect();
  30084. var aspect = rect.width / rect.height;
  30085. if (!width) {
  30086. width = aspect * height;
  30087. }
  30088. else if (!height) {
  30089. height = width / aspect;
  30090. }
  30091. var target = new BoundingRect(x, y, width, height);
  30092. var transform = rect.calculateTransform(target);
  30093. var geometries = this.geometries;
  30094. for (var i = 0; i < geometries.length; i++) {
  30095. // Only support polygon.
  30096. if (geometries[i].type !== 'polygon') {
  30097. continue;
  30098. }
  30099. var exterior = geometries[i].exterior;
  30100. var interiors = geometries[i].interiors;
  30101. for (var p = 0; p < exterior.length; p++) {
  30102. applyTransform(exterior[p], exterior[p], transform);
  30103. }
  30104. for (var h = 0; h < (interiors ? interiors.length : 0); h++) {
  30105. for (var p = 0; p < interiors[h].length; p++) {
  30106. applyTransform(interiors[h][p], interiors[h][p], transform);
  30107. }
  30108. }
  30109. }
  30110. rect = this._rect;
  30111. rect.copy(target);
  30112. // Update center
  30113. this.center = [
  30114. rect.x + rect.width / 2,
  30115. rect.y + rect.height / 2
  30116. ];
  30117. },
  30118. cloneShallow: function (name) {
  30119. name == null && (name = this.name);
  30120. var newRegion = new Region(name, this.geometries, this.center);
  30121. newRegion._rect = this._rect;
  30122. newRegion.transformTo = null; // Simply avoid to be called.
  30123. return newRegion;
  30124. }
  30125. };
  30126. /*
  30127. * Licensed to the Apache Software Foundation (ASF) under one
  30128. * or more contributor license agreements. See the NOTICE file
  30129. * distributed with this work for additional information
  30130. * regarding copyright ownership. The ASF licenses this file
  30131. * to you under the Apache License, Version 2.0 (the
  30132. * "License"); you may not use this file except in compliance
  30133. * with the License. You may obtain a copy of the License at
  30134. *
  30135. * http://www.apache.org/licenses/LICENSE-2.0
  30136. *
  30137. * Unless required by applicable law or agreed to in writing,
  30138. * software distributed under the License is distributed on an
  30139. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  30140. * KIND, either express or implied. See the License for the
  30141. * specific language governing permissions and limitations
  30142. * under the License.
  30143. */
  30144. /**
  30145. * Parse and decode geo json
  30146. * @module echarts/coord/geo/parseGeoJson
  30147. */
  30148. function decode(json) {
  30149. if (!json.UTF8Encoding) {
  30150. return json;
  30151. }
  30152. var encodeScale = json.UTF8Scale;
  30153. if (encodeScale == null) {
  30154. encodeScale = 1024;
  30155. }
  30156. var features = json.features;
  30157. for (var f = 0; f < features.length; f++) {
  30158. var feature = features[f];
  30159. var geometry = feature.geometry;
  30160. var coordinates = geometry.coordinates;
  30161. var encodeOffsets = geometry.encodeOffsets;
  30162. for (var c = 0; c < coordinates.length; c++) {
  30163. var coordinate = coordinates[c];
  30164. if (geometry.type === 'Polygon') {
  30165. coordinates[c] = decodePolygon(
  30166. coordinate,
  30167. encodeOffsets[c],
  30168. encodeScale
  30169. );
  30170. }
  30171. else if (geometry.type === 'MultiPolygon') {
  30172. for (var c2 = 0; c2 < coordinate.length; c2++) {
  30173. var polygon = coordinate[c2];
  30174. coordinate[c2] = decodePolygon(
  30175. polygon,
  30176. encodeOffsets[c][c2],
  30177. encodeScale
  30178. );
  30179. }
  30180. }
  30181. }
  30182. }
  30183. // Has been decoded
  30184. json.UTF8Encoding = false;
  30185. return json;
  30186. }
  30187. function decodePolygon(coordinate, encodeOffsets, encodeScale) {
  30188. var result = [];
  30189. var prevX = encodeOffsets[0];
  30190. var prevY = encodeOffsets[1];
  30191. for (var i = 0; i < coordinate.length; i += 2) {
  30192. var x = coordinate.charCodeAt(i) - 64;
  30193. var y = coordinate.charCodeAt(i + 1) - 64;
  30194. // ZigZag decoding
  30195. x = (x >> 1) ^ (-(x & 1));
  30196. y = (y >> 1) ^ (-(y & 1));
  30197. // Delta deocding
  30198. x += prevX;
  30199. y += prevY;
  30200. prevX = x;
  30201. prevY = y;
  30202. // Dequantize
  30203. result.push([x / encodeScale, y / encodeScale]);
  30204. }
  30205. return result;
  30206. }
  30207. /**
  30208. * @alias module:echarts/coord/geo/parseGeoJson
  30209. * @param {Object} geoJson
  30210. * @return {module:zrender/container/Group}
  30211. */
  30212. var parseGeoJSON = function (geoJson) {
  30213. decode(geoJson);
  30214. return map(filter(geoJson.features, function (featureObj) {
  30215. // Output of mapshaper may have geometry null
  30216. return featureObj.geometry
  30217. && featureObj.properties
  30218. && featureObj.geometry.coordinates.length > 0;
  30219. }), function (featureObj) {
  30220. var properties = featureObj.properties;
  30221. var geo = featureObj.geometry;
  30222. var coordinates = geo.coordinates;
  30223. var geometries = [];
  30224. if (geo.type === 'Polygon') {
  30225. geometries.push({
  30226. type: 'polygon',
  30227. // According to the GeoJSON specification.
  30228. // First must be exterior, and the rest are all interior(holes).
  30229. exterior: coordinates[0],
  30230. interiors: coordinates.slice(1)
  30231. });
  30232. }
  30233. if (geo.type === 'MultiPolygon') {
  30234. each$1(coordinates, function (item) {
  30235. if (item[0]) {
  30236. geometries.push({
  30237. type: 'polygon',
  30238. exterior: item[0],
  30239. interiors: item.slice(1)
  30240. });
  30241. }
  30242. });
  30243. }
  30244. var region = new Region(
  30245. properties.name,
  30246. geometries,
  30247. properties.cp
  30248. );
  30249. region.properties = properties;
  30250. return region;
  30251. });
  30252. };
  30253. /*
  30254. * Licensed to the Apache Software Foundation (ASF) under one
  30255. * or more contributor license agreements. See the NOTICE file
  30256. * distributed with this work for additional information
  30257. * regarding copyright ownership. The ASF licenses this file
  30258. * to you under the Apache License, Version 2.0 (the
  30259. * "License"); you may not use this file except in compliance
  30260. * with the License. You may obtain a copy of the License at
  30261. *
  30262. * http://www.apache.org/licenses/LICENSE-2.0
  30263. *
  30264. * Unless required by applicable law or agreed to in writing,
  30265. * software distributed under the License is distributed on an
  30266. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  30267. * KIND, either express or implied. See the License for the
  30268. * specific language governing permissions and limitations
  30269. * under the License.
  30270. */
  30271. var inner$6 = makeInner();
  30272. /**
  30273. * @param {module:echats/coord/Axis} axis
  30274. * @return {Object} {
  30275. * labels: [{
  30276. * formattedLabel: string,
  30277. * rawLabel: string,
  30278. * tickValue: number
  30279. * }, ...],
  30280. * labelCategoryInterval: number
  30281. * }
  30282. */
  30283. function createAxisLabels(axis) {
  30284. // Only ordinal scale support tick interval
  30285. return axis.type === 'category'
  30286. ? makeCategoryLabels(axis)
  30287. : makeRealNumberLabels(axis);
  30288. }
  30289. /**
  30290. * @param {module:echats/coord/Axis} axis
  30291. * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.
  30292. * @return {Object} {
  30293. * ticks: Array.<number>
  30294. * tickCategoryInterval: number
  30295. * }
  30296. */
  30297. function createAxisTicks(axis, tickModel) {
  30298. // Only ordinal scale support tick interval
  30299. return axis.type === 'category'
  30300. ? makeCategoryTicks(axis, tickModel)
  30301. : {ticks: axis.scale.getTicks()};
  30302. }
  30303. function makeCategoryLabels(axis) {
  30304. var labelModel = axis.getLabelModel();
  30305. var result = makeCategoryLabelsActually(axis, labelModel);
  30306. return (!labelModel.get('show') || axis.scale.isBlank())
  30307. ? {labels: [], labelCategoryInterval: result.labelCategoryInterval}
  30308. : result;
  30309. }
  30310. function makeCategoryLabelsActually(axis, labelModel) {
  30311. var labelsCache = getListCache(axis, 'labels');
  30312. var optionLabelInterval = getOptionCategoryInterval(labelModel);
  30313. var result = listCacheGet(labelsCache, optionLabelInterval);
  30314. if (result) {
  30315. return result;
  30316. }
  30317. var labels;
  30318. var numericLabelInterval;
  30319. if (isFunction$1(optionLabelInterval)) {
  30320. labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);
  30321. }
  30322. else {
  30323. numericLabelInterval = optionLabelInterval === 'auto'
  30324. ? makeAutoCategoryInterval(axis) : optionLabelInterval;
  30325. labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);
  30326. }
  30327. // Cache to avoid calling interval function repeatly.
  30328. return listCacheSet(labelsCache, optionLabelInterval, {
  30329. labels: labels, labelCategoryInterval: numericLabelInterval
  30330. });
  30331. }
  30332. function makeCategoryTicks(axis, tickModel) {
  30333. var ticksCache = getListCache(axis, 'ticks');
  30334. var optionTickInterval = getOptionCategoryInterval(tickModel);
  30335. var result = listCacheGet(ticksCache, optionTickInterval);
  30336. if (result) {
  30337. return result;
  30338. }
  30339. var ticks;
  30340. var tickCategoryInterval;
  30341. // Optimize for the case that large category data and no label displayed,
  30342. // we should not return all ticks.
  30343. if (!tickModel.get('show') || axis.scale.isBlank()) {
  30344. ticks = [];
  30345. }
  30346. if (isFunction$1(optionTickInterval)) {
  30347. ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);
  30348. }
  30349. // Always use label interval by default despite label show. Consider this
  30350. // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows
  30351. // labels. `splitLine` and `axisTick` should be consistent in this case.
  30352. else if (optionTickInterval === 'auto') {
  30353. var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());
  30354. tickCategoryInterval = labelsResult.labelCategoryInterval;
  30355. ticks = map(labelsResult.labels, function (labelItem) {
  30356. return labelItem.tickValue;
  30357. });
  30358. }
  30359. else {
  30360. tickCategoryInterval = optionTickInterval;
  30361. ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);
  30362. }
  30363. // Cache to avoid calling interval function repeatly.
  30364. return listCacheSet(ticksCache, optionTickInterval, {
  30365. ticks: ticks, tickCategoryInterval: tickCategoryInterval
  30366. });
  30367. }
  30368. function makeRealNumberLabels(axis) {
  30369. var ticks = axis.scale.getTicks();
  30370. var labelFormatter = makeLabelFormatter(axis);
  30371. return {
  30372. labels: map(ticks, function (tickValue, idx) {
  30373. return {
  30374. formattedLabel: labelFormatter(tickValue, idx),
  30375. rawLabel: axis.scale.getLabel(tickValue),
  30376. tickValue: tickValue
  30377. };
  30378. })
  30379. };
  30380. }
  30381. // Large category data calculation is performence sensitive, and ticks and label
  30382. // probably be fetched by multiple times. So we cache the result.
  30383. // axis is created each time during a ec process, so we do not need to clear cache.
  30384. function getListCache(axis, prop) {
  30385. // Because key can be funciton, and cache size always be small, we use array cache.
  30386. return inner$6(axis)[prop] || (inner$6(axis)[prop] = []);
  30387. }
  30388. function listCacheGet(cache, key) {
  30389. for (var i = 0; i < cache.length; i++) {
  30390. if (cache[i].key === key) {
  30391. return cache[i].value;
  30392. }
  30393. }
  30394. }
  30395. function listCacheSet(cache, key, value) {
  30396. cache.push({key: key, value: value});
  30397. return value;
  30398. }
  30399. function makeAutoCategoryInterval(axis) {
  30400. var result = inner$6(axis).autoInterval;
  30401. return result != null
  30402. ? result
  30403. : (inner$6(axis).autoInterval = axis.calculateCategoryInterval());
  30404. }
  30405. /**
  30406. * Calculate interval for category axis ticks and labels.
  30407. * To get precise result, at least one of `getRotate` and `isHorizontal`
  30408. * should be implemented in axis.
  30409. */
  30410. function calculateCategoryInterval(axis) {
  30411. var params = fetchAutoCategoryIntervalCalculationParams(axis);
  30412. var labelFormatter = makeLabelFormatter(axis);
  30413. var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;
  30414. var ordinalScale = axis.scale;
  30415. var ordinalExtent = ordinalScale.getExtent();
  30416. // Providing this method is for optimization:
  30417. // avoid generating a long array by `getTicks`
  30418. // in large category data case.
  30419. var tickCount = ordinalScale.count();
  30420. if (ordinalExtent[1] - ordinalExtent[0] < 1) {
  30421. return 0;
  30422. }
  30423. var step = 1;
  30424. // Simple optimization. Empirical value: tick count should less than 40.
  30425. if (tickCount > 40) {
  30426. step = Math.max(1, Math.floor(tickCount / 40));
  30427. }
  30428. var tickValue = ordinalExtent[0];
  30429. var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);
  30430. var unitW = Math.abs(unitSpan * Math.cos(rotation));
  30431. var unitH = Math.abs(unitSpan * Math.sin(rotation));
  30432. var maxW = 0;
  30433. var maxH = 0;
  30434. // Caution: Performance sensitive for large category data.
  30435. // Consider dataZoom, we should make appropriate step to avoid O(n) loop.
  30436. for (; tickValue <= ordinalExtent[1]; tickValue += step) {
  30437. var width = 0;
  30438. var height = 0;
  30439. // Not precise, do not consider align and vertical align
  30440. // and each distance from axis line yet.
  30441. var rect = getBoundingRect(
  30442. labelFormatter(tickValue), params.font, 'center', 'top'
  30443. );
  30444. // Magic number
  30445. width = rect.width * 1.3;
  30446. height = rect.height * 1.3;
  30447. // Min size, void long loop.
  30448. maxW = Math.max(maxW, width, 7);
  30449. maxH = Math.max(maxH, height, 7);
  30450. }
  30451. var dw = maxW / unitW;
  30452. var dh = maxH / unitH;
  30453. // 0/0 is NaN, 1/0 is Infinity.
  30454. isNaN(dw) && (dw = Infinity);
  30455. isNaN(dh) && (dh = Infinity);
  30456. var interval = Math.max(0, Math.floor(Math.min(dw, dh)));
  30457. var cache = inner$6(axis.model);
  30458. var lastAutoInterval = cache.lastAutoInterval;
  30459. var lastTickCount = cache.lastTickCount;
  30460. // Use cache to keep interval stable while moving zoom window,
  30461. // otherwise the calculated interval might jitter when the zoom
  30462. // window size is close to the interval-changing size.
  30463. if (lastAutoInterval != null
  30464. && lastTickCount != null
  30465. && Math.abs(lastAutoInterval - interval) <= 1
  30466. && Math.abs(lastTickCount - tickCount) <= 1
  30467. // Always choose the bigger one, otherwise the critical
  30468. // point is not the same when zooming in or zooming out.
  30469. && lastAutoInterval > interval
  30470. ) {
  30471. interval = lastAutoInterval;
  30472. }
  30473. // Only update cache if cache not used, otherwise the
  30474. // changing of interval is too insensitive.
  30475. else {
  30476. cache.lastTickCount = tickCount;
  30477. cache.lastAutoInterval = interval;
  30478. }
  30479. return interval;
  30480. }
  30481. function fetchAutoCategoryIntervalCalculationParams(axis) {
  30482. var labelModel = axis.getLabelModel();
  30483. return {
  30484. axisRotate: axis.getRotate
  30485. ? axis.getRotate()
  30486. : (axis.isHorizontal && !axis.isHorizontal())
  30487. ? 90
  30488. : 0,
  30489. labelRotate: labelModel.get('rotate') || 0,
  30490. font: labelModel.getFont()
  30491. };
  30492. }
  30493. function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {
  30494. var labelFormatter = makeLabelFormatter(axis);
  30495. var ordinalScale = axis.scale;
  30496. var ordinalExtent = ordinalScale.getExtent();
  30497. var labelModel = axis.getLabelModel();
  30498. var result = [];
  30499. // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...
  30500. var step = Math.max((categoryInterval || 0) + 1, 1);
  30501. var startTick = ordinalExtent[0];
  30502. var tickCount = ordinalScale.count();
  30503. // Calculate start tick based on zero if possible to keep label consistent
  30504. // while zooming and moving while interval > 0. Otherwise the selection
  30505. // of displayable ticks and symbols probably keep changing.
  30506. // 3 is empirical value.
  30507. if (startTick !== 0 && step > 1 && tickCount / step > 2) {
  30508. startTick = Math.round(Math.ceil(startTick / step) * step);
  30509. }
  30510. // (1) Only add min max label here but leave overlap checking
  30511. // to render stage, which also ensure the returned list
  30512. // suitable for splitLine and splitArea rendering.
  30513. // (2) Scales except category always contain min max label so
  30514. // do not need to perform this process.
  30515. var showAllLabel = shouldShowAllLabels(axis);
  30516. var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel;
  30517. var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel;
  30518. if (includeMinLabel && startTick !== ordinalExtent[0]) {
  30519. addItem(ordinalExtent[0]);
  30520. }
  30521. // Optimize: avoid generating large array by `ordinalScale.getTicks()`.
  30522. var tickValue = startTick;
  30523. for (; tickValue <= ordinalExtent[1]; tickValue += step) {
  30524. addItem(tickValue);
  30525. }
  30526. if (includeMaxLabel && tickValue !== ordinalExtent[1]) {
  30527. addItem(ordinalExtent[1]);
  30528. }
  30529. function addItem(tVal) {
  30530. result.push(onlyTick
  30531. ? tVal
  30532. : {
  30533. formattedLabel: labelFormatter(tVal),
  30534. rawLabel: ordinalScale.getLabel(tVal),
  30535. tickValue: tVal
  30536. }
  30537. );
  30538. }
  30539. return result;
  30540. }
  30541. // When interval is function, the result `false` means ignore the tick.
  30542. // It is time consuming for large category data.
  30543. function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {
  30544. var ordinalScale = axis.scale;
  30545. var labelFormatter = makeLabelFormatter(axis);
  30546. var result = [];
  30547. each$1(ordinalScale.getTicks(), function (tickValue) {
  30548. var rawLabel = ordinalScale.getLabel(tickValue);
  30549. if (categoryInterval(tickValue, rawLabel)) {
  30550. result.push(onlyTick
  30551. ? tickValue
  30552. : {
  30553. formattedLabel: labelFormatter(tickValue),
  30554. rawLabel: rawLabel,
  30555. tickValue: tickValue
  30556. }
  30557. );
  30558. }
  30559. });
  30560. return result;
  30561. }
  30562. /*
  30563. * Licensed to the Apache Software Foundation (ASF) under one
  30564. * or more contributor license agreements. See the NOTICE file
  30565. * distributed with this work for additional information
  30566. * regarding copyright ownership. The ASF licenses this file
  30567. * to you under the Apache License, Version 2.0 (the
  30568. * "License"); you may not use this file except in compliance
  30569. * with the License. You may obtain a copy of the License at
  30570. *
  30571. * http://www.apache.org/licenses/LICENSE-2.0
  30572. *
  30573. * Unless required by applicable law or agreed to in writing,
  30574. * software distributed under the License is distributed on an
  30575. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  30576. * KIND, either express or implied. See the License for the
  30577. * specific language governing permissions and limitations
  30578. * under the License.
  30579. */
  30580. var NORMALIZED_EXTENT = [0, 1];
  30581. /**
  30582. * Base class of Axis.
  30583. * @constructor
  30584. */
  30585. var Axis = function (dim, scale, extent) {
  30586. /**
  30587. * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'.
  30588. * @type {string}
  30589. */
  30590. this.dim = dim;
  30591. /**
  30592. * Axis scale
  30593. * @type {module:echarts/coord/scale/*}
  30594. */
  30595. this.scale = scale;
  30596. /**
  30597. * @type {Array.<number>}
  30598. * @private
  30599. */
  30600. this._extent = extent || [0, 0];
  30601. /**
  30602. * @type {boolean}
  30603. */
  30604. this.inverse = false;
  30605. /**
  30606. * Usually true when axis has a ordinal scale
  30607. * @type {boolean}
  30608. */
  30609. this.onBand = false;
  30610. };
  30611. Axis.prototype = {
  30612. constructor: Axis,
  30613. /**
  30614. * If axis extent contain given coord
  30615. * @param {number} coord
  30616. * @return {boolean}
  30617. */
  30618. contain: function (coord) {
  30619. var extent = this._extent;
  30620. var min = Math.min(extent[0], extent[1]);
  30621. var max = Math.max(extent[0], extent[1]);
  30622. return coord >= min && coord <= max;
  30623. },
  30624. /**
  30625. * If axis extent contain given data
  30626. * @param {number} data
  30627. * @return {boolean}
  30628. */
  30629. containData: function (data) {
  30630. return this.contain(this.dataToCoord(data));
  30631. },
  30632. /**
  30633. * Get coord extent.
  30634. * @return {Array.<number>}
  30635. */
  30636. getExtent: function () {
  30637. return this._extent.slice();
  30638. },
  30639. /**
  30640. * Get precision used for formatting
  30641. * @param {Array.<number>} [dataExtent]
  30642. * @return {number}
  30643. */
  30644. getPixelPrecision: function (dataExtent) {
  30645. return getPixelPrecision(
  30646. dataExtent || this.scale.getExtent(),
  30647. this._extent
  30648. );
  30649. },
  30650. /**
  30651. * Set coord extent
  30652. * @param {number} start
  30653. * @param {number} end
  30654. */
  30655. setExtent: function (start, end) {
  30656. var extent = this._extent;
  30657. extent[0] = start;
  30658. extent[1] = end;
  30659. },
  30660. /**
  30661. * Convert data to coord. Data is the rank if it has an ordinal scale
  30662. * @param {number} data
  30663. * @param {boolean} clamp
  30664. * @return {number}
  30665. */
  30666. dataToCoord: function (data, clamp) {
  30667. var extent = this._extent;
  30668. var scale = this.scale;
  30669. data = scale.normalize(data);
  30670. if (this.onBand && scale.type === 'ordinal') {
  30671. extent = extent.slice();
  30672. fixExtentWithBands(extent, scale.count());
  30673. }
  30674. return linearMap(data, NORMALIZED_EXTENT, extent, clamp);
  30675. },
  30676. /**
  30677. * Convert coord to data. Data is the rank if it has an ordinal scale
  30678. * @param {number} coord
  30679. * @param {boolean} clamp
  30680. * @return {number}
  30681. */
  30682. coordToData: function (coord, clamp) {
  30683. var extent = this._extent;
  30684. var scale = this.scale;
  30685. if (this.onBand && scale.type === 'ordinal') {
  30686. extent = extent.slice();
  30687. fixExtentWithBands(extent, scale.count());
  30688. }
  30689. var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);
  30690. return this.scale.scale(t);
  30691. },
  30692. /**
  30693. * Convert pixel point to data in axis
  30694. * @param {Array.<number>} point
  30695. * @param {boolean} clamp
  30696. * @return {number} data
  30697. */
  30698. pointToData: function (point, clamp) {
  30699. // Should be implemented in derived class if necessary.
  30700. },
  30701. /**
  30702. * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,
  30703. * `axis.getTicksCoords` considers `onBand`, which is used by
  30704. * `boundaryGap:true` of category axis and splitLine and splitArea.
  30705. * @param {Object} [opt]
  30706. * @param {number} [opt.tickModel=axis.model.getModel('axisTick')]
  30707. * @param {boolean} [opt.clamp] If `true`, the first and the last
  30708. * tick must be at the axis end points. Otherwise, clip ticks
  30709. * that outside the axis extent.
  30710. * @return {Array.<Object>} [{
  30711. * coord: ...,
  30712. * tickValue: ...
  30713. * }, ...]
  30714. */
  30715. getTicksCoords: function (opt) {
  30716. opt = opt || {};
  30717. var tickModel = opt.tickModel || this.getTickModel();
  30718. var result = createAxisTicks(this, tickModel);
  30719. var ticks = result.ticks;
  30720. var ticksCoords = map(ticks, function (tickValue) {
  30721. return {
  30722. coord: this.dataToCoord(tickValue),
  30723. tickValue: tickValue
  30724. };
  30725. }, this);
  30726. var alignWithLabel = tickModel.get('alignWithLabel');
  30727. fixOnBandTicksCoords(
  30728. this, ticksCoords, result.tickCategoryInterval, alignWithLabel, opt.clamp
  30729. );
  30730. return ticksCoords;
  30731. },
  30732. /**
  30733. * @return {Array.<Object>} [{
  30734. * formattedLabel: string,
  30735. * rawLabel: axis.scale.getLabel(tickValue)
  30736. * tickValue: number
  30737. * }, ...]
  30738. */
  30739. getViewLabels: function () {
  30740. return createAxisLabels(this).labels;
  30741. },
  30742. /**
  30743. * @return {module:echarts/coord/model/Model}
  30744. */
  30745. getLabelModel: function () {
  30746. return this.model.getModel('axisLabel');
  30747. },
  30748. /**
  30749. * Notice here we only get the default tick model. For splitLine
  30750. * or splitArea, we should pass the splitLineModel or splitAreaModel
  30751. * manually when calling `getTicksCoords`.
  30752. * In GL, this method may be overrided to:
  30753. * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`
  30754. * @return {module:echarts/coord/model/Model}
  30755. */
  30756. getTickModel: function () {
  30757. return this.model.getModel('axisTick');
  30758. },
  30759. /**
  30760. * Get width of band
  30761. * @return {number}
  30762. */
  30763. getBandWidth: function () {
  30764. var axisExtent = this._extent;
  30765. var dataExtent = this.scale.getExtent();
  30766. var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);
  30767. // Fix #2728, avoid NaN when only one data.
  30768. len === 0 && (len = 1);
  30769. var size = Math.abs(axisExtent[1] - axisExtent[0]);
  30770. return Math.abs(size) / len;
  30771. },
  30772. /**
  30773. * @abstract
  30774. * @return {boolean} Is horizontal
  30775. */
  30776. isHorizontal: null,
  30777. /**
  30778. * @abstract
  30779. * @return {number} Get axis rotate, by degree.
  30780. */
  30781. getRotate: null,
  30782. /**
  30783. * Only be called in category axis.
  30784. * Can be overrided, consider other axes like in 3D.
  30785. * @return {number} Auto interval for cateogry axis tick and label
  30786. */
  30787. calculateCategoryInterval: function () {
  30788. return calculateCategoryInterval(this);
  30789. }
  30790. };
  30791. function fixExtentWithBands(extent, nTick) {
  30792. var size = extent[1] - extent[0];
  30793. var len = nTick;
  30794. var margin = size / len / 2;
  30795. extent[0] += margin;
  30796. extent[1] -= margin;
  30797. }
  30798. // If axis has labels [1, 2, 3, 4]. Bands on the axis are
  30799. // |---1---|---2---|---3---|---4---|.
  30800. // So the displayed ticks and splitLine/splitArea should between
  30801. // each data item, otherwise cause misleading (e.g., split tow bars
  30802. // of a single data item when there are two bar series).
  30803. // Also consider if tickCategoryInterval > 0 and onBand, ticks and
  30804. // splitLine/spliteArea should layout appropriately corresponding
  30805. // to displayed labels. (So we should not use `getBandWidth` in this
  30806. // case).
  30807. function fixOnBandTicksCoords(axis, ticksCoords, tickCategoryInterval, alignWithLabel, clamp) {
  30808. var ticksLen = ticksCoords.length;
  30809. if (!axis.onBand || alignWithLabel || !ticksLen) {
  30810. return;
  30811. }
  30812. var axisExtent = axis.getExtent();
  30813. var last;
  30814. if (ticksLen === 1) {
  30815. ticksCoords[0].coord = axisExtent[0];
  30816. last = ticksCoords[1] = {coord: axisExtent[0]};
  30817. }
  30818. else {
  30819. var shift = (ticksCoords[1].coord - ticksCoords[0].coord);
  30820. each$1(ticksCoords, function (ticksItem) {
  30821. ticksItem.coord -= shift / 2;
  30822. var tickCategoryInterval = tickCategoryInterval || 0;
  30823. // Avoid split a single data item when odd interval.
  30824. if (tickCategoryInterval % 2 > 0) {
  30825. ticksItem.coord -= shift / ((tickCategoryInterval + 1) * 2);
  30826. }
  30827. });
  30828. last = {coord: ticksCoords[ticksLen - 1].coord + shift};
  30829. ticksCoords.push(last);
  30830. }
  30831. var inverse = axisExtent[0] > axisExtent[1];
  30832. if (littleThan(ticksCoords[0].coord, axisExtent[0])) {
  30833. clamp ? (ticksCoords[0].coord = axisExtent[0]) : ticksCoords.shift();
  30834. }
  30835. if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {
  30836. ticksCoords.unshift({coord: axisExtent[0]});
  30837. }
  30838. if (littleThan(axisExtent[1], last.coord)) {
  30839. clamp ? (last.coord = axisExtent[1]) : ticksCoords.pop();
  30840. }
  30841. if (clamp && littleThan(last.coord, axisExtent[1])) {
  30842. ticksCoords.push({coord: axisExtent[1]});
  30843. }
  30844. function littleThan(a, b) {
  30845. return inverse ? a > b : a < b;
  30846. }
  30847. }
  30848. /*
  30849. * Licensed to the Apache Software Foundation (ASF) under one
  30850. * or more contributor license agreements. See the NOTICE file
  30851. * distributed with this work for additional information
  30852. * regarding copyright ownership. The ASF licenses this file
  30853. * to you under the Apache License, Version 2.0 (the
  30854. * "License"); you may not use this file except in compliance
  30855. * with the License. You may obtain a copy of the License at
  30856. *
  30857. * http://www.apache.org/licenses/LICENSE-2.0
  30858. *
  30859. * Unless required by applicable law or agreed to in writing,
  30860. * software distributed under the License is distributed on an
  30861. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  30862. * KIND, either express or implied. See the License for the
  30863. * specific language governing permissions and limitations
  30864. * under the License.
  30865. */
  30866. /**
  30867. * Do not mount those modules on 'src/echarts' for better tree shaking.
  30868. */
  30869. var parseGeoJson = parseGeoJSON;
  30870. var ecUtil = {};
  30871. each$1(
  30872. [
  30873. 'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter',
  30874. 'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction',
  30875. 'extend', 'defaults', 'clone', 'merge'
  30876. ],
  30877. function (name) {
  30878. ecUtil[name] = zrUtil[name];
  30879. }
  30880. );
  30881. var graphic$1 = {};
  30882. each$1(
  30883. [
  30884. 'extendShape', 'extendPath', 'makePath', 'makeImage',
  30885. 'mergePath', 'resizePath', 'createIcon',
  30886. 'setHoverStyle', 'setLabelStyle', 'setTextStyle', 'setText',
  30887. 'getFont', 'updateProps', 'initProps', 'getTransform',
  30888. 'clipPointsByRect', 'clipRectByRect',
  30889. 'Group',
  30890. 'Image',
  30891. 'Text',
  30892. 'Circle',
  30893. 'Sector',
  30894. 'Ring',
  30895. 'Polygon',
  30896. 'Polyline',
  30897. 'Rect',
  30898. 'Line',
  30899. 'BezierCurve',
  30900. 'Arc',
  30901. 'IncrementalDisplayable',
  30902. 'CompoundPath',
  30903. 'LinearGradient',
  30904. 'RadialGradient',
  30905. 'BoundingRect'
  30906. ],
  30907. function (name) {
  30908. graphic$1[name] = graphic[name];
  30909. }
  30910. );
  30911. /*
  30912. * Licensed to the Apache Software Foundation (ASF) under one
  30913. * or more contributor license agreements. See the NOTICE file
  30914. * distributed with this work for additional information
  30915. * regarding copyright ownership. The ASF licenses this file
  30916. * to you under the Apache License, Version 2.0 (the
  30917. * "License"); you may not use this file except in compliance
  30918. * with the License. You may obtain a copy of the License at
  30919. *
  30920. * http://www.apache.org/licenses/LICENSE-2.0
  30921. *
  30922. * Unless required by applicable law or agreed to in writing,
  30923. * software distributed under the License is distributed on an
  30924. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  30925. * KIND, either express or implied. See the License for the
  30926. * specific language governing permissions and limitations
  30927. * under the License.
  30928. */
  30929. SeriesModel.extend({
  30930. type: 'series.line',
  30931. dependencies: ['grid', 'polar'],
  30932. getInitialData: function (option, ecModel) {
  30933. if (__DEV__) {
  30934. var coordSys = option.coordinateSystem;
  30935. if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
  30936. throw new Error('Line not support coordinateSystem besides cartesian and polar');
  30937. }
  30938. }
  30939. return createListFromArray(this.getSource(), this);
  30940. },
  30941. defaultOption: {
  30942. zlevel: 0,
  30943. z: 2,
  30944. coordinateSystem: 'cartesian2d',
  30945. legendHoverLink: true,
  30946. hoverAnimation: true,
  30947. // stack: null
  30948. // xAxisIndex: 0,
  30949. // yAxisIndex: 0,
  30950. // polarIndex: 0,
  30951. // If clip the overflow value
  30952. clipOverflow: true,
  30953. // cursor: null,
  30954. label: {
  30955. position: 'top'
  30956. },
  30957. // itemStyle: {
  30958. // },
  30959. lineStyle: {
  30960. width: 2,
  30961. type: 'solid'
  30962. },
  30963. // areaStyle: {
  30964. // origin of areaStyle. Valid values:
  30965. // `'auto'/null/undefined`: from axisLine to data
  30966. // `'start'`: from min to data
  30967. // `'end'`: from data to max
  30968. // origin: 'auto'
  30969. // },
  30970. // false, 'start', 'end', 'middle'
  30971. step: false,
  30972. // Disabled if step is true
  30973. smooth: false,
  30974. smoothMonotone: null,
  30975. symbol: 'emptyCircle',
  30976. symbolSize: 4,
  30977. symbolRotate: null,
  30978. showSymbol: true,
  30979. // `false`: follow the label interval strategy.
  30980. // `true`: show all symbols.
  30981. // `'auto'`: If possible, show all symbols, otherwise
  30982. // follow the label interval strategy.
  30983. showAllSymbol: 'auto',
  30984. // Whether to connect break point.
  30985. connectNulls: false,
  30986. // Sampling for large data. Can be: 'average', 'max', 'min', 'sum'.
  30987. sampling: 'none',
  30988. animationEasing: 'linear',
  30989. // Disable progressive
  30990. progressive: 0,
  30991. hoverLayerThreshold: Infinity
  30992. }
  30993. });
  30994. /*
  30995. * Licensed to the Apache Software Foundation (ASF) under one
  30996. * or more contributor license agreements. See the NOTICE file
  30997. * distributed with this work for additional information
  30998. * regarding copyright ownership. The ASF licenses this file
  30999. * to you under the Apache License, Version 2.0 (the
  31000. * "License"); you may not use this file except in compliance
  31001. * with the License. You may obtain a copy of the License at
  31002. *
  31003. * http://www.apache.org/licenses/LICENSE-2.0
  31004. *
  31005. * Unless required by applicable law or agreed to in writing,
  31006. * software distributed under the License is distributed on an
  31007. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31008. * KIND, either express or implied. See the License for the
  31009. * specific language governing permissions and limitations
  31010. * under the License.
  31011. */
  31012. /**
  31013. * @param {module:echarts/data/List} data
  31014. * @param {number} dataIndex
  31015. * @return {string} label string. Not null/undefined
  31016. */
  31017. function getDefaultLabel(data, dataIndex) {
  31018. var labelDims = data.mapDimension('defaultedLabel', true);
  31019. var len = labelDims.length;
  31020. // Simple optimization (in lots of cases, label dims length is 1)
  31021. if (len === 1) {
  31022. return retrieveRawValue(data, dataIndex, labelDims[0]);
  31023. }
  31024. else if (len) {
  31025. var vals = [];
  31026. for (var i = 0; i < labelDims.length; i++) {
  31027. var val = retrieveRawValue(data, dataIndex, labelDims[i]);
  31028. vals.push(val);
  31029. }
  31030. return vals.join(' ');
  31031. }
  31032. }
  31033. /*
  31034. * Licensed to the Apache Software Foundation (ASF) under one
  31035. * or more contributor license agreements. See the NOTICE file
  31036. * distributed with this work for additional information
  31037. * regarding copyright ownership. The ASF licenses this file
  31038. * to you under the Apache License, Version 2.0 (the
  31039. * "License"); you may not use this file except in compliance
  31040. * with the License. You may obtain a copy of the License at
  31041. *
  31042. * http://www.apache.org/licenses/LICENSE-2.0
  31043. *
  31044. * Unless required by applicable law or agreed to in writing,
  31045. * software distributed under the License is distributed on an
  31046. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31047. * KIND, either express or implied. See the License for the
  31048. * specific language governing permissions and limitations
  31049. * under the License.
  31050. */
  31051. /**
  31052. * @module echarts/chart/helper/Symbol
  31053. */
  31054. /**
  31055. * @constructor
  31056. * @alias {module:echarts/chart/helper/Symbol}
  31057. * @param {module:echarts/data/List} data
  31058. * @param {number} idx
  31059. * @extends {module:zrender/graphic/Group}
  31060. */
  31061. function SymbolClz$1(data, idx, seriesScope) {
  31062. Group.call(this);
  31063. this.updateData(data, idx, seriesScope);
  31064. }
  31065. var symbolProto = SymbolClz$1.prototype;
  31066. /**
  31067. * @public
  31068. * @static
  31069. * @param {module:echarts/data/List} data
  31070. * @param {number} dataIndex
  31071. * @return {Array.<number>} [width, height]
  31072. */
  31073. var getSymbolSize = SymbolClz$1.getSymbolSize = function (data, idx) {
  31074. var symbolSize = data.getItemVisual(idx, 'symbolSize');
  31075. return symbolSize instanceof Array
  31076. ? symbolSize.slice()
  31077. : [+symbolSize, +symbolSize];
  31078. };
  31079. function getScale(symbolSize) {
  31080. return [symbolSize[0] / 2, symbolSize[1] / 2];
  31081. }
  31082. function driftSymbol(dx, dy) {
  31083. this.parent.drift(dx, dy);
  31084. }
  31085. symbolProto._createSymbol = function (
  31086. symbolType,
  31087. data,
  31088. idx,
  31089. symbolSize,
  31090. keepAspect
  31091. ) {
  31092. // Remove paths created before
  31093. this.removeAll();
  31094. var color = data.getItemVisual(idx, 'color');
  31095. // var symbolPath = createSymbol(
  31096. // symbolType, -0.5, -0.5, 1, 1, color
  31097. // );
  31098. // If width/height are set too small (e.g., set to 1) on ios10
  31099. // and macOS Sierra, a circle stroke become a rect, no matter what
  31100. // the scale is set. So we set width/height as 2. See #4150.
  31101. var symbolPath = createSymbol(
  31102. symbolType, -1, -1, 2, 2, color, keepAspect
  31103. );
  31104. symbolPath.attr({
  31105. z2: 100,
  31106. culling: true,
  31107. scale: getScale(symbolSize)
  31108. });
  31109. // Rewrite drift method
  31110. symbolPath.drift = driftSymbol;
  31111. this._symbolType = symbolType;
  31112. this.add(symbolPath);
  31113. };
  31114. /**
  31115. * Stop animation
  31116. * @param {boolean} toLastFrame
  31117. */
  31118. symbolProto.stopSymbolAnimation = function (toLastFrame) {
  31119. this.childAt(0).stopAnimation(toLastFrame);
  31120. };
  31121. /**
  31122. * FIXME:
  31123. * Caution: This method breaks the encapsulation of this module,
  31124. * but it indeed brings convenience. So do not use the method
  31125. * unless you detailedly know all the implements of `Symbol`,
  31126. * especially animation.
  31127. *
  31128. * Get symbol path element.
  31129. */
  31130. symbolProto.getSymbolPath = function () {
  31131. return this.childAt(0);
  31132. };
  31133. /**
  31134. * Get scale(aka, current symbol size).
  31135. * Including the change caused by animation
  31136. */
  31137. symbolProto.getScale = function () {
  31138. return this.childAt(0).scale;
  31139. };
  31140. /**
  31141. * Highlight symbol
  31142. */
  31143. symbolProto.highlight = function () {
  31144. this.childAt(0).trigger('emphasis');
  31145. };
  31146. /**
  31147. * Downplay symbol
  31148. */
  31149. symbolProto.downplay = function () {
  31150. this.childAt(0).trigger('normal');
  31151. };
  31152. /**
  31153. * @param {number} zlevel
  31154. * @param {number} z
  31155. */
  31156. symbolProto.setZ = function (zlevel, z) {
  31157. var symbolPath = this.childAt(0);
  31158. symbolPath.zlevel = zlevel;
  31159. symbolPath.z = z;
  31160. };
  31161. symbolProto.setDraggable = function (draggable) {
  31162. var symbolPath = this.childAt(0);
  31163. symbolPath.draggable = draggable;
  31164. symbolPath.cursor = draggable ? 'move' : 'pointer';
  31165. };
  31166. /**
  31167. * Update symbol properties
  31168. * @param {module:echarts/data/List} data
  31169. * @param {number} idx
  31170. * @param {Object} [seriesScope]
  31171. * @param {Object} [seriesScope.itemStyle]
  31172. * @param {Object} [seriesScope.hoverItemStyle]
  31173. * @param {Object} [seriesScope.symbolRotate]
  31174. * @param {Object} [seriesScope.symbolOffset]
  31175. * @param {module:echarts/model/Model} [seriesScope.labelModel]
  31176. * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]
  31177. * @param {boolean} [seriesScope.hoverAnimation]
  31178. * @param {Object} [seriesScope.cursorStyle]
  31179. * @param {module:echarts/model/Model} [seriesScope.itemModel]
  31180. * @param {string} [seriesScope.symbolInnerColor]
  31181. * @param {Object} [seriesScope.fadeIn=false]
  31182. */
  31183. symbolProto.updateData = function (data, idx, seriesScope) {
  31184. this.silent = false;
  31185. var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
  31186. var seriesModel = data.hostModel;
  31187. var symbolSize = getSymbolSize(data, idx);
  31188. var isInit = symbolType !== this._symbolType;
  31189. if (isInit) {
  31190. var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');
  31191. this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);
  31192. }
  31193. else {
  31194. var symbolPath = this.childAt(0);
  31195. symbolPath.silent = false;
  31196. updateProps(symbolPath, {
  31197. scale: getScale(symbolSize)
  31198. }, seriesModel, idx);
  31199. }
  31200. this._updateCommon(data, idx, symbolSize, seriesScope);
  31201. if (isInit) {
  31202. var symbolPath = this.childAt(0);
  31203. var fadeIn = seriesScope && seriesScope.fadeIn;
  31204. var target = {scale: symbolPath.scale.slice()};
  31205. fadeIn && (target.style = {opacity: symbolPath.style.opacity});
  31206. symbolPath.scale = [0, 0];
  31207. fadeIn && (symbolPath.style.opacity = 0);
  31208. initProps(symbolPath, target, seriesModel, idx);
  31209. }
  31210. this._seriesModel = seriesModel;
  31211. };
  31212. // Update common properties
  31213. var normalStyleAccessPath = ['itemStyle'];
  31214. var emphasisStyleAccessPath = ['emphasis', 'itemStyle'];
  31215. var normalLabelAccessPath = ['label'];
  31216. var emphasisLabelAccessPath = ['emphasis', 'label'];
  31217. /**
  31218. * @param {module:echarts/data/List} data
  31219. * @param {number} idx
  31220. * @param {Array.<number>} symbolSize
  31221. * @param {Object} [seriesScope]
  31222. */
  31223. symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
  31224. var symbolPath = this.childAt(0);
  31225. var seriesModel = data.hostModel;
  31226. var color = data.getItemVisual(idx, 'color');
  31227. // Reset style
  31228. if (symbolPath.type !== 'image') {
  31229. symbolPath.useStyle({
  31230. strokeNoScale: true
  31231. });
  31232. }
  31233. var itemStyle = seriesScope && seriesScope.itemStyle;
  31234. var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
  31235. var symbolRotate = seriesScope && seriesScope.symbolRotate;
  31236. var symbolOffset = seriesScope && seriesScope.symbolOffset;
  31237. var labelModel = seriesScope && seriesScope.labelModel;
  31238. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
  31239. var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
  31240. var cursorStyle = seriesScope && seriesScope.cursorStyle;
  31241. if (!seriesScope || data.hasItemOption) {
  31242. var itemModel = (seriesScope && seriesScope.itemModel)
  31243. ? seriesScope.itemModel : data.getItemModel(idx);
  31244. // Color must be excluded.
  31245. // Because symbol provide setColor individually to set fill and stroke
  31246. itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
  31247. hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
  31248. symbolRotate = itemModel.getShallow('symbolRotate');
  31249. symbolOffset = itemModel.getShallow('symbolOffset');
  31250. labelModel = itemModel.getModel(normalLabelAccessPath);
  31251. hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
  31252. hoverAnimation = itemModel.getShallow('hoverAnimation');
  31253. cursorStyle = itemModel.getShallow('cursor');
  31254. }
  31255. else {
  31256. hoverItemStyle = extend({}, hoverItemStyle);
  31257. }
  31258. var elStyle = symbolPath.style;
  31259. symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
  31260. if (symbolOffset) {
  31261. symbolPath.attr('position', [
  31262. parsePercent$1(symbolOffset[0], symbolSize[0]),
  31263. parsePercent$1(symbolOffset[1], symbolSize[1])
  31264. ]);
  31265. }
  31266. cursorStyle && symbolPath.attr('cursor', cursorStyle);
  31267. // PENDING setColor before setStyle!!!
  31268. symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);
  31269. symbolPath.setStyle(itemStyle);
  31270. var opacity = data.getItemVisual(idx, 'opacity');
  31271. if (opacity != null) {
  31272. elStyle.opacity = opacity;
  31273. }
  31274. var liftZ = data.getItemVisual(idx, 'liftZ');
  31275. var z2Origin = symbolPath.__z2Origin;
  31276. if (liftZ != null) {
  31277. if (z2Origin == null) {
  31278. symbolPath.__z2Origin = symbolPath.z2;
  31279. symbolPath.z2 += liftZ;
  31280. }
  31281. }
  31282. else if (z2Origin != null) {
  31283. symbolPath.z2 = z2Origin;
  31284. symbolPath.__z2Origin = null;
  31285. }
  31286. var useNameLabel = seriesScope && seriesScope.useNameLabel;
  31287. setLabelStyle(
  31288. elStyle, hoverItemStyle, labelModel, hoverLabelModel,
  31289. {
  31290. labelFetcher: seriesModel,
  31291. labelDataIndex: idx,
  31292. defaultText: getLabelDefaultText,
  31293. isRectText: true,
  31294. autoColor: color
  31295. }
  31296. );
  31297. // Do not execute util needed.
  31298. function getLabelDefaultText(idx, opt) {
  31299. return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
  31300. }
  31301. symbolPath.off('mouseover')
  31302. .off('mouseout')
  31303. .off('emphasis')
  31304. .off('normal');
  31305. symbolPath.hoverStyle = hoverItemStyle;
  31306. // FIXME
  31307. // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
  31308. setHoverStyle(symbolPath);
  31309. symbolPath.__symbolOriginalScale = getScale(symbolSize);
  31310. if (hoverAnimation && seriesModel.isAnimationEnabled()) {
  31311. // Note: consider `off`, should use static function here.
  31312. symbolPath.on('mouseover', onMouseOver)
  31313. .on('mouseout', onMouseOut)
  31314. .on('emphasis', onEmphasis)
  31315. .on('normal', onNormal);
  31316. }
  31317. };
  31318. function onMouseOver() {
  31319. // see comment in `graphic.isInEmphasis`
  31320. !isInEmphasis(this) && onEmphasis.call(this);
  31321. }
  31322. function onMouseOut() {
  31323. // see comment in `graphic.isInEmphasis`
  31324. !isInEmphasis(this) && onNormal.call(this);
  31325. }
  31326. function onEmphasis() {
  31327. // Do not support this hover animation util some scenario required.
  31328. // Animation can only be supported in hover layer when using `el.incremetal`.
  31329. if (this.incremental || this.useHoverLayer) {
  31330. return;
  31331. }
  31332. var scale = this.__symbolOriginalScale;
  31333. var ratio = scale[1] / scale[0];
  31334. this.animateTo({
  31335. scale: [
  31336. Math.max(scale[0] * 1.1, scale[0] + 3),
  31337. Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
  31338. ]
  31339. }, 400, 'elasticOut');
  31340. }
  31341. function onNormal() {
  31342. if (this.incremental || this.useHoverLayer) {
  31343. return;
  31344. }
  31345. this.animateTo({
  31346. scale: this.__symbolOriginalScale
  31347. }, 400, 'elasticOut');
  31348. }
  31349. /**
  31350. * @param {Function} cb
  31351. * @param {Object} [opt]
  31352. * @param {Object} [opt.keepLabel=true]
  31353. */
  31354. symbolProto.fadeOut = function (cb, opt) {
  31355. var symbolPath = this.childAt(0);
  31356. // Avoid mistaken hover when fading out
  31357. this.silent = symbolPath.silent = true;
  31358. // Not show text when animating
  31359. !(opt && opt.keepLabel) && (symbolPath.style.text = null);
  31360. updateProps(
  31361. symbolPath,
  31362. {
  31363. style: {opacity: 0},
  31364. scale: [0, 0]
  31365. },
  31366. this._seriesModel,
  31367. this.dataIndex,
  31368. cb
  31369. );
  31370. };
  31371. inherits(SymbolClz$1, Group);
  31372. /*
  31373. * Licensed to the Apache Software Foundation (ASF) under one
  31374. * or more contributor license agreements. See the NOTICE file
  31375. * distributed with this work for additional information
  31376. * regarding copyright ownership. The ASF licenses this file
  31377. * to you under the Apache License, Version 2.0 (the
  31378. * "License"); you may not use this file except in compliance
  31379. * with the License. You may obtain a copy of the License at
  31380. *
  31381. * http://www.apache.org/licenses/LICENSE-2.0
  31382. *
  31383. * Unless required by applicable law or agreed to in writing,
  31384. * software distributed under the License is distributed on an
  31385. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31386. * KIND, either express or implied. See the License for the
  31387. * specific language governing permissions and limitations
  31388. * under the License.
  31389. */
  31390. /**
  31391. * @module echarts/chart/helper/SymbolDraw
  31392. */
  31393. /**
  31394. * @constructor
  31395. * @alias module:echarts/chart/helper/SymbolDraw
  31396. * @param {module:zrender/graphic/Group} [symbolCtor]
  31397. */
  31398. function SymbolDraw(symbolCtor) {
  31399. this.group = new Group();
  31400. this._symbolCtor = symbolCtor || SymbolClz$1;
  31401. }
  31402. var symbolDrawProto = SymbolDraw.prototype;
  31403. function symbolNeedsDraw(data, point, idx, opt) {
  31404. return point && !isNaN(point[0]) && !isNaN(point[1])
  31405. && !(opt.isIgnore && opt.isIgnore(idx))
  31406. // We do not set clipShape on group, because it will cut part of
  31407. // the symbol element shape. We use the same clip shape here as
  31408. // the line clip.
  31409. && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1]))
  31410. && data.getItemVisual(idx, 'symbol') !== 'none';
  31411. }
  31412. /**
  31413. * Update symbols draw by new data
  31414. * @param {module:echarts/data/List} data
  31415. * @param {Object} [opt] Or isIgnore
  31416. * @param {Function} [opt.isIgnore]
  31417. * @param {Object} [opt.clipShape]
  31418. */
  31419. symbolDrawProto.updateData = function (data, opt) {
  31420. opt = normalizeUpdateOpt(opt);
  31421. var group = this.group;
  31422. var seriesModel = data.hostModel;
  31423. var oldData = this._data;
  31424. var SymbolCtor = this._symbolCtor;
  31425. var seriesScope = makeSeriesScope(data);
  31426. // There is no oldLineData only when first rendering or switching from
  31427. // stream mode to normal mode, where previous elements should be removed.
  31428. if (!oldData) {
  31429. group.removeAll();
  31430. }
  31431. data.diff(oldData)
  31432. .add(function (newIdx) {
  31433. var point = data.getItemLayout(newIdx);
  31434. if (symbolNeedsDraw(data, point, newIdx, opt)) {
  31435. var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
  31436. symbolEl.attr('position', point);
  31437. data.setItemGraphicEl(newIdx, symbolEl);
  31438. group.add(symbolEl);
  31439. }
  31440. })
  31441. .update(function (newIdx, oldIdx) {
  31442. var symbolEl = oldData.getItemGraphicEl(oldIdx);
  31443. var point = data.getItemLayout(newIdx);
  31444. if (!symbolNeedsDraw(data, point, newIdx, opt)) {
  31445. group.remove(symbolEl);
  31446. return;
  31447. }
  31448. if (!symbolEl) {
  31449. symbolEl = new SymbolCtor(data, newIdx);
  31450. symbolEl.attr('position', point);
  31451. }
  31452. else {
  31453. symbolEl.updateData(data, newIdx, seriesScope);
  31454. updateProps(symbolEl, {
  31455. position: point
  31456. }, seriesModel);
  31457. }
  31458. // Add back
  31459. group.add(symbolEl);
  31460. data.setItemGraphicEl(newIdx, symbolEl);
  31461. })
  31462. .remove(function (oldIdx) {
  31463. var el = oldData.getItemGraphicEl(oldIdx);
  31464. el && el.fadeOut(function () {
  31465. group.remove(el);
  31466. });
  31467. })
  31468. .execute();
  31469. this._data = data;
  31470. };
  31471. symbolDrawProto.isPersistent = function () {
  31472. return true;
  31473. };
  31474. symbolDrawProto.updateLayout = function () {
  31475. var data = this._data;
  31476. if (data) {
  31477. // Not use animation
  31478. data.eachItemGraphicEl(function (el, idx) {
  31479. var point = data.getItemLayout(idx);
  31480. el.attr('position', point);
  31481. });
  31482. }
  31483. };
  31484. symbolDrawProto.incrementalPrepareUpdate = function (data) {
  31485. this._seriesScope = makeSeriesScope(data);
  31486. this._data = null;
  31487. this.group.removeAll();
  31488. };
  31489. /**
  31490. * Update symbols draw by new data
  31491. * @param {module:echarts/data/List} data
  31492. * @param {Object} [opt] Or isIgnore
  31493. * @param {Function} [opt.isIgnore]
  31494. * @param {Object} [opt.clipShape]
  31495. */
  31496. symbolDrawProto.incrementalUpdate = function (taskParams, data, opt) {
  31497. opt = normalizeUpdateOpt(opt);
  31498. function updateIncrementalAndHover(el) {
  31499. if (!el.isGroup) {
  31500. el.incremental = el.useHoverLayer = true;
  31501. }
  31502. }
  31503. for (var idx = taskParams.start; idx < taskParams.end; idx++) {
  31504. var point = data.getItemLayout(idx);
  31505. if (symbolNeedsDraw(data, point, idx, opt)) {
  31506. var el = new this._symbolCtor(data, idx, this._seriesScope);
  31507. el.traverse(updateIncrementalAndHover);
  31508. el.attr('position', point);
  31509. this.group.add(el);
  31510. data.setItemGraphicEl(idx, el);
  31511. }
  31512. }
  31513. };
  31514. function normalizeUpdateOpt(opt) {
  31515. if (opt != null && !isObject$1(opt)) {
  31516. opt = {isIgnore: opt};
  31517. }
  31518. return opt || {};
  31519. }
  31520. symbolDrawProto.remove = function (enableAnimation) {
  31521. var group = this.group;
  31522. var data = this._data;
  31523. // Incremental model do not have this._data.
  31524. if (data && enableAnimation) {
  31525. data.eachItemGraphicEl(function (el) {
  31526. el.fadeOut(function () {
  31527. group.remove(el);
  31528. });
  31529. });
  31530. }
  31531. else {
  31532. group.removeAll();
  31533. }
  31534. };
  31535. function makeSeriesScope(data) {
  31536. var seriesModel = data.hostModel;
  31537. return {
  31538. itemStyle: seriesModel.getModel('itemStyle').getItemStyle(['color']),
  31539. hoverItemStyle: seriesModel.getModel('emphasis.itemStyle').getItemStyle(),
  31540. symbolRotate: seriesModel.get('symbolRotate'),
  31541. symbolOffset: seriesModel.get('symbolOffset'),
  31542. hoverAnimation: seriesModel.get('hoverAnimation'),
  31543. labelModel: seriesModel.getModel('label'),
  31544. hoverLabelModel: seriesModel.getModel('emphasis.label'),
  31545. cursorStyle: seriesModel.get('cursor')
  31546. };
  31547. }
  31548. /*
  31549. * Licensed to the Apache Software Foundation (ASF) under one
  31550. * or more contributor license agreements. See the NOTICE file
  31551. * distributed with this work for additional information
  31552. * regarding copyright ownership. The ASF licenses this file
  31553. * to you under the Apache License, Version 2.0 (the
  31554. * "License"); you may not use this file except in compliance
  31555. * with the License. You may obtain a copy of the License at
  31556. *
  31557. * http://www.apache.org/licenses/LICENSE-2.0
  31558. *
  31559. * Unless required by applicable law or agreed to in writing,
  31560. * software distributed under the License is distributed on an
  31561. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31562. * KIND, either express or implied. See the License for the
  31563. * specific language governing permissions and limitations
  31564. * under the License.
  31565. */
  31566. /**
  31567. * @param {Object} coordSys
  31568. * @param {module:echarts/data/List} data
  31569. * @param {string} valueOrigin lineSeries.option.areaStyle.origin
  31570. */
  31571. function prepareDataCoordInfo(coordSys, data, valueOrigin) {
  31572. var baseAxis = coordSys.getBaseAxis();
  31573. var valueAxis = coordSys.getOtherAxis(baseAxis);
  31574. var valueStart = getValueStart(valueAxis, valueOrigin);
  31575. var baseAxisDim = baseAxis.dim;
  31576. var valueAxisDim = valueAxis.dim;
  31577. var valueDim = data.mapDimension(valueAxisDim);
  31578. var baseDim = data.mapDimension(baseAxisDim);
  31579. var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;
  31580. var dims = map(coordSys.dimensions, function (coordDim) {
  31581. return data.mapDimension(coordDim);
  31582. });
  31583. var stacked;
  31584. var stackResultDim = data.getCalculationInfo('stackResultDimension');
  31585. if (stacked |= isDimensionStacked(data, dims[0] /*, dims[1]*/)) { // jshint ignore:line
  31586. dims[0] = stackResultDim;
  31587. }
  31588. if (stacked |= isDimensionStacked(data, dims[1] /*, dims[0]*/)) { // jshint ignore:line
  31589. dims[1] = stackResultDim;
  31590. }
  31591. return {
  31592. dataDimsForPoint: dims,
  31593. valueStart: valueStart,
  31594. valueAxisDim: valueAxisDim,
  31595. baseAxisDim: baseAxisDim,
  31596. stacked: !!stacked,
  31597. valueDim: valueDim,
  31598. baseDim: baseDim,
  31599. baseDataOffset: baseDataOffset,
  31600. stackedOverDimension: data.getCalculationInfo('stackedOverDimension')
  31601. };
  31602. }
  31603. function getValueStart(valueAxis, valueOrigin) {
  31604. var valueStart = 0;
  31605. var extent = valueAxis.scale.getExtent();
  31606. if (valueOrigin === 'start') {
  31607. valueStart = extent[0];
  31608. }
  31609. else if (valueOrigin === 'end') {
  31610. valueStart = extent[1];
  31611. }
  31612. // auto
  31613. else {
  31614. // Both positive
  31615. if (extent[0] > 0) {
  31616. valueStart = extent[0];
  31617. }
  31618. // Both negative
  31619. else if (extent[1] < 0) {
  31620. valueStart = extent[1];
  31621. }
  31622. // If is one positive, and one negative, onZero shall be true
  31623. }
  31624. return valueStart;
  31625. }
  31626. function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {
  31627. var value = NaN;
  31628. if (dataCoordInfo.stacked) {
  31629. value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);
  31630. }
  31631. if (isNaN(value)) {
  31632. value = dataCoordInfo.valueStart;
  31633. }
  31634. var baseDataOffset = dataCoordInfo.baseDataOffset;
  31635. var stackedData = [];
  31636. stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);
  31637. stackedData[1 - baseDataOffset] = value;
  31638. return coordSys.dataToPoint(stackedData);
  31639. }
  31640. /*
  31641. * Licensed to the Apache Software Foundation (ASF) under one
  31642. * or more contributor license agreements. See the NOTICE file
  31643. * distributed with this work for additional information
  31644. * regarding copyright ownership. The ASF licenses this file
  31645. * to you under the Apache License, Version 2.0 (the
  31646. * "License"); you may not use this file except in compliance
  31647. * with the License. You may obtain a copy of the License at
  31648. *
  31649. * http://www.apache.org/licenses/LICENSE-2.0
  31650. *
  31651. * Unless required by applicable law or agreed to in writing,
  31652. * software distributed under the License is distributed on an
  31653. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31654. * KIND, either express or implied. See the License for the
  31655. * specific language governing permissions and limitations
  31656. * under the License.
  31657. */
  31658. // var arrayDiff = require('zrender/src/core/arrayDiff');
  31659. // 'zrender/src/core/arrayDiff' has been used before, but it did
  31660. // not do well in performance when roam with fixed dataZoom window.
  31661. // function convertToIntId(newIdList, oldIdList) {
  31662. // // Generate int id instead of string id.
  31663. // // Compare string maybe slow in score function of arrDiff
  31664. // // Assume id in idList are all unique
  31665. // var idIndicesMap = {};
  31666. // var idx = 0;
  31667. // for (var i = 0; i < newIdList.length; i++) {
  31668. // idIndicesMap[newIdList[i]] = idx;
  31669. // newIdList[i] = idx++;
  31670. // }
  31671. // for (var i = 0; i < oldIdList.length; i++) {
  31672. // var oldId = oldIdList[i];
  31673. // // Same with newIdList
  31674. // if (idIndicesMap[oldId]) {
  31675. // oldIdList[i] = idIndicesMap[oldId];
  31676. // }
  31677. // else {
  31678. // oldIdList[i] = idx++;
  31679. // }
  31680. // }
  31681. // }
  31682. function diffData(oldData, newData) {
  31683. var diffResult = [];
  31684. newData.diff(oldData)
  31685. .add(function (idx) {
  31686. diffResult.push({cmd: '+', idx: idx});
  31687. })
  31688. .update(function (newIdx, oldIdx) {
  31689. diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});
  31690. })
  31691. .remove(function (idx) {
  31692. diffResult.push({cmd: '-', idx: idx});
  31693. })
  31694. .execute();
  31695. return diffResult;
  31696. }
  31697. var lineAnimationDiff = function (
  31698. oldData, newData,
  31699. oldStackedOnPoints, newStackedOnPoints,
  31700. oldCoordSys, newCoordSys,
  31701. oldValueOrigin, newValueOrigin
  31702. ) {
  31703. var diff = diffData(oldData, newData);
  31704. // var newIdList = newData.mapArray(newData.getId);
  31705. // var oldIdList = oldData.mapArray(oldData.getId);
  31706. // convertToIntId(newIdList, oldIdList);
  31707. // // FIXME One data ?
  31708. // diff = arrayDiff(oldIdList, newIdList);
  31709. var currPoints = [];
  31710. var nextPoints = [];
  31711. // Points for stacking base line
  31712. var currStackedPoints = [];
  31713. var nextStackedPoints = [];
  31714. var status = [];
  31715. var sortedIndices = [];
  31716. var rawIndices = [];
  31717. var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin);
  31718. var oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);
  31719. for (var i = 0; i < diff.length; i++) {
  31720. var diffItem = diff[i];
  31721. var pointAdded = true;
  31722. // FIXME, animation is not so perfect when dataZoom window moves fast
  31723. // Which is in case remvoing or add more than one data in the tail or head
  31724. switch (diffItem.cmd) {
  31725. case '=':
  31726. var currentPt = oldData.getItemLayout(diffItem.idx);
  31727. var nextPt = newData.getItemLayout(diffItem.idx1);
  31728. // If previous data is NaN, use next point directly
  31729. if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
  31730. currentPt = nextPt.slice();
  31731. }
  31732. currPoints.push(currentPt);
  31733. nextPoints.push(nextPt);
  31734. currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
  31735. nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
  31736. rawIndices.push(newData.getRawIndex(diffItem.idx1));
  31737. break;
  31738. case '+':
  31739. var idx = diffItem.idx;
  31740. currPoints.push(
  31741. oldCoordSys.dataToPoint([
  31742. newData.get(newDataOldCoordInfo.dataDimsForPoint[0], idx),
  31743. newData.get(newDataOldCoordInfo.dataDimsForPoint[1], idx)
  31744. ])
  31745. );
  31746. nextPoints.push(newData.getItemLayout(idx).slice());
  31747. currStackedPoints.push(
  31748. getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, idx)
  31749. );
  31750. nextStackedPoints.push(newStackedOnPoints[idx]);
  31751. rawIndices.push(newData.getRawIndex(idx));
  31752. break;
  31753. case '-':
  31754. var idx = diffItem.idx;
  31755. var rawIndex = oldData.getRawIndex(idx);
  31756. // Data is replaced. In the case of dynamic data queue
  31757. // FIXME FIXME FIXME
  31758. if (rawIndex !== idx) {
  31759. currPoints.push(oldData.getItemLayout(idx));
  31760. nextPoints.push(newCoordSys.dataToPoint([
  31761. oldData.get(oldDataNewCoordInfo.dataDimsForPoint[0], idx),
  31762. oldData.get(oldDataNewCoordInfo.dataDimsForPoint[1], idx)
  31763. ]));
  31764. currStackedPoints.push(oldStackedOnPoints[idx]);
  31765. nextStackedPoints.push(
  31766. getStackedOnPoint(oldDataNewCoordInfo, newCoordSys, oldData, idx)
  31767. );
  31768. rawIndices.push(rawIndex);
  31769. }
  31770. else {
  31771. pointAdded = false;
  31772. }
  31773. }
  31774. // Original indices
  31775. if (pointAdded) {
  31776. status.push(diffItem);
  31777. sortedIndices.push(sortedIndices.length);
  31778. }
  31779. }
  31780. // Diff result may be crossed if all items are changed
  31781. // Sort by data index
  31782. sortedIndices.sort(function (a, b) {
  31783. return rawIndices[a] - rawIndices[b];
  31784. });
  31785. var sortedCurrPoints = [];
  31786. var sortedNextPoints = [];
  31787. var sortedCurrStackedPoints = [];
  31788. var sortedNextStackedPoints = [];
  31789. var sortedStatus = [];
  31790. for (var i = 0; i < sortedIndices.length; i++) {
  31791. var idx = sortedIndices[i];
  31792. sortedCurrPoints[i] = currPoints[idx];
  31793. sortedNextPoints[i] = nextPoints[idx];
  31794. sortedCurrStackedPoints[i] = currStackedPoints[idx];
  31795. sortedNextStackedPoints[i] = nextStackedPoints[idx];
  31796. sortedStatus[i] = status[idx];
  31797. }
  31798. return {
  31799. current: sortedCurrPoints,
  31800. next: sortedNextPoints,
  31801. stackedOnCurrent: sortedCurrStackedPoints,
  31802. stackedOnNext: sortedNextStackedPoints,
  31803. status: sortedStatus
  31804. };
  31805. };
  31806. /*
  31807. * Licensed to the Apache Software Foundation (ASF) under one
  31808. * or more contributor license agreements. See the NOTICE file
  31809. * distributed with this work for additional information
  31810. * regarding copyright ownership. The ASF licenses this file
  31811. * to you under the Apache License, Version 2.0 (the
  31812. * "License"); you may not use this file except in compliance
  31813. * with the License. You may obtain a copy of the License at
  31814. *
  31815. * http://www.apache.org/licenses/LICENSE-2.0
  31816. *
  31817. * Unless required by applicable law or agreed to in writing,
  31818. * software distributed under the License is distributed on an
  31819. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  31820. * KIND, either express or implied. See the License for the
  31821. * specific language governing permissions and limitations
  31822. * under the License.
  31823. */
  31824. // Poly path support NaN point
  31825. var vec2Min = min;
  31826. var vec2Max = max;
  31827. var scaleAndAdd$1 = scaleAndAdd;
  31828. var v2Copy = copy;
  31829. // Temporary variable
  31830. var v = [];
  31831. var cp0 = [];
  31832. var cp1 = [];
  31833. function isPointNull(p) {
  31834. return isNaN(p[0]) || isNaN(p[1]);
  31835. }
  31836. function drawSegment(
  31837. ctx, points, start, segLen, allLen,
  31838. dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
  31839. ) {
  31840. // if (smoothMonotone == null) {
  31841. // if (isMono(points, 'x')) {
  31842. // return drawMono(ctx, points, start, segLen, allLen,
  31843. // dir, smoothMin, smoothMax, smooth, 'x', connectNulls);
  31844. // }
  31845. // else if (isMono(points, 'y')) {
  31846. // return drawMono(ctx, points, start, segLen, allLen,
  31847. // dir, smoothMin, smoothMax, smooth, 'y', connectNulls);
  31848. // }
  31849. // else {
  31850. // return drawNonMono.apply(this, arguments);
  31851. // }
  31852. // }
  31853. // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) {
  31854. // return drawMono.apply(this, arguments);
  31855. // }
  31856. // else {
  31857. // return drawNonMono.apply(this, arguments);
  31858. // }
  31859. if (smoothMonotone === 'none' || !smoothMonotone) {
  31860. return drawNonMono.apply(this, arguments);
  31861. }
  31862. else {
  31863. return drawMono.apply(this, arguments);
  31864. }
  31865. }
  31866. /**
  31867. * Check if points is in monotone.
  31868. *
  31869. * @param {number[][]} points Array of points which is in [x, y] form
  31870. * @param {string} smoothMonotone 'x', 'y', or 'none', stating for which
  31871. * dimension that is checking.
  31872. * If is 'none', `drawNonMono` should be
  31873. * called.
  31874. * If is undefined, either being monotone
  31875. * in 'x' or 'y' will call `drawMono`.
  31876. */
  31877. // function isMono(points, smoothMonotone) {
  31878. // if (points.length <= 1) {
  31879. // return true;
  31880. // }
  31881. // var dim = smoothMonotone === 'x' ? 0 : 1;
  31882. // var last = points[0][dim];
  31883. // var lastDiff = 0;
  31884. // for (var i = 1; i < points.length; ++i) {
  31885. // var diff = points[i][dim] - last;
  31886. // if (!isNaN(diff) && !isNaN(lastDiff)
  31887. // && diff !== 0 && lastDiff !== 0
  31888. // && ((diff >= 0) !== (lastDiff >= 0))
  31889. // ) {
  31890. // return false;
  31891. // }
  31892. // if (!isNaN(diff) && diff !== 0) {
  31893. // lastDiff = diff;
  31894. // last = points[i][dim];
  31895. // }
  31896. // }
  31897. // return true;
  31898. // }
  31899. /**
  31900. * Draw smoothed line in monotone, in which only vertical or horizontal bezier
  31901. * control points will be used. This should be used when points are monotone
  31902. * either in x or y dimension.
  31903. */
  31904. function drawMono(
  31905. ctx, points, start, segLen, allLen,
  31906. dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
  31907. ) {
  31908. var prevIdx = 0;
  31909. var idx = start;
  31910. for (var k = 0; k < segLen; k++) {
  31911. var p = points[idx];
  31912. if (idx >= allLen || idx < 0) {
  31913. break;
  31914. }
  31915. if (isPointNull(p)) {
  31916. if (connectNulls) {
  31917. idx += dir;
  31918. continue;
  31919. }
  31920. break;
  31921. }
  31922. if (idx === start) {
  31923. ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
  31924. }
  31925. else {
  31926. if (smooth > 0) {
  31927. var prevP = points[prevIdx];
  31928. var dim = smoothMonotone === 'y' ? 1 : 0;
  31929. // Length of control point to p, either in x or y, but not both
  31930. var ctrlLen = (p[dim] - prevP[dim]) * smooth;
  31931. v2Copy(cp0, prevP);
  31932. cp0[dim] = prevP[dim] + ctrlLen;
  31933. v2Copy(cp1, p);
  31934. cp1[dim] = p[dim] - ctrlLen;
  31935. ctx.bezierCurveTo(
  31936. cp0[0], cp0[1],
  31937. cp1[0], cp1[1],
  31938. p[0], p[1]
  31939. );
  31940. }
  31941. else {
  31942. ctx.lineTo(p[0], p[1]);
  31943. }
  31944. }
  31945. prevIdx = idx;
  31946. idx += dir;
  31947. }
  31948. return k;
  31949. }
  31950. /**
  31951. * Draw smoothed line in non-monotone, in may cause undesired curve in extreme
  31952. * situations. This should be used when points are non-monotone neither in x or
  31953. * y dimension.
  31954. */
  31955. function drawNonMono(
  31956. ctx, points, start, segLen, allLen,
  31957. dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
  31958. ) {
  31959. var prevIdx = 0;
  31960. var idx = start;
  31961. for (var k = 0; k < segLen; k++) {
  31962. var p = points[idx];
  31963. if (idx >= allLen || idx < 0) {
  31964. break;
  31965. }
  31966. if (isPointNull(p)) {
  31967. if (connectNulls) {
  31968. idx += dir;
  31969. continue;
  31970. }
  31971. break;
  31972. }
  31973. if (idx === start) {
  31974. ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
  31975. v2Copy(cp0, p);
  31976. }
  31977. else {
  31978. if (smooth > 0) {
  31979. var nextIdx = idx + dir;
  31980. var nextP = points[nextIdx];
  31981. if (connectNulls) {
  31982. // Find next point not null
  31983. while (nextP && isPointNull(points[nextIdx])) {
  31984. nextIdx += dir;
  31985. nextP = points[nextIdx];
  31986. }
  31987. }
  31988. var ratioNextSeg = 0.5;
  31989. var prevP = points[prevIdx];
  31990. var nextP = points[nextIdx];
  31991. // Last point
  31992. if (!nextP || isPointNull(nextP)) {
  31993. v2Copy(cp1, p);
  31994. }
  31995. else {
  31996. // If next data is null in not connect case
  31997. if (isPointNull(nextP) && !connectNulls) {
  31998. nextP = p;
  31999. }
  32000. sub(v, nextP, prevP);
  32001. var lenPrevSeg;
  32002. var lenNextSeg;
  32003. if (smoothMonotone === 'x' || smoothMonotone === 'y') {
  32004. var dim = smoothMonotone === 'x' ? 0 : 1;
  32005. lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
  32006. lenNextSeg = Math.abs(p[dim] - nextP[dim]);
  32007. }
  32008. else {
  32009. lenPrevSeg = dist(p, prevP);
  32010. lenNextSeg = dist(p, nextP);
  32011. }
  32012. // Use ratio of seg length
  32013. ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
  32014. scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg));
  32015. }
  32016. // Smooth constraint
  32017. vec2Min(cp0, cp0, smoothMax);
  32018. vec2Max(cp0, cp0, smoothMin);
  32019. vec2Min(cp1, cp1, smoothMax);
  32020. vec2Max(cp1, cp1, smoothMin);
  32021. ctx.bezierCurveTo(
  32022. cp0[0], cp0[1],
  32023. cp1[0], cp1[1],
  32024. p[0], p[1]
  32025. );
  32026. // cp0 of next segment
  32027. scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg);
  32028. }
  32029. else {
  32030. ctx.lineTo(p[0], p[1]);
  32031. }
  32032. }
  32033. prevIdx = idx;
  32034. idx += dir;
  32035. }
  32036. return k;
  32037. }
  32038. function getBoundingBox(points, smoothConstraint) {
  32039. var ptMin = [Infinity, Infinity];
  32040. var ptMax = [-Infinity, -Infinity];
  32041. if (smoothConstraint) {
  32042. for (var i = 0; i < points.length; i++) {
  32043. var pt = points[i];
  32044. if (pt[0] < ptMin[0]) {
  32045. ptMin[0] = pt[0];
  32046. }
  32047. if (pt[1] < ptMin[1]) {
  32048. ptMin[1] = pt[1];
  32049. }
  32050. if (pt[0] > ptMax[0]) {
  32051. ptMax[0] = pt[0];
  32052. }
  32053. if (pt[1] > ptMax[1]) {
  32054. ptMax[1] = pt[1];
  32055. }
  32056. }
  32057. }
  32058. return {
  32059. min: smoothConstraint ? ptMin : ptMax,
  32060. max: smoothConstraint ? ptMax : ptMin
  32061. };
  32062. }
  32063. var Polyline$1 = Path.extend({
  32064. type: 'ec-polyline',
  32065. shape: {
  32066. points: [],
  32067. smooth: 0,
  32068. smoothConstraint: true,
  32069. smoothMonotone: null,
  32070. connectNulls: false
  32071. },
  32072. style: {
  32073. fill: null,
  32074. stroke: '#000'
  32075. },
  32076. brush: fixClipWithShadow(Path.prototype.brush),
  32077. buildPath: function (ctx, shape) {
  32078. var points = shape.points;
  32079. var i = 0;
  32080. var len$$1 = points.length;
  32081. var result = getBoundingBox(points, shape.smoothConstraint);
  32082. if (shape.connectNulls) {
  32083. // Must remove first and last null values avoid draw error in polygon
  32084. for (; len$$1 > 0; len$$1--) {
  32085. if (!isPointNull(points[len$$1 - 1])) {
  32086. break;
  32087. }
  32088. }
  32089. for (; i < len$$1; i++) {
  32090. if (!isPointNull(points[i])) {
  32091. break;
  32092. }
  32093. }
  32094. }
  32095. while (i < len$$1) {
  32096. i += drawSegment(
  32097. ctx, points, i, len$$1, len$$1,
  32098. 1, result.min, result.max, shape.smooth,
  32099. shape.smoothMonotone, shape.connectNulls
  32100. ) + 1;
  32101. }
  32102. }
  32103. });
  32104. var Polygon$1 = Path.extend({
  32105. type: 'ec-polygon',
  32106. shape: {
  32107. points: [],
  32108. // Offset between stacked base points and points
  32109. stackedOnPoints: [],
  32110. smooth: 0,
  32111. stackedOnSmooth: 0,
  32112. smoothConstraint: true,
  32113. smoothMonotone: null,
  32114. connectNulls: false
  32115. },
  32116. brush: fixClipWithShadow(Path.prototype.brush),
  32117. buildPath: function (ctx, shape) {
  32118. var points = shape.points;
  32119. var stackedOnPoints = shape.stackedOnPoints;
  32120. var i = 0;
  32121. var len$$1 = points.length;
  32122. var smoothMonotone = shape.smoothMonotone;
  32123. var bbox = getBoundingBox(points, shape.smoothConstraint);
  32124. var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
  32125. if (shape.connectNulls) {
  32126. // Must remove first and last null values avoid draw error in polygon
  32127. for (; len$$1 > 0; len$$1--) {
  32128. if (!isPointNull(points[len$$1 - 1])) {
  32129. break;
  32130. }
  32131. }
  32132. for (; i < len$$1; i++) {
  32133. if (!isPointNull(points[i])) {
  32134. break;
  32135. }
  32136. }
  32137. }
  32138. while (i < len$$1) {
  32139. var k = drawSegment(
  32140. ctx, points, i, len$$1, len$$1,
  32141. 1, bbox.min, bbox.max, shape.smooth,
  32142. smoothMonotone, shape.connectNulls
  32143. );
  32144. drawSegment(
  32145. ctx, stackedOnPoints, i + k - 1, k, len$$1,
  32146. -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,
  32147. smoothMonotone, shape.connectNulls
  32148. );
  32149. i += k + 1;
  32150. ctx.closePath();
  32151. }
  32152. }
  32153. });
  32154. /*
  32155. * Licensed to the Apache Software Foundation (ASF) under one
  32156. * or more contributor license agreements. See the NOTICE file
  32157. * distributed with this work for additional information
  32158. * regarding copyright ownership. The ASF licenses this file
  32159. * to you under the Apache License, Version 2.0 (the
  32160. * "License"); you may not use this file except in compliance
  32161. * with the License. You may obtain a copy of the License at
  32162. *
  32163. * http://www.apache.org/licenses/LICENSE-2.0
  32164. *
  32165. * Unless required by applicable law or agreed to in writing,
  32166. * software distributed under the License is distributed on an
  32167. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  32168. * KIND, either express or implied. See the License for the
  32169. * specific language governing permissions and limitations
  32170. * under the License.
  32171. */
  32172. // FIXME step not support polar
  32173. function isPointsSame(points1, points2) {
  32174. if (points1.length !== points2.length) {
  32175. return;
  32176. }
  32177. for (var i = 0; i < points1.length; i++) {
  32178. var p1 = points1[i];
  32179. var p2 = points2[i];
  32180. if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
  32181. return;
  32182. }
  32183. }
  32184. return true;
  32185. }
  32186. function getSmooth(smooth) {
  32187. return typeof (smooth) === 'number' ? smooth : (smooth ? 0.5 : 0);
  32188. }
  32189. function getAxisExtentWithGap(axis) {
  32190. var extent = axis.getGlobalExtent();
  32191. if (axis.onBand) {
  32192. // Remove extra 1px to avoid line miter in clipped edge
  32193. var halfBandWidth = axis.getBandWidth() / 2 - 1;
  32194. var dir = extent[1] > extent[0] ? 1 : -1;
  32195. extent[0] += dir * halfBandWidth;
  32196. extent[1] -= dir * halfBandWidth;
  32197. }
  32198. return extent;
  32199. }
  32200. /**
  32201. * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
  32202. * @param {module:echarts/data/List} data
  32203. * @param {Object} dataCoordInfo
  32204. * @param {Array.<Array.<number>>} points
  32205. */
  32206. function getStackedOnPoints(coordSys, data, dataCoordInfo) {
  32207. if (!dataCoordInfo.valueDim) {
  32208. return [];
  32209. }
  32210. var points = [];
  32211. for (var idx = 0, len = data.count(); idx < len; idx++) {
  32212. points.push(getStackedOnPoint(dataCoordInfo, coordSys, data, idx));
  32213. }
  32214. return points;
  32215. }
  32216. function createGridClipShape(cartesian, hasAnimation, forSymbol, seriesModel) {
  32217. var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
  32218. var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
  32219. var isHorizontal = cartesian.getBaseAxis().isHorizontal();
  32220. var x = Math.min(xExtent[0], xExtent[1]);
  32221. var y = Math.min(yExtent[0], yExtent[1]);
  32222. var width = Math.max(xExtent[0], xExtent[1]) - x;
  32223. var height = Math.max(yExtent[0], yExtent[1]) - y;
  32224. // Avoid float number rounding error for symbol on the edge of axis extent.
  32225. // See #7913 and `test/dataZoom-clip.html`.
  32226. if (forSymbol) {
  32227. x -= 0.5;
  32228. width += 0.5;
  32229. y -= 0.5;
  32230. height += 0.5;
  32231. }
  32232. else {
  32233. var lineWidth = seriesModel.get('lineStyle.width') || 2;
  32234. // Expand clip shape to avoid clipping when line value exceeds axis
  32235. var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
  32236. if (isHorizontal) {
  32237. y -= expandSize;
  32238. height += expandSize * 2;
  32239. }
  32240. else {
  32241. x -= expandSize;
  32242. width += expandSize * 2;
  32243. }
  32244. }
  32245. var clipPath = new Rect({
  32246. shape: {
  32247. x: x,
  32248. y: y,
  32249. width: width,
  32250. height: height
  32251. }
  32252. });
  32253. if (hasAnimation) {
  32254. clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
  32255. initProps(clipPath, {
  32256. shape: {
  32257. width: width,
  32258. height: height
  32259. }
  32260. }, seriesModel);
  32261. }
  32262. return clipPath;
  32263. }
  32264. function createPolarClipShape(polar, hasAnimation, forSymbol, seriesModel) {
  32265. var angleAxis = polar.getAngleAxis();
  32266. var radiusAxis = polar.getRadiusAxis();
  32267. var radiusExtent = radiusAxis.getExtent().slice();
  32268. radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse();
  32269. var angleExtent = angleAxis.getExtent();
  32270. var RADIAN = Math.PI / 180;
  32271. // Avoid float number rounding error for symbol on the edge of axis extent.
  32272. if (forSymbol) {
  32273. radiusExtent[0] -= 0.5;
  32274. radiusExtent[1] += 0.5;
  32275. }
  32276. var clipPath = new Sector({
  32277. shape: {
  32278. cx: round$2(polar.cx, 1),
  32279. cy: round$2(polar.cy, 1),
  32280. r0: round$2(radiusExtent[0], 1),
  32281. r: round$2(radiusExtent[1], 1),
  32282. startAngle: -angleExtent[0] * RADIAN,
  32283. endAngle: -angleExtent[1] * RADIAN,
  32284. clockwise: angleAxis.inverse
  32285. }
  32286. });
  32287. if (hasAnimation) {
  32288. clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
  32289. initProps(clipPath, {
  32290. shape: {
  32291. endAngle: -angleExtent[1] * RADIAN
  32292. }
  32293. }, seriesModel);
  32294. }
  32295. return clipPath;
  32296. }
  32297. function createClipShape(coordSys, hasAnimation, forSymbol, seriesModel) {
  32298. return coordSys.type === 'polar'
  32299. ? createPolarClipShape(coordSys, hasAnimation, forSymbol, seriesModel)
  32300. : createGridClipShape(coordSys, hasAnimation, forSymbol, seriesModel);
  32301. }
  32302. function turnPointsIntoStep(points, coordSys, stepTurnAt) {
  32303. var baseAxis = coordSys.getBaseAxis();
  32304. var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
  32305. var stepPoints = [];
  32306. for (var i = 0; i < points.length - 1; i++) {
  32307. var nextPt = points[i + 1];
  32308. var pt = points[i];
  32309. stepPoints.push(pt);
  32310. var stepPt = [];
  32311. switch (stepTurnAt) {
  32312. case 'end':
  32313. stepPt[baseIndex] = nextPt[baseIndex];
  32314. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  32315. // default is start
  32316. stepPoints.push(stepPt);
  32317. break;
  32318. case 'middle':
  32319. // default is start
  32320. var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
  32321. var stepPt2 = [];
  32322. stepPt[baseIndex] = stepPt2[baseIndex] = middle;
  32323. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  32324. stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
  32325. stepPoints.push(stepPt);
  32326. stepPoints.push(stepPt2);
  32327. break;
  32328. default:
  32329. stepPt[baseIndex] = pt[baseIndex];
  32330. stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
  32331. // default is start
  32332. stepPoints.push(stepPt);
  32333. }
  32334. }
  32335. // Last points
  32336. points[i] && stepPoints.push(points[i]);
  32337. return stepPoints;
  32338. }
  32339. function getVisualGradient(data, coordSys) {
  32340. var visualMetaList = data.getVisual('visualMeta');
  32341. if (!visualMetaList || !visualMetaList.length || !data.count()) {
  32342. // When data.count() is 0, gradient range can not be calculated.
  32343. return;
  32344. }
  32345. if (coordSys.type !== 'cartesian2d') {
  32346. if (__DEV__) {
  32347. console.warn('Visual map on line style is only supported on cartesian2d.');
  32348. }
  32349. return;
  32350. }
  32351. var coordDim;
  32352. var visualMeta;
  32353. for (var i = visualMetaList.length - 1; i >= 0; i--) {
  32354. var dimIndex = visualMetaList[i].dimension;
  32355. var dimName = data.dimensions[dimIndex];
  32356. var dimInfo = data.getDimensionInfo(dimName);
  32357. coordDim = dimInfo && dimInfo.coordDim;
  32358. // Can only be x or y
  32359. if (coordDim === 'x' || coordDim === 'y') {
  32360. visualMeta = visualMetaList[i];
  32361. break;
  32362. }
  32363. }
  32364. if (!visualMeta) {
  32365. if (__DEV__) {
  32366. console.warn('Visual map on line style only support x or y dimension.');
  32367. }
  32368. return;
  32369. }
  32370. // If the area to be rendered is bigger than area defined by LinearGradient,
  32371. // the canvas spec prescribes that the color of the first stop and the last
  32372. // stop should be used. But if two stops are added at offset 0, in effect
  32373. // browsers use the color of the second stop to render area outside
  32374. // LinearGradient. So we can only infinitesimally extend area defined in
  32375. // LinearGradient to render `outerColors`.
  32376. var axis = coordSys.getAxis(coordDim);
  32377. // dataToCoor mapping may not be linear, but must be monotonic.
  32378. var colorStops = map(visualMeta.stops, function (stop) {
  32379. return {
  32380. coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
  32381. color: stop.color
  32382. };
  32383. });
  32384. var stopLen = colorStops.length;
  32385. var outerColors = visualMeta.outerColors.slice();
  32386. if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
  32387. colorStops.reverse();
  32388. outerColors.reverse();
  32389. }
  32390. var tinyExtent = 10; // Arbitrary value: 10px
  32391. var minCoord = colorStops[0].coord - tinyExtent;
  32392. var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
  32393. var coordSpan = maxCoord - minCoord;
  32394. if (coordSpan < 1e-3) {
  32395. return 'transparent';
  32396. }
  32397. each$1(colorStops, function (stop) {
  32398. stop.offset = (stop.coord - minCoord) / coordSpan;
  32399. });
  32400. colorStops.push({
  32401. offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
  32402. color: outerColors[1] || 'transparent'
  32403. });
  32404. colorStops.unshift({ // notice colorStops.length have been changed.
  32405. offset: stopLen ? colorStops[0].offset : 0.5,
  32406. color: outerColors[0] || 'transparent'
  32407. });
  32408. // zrUtil.each(colorStops, function (colorStop) {
  32409. // // Make sure each offset has rounded px to avoid not sharp edge
  32410. // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
  32411. // });
  32412. var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true);
  32413. gradient[coordDim] = minCoord;
  32414. gradient[coordDim + '2'] = maxCoord;
  32415. return gradient;
  32416. }
  32417. function getIsIgnoreFunc(seriesModel, data, coordSys) {
  32418. var showAllSymbol = seriesModel.get('showAllSymbol');
  32419. var isAuto = showAllSymbol === 'auto';
  32420. if (showAllSymbol && !isAuto) {
  32421. return;
  32422. }
  32423. var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
  32424. if (!categoryAxis) {
  32425. return;
  32426. }
  32427. // Note that category label interval strategy might bring some weird effect
  32428. // in some scenario: users may wonder why some of the symbols are not
  32429. // displayed. So we show all symbols as possible as we can.
  32430. if (isAuto
  32431. // Simplify the logic, do not determine label overlap here.
  32432. && canShowAllSymbolForCategory(categoryAxis, data)
  32433. ) {
  32434. return;
  32435. }
  32436. // Otherwise follow the label interval strategy on category axis.
  32437. var categoryDataDim = data.mapDimension(categoryAxis.dim);
  32438. var labelMap = {};
  32439. each$1(categoryAxis.getViewLabels(), function (labelItem) {
  32440. labelMap[labelItem.tickValue] = 1;
  32441. });
  32442. return function (dataIndex) {
  32443. return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));
  32444. };
  32445. }
  32446. function canShowAllSymbolForCategory(categoryAxis, data) {
  32447. // In mose cases, line is monotonous on category axis, and the label size
  32448. // is close with each other. So we check the symbol size and some of the
  32449. // label size alone with the category axis to estimate whether all symbol
  32450. // can be shown without overlap.
  32451. var axisExtent = categoryAxis.getExtent();
  32452. var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();
  32453. isNaN(availSize) && (availSize = 0); // 0/0 is NaN.
  32454. // Sampling some points, max 5.
  32455. var dataLen = data.count();
  32456. var step = Math.max(1, Math.round(dataLen / 5));
  32457. for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {
  32458. if (SymbolClz$1.getSymbolSize(
  32459. data, dataIndex
  32460. // Only for cartesian, where `isHorizontal` exists.
  32461. )[categoryAxis.isHorizontal() ? 1 : 0]
  32462. // Empirical number
  32463. * 1.5 > availSize
  32464. ) {
  32465. return false;
  32466. }
  32467. }
  32468. return true;
  32469. }
  32470. Chart.extend({
  32471. type: 'line',
  32472. init: function () {
  32473. var lineGroup = new Group();
  32474. var symbolDraw = new SymbolDraw();
  32475. this.group.add(symbolDraw.group);
  32476. this._symbolDraw = symbolDraw;
  32477. this._lineGroup = lineGroup;
  32478. },
  32479. render: function (seriesModel, ecModel, api) {
  32480. var coordSys = seriesModel.coordinateSystem;
  32481. var group = this.group;
  32482. var data = seriesModel.getData();
  32483. var lineStyleModel = seriesModel.getModel('lineStyle');
  32484. var areaStyleModel = seriesModel.getModel('areaStyle');
  32485. var points = data.mapArray(data.getItemLayout);
  32486. var isCoordSysPolar = coordSys.type === 'polar';
  32487. var prevCoordSys = this._coordSys;
  32488. var symbolDraw = this._symbolDraw;
  32489. var polyline = this._polyline;
  32490. var polygon = this._polygon;
  32491. var lineGroup = this._lineGroup;
  32492. var hasAnimation = seriesModel.get('animation');
  32493. var isAreaChart = !areaStyleModel.isEmpty();
  32494. var valueOrigin = areaStyleModel.get('origin');
  32495. var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);
  32496. var stackedOnPoints = getStackedOnPoints(coordSys, data, dataCoordInfo);
  32497. var showSymbol = seriesModel.get('showSymbol');
  32498. var isIgnoreFunc = showSymbol && !isCoordSysPolar
  32499. && getIsIgnoreFunc(seriesModel, data, coordSys);
  32500. // Remove temporary symbols
  32501. var oldData = this._data;
  32502. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  32503. if (el.__temp) {
  32504. group.remove(el);
  32505. oldData.setItemGraphicEl(idx, null);
  32506. }
  32507. });
  32508. // Remove previous created symbols if showSymbol changed to false
  32509. if (!showSymbol) {
  32510. symbolDraw.remove();
  32511. }
  32512. group.add(lineGroup);
  32513. // FIXME step not support polar
  32514. var step = !isCoordSysPolar && seriesModel.get('step');
  32515. // Initialization animation or coordinate system changed
  32516. if (
  32517. !(polyline && prevCoordSys.type === coordSys.type && step === this._step)
  32518. ) {
  32519. showSymbol && symbolDraw.updateData(data, {
  32520. isIgnore: isIgnoreFunc,
  32521. clipShape: createClipShape(coordSys, false, true, seriesModel)
  32522. });
  32523. if (step) {
  32524. // TODO If stacked series is not step
  32525. points = turnPointsIntoStep(points, coordSys, step);
  32526. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  32527. }
  32528. polyline = this._newPolyline(points, coordSys, hasAnimation);
  32529. if (isAreaChart) {
  32530. polygon = this._newPolygon(
  32531. points, stackedOnPoints,
  32532. coordSys, hasAnimation
  32533. );
  32534. }
  32535. lineGroup.setClipPath(createClipShape(coordSys, true, false, seriesModel));
  32536. }
  32537. else {
  32538. if (isAreaChart && !polygon) {
  32539. // If areaStyle is added
  32540. polygon = this._newPolygon(
  32541. points, stackedOnPoints,
  32542. coordSys, hasAnimation
  32543. );
  32544. }
  32545. else if (polygon && !isAreaChart) {
  32546. // If areaStyle is removed
  32547. lineGroup.remove(polygon);
  32548. polygon = this._polygon = null;
  32549. }
  32550. // Update clipPath
  32551. lineGroup.setClipPath(createClipShape(coordSys, false, false, seriesModel));
  32552. // Always update, or it is wrong in the case turning on legend
  32553. // because points are not changed
  32554. showSymbol && symbolDraw.updateData(data, {
  32555. isIgnore: isIgnoreFunc,
  32556. clipShape: createClipShape(coordSys, false, true, seriesModel)
  32557. });
  32558. // Stop symbol animation and sync with line points
  32559. // FIXME performance?
  32560. data.eachItemGraphicEl(function (el) {
  32561. el.stopAnimation(true);
  32562. });
  32563. // In the case data zoom triggerred refreshing frequently
  32564. // Data may not change if line has a category axis. So it should animate nothing
  32565. if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
  32566. || !isPointsSame(this._points, points)
  32567. ) {
  32568. if (hasAnimation) {
  32569. this._updateAnimation(
  32570. data, stackedOnPoints, coordSys, api, step, valueOrigin
  32571. );
  32572. }
  32573. else {
  32574. // Not do it in update with animation
  32575. if (step) {
  32576. // TODO If stacked series is not step
  32577. points = turnPointsIntoStep(points, coordSys, step);
  32578. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  32579. }
  32580. polyline.setShape({
  32581. points: points
  32582. });
  32583. polygon && polygon.setShape({
  32584. points: points,
  32585. stackedOnPoints: stackedOnPoints
  32586. });
  32587. }
  32588. }
  32589. }
  32590. var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
  32591. polyline.useStyle(defaults(
  32592. // Use color in lineStyle first
  32593. lineStyleModel.getLineStyle(),
  32594. {
  32595. fill: 'none',
  32596. stroke: visualColor,
  32597. lineJoin: 'bevel'
  32598. }
  32599. ));
  32600. var smooth = seriesModel.get('smooth');
  32601. smooth = getSmooth(seriesModel.get('smooth'));
  32602. polyline.setShape({
  32603. smooth: smooth,
  32604. smoothMonotone: seriesModel.get('smoothMonotone'),
  32605. connectNulls: seriesModel.get('connectNulls')
  32606. });
  32607. if (polygon) {
  32608. var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');
  32609. var stackedOnSmooth = 0;
  32610. polygon.useStyle(defaults(
  32611. areaStyleModel.getAreaStyle(),
  32612. {
  32613. fill: visualColor,
  32614. opacity: 0.7,
  32615. lineJoin: 'bevel'
  32616. }
  32617. ));
  32618. if (stackedOnSeries) {
  32619. stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
  32620. }
  32621. polygon.setShape({
  32622. smooth: smooth,
  32623. stackedOnSmooth: stackedOnSmooth,
  32624. smoothMonotone: seriesModel.get('smoothMonotone'),
  32625. connectNulls: seriesModel.get('connectNulls')
  32626. });
  32627. }
  32628. this._data = data;
  32629. // Save the coordinate system for transition animation when data changed
  32630. this._coordSys = coordSys;
  32631. this._stackedOnPoints = stackedOnPoints;
  32632. this._points = points;
  32633. this._step = step;
  32634. this._valueOrigin = valueOrigin;
  32635. },
  32636. dispose: function () {},
  32637. highlight: function (seriesModel, ecModel, api, payload) {
  32638. var data = seriesModel.getData();
  32639. var dataIndex = queryDataIndex(data, payload);
  32640. if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {
  32641. var symbol = data.getItemGraphicEl(dataIndex);
  32642. if (!symbol) {
  32643. // Create a temporary symbol if it is not exists
  32644. var pt = data.getItemLayout(dataIndex);
  32645. if (!pt) {
  32646. // Null data
  32647. return;
  32648. }
  32649. symbol = new SymbolClz$1(data, dataIndex);
  32650. symbol.position = pt;
  32651. symbol.setZ(
  32652. seriesModel.get('zlevel'),
  32653. seriesModel.get('z')
  32654. );
  32655. symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
  32656. symbol.__temp = true;
  32657. data.setItemGraphicEl(dataIndex, symbol);
  32658. // Stop scale animation
  32659. symbol.stopSymbolAnimation(true);
  32660. this.group.add(symbol);
  32661. }
  32662. symbol.highlight();
  32663. }
  32664. else {
  32665. // Highlight whole series
  32666. Chart.prototype.highlight.call(
  32667. this, seriesModel, ecModel, api, payload
  32668. );
  32669. }
  32670. },
  32671. downplay: function (seriesModel, ecModel, api, payload) {
  32672. var data = seriesModel.getData();
  32673. var dataIndex = queryDataIndex(data, payload);
  32674. if (dataIndex != null && dataIndex >= 0) {
  32675. var symbol = data.getItemGraphicEl(dataIndex);
  32676. if (symbol) {
  32677. if (symbol.__temp) {
  32678. data.setItemGraphicEl(dataIndex, null);
  32679. this.group.remove(symbol);
  32680. }
  32681. else {
  32682. symbol.downplay();
  32683. }
  32684. }
  32685. }
  32686. else {
  32687. // FIXME
  32688. // can not downplay completely.
  32689. // Downplay whole series
  32690. Chart.prototype.downplay.call(
  32691. this, seriesModel, ecModel, api, payload
  32692. );
  32693. }
  32694. },
  32695. /**
  32696. * @param {module:zrender/container/Group} group
  32697. * @param {Array.<Array.<number>>} points
  32698. * @private
  32699. */
  32700. _newPolyline: function (points) {
  32701. var polyline = this._polyline;
  32702. // Remove previous created polyline
  32703. if (polyline) {
  32704. this._lineGroup.remove(polyline);
  32705. }
  32706. polyline = new Polyline$1({
  32707. shape: {
  32708. points: points
  32709. },
  32710. silent: true,
  32711. z2: 10
  32712. });
  32713. this._lineGroup.add(polyline);
  32714. this._polyline = polyline;
  32715. return polyline;
  32716. },
  32717. /**
  32718. * @param {module:zrender/container/Group} group
  32719. * @param {Array.<Array.<number>>} stackedOnPoints
  32720. * @param {Array.<Array.<number>>} points
  32721. * @private
  32722. */
  32723. _newPolygon: function (points, stackedOnPoints) {
  32724. var polygon = this._polygon;
  32725. // Remove previous created polygon
  32726. if (polygon) {
  32727. this._lineGroup.remove(polygon);
  32728. }
  32729. polygon = new Polygon$1({
  32730. shape: {
  32731. points: points,
  32732. stackedOnPoints: stackedOnPoints
  32733. },
  32734. silent: true
  32735. });
  32736. this._lineGroup.add(polygon);
  32737. this._polygon = polygon;
  32738. return polygon;
  32739. },
  32740. /**
  32741. * @private
  32742. */
  32743. // FIXME Two value axis
  32744. _updateAnimation: function (data, stackedOnPoints, coordSys, api, step, valueOrigin) {
  32745. var polyline = this._polyline;
  32746. var polygon = this._polygon;
  32747. var seriesModel = data.hostModel;
  32748. var diff = lineAnimationDiff(
  32749. this._data, data,
  32750. this._stackedOnPoints, stackedOnPoints,
  32751. this._coordSys, coordSys,
  32752. this._valueOrigin, valueOrigin
  32753. );
  32754. var current = diff.current;
  32755. var stackedOnCurrent = diff.stackedOnCurrent;
  32756. var next = diff.next;
  32757. var stackedOnNext = diff.stackedOnNext;
  32758. if (step) {
  32759. // TODO If stacked series is not step
  32760. current = turnPointsIntoStep(diff.current, coordSys, step);
  32761. stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
  32762. next = turnPointsIntoStep(diff.next, coordSys, step);
  32763. stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
  32764. }
  32765. // `diff.current` is subset of `current` (which should be ensured by
  32766. // turnPointsIntoStep), so points in `__points` can be updated when
  32767. // points in `current` are update during animation.
  32768. polyline.shape.__points = diff.current;
  32769. polyline.shape.points = current;
  32770. updateProps(polyline, {
  32771. shape: {
  32772. points: next
  32773. }
  32774. }, seriesModel);
  32775. if (polygon) {
  32776. polygon.setShape({
  32777. points: current,
  32778. stackedOnPoints: stackedOnCurrent
  32779. });
  32780. updateProps(polygon, {
  32781. shape: {
  32782. points: next,
  32783. stackedOnPoints: stackedOnNext
  32784. }
  32785. }, seriesModel);
  32786. }
  32787. var updatedDataInfo = [];
  32788. var diffStatus = diff.status;
  32789. for (var i = 0; i < diffStatus.length; i++) {
  32790. var cmd = diffStatus[i].cmd;
  32791. if (cmd === '=') {
  32792. var el = data.getItemGraphicEl(diffStatus[i].idx1);
  32793. if (el) {
  32794. updatedDataInfo.push({
  32795. el: el,
  32796. ptIdx: i // Index of points
  32797. });
  32798. }
  32799. }
  32800. }
  32801. if (polyline.animators && polyline.animators.length) {
  32802. polyline.animators[0].during(function () {
  32803. for (var i = 0; i < updatedDataInfo.length; i++) {
  32804. var el = updatedDataInfo[i].el;
  32805. el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);
  32806. }
  32807. });
  32808. }
  32809. },
  32810. remove: function (ecModel) {
  32811. var group = this.group;
  32812. var oldData = this._data;
  32813. this._lineGroup.removeAll();
  32814. this._symbolDraw.remove(true);
  32815. // Remove temporary created elements when highlighting
  32816. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  32817. if (el.__temp) {
  32818. group.remove(el);
  32819. oldData.setItemGraphicEl(idx, null);
  32820. }
  32821. });
  32822. this._polyline
  32823. = this._polygon
  32824. = this._coordSys
  32825. = this._points
  32826. = this._stackedOnPoints
  32827. = this._data = null;
  32828. }
  32829. });
  32830. /*
  32831. * Licensed to the Apache Software Foundation (ASF) under one
  32832. * or more contributor license agreements. See the NOTICE file
  32833. * distributed with this work for additional information
  32834. * regarding copyright ownership. The ASF licenses this file
  32835. * to you under the Apache License, Version 2.0 (the
  32836. * "License"); you may not use this file except in compliance
  32837. * with the License. You may obtain a copy of the License at
  32838. *
  32839. * http://www.apache.org/licenses/LICENSE-2.0
  32840. *
  32841. * Unless required by applicable law or agreed to in writing,
  32842. * software distributed under the License is distributed on an
  32843. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  32844. * KIND, either express or implied. See the License for the
  32845. * specific language governing permissions and limitations
  32846. * under the License.
  32847. */
  32848. var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol) {
  32849. // Encoding visual for all series include which is filtered for legend drawing
  32850. return {
  32851. seriesType: seriesType,
  32852. // For legend.
  32853. performRawSeries: true,
  32854. reset: function (seriesModel, ecModel, api) {
  32855. var data = seriesModel.getData();
  32856. var symbolType = seriesModel.get('symbol') || defaultSymbolType;
  32857. var symbolSize = seriesModel.get('symbolSize');
  32858. var keepAspect = seriesModel.get('symbolKeepAspect');
  32859. data.setVisual({
  32860. legendSymbol: legendSymbol || symbolType,
  32861. symbol: symbolType,
  32862. symbolSize: symbolSize,
  32863. symbolKeepAspect: keepAspect
  32864. });
  32865. // Only visible series has each data be visual encoded
  32866. if (ecModel.isSeriesFiltered(seriesModel)) {
  32867. return;
  32868. }
  32869. var hasCallback = typeof symbolSize === 'function';
  32870. function dataEach(data, idx) {
  32871. if (typeof symbolSize === 'function') {
  32872. var rawValue = seriesModel.getRawValue(idx);
  32873. // FIXME
  32874. var params = seriesModel.getDataParams(idx);
  32875. data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
  32876. }
  32877. if (data.hasItemOption) {
  32878. var itemModel = data.getItemModel(idx);
  32879. var itemSymbolType = itemModel.getShallow('symbol', true);
  32880. var itemSymbolSize = itemModel.getShallow('symbolSize',
  32881. true);
  32882. var itemSymbolKeepAspect
  32883. = itemModel.getShallow('symbolKeepAspect', true);
  32884. // If has item symbol
  32885. if (itemSymbolType != null) {
  32886. data.setItemVisual(idx, 'symbol', itemSymbolType);
  32887. }
  32888. if (itemSymbolSize != null) {
  32889. // PENDING Transform symbolSize ?
  32890. data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
  32891. }
  32892. if (itemSymbolKeepAspect != null) {
  32893. data.setItemVisual(idx, 'symbolKeepAspect',
  32894. itemSymbolKeepAspect);
  32895. }
  32896. }
  32897. }
  32898. return { dataEach: (data.hasItemOption || hasCallback) ? dataEach : null };
  32899. }
  32900. };
  32901. };
  32902. /*
  32903. * Licensed to the Apache Software Foundation (ASF) under one
  32904. * or more contributor license agreements. See the NOTICE file
  32905. * distributed with this work for additional information
  32906. * regarding copyright ownership. The ASF licenses this file
  32907. * to you under the Apache License, Version 2.0 (the
  32908. * "License"); you may not use this file except in compliance
  32909. * with the License. You may obtain a copy of the License at
  32910. *
  32911. * http://www.apache.org/licenses/LICENSE-2.0
  32912. *
  32913. * Unless required by applicable law or agreed to in writing,
  32914. * software distributed under the License is distributed on an
  32915. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  32916. * KIND, either express or implied. See the License for the
  32917. * specific language governing permissions and limitations
  32918. * under the License.
  32919. */
  32920. /* global Float32Array */
  32921. var pointsLayout = function (seriesType) {
  32922. return {
  32923. seriesType: seriesType,
  32924. plan: createRenderPlanner(),
  32925. reset: function (seriesModel) {
  32926. var data = seriesModel.getData();
  32927. var coordSys = seriesModel.coordinateSystem;
  32928. var pipelineContext = seriesModel.pipelineContext;
  32929. var isLargeRender = pipelineContext.large;
  32930. if (!coordSys) {
  32931. return;
  32932. }
  32933. var dims = map(coordSys.dimensions, function (dim) {
  32934. return data.mapDimension(dim);
  32935. }).slice(0, 2);
  32936. var dimLen = dims.length;
  32937. var stackResultDim = data.getCalculationInfo('stackResultDimension');
  32938. if (isDimensionStacked(data, dims[0] /*, dims[1]*/)) {
  32939. dims[0] = stackResultDim;
  32940. }
  32941. if (isDimensionStacked(data, dims[1] /*, dims[0]*/)) {
  32942. dims[1] = stackResultDim;
  32943. }
  32944. function progress(params, data) {
  32945. var segCount = params.end - params.start;
  32946. var points = isLargeRender && new Float32Array(segCount * dimLen);
  32947. for (var i = params.start, offset = 0, tmpIn = [], tmpOut = []; i < params.end; i++) {
  32948. var point;
  32949. if (dimLen === 1) {
  32950. var x = data.get(dims[0], i);
  32951. point = !isNaN(x) && coordSys.dataToPoint(x, null, tmpOut);
  32952. }
  32953. else {
  32954. var x = tmpIn[0] = data.get(dims[0], i);
  32955. var y = tmpIn[1] = data.get(dims[1], i);
  32956. // Also {Array.<number>}, not undefined to avoid if...else... statement
  32957. point = !isNaN(x) && !isNaN(y) && coordSys.dataToPoint(tmpIn, null, tmpOut);
  32958. }
  32959. if (isLargeRender) {
  32960. points[offset++] = point ? point[0] : NaN;
  32961. points[offset++] = point ? point[1] : NaN;
  32962. }
  32963. else {
  32964. data.setItemLayout(i, (point && point.slice()) || [NaN, NaN]);
  32965. }
  32966. }
  32967. isLargeRender && data.setLayout('symbolPoints', points);
  32968. }
  32969. return dimLen && {progress: progress};
  32970. }
  32971. };
  32972. };
  32973. /*
  32974. * Licensed to the Apache Software Foundation (ASF) under one
  32975. * or more contributor license agreements. See the NOTICE file
  32976. * distributed with this work for additional information
  32977. * regarding copyright ownership. The ASF licenses this file
  32978. * to you under the Apache License, Version 2.0 (the
  32979. * "License"); you may not use this file except in compliance
  32980. * with the License. You may obtain a copy of the License at
  32981. *
  32982. * http://www.apache.org/licenses/LICENSE-2.0
  32983. *
  32984. * Unless required by applicable law or agreed to in writing,
  32985. * software distributed under the License is distributed on an
  32986. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  32987. * KIND, either express or implied. See the License for the
  32988. * specific language governing permissions and limitations
  32989. * under the License.
  32990. */
  32991. var samplers = {
  32992. average: function (frame) {
  32993. var sum = 0;
  32994. var count = 0;
  32995. for (var i = 0; i < frame.length; i++) {
  32996. if (!isNaN(frame[i])) {
  32997. sum += frame[i];
  32998. count++;
  32999. }
  33000. }
  33001. // Return NaN if count is 0
  33002. return count === 0 ? NaN : sum / count;
  33003. },
  33004. sum: function (frame) {
  33005. var sum = 0;
  33006. for (var i = 0; i < frame.length; i++) {
  33007. // Ignore NaN
  33008. sum += frame[i] || 0;
  33009. }
  33010. return sum;
  33011. },
  33012. max: function (frame) {
  33013. var max = -Infinity;
  33014. for (var i = 0; i < frame.length; i++) {
  33015. frame[i] > max && (max = frame[i]);
  33016. }
  33017. // NaN will cause illegal axis extent.
  33018. return isFinite(max) ? max : NaN;
  33019. },
  33020. min: function (frame) {
  33021. var min = Infinity;
  33022. for (var i = 0; i < frame.length; i++) {
  33023. frame[i] < min && (min = frame[i]);
  33024. }
  33025. // NaN will cause illegal axis extent.
  33026. return isFinite(min) ? min : NaN;
  33027. },
  33028. // TODO
  33029. // Median
  33030. nearest: function (frame) {
  33031. return frame[0];
  33032. }
  33033. };
  33034. var indexSampler = function (frame, value) {
  33035. return Math.round(frame.length / 2);
  33036. };
  33037. var dataSample = function (seriesType) {
  33038. return {
  33039. seriesType: seriesType,
  33040. modifyOutputEnd: true,
  33041. reset: function (seriesModel, ecModel, api) {
  33042. var data = seriesModel.getData();
  33043. var sampling = seriesModel.get('sampling');
  33044. var coordSys = seriesModel.coordinateSystem;
  33045. // Only cartesian2d support down sampling
  33046. if (coordSys.type === 'cartesian2d' && sampling) {
  33047. var baseAxis = coordSys.getBaseAxis();
  33048. var valueAxis = coordSys.getOtherAxis(baseAxis);
  33049. var extent = baseAxis.getExtent();
  33050. // Coordinste system has been resized
  33051. var size = extent[1] - extent[0];
  33052. var rate = Math.round(data.count() / size);
  33053. if (rate > 1) {
  33054. var sampler;
  33055. if (typeof sampling === 'string') {
  33056. sampler = samplers[sampling];
  33057. }
  33058. else if (typeof sampling === 'function') {
  33059. sampler = sampling;
  33060. }
  33061. if (sampler) {
  33062. // Only support sample the first dim mapped from value axis.
  33063. seriesModel.setData(data.downSample(
  33064. data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler
  33065. ));
  33066. }
  33067. }
  33068. }
  33069. }
  33070. };
  33071. };
  33072. /*
  33073. * Licensed to the Apache Software Foundation (ASF) under one
  33074. * or more contributor license agreements. See the NOTICE file
  33075. * distributed with this work for additional information
  33076. * regarding copyright ownership. The ASF licenses this file
  33077. * to you under the Apache License, Version 2.0 (the
  33078. * "License"); you may not use this file except in compliance
  33079. * with the License. You may obtain a copy of the License at
  33080. *
  33081. * http://www.apache.org/licenses/LICENSE-2.0
  33082. *
  33083. * Unless required by applicable law or agreed to in writing,
  33084. * software distributed under the License is distributed on an
  33085. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33086. * KIND, either express or implied. See the License for the
  33087. * specific language governing permissions and limitations
  33088. * under the License.
  33089. */
  33090. /**
  33091. * Cartesian coordinate system
  33092. * @module echarts/coord/Cartesian
  33093. *
  33094. */
  33095. function dimAxisMapper(dim) {
  33096. return this._axes[dim];
  33097. }
  33098. /**
  33099. * @alias module:echarts/coord/Cartesian
  33100. * @constructor
  33101. */
  33102. var Cartesian = function (name) {
  33103. this._axes = {};
  33104. this._dimList = [];
  33105. /**
  33106. * @type {string}
  33107. */
  33108. this.name = name || '';
  33109. };
  33110. Cartesian.prototype = {
  33111. constructor: Cartesian,
  33112. type: 'cartesian',
  33113. /**
  33114. * Get axis
  33115. * @param {number|string} dim
  33116. * @return {module:echarts/coord/Cartesian~Axis}
  33117. */
  33118. getAxis: function (dim) {
  33119. return this._axes[dim];
  33120. },
  33121. /**
  33122. * Get axes list
  33123. * @return {Array.<module:echarts/coord/Cartesian~Axis>}
  33124. */
  33125. getAxes: function () {
  33126. return map(this._dimList, dimAxisMapper, this);
  33127. },
  33128. /**
  33129. * Get axes list by given scale type
  33130. */
  33131. getAxesByScale: function (scaleType) {
  33132. scaleType = scaleType.toLowerCase();
  33133. return filter(
  33134. this.getAxes(),
  33135. function (axis) {
  33136. return axis.scale.type === scaleType;
  33137. }
  33138. );
  33139. },
  33140. /**
  33141. * Add axis
  33142. * @param {module:echarts/coord/Cartesian.Axis}
  33143. */
  33144. addAxis: function (axis) {
  33145. var dim = axis.dim;
  33146. this._axes[dim] = axis;
  33147. this._dimList.push(dim);
  33148. },
  33149. /**
  33150. * Convert data to coord in nd space
  33151. * @param {Array.<number>|Object.<string, number>} val
  33152. * @return {Array.<number>|Object.<string, number>}
  33153. */
  33154. dataToCoord: function (val) {
  33155. return this._dataCoordConvert(val, 'dataToCoord');
  33156. },
  33157. /**
  33158. * Convert coord in nd space to data
  33159. * @param {Array.<number>|Object.<string, number>} val
  33160. * @return {Array.<number>|Object.<string, number>}
  33161. */
  33162. coordToData: function (val) {
  33163. return this._dataCoordConvert(val, 'coordToData');
  33164. },
  33165. _dataCoordConvert: function (input, method) {
  33166. var dimList = this._dimList;
  33167. var output = input instanceof Array ? [] : {};
  33168. for (var i = 0; i < dimList.length; i++) {
  33169. var dim = dimList[i];
  33170. var axis = this._axes[dim];
  33171. output[dim] = axis[method](input[dim]);
  33172. }
  33173. return output;
  33174. }
  33175. };
  33176. /*
  33177. * Licensed to the Apache Software Foundation (ASF) under one
  33178. * or more contributor license agreements. See the NOTICE file
  33179. * distributed with this work for additional information
  33180. * regarding copyright ownership. The ASF licenses this file
  33181. * to you under the Apache License, Version 2.0 (the
  33182. * "License"); you may not use this file except in compliance
  33183. * with the License. You may obtain a copy of the License at
  33184. *
  33185. * http://www.apache.org/licenses/LICENSE-2.0
  33186. *
  33187. * Unless required by applicable law or agreed to in writing,
  33188. * software distributed under the License is distributed on an
  33189. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33190. * KIND, either express or implied. See the License for the
  33191. * specific language governing permissions and limitations
  33192. * under the License.
  33193. */
  33194. function Cartesian2D(name) {
  33195. Cartesian.call(this, name);
  33196. }
  33197. Cartesian2D.prototype = {
  33198. constructor: Cartesian2D,
  33199. type: 'cartesian2d',
  33200. /**
  33201. * @type {Array.<string>}
  33202. * @readOnly
  33203. */
  33204. dimensions: ['x', 'y'],
  33205. /**
  33206. * Base axis will be used on stacking.
  33207. *
  33208. * @return {module:echarts/coord/cartesian/Axis2D}
  33209. */
  33210. getBaseAxis: function () {
  33211. return this.getAxesByScale('ordinal')[0]
  33212. || this.getAxesByScale('time')[0]
  33213. || this.getAxis('x');
  33214. },
  33215. /**
  33216. * If contain point
  33217. * @param {Array.<number>} point
  33218. * @return {boolean}
  33219. */
  33220. containPoint: function (point) {
  33221. var axisX = this.getAxis('x');
  33222. var axisY = this.getAxis('y');
  33223. return axisX.contain(axisX.toLocalCoord(point[0]))
  33224. && axisY.contain(axisY.toLocalCoord(point[1]));
  33225. },
  33226. /**
  33227. * If contain data
  33228. * @param {Array.<number>} data
  33229. * @return {boolean}
  33230. */
  33231. containData: function (data) {
  33232. return this.getAxis('x').containData(data[0])
  33233. && this.getAxis('y').containData(data[1]);
  33234. },
  33235. /**
  33236. * @param {Array.<number>} data
  33237. * @param {Array.<number>} out
  33238. * @return {Array.<number>}
  33239. */
  33240. dataToPoint: function (data, reserved, out) {
  33241. var xAxis = this.getAxis('x');
  33242. var yAxis = this.getAxis('y');
  33243. out = out || [];
  33244. out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(data[0]));
  33245. out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(data[1]));
  33246. return out;
  33247. },
  33248. /**
  33249. * @param {Array.<number>} data
  33250. * @param {Array.<number>} out
  33251. * @return {Array.<number>}
  33252. */
  33253. clampData: function (data, out) {
  33254. var xScale = this.getAxis('x').scale;
  33255. var yScale = this.getAxis('y').scale;
  33256. var xAxisExtent = xScale.getExtent();
  33257. var yAxisExtent = yScale.getExtent();
  33258. var x = xScale.parse(data[0]);
  33259. var y = yScale.parse(data[1]);
  33260. out = out || [];
  33261. out[0] = Math.min(
  33262. Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x),
  33263. Math.max(xAxisExtent[0], xAxisExtent[1])
  33264. );
  33265. out[1] = Math.min(
  33266. Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y),
  33267. Math.max(yAxisExtent[0], yAxisExtent[1])
  33268. );
  33269. return out;
  33270. },
  33271. /**
  33272. * @param {Array.<number>} point
  33273. * @param {Array.<number>} out
  33274. * @return {Array.<number>}
  33275. */
  33276. pointToData: function (point, out) {
  33277. var xAxis = this.getAxis('x');
  33278. var yAxis = this.getAxis('y');
  33279. out = out || [];
  33280. out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]));
  33281. out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]));
  33282. return out;
  33283. },
  33284. /**
  33285. * Get other axis
  33286. * @param {module:echarts/coord/cartesian/Axis2D} axis
  33287. */
  33288. getOtherAxis: function (axis) {
  33289. return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
  33290. }
  33291. };
  33292. inherits(Cartesian2D, Cartesian);
  33293. /*
  33294. * Licensed to the Apache Software Foundation (ASF) under one
  33295. * or more contributor license agreements. See the NOTICE file
  33296. * distributed with this work for additional information
  33297. * regarding copyright ownership. The ASF licenses this file
  33298. * to you under the Apache License, Version 2.0 (the
  33299. * "License"); you may not use this file except in compliance
  33300. * with the License. You may obtain a copy of the License at
  33301. *
  33302. * http://www.apache.org/licenses/LICENSE-2.0
  33303. *
  33304. * Unless required by applicable law or agreed to in writing,
  33305. * software distributed under the License is distributed on an
  33306. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33307. * KIND, either express or implied. See the License for the
  33308. * specific language governing permissions and limitations
  33309. * under the License.
  33310. */
  33311. /**
  33312. * Extend axis 2d
  33313. * @constructor module:echarts/coord/cartesian/Axis2D
  33314. * @extends {module:echarts/coord/cartesian/Axis}
  33315. * @param {string} dim
  33316. * @param {*} scale
  33317. * @param {Array.<number>} coordExtent
  33318. * @param {string} axisType
  33319. * @param {string} position
  33320. */
  33321. var Axis2D = function (dim, scale, coordExtent, axisType, position) {
  33322. Axis.call(this, dim, scale, coordExtent);
  33323. /**
  33324. * Axis type
  33325. * - 'category'
  33326. * - 'value'
  33327. * - 'time'
  33328. * - 'log'
  33329. * @type {string}
  33330. */
  33331. this.type = axisType || 'value';
  33332. /**
  33333. * Axis position
  33334. * - 'top'
  33335. * - 'bottom'
  33336. * - 'left'
  33337. * - 'right'
  33338. */
  33339. this.position = position || 'bottom';
  33340. };
  33341. Axis2D.prototype = {
  33342. constructor: Axis2D,
  33343. /**
  33344. * Index of axis, can be used as key
  33345. */
  33346. index: 0,
  33347. /**
  33348. * Implemented in <module:echarts/coord/cartesian/Grid>.
  33349. * @return {Array.<module:echarts/coord/cartesian/Axis2D>}
  33350. * If not on zero of other axis, return null/undefined.
  33351. * If no axes, return an empty array.
  33352. */
  33353. getAxesOnZeroOf: null,
  33354. /**
  33355. * Axis model
  33356. * @param {module:echarts/coord/cartesian/AxisModel}
  33357. */
  33358. model: null,
  33359. isHorizontal: function () {
  33360. var position = this.position;
  33361. return position === 'top' || position === 'bottom';
  33362. },
  33363. /**
  33364. * Each item cooresponds to this.getExtent(), which
  33365. * means globalExtent[0] may greater than globalExtent[1],
  33366. * unless `asc` is input.
  33367. *
  33368. * @param {boolean} [asc]
  33369. * @return {Array.<number>}
  33370. */
  33371. getGlobalExtent: function (asc) {
  33372. var ret = this.getExtent();
  33373. ret[0] = this.toGlobalCoord(ret[0]);
  33374. ret[1] = this.toGlobalCoord(ret[1]);
  33375. asc && ret[0] > ret[1] && ret.reverse();
  33376. return ret;
  33377. },
  33378. getOtherAxis: function () {
  33379. this.grid.getOtherAxis();
  33380. },
  33381. /**
  33382. * @override
  33383. */
  33384. pointToData: function (point, clamp) {
  33385. return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
  33386. },
  33387. /**
  33388. * Transform global coord to local coord,
  33389. * i.e. var localCoord = axis.toLocalCoord(80);
  33390. * designate by module:echarts/coord/cartesian/Grid.
  33391. * @type {Function}
  33392. */
  33393. toLocalCoord: null,
  33394. /**
  33395. * Transform global coord to local coord,
  33396. * i.e. var globalCoord = axis.toLocalCoord(40);
  33397. * designate by module:echarts/coord/cartesian/Grid.
  33398. * @type {Function}
  33399. */
  33400. toGlobalCoord: null
  33401. };
  33402. inherits(Axis2D, Axis);
  33403. /*
  33404. * Licensed to the Apache Software Foundation (ASF) under one
  33405. * or more contributor license agreements. See the NOTICE file
  33406. * distributed with this work for additional information
  33407. * regarding copyright ownership. The ASF licenses this file
  33408. * to you under the Apache License, Version 2.0 (the
  33409. * "License"); you may not use this file except in compliance
  33410. * with the License. You may obtain a copy of the License at
  33411. *
  33412. * http://www.apache.org/licenses/LICENSE-2.0
  33413. *
  33414. * Unless required by applicable law or agreed to in writing,
  33415. * software distributed under the License is distributed on an
  33416. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33417. * KIND, either express or implied. See the License for the
  33418. * specific language governing permissions and limitations
  33419. * under the License.
  33420. */
  33421. var defaultOption = {
  33422. show: true,
  33423. zlevel: 0,
  33424. z: 0,
  33425. // Inverse the axis.
  33426. inverse: false,
  33427. // Axis name displayed.
  33428. name: '',
  33429. // 'start' | 'middle' | 'end'
  33430. nameLocation: 'end',
  33431. // By degree. By defualt auto rotate by nameLocation.
  33432. nameRotate: null,
  33433. nameTruncate: {
  33434. maxWidth: null,
  33435. ellipsis: '...',
  33436. placeholder: '.'
  33437. },
  33438. // Use global text style by default.
  33439. nameTextStyle: {},
  33440. // The gap between axisName and axisLine.
  33441. nameGap: 15,
  33442. // Default `false` to support tooltip.
  33443. silent: false,
  33444. // Default `false` to avoid legacy user event listener fail.
  33445. triggerEvent: false,
  33446. tooltip: {
  33447. show: false
  33448. },
  33449. axisPointer: {},
  33450. axisLine: {
  33451. show: true,
  33452. onZero: true,
  33453. onZeroAxisIndex: null,
  33454. lineStyle: {
  33455. color: '#333',
  33456. width: 1,
  33457. type: 'solid'
  33458. },
  33459. // The arrow at both ends the the axis.
  33460. symbol: ['none', 'none'],
  33461. symbolSize: [10, 15]
  33462. },
  33463. axisTick: {
  33464. show: true,
  33465. // Whether axisTick is inside the grid or outside the grid.
  33466. inside: false,
  33467. // The length of axisTick.
  33468. length: 5,
  33469. lineStyle: {
  33470. width: 1
  33471. }
  33472. },
  33473. axisLabel: {
  33474. show: true,
  33475. // Whether axisLabel is inside the grid or outside the grid.
  33476. inside: false,
  33477. rotate: 0,
  33478. // true | false | null/undefined (auto)
  33479. showMinLabel: null,
  33480. // true | false | null/undefined (auto)
  33481. showMaxLabel: null,
  33482. margin: 8,
  33483. // formatter: null,
  33484. fontSize: 12
  33485. },
  33486. splitLine: {
  33487. show: true,
  33488. lineStyle: {
  33489. color: ['#ccc'],
  33490. width: 1,
  33491. type: 'solid'
  33492. }
  33493. },
  33494. splitArea: {
  33495. show: false,
  33496. areaStyle: {
  33497. color: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)']
  33498. }
  33499. }
  33500. };
  33501. var axisDefault = {};
  33502. axisDefault.categoryAxis = merge({
  33503. // The gap at both ends of the axis. For categoryAxis, boolean.
  33504. boundaryGap: true,
  33505. // Set false to faster category collection.
  33506. // Only usefull in the case like: category is
  33507. // ['2012-01-01', '2012-01-02', ...], where the input
  33508. // data has been ensured not duplicate and is large data.
  33509. // null means "auto":
  33510. // if axis.data provided, do not deduplication,
  33511. // else do deduplication.
  33512. deduplication: null,
  33513. // splitArea: {
  33514. // show: false
  33515. // },
  33516. splitLine: {
  33517. show: false
  33518. },
  33519. axisTick: {
  33520. // If tick is align with label when boundaryGap is true
  33521. alignWithLabel: false,
  33522. interval: 'auto'
  33523. },
  33524. axisLabel: {
  33525. interval: 'auto'
  33526. }
  33527. }, defaultOption);
  33528. axisDefault.valueAxis = merge({
  33529. // The gap at both ends of the axis. For value axis, [GAP, GAP], where
  33530. // `GAP` can be an absolute pixel number (like `35`), or percent (like `'30%'`)
  33531. boundaryGap: [0, 0],
  33532. // TODO
  33533. // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]
  33534. // Min value of the axis. can be:
  33535. // + a number
  33536. // + 'dataMin': use the min value in data.
  33537. // + null/undefined: auto decide min value (consider pretty look and boundaryGap).
  33538. // min: null,
  33539. // Max value of the axis. can be:
  33540. // + a number
  33541. // + 'dataMax': use the max value in data.
  33542. // + null/undefined: auto decide max value (consider pretty look and boundaryGap).
  33543. // max: null,
  33544. // Readonly prop, specifies start value of the range when using data zoom.
  33545. // rangeStart: null
  33546. // Readonly prop, specifies end value of the range when using data zoom.
  33547. // rangeEnd: null
  33548. // Optional value can be:
  33549. // + `false`: always include value 0.
  33550. // + `true`: the extent do not consider value 0.
  33551. // scale: false,
  33552. // AxisTick and axisLabel and splitLine are caculated based on splitNumber.
  33553. splitNumber: 5
  33554. // Interval specifies the span of the ticks is mandatorily.
  33555. // interval: null
  33556. // Specify min interval when auto calculate tick interval.
  33557. // minInterval: null
  33558. // Specify max interval when auto calculate tick interval.
  33559. // maxInterval: null
  33560. }, defaultOption);
  33561. axisDefault.timeAxis = defaults({
  33562. scale: true,
  33563. min: 'dataMin',
  33564. max: 'dataMax'
  33565. }, axisDefault.valueAxis);
  33566. axisDefault.logAxis = defaults({
  33567. scale: true,
  33568. logBase: 10
  33569. }, axisDefault.valueAxis);
  33570. /*
  33571. * Licensed to the Apache Software Foundation (ASF) under one
  33572. * or more contributor license agreements. See the NOTICE file
  33573. * distributed with this work for additional information
  33574. * regarding copyright ownership. The ASF licenses this file
  33575. * to you under the Apache License, Version 2.0 (the
  33576. * "License"); you may not use this file except in compliance
  33577. * with the License. You may obtain a copy of the License at
  33578. *
  33579. * http://www.apache.org/licenses/LICENSE-2.0
  33580. *
  33581. * Unless required by applicable law or agreed to in writing,
  33582. * software distributed under the License is distributed on an
  33583. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33584. * KIND, either express or implied. See the License for the
  33585. * specific language governing permissions and limitations
  33586. * under the License.
  33587. */
  33588. // FIXME axisType is fixed ?
  33589. var AXIS_TYPES = ['value', 'category', 'time', 'log'];
  33590. /**
  33591. * Generate sub axis model class
  33592. * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
  33593. * @param {module:echarts/model/Component} BaseAxisModelClass
  33594. * @param {Function} axisTypeDefaulter
  33595. * @param {Object} [extraDefaultOption]
  33596. */
  33597. var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
  33598. each$1(AXIS_TYPES, function (axisType) {
  33599. BaseAxisModelClass.extend({
  33600. /**
  33601. * @readOnly
  33602. */
  33603. type: axisName + 'Axis.' + axisType,
  33604. mergeDefaultAndTheme: function (option, ecModel) {
  33605. var layoutMode = this.layoutMode;
  33606. var inputPositionParams = layoutMode
  33607. ? getLayoutParams(option) : {};
  33608. var themeModel = ecModel.getTheme();
  33609. merge(option, themeModel.get(axisType + 'Axis'));
  33610. merge(option, this.getDefaultOption());
  33611. option.type = axisTypeDefaulter(axisName, option);
  33612. if (layoutMode) {
  33613. mergeLayoutParam(option, inputPositionParams, layoutMode);
  33614. }
  33615. },
  33616. /**
  33617. * @override
  33618. */
  33619. optionUpdated: function () {
  33620. var thisOption = this.option;
  33621. if (thisOption.type === 'category') {
  33622. this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);
  33623. }
  33624. },
  33625. /**
  33626. * Should not be called before all of 'getInitailData' finished.
  33627. * Because categories are collected during initializing data.
  33628. */
  33629. getCategories: function (rawData) {
  33630. var option = this.option;
  33631. // FIXME
  33632. // warning if called before all of 'getInitailData' finished.
  33633. if (option.type === 'category') {
  33634. if (rawData) {
  33635. return option.data;
  33636. }
  33637. return this.__ordinalMeta.categories;
  33638. }
  33639. },
  33640. getOrdinalMeta: function () {
  33641. return this.__ordinalMeta;
  33642. },
  33643. defaultOption: mergeAll(
  33644. [
  33645. {},
  33646. axisDefault[axisType + 'Axis'],
  33647. extraDefaultOption
  33648. ],
  33649. true
  33650. )
  33651. });
  33652. });
  33653. ComponentModel.registerSubTypeDefaulter(
  33654. axisName + 'Axis',
  33655. curry(axisTypeDefaulter, axisName)
  33656. );
  33657. };
  33658. /*
  33659. * Licensed to the Apache Software Foundation (ASF) under one
  33660. * or more contributor license agreements. See the NOTICE file
  33661. * distributed with this work for additional information
  33662. * regarding copyright ownership. The ASF licenses this file
  33663. * to you under the Apache License, Version 2.0 (the
  33664. * "License"); you may not use this file except in compliance
  33665. * with the License. You may obtain a copy of the License at
  33666. *
  33667. * http://www.apache.org/licenses/LICENSE-2.0
  33668. *
  33669. * Unless required by applicable law or agreed to in writing,
  33670. * software distributed under the License is distributed on an
  33671. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33672. * KIND, either express or implied. See the License for the
  33673. * specific language governing permissions and limitations
  33674. * under the License.
  33675. */
  33676. var AxisModel = ComponentModel.extend({
  33677. type: 'cartesian2dAxis',
  33678. /**
  33679. * @type {module:echarts/coord/cartesian/Axis2D}
  33680. */
  33681. axis: null,
  33682. /**
  33683. * @override
  33684. */
  33685. init: function () {
  33686. AxisModel.superApply(this, 'init', arguments);
  33687. this.resetRange();
  33688. },
  33689. /**
  33690. * @override
  33691. */
  33692. mergeOption: function () {
  33693. AxisModel.superApply(this, 'mergeOption', arguments);
  33694. this.resetRange();
  33695. },
  33696. /**
  33697. * @override
  33698. */
  33699. restoreData: function () {
  33700. AxisModel.superApply(this, 'restoreData', arguments);
  33701. this.resetRange();
  33702. },
  33703. /**
  33704. * @override
  33705. * @return {module:echarts/model/Component}
  33706. */
  33707. getCoordSysModel: function () {
  33708. return this.ecModel.queryComponents({
  33709. mainType: 'grid',
  33710. index: this.option.gridIndex,
  33711. id: this.option.gridId
  33712. })[0];
  33713. }
  33714. });
  33715. function getAxisType(axisDim, option) {
  33716. // Default axis with data is category axis
  33717. return option.type || (option.data ? 'category' : 'value');
  33718. }
  33719. merge(AxisModel.prototype, axisModelCommonMixin);
  33720. var extraOption = {
  33721. // gridIndex: 0,
  33722. // gridId: '',
  33723. // Offset is for multiple axis on the same position
  33724. offset: 0
  33725. };
  33726. axisModelCreator('x', AxisModel, getAxisType, extraOption);
  33727. axisModelCreator('y', AxisModel, getAxisType, extraOption);
  33728. /*
  33729. * Licensed to the Apache Software Foundation (ASF) under one
  33730. * or more contributor license agreements. See the NOTICE file
  33731. * distributed with this work for additional information
  33732. * regarding copyright ownership. The ASF licenses this file
  33733. * to you under the Apache License, Version 2.0 (the
  33734. * "License"); you may not use this file except in compliance
  33735. * with the License. You may obtain a copy of the License at
  33736. *
  33737. * http://www.apache.org/licenses/LICENSE-2.0
  33738. *
  33739. * Unless required by applicable law or agreed to in writing,
  33740. * software distributed under the License is distributed on an
  33741. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33742. * KIND, either express or implied. See the License for the
  33743. * specific language governing permissions and limitations
  33744. * under the License.
  33745. */
  33746. // Grid 是在有直角坐标系的时候必须要存在的
  33747. // 所以这里也要被 Cartesian2D 依赖
  33748. ComponentModel.extend({
  33749. type: 'grid',
  33750. dependencies: ['xAxis', 'yAxis'],
  33751. layoutMode: 'box',
  33752. /**
  33753. * @type {module:echarts/coord/cartesian/Grid}
  33754. */
  33755. coordinateSystem: null,
  33756. defaultOption: {
  33757. show: false,
  33758. zlevel: 0,
  33759. z: 0,
  33760. left: '10%',
  33761. top: 60,
  33762. right: '10%',
  33763. bottom: 60,
  33764. // If grid size contain label
  33765. containLabel: false,
  33766. // width: {totalWidth} - left - right,
  33767. // height: {totalHeight} - top - bottom,
  33768. backgroundColor: 'rgba(0,0,0,0)',
  33769. borderWidth: 1,
  33770. borderColor: '#ccc'
  33771. }
  33772. });
  33773. /*
  33774. * Licensed to the Apache Software Foundation (ASF) under one
  33775. * or more contributor license agreements. See the NOTICE file
  33776. * distributed with this work for additional information
  33777. * regarding copyright ownership. The ASF licenses this file
  33778. * to you under the Apache License, Version 2.0 (the
  33779. * "License"); you may not use this file except in compliance
  33780. * with the License. You may obtain a copy of the License at
  33781. *
  33782. * http://www.apache.org/licenses/LICENSE-2.0
  33783. *
  33784. * Unless required by applicable law or agreed to in writing,
  33785. * software distributed under the License is distributed on an
  33786. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33787. * KIND, either express or implied. See the License for the
  33788. * specific language governing permissions and limitations
  33789. * under the License.
  33790. */
  33791. /**
  33792. * Grid is a region which contains at most 4 cartesian systems
  33793. *
  33794. * TODO Default cartesian
  33795. */
  33796. // Depends on GridModel, AxisModel, which performs preprocess.
  33797. /**
  33798. * Check if the axis is used in the specified grid
  33799. * @inner
  33800. */
  33801. function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
  33802. return axisModel.getCoordSysModel() === gridModel;
  33803. }
  33804. function Grid(gridModel, ecModel, api) {
  33805. /**
  33806. * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
  33807. * @private
  33808. */
  33809. this._coordsMap = {};
  33810. /**
  33811. * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
  33812. * @private
  33813. */
  33814. this._coordsList = [];
  33815. /**
  33816. * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
  33817. * @private
  33818. */
  33819. this._axesMap = {};
  33820. /**
  33821. * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
  33822. * @private
  33823. */
  33824. this._axesList = [];
  33825. this._initCartesian(gridModel, ecModel, api);
  33826. this.model = gridModel;
  33827. }
  33828. var gridProto = Grid.prototype;
  33829. gridProto.type = 'grid';
  33830. gridProto.axisPointerEnabled = true;
  33831. gridProto.getRect = function () {
  33832. return this._rect;
  33833. };
  33834. gridProto.update = function (ecModel, api) {
  33835. var axesMap = this._axesMap;
  33836. this._updateScale(ecModel, this.model);
  33837. each$1(axesMap.x, function (xAxis) {
  33838. niceScaleExtent(xAxis.scale, xAxis.model);
  33839. });
  33840. each$1(axesMap.y, function (yAxis) {
  33841. niceScaleExtent(yAxis.scale, yAxis.model);
  33842. });
  33843. // Key: axisDim_axisIndex, value: boolean, whether onZero target.
  33844. var onZeroRecords = {};
  33845. each$1(axesMap.x, function (xAxis) {
  33846. fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);
  33847. });
  33848. each$1(axesMap.y, function (yAxis) {
  33849. fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);
  33850. });
  33851. // Resize again if containLabel is enabled
  33852. // FIXME It may cause getting wrong grid size in data processing stage
  33853. this.resize(this.model, api);
  33854. };
  33855. function fixAxisOnZero(axesMap, otherAxisDim, axis, onZeroRecords) {
  33856. axis.getAxesOnZeroOf = function () {
  33857. // TODO: onZero of multiple axes.
  33858. return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];
  33859. };
  33860. // onZero can not be enabled in these two situations:
  33861. // 1. When any other axis is a category axis.
  33862. // 2. When no axis is cross 0 point.
  33863. var otherAxes = axesMap[otherAxisDim];
  33864. var otherAxisOnZeroOf;
  33865. var axisModel = axis.model;
  33866. var onZero = axisModel.get('axisLine.onZero');
  33867. var onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex');
  33868. if (!onZero) {
  33869. return;
  33870. }
  33871. // If target axis is specified.
  33872. if (onZeroAxisIndex != null) {
  33873. if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {
  33874. otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];
  33875. }
  33876. }
  33877. else {
  33878. // Find the first available other axis.
  33879. for (var idx in otherAxes) {
  33880. if (otherAxes.hasOwnProperty(idx)
  33881. && canOnZeroToAxis(otherAxes[idx])
  33882. // Consider that two Y axes on one value axis,
  33883. // if both onZero, the two Y axes overlap.
  33884. && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]
  33885. ) {
  33886. otherAxisOnZeroOf = otherAxes[idx];
  33887. break;
  33888. }
  33889. }
  33890. }
  33891. if (otherAxisOnZeroOf) {
  33892. onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;
  33893. }
  33894. function getOnZeroRecordKey(axis) {
  33895. return axis.dim + '_' + axis.index;
  33896. }
  33897. }
  33898. function canOnZeroToAxis(axis) {
  33899. return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);
  33900. }
  33901. /**
  33902. * Resize the grid
  33903. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  33904. * @param {module:echarts/ExtensionAPI} api
  33905. */
  33906. gridProto.resize = function (gridModel, api, ignoreContainLabel) {
  33907. var gridRect = getLayoutRect(
  33908. gridModel.getBoxLayoutParams(), {
  33909. width: api.getWidth(),
  33910. height: api.getHeight()
  33911. });
  33912. this._rect = gridRect;
  33913. var axesList = this._axesList;
  33914. adjustAxes();
  33915. // Minus label size
  33916. if (!ignoreContainLabel && gridModel.get('containLabel')) {
  33917. each$1(axesList, function (axis) {
  33918. if (!axis.model.get('axisLabel.inside')) {
  33919. var labelUnionRect = estimateLabelUnionRect(axis);
  33920. if (labelUnionRect) {
  33921. var dim = axis.isHorizontal() ? 'height' : 'width';
  33922. var margin = axis.model.get('axisLabel.margin');
  33923. gridRect[dim] -= labelUnionRect[dim] + margin;
  33924. if (axis.position === 'top') {
  33925. gridRect.y += labelUnionRect.height + margin;
  33926. }
  33927. else if (axis.position === 'left') {
  33928. gridRect.x += labelUnionRect.width + margin;
  33929. }
  33930. }
  33931. }
  33932. });
  33933. adjustAxes();
  33934. }
  33935. function adjustAxes() {
  33936. each$1(axesList, function (axis) {
  33937. var isHorizontal = axis.isHorizontal();
  33938. var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
  33939. var idx = axis.inverse ? 1 : 0;
  33940. axis.setExtent(extent[idx], extent[1 - idx]);
  33941. updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);
  33942. });
  33943. }
  33944. };
  33945. /**
  33946. * @param {string} axisType
  33947. * @param {number} [axisIndex]
  33948. */
  33949. gridProto.getAxis = function (axisType, axisIndex) {
  33950. var axesMapOnDim = this._axesMap[axisType];
  33951. if (axesMapOnDim != null) {
  33952. if (axisIndex == null) {
  33953. // Find first axis
  33954. for (var name in axesMapOnDim) {
  33955. if (axesMapOnDim.hasOwnProperty(name)) {
  33956. return axesMapOnDim[name];
  33957. }
  33958. }
  33959. }
  33960. return axesMapOnDim[axisIndex];
  33961. }
  33962. };
  33963. /**
  33964. * @return {Array.<module:echarts/coord/Axis>}
  33965. */
  33966. gridProto.getAxes = function () {
  33967. return this._axesList.slice();
  33968. };
  33969. /**
  33970. * Usage:
  33971. * grid.getCartesian(xAxisIndex, yAxisIndex);
  33972. * grid.getCartesian(xAxisIndex);
  33973. * grid.getCartesian(null, yAxisIndex);
  33974. * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
  33975. *
  33976. * @param {number|Object} [xAxisIndex]
  33977. * @param {number} [yAxisIndex]
  33978. */
  33979. gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
  33980. if (xAxisIndex != null && yAxisIndex != null) {
  33981. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  33982. return this._coordsMap[key];
  33983. }
  33984. if (isObject$1(xAxisIndex)) {
  33985. yAxisIndex = xAxisIndex.yAxisIndex;
  33986. xAxisIndex = xAxisIndex.xAxisIndex;
  33987. }
  33988. // When only xAxisIndex or yAxisIndex given, find its first cartesian.
  33989. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  33990. if (coordList[i].getAxis('x').index === xAxisIndex
  33991. || coordList[i].getAxis('y').index === yAxisIndex
  33992. ) {
  33993. return coordList[i];
  33994. }
  33995. }
  33996. };
  33997. gridProto.getCartesians = function () {
  33998. return this._coordsList.slice();
  33999. };
  34000. /**
  34001. * @implements
  34002. * see {module:echarts/CoodinateSystem}
  34003. */
  34004. gridProto.convertToPixel = function (ecModel, finder, value) {
  34005. var target = this._findConvertTarget(ecModel, finder);
  34006. return target.cartesian
  34007. ? target.cartesian.dataToPoint(value)
  34008. : target.axis
  34009. ? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
  34010. : null;
  34011. };
  34012. /**
  34013. * @implements
  34014. * see {module:echarts/CoodinateSystem}
  34015. */
  34016. gridProto.convertFromPixel = function (ecModel, finder, value) {
  34017. var target = this._findConvertTarget(ecModel, finder);
  34018. return target.cartesian
  34019. ? target.cartesian.pointToData(value)
  34020. : target.axis
  34021. ? target.axis.coordToData(target.axis.toLocalCoord(value))
  34022. : null;
  34023. };
  34024. /**
  34025. * @inner
  34026. */
  34027. gridProto._findConvertTarget = function (ecModel, finder) {
  34028. var seriesModel = finder.seriesModel;
  34029. var xAxisModel = finder.xAxisModel
  34030. || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
  34031. var yAxisModel = finder.yAxisModel
  34032. || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);
  34033. var gridModel = finder.gridModel;
  34034. var coordsList = this._coordsList;
  34035. var cartesian;
  34036. var axis;
  34037. if (seriesModel) {
  34038. cartesian = seriesModel.coordinateSystem;
  34039. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  34040. }
  34041. else if (xAxisModel && yAxisModel) {
  34042. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  34043. }
  34044. else if (xAxisModel) {
  34045. axis = this.getAxis('x', xAxisModel.componentIndex);
  34046. }
  34047. else if (yAxisModel) {
  34048. axis = this.getAxis('y', yAxisModel.componentIndex);
  34049. }
  34050. // Lowest priority.
  34051. else if (gridModel) {
  34052. var grid = gridModel.coordinateSystem;
  34053. if (grid === this) {
  34054. cartesian = this._coordsList[0];
  34055. }
  34056. }
  34057. return {cartesian: cartesian, axis: axis};
  34058. };
  34059. /**
  34060. * @implements
  34061. * see {module:echarts/CoodinateSystem}
  34062. */
  34063. gridProto.containPoint = function (point) {
  34064. var coord = this._coordsList[0];
  34065. if (coord) {
  34066. return coord.containPoint(point);
  34067. }
  34068. };
  34069. /**
  34070. * Initialize cartesian coordinate systems
  34071. * @private
  34072. */
  34073. gridProto._initCartesian = function (gridModel, ecModel, api) {
  34074. var axisPositionUsed = {
  34075. left: false,
  34076. right: false,
  34077. top: false,
  34078. bottom: false
  34079. };
  34080. var axesMap = {
  34081. x: {},
  34082. y: {}
  34083. };
  34084. var axesCount = {
  34085. x: 0,
  34086. y: 0
  34087. };
  34088. /// Create axis
  34089. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  34090. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  34091. if (!axesCount.x || !axesCount.y) {
  34092. // Roll back when there no either x or y axis
  34093. this._axesMap = {};
  34094. this._axesList = [];
  34095. return;
  34096. }
  34097. this._axesMap = axesMap;
  34098. /// Create cartesian2d
  34099. each$1(axesMap.x, function (xAxis, xAxisIndex) {
  34100. each$1(axesMap.y, function (yAxis, yAxisIndex) {
  34101. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  34102. var cartesian = new Cartesian2D(key);
  34103. cartesian.grid = this;
  34104. cartesian.model = gridModel;
  34105. this._coordsMap[key] = cartesian;
  34106. this._coordsList.push(cartesian);
  34107. cartesian.addAxis(xAxis);
  34108. cartesian.addAxis(yAxis);
  34109. }, this);
  34110. }, this);
  34111. function createAxisCreator(axisType) {
  34112. return function (axisModel, idx) {
  34113. if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
  34114. return;
  34115. }
  34116. var axisPosition = axisModel.get('position');
  34117. if (axisType === 'x') {
  34118. // Fix position
  34119. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  34120. // Default bottom of X
  34121. axisPosition = 'bottom';
  34122. if (axisPositionUsed[axisPosition]) {
  34123. axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
  34124. }
  34125. }
  34126. }
  34127. else {
  34128. // Fix position
  34129. if (axisPosition !== 'left' && axisPosition !== 'right') {
  34130. // Default left of Y
  34131. axisPosition = 'left';
  34132. if (axisPositionUsed[axisPosition]) {
  34133. axisPosition = axisPosition === 'left' ? 'right' : 'left';
  34134. }
  34135. }
  34136. }
  34137. axisPositionUsed[axisPosition] = true;
  34138. var axis = new Axis2D(
  34139. axisType, createScaleByModel(axisModel),
  34140. [0, 0],
  34141. axisModel.get('type'),
  34142. axisPosition
  34143. );
  34144. var isCategory = axis.type === 'category';
  34145. axis.onBand = isCategory && axisModel.get('boundaryGap');
  34146. axis.inverse = axisModel.get('inverse');
  34147. // Inject axis into axisModel
  34148. axisModel.axis = axis;
  34149. // Inject axisModel into axis
  34150. axis.model = axisModel;
  34151. // Inject grid info axis
  34152. axis.grid = this;
  34153. // Index of axis, can be used as key
  34154. axis.index = idx;
  34155. this._axesList.push(axis);
  34156. axesMap[axisType][idx] = axis;
  34157. axesCount[axisType]++;
  34158. };
  34159. }
  34160. };
  34161. /**
  34162. * Update cartesian properties from series
  34163. * @param {module:echarts/model/Option} option
  34164. * @private
  34165. */
  34166. gridProto._updateScale = function (ecModel, gridModel) {
  34167. // Reset scale
  34168. each$1(this._axesList, function (axis) {
  34169. axis.scale.setExtent(Infinity, -Infinity);
  34170. });
  34171. ecModel.eachSeries(function (seriesModel) {
  34172. if (isCartesian2D(seriesModel)) {
  34173. var axesModels = findAxesModels(seriesModel, ecModel);
  34174. var xAxisModel = axesModels[0];
  34175. var yAxisModel = axesModels[1];
  34176. if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
  34177. || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
  34178. ) {
  34179. return;
  34180. }
  34181. var cartesian = this.getCartesian(
  34182. xAxisModel.componentIndex, yAxisModel.componentIndex
  34183. );
  34184. var data = seriesModel.getData();
  34185. var xAxis = cartesian.getAxis('x');
  34186. var yAxis = cartesian.getAxis('y');
  34187. if (data.type === 'list') {
  34188. unionExtent(data, xAxis, seriesModel);
  34189. unionExtent(data, yAxis, seriesModel);
  34190. }
  34191. }
  34192. }, this);
  34193. function unionExtent(data, axis, seriesModel) {
  34194. each$1(data.mapDimension(axis.dim, true), function (dim) {
  34195. axis.scale.unionExtentFromData(
  34196. // For example, the extent of the orginal dimension
  34197. // is [0.1, 0.5], the extent of the `stackResultDimension`
  34198. // is [7, 9], the final extent should not include [0.1, 0.5].
  34199. data, getStackedDimension(data, dim)
  34200. );
  34201. });
  34202. }
  34203. };
  34204. /**
  34205. * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
  34206. * @return {Object} {baseAxes: [], otherAxes: []}
  34207. */
  34208. gridProto.getTooltipAxes = function (dim) {
  34209. var baseAxes = [];
  34210. var otherAxes = [];
  34211. each$1(this.getCartesians(), function (cartesian) {
  34212. var baseAxis = (dim != null && dim !== 'auto')
  34213. ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  34214. var otherAxis = cartesian.getOtherAxis(baseAxis);
  34215. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  34216. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  34217. });
  34218. return {baseAxes: baseAxes, otherAxes: otherAxes};
  34219. };
  34220. /**
  34221. * @inner
  34222. */
  34223. function updateAxisTransform(axis, coordBase) {
  34224. var axisExtent = axis.getExtent();
  34225. var axisExtentSum = axisExtent[0] + axisExtent[1];
  34226. // Fast transform
  34227. axis.toGlobalCoord = axis.dim === 'x'
  34228. ? function (coord) {
  34229. return coord + coordBase;
  34230. }
  34231. : function (coord) {
  34232. return axisExtentSum - coord + coordBase;
  34233. };
  34234. axis.toLocalCoord = axis.dim === 'x'
  34235. ? function (coord) {
  34236. return coord - coordBase;
  34237. }
  34238. : function (coord) {
  34239. return axisExtentSum - coord + coordBase;
  34240. };
  34241. }
  34242. var axesTypes = ['xAxis', 'yAxis'];
  34243. /**
  34244. * @inner
  34245. */
  34246. function findAxesModels(seriesModel, ecModel) {
  34247. return map(axesTypes, function (axisType) {
  34248. var axisModel = seriesModel.getReferringComponents(axisType)[0];
  34249. if (__DEV__) {
  34250. if (!axisModel) {
  34251. throw new Error(axisType + ' "' + retrieve(
  34252. seriesModel.get(axisType + 'Index'),
  34253. seriesModel.get(axisType + 'Id'),
  34254. 0
  34255. ) + '" not found');
  34256. }
  34257. }
  34258. return axisModel;
  34259. });
  34260. }
  34261. /**
  34262. * @inner
  34263. */
  34264. function isCartesian2D(seriesModel) {
  34265. return seriesModel.get('coordinateSystem') === 'cartesian2d';
  34266. }
  34267. Grid.create = function (ecModel, api) {
  34268. var grids = [];
  34269. ecModel.eachComponent('grid', function (gridModel, idx) {
  34270. var grid = new Grid(gridModel, ecModel, api);
  34271. grid.name = 'grid_' + idx;
  34272. // dataSampling requires axis extent, so resize
  34273. // should be performed in create stage.
  34274. grid.resize(gridModel, api, true);
  34275. gridModel.coordinateSystem = grid;
  34276. grids.push(grid);
  34277. });
  34278. // Inject the coordinateSystems into seriesModel
  34279. ecModel.eachSeries(function (seriesModel) {
  34280. if (!isCartesian2D(seriesModel)) {
  34281. return;
  34282. }
  34283. var axesModels = findAxesModels(seriesModel, ecModel);
  34284. var xAxisModel = axesModels[0];
  34285. var yAxisModel = axesModels[1];
  34286. var gridModel = xAxisModel.getCoordSysModel();
  34287. if (__DEV__) {
  34288. if (!gridModel) {
  34289. throw new Error(
  34290. 'Grid "' + retrieve(
  34291. xAxisModel.get('gridIndex'),
  34292. xAxisModel.get('gridId'),
  34293. 0
  34294. ) + '" not found'
  34295. );
  34296. }
  34297. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  34298. throw new Error('xAxis and yAxis must use the same grid');
  34299. }
  34300. }
  34301. var grid = gridModel.coordinateSystem;
  34302. seriesModel.coordinateSystem = grid.getCartesian(
  34303. xAxisModel.componentIndex, yAxisModel.componentIndex
  34304. );
  34305. });
  34306. return grids;
  34307. };
  34308. // For deciding which dimensions to use when creating list data
  34309. Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
  34310. CoordinateSystemManager.register('cartesian2d', Grid);
  34311. /*
  34312. * Licensed to the Apache Software Foundation (ASF) under one
  34313. * or more contributor license agreements. See the NOTICE file
  34314. * distributed with this work for additional information
  34315. * regarding copyright ownership. The ASF licenses this file
  34316. * to you under the Apache License, Version 2.0 (the
  34317. * "License"); you may not use this file except in compliance
  34318. * with the License. You may obtain a copy of the License at
  34319. *
  34320. * http://www.apache.org/licenses/LICENSE-2.0
  34321. *
  34322. * Unless required by applicable law or agreed to in writing,
  34323. * software distributed under the License is distributed on an
  34324. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  34325. * KIND, either express or implied. See the License for the
  34326. * specific language governing permissions and limitations
  34327. * under the License.
  34328. */
  34329. var PI$2 = Math.PI;
  34330. function makeAxisEventDataBase(axisModel) {
  34331. var eventData = {
  34332. componentType: axisModel.mainType,
  34333. componentIndex: axisModel.componentIndex
  34334. };
  34335. eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
  34336. return eventData;
  34337. }
  34338. /**
  34339. * A final axis is translated and rotated from a "standard axis".
  34340. * So opt.position and opt.rotation is required.
  34341. *
  34342. * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
  34343. * for example: (0, 0) ------------> (0, 50)
  34344. *
  34345. * nameDirection or tickDirection or labelDirection is 1 means tick
  34346. * or label is below the standard axis, whereas is -1 means above
  34347. * the standard axis. labelOffset means offset between label and axis,
  34348. * which is useful when 'onZero', where axisLabel is in the grid and
  34349. * label in outside grid.
  34350. *
  34351. * Tips: like always,
  34352. * positive rotation represents anticlockwise, and negative rotation
  34353. * represents clockwise.
  34354. * The direction of position coordinate is the same as the direction
  34355. * of screen coordinate.
  34356. *
  34357. * Do not need to consider axis 'inverse', which is auto processed by
  34358. * axis extent.
  34359. *
  34360. * @param {module:zrender/container/Group} group
  34361. * @param {Object} axisModel
  34362. * @param {Object} opt Standard axis parameters.
  34363. * @param {Array.<number>} opt.position [x, y]
  34364. * @param {number} opt.rotation by radian
  34365. * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.
  34366. * @param {number} [opt.tickDirection=1] 1 or -1
  34367. * @param {number} [opt.labelDirection=1] 1 or -1
  34368. * @param {number} [opt.labelOffset=0] Usefull when onZero.
  34369. * @param {string} [opt.axisLabelShow] default get from axisModel.
  34370. * @param {string} [opt.axisName] default get from axisModel.
  34371. * @param {number} [opt.axisNameAvailableWidth]
  34372. * @param {number} [opt.labelRotate] by degree, default get from axisModel.
  34373. * @param {number} [opt.strokeContainThreshold] Default label interval when label
  34374. * @param {number} [opt.nameTruncateMaxWidth]
  34375. */
  34376. var AxisBuilder = function (axisModel, opt) {
  34377. /**
  34378. * @readOnly
  34379. */
  34380. this.opt = opt;
  34381. /**
  34382. * @readOnly
  34383. */
  34384. this.axisModel = axisModel;
  34385. // Default value
  34386. defaults(
  34387. opt,
  34388. {
  34389. labelOffset: 0,
  34390. nameDirection: 1,
  34391. tickDirection: 1,
  34392. labelDirection: 1,
  34393. silent: true
  34394. }
  34395. );
  34396. /**
  34397. * @readOnly
  34398. */
  34399. this.group = new Group();
  34400. // FIXME Not use a seperate text group?
  34401. var dumbGroup = new Group({
  34402. position: opt.position.slice(),
  34403. rotation: opt.rotation
  34404. });
  34405. // this.group.add(dumbGroup);
  34406. // this._dumbGroup = dumbGroup;
  34407. dumbGroup.updateTransform();
  34408. this._transform = dumbGroup.transform;
  34409. this._dumbGroup = dumbGroup;
  34410. };
  34411. AxisBuilder.prototype = {
  34412. constructor: AxisBuilder,
  34413. hasBuilder: function (name) {
  34414. return !!builders[name];
  34415. },
  34416. add: function (name) {
  34417. builders[name].call(this);
  34418. },
  34419. getGroup: function () {
  34420. return this.group;
  34421. }
  34422. };
  34423. var builders = {
  34424. /**
  34425. * @private
  34426. */
  34427. axisLine: function () {
  34428. var opt = this.opt;
  34429. var axisModel = this.axisModel;
  34430. if (!axisModel.get('axisLine.show')) {
  34431. return;
  34432. }
  34433. var extent = this.axisModel.axis.getExtent();
  34434. var matrix = this._transform;
  34435. var pt1 = [extent[0], 0];
  34436. var pt2 = [extent[1], 0];
  34437. if (matrix) {
  34438. applyTransform(pt1, pt1, matrix);
  34439. applyTransform(pt2, pt2, matrix);
  34440. }
  34441. var lineStyle = extend(
  34442. {
  34443. lineCap: 'round'
  34444. },
  34445. axisModel.getModel('axisLine.lineStyle').getLineStyle()
  34446. );
  34447. this.group.add(new Line(subPixelOptimizeLine({
  34448. // Id for animation
  34449. anid: 'line',
  34450. shape: {
  34451. x1: pt1[0],
  34452. y1: pt1[1],
  34453. x2: pt2[0],
  34454. y2: pt2[1]
  34455. },
  34456. style: lineStyle,
  34457. strokeContainThreshold: opt.strokeContainThreshold || 5,
  34458. silent: true,
  34459. z2: 1
  34460. })));
  34461. var arrows = axisModel.get('axisLine.symbol');
  34462. var arrowSize = axisModel.get('axisLine.symbolSize');
  34463. var arrowOffset = axisModel.get('axisLine.symbolOffset') || 0;
  34464. if (typeof arrowOffset === 'number') {
  34465. arrowOffset = [arrowOffset, arrowOffset];
  34466. }
  34467. if (arrows != null) {
  34468. if (typeof arrows === 'string') {
  34469. // Use the same arrow for start and end point
  34470. arrows = [arrows, arrows];
  34471. }
  34472. if (typeof arrowSize === 'string'
  34473. || typeof arrowSize === 'number'
  34474. ) {
  34475. // Use the same size for width and height
  34476. arrowSize = [arrowSize, arrowSize];
  34477. }
  34478. var symbolWidth = arrowSize[0];
  34479. var symbolHeight = arrowSize[1];
  34480. each$1([{
  34481. rotate: opt.rotation + Math.PI / 2,
  34482. offset: arrowOffset[0],
  34483. r: 0
  34484. }, {
  34485. rotate: opt.rotation - Math.PI / 2,
  34486. offset: arrowOffset[1],
  34487. r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0])
  34488. + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))
  34489. }], function (point, index) {
  34490. if (arrows[index] !== 'none' && arrows[index] != null) {
  34491. var symbol = createSymbol(
  34492. arrows[index],
  34493. -symbolWidth / 2,
  34494. -symbolHeight / 2,
  34495. symbolWidth,
  34496. symbolHeight,
  34497. lineStyle.stroke,
  34498. true
  34499. );
  34500. // Calculate arrow position with offset
  34501. var r = point.r + point.offset;
  34502. var pos = [
  34503. pt1[0] + r * Math.cos(opt.rotation),
  34504. pt1[1] - r * Math.sin(opt.rotation)
  34505. ];
  34506. symbol.attr({
  34507. rotation: point.rotate,
  34508. position: pos,
  34509. silent: true,
  34510. z2: 11
  34511. });
  34512. this.group.add(symbol);
  34513. }
  34514. }, this);
  34515. }
  34516. },
  34517. /**
  34518. * @private
  34519. */
  34520. axisTickLabel: function () {
  34521. var axisModel = this.axisModel;
  34522. var opt = this.opt;
  34523. var tickEls = buildAxisTick(this, axisModel, opt);
  34524. var labelEls = buildAxisLabel(this, axisModel, opt);
  34525. fixMinMaxLabelShow(axisModel, labelEls, tickEls);
  34526. },
  34527. /**
  34528. * @private
  34529. */
  34530. axisName: function () {
  34531. var opt = this.opt;
  34532. var axisModel = this.axisModel;
  34533. var name = retrieve(opt.axisName, axisModel.get('name'));
  34534. if (!name) {
  34535. return;
  34536. }
  34537. var nameLocation = axisModel.get('nameLocation');
  34538. var nameDirection = opt.nameDirection;
  34539. var textStyleModel = axisModel.getModel('nameTextStyle');
  34540. var gap = axisModel.get('nameGap') || 0;
  34541. var extent = this.axisModel.axis.getExtent();
  34542. var gapSignal = extent[0] > extent[1] ? -1 : 1;
  34543. var pos = [
  34544. nameLocation === 'start'
  34545. ? extent[0] - gapSignal * gap
  34546. : nameLocation === 'end'
  34547. ? extent[1] + gapSignal * gap
  34548. : (extent[0] + extent[1]) / 2, // 'middle'
  34549. // Reuse labelOffset.
  34550. isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0
  34551. ];
  34552. var labelLayout;
  34553. var nameRotation = axisModel.get('nameRotate');
  34554. if (nameRotation != null) {
  34555. nameRotation = nameRotation * PI$2 / 180; // To radian.
  34556. }
  34557. var axisNameAvailableWidth;
  34558. if (isNameLocationCenter(nameLocation)) {
  34559. labelLayout = innerTextLayout(
  34560. opt.rotation,
  34561. nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
  34562. nameDirection
  34563. );
  34564. }
  34565. else {
  34566. labelLayout = endTextLayout(
  34567. opt, nameLocation, nameRotation || 0, extent
  34568. );
  34569. axisNameAvailableWidth = opt.axisNameAvailableWidth;
  34570. if (axisNameAvailableWidth != null) {
  34571. axisNameAvailableWidth = Math.abs(
  34572. axisNameAvailableWidth / Math.sin(labelLayout.rotation)
  34573. );
  34574. !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
  34575. }
  34576. }
  34577. var textFont = textStyleModel.getFont();
  34578. var truncateOpt = axisModel.get('nameTruncate', true) || {};
  34579. var ellipsis = truncateOpt.ellipsis;
  34580. var maxWidth = retrieve(
  34581. opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
  34582. );
  34583. // FIXME
  34584. // truncate rich text? (consider performance)
  34585. var truncatedText = (ellipsis != null && maxWidth != null)
  34586. ? truncateText$1(
  34587. name, maxWidth, textFont, ellipsis,
  34588. {minChar: 2, placeholder: truncateOpt.placeholder}
  34589. )
  34590. : name;
  34591. var tooltipOpt = axisModel.get('tooltip', true);
  34592. var mainType = axisModel.mainType;
  34593. var formatterParams = {
  34594. componentType: mainType,
  34595. name: name,
  34596. $vars: ['name']
  34597. };
  34598. formatterParams[mainType + 'Index'] = axisModel.componentIndex;
  34599. var textEl = new Text({
  34600. // Id for animation
  34601. anid: 'name',
  34602. __fullText: name,
  34603. __truncatedText: truncatedText,
  34604. position: pos,
  34605. rotation: labelLayout.rotation,
  34606. silent: isSilent(axisModel),
  34607. z2: 1,
  34608. tooltip: (tooltipOpt && tooltipOpt.show)
  34609. ? extend({
  34610. content: name,
  34611. formatter: function () {
  34612. return name;
  34613. },
  34614. formatterParams: formatterParams
  34615. }, tooltipOpt)
  34616. : null
  34617. });
  34618. setTextStyle(textEl.style, textStyleModel, {
  34619. text: truncatedText,
  34620. textFont: textFont,
  34621. textFill: textStyleModel.getTextColor()
  34622. || axisModel.get('axisLine.lineStyle.color'),
  34623. textAlign: labelLayout.textAlign,
  34624. textVerticalAlign: labelLayout.textVerticalAlign
  34625. });
  34626. if (axisModel.get('triggerEvent')) {
  34627. textEl.eventData = makeAxisEventDataBase(axisModel);
  34628. textEl.eventData.targetType = 'axisName';
  34629. textEl.eventData.name = name;
  34630. }
  34631. // FIXME
  34632. this._dumbGroup.add(textEl);
  34633. textEl.updateTransform();
  34634. this.group.add(textEl);
  34635. textEl.decomposeTransform();
  34636. }
  34637. };
  34638. /**
  34639. * @public
  34640. * @static
  34641. * @param {Object} opt
  34642. * @param {number} axisRotation in radian
  34643. * @param {number} textRotation in radian
  34644. * @param {number} direction
  34645. * @return {Object} {
  34646. * rotation, // according to axis
  34647. * textAlign,
  34648. * textVerticalAlign
  34649. * }
  34650. */
  34651. var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
  34652. var rotationDiff = remRadian(textRotation - axisRotation);
  34653. var textAlign;
  34654. var textVerticalAlign;
  34655. if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.
  34656. textVerticalAlign = direction > 0 ? 'top' : 'bottom';
  34657. textAlign = 'center';
  34658. }
  34659. else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line.
  34660. textVerticalAlign = direction > 0 ? 'bottom' : 'top';
  34661. textAlign = 'center';
  34662. }
  34663. else {
  34664. textVerticalAlign = 'middle';
  34665. if (rotationDiff > 0 && rotationDiff < PI$2) {
  34666. textAlign = direction > 0 ? 'right' : 'left';
  34667. }
  34668. else {
  34669. textAlign = direction > 0 ? 'left' : 'right';
  34670. }
  34671. }
  34672. return {
  34673. rotation: rotationDiff,
  34674. textAlign: textAlign,
  34675. textVerticalAlign: textVerticalAlign
  34676. };
  34677. };
  34678. function endTextLayout(opt, textPosition, textRotate, extent) {
  34679. var rotationDiff = remRadian(textRotate - opt.rotation);
  34680. var textAlign;
  34681. var textVerticalAlign;
  34682. var inverse = extent[0] > extent[1];
  34683. var onLeft = (textPosition === 'start' && !inverse)
  34684. || (textPosition !== 'start' && inverse);
  34685. if (isRadianAroundZero(rotationDiff - PI$2 / 2)) {
  34686. textVerticalAlign = onLeft ? 'bottom' : 'top';
  34687. textAlign = 'center';
  34688. }
  34689. else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) {
  34690. textVerticalAlign = onLeft ? 'top' : 'bottom';
  34691. textAlign = 'center';
  34692. }
  34693. else {
  34694. textVerticalAlign = 'middle';
  34695. if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) {
  34696. textAlign = onLeft ? 'left' : 'right';
  34697. }
  34698. else {
  34699. textAlign = onLeft ? 'right' : 'left';
  34700. }
  34701. }
  34702. return {
  34703. rotation: rotationDiff,
  34704. textAlign: textAlign,
  34705. textVerticalAlign: textVerticalAlign
  34706. };
  34707. }
  34708. function isSilent(axisModel) {
  34709. var tooltipOpt = axisModel.get('tooltip');
  34710. return axisModel.get('silent')
  34711. // Consider mouse cursor, add these restrictions.
  34712. || !(
  34713. axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)
  34714. );
  34715. }
  34716. function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
  34717. if (shouldShowAllLabels(axisModel.axis)) {
  34718. return;
  34719. }
  34720. // If min or max are user set, we need to check
  34721. // If the tick on min(max) are overlap on their neighbour tick
  34722. // If they are overlapped, we need to hide the min(max) tick label
  34723. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  34724. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  34725. // FIXME
  34726. // Have not consider onBand yet, where tick els is more than label els.
  34727. labelEls = labelEls || [];
  34728. tickEls = tickEls || [];
  34729. var firstLabel = labelEls[0];
  34730. var nextLabel = labelEls[1];
  34731. var lastLabel = labelEls[labelEls.length - 1];
  34732. var prevLabel = labelEls[labelEls.length - 2];
  34733. var firstTick = tickEls[0];
  34734. var nextTick = tickEls[1];
  34735. var lastTick = tickEls[tickEls.length - 1];
  34736. var prevTick = tickEls[tickEls.length - 2];
  34737. if (showMinLabel === false) {
  34738. ignoreEl(firstLabel);
  34739. ignoreEl(firstTick);
  34740. }
  34741. else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
  34742. if (showMinLabel) {
  34743. ignoreEl(nextLabel);
  34744. ignoreEl(nextTick);
  34745. }
  34746. else {
  34747. ignoreEl(firstLabel);
  34748. ignoreEl(firstTick);
  34749. }
  34750. }
  34751. if (showMaxLabel === false) {
  34752. ignoreEl(lastLabel);
  34753. ignoreEl(lastTick);
  34754. }
  34755. else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
  34756. if (showMaxLabel) {
  34757. ignoreEl(prevLabel);
  34758. ignoreEl(prevTick);
  34759. }
  34760. else {
  34761. ignoreEl(lastLabel);
  34762. ignoreEl(lastTick);
  34763. }
  34764. }
  34765. }
  34766. function ignoreEl(el) {
  34767. el && (el.ignore = true);
  34768. }
  34769. function isTwoLabelOverlapped(current, next, labelLayout) {
  34770. // current and next has the same rotation.
  34771. var firstRect = current && current.getBoundingRect().clone();
  34772. var nextRect = next && next.getBoundingRect().clone();
  34773. if (!firstRect || !nextRect) {
  34774. return;
  34775. }
  34776. // When checking intersect of two rotated labels, we use mRotationBack
  34777. // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
  34778. var mRotationBack = identity([]);
  34779. rotate(mRotationBack, mRotationBack, -current.rotation);
  34780. firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));
  34781. nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));
  34782. return firstRect.intersect(nextRect);
  34783. }
  34784. function isNameLocationCenter(nameLocation) {
  34785. return nameLocation === 'middle' || nameLocation === 'center';
  34786. }
  34787. function buildAxisTick(axisBuilder, axisModel, opt) {
  34788. var axis = axisModel.axis;
  34789. if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
  34790. return;
  34791. }
  34792. var tickModel = axisModel.getModel('axisTick');
  34793. var lineStyleModel = tickModel.getModel('lineStyle');
  34794. var tickLen = tickModel.get('length');
  34795. var ticksCoords = axis.getTicksCoords();
  34796. var pt1 = [];
  34797. var pt2 = [];
  34798. var matrix = axisBuilder._transform;
  34799. var tickEls = [];
  34800. for (var i = 0; i < ticksCoords.length; i++) {
  34801. var tickCoord = ticksCoords[i].coord;
  34802. pt1[0] = tickCoord;
  34803. pt1[1] = 0;
  34804. pt2[0] = tickCoord;
  34805. pt2[1] = opt.tickDirection * tickLen;
  34806. if (matrix) {
  34807. applyTransform(pt1, pt1, matrix);
  34808. applyTransform(pt2, pt2, matrix);
  34809. }
  34810. // Tick line, Not use group transform to have better line draw
  34811. var tickEl = new Line(subPixelOptimizeLine({
  34812. // Id for animation
  34813. anid: 'tick_' + ticksCoords[i].tickValue,
  34814. shape: {
  34815. x1: pt1[0],
  34816. y1: pt1[1],
  34817. x2: pt2[0],
  34818. y2: pt2[1]
  34819. },
  34820. style: defaults(
  34821. lineStyleModel.getLineStyle(),
  34822. {
  34823. stroke: axisModel.get('axisLine.lineStyle.color')
  34824. }
  34825. ),
  34826. z2: 2,
  34827. silent: true
  34828. }));
  34829. axisBuilder.group.add(tickEl);
  34830. tickEls.push(tickEl);
  34831. }
  34832. return tickEls;
  34833. }
  34834. function buildAxisLabel(axisBuilder, axisModel, opt) {
  34835. var axis = axisModel.axis;
  34836. var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
  34837. if (!show || axis.scale.isBlank()) {
  34838. return;
  34839. }
  34840. var labelModel = axisModel.getModel('axisLabel');
  34841. var labelMargin = labelModel.get('margin');
  34842. var labels = axis.getViewLabels();
  34843. // Special label rotate.
  34844. var labelRotation = (
  34845. retrieve(opt.labelRotate, labelModel.get('rotate')) || 0
  34846. ) * PI$2 / 180;
  34847. var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
  34848. var rawCategoryData = axisModel.getCategories(true);
  34849. var labelEls = [];
  34850. var silent = isSilent(axisModel);
  34851. var triggerEvent = axisModel.get('triggerEvent');
  34852. each$1(labels, function (labelItem, index) {
  34853. var tickValue = labelItem.tickValue;
  34854. var formattedLabel = labelItem.formattedLabel;
  34855. var rawLabel = labelItem.rawLabel;
  34856. var itemLabelModel = labelModel;
  34857. if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) {
  34858. itemLabelModel = new Model(
  34859. rawCategoryData[tickValue].textStyle, labelModel, axisModel.ecModel
  34860. );
  34861. }
  34862. var textColor = itemLabelModel.getTextColor()
  34863. || axisModel.get('axisLine.lineStyle.color');
  34864. var tickCoord = axis.dataToCoord(tickValue);
  34865. var pos = [
  34866. tickCoord,
  34867. opt.labelOffset + opt.labelDirection * labelMargin
  34868. ];
  34869. var textEl = new Text({
  34870. // Id for animation
  34871. anid: 'label_' + tickValue,
  34872. position: pos,
  34873. rotation: labelLayout.rotation,
  34874. silent: silent,
  34875. z2: 10
  34876. });
  34877. setTextStyle(textEl.style, itemLabelModel, {
  34878. text: formattedLabel,
  34879. textAlign: itemLabelModel.getShallow('align', true)
  34880. || labelLayout.textAlign,
  34881. textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true)
  34882. || itemLabelModel.getShallow('baseline', true)
  34883. || labelLayout.textVerticalAlign,
  34884. textFill: typeof textColor === 'function'
  34885. ? textColor(
  34886. // (1) In category axis with data zoom, tick is not the original
  34887. // index of axis.data. So tick should not be exposed to user
  34888. // in category axis.
  34889. // (2) Compatible with previous version, which always use formatted label as
  34890. // input. But in interval scale the formatted label is like '223,445', which
  34891. // maked user repalce ','. So we modify it to return original val but remain
  34892. // it as 'string' to avoid error in replacing.
  34893. axis.type === 'category'
  34894. ? rawLabel
  34895. : axis.type === 'value'
  34896. ? tickValue + ''
  34897. : tickValue,
  34898. index
  34899. )
  34900. : textColor
  34901. });
  34902. // Pack data for mouse event
  34903. if (triggerEvent) {
  34904. textEl.eventData = makeAxisEventDataBase(axisModel);
  34905. textEl.eventData.targetType = 'axisLabel';
  34906. textEl.eventData.value = rawLabel;
  34907. }
  34908. // FIXME
  34909. axisBuilder._dumbGroup.add(textEl);
  34910. textEl.updateTransform();
  34911. labelEls.push(textEl);
  34912. axisBuilder.group.add(textEl);
  34913. textEl.decomposeTransform();
  34914. });
  34915. return labelEls;
  34916. }
  34917. /*
  34918. * Licensed to the Apache Software Foundation (ASF) under one
  34919. * or more contributor license agreements. See the NOTICE file
  34920. * distributed with this work for additional information
  34921. * regarding copyright ownership. The ASF licenses this file
  34922. * to you under the Apache License, Version 2.0 (the
  34923. * "License"); you may not use this file except in compliance
  34924. * with the License. You may obtain a copy of the License at
  34925. *
  34926. * http://www.apache.org/licenses/LICENSE-2.0
  34927. *
  34928. * Unless required by applicable law or agreed to in writing,
  34929. * software distributed under the License is distributed on an
  34930. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  34931. * KIND, either express or implied. See the License for the
  34932. * specific language governing permissions and limitations
  34933. * under the License.
  34934. */
  34935. var each$6 = each$1;
  34936. var curry$1 = curry;
  34937. // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  34938. // allAxesInfo should be updated when setOption performed.
  34939. function collect(ecModel, api) {
  34940. var result = {
  34941. /**
  34942. * key: makeKey(axis.model)
  34943. * value: {
  34944. * axis,
  34945. * coordSys,
  34946. * axisPointerModel,
  34947. * triggerTooltip,
  34948. * involveSeries,
  34949. * snap,
  34950. * seriesModels,
  34951. * seriesDataCount
  34952. * }
  34953. */
  34954. axesInfo: {},
  34955. seriesInvolved: false,
  34956. /**
  34957. * key: makeKey(coordSys.model)
  34958. * value: Object: key makeKey(axis.model), value: axisInfo
  34959. */
  34960. coordSysAxesInfo: {},
  34961. coordSysMap: {}
  34962. };
  34963. collectAxesInfo(result, ecModel, api);
  34964. // Check seriesInvolved for performance, in case too many series in some chart.
  34965. result.seriesInvolved && collectSeriesInfo(result, ecModel);
  34966. return result;
  34967. }
  34968. function collectAxesInfo(result, ecModel, api) {
  34969. var globalTooltipModel = ecModel.getComponent('tooltip');
  34970. var globalAxisPointerModel = ecModel.getComponent('axisPointer');
  34971. // links can only be set on global.
  34972. var linksOption = globalAxisPointerModel.get('link', true) || [];
  34973. var linkGroups = [];
  34974. // Collect axes info.
  34975. each$6(api.getCoordinateSystems(), function (coordSys) {
  34976. // Some coordinate system do not support axes, like geo.
  34977. if (!coordSys.axisPointerEnabled) {
  34978. return;
  34979. }
  34980. var coordSysKey = makeKey(coordSys.model);
  34981. var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};
  34982. result.coordSysMap[coordSysKey] = coordSys;
  34983. // Set tooltip (like 'cross') is a convienent way to show axisPointer
  34984. // for user. So we enable seting tooltip on coordSys model.
  34985. var coordSysModel = coordSys.model;
  34986. var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);
  34987. each$6(coordSys.getAxes(), curry$1(saveTooltipAxisInfo, false, null));
  34988. // If axis tooltip used, choose tooltip axis for each coordSys.
  34989. // Notice this case: coordSys is `grid` but not `cartesian2D` here.
  34990. if (coordSys.getTooltipAxes
  34991. && globalTooltipModel
  34992. // If tooltip.showContent is set as false, tooltip will not
  34993. // show but axisPointer will show as normal.
  34994. && baseTooltipModel.get('show')
  34995. ) {
  34996. // Compatible with previous logic. But series.tooltip.trigger: 'axis'
  34997. // or series.data[n].tooltip.trigger: 'axis' are not support any more.
  34998. var triggerAxis = baseTooltipModel.get('trigger') === 'axis';
  34999. var cross = baseTooltipModel.get('axisPointer.type') === 'cross';
  35000. var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));
  35001. if (triggerAxis || cross) {
  35002. each$6(tooltipAxes.baseAxes, curry$1(
  35003. saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis
  35004. ));
  35005. }
  35006. if (cross) {
  35007. each$6(tooltipAxes.otherAxes, curry$1(saveTooltipAxisInfo, 'cross', false));
  35008. }
  35009. }
  35010. // fromTooltip: true | false | 'cross'
  35011. // triggerTooltip: true | false | null
  35012. function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
  35013. var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);
  35014. var axisPointerShow = axisPointerModel.get('show');
  35015. if (!axisPointerShow || (
  35016. axisPointerShow === 'auto'
  35017. && !fromTooltip
  35018. && !isHandleTrigger(axisPointerModel)
  35019. )) {
  35020. return;
  35021. }
  35022. if (triggerTooltip == null) {
  35023. triggerTooltip = axisPointerModel.get('triggerTooltip');
  35024. }
  35025. axisPointerModel = fromTooltip
  35026. ? makeAxisPointerModel(
  35027. axis, baseTooltipModel, globalAxisPointerModel, ecModel,
  35028. fromTooltip, triggerTooltip
  35029. )
  35030. : axisPointerModel;
  35031. var snap = axisPointerModel.get('snap');
  35032. var key = makeKey(axis.model);
  35033. var involveSeries = triggerTooltip || snap || axis.type === 'category';
  35034. // If result.axesInfo[key] exist, override it (tooltip has higher priority).
  35035. var axisInfo = result.axesInfo[key] = {
  35036. key: key,
  35037. axis: axis,
  35038. coordSys: coordSys,
  35039. axisPointerModel: axisPointerModel,
  35040. triggerTooltip: triggerTooltip,
  35041. involveSeries: involveSeries,
  35042. snap: snap,
  35043. useHandle: isHandleTrigger(axisPointerModel),
  35044. seriesModels: []
  35045. };
  35046. axesInfoInCoordSys[key] = axisInfo;
  35047. result.seriesInvolved |= involveSeries;
  35048. var groupIndex = getLinkGroupIndex(linksOption, axis);
  35049. if (groupIndex != null) {
  35050. var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}});
  35051. linkGroup.axesInfo[key] = axisInfo;
  35052. linkGroup.mapper = linksOption[groupIndex].mapper;
  35053. axisInfo.linkGroup = linkGroup;
  35054. }
  35055. }
  35056. });
  35057. }
  35058. function makeAxisPointerModel(
  35059. axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip
  35060. ) {
  35061. var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');
  35062. var volatileOption = {};
  35063. each$6(
  35064. [
  35065. 'type', 'snap', 'lineStyle', 'shadowStyle', 'label',
  35066. 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'
  35067. ],
  35068. function (field) {
  35069. volatileOption[field] = clone(tooltipAxisPointerModel.get(field));
  35070. }
  35071. );
  35072. // category axis do not auto snap, otherwise some tick that do not
  35073. // has value can not be hovered. value/time/log axis default snap if
  35074. // triggered from tooltip and trigger tooltip.
  35075. volatileOption.snap = axis.type !== 'category' && !!triggerTooltip;
  35076. // Compatibel with previous behavior, tooltip axis do not show label by default.
  35077. // Only these properties can be overrided from tooltip to axisPointer.
  35078. if (tooltipAxisPointerModel.get('type') === 'cross') {
  35079. volatileOption.type = 'line';
  35080. }
  35081. var labelOption = volatileOption.label || (volatileOption.label = {});
  35082. // Follow the convention, do not show label when triggered by tooltip by default.
  35083. labelOption.show == null && (labelOption.show = false);
  35084. if (fromTooltip === 'cross') {
  35085. // When 'cross', both axes show labels.
  35086. var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get('label.show');
  35087. labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true;
  35088. // If triggerTooltip, this is a base axis, which should better not use cross style
  35089. // (cross style is dashed by default)
  35090. if (!triggerTooltip) {
  35091. var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');
  35092. crossStyle && defaults(labelOption, crossStyle.textStyle);
  35093. }
  35094. }
  35095. return axis.model.getModel(
  35096. 'axisPointer',
  35097. new Model(volatileOption, globalAxisPointerModel, ecModel)
  35098. );
  35099. }
  35100. function collectSeriesInfo(result, ecModel) {
  35101. // Prepare data for axis trigger
  35102. ecModel.eachSeries(function (seriesModel) {
  35103. // Notice this case: this coordSys is `cartesian2D` but not `grid`.
  35104. var coordSys = seriesModel.coordinateSystem;
  35105. var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);
  35106. var seriesTooltipShow = seriesModel.get('tooltip.show', true);
  35107. if (!coordSys
  35108. || seriesTooltipTrigger === 'none'
  35109. || seriesTooltipTrigger === false
  35110. || seriesTooltipTrigger === 'item'
  35111. || seriesTooltipShow === false
  35112. || seriesModel.get('axisPointer.show', true) === false
  35113. ) {
  35114. return;
  35115. }
  35116. each$6(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {
  35117. var axis = axisInfo.axis;
  35118. if (coordSys.getAxis(axis.dim) === axis) {
  35119. axisInfo.seriesModels.push(seriesModel);
  35120. axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);
  35121. axisInfo.seriesDataCount += seriesModel.getData().count();
  35122. }
  35123. });
  35124. }, this);
  35125. }
  35126. /**
  35127. * For example:
  35128. * {
  35129. * axisPointer: {
  35130. * links: [{
  35131. * xAxisIndex: [2, 4],
  35132. * yAxisIndex: 'all'
  35133. * }, {
  35134. * xAxisId: ['a5', 'a7'],
  35135. * xAxisName: 'xxx'
  35136. * }]
  35137. * }
  35138. * }
  35139. */
  35140. function getLinkGroupIndex(linksOption, axis) {
  35141. var axisModel = axis.model;
  35142. var dim = axis.dim;
  35143. for (var i = 0; i < linksOption.length; i++) {
  35144. var linkOption = linksOption[i] || {};
  35145. if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id)
  35146. || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex)
  35147. || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)
  35148. ) {
  35149. return i;
  35150. }
  35151. }
  35152. }
  35153. function checkPropInLink(linkPropValue, axisPropValue) {
  35154. return linkPropValue === 'all'
  35155. || (isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0)
  35156. || linkPropValue === axisPropValue;
  35157. }
  35158. function fixValue(axisModel) {
  35159. var axisInfo = getAxisInfo(axisModel);
  35160. if (!axisInfo) {
  35161. return;
  35162. }
  35163. var axisPointerModel = axisInfo.axisPointerModel;
  35164. var scale = axisInfo.axis.scale;
  35165. var option = axisPointerModel.option;
  35166. var status = axisPointerModel.get('status');
  35167. var value = axisPointerModel.get('value');
  35168. // Parse init value for category and time axis.
  35169. if (value != null) {
  35170. value = scale.parse(value);
  35171. }
  35172. var useHandle = isHandleTrigger(axisPointerModel);
  35173. // If `handle` used, `axisPointer` will always be displayed, so value
  35174. // and status should be initialized.
  35175. if (status == null) {
  35176. option.status = useHandle ? 'show' : 'hide';
  35177. }
  35178. var extent = scale.getExtent().slice();
  35179. extent[0] > extent[1] && extent.reverse();
  35180. if (// Pick a value on axis when initializing.
  35181. value == null
  35182. // If both `handle` and `dataZoom` are used, value may be out of axis extent,
  35183. // where we should re-pick a value to keep `handle` displaying normally.
  35184. || value > extent[1]
  35185. ) {
  35186. // Make handle displayed on the end of the axis when init, which looks better.
  35187. value = extent[1];
  35188. }
  35189. if (value < extent[0]) {
  35190. value = extent[0];
  35191. }
  35192. option.value = value;
  35193. if (useHandle) {
  35194. option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
  35195. }
  35196. }
  35197. function getAxisInfo(axisModel) {
  35198. var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
  35199. return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
  35200. }
  35201. function getAxisPointerModel(axisModel) {
  35202. var axisInfo = getAxisInfo(axisModel);
  35203. return axisInfo && axisInfo.axisPointerModel;
  35204. }
  35205. function isHandleTrigger(axisPointerModel) {
  35206. return !!axisPointerModel.get('handle.show');
  35207. }
  35208. /**
  35209. * @param {module:echarts/model/Model} model
  35210. * @return {string} unique key
  35211. */
  35212. function makeKey(model) {
  35213. return model.type + '||' + model.id;
  35214. }
  35215. /*
  35216. * Licensed to the Apache Software Foundation (ASF) under one
  35217. * or more contributor license agreements. See the NOTICE file
  35218. * distributed with this work for additional information
  35219. * regarding copyright ownership. The ASF licenses this file
  35220. * to you under the Apache License, Version 2.0 (the
  35221. * "License"); you may not use this file except in compliance
  35222. * with the License. You may obtain a copy of the License at
  35223. *
  35224. * http://www.apache.org/licenses/LICENSE-2.0
  35225. *
  35226. * Unless required by applicable law or agreed to in writing,
  35227. * software distributed under the License is distributed on an
  35228. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35229. * KIND, either express or implied. See the License for the
  35230. * specific language governing permissions and limitations
  35231. * under the License.
  35232. */
  35233. /**
  35234. * Base class of AxisView.
  35235. */
  35236. var AxisView = extendComponentView({
  35237. type: 'axis',
  35238. /**
  35239. * @private
  35240. */
  35241. _axisPointer: null,
  35242. /**
  35243. * @protected
  35244. * @type {string}
  35245. */
  35246. axisPointerClass: null,
  35247. /**
  35248. * @override
  35249. */
  35250. render: function (axisModel, ecModel, api, payload) {
  35251. // FIXME
  35252. // This process should proformed after coordinate systems updated
  35253. // (axis scale updated), and should be performed each time update.
  35254. // So put it here temporarily, although it is not appropriate to
  35255. // put a model-writing procedure in `view`.
  35256. this.axisPointerClass && fixValue(axisModel);
  35257. AxisView.superApply(this, 'render', arguments);
  35258. updateAxisPointer(this, axisModel, ecModel, api, payload, true);
  35259. },
  35260. /**
  35261. * Action handler.
  35262. * @public
  35263. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  35264. * @param {module:echarts/model/Global} ecModel
  35265. * @param {module:echarts/ExtensionAPI} api
  35266. * @param {Object} payload
  35267. */
  35268. updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
  35269. updateAxisPointer(this, axisModel, ecModel, api, payload, false);
  35270. },
  35271. /**
  35272. * @override
  35273. */
  35274. remove: function (ecModel, api) {
  35275. var axisPointer = this._axisPointer;
  35276. axisPointer && axisPointer.remove(api);
  35277. AxisView.superApply(this, 'remove', arguments);
  35278. },
  35279. /**
  35280. * @override
  35281. */
  35282. dispose: function (ecModel, api) {
  35283. disposeAxisPointer(this, api);
  35284. AxisView.superApply(this, 'dispose', arguments);
  35285. }
  35286. });
  35287. function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
  35288. var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
  35289. if (!Clazz) {
  35290. return;
  35291. }
  35292. var axisPointerModel = getAxisPointerModel(axisModel);
  35293. axisPointerModel
  35294. ? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))
  35295. .render(axisModel, axisPointerModel, api, forceRender)
  35296. : disposeAxisPointer(axisView, api);
  35297. }
  35298. function disposeAxisPointer(axisView, ecModel, api) {
  35299. var axisPointer = axisView._axisPointer;
  35300. axisPointer && axisPointer.dispose(ecModel, api);
  35301. axisView._axisPointer = null;
  35302. }
  35303. var axisPointerClazz = [];
  35304. AxisView.registerAxisPointerClass = function (type, clazz) {
  35305. if (__DEV__) {
  35306. if (axisPointerClazz[type]) {
  35307. throw new Error('axisPointer ' + type + ' exists');
  35308. }
  35309. }
  35310. axisPointerClazz[type] = clazz;
  35311. };
  35312. AxisView.getAxisPointerClass = function (type) {
  35313. return type && axisPointerClazz[type];
  35314. };
  35315. /*
  35316. * Licensed to the Apache Software Foundation (ASF) under one
  35317. * or more contributor license agreements. See the NOTICE file
  35318. * distributed with this work for additional information
  35319. * regarding copyright ownership. The ASF licenses this file
  35320. * to you under the Apache License, Version 2.0 (the
  35321. * "License"); you may not use this file except in compliance
  35322. * with the License. You may obtain a copy of the License at
  35323. *
  35324. * http://www.apache.org/licenses/LICENSE-2.0
  35325. *
  35326. * Unless required by applicable law or agreed to in writing,
  35327. * software distributed under the License is distributed on an
  35328. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35329. * KIND, either express or implied. See the License for the
  35330. * specific language governing permissions and limitations
  35331. * under the License.
  35332. */
  35333. /**
  35334. * Can only be called after coordinate system creation stage.
  35335. * (Can be called before coordinate system update stage).
  35336. *
  35337. * @param {Object} opt {labelInside}
  35338. * @return {Object} {
  35339. * position, rotation, labelDirection, labelOffset,
  35340. * tickDirection, labelRotate, z2
  35341. * }
  35342. */
  35343. function layout$1(gridModel, axisModel, opt) {
  35344. opt = opt || {};
  35345. var grid = gridModel.coordinateSystem;
  35346. var axis = axisModel.axis;
  35347. var layout = {};
  35348. var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];
  35349. var rawAxisPosition = axis.position;
  35350. var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;
  35351. var axisDim = axis.dim;
  35352. var rect = grid.getRect();
  35353. var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
  35354. var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2};
  35355. var axisOffset = axisModel.get('offset') || 0;
  35356. var posBound = axisDim === 'x'
  35357. ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]
  35358. : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];
  35359. if (otherAxisOnZeroOf) {
  35360. var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));
  35361. posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);
  35362. }
  35363. // Axis position
  35364. layout.position = [
  35365. axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],
  35366. axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]
  35367. ];
  35368. // Axis rotation
  35369. layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
  35370. // Tick and label direction, x y is axisDim
  35371. var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
  35372. layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
  35373. layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;
  35374. if (axisModel.get('axisTick.inside')) {
  35375. layout.tickDirection = -layout.tickDirection;
  35376. }
  35377. if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
  35378. layout.labelDirection = -layout.labelDirection;
  35379. }
  35380. // Special label rotation
  35381. var labelRotate = axisModel.get('axisLabel.rotate');
  35382. layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;
  35383. // Over splitLine and splitArea
  35384. layout.z2 = 1;
  35385. return layout;
  35386. }
  35387. /*
  35388. * Licensed to the Apache Software Foundation (ASF) under one
  35389. * or more contributor license agreements. See the NOTICE file
  35390. * distributed with this work for additional information
  35391. * regarding copyright ownership. The ASF licenses this file
  35392. * to you under the Apache License, Version 2.0 (the
  35393. * "License"); you may not use this file except in compliance
  35394. * with the License. You may obtain a copy of the License at
  35395. *
  35396. * http://www.apache.org/licenses/LICENSE-2.0
  35397. *
  35398. * Unless required by applicable law or agreed to in writing,
  35399. * software distributed under the License is distributed on an
  35400. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35401. * KIND, either express or implied. See the License for the
  35402. * specific language governing permissions and limitations
  35403. * under the License.
  35404. */
  35405. var axisBuilderAttrs = [
  35406. 'axisLine', 'axisTickLabel', 'axisName'
  35407. ];
  35408. var selfBuilderAttrs = [
  35409. 'splitArea', 'splitLine'
  35410. ];
  35411. // function getAlignWithLabel(model, axisModel) {
  35412. // var alignWithLabel = model.get('alignWithLabel');
  35413. // if (alignWithLabel === 'auto') {
  35414. // alignWithLabel = axisModel.get('axisTick.alignWithLabel');
  35415. // }
  35416. // return alignWithLabel;
  35417. // }
  35418. var CartesianAxisView = AxisView.extend({
  35419. type: 'cartesianAxis',
  35420. axisPointerClass: 'CartesianAxisPointer',
  35421. /**
  35422. * @override
  35423. */
  35424. render: function (axisModel, ecModel, api, payload) {
  35425. this.group.removeAll();
  35426. var oldAxisGroup = this._axisGroup;
  35427. this._axisGroup = new Group();
  35428. this.group.add(this._axisGroup);
  35429. if (!axisModel.get('show')) {
  35430. return;
  35431. }
  35432. var gridModel = axisModel.getCoordSysModel();
  35433. var layout = layout$1(gridModel, axisModel);
  35434. var axisBuilder = new AxisBuilder(axisModel, layout);
  35435. each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder);
  35436. this._axisGroup.add(axisBuilder.getGroup());
  35437. each$1(selfBuilderAttrs, function (name) {
  35438. if (axisModel.get(name + '.show')) {
  35439. this['_' + name](axisModel, gridModel);
  35440. }
  35441. }, this);
  35442. groupTransition(oldAxisGroup, this._axisGroup, axisModel);
  35443. CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
  35444. },
  35445. remove: function () {
  35446. this._splitAreaColors = null;
  35447. },
  35448. /**
  35449. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  35450. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  35451. * @private
  35452. */
  35453. _splitLine: function (axisModel, gridModel) {
  35454. var axis = axisModel.axis;
  35455. if (axis.scale.isBlank()) {
  35456. return;
  35457. }
  35458. var splitLineModel = axisModel.getModel('splitLine');
  35459. var lineStyleModel = splitLineModel.getModel('lineStyle');
  35460. var lineColors = lineStyleModel.get('color');
  35461. lineColors = isArray(lineColors) ? lineColors : [lineColors];
  35462. var gridRect = gridModel.coordinateSystem.getRect();
  35463. var isHorizontal = axis.isHorizontal();
  35464. var lineCount = 0;
  35465. var ticksCoords = axis.getTicksCoords({
  35466. tickModel: splitLineModel
  35467. });
  35468. var p1 = [];
  35469. var p2 = [];
  35470. // Simple optimization
  35471. // Batching the lines if color are the same
  35472. var lineStyle = lineStyleModel.getLineStyle();
  35473. for (var i = 0; i < ticksCoords.length; i++) {
  35474. var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);
  35475. if (isHorizontal) {
  35476. p1[0] = tickCoord;
  35477. p1[1] = gridRect.y;
  35478. p2[0] = tickCoord;
  35479. p2[1] = gridRect.y + gridRect.height;
  35480. }
  35481. else {
  35482. p1[0] = gridRect.x;
  35483. p1[1] = tickCoord;
  35484. p2[0] = gridRect.x + gridRect.width;
  35485. p2[1] = tickCoord;
  35486. }
  35487. var colorIndex = (lineCount++) % lineColors.length;
  35488. var tickValue = ticksCoords[i].tickValue;
  35489. this._axisGroup.add(new Line(subPixelOptimizeLine({
  35490. anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,
  35491. shape: {
  35492. x1: p1[0],
  35493. y1: p1[1],
  35494. x2: p2[0],
  35495. y2: p2[1]
  35496. },
  35497. style: defaults({
  35498. stroke: lineColors[colorIndex]
  35499. }, lineStyle),
  35500. silent: true
  35501. })));
  35502. }
  35503. },
  35504. /**
  35505. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  35506. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  35507. * @private
  35508. */
  35509. _splitArea: function (axisModel, gridModel) {
  35510. var axis = axisModel.axis;
  35511. if (axis.scale.isBlank()) {
  35512. return;
  35513. }
  35514. var splitAreaModel = axisModel.getModel('splitArea');
  35515. var areaStyleModel = splitAreaModel.getModel('areaStyle');
  35516. var areaColors = areaStyleModel.get('color');
  35517. var gridRect = gridModel.coordinateSystem.getRect();
  35518. var ticksCoords = axis.getTicksCoords({
  35519. tickModel: splitAreaModel,
  35520. clamp: true
  35521. });
  35522. if (!ticksCoords.length) {
  35523. return;
  35524. }
  35525. // For Making appropriate splitArea animation, the color and anid
  35526. // should be corresponding to previous one if possible.
  35527. var areaColorsLen = areaColors.length;
  35528. var lastSplitAreaColors = this._splitAreaColors;
  35529. var newSplitAreaColors = createHashMap();
  35530. var colorIndex = 0;
  35531. if (lastSplitAreaColors) {
  35532. for (var i = 0; i < ticksCoords.length; i++) {
  35533. var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);
  35534. if (cIndex != null) {
  35535. colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;
  35536. break;
  35537. }
  35538. }
  35539. }
  35540. var prev = axis.toGlobalCoord(ticksCoords[0].coord);
  35541. var areaStyle = areaStyleModel.getAreaStyle();
  35542. areaColors = isArray(areaColors) ? areaColors : [areaColors];
  35543. for (var i = 1; i < ticksCoords.length; i++) {
  35544. var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);
  35545. var x;
  35546. var y;
  35547. var width;
  35548. var height;
  35549. if (axis.isHorizontal()) {
  35550. x = prev;
  35551. y = gridRect.y;
  35552. width = tickCoord - x;
  35553. height = gridRect.height;
  35554. prev = x + width;
  35555. }
  35556. else {
  35557. x = gridRect.x;
  35558. y = prev;
  35559. width = gridRect.width;
  35560. height = tickCoord - y;
  35561. prev = y + height;
  35562. }
  35563. var tickValue = ticksCoords[i - 1].tickValue;
  35564. tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);
  35565. this._axisGroup.add(new Rect({
  35566. anid: tickValue != null ? 'area_' + tickValue : null,
  35567. shape: {
  35568. x: x,
  35569. y: y,
  35570. width: width,
  35571. height: height
  35572. },
  35573. style: defaults({
  35574. fill: areaColors[colorIndex]
  35575. }, areaStyle),
  35576. silent: true
  35577. }));
  35578. colorIndex = (colorIndex + 1) % areaColorsLen;
  35579. }
  35580. this._splitAreaColors = newSplitAreaColors;
  35581. }
  35582. });
  35583. CartesianAxisView.extend({
  35584. type: 'xAxis'
  35585. });
  35586. CartesianAxisView.extend({
  35587. type: 'yAxis'
  35588. });
  35589. /*
  35590. * Licensed to the Apache Software Foundation (ASF) under one
  35591. * or more contributor license agreements. See the NOTICE file
  35592. * distributed with this work for additional information
  35593. * regarding copyright ownership. The ASF licenses this file
  35594. * to you under the Apache License, Version 2.0 (the
  35595. * "License"); you may not use this file except in compliance
  35596. * with the License. You may obtain a copy of the License at
  35597. *
  35598. * http://www.apache.org/licenses/LICENSE-2.0
  35599. *
  35600. * Unless required by applicable law or agreed to in writing,
  35601. * software distributed under the License is distributed on an
  35602. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35603. * KIND, either express or implied. See the License for the
  35604. * specific language governing permissions and limitations
  35605. * under the License.
  35606. */
  35607. /*
  35608. * Licensed to the Apache Software Foundation (ASF) under one
  35609. * or more contributor license agreements. See the NOTICE file
  35610. * distributed with this work for additional information
  35611. * regarding copyright ownership. The ASF licenses this file
  35612. * to you under the Apache License, Version 2.0 (the
  35613. * "License"); you may not use this file except in compliance
  35614. * with the License. You may obtain a copy of the License at
  35615. *
  35616. * http://www.apache.org/licenses/LICENSE-2.0
  35617. *
  35618. * Unless required by applicable law or agreed to in writing,
  35619. * software distributed under the License is distributed on an
  35620. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35621. * KIND, either express or implied. See the License for the
  35622. * specific language governing permissions and limitations
  35623. * under the License.
  35624. */
  35625. // Grid view
  35626. extendComponentView({
  35627. type: 'grid',
  35628. render: function (gridModel, ecModel) {
  35629. this.group.removeAll();
  35630. if (gridModel.get('show')) {
  35631. this.group.add(new Rect({
  35632. shape: gridModel.coordinateSystem.getRect(),
  35633. style: defaults({
  35634. fill: gridModel.get('backgroundColor')
  35635. }, gridModel.getItemStyle()),
  35636. silent: true,
  35637. z2: -1
  35638. }));
  35639. }
  35640. }
  35641. });
  35642. registerPreprocessor(function (option) {
  35643. // Only create grid when need
  35644. if (option.xAxis && option.yAxis && !option.grid) {
  35645. option.grid = {};
  35646. }
  35647. });
  35648. /*
  35649. * Licensed to the Apache Software Foundation (ASF) under one
  35650. * or more contributor license agreements. See the NOTICE file
  35651. * distributed with this work for additional information
  35652. * regarding copyright ownership. The ASF licenses this file
  35653. * to you under the Apache License, Version 2.0 (the
  35654. * "License"); you may not use this file except in compliance
  35655. * with the License. You may obtain a copy of the License at
  35656. *
  35657. * http://www.apache.org/licenses/LICENSE-2.0
  35658. *
  35659. * Unless required by applicable law or agreed to in writing,
  35660. * software distributed under the License is distributed on an
  35661. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35662. * KIND, either express or implied. See the License for the
  35663. * specific language governing permissions and limitations
  35664. * under the License.
  35665. */
  35666. // In case developer forget to include grid component
  35667. registerVisual(visualSymbol('line', 'circle', 'line'));
  35668. registerLayout(pointsLayout('line'));
  35669. // Down sample after filter
  35670. registerProcessor(
  35671. PRIORITY.PROCESSOR.STATISTIC,
  35672. dataSample('line')
  35673. );
  35674. /*
  35675. * Licensed to the Apache Software Foundation (ASF) under one
  35676. * or more contributor license agreements. See the NOTICE file
  35677. * distributed with this work for additional information
  35678. * regarding copyright ownership. The ASF licenses this file
  35679. * to you under the Apache License, Version 2.0 (the
  35680. * "License"); you may not use this file except in compliance
  35681. * with the License. You may obtain a copy of the License at
  35682. *
  35683. * http://www.apache.org/licenses/LICENSE-2.0
  35684. *
  35685. * Unless required by applicable law or agreed to in writing,
  35686. * software distributed under the License is distributed on an
  35687. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35688. * KIND, either express or implied. See the License for the
  35689. * specific language governing permissions and limitations
  35690. * under the License.
  35691. */
  35692. var BaseBarSeries = SeriesModel.extend({
  35693. type: 'series.__base_bar__',
  35694. getInitialData: function (option, ecModel) {
  35695. return createListFromArray(this.getSource(), this);
  35696. },
  35697. getMarkerPosition: function (value) {
  35698. var coordSys = this.coordinateSystem;
  35699. if (coordSys) {
  35700. // PENDING if clamp ?
  35701. var pt = coordSys.dataToPoint(coordSys.clampData(value));
  35702. var data = this.getData();
  35703. var offset = data.getLayout('offset');
  35704. var size = data.getLayout('size');
  35705. var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;
  35706. pt[offsetIndex] += offset + size / 2;
  35707. return pt;
  35708. }
  35709. return [NaN, NaN];
  35710. },
  35711. defaultOption: {
  35712. zlevel: 0, // 一级层叠
  35713. z: 2, // 二级层叠
  35714. coordinateSystem: 'cartesian2d',
  35715. legendHoverLink: true,
  35716. // stack: null
  35717. // Cartesian coordinate system
  35718. // xAxisIndex: 0,
  35719. // yAxisIndex: 0,
  35720. // 最小高度改为0
  35721. barMinHeight: 0,
  35722. // 最小角度为0,仅对极坐标系下的柱状图有效
  35723. barMinAngle: 0,
  35724. // cursor: null,
  35725. large: false,
  35726. largeThreshold: 400,
  35727. progressive: 3e3,
  35728. progressiveChunkMode: 'mod',
  35729. // barMaxWidth: null,
  35730. // 默认自适应
  35731. // barWidth: null,
  35732. // 柱间距离,默认为柱形宽度的30%,可设固定值
  35733. // barGap: '30%',
  35734. // 类目间柱形距离,默认为类目间距的20%,可设固定值
  35735. // barCategoryGap: '20%',
  35736. // label: {
  35737. // show: false
  35738. // },
  35739. itemStyle: {},
  35740. emphasis: {}
  35741. }
  35742. });
  35743. /*
  35744. * Licensed to the Apache Software Foundation (ASF) under one
  35745. * or more contributor license agreements. See the NOTICE file
  35746. * distributed with this work for additional information
  35747. * regarding copyright ownership. The ASF licenses this file
  35748. * to you under the Apache License, Version 2.0 (the
  35749. * "License"); you may not use this file except in compliance
  35750. * with the License. You may obtain a copy of the License at
  35751. *
  35752. * http://www.apache.org/licenses/LICENSE-2.0
  35753. *
  35754. * Unless required by applicable law or agreed to in writing,
  35755. * software distributed under the License is distributed on an
  35756. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35757. * KIND, either express or implied. See the License for the
  35758. * specific language governing permissions and limitations
  35759. * under the License.
  35760. */
  35761. BaseBarSeries.extend({
  35762. type: 'series.bar',
  35763. dependencies: ['grid', 'polar'],
  35764. brushSelector: 'rect',
  35765. /**
  35766. * @override
  35767. */
  35768. getProgressive: function () {
  35769. // Do not support progressive in normal mode.
  35770. return this.get('large')
  35771. ? this.get('progressive')
  35772. : false;
  35773. },
  35774. /**
  35775. * @override
  35776. */
  35777. getProgressiveThreshold: function () {
  35778. // Do not support progressive in normal mode.
  35779. var progressiveThreshold = this.get('progressiveThreshold');
  35780. var largeThreshold = this.get('largeThreshold');
  35781. if (largeThreshold > progressiveThreshold) {
  35782. progressiveThreshold = largeThreshold;
  35783. }
  35784. return progressiveThreshold;
  35785. }
  35786. });
  35787. /*
  35788. * Licensed to the Apache Software Foundation (ASF) under one
  35789. * or more contributor license agreements. See the NOTICE file
  35790. * distributed with this work for additional information
  35791. * regarding copyright ownership. The ASF licenses this file
  35792. * to you under the Apache License, Version 2.0 (the
  35793. * "License"); you may not use this file except in compliance
  35794. * with the License. You may obtain a copy of the License at
  35795. *
  35796. * http://www.apache.org/licenses/LICENSE-2.0
  35797. *
  35798. * Unless required by applicable law or agreed to in writing,
  35799. * software distributed under the License is distributed on an
  35800. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35801. * KIND, either express or implied. See the License for the
  35802. * specific language governing permissions and limitations
  35803. * under the License.
  35804. */
  35805. function setLabel(
  35806. normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside
  35807. ) {
  35808. var labelModel = itemModel.getModel('label');
  35809. var hoverLabelModel = itemModel.getModel('emphasis.label');
  35810. setLabelStyle(
  35811. normalStyle, hoverStyle, labelModel, hoverLabelModel,
  35812. {
  35813. labelFetcher: seriesModel,
  35814. labelDataIndex: dataIndex,
  35815. defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),
  35816. isRectText: true,
  35817. autoColor: color
  35818. }
  35819. );
  35820. fixPosition(normalStyle);
  35821. fixPosition(hoverStyle);
  35822. }
  35823. function fixPosition(style, labelPositionOutside) {
  35824. if (style.textPosition === 'outside') {
  35825. style.textPosition = labelPositionOutside;
  35826. }
  35827. }
  35828. /*
  35829. * Licensed to the Apache Software Foundation (ASF) under one
  35830. * or more contributor license agreements. See the NOTICE file
  35831. * distributed with this work for additional information
  35832. * regarding copyright ownership. The ASF licenses this file
  35833. * to you under the Apache License, Version 2.0 (the
  35834. * "License"); you may not use this file except in compliance
  35835. * with the License. You may obtain a copy of the License at
  35836. *
  35837. * http://www.apache.org/licenses/LICENSE-2.0
  35838. *
  35839. * Unless required by applicable law or agreed to in writing,
  35840. * software distributed under the License is distributed on an
  35841. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35842. * KIND, either express or implied. See the License for the
  35843. * specific language governing permissions and limitations
  35844. * under the License.
  35845. */
  35846. var getBarItemStyle = makeStyleMapper(
  35847. [
  35848. ['fill', 'color'],
  35849. ['stroke', 'borderColor'],
  35850. ['lineWidth', 'borderWidth'],
  35851. // Compatitable with 2
  35852. ['stroke', 'barBorderColor'],
  35853. ['lineWidth', 'barBorderWidth'],
  35854. ['opacity'],
  35855. ['shadowBlur'],
  35856. ['shadowOffsetX'],
  35857. ['shadowOffsetY'],
  35858. ['shadowColor']
  35859. ]
  35860. );
  35861. var barItemStyle = {
  35862. getBarItemStyle: function (excludes) {
  35863. var style = getBarItemStyle(this, excludes);
  35864. if (this.getBorderLineDash) {
  35865. var lineDash = this.getBorderLineDash();
  35866. lineDash && (style.lineDash = lineDash);
  35867. }
  35868. return style;
  35869. }
  35870. };
  35871. /*
  35872. * Licensed to the Apache Software Foundation (ASF) under one
  35873. * or more contributor license agreements. See the NOTICE file
  35874. * distributed with this work for additional information
  35875. * regarding copyright ownership. The ASF licenses this file
  35876. * to you under the Apache License, Version 2.0 (the
  35877. * "License"); you may not use this file except in compliance
  35878. * with the License. You may obtain a copy of the License at
  35879. *
  35880. * http://www.apache.org/licenses/LICENSE-2.0
  35881. *
  35882. * Unless required by applicable law or agreed to in writing,
  35883. * software distributed under the License is distributed on an
  35884. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35885. * KIND, either express or implied. See the License for the
  35886. * specific language governing permissions and limitations
  35887. * under the License.
  35888. */
  35889. var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'barBorderWidth'];
  35890. // FIXME
  35891. // Just for compatible with ec2.
  35892. extend(Model.prototype, barItemStyle);
  35893. extendChartView({
  35894. type: 'bar',
  35895. render: function (seriesModel, ecModel, api) {
  35896. this._updateDrawMode(seriesModel);
  35897. var coordinateSystemType = seriesModel.get('coordinateSystem');
  35898. if (coordinateSystemType === 'cartesian2d'
  35899. || coordinateSystemType === 'polar'
  35900. ) {
  35901. this._isLargeDraw
  35902. ? this._renderLarge(seriesModel, ecModel, api)
  35903. : this._renderNormal(seriesModel, ecModel, api);
  35904. }
  35905. else if (__DEV__) {
  35906. console.warn('Only cartesian2d and polar supported for bar.');
  35907. }
  35908. return this.group;
  35909. },
  35910. incrementalPrepareRender: function (seriesModel, ecModel, api) {
  35911. this._clear();
  35912. this._updateDrawMode(seriesModel);
  35913. },
  35914. incrementalRender: function (params, seriesModel, ecModel, api) {
  35915. // Do not support progressive in normal mode.
  35916. this._incrementalRenderLarge(params, seriesModel);
  35917. },
  35918. _updateDrawMode: function (seriesModel) {
  35919. var isLargeDraw = seriesModel.pipelineContext.large;
  35920. if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) {
  35921. this._isLargeDraw = isLargeDraw;
  35922. this._clear();
  35923. }
  35924. },
  35925. _renderNormal: function (seriesModel, ecModel, api) {
  35926. var group = this.group;
  35927. var data = seriesModel.getData();
  35928. var oldData = this._data;
  35929. var coord = seriesModel.coordinateSystem;
  35930. var baseAxis = coord.getBaseAxis();
  35931. var isHorizontalOrRadial;
  35932. if (coord.type === 'cartesian2d') {
  35933. isHorizontalOrRadial = baseAxis.isHorizontal();
  35934. }
  35935. else if (coord.type === 'polar') {
  35936. isHorizontalOrRadial = baseAxis.dim === 'angle';
  35937. }
  35938. var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
  35939. data.diff(oldData)
  35940. .add(function (dataIndex) {
  35941. if (!data.hasValue(dataIndex)) {
  35942. return;
  35943. }
  35944. var itemModel = data.getItemModel(dataIndex);
  35945. var layout = getLayout[coord.type](data, dataIndex, itemModel);
  35946. var el = elementCreator[coord.type](
  35947. data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel
  35948. );
  35949. data.setItemGraphicEl(dataIndex, el);
  35950. group.add(el);
  35951. updateStyle(
  35952. el, data, dataIndex, itemModel, layout,
  35953. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  35954. );
  35955. })
  35956. .update(function (newIndex, oldIndex) {
  35957. var el = oldData.getItemGraphicEl(oldIndex);
  35958. if (!data.hasValue(newIndex)) {
  35959. group.remove(el);
  35960. return;
  35961. }
  35962. var itemModel = data.getItemModel(newIndex);
  35963. var layout = getLayout[coord.type](data, newIndex, itemModel);
  35964. if (el) {
  35965. updateProps(el, {shape: layout}, animationModel, newIndex);
  35966. }
  35967. else {
  35968. el = elementCreator[coord.type](
  35969. data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true
  35970. );
  35971. }
  35972. data.setItemGraphicEl(newIndex, el);
  35973. // Add back
  35974. group.add(el);
  35975. updateStyle(
  35976. el, data, newIndex, itemModel, layout,
  35977. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  35978. );
  35979. })
  35980. .remove(function (dataIndex) {
  35981. var el = oldData.getItemGraphicEl(dataIndex);
  35982. if (coord.type === 'cartesian2d') {
  35983. el && removeRect(dataIndex, animationModel, el);
  35984. }
  35985. else {
  35986. el && removeSector(dataIndex, animationModel, el);
  35987. }
  35988. })
  35989. .execute();
  35990. this._data = data;
  35991. },
  35992. _renderLarge: function (seriesModel, ecModel, api) {
  35993. this._clear();
  35994. createLarge(seriesModel, this.group);
  35995. },
  35996. _incrementalRenderLarge: function (params, seriesModel) {
  35997. createLarge(seriesModel, this.group, true);
  35998. },
  35999. dispose: noop,
  36000. remove: function (ecModel) {
  36001. this._clear(ecModel);
  36002. },
  36003. _clear: function (ecModel) {
  36004. var group = this.group;
  36005. var data = this._data;
  36006. if (ecModel && ecModel.get('animation') && data && !this._isLargeDraw) {
  36007. data.eachItemGraphicEl(function (el) {
  36008. if (el.type === 'sector') {
  36009. removeSector(el.dataIndex, ecModel, el);
  36010. }
  36011. else {
  36012. removeRect(el.dataIndex, ecModel, el);
  36013. }
  36014. });
  36015. }
  36016. else {
  36017. group.removeAll();
  36018. }
  36019. this._data = null;
  36020. }
  36021. });
  36022. var elementCreator = {
  36023. cartesian2d: function (
  36024. data, dataIndex, itemModel, layout, isHorizontal,
  36025. animationModel, isUpdate
  36026. ) {
  36027. var rect = new Rect({shape: extend({}, layout)});
  36028. // Animation
  36029. if (animationModel) {
  36030. var rectShape = rect.shape;
  36031. var animateProperty = isHorizontal ? 'height' : 'width';
  36032. var animateTarget = {};
  36033. rectShape[animateProperty] = 0;
  36034. animateTarget[animateProperty] = layout[animateProperty];
  36035. graphic[isUpdate ? 'updateProps' : 'initProps'](rect, {
  36036. shape: animateTarget
  36037. }, animationModel, dataIndex);
  36038. }
  36039. return rect;
  36040. },
  36041. polar: function (
  36042. data, dataIndex, itemModel, layout, isRadial,
  36043. animationModel, isUpdate
  36044. ) {
  36045. // Keep the same logic with bar in catesion: use end value to control
  36046. // direction. Notice that if clockwise is true (by default), the sector
  36047. // will always draw clockwisely, no matter whether endAngle is greater
  36048. // or less than startAngle.
  36049. var clockwise = layout.startAngle < layout.endAngle;
  36050. var sector = new Sector({
  36051. shape: defaults({clockwise: clockwise}, layout)
  36052. });
  36053. // Animation
  36054. if (animationModel) {
  36055. var sectorShape = sector.shape;
  36056. var animateProperty = isRadial ? 'r' : 'endAngle';
  36057. var animateTarget = {};
  36058. sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;
  36059. animateTarget[animateProperty] = layout[animateProperty];
  36060. graphic[isUpdate ? 'updateProps' : 'initProps'](sector, {
  36061. shape: animateTarget
  36062. }, animationModel, dataIndex);
  36063. }
  36064. return sector;
  36065. }
  36066. };
  36067. function removeRect(dataIndex, animationModel, el) {
  36068. // Not show text when animating
  36069. el.style.text = null;
  36070. updateProps(el, {
  36071. shape: {
  36072. width: 0
  36073. }
  36074. }, animationModel, dataIndex, function () {
  36075. el.parent && el.parent.remove(el);
  36076. });
  36077. }
  36078. function removeSector(dataIndex, animationModel, el) {
  36079. // Not show text when animating
  36080. el.style.text = null;
  36081. updateProps(el, {
  36082. shape: {
  36083. r: el.shape.r0
  36084. }
  36085. }, animationModel, dataIndex, function () {
  36086. el.parent && el.parent.remove(el);
  36087. });
  36088. }
  36089. var getLayout = {
  36090. cartesian2d: function (data, dataIndex, itemModel) {
  36091. var layout = data.getItemLayout(dataIndex);
  36092. var fixedLineWidth = getLineWidth(itemModel, layout);
  36093. // fix layout with lineWidth
  36094. var signX = layout.width > 0 ? 1 : -1;
  36095. var signY = layout.height > 0 ? 1 : -1;
  36096. return {
  36097. x: layout.x + signX * fixedLineWidth / 2,
  36098. y: layout.y + signY * fixedLineWidth / 2,
  36099. width: layout.width - signX * fixedLineWidth,
  36100. height: layout.height - signY * fixedLineWidth
  36101. };
  36102. },
  36103. polar: function (data, dataIndex, itemModel) {
  36104. var layout = data.getItemLayout(dataIndex);
  36105. return {
  36106. cx: layout.cx,
  36107. cy: layout.cy,
  36108. r0: layout.r0,
  36109. r: layout.r,
  36110. startAngle: layout.startAngle,
  36111. endAngle: layout.endAngle
  36112. };
  36113. }
  36114. };
  36115. function updateStyle(
  36116. el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar
  36117. ) {
  36118. var color = data.getItemVisual(dataIndex, 'color');
  36119. var opacity = data.getItemVisual(dataIndex, 'opacity');
  36120. var itemStyleModel = itemModel.getModel('itemStyle');
  36121. var hoverStyle = itemModel.getModel('emphasis.itemStyle').getBarItemStyle();
  36122. if (!isPolar) {
  36123. el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
  36124. }
  36125. el.useStyle(defaults(
  36126. {
  36127. fill: color,
  36128. opacity: opacity
  36129. },
  36130. itemStyleModel.getBarItemStyle()
  36131. ));
  36132. var cursorStyle = itemModel.getShallow('cursor');
  36133. cursorStyle && el.attr('cursor', cursorStyle);
  36134. var labelPositionOutside = isHorizontal
  36135. ? (layout.height > 0 ? 'bottom' : 'top')
  36136. : (layout.width > 0 ? 'left' : 'right');
  36137. if (!isPolar) {
  36138. setLabel(
  36139. el.style, hoverStyle, itemModel, color,
  36140. seriesModel, dataIndex, labelPositionOutside
  36141. );
  36142. }
  36143. setHoverStyle(el, hoverStyle);
  36144. }
  36145. // In case width or height are too small.
  36146. function getLineWidth(itemModel, rawLayout) {
  36147. var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;
  36148. return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));
  36149. }
  36150. var LargePath = Path.extend({
  36151. type: 'largeBar',
  36152. shape: {points: []},
  36153. buildPath: function (ctx, shape) {
  36154. // Drawing lines is more efficient than drawing
  36155. // a whole line or drawing rects.
  36156. var points = shape.points;
  36157. var startPoint = this.__startPoint;
  36158. var valueIdx = this.__valueIdx;
  36159. for (var i = 0; i < points.length; i += 2) {
  36160. startPoint[this.__valueIdx] = points[i + valueIdx];
  36161. ctx.moveTo(startPoint[0], startPoint[1]);
  36162. ctx.lineTo(points[i], points[i + 1]);
  36163. }
  36164. }
  36165. });
  36166. function createLarge(seriesModel, group, incremental) {
  36167. // TODO support polar
  36168. var data = seriesModel.getData();
  36169. var startPoint = [];
  36170. var valueIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;
  36171. startPoint[1 - valueIdx] = data.getLayout('valueAxisStart');
  36172. var el = new LargePath({
  36173. shape: {points: data.getLayout('largePoints')},
  36174. incremental: !!incremental,
  36175. __startPoint: startPoint,
  36176. __valueIdx: valueIdx
  36177. });
  36178. group.add(el);
  36179. setLargeStyle(el, seriesModel, data);
  36180. }
  36181. function setLargeStyle(el, seriesModel, data) {
  36182. var borderColor = data.getVisual('borderColor') || data.getVisual('color');
  36183. var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(['color', 'borderColor']);
  36184. el.useStyle(itemStyle);
  36185. el.style.fill = null;
  36186. el.style.stroke = borderColor;
  36187. el.style.lineWidth = data.getLayout('barWidth');
  36188. }
  36189. /*
  36190. * Licensed to the Apache Software Foundation (ASF) under one
  36191. * or more contributor license agreements. See the NOTICE file
  36192. * distributed with this work for additional information
  36193. * regarding copyright ownership. The ASF licenses this file
  36194. * to you under the Apache License, Version 2.0 (the
  36195. * "License"); you may not use this file except in compliance
  36196. * with the License. You may obtain a copy of the License at
  36197. *
  36198. * http://www.apache.org/licenses/LICENSE-2.0
  36199. *
  36200. * Unless required by applicable law or agreed to in writing,
  36201. * software distributed under the License is distributed on an
  36202. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36203. * KIND, either express or implied. See the License for the
  36204. * specific language governing permissions and limitations
  36205. * under the License.
  36206. */
  36207. // In case developer forget to include grid component
  36208. registerLayout(curry(layout, 'bar'));
  36209. // Should after normal bar layout, otherwise it is blocked by normal bar layout.
  36210. registerLayout(largeLayout);
  36211. registerVisual({
  36212. seriesType: 'bar',
  36213. reset: function (seriesModel) {
  36214. // Visual coding for legend
  36215. seriesModel.getData().setVisual('legendSymbol', 'roundRect');
  36216. }
  36217. });
  36218. /*
  36219. * Licensed to the Apache Software Foundation (ASF) under one
  36220. * or more contributor license agreements. See the NOTICE file
  36221. * distributed with this work for additional information
  36222. * regarding copyright ownership. The ASF licenses this file
  36223. * to you under the Apache License, Version 2.0 (the
  36224. * "License"); you may not use this file except in compliance
  36225. * with the License. You may obtain a copy of the License at
  36226. *
  36227. * http://www.apache.org/licenses/LICENSE-2.0
  36228. *
  36229. * Unless required by applicable law or agreed to in writing,
  36230. * software distributed under the License is distributed on an
  36231. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36232. * KIND, either express or implied. See the License for the
  36233. * specific language governing permissions and limitations
  36234. * under the License.
  36235. */
  36236. /**
  36237. * [Usage]:
  36238. * (1)
  36239. * createListSimply(seriesModel, ['value']);
  36240. * (2)
  36241. * createListSimply(seriesModel, {
  36242. * coordDimensions: ['value'],
  36243. * dimensionsCount: 5
  36244. * });
  36245. *
  36246. * @param {module:echarts/model/Series} seriesModel
  36247. * @param {Object|Array.<string|Object>} opt opt or coordDimensions
  36248. * The options in opt, see `echarts/data/helper/createDimensions`
  36249. * @param {Array.<string>} [nameList]
  36250. * @return {module:echarts/data/List}
  36251. */
  36252. var createListSimply = function (seriesModel, opt, nameList) {
  36253. opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt);
  36254. var source = seriesModel.getSource();
  36255. var dimensionsInfo = createDimensions(source, opt);
  36256. var list = new List(dimensionsInfo, seriesModel);
  36257. list.initData(source, nameList);
  36258. return list;
  36259. };
  36260. /*
  36261. * Licensed to the Apache Software Foundation (ASF) under one
  36262. * or more contributor license agreements. See the NOTICE file
  36263. * distributed with this work for additional information
  36264. * regarding copyright ownership. The ASF licenses this file
  36265. * to you under the Apache License, Version 2.0 (the
  36266. * "License"); you may not use this file except in compliance
  36267. * with the License. You may obtain a copy of the License at
  36268. *
  36269. * http://www.apache.org/licenses/LICENSE-2.0
  36270. *
  36271. * Unless required by applicable law or agreed to in writing,
  36272. * software distributed under the License is distributed on an
  36273. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36274. * KIND, either express or implied. See the License for the
  36275. * specific language governing permissions and limitations
  36276. * under the License.
  36277. */
  36278. /**
  36279. * Data selectable mixin for chart series.
  36280. * To eanble data select, option of series must have `selectedMode`.
  36281. * And each data item will use `selected` to toggle itself selected status
  36282. */
  36283. var dataSelectableMixin = {
  36284. /**
  36285. * @param {Array.<Object>} targetList [{name, value, selected}, ...]
  36286. * If targetList is an array, it should like [{name: ..., value: ...}, ...].
  36287. * If targetList is a "List", it must have coordDim: 'value' dimension and name.
  36288. */
  36289. updateSelectedMap: function (targetList) {
  36290. this._targetList = isArray(targetList) ? targetList.slice() : [];
  36291. this._selectTargetMap = reduce(targetList || [], function (targetMap, target) {
  36292. targetMap.set(target.name, target);
  36293. return targetMap;
  36294. }, createHashMap());
  36295. },
  36296. /**
  36297. * Either name or id should be passed as input here.
  36298. * If both of them are defined, id is used.
  36299. *
  36300. * @param {string|undefined} name name of data
  36301. * @param {number|undefined} id dataIndex of data
  36302. */
  36303. // PENGING If selectedMode is null ?
  36304. select: function (name, id) {
  36305. var target = id != null
  36306. ? this._targetList[id]
  36307. : this._selectTargetMap.get(name);
  36308. var selectedMode = this.get('selectedMode');
  36309. if (selectedMode === 'single') {
  36310. this._selectTargetMap.each(function (target) {
  36311. target.selected = false;
  36312. });
  36313. }
  36314. target && (target.selected = true);
  36315. },
  36316. /**
  36317. * Either name or id should be passed as input here.
  36318. * If both of them are defined, id is used.
  36319. *
  36320. * @param {string|undefined} name name of data
  36321. * @param {number|undefined} id dataIndex of data
  36322. */
  36323. unSelect: function (name, id) {
  36324. var target = id != null
  36325. ? this._targetList[id]
  36326. : this._selectTargetMap.get(name);
  36327. // var selectedMode = this.get('selectedMode');
  36328. // selectedMode !== 'single' && target && (target.selected = false);
  36329. target && (target.selected = false);
  36330. },
  36331. /**
  36332. * Either name or id should be passed as input here.
  36333. * If both of them are defined, id is used.
  36334. *
  36335. * @param {string|undefined} name name of data
  36336. * @param {number|undefined} id dataIndex of data
  36337. */
  36338. toggleSelected: function (name, id) {
  36339. var target = id != null
  36340. ? this._targetList[id]
  36341. : this._selectTargetMap.get(name);
  36342. if (target != null) {
  36343. this[target.selected ? 'unSelect' : 'select'](name, id);
  36344. return target.selected;
  36345. }
  36346. },
  36347. /**
  36348. * Either name or id should be passed as input here.
  36349. * If both of them are defined, id is used.
  36350. *
  36351. * @param {string|undefined} name name of data
  36352. * @param {number|undefined} id dataIndex of data
  36353. */
  36354. isSelected: function (name, id) {
  36355. var target = id != null
  36356. ? this._targetList[id]
  36357. : this._selectTargetMap.get(name);
  36358. return target && target.selected;
  36359. }
  36360. };
  36361. /*
  36362. * Licensed to the Apache Software Foundation (ASF) under one
  36363. * or more contributor license agreements. See the NOTICE file
  36364. * distributed with this work for additional information
  36365. * regarding copyright ownership. The ASF licenses this file
  36366. * to you under the Apache License, Version 2.0 (the
  36367. * "License"); you may not use this file except in compliance
  36368. * with the License. You may obtain a copy of the License at
  36369. *
  36370. * http://www.apache.org/licenses/LICENSE-2.0
  36371. *
  36372. * Unless required by applicable law or agreed to in writing,
  36373. * software distributed under the License is distributed on an
  36374. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36375. * KIND, either express or implied. See the License for the
  36376. * specific language governing permissions and limitations
  36377. * under the License.
  36378. */
  36379. var PieSeries = extendSeriesModel({
  36380. type: 'series.pie',
  36381. // Overwrite
  36382. init: function (option) {
  36383. PieSeries.superApply(this, 'init', arguments);
  36384. // Enable legend selection for each data item
  36385. // Use a function instead of direct access because data reference may changed
  36386. this.legendDataProvider = function () {
  36387. return this.getRawData();
  36388. };
  36389. this.updateSelectedMap(this._createSelectableList());
  36390. this._defaultLabelLine(option);
  36391. },
  36392. // Overwrite
  36393. mergeOption: function (newOption) {
  36394. PieSeries.superCall(this, 'mergeOption', newOption);
  36395. this.updateSelectedMap(this._createSelectableList());
  36396. },
  36397. getInitialData: function (option, ecModel) {
  36398. return createListSimply(this, ['value']);
  36399. },
  36400. _createSelectableList: function () {
  36401. var data = this.getRawData();
  36402. var valueDim = data.mapDimension('value');
  36403. var targetList = [];
  36404. for (var i = 0, len = data.count(); i < len; i++) {
  36405. targetList.push({
  36406. name: data.getName(i),
  36407. value: data.get(valueDim, i),
  36408. selected: retrieveRawAttr(data, i, 'selected')
  36409. });
  36410. }
  36411. return targetList;
  36412. },
  36413. // Overwrite
  36414. getDataParams: function (dataIndex) {
  36415. var data = this.getData();
  36416. var params = PieSeries.superCall(this, 'getDataParams', dataIndex);
  36417. // FIXME toFixed?
  36418. var valueList = [];
  36419. data.each(data.mapDimension('value'), function (value) {
  36420. valueList.push(value);
  36421. });
  36422. params.percent = getPercentWithPrecision(
  36423. valueList,
  36424. dataIndex,
  36425. data.hostModel.get('percentPrecision')
  36426. );
  36427. params.$vars.push('percent');
  36428. return params;
  36429. },
  36430. _defaultLabelLine: function (option) {
  36431. // Extend labelLine emphasis
  36432. defaultEmphasis(option, 'labelLine', ['show']);
  36433. var labelLineNormalOpt = option.labelLine;
  36434. var labelLineEmphasisOpt = option.emphasis.labelLine;
  36435. // Not show label line if `label.normal.show = false`
  36436. labelLineNormalOpt.show = labelLineNormalOpt.show
  36437. && option.label.show;
  36438. labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
  36439. && option.emphasis.label.show;
  36440. },
  36441. defaultOption: {
  36442. zlevel: 0,
  36443. z: 2,
  36444. legendHoverLink: true,
  36445. hoverAnimation: true,
  36446. // 默认全局居中
  36447. center: ['50%', '50%'],
  36448. radius: [0, '75%'],
  36449. // 默认顺时针
  36450. clockwise: true,
  36451. startAngle: 90,
  36452. // 最小角度改为0
  36453. minAngle: 0,
  36454. // 选中时扇区偏移量
  36455. selectedOffset: 10,
  36456. // 高亮扇区偏移量
  36457. hoverOffset: 10,
  36458. // If use strategy to avoid label overlapping
  36459. avoidLabelOverlap: true,
  36460. // 选择模式,默认关闭,可选single,multiple
  36461. // selectedMode: false,
  36462. // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
  36463. // roseType: null,
  36464. percentPrecision: 2,
  36465. // If still show when all data zero.
  36466. stillShowZeroSum: true,
  36467. // cursor: null,
  36468. label: {
  36469. // If rotate around circle
  36470. rotate: false,
  36471. show: true,
  36472. // 'outer', 'inside', 'center'
  36473. position: 'outer'
  36474. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  36475. // 默认使用全局文本样式,详见TEXTSTYLE
  36476. // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
  36477. },
  36478. // Enabled when label.normal.position is 'outer'
  36479. labelLine: {
  36480. show: true,
  36481. // 引导线两段中的第一段长度
  36482. length: 15,
  36483. // 引导线两段中的第二段长度
  36484. length2: 15,
  36485. smooth: false,
  36486. lineStyle: {
  36487. // color: 各异,
  36488. width: 1,
  36489. type: 'solid'
  36490. }
  36491. },
  36492. itemStyle: {
  36493. borderWidth: 1
  36494. },
  36495. // Animation type canbe expansion, scale
  36496. animationType: 'expansion',
  36497. animationEasing: 'cubicOut'
  36498. }
  36499. });
  36500. mixin(PieSeries, dataSelectableMixin);
  36501. /*
  36502. * Licensed to the Apache Software Foundation (ASF) under one
  36503. * or more contributor license agreements. See the NOTICE file
  36504. * distributed with this work for additional information
  36505. * regarding copyright ownership. The ASF licenses this file
  36506. * to you under the Apache License, Version 2.0 (the
  36507. * "License"); you may not use this file except in compliance
  36508. * with the License. You may obtain a copy of the License at
  36509. *
  36510. * http://www.apache.org/licenses/LICENSE-2.0
  36511. *
  36512. * Unless required by applicable law or agreed to in writing,
  36513. * software distributed under the License is distributed on an
  36514. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36515. * KIND, either express or implied. See the License for the
  36516. * specific language governing permissions and limitations
  36517. * under the License.
  36518. */
  36519. /**
  36520. * @param {module:echarts/model/Series} seriesModel
  36521. * @param {boolean} hasAnimation
  36522. * @inner
  36523. */
  36524. function updateDataSelected(uid, seriesModel, hasAnimation, api) {
  36525. var data = seriesModel.getData();
  36526. var dataIndex = this.dataIndex;
  36527. var name = data.getName(dataIndex);
  36528. var selectedOffset = seriesModel.get('selectedOffset');
  36529. api.dispatchAction({
  36530. type: 'pieToggleSelect',
  36531. from: uid,
  36532. name: name,
  36533. seriesId: seriesModel.id
  36534. });
  36535. data.each(function (idx) {
  36536. toggleItemSelected(
  36537. data.getItemGraphicEl(idx),
  36538. data.getItemLayout(idx),
  36539. seriesModel.isSelected(data.getName(idx)),
  36540. selectedOffset,
  36541. hasAnimation
  36542. );
  36543. });
  36544. }
  36545. /**
  36546. * @param {module:zrender/graphic/Sector} el
  36547. * @param {Object} layout
  36548. * @param {boolean} isSelected
  36549. * @param {number} selectedOffset
  36550. * @param {boolean} hasAnimation
  36551. * @inner
  36552. */
  36553. function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {
  36554. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  36555. var dx = Math.cos(midAngle);
  36556. var dy = Math.sin(midAngle);
  36557. var offset = isSelected ? selectedOffset : 0;
  36558. var position = [dx * offset, dy * offset];
  36559. hasAnimation
  36560. // animateTo will stop revious animation like update transition
  36561. ? el.animate()
  36562. .when(200, {
  36563. position: position
  36564. })
  36565. .start('bounceOut')
  36566. : el.attr('position', position);
  36567. }
  36568. /**
  36569. * Piece of pie including Sector, Label, LabelLine
  36570. * @constructor
  36571. * @extends {module:zrender/graphic/Group}
  36572. */
  36573. function PiePiece(data, idx) {
  36574. Group.call(this);
  36575. var sector = new Sector({
  36576. z2: 2
  36577. });
  36578. var polyline = new Polyline();
  36579. var text = new Text();
  36580. this.add(sector);
  36581. this.add(polyline);
  36582. this.add(text);
  36583. this.updateData(data, idx, true);
  36584. // Hover to change label and labelLine
  36585. function onEmphasis() {
  36586. polyline.ignore = polyline.hoverIgnore;
  36587. text.ignore = text.hoverIgnore;
  36588. }
  36589. function onNormal() {
  36590. polyline.ignore = polyline.normalIgnore;
  36591. text.ignore = text.normalIgnore;
  36592. }
  36593. this.on('emphasis', onEmphasis)
  36594. .on('normal', onNormal)
  36595. .on('mouseover', onEmphasis)
  36596. .on('mouseout', onNormal);
  36597. }
  36598. var piePieceProto = PiePiece.prototype;
  36599. piePieceProto.updateData = function (data, idx, firstCreate) {
  36600. var sector = this.childAt(0);
  36601. var seriesModel = data.hostModel;
  36602. var itemModel = data.getItemModel(idx);
  36603. var layout = data.getItemLayout(idx);
  36604. var sectorShape = extend({}, layout);
  36605. sectorShape.label = null;
  36606. if (firstCreate) {
  36607. sector.setShape(sectorShape);
  36608. var animationType = seriesModel.getShallow('animationType');
  36609. if (animationType === 'scale') {
  36610. sector.shape.r = layout.r0;
  36611. initProps(sector, {
  36612. shape: {
  36613. r: layout.r
  36614. }
  36615. }, seriesModel, idx);
  36616. }
  36617. // Expansion
  36618. else {
  36619. sector.shape.endAngle = layout.startAngle;
  36620. updateProps(sector, {
  36621. shape: {
  36622. endAngle: layout.endAngle
  36623. }
  36624. }, seriesModel, idx);
  36625. }
  36626. }
  36627. else {
  36628. updateProps(sector, {
  36629. shape: sectorShape
  36630. }, seriesModel, idx);
  36631. }
  36632. // Update common style
  36633. var visualColor = data.getItemVisual(idx, 'color');
  36634. sector.useStyle(
  36635. defaults(
  36636. {
  36637. lineJoin: 'bevel',
  36638. fill: visualColor
  36639. },
  36640. itemModel.getModel('itemStyle').getItemStyle()
  36641. )
  36642. );
  36643. sector.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
  36644. var cursorStyle = itemModel.getShallow('cursor');
  36645. cursorStyle && sector.attr('cursor', cursorStyle);
  36646. // Toggle selected
  36647. toggleItemSelected(
  36648. this,
  36649. data.getItemLayout(idx),
  36650. seriesModel.isSelected(null, idx),
  36651. seriesModel.get('selectedOffset'),
  36652. seriesModel.get('animation')
  36653. );
  36654. function onEmphasis() {
  36655. // Sector may has animation of updating data. Force to move to the last frame
  36656. // Or it may stopped on the wrong shape
  36657. sector.stopAnimation(true);
  36658. sector.animateTo({
  36659. shape: {
  36660. r: layout.r + seriesModel.get('hoverOffset')
  36661. }
  36662. }, 300, 'elasticOut');
  36663. }
  36664. function onNormal() {
  36665. sector.stopAnimation(true);
  36666. sector.animateTo({
  36667. shape: {
  36668. r: layout.r
  36669. }
  36670. }, 300, 'elasticOut');
  36671. }
  36672. sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
  36673. if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {
  36674. sector
  36675. .on('mouseover', onEmphasis)
  36676. .on('mouseout', onNormal)
  36677. .on('emphasis', onEmphasis)
  36678. .on('normal', onNormal);
  36679. }
  36680. this._updateLabel(data, idx);
  36681. setHoverStyle(this);
  36682. };
  36683. piePieceProto._updateLabel = function (data, idx) {
  36684. var labelLine = this.childAt(1);
  36685. var labelText = this.childAt(2);
  36686. var seriesModel = data.hostModel;
  36687. var itemModel = data.getItemModel(idx);
  36688. var layout = data.getItemLayout(idx);
  36689. var labelLayout = layout.label;
  36690. var visualColor = data.getItemVisual(idx, 'color');
  36691. updateProps(labelLine, {
  36692. shape: {
  36693. points: labelLayout.linePoints || [
  36694. [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]
  36695. ]
  36696. }
  36697. }, seriesModel, idx);
  36698. updateProps(labelText, {
  36699. style: {
  36700. x: labelLayout.x,
  36701. y: labelLayout.y
  36702. }
  36703. }, seriesModel, idx);
  36704. labelText.attr({
  36705. rotation: labelLayout.rotation,
  36706. origin: [labelLayout.x, labelLayout.y],
  36707. z2: 10
  36708. });
  36709. var labelModel = itemModel.getModel('label');
  36710. var labelHoverModel = itemModel.getModel('emphasis.label');
  36711. var labelLineModel = itemModel.getModel('labelLine');
  36712. var labelLineHoverModel = itemModel.getModel('emphasis.labelLine');
  36713. var visualColor = data.getItemVisual(idx, 'color');
  36714. setLabelStyle(
  36715. labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,
  36716. {
  36717. labelFetcher: data.hostModel,
  36718. labelDataIndex: idx,
  36719. defaultText: data.getName(idx),
  36720. autoColor: visualColor,
  36721. useInsideStyle: !!labelLayout.inside
  36722. },
  36723. {
  36724. textAlign: labelLayout.textAlign,
  36725. textVerticalAlign: labelLayout.verticalAlign,
  36726. opacity: data.getItemVisual(idx, 'opacity')
  36727. }
  36728. );
  36729. labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
  36730. labelText.hoverIgnore = !labelHoverModel.get('show');
  36731. labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
  36732. labelLine.hoverIgnore = !labelLineHoverModel.get('show');
  36733. // Default use item visual color
  36734. labelLine.setStyle({
  36735. stroke: visualColor,
  36736. opacity: data.getItemVisual(idx, 'opacity')
  36737. });
  36738. labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
  36739. labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
  36740. var smooth = labelLineModel.get('smooth');
  36741. if (smooth && smooth === true) {
  36742. smooth = 0.4;
  36743. }
  36744. labelLine.setShape({
  36745. smooth: smooth
  36746. });
  36747. };
  36748. inherits(PiePiece, Group);
  36749. // Pie view
  36750. var PieView = Chart.extend({
  36751. type: 'pie',
  36752. init: function () {
  36753. var sectorGroup = new Group();
  36754. this._sectorGroup = sectorGroup;
  36755. },
  36756. render: function (seriesModel, ecModel, api, payload) {
  36757. if (payload && (payload.from === this.uid)) {
  36758. return;
  36759. }
  36760. var data = seriesModel.getData();
  36761. var oldData = this._data;
  36762. var group = this.group;
  36763. var hasAnimation = ecModel.get('animation');
  36764. var isFirstRender = !oldData;
  36765. var animationType = seriesModel.get('animationType');
  36766. var onSectorClick = curry(
  36767. updateDataSelected, this.uid, seriesModel, hasAnimation, api
  36768. );
  36769. var selectedMode = seriesModel.get('selectedMode');
  36770. data.diff(oldData)
  36771. .add(function (idx) {
  36772. var piePiece = new PiePiece(data, idx);
  36773. // Default expansion animation
  36774. if (isFirstRender && animationType !== 'scale') {
  36775. piePiece.eachChild(function (child) {
  36776. child.stopAnimation(true);
  36777. });
  36778. }
  36779. selectedMode && piePiece.on('click', onSectorClick);
  36780. data.setItemGraphicEl(idx, piePiece);
  36781. group.add(piePiece);
  36782. })
  36783. .update(function (newIdx, oldIdx) {
  36784. var piePiece = oldData.getItemGraphicEl(oldIdx);
  36785. piePiece.updateData(data, newIdx);
  36786. piePiece.off('click');
  36787. selectedMode && piePiece.on('click', onSectorClick);
  36788. group.add(piePiece);
  36789. data.setItemGraphicEl(newIdx, piePiece);
  36790. })
  36791. .remove(function (idx) {
  36792. var piePiece = oldData.getItemGraphicEl(idx);
  36793. group.remove(piePiece);
  36794. })
  36795. .execute();
  36796. if (
  36797. hasAnimation && isFirstRender && data.count() > 0
  36798. // Default expansion animation
  36799. && animationType !== 'scale'
  36800. ) {
  36801. var shape = data.getItemLayout(0);
  36802. var r = Math.max(api.getWidth(), api.getHeight()) / 2;
  36803. var removeClipPath = bind(group.removeClipPath, group);
  36804. group.setClipPath(this._createClipPath(
  36805. shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel
  36806. ));
  36807. }
  36808. else {
  36809. // clipPath is used in first-time animation, so remove it when otherwise. See: #8994
  36810. group.removeClipPath();
  36811. }
  36812. this._data = data;
  36813. },
  36814. dispose: function () {},
  36815. _createClipPath: function (
  36816. cx, cy, r, startAngle, clockwise, cb, seriesModel
  36817. ) {
  36818. var clipPath = new Sector({
  36819. shape: {
  36820. cx: cx,
  36821. cy: cy,
  36822. r0: 0,
  36823. r: r,
  36824. startAngle: startAngle,
  36825. endAngle: startAngle,
  36826. clockwise: clockwise
  36827. }
  36828. });
  36829. initProps(clipPath, {
  36830. shape: {
  36831. endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2
  36832. }
  36833. }, seriesModel, cb);
  36834. return clipPath;
  36835. },
  36836. /**
  36837. * @implement
  36838. */
  36839. containPoint: function (point, seriesModel) {
  36840. var data = seriesModel.getData();
  36841. var itemLayout = data.getItemLayout(0);
  36842. if (itemLayout) {
  36843. var dx = point[0] - itemLayout.cx;
  36844. var dy = point[1] - itemLayout.cy;
  36845. var radius = Math.sqrt(dx * dx + dy * dy);
  36846. return radius <= itemLayout.r && radius >= itemLayout.r0;
  36847. }
  36848. }
  36849. });
  36850. /*
  36851. * Licensed to the Apache Software Foundation (ASF) under one
  36852. * or more contributor license agreements. See the NOTICE file
  36853. * distributed with this work for additional information
  36854. * regarding copyright ownership. The ASF licenses this file
  36855. * to you under the Apache License, Version 2.0 (the
  36856. * "License"); you may not use this file except in compliance
  36857. * with the License. You may obtain a copy of the License at
  36858. *
  36859. * http://www.apache.org/licenses/LICENSE-2.0
  36860. *
  36861. * Unless required by applicable law or agreed to in writing,
  36862. * software distributed under the License is distributed on an
  36863. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36864. * KIND, either express or implied. See the License for the
  36865. * specific language governing permissions and limitations
  36866. * under the License.
  36867. */
  36868. var createDataSelectAction = function (seriesType, actionInfos) {
  36869. each$1(actionInfos, function (actionInfo) {
  36870. actionInfo.update = 'updateView';
  36871. /**
  36872. * @payload
  36873. * @property {string} seriesName
  36874. * @property {string} name
  36875. */
  36876. registerAction(actionInfo, function (payload, ecModel) {
  36877. var selected = {};
  36878. ecModel.eachComponent(
  36879. {mainType: 'series', subType: seriesType, query: payload},
  36880. function (seriesModel) {
  36881. if (seriesModel[actionInfo.method]) {
  36882. seriesModel[actionInfo.method](
  36883. payload.name,
  36884. payload.dataIndex
  36885. );
  36886. }
  36887. var data = seriesModel.getData();
  36888. // Create selected map
  36889. data.each(function (idx) {
  36890. var name = data.getName(idx);
  36891. selected[name] = seriesModel.isSelected(name)
  36892. || false;
  36893. });
  36894. }
  36895. );
  36896. return {
  36897. name: payload.name,
  36898. selected: selected
  36899. };
  36900. });
  36901. });
  36902. };
  36903. /*
  36904. * Licensed to the Apache Software Foundation (ASF) under one
  36905. * or more contributor license agreements. See the NOTICE file
  36906. * distributed with this work for additional information
  36907. * regarding copyright ownership. The ASF licenses this file
  36908. * to you under the Apache License, Version 2.0 (the
  36909. * "License"); you may not use this file except in compliance
  36910. * with the License. You may obtain a copy of the License at
  36911. *
  36912. * http://www.apache.org/licenses/LICENSE-2.0
  36913. *
  36914. * Unless required by applicable law or agreed to in writing,
  36915. * software distributed under the License is distributed on an
  36916. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36917. * KIND, either express or implied. See the License for the
  36918. * specific language governing permissions and limitations
  36919. * under the License.
  36920. */
  36921. // Pick color from palette for each data item.
  36922. // Applicable for charts that require applying color palette
  36923. // in data level (like pie, funnel, chord).
  36924. var dataColor = function (seriesType) {
  36925. return {
  36926. getTargetSeries: function (ecModel) {
  36927. // Pie and funnel may use diferrent scope
  36928. var paletteScope = {};
  36929. var seiresModelMap = createHashMap();
  36930. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  36931. seriesModel.__paletteScope = paletteScope;
  36932. seiresModelMap.set(seriesModel.uid, seriesModel);
  36933. });
  36934. return seiresModelMap;
  36935. },
  36936. reset: function (seriesModel, ecModel) {
  36937. var dataAll = seriesModel.getRawData();
  36938. var idxMap = {};
  36939. var data = seriesModel.getData();
  36940. data.each(function (idx) {
  36941. var rawIdx = data.getRawIndex(idx);
  36942. idxMap[rawIdx] = idx;
  36943. });
  36944. dataAll.each(function (rawIdx) {
  36945. var filteredIdx = idxMap[rawIdx];
  36946. // If series.itemStyle.normal.color is a function. itemVisual may be encoded
  36947. var singleDataColor = filteredIdx != null
  36948. && data.getItemVisual(filteredIdx, 'color', true);
  36949. if (!singleDataColor) {
  36950. // FIXME Performance
  36951. var itemModel = dataAll.getItemModel(rawIdx);
  36952. var color = itemModel.get('itemStyle.color')
  36953. || seriesModel.getColorFromPalette(
  36954. dataAll.getName(rawIdx) || (rawIdx + ''), seriesModel.__paletteScope,
  36955. dataAll.count()
  36956. );
  36957. // Legend may use the visual info in data before processed
  36958. dataAll.setItemVisual(rawIdx, 'color', color);
  36959. // Data is not filtered
  36960. if (filteredIdx != null) {
  36961. data.setItemVisual(filteredIdx, 'color', color);
  36962. }
  36963. }
  36964. else {
  36965. // Set data all color for legend
  36966. dataAll.setItemVisual(rawIdx, 'color', singleDataColor);
  36967. }
  36968. });
  36969. }
  36970. };
  36971. };
  36972. /*
  36973. * Licensed to the Apache Software Foundation (ASF) under one
  36974. * or more contributor license agreements. See the NOTICE file
  36975. * distributed with this work for additional information
  36976. * regarding copyright ownership. The ASF licenses this file
  36977. * to you under the Apache License, Version 2.0 (the
  36978. * "License"); you may not use this file except in compliance
  36979. * with the License. You may obtain a copy of the License at
  36980. *
  36981. * http://www.apache.org/licenses/LICENSE-2.0
  36982. *
  36983. * Unless required by applicable law or agreed to in writing,
  36984. * software distributed under the License is distributed on an
  36985. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36986. * KIND, either express or implied. See the License for the
  36987. * specific language governing permissions and limitations
  36988. * under the License.
  36989. */
  36990. // FIXME emphasis label position is not same with normal label position
  36991. function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
  36992. list.sort(function (a, b) {
  36993. return a.y - b.y;
  36994. });
  36995. function shiftDown(start, end, delta, dir) {
  36996. for (var j = start; j < end; j++) {
  36997. list[j].y += delta;
  36998. if (j > start
  36999. && j + 1 < end
  37000. && list[j + 1].y > list[j].y + list[j].height
  37001. ) {
  37002. shiftUp(j, delta / 2);
  37003. return;
  37004. }
  37005. }
  37006. shiftUp(end - 1, delta / 2);
  37007. }
  37008. function shiftUp(end, delta) {
  37009. for (var j = end; j >= 0; j--) {
  37010. list[j].y -= delta;
  37011. if (j > 0
  37012. && list[j].y > list[j - 1].y + list[j - 1].height
  37013. ) {
  37014. break;
  37015. }
  37016. }
  37017. }
  37018. function changeX(list, isDownList, cx, cy, r, dir) {
  37019. var lastDeltaX = dir > 0
  37020. ? isDownList // right-side
  37021. ? Number.MAX_VALUE // down
  37022. : 0 // up
  37023. : isDownList // left-side
  37024. ? Number.MAX_VALUE // down
  37025. : 0; // up
  37026. for (var i = 0, l = list.length; i < l; i++) {
  37027. var deltaY = Math.abs(list[i].y - cy);
  37028. var length = list[i].len;
  37029. var length2 = list[i].len2;
  37030. var deltaX = (deltaY < r + length)
  37031. ? Math.sqrt(
  37032. (r + length + length2) * (r + length + length2)
  37033. - deltaY * deltaY
  37034. )
  37035. : Math.abs(list[i].x - cx);
  37036. if (isDownList && deltaX >= lastDeltaX) {
  37037. // right-down, left-down
  37038. deltaX = lastDeltaX - 10;
  37039. }
  37040. if (!isDownList && deltaX <= lastDeltaX) {
  37041. // right-up, left-up
  37042. deltaX = lastDeltaX + 10;
  37043. }
  37044. list[i].x = cx + deltaX * dir;
  37045. lastDeltaX = deltaX;
  37046. }
  37047. }
  37048. var lastY = 0;
  37049. var delta;
  37050. var len = list.length;
  37051. var upList = [];
  37052. var downList = [];
  37053. for (var i = 0; i < len; i++) {
  37054. delta = list[i].y - lastY;
  37055. if (delta < 0) {
  37056. shiftDown(i, len, -delta, dir);
  37057. }
  37058. lastY = list[i].y + list[i].height;
  37059. }
  37060. if (viewHeight - lastY < 0) {
  37061. shiftUp(len - 1, lastY - viewHeight);
  37062. }
  37063. for (var i = 0; i < len; i++) {
  37064. if (list[i].y >= cy) {
  37065. downList.push(list[i]);
  37066. }
  37067. else {
  37068. upList.push(list[i]);
  37069. }
  37070. }
  37071. changeX(upList, false, cx, cy, r, dir);
  37072. changeX(downList, true, cx, cy, r, dir);
  37073. }
  37074. function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
  37075. var leftList = [];
  37076. var rightList = [];
  37077. for (var i = 0; i < labelLayoutList.length; i++) {
  37078. if (isPositionCenter(labelLayoutList[i])) {
  37079. continue;
  37080. }
  37081. if (labelLayoutList[i].x < cx) {
  37082. leftList.push(labelLayoutList[i]);
  37083. }
  37084. else {
  37085. rightList.push(labelLayoutList[i]);
  37086. }
  37087. }
  37088. adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
  37089. adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
  37090. for (var i = 0; i < labelLayoutList.length; i++) {
  37091. if (isPositionCenter(labelLayoutList[i])) {
  37092. continue;
  37093. }
  37094. var linePoints = labelLayoutList[i].linePoints;
  37095. if (linePoints) {
  37096. var dist = linePoints[1][0] - linePoints[2][0];
  37097. if (labelLayoutList[i].x < cx) {
  37098. linePoints[2][0] = labelLayoutList[i].x + 3;
  37099. }
  37100. else {
  37101. linePoints[2][0] = labelLayoutList[i].x - 3;
  37102. }
  37103. linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
  37104. linePoints[1][0] = linePoints[2][0] + dist;
  37105. }
  37106. }
  37107. }
  37108. function isPositionCenter(layout) {
  37109. // Not change x for center label
  37110. return layout.position === 'center';
  37111. }
  37112. var labelLayout = function (seriesModel, r, viewWidth, viewHeight) {
  37113. var data = seriesModel.getData();
  37114. var labelLayoutList = [];
  37115. var cx;
  37116. var cy;
  37117. var hasLabelRotate = false;
  37118. data.each(function (idx) {
  37119. var layout = data.getItemLayout(idx);
  37120. var itemModel = data.getItemModel(idx);
  37121. var labelModel = itemModel.getModel('label');
  37122. // Use position in normal or emphasis
  37123. var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position');
  37124. var labelLineModel = itemModel.getModel('labelLine');
  37125. var labelLineLen = labelLineModel.get('length');
  37126. var labelLineLen2 = labelLineModel.get('length2');
  37127. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  37128. var dx = Math.cos(midAngle);
  37129. var dy = Math.sin(midAngle);
  37130. var textX;
  37131. var textY;
  37132. var linePoints;
  37133. var textAlign;
  37134. cx = layout.cx;
  37135. cy = layout.cy;
  37136. var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
  37137. if (labelPosition === 'center') {
  37138. textX = layout.cx;
  37139. textY = layout.cy;
  37140. textAlign = 'center';
  37141. }
  37142. else {
  37143. var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx;
  37144. var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy;
  37145. textX = x1 + dx * 3;
  37146. textY = y1 + dy * 3;
  37147. if (!isLabelInside) {
  37148. // For roseType
  37149. var x2 = x1 + dx * (labelLineLen + r - layout.r);
  37150. var y2 = y1 + dy * (labelLineLen + r - layout.r);
  37151. var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
  37152. var y3 = y2;
  37153. textX = x3 + (dx < 0 ? -5 : 5);
  37154. textY = y3;
  37155. linePoints = [[x1, y1], [x2, y2], [x3, y3]];
  37156. }
  37157. textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
  37158. }
  37159. var font = labelModel.getFont();
  37160. var labelRotate = labelModel.get('rotate')
  37161. ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0;
  37162. var text = seriesModel.getFormattedLabel(idx, 'normal')
  37163. || data.getName(idx);
  37164. var textRect = getBoundingRect(
  37165. text, font, textAlign, 'top'
  37166. );
  37167. hasLabelRotate = !!labelRotate;
  37168. layout.label = {
  37169. x: textX,
  37170. y: textY,
  37171. position: labelPosition,
  37172. height: textRect.height,
  37173. len: labelLineLen,
  37174. len2: labelLineLen2,
  37175. linePoints: linePoints,
  37176. textAlign: textAlign,
  37177. verticalAlign: 'middle',
  37178. rotation: labelRotate,
  37179. inside: isLabelInside
  37180. };
  37181. // Not layout the inside label
  37182. if (!isLabelInside) {
  37183. labelLayoutList.push(layout.label);
  37184. }
  37185. });
  37186. if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
  37187. avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
  37188. }
  37189. };
  37190. /*
  37191. * Licensed to the Apache Software Foundation (ASF) under one
  37192. * or more contributor license agreements. See the NOTICE file
  37193. * distributed with this work for additional information
  37194. * regarding copyright ownership. The ASF licenses this file
  37195. * to you under the Apache License, Version 2.0 (the
  37196. * "License"); you may not use this file except in compliance
  37197. * with the License. You may obtain a copy of the License at
  37198. *
  37199. * http://www.apache.org/licenses/LICENSE-2.0
  37200. *
  37201. * Unless required by applicable law or agreed to in writing,
  37202. * software distributed under the License is distributed on an
  37203. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37204. * KIND, either express or implied. See the License for the
  37205. * specific language governing permissions and limitations
  37206. * under the License.
  37207. */
  37208. var PI2$4 = Math.PI * 2;
  37209. var RADIAN = Math.PI / 180;
  37210. var pieLayout = function (seriesType, ecModel, api, payload) {
  37211. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  37212. var data = seriesModel.getData();
  37213. var valueDim = data.mapDimension('value');
  37214. var center = seriesModel.get('center');
  37215. var radius = seriesModel.get('radius');
  37216. if (!isArray(radius)) {
  37217. radius = [0, radius];
  37218. }
  37219. if (!isArray(center)) {
  37220. center = [center, center];
  37221. }
  37222. var width = api.getWidth();
  37223. var height = api.getHeight();
  37224. var size = Math.min(width, height);
  37225. var cx = parsePercent$1(center[0], width);
  37226. var cy = parsePercent$1(center[1], height);
  37227. var r0 = parsePercent$1(radius[0], size / 2);
  37228. var r = parsePercent$1(radius[1], size / 2);
  37229. var startAngle = -seriesModel.get('startAngle') * RADIAN;
  37230. var minAngle = seriesModel.get('minAngle') * RADIAN;
  37231. var validDataCount = 0;
  37232. data.each(valueDim, function (value) {
  37233. !isNaN(value) && validDataCount++;
  37234. });
  37235. var sum = data.getSum(valueDim);
  37236. // Sum may be 0
  37237. var unitRadian = Math.PI / (sum || validDataCount) * 2;
  37238. var clockwise = seriesModel.get('clockwise');
  37239. var roseType = seriesModel.get('roseType');
  37240. var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
  37241. // [0...max]
  37242. var extent = data.getDataExtent(valueDim);
  37243. extent[0] = 0;
  37244. // In the case some sector angle is smaller than minAngle
  37245. var restAngle = PI2$4;
  37246. var valueSumLargerThanMinAngle = 0;
  37247. var currentAngle = startAngle;
  37248. var dir = clockwise ? 1 : -1;
  37249. data.each(valueDim, function (value, idx) {
  37250. var angle;
  37251. if (isNaN(value)) {
  37252. data.setItemLayout(idx, {
  37253. angle: NaN,
  37254. startAngle: NaN,
  37255. endAngle: NaN,
  37256. clockwise: clockwise,
  37257. cx: cx,
  37258. cy: cy,
  37259. r0: r0,
  37260. r: roseType
  37261. ? NaN
  37262. : r
  37263. });
  37264. return;
  37265. }
  37266. // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
  37267. if (roseType !== 'area') {
  37268. angle = (sum === 0 && stillShowZeroSum)
  37269. ? unitRadian : (value * unitRadian);
  37270. }
  37271. else {
  37272. angle = PI2$4 / validDataCount;
  37273. }
  37274. if (angle < minAngle) {
  37275. angle = minAngle;
  37276. restAngle -= minAngle;
  37277. }
  37278. else {
  37279. valueSumLargerThanMinAngle += value;
  37280. }
  37281. var endAngle = currentAngle + dir * angle;
  37282. data.setItemLayout(idx, {
  37283. angle: angle,
  37284. startAngle: currentAngle,
  37285. endAngle: endAngle,
  37286. clockwise: clockwise,
  37287. cx: cx,
  37288. cy: cy,
  37289. r0: r0,
  37290. r: roseType
  37291. ? linearMap(value, extent, [r0, r])
  37292. : r
  37293. });
  37294. currentAngle = endAngle;
  37295. });
  37296. // Some sector is constrained by minAngle
  37297. // Rest sectors needs recalculate angle
  37298. if (restAngle < PI2$4 && validDataCount) {
  37299. // Average the angle if rest angle is not enough after all angles is
  37300. // Constrained by minAngle
  37301. if (restAngle <= 1e-3) {
  37302. var angle = PI2$4 / validDataCount;
  37303. data.each(valueDim, function (value, idx) {
  37304. if (!isNaN(value)) {
  37305. var layout = data.getItemLayout(idx);
  37306. layout.angle = angle;
  37307. layout.startAngle = startAngle + dir * idx * angle;
  37308. layout.endAngle = startAngle + dir * (idx + 1) * angle;
  37309. }
  37310. });
  37311. }
  37312. else {
  37313. unitRadian = restAngle / valueSumLargerThanMinAngle;
  37314. currentAngle = startAngle;
  37315. data.each(valueDim, function (value, idx) {
  37316. if (!isNaN(value)) {
  37317. var layout = data.getItemLayout(idx);
  37318. var angle = layout.angle === minAngle
  37319. ? minAngle : value * unitRadian;
  37320. layout.startAngle = currentAngle;
  37321. layout.endAngle = currentAngle + dir * angle;
  37322. currentAngle += dir * angle;
  37323. }
  37324. });
  37325. }
  37326. }
  37327. labelLayout(seriesModel, r, width, height);
  37328. });
  37329. };
  37330. /*
  37331. * Licensed to the Apache Software Foundation (ASF) under one
  37332. * or more contributor license agreements. See the NOTICE file
  37333. * distributed with this work for additional information
  37334. * regarding copyright ownership. The ASF licenses this file
  37335. * to you under the Apache License, Version 2.0 (the
  37336. * "License"); you may not use this file except in compliance
  37337. * with the License. You may obtain a copy of the License at
  37338. *
  37339. * http://www.apache.org/licenses/LICENSE-2.0
  37340. *
  37341. * Unless required by applicable law or agreed to in writing,
  37342. * software distributed under the License is distributed on an
  37343. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37344. * KIND, either express or implied. See the License for the
  37345. * specific language governing permissions and limitations
  37346. * under the License.
  37347. */
  37348. var dataFilter = function (seriesType) {
  37349. return {
  37350. seriesType: seriesType,
  37351. reset: function (seriesModel, ecModel) {
  37352. var legendModels = ecModel.findComponents({
  37353. mainType: 'legend'
  37354. });
  37355. if (!legendModels || !legendModels.length) {
  37356. return;
  37357. }
  37358. var data = seriesModel.getData();
  37359. data.filterSelf(function (idx) {
  37360. var name = data.getName(idx);
  37361. // If in any legend component the status is not selected.
  37362. for (var i = 0; i < legendModels.length; i++) {
  37363. if (!legendModels[i].isSelected(name)) {
  37364. return false;
  37365. }
  37366. }
  37367. return true;
  37368. });
  37369. }
  37370. };
  37371. };
  37372. /*
  37373. * Licensed to the Apache Software Foundation (ASF) under one
  37374. * or more contributor license agreements. See the NOTICE file
  37375. * distributed with this work for additional information
  37376. * regarding copyright ownership. The ASF licenses this file
  37377. * to you under the Apache License, Version 2.0 (the
  37378. * "License"); you may not use this file except in compliance
  37379. * with the License. You may obtain a copy of the License at
  37380. *
  37381. * http://www.apache.org/licenses/LICENSE-2.0
  37382. *
  37383. * Unless required by applicable law or agreed to in writing,
  37384. * software distributed under the License is distributed on an
  37385. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37386. * KIND, either express or implied. See the License for the
  37387. * specific language governing permissions and limitations
  37388. * under the License.
  37389. */
  37390. createDataSelectAction('pie', [{
  37391. type: 'pieToggleSelect',
  37392. event: 'pieselectchanged',
  37393. method: 'toggleSelected'
  37394. }, {
  37395. type: 'pieSelect',
  37396. event: 'pieselected',
  37397. method: 'select'
  37398. }, {
  37399. type: 'pieUnSelect',
  37400. event: 'pieunselected',
  37401. method: 'unSelect'
  37402. }]);
  37403. registerVisual(dataColor('pie'));
  37404. registerLayout(curry(pieLayout, 'pie'));
  37405. registerProcessor(dataFilter('pie'));
  37406. /*
  37407. * Licensed to the Apache Software Foundation (ASF) under one
  37408. * or more contributor license agreements. See the NOTICE file
  37409. * distributed with this work for additional information
  37410. * regarding copyright ownership. The ASF licenses this file
  37411. * to you under the Apache License, Version 2.0 (the
  37412. * "License"); you may not use this file except in compliance
  37413. * with the License. You may obtain a copy of the License at
  37414. *
  37415. * http://www.apache.org/licenses/LICENSE-2.0
  37416. *
  37417. * Unless required by applicable law or agreed to in writing,
  37418. * software distributed under the License is distributed on an
  37419. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37420. * KIND, either express or implied. See the License for the
  37421. * specific language governing permissions and limitations
  37422. * under the License.
  37423. */
  37424. SeriesModel.extend({
  37425. type: 'series.scatter',
  37426. dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'],
  37427. getInitialData: function (option, ecModel) {
  37428. return createListFromArray(this.getSource(), this);
  37429. },
  37430. brushSelector: 'point',
  37431. getProgressive: function () {
  37432. var progressive = this.option.progressive;
  37433. if (progressive == null) {
  37434. // PENDING
  37435. return this.option.large ? 5e3 : this.get('progressive');
  37436. }
  37437. return progressive;
  37438. },
  37439. getProgressiveThreshold: function () {
  37440. var progressiveThreshold = this.option.progressiveThreshold;
  37441. if (progressiveThreshold == null) {
  37442. // PENDING
  37443. return this.option.large ? 1e4 : this.get('progressiveThreshold');
  37444. }
  37445. return progressiveThreshold;
  37446. },
  37447. defaultOption: {
  37448. coordinateSystem: 'cartesian2d',
  37449. zlevel: 0,
  37450. z: 2,
  37451. legendHoverLink: true,
  37452. hoverAnimation: true,
  37453. // Cartesian coordinate system
  37454. // xAxisIndex: 0,
  37455. // yAxisIndex: 0,
  37456. // Polar coordinate system
  37457. // polarIndex: 0,
  37458. // Geo coordinate system
  37459. // geoIndex: 0,
  37460. // symbol: null, // 图形类型
  37461. symbolSize: 10, // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
  37462. // symbolRotate: null, // 图形旋转控制
  37463. large: false,
  37464. // Available when large is true
  37465. largeThreshold: 2000,
  37466. // cursor: null,
  37467. // label: {
  37468. // show: false
  37469. // distance: 5,
  37470. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  37471. // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
  37472. // 'inside'|'left'|'right'|'top'|'bottom'
  37473. // 默认使用全局文本样式,详见TEXTSTYLE
  37474. // },
  37475. itemStyle: {
  37476. opacity: 0.8
  37477. // color: 各异
  37478. }
  37479. // progressive: null
  37480. }
  37481. });
  37482. /*
  37483. * Licensed to the Apache Software Foundation (ASF) under one
  37484. * or more contributor license agreements. See the NOTICE file
  37485. * distributed with this work for additional information
  37486. * regarding copyright ownership. The ASF licenses this file
  37487. * to you under the Apache License, Version 2.0 (the
  37488. * "License"); you may not use this file except in compliance
  37489. * with the License. You may obtain a copy of the License at
  37490. *
  37491. * http://www.apache.org/licenses/LICENSE-2.0
  37492. *
  37493. * Unless required by applicable law or agreed to in writing,
  37494. * software distributed under the License is distributed on an
  37495. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37496. * KIND, either express or implied. See the License for the
  37497. * specific language governing permissions and limitations
  37498. * under the License.
  37499. */
  37500. /* global Float32Array */
  37501. // TODO Batch by color
  37502. var BOOST_SIZE_THRESHOLD = 4;
  37503. var LargeSymbolPath = extendShape({
  37504. shape: {
  37505. points: null
  37506. },
  37507. symbolProxy: null,
  37508. buildPath: function (path, shape) {
  37509. var points = shape.points;
  37510. var size = shape.size;
  37511. var symbolProxy = this.symbolProxy;
  37512. var symbolProxyShape = symbolProxy.shape;
  37513. var ctx = path.getContext ? path.getContext() : path;
  37514. var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;
  37515. // Do draw in afterBrush.
  37516. if (canBoost) {
  37517. return;
  37518. }
  37519. for (var i = 0; i < points.length;) {
  37520. var x = points[i++];
  37521. var y = points[i++];
  37522. if (isNaN(x) || isNaN(y)) {
  37523. continue;
  37524. }
  37525. symbolProxyShape.x = x - size[0] / 2;
  37526. symbolProxyShape.y = y - size[1] / 2;
  37527. symbolProxyShape.width = size[0];
  37528. symbolProxyShape.height = size[1];
  37529. symbolProxy.buildPath(path, symbolProxyShape, true);
  37530. }
  37531. },
  37532. afterBrush: function (ctx) {
  37533. var shape = this.shape;
  37534. var points = shape.points;
  37535. var size = shape.size;
  37536. var canBoost = size[0] < BOOST_SIZE_THRESHOLD;
  37537. if (!canBoost) {
  37538. return;
  37539. }
  37540. this.setTransform(ctx);
  37541. // PENDING If style or other canvas status changed?
  37542. for (var i = 0; i < points.length;) {
  37543. var x = points[i++];
  37544. var y = points[i++];
  37545. if (isNaN(x) || isNaN(y)) {
  37546. continue;
  37547. }
  37548. // fillRect is faster than building a rect path and draw.
  37549. // And it support light globalCompositeOperation.
  37550. ctx.fillRect(
  37551. x - size[0] / 2, y - size[1] / 2,
  37552. size[0], size[1]
  37553. );
  37554. }
  37555. this.restoreTransform(ctx);
  37556. },
  37557. findDataIndex: function (x, y) {
  37558. // TODO ???
  37559. // Consider transform
  37560. var shape = this.shape;
  37561. var points = shape.points;
  37562. var size = shape.size;
  37563. var w = Math.max(size[0], 4);
  37564. var h = Math.max(size[1], 4);
  37565. // Not consider transform
  37566. // Treat each element as a rect
  37567. // top down traverse
  37568. for (var idx = points.length / 2 - 1; idx >= 0; idx--) {
  37569. var i = idx * 2;
  37570. var x0 = points[i] - w / 2;
  37571. var y0 = points[i + 1] - h / 2;
  37572. if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {
  37573. return idx;
  37574. }
  37575. }
  37576. return -1;
  37577. }
  37578. });
  37579. function LargeSymbolDraw() {
  37580. this.group = new Group();
  37581. }
  37582. var largeSymbolProto = LargeSymbolDraw.prototype;
  37583. largeSymbolProto.isPersistent = function () {
  37584. return !this._incremental;
  37585. };
  37586. /**
  37587. * Update symbols draw by new data
  37588. * @param {module:echarts/data/List} data
  37589. */
  37590. largeSymbolProto.updateData = function (data) {
  37591. this.group.removeAll();
  37592. var symbolEl = new LargeSymbolPath({
  37593. rectHover: true,
  37594. cursor: 'default'
  37595. });
  37596. symbolEl.setShape({
  37597. points: data.getLayout('symbolPoints')
  37598. });
  37599. this._setCommon(symbolEl, data);
  37600. this.group.add(symbolEl);
  37601. this._incremental = null;
  37602. };
  37603. largeSymbolProto.updateLayout = function (data) {
  37604. if (this._incremental) {
  37605. return;
  37606. }
  37607. var points = data.getLayout('symbolPoints');
  37608. this.group.eachChild(function (child) {
  37609. if (child.startIndex != null) {
  37610. var len = (child.endIndex - child.startIndex) * 2;
  37611. var byteOffset = child.startIndex * 4 * 2;
  37612. points = new Float32Array(points.buffer, byteOffset, len);
  37613. }
  37614. child.setShape('points', points);
  37615. });
  37616. };
  37617. largeSymbolProto.incrementalPrepareUpdate = function (data) {
  37618. this.group.removeAll();
  37619. this._clearIncremental();
  37620. // Only use incremental displayables when data amount is larger than 2 million.
  37621. // PENDING Incremental data?
  37622. if (data.count() > 2e6) {
  37623. if (!this._incremental) {
  37624. this._incremental = new IncrementalDisplayble({
  37625. silent: true
  37626. });
  37627. }
  37628. this.group.add(this._incremental);
  37629. }
  37630. else {
  37631. this._incremental = null;
  37632. }
  37633. };
  37634. largeSymbolProto.incrementalUpdate = function (taskParams, data) {
  37635. var symbolEl;
  37636. if (this._incremental) {
  37637. symbolEl = new LargeSymbolPath();
  37638. this._incremental.addDisplayable(symbolEl, true);
  37639. }
  37640. else {
  37641. symbolEl = new LargeSymbolPath({
  37642. rectHover: true,
  37643. cursor: 'default',
  37644. startIndex: taskParams.start,
  37645. endIndex: taskParams.end
  37646. });
  37647. symbolEl.incremental = true;
  37648. this.group.add(symbolEl);
  37649. }
  37650. symbolEl.setShape({
  37651. points: data.getLayout('symbolPoints')
  37652. });
  37653. this._setCommon(symbolEl, data, !!this._incremental);
  37654. };
  37655. largeSymbolProto._setCommon = function (symbolEl, data, isIncremental) {
  37656. var hostModel = data.hostModel;
  37657. // TODO
  37658. // if (data.hasItemVisual.symbolSize) {
  37659. // // TODO typed array?
  37660. // symbolEl.setShape('sizes', data.mapArray(
  37661. // function (idx) {
  37662. // var size = data.getItemVisual(idx, 'symbolSize');
  37663. // return (size instanceof Array) ? size : [size, size];
  37664. // }
  37665. // ));
  37666. // }
  37667. // else {
  37668. var size = data.getVisual('symbolSize');
  37669. symbolEl.setShape('size', (size instanceof Array) ? size : [size, size]);
  37670. // }
  37671. // Create symbolProxy to build path for each data
  37672. symbolEl.symbolProxy = createSymbol(
  37673. data.getVisual('symbol'), 0, 0, 0, 0
  37674. );
  37675. // Use symbolProxy setColor method
  37676. symbolEl.setColor = symbolEl.symbolProxy.setColor;
  37677. var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;
  37678. symbolEl.useStyle(
  37679. // Draw shadow when doing fillRect is extremely slow.
  37680. hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color'])
  37681. );
  37682. var visualColor = data.getVisual('color');
  37683. if (visualColor) {
  37684. symbolEl.setColor(visualColor);
  37685. }
  37686. if (!isIncremental) {
  37687. // Enable tooltip
  37688. // PENDING May have performance issue when path is extremely large
  37689. symbolEl.seriesIndex = hostModel.seriesIndex;
  37690. symbolEl.on('mousemove', function (e) {
  37691. symbolEl.dataIndex = null;
  37692. var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY);
  37693. if (dataIndex >= 0) {
  37694. // Provide dataIndex for tooltip
  37695. symbolEl.dataIndex = dataIndex + (symbolEl.startIndex || 0);
  37696. }
  37697. });
  37698. }
  37699. };
  37700. largeSymbolProto.remove = function () {
  37701. this._clearIncremental();
  37702. this._incremental = null;
  37703. this.group.removeAll();
  37704. };
  37705. largeSymbolProto._clearIncremental = function () {
  37706. var incremental = this._incremental;
  37707. if (incremental) {
  37708. incremental.clearDisplaybles();
  37709. }
  37710. };
  37711. /*
  37712. * Licensed to the Apache Software Foundation (ASF) under one
  37713. * or more contributor license agreements. See the NOTICE file
  37714. * distributed with this work for additional information
  37715. * regarding copyright ownership. The ASF licenses this file
  37716. * to you under the Apache License, Version 2.0 (the
  37717. * "License"); you may not use this file except in compliance
  37718. * with the License. You may obtain a copy of the License at
  37719. *
  37720. * http://www.apache.org/licenses/LICENSE-2.0
  37721. *
  37722. * Unless required by applicable law or agreed to in writing,
  37723. * software distributed under the License is distributed on an
  37724. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37725. * KIND, either express or implied. See the License for the
  37726. * specific language governing permissions and limitations
  37727. * under the License.
  37728. */
  37729. extendChartView({
  37730. type: 'scatter',
  37731. render: function (seriesModel, ecModel, api) {
  37732. var data = seriesModel.getData();
  37733. var symbolDraw = this._updateSymbolDraw(data, seriesModel);
  37734. symbolDraw.updateData(data);
  37735. this._finished = true;
  37736. },
  37737. incrementalPrepareRender: function (seriesModel, ecModel, api) {
  37738. var data = seriesModel.getData();
  37739. var symbolDraw = this._updateSymbolDraw(data, seriesModel);
  37740. symbolDraw.incrementalPrepareUpdate(data);
  37741. this._finished = false;
  37742. },
  37743. incrementalRender: function (taskParams, seriesModel, ecModel) {
  37744. this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData());
  37745. this._finished = taskParams.end === seriesModel.getData().count();
  37746. },
  37747. updateTransform: function (seriesModel, ecModel, api) {
  37748. var data = seriesModel.getData();
  37749. // Must mark group dirty and make sure the incremental layer will be cleared
  37750. // PENDING
  37751. this.group.dirty();
  37752. if (!this._finished || data.count() > 1e4 || !this._symbolDraw.isPersistent()) {
  37753. return {
  37754. update: true
  37755. };
  37756. }
  37757. else {
  37758. var res = pointsLayout().reset(seriesModel);
  37759. if (res.progress) {
  37760. res.progress({ start: 0, end: data.count() }, data);
  37761. }
  37762. this._symbolDraw.updateLayout(data);
  37763. }
  37764. },
  37765. _updateSymbolDraw: function (data, seriesModel) {
  37766. var symbolDraw = this._symbolDraw;
  37767. var pipelineContext = seriesModel.pipelineContext;
  37768. var isLargeDraw = pipelineContext.large;
  37769. if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {
  37770. symbolDraw && symbolDraw.remove();
  37771. symbolDraw = this._symbolDraw = isLargeDraw
  37772. ? new LargeSymbolDraw()
  37773. : new SymbolDraw();
  37774. this._isLargeDraw = isLargeDraw;
  37775. this.group.removeAll();
  37776. }
  37777. this.group.add(symbolDraw.group);
  37778. return symbolDraw;
  37779. },
  37780. remove: function (ecModel, api) {
  37781. this._symbolDraw && this._symbolDraw.remove(true);
  37782. this._symbolDraw = null;
  37783. },
  37784. dispose: function () {}
  37785. });
  37786. /*
  37787. * Licensed to the Apache Software Foundation (ASF) under one
  37788. * or more contributor license agreements. See the NOTICE file
  37789. * distributed with this work for additional information
  37790. * regarding copyright ownership. The ASF licenses this file
  37791. * to you under the Apache License, Version 2.0 (the
  37792. * "License"); you may not use this file except in compliance
  37793. * with the License. You may obtain a copy of the License at
  37794. *
  37795. * http://www.apache.org/licenses/LICENSE-2.0
  37796. *
  37797. * Unless required by applicable law or agreed to in writing,
  37798. * software distributed under the License is distributed on an
  37799. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37800. * KIND, either express or implied. See the License for the
  37801. * specific language governing permissions and limitations
  37802. * under the License.
  37803. */
  37804. // import * as zrUtil from 'zrender/src/core/util';
  37805. // In case developer forget to include grid component
  37806. registerVisual(visualSymbol('scatter', 'circle'));
  37807. registerLayout(pointsLayout('scatter'));
  37808. // echarts.registerProcessor(function (ecModel, api) {
  37809. // ecModel.eachSeriesByType('scatter', function (seriesModel) {
  37810. // var data = seriesModel.getData();
  37811. // var coordSys = seriesModel.coordinateSystem;
  37812. // if (coordSys.type !== 'geo') {
  37813. // return;
  37814. // }
  37815. // var startPt = coordSys.pointToData([0, 0]);
  37816. // var endPt = coordSys.pointToData([api.getWidth(), api.getHeight()]);
  37817. // var dims = zrUtil.map(coordSys.dimensions, function (dim) {
  37818. // return data.mapDimension(dim);
  37819. // });
  37820. // var range = {};
  37821. // range[dims[0]] = [Math.min(startPt[0], endPt[0]), Math.max(startPt[0], endPt[0])];
  37822. // range[dims[1]] = [Math.min(startPt[1], endPt[1]), Math.max(startPt[1], endPt[1])];
  37823. // data.selectRange(range);
  37824. // });
  37825. // });
  37826. /*
  37827. * Licensed to the Apache Software Foundation (ASF) under one
  37828. * or more contributor license agreements. See the NOTICE file
  37829. * distributed with this work for additional information
  37830. * regarding copyright ownership. The ASF licenses this file
  37831. * to you under the Apache License, Version 2.0 (the
  37832. * "License"); you may not use this file except in compliance
  37833. * with the License. You may obtain a copy of the License at
  37834. *
  37835. * http://www.apache.org/licenses/LICENSE-2.0
  37836. *
  37837. * Unless required by applicable law or agreed to in writing,
  37838. * software distributed under the License is distributed on an
  37839. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37840. * KIND, either express or implied. See the License for the
  37841. * specific language governing permissions and limitations
  37842. * under the License.
  37843. */
  37844. // -------------
  37845. // Preprocessor
  37846. // -------------
  37847. registerPreprocessor(function (option) {
  37848. var graphicOption = option.graphic;
  37849. // Convert
  37850. // {graphic: [{left: 10, type: 'circle'}, ...]}
  37851. // or
  37852. // {graphic: {left: 10, type: 'circle'}}
  37853. // to
  37854. // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}
  37855. if (isArray(graphicOption)) {
  37856. if (!graphicOption[0] || !graphicOption[0].elements) {
  37857. option.graphic = [{elements: graphicOption}];
  37858. }
  37859. else {
  37860. // Only one graphic instance can be instantiated. (We dont
  37861. // want that too many views are created in echarts._viewMap)
  37862. option.graphic = [option.graphic[0]];
  37863. }
  37864. }
  37865. else if (graphicOption && !graphicOption.elements) {
  37866. option.graphic = [{elements: [graphicOption]}];
  37867. }
  37868. });
  37869. // ------
  37870. // Model
  37871. // ------
  37872. var GraphicModel = extendComponentModel({
  37873. type: 'graphic',
  37874. defaultOption: {
  37875. // Extra properties for each elements:
  37876. //
  37877. // left/right/top/bottom: (like 12, '22%', 'center', default undefined)
  37878. // If left/rigth is set, shape.x/shape.cx/position will not be used.
  37879. // If top/bottom is set, shape.y/shape.cy/position will not be used.
  37880. // This mechanism is useful when you want to position a group/element
  37881. // against the right side or the center of this container.
  37882. //
  37883. // width/height: (can only be pixel value, default 0)
  37884. // Only be used to specify contianer(group) size, if needed. And
  37885. // can not be percentage value (like '33%'). See the reason in the
  37886. // layout algorithm below.
  37887. //
  37888. // bounding: (enum: 'all' (default) | 'raw')
  37889. // Specify how to calculate boundingRect when locating.
  37890. // 'all': Get uioned and transformed boundingRect
  37891. // from both itself and its descendants.
  37892. // This mode simplies confining a group of elements in the bounding
  37893. // of their ancester container (e.g., using 'right: 0').
  37894. // 'raw': Only use the boundingRect of itself and before transformed.
  37895. // This mode is similar to css behavior, which is useful when you
  37896. // want an element to be able to overflow its container. (Consider
  37897. // a rotated circle needs to be located in a corner.)
  37898. // info: custom info. enables user to mount some info on elements and use them
  37899. // in event handlers. Update them only when user specified, otherwise, remain.
  37900. // Note: elements is always behind its ancestors in this elements array.
  37901. elements: [],
  37902. parentId: null
  37903. },
  37904. /**
  37905. * Save el options for the sake of the performance (only update modified graphics).
  37906. * The order is the same as those in option. (ancesters -> descendants)
  37907. *
  37908. * @private
  37909. * @type {Array.<Object>}
  37910. */
  37911. _elOptionsToUpdate: null,
  37912. /**
  37913. * @override
  37914. */
  37915. mergeOption: function (option) {
  37916. // Prevent default merge to elements
  37917. var elements = this.option.elements;
  37918. this.option.elements = null;
  37919. GraphicModel.superApply(this, 'mergeOption', arguments);
  37920. this.option.elements = elements;
  37921. },
  37922. /**
  37923. * @override
  37924. */
  37925. optionUpdated: function (newOption, isInit) {
  37926. var thisOption = this.option;
  37927. var newList = (isInit ? thisOption : newOption).elements;
  37928. var existList = thisOption.elements = isInit ? [] : thisOption.elements;
  37929. var flattenedList = [];
  37930. this._flatten(newList, flattenedList);
  37931. var mappingResult = mappingToExists(existList, flattenedList);
  37932. makeIdAndName(mappingResult);
  37933. // Clear elOptionsToUpdate
  37934. var elOptionsToUpdate = this._elOptionsToUpdate = [];
  37935. each$1(mappingResult, function (resultItem, index) {
  37936. var newElOption = resultItem.option;
  37937. if (__DEV__) {
  37938. assert$1(
  37939. isObject$1(newElOption) || resultItem.exist,
  37940. 'Empty graphic option definition'
  37941. );
  37942. }
  37943. if (!newElOption) {
  37944. return;
  37945. }
  37946. elOptionsToUpdate.push(newElOption);
  37947. setKeyInfoToNewElOption(resultItem, newElOption);
  37948. mergeNewElOptionToExist(existList, index, newElOption);
  37949. setLayoutInfoToExist(existList[index], newElOption);
  37950. }, this);
  37951. // Clean
  37952. for (var i = existList.length - 1; i >= 0; i--) {
  37953. if (existList[i] == null) {
  37954. existList.splice(i, 1);
  37955. }
  37956. else {
  37957. // $action should be volatile, otherwise option gotten from
  37958. // `getOption` will contain unexpected $action.
  37959. delete existList[i].$action;
  37960. }
  37961. }
  37962. },
  37963. /**
  37964. * Convert
  37965. * [{
  37966. * type: 'group',
  37967. * id: 'xx',
  37968. * children: [{type: 'circle'}, {type: 'polygon'}]
  37969. * }]
  37970. * to
  37971. * [
  37972. * {type: 'group', id: 'xx'},
  37973. * {type: 'circle', parentId: 'xx'},
  37974. * {type: 'polygon', parentId: 'xx'}
  37975. * ]
  37976. *
  37977. * @private
  37978. * @param {Array.<Object>} optionList option list
  37979. * @param {Array.<Object>} result result of flatten
  37980. * @param {Object} parentOption parent option
  37981. */
  37982. _flatten: function (optionList, result, parentOption) {
  37983. each$1(optionList, function (option) {
  37984. if (!option) {
  37985. return;
  37986. }
  37987. if (parentOption) {
  37988. option.parentOption = parentOption;
  37989. }
  37990. result.push(option);
  37991. var children = option.children;
  37992. if (option.type === 'group' && children) {
  37993. this._flatten(children, result, option);
  37994. }
  37995. // Deleting for JSON output, and for not affecting group creation.
  37996. delete option.children;
  37997. }, this);
  37998. },
  37999. // FIXME
  38000. // Pass to view using payload? setOption has a payload?
  38001. useElOptionsToUpdate: function () {
  38002. var els = this._elOptionsToUpdate;
  38003. // Clear to avoid render duplicately when zooming.
  38004. this._elOptionsToUpdate = null;
  38005. return els;
  38006. }
  38007. });
  38008. // -----
  38009. // View
  38010. // -----
  38011. extendComponentView({
  38012. type: 'graphic',
  38013. /**
  38014. * @override
  38015. */
  38016. init: function (ecModel, api) {
  38017. /**
  38018. * @private
  38019. * @type {module:zrender/core/util.HashMap}
  38020. */
  38021. this._elMap = createHashMap();
  38022. /**
  38023. * @private
  38024. * @type {module:echarts/graphic/GraphicModel}
  38025. */
  38026. this._lastGraphicModel;
  38027. },
  38028. /**
  38029. * @override
  38030. */
  38031. render: function (graphicModel, ecModel, api) {
  38032. // Having leveraged between use cases and algorithm complexity, a very
  38033. // simple layout mechanism is used:
  38034. // The size(width/height) can be determined by itself or its parent (not
  38035. // implemented yet), but can not by its children. (Top-down travel)
  38036. // The location(x/y) can be determined by the bounding rect of itself
  38037. // (can including its descendants or not) and the size of its parent.
  38038. // (Bottom-up travel)
  38039. // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,
  38040. // view will be reused.
  38041. if (graphicModel !== this._lastGraphicModel) {
  38042. this._clear();
  38043. }
  38044. this._lastGraphicModel = graphicModel;
  38045. this._updateElements(graphicModel);
  38046. this._relocate(graphicModel, api);
  38047. },
  38048. /**
  38049. * Update graphic elements.
  38050. *
  38051. * @private
  38052. * @param {Object} graphicModel graphic model
  38053. */
  38054. _updateElements: function (graphicModel) {
  38055. var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();
  38056. if (!elOptionsToUpdate) {
  38057. return;
  38058. }
  38059. var elMap = this._elMap;
  38060. var rootGroup = this.group;
  38061. // Top-down tranverse to assign graphic settings to each elements.
  38062. each$1(elOptionsToUpdate, function (elOption) {
  38063. var $action = elOption.$action;
  38064. var id = elOption.id;
  38065. var existEl = elMap.get(id);
  38066. var parentId = elOption.parentId;
  38067. var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;
  38068. var elOptionStyle = elOption.style;
  38069. if (elOption.type === 'text' && elOptionStyle) {
  38070. // In top/bottom mode, textVerticalAlign should not be used, which cause
  38071. // inaccurately locating.
  38072. if (elOption.hv && elOption.hv[1]) {
  38073. elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = null;
  38074. }
  38075. // Compatible with previous setting: both support fill and textFill,
  38076. // stroke and textStroke.
  38077. !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (
  38078. elOptionStyle.textFill = elOptionStyle.fill
  38079. );
  38080. !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (
  38081. elOptionStyle.textStroke = elOptionStyle.stroke
  38082. );
  38083. }
  38084. // Remove unnecessary props to avoid potential problems.
  38085. var elOptionCleaned = getCleanedElOption(elOption);
  38086. // For simple, do not support parent change, otherwise reorder is needed.
  38087. if (__DEV__) {
  38088. existEl && assert$1(
  38089. targetElParent === existEl.parent,
  38090. 'Changing parent is not supported.'
  38091. );
  38092. }
  38093. if (!$action || $action === 'merge') {
  38094. existEl
  38095. ? existEl.attr(elOptionCleaned)
  38096. : createEl(id, targetElParent, elOptionCleaned, elMap);
  38097. }
  38098. else if ($action === 'replace') {
  38099. removeEl(existEl, elMap);
  38100. createEl(id, targetElParent, elOptionCleaned, elMap);
  38101. }
  38102. else if ($action === 'remove') {
  38103. removeEl(existEl, elMap);
  38104. }
  38105. var el = elMap.get(id);
  38106. if (el) {
  38107. el.__ecGraphicWidth = elOption.width;
  38108. el.__ecGraphicHeight = elOption.height;
  38109. setEventData(el, graphicModel, elOption);
  38110. }
  38111. });
  38112. },
  38113. /**
  38114. * Locate graphic elements.
  38115. *
  38116. * @private
  38117. * @param {Object} graphicModel graphic model
  38118. * @param {module:echarts/ExtensionAPI} api extension API
  38119. */
  38120. _relocate: function (graphicModel, api) {
  38121. var elOptions = graphicModel.option.elements;
  38122. var rootGroup = this.group;
  38123. var elMap = this._elMap;
  38124. // Bottom-up tranvese all elements (consider ec resize) to locate elements.
  38125. for (var i = elOptions.length - 1; i >= 0; i--) {
  38126. var elOption = elOptions[i];
  38127. var el = elMap.get(elOption.id);
  38128. if (!el) {
  38129. continue;
  38130. }
  38131. var parentEl = el.parent;
  38132. var containerInfo = parentEl === rootGroup
  38133. ? {
  38134. width: api.getWidth(),
  38135. height: api.getHeight()
  38136. }
  38137. : { // Like 'position:absolut' in css, default 0.
  38138. width: parentEl.__ecGraphicWidth || 0,
  38139. height: parentEl.__ecGraphicHeight || 0
  38140. };
  38141. positionElement(
  38142. el, elOption, containerInfo, null,
  38143. {hv: elOption.hv, boundingMode: elOption.bounding}
  38144. );
  38145. }
  38146. },
  38147. /**
  38148. * Clear all elements.
  38149. *
  38150. * @private
  38151. */
  38152. _clear: function () {
  38153. var elMap = this._elMap;
  38154. elMap.each(function (el) {
  38155. removeEl(el, elMap);
  38156. });
  38157. this._elMap = createHashMap();
  38158. },
  38159. /**
  38160. * @override
  38161. */
  38162. dispose: function () {
  38163. this._clear();
  38164. }
  38165. });
  38166. function createEl(id, targetElParent, elOption, elMap) {
  38167. var graphicType = elOption.type;
  38168. if (__DEV__) {
  38169. assert$1(graphicType, 'graphic type MUST be set');
  38170. }
  38171. var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)];
  38172. if (__DEV__) {
  38173. assert$1(Clz, 'graphic type can not be found');
  38174. }
  38175. var el = new Clz(elOption);
  38176. targetElParent.add(el);
  38177. elMap.set(id, el);
  38178. el.__ecGraphicId = id;
  38179. }
  38180. function removeEl(existEl, elMap) {
  38181. var existElParent = existEl && existEl.parent;
  38182. if (existElParent) {
  38183. existEl.type === 'group' && existEl.traverse(function (el) {
  38184. removeEl(el, elMap);
  38185. });
  38186. elMap.removeKey(existEl.__ecGraphicId);
  38187. existElParent.remove(existEl);
  38188. }
  38189. }
  38190. // Remove unnecessary props to avoid potential problems.
  38191. function getCleanedElOption(elOption) {
  38192. elOption = extend({}, elOption);
  38193. each$1(
  38194. ['id', 'parentId', '$action', 'hv', 'bounding'].concat(LOCATION_PARAMS),
  38195. function (name) {
  38196. delete elOption[name];
  38197. }
  38198. );
  38199. return elOption;
  38200. }
  38201. function isSetLoc(obj, props) {
  38202. var isSet;
  38203. each$1(props, function (prop) {
  38204. obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);
  38205. });
  38206. return isSet;
  38207. }
  38208. function setKeyInfoToNewElOption(resultItem, newElOption) {
  38209. var existElOption = resultItem.exist;
  38210. // Set id and type after id assigned.
  38211. newElOption.id = resultItem.keyInfo.id;
  38212. !newElOption.type && existElOption && (newElOption.type = existElOption.type);
  38213. // Set parent id if not specified
  38214. if (newElOption.parentId == null) {
  38215. var newElParentOption = newElOption.parentOption;
  38216. if (newElParentOption) {
  38217. newElOption.parentId = newElParentOption.id;
  38218. }
  38219. else if (existElOption) {
  38220. newElOption.parentId = existElOption.parentId;
  38221. }
  38222. }
  38223. // Clear
  38224. newElOption.parentOption = null;
  38225. }
  38226. function mergeNewElOptionToExist(existList, index, newElOption) {
  38227. // Update existing options, for `getOption` feature.
  38228. var newElOptCopy = extend({}, newElOption);
  38229. var existElOption = existList[index];
  38230. var $action = newElOption.$action || 'merge';
  38231. if ($action === 'merge') {
  38232. if (existElOption) {
  38233. if (__DEV__) {
  38234. var newType = newElOption.type;
  38235. assert$1(
  38236. !newType || existElOption.type === newType,
  38237. 'Please set $action: "replace" to change `type`'
  38238. );
  38239. }
  38240. // We can ensure that newElOptCopy and existElOption are not
  38241. // the same object, so `merge` will not change newElOptCopy.
  38242. merge(existElOption, newElOptCopy, true);
  38243. // Rigid body, use ignoreSize.
  38244. mergeLayoutParam(existElOption, newElOptCopy, {ignoreSize: true});
  38245. // Will be used in render.
  38246. copyLayoutParams(newElOption, existElOption);
  38247. }
  38248. else {
  38249. existList[index] = newElOptCopy;
  38250. }
  38251. }
  38252. else if ($action === 'replace') {
  38253. existList[index] = newElOptCopy;
  38254. }
  38255. else if ($action === 'remove') {
  38256. // null will be cleaned later.
  38257. existElOption && (existList[index] = null);
  38258. }
  38259. }
  38260. function setLayoutInfoToExist(existItem, newElOption) {
  38261. if (!existItem) {
  38262. return;
  38263. }
  38264. existItem.hv = newElOption.hv = [
  38265. // Rigid body, dont care `width`.
  38266. isSetLoc(newElOption, ['left', 'right']),
  38267. // Rigid body, dont care `height`.
  38268. isSetLoc(newElOption, ['top', 'bottom'])
  38269. ];
  38270. // Give default group size. Otherwise layout error may occur.
  38271. if (existItem.type === 'group') {
  38272. existItem.width == null && (existItem.width = newElOption.width = 0);
  38273. existItem.height == null && (existItem.height = newElOption.height = 0);
  38274. }
  38275. }
  38276. function setEventData(el, graphicModel, elOption) {
  38277. var eventData = el.eventData;
  38278. // Simple optimize for large amount of elements that no need event.
  38279. if (!el.silent && !el.ignore && !eventData) {
  38280. eventData = el.eventData = {
  38281. componentType: 'graphic',
  38282. componentIndex: graphicModel.componentIndex,
  38283. name: el.name
  38284. };
  38285. }
  38286. // `elOption.info` enables user to mount some info on
  38287. // elements and use them in event handlers.
  38288. if (eventData) {
  38289. eventData.info = el.info;
  38290. }
  38291. }
  38292. /*
  38293. * Licensed to the Apache Software Foundation (ASF) under one
  38294. * or more contributor license agreements. See the NOTICE file
  38295. * distributed with this work for additional information
  38296. * regarding copyright ownership. The ASF licenses this file
  38297. * to you under the Apache License, Version 2.0 (the
  38298. * "License"); you may not use this file except in compliance
  38299. * with the License. You may obtain a copy of the License at
  38300. *
  38301. * http://www.apache.org/licenses/LICENSE-2.0
  38302. *
  38303. * Unless required by applicable law or agreed to in writing,
  38304. * software distributed under the License is distributed on an
  38305. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38306. * KIND, either express or implied. See the License for the
  38307. * specific language governing permissions and limitations
  38308. * under the License.
  38309. */
  38310. /**
  38311. * @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside}
  38312. * @param {module:echarts/model/Global} ecModel
  38313. * @return {Object} {point: [x, y], el: ...} point Will not be null.
  38314. */
  38315. var findPointFromSeries = function (finder, ecModel) {
  38316. var point = [];
  38317. var seriesIndex = finder.seriesIndex;
  38318. var seriesModel;
  38319. if (seriesIndex == null || !(
  38320. seriesModel = ecModel.getSeriesByIndex(seriesIndex)
  38321. )) {
  38322. return {point: []};
  38323. }
  38324. var data = seriesModel.getData();
  38325. var dataIndex = queryDataIndex(data, finder);
  38326. if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {
  38327. return {point: []};
  38328. }
  38329. var el = data.getItemGraphicEl(dataIndex);
  38330. var coordSys = seriesModel.coordinateSystem;
  38331. if (seriesModel.getTooltipPosition) {
  38332. point = seriesModel.getTooltipPosition(dataIndex) || [];
  38333. }
  38334. else if (coordSys && coordSys.dataToPoint) {
  38335. point = coordSys.dataToPoint(
  38336. data.getValues(
  38337. map(coordSys.dimensions, function (dim) {
  38338. return data.mapDimension(dim);
  38339. }), dataIndex, true
  38340. )
  38341. ) || [];
  38342. }
  38343. else if (el) {
  38344. // Use graphic bounding rect
  38345. var rect = el.getBoundingRect().clone();
  38346. rect.applyTransform(el.transform);
  38347. point = [
  38348. rect.x + rect.width / 2,
  38349. rect.y + rect.height / 2
  38350. ];
  38351. }
  38352. return {point: point, el: el};
  38353. };
  38354. /*
  38355. * Licensed to the Apache Software Foundation (ASF) under one
  38356. * or more contributor license agreements. See the NOTICE file
  38357. * distributed with this work for additional information
  38358. * regarding copyright ownership. The ASF licenses this file
  38359. * to you under the Apache License, Version 2.0 (the
  38360. * "License"); you may not use this file except in compliance
  38361. * with the License. You may obtain a copy of the License at
  38362. *
  38363. * http://www.apache.org/licenses/LICENSE-2.0
  38364. *
  38365. * Unless required by applicable law or agreed to in writing,
  38366. * software distributed under the License is distributed on an
  38367. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38368. * KIND, either express or implied. See the License for the
  38369. * specific language governing permissions and limitations
  38370. * under the License.
  38371. */
  38372. var each$7 = each$1;
  38373. var curry$2 = curry;
  38374. var inner$7 = makeInner();
  38375. /**
  38376. * Basic logic: check all axis, if they do not demand show/highlight,
  38377. * then hide/downplay them.
  38378. *
  38379. * @param {Object} coordSysAxesInfo
  38380. * @param {Object} payload
  38381. * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave'
  38382. * @param {Array.<number>} [payload.x] x and y, which are mandatory, specify a point to
  38383. * trigger axisPointer and tooltip.
  38384. * @param {Array.<number>} [payload.y] x and y, which are mandatory, specify a point to
  38385. * trigger axisPointer and tooltip.
  38386. * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes.
  38387. * @param {Object} [payload.dataIndex] finder, restrict target axes.
  38388. * @param {Object} [payload.axesInfo] finder, restrict target axes.
  38389. * [{
  38390. * axisDim: 'x'|'y'|'angle'|...,
  38391. * axisIndex: ...,
  38392. * value: ...
  38393. * }, ...]
  38394. * @param {Function} [payload.dispatchAction]
  38395. * @param {Object} [payload.tooltipOption]
  38396. * @param {Object|Array.<number>|Function} [payload.position] Tooltip position,
  38397. * which can be specified in dispatchAction
  38398. * @param {module:echarts/model/Global} ecModel
  38399. * @param {module:echarts/ExtensionAPI} api
  38400. * @return {Object} content of event obj for echarts.connect.
  38401. */
  38402. var axisTrigger = function (payload, ecModel, api) {
  38403. var currTrigger = payload.currTrigger;
  38404. var point = [payload.x, payload.y];
  38405. var finder = payload;
  38406. var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);
  38407. var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
  38408. // Pending
  38409. // See #6121. But we are not able to reproduce it yet.
  38410. if (!coordSysAxesInfo) {
  38411. return;
  38412. }
  38413. if (illegalPoint(point)) {
  38414. // Used in the default behavior of `connection`: use the sample seriesIndex
  38415. // and dataIndex. And also used in the tooltipView trigger.
  38416. point = findPointFromSeries({
  38417. seriesIndex: finder.seriesIndex,
  38418. // Do not use dataIndexInside from other ec instance.
  38419. // FIXME: auto detect it?
  38420. dataIndex: finder.dataIndex
  38421. }, ecModel).point;
  38422. }
  38423. var isIllegalPoint = illegalPoint(point);
  38424. // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
  38425. // Notice: In this case, it is difficult to get the `point` (which is necessary to show
  38426. // tooltip, so if point is not given, we just use the point found by sample seriesIndex
  38427. // and dataIndex.
  38428. var inputAxesInfo = finder.axesInfo;
  38429. var axesInfo = coordSysAxesInfo.axesInfo;
  38430. var shouldHide = currTrigger === 'leave' || illegalPoint(point);
  38431. var outputFinder = {};
  38432. var showValueMap = {};
  38433. var dataByCoordSys = {list: [], map: {}};
  38434. var updaters = {
  38435. showPointer: curry$2(showPointer, showValueMap),
  38436. showTooltip: curry$2(showTooltip, dataByCoordSys)
  38437. };
  38438. // Process for triggered axes.
  38439. each$7(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
  38440. // If a point given, it must be contained by the coordinate system.
  38441. var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);
  38442. each$7(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
  38443. var axis = axisInfo.axis;
  38444. var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo);
  38445. // If no inputAxesInfo, no axis is restricted.
  38446. if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {
  38447. var val = inputAxisInfo && inputAxisInfo.value;
  38448. if (val == null && !isIllegalPoint) {
  38449. val = axis.pointToData(point);
  38450. }
  38451. val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder);
  38452. }
  38453. });
  38454. });
  38455. // Process for linked axes.
  38456. var linkTriggers = {};
  38457. each$7(axesInfo, function (tarAxisInfo, tarKey) {
  38458. var linkGroup = tarAxisInfo.linkGroup;
  38459. // If axis has been triggered in the previous stage, it should not be triggered by link.
  38460. if (linkGroup && !showValueMap[tarKey]) {
  38461. each$7(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
  38462. var srcValItem = showValueMap[srcKey];
  38463. // If srcValItem exist, source axis is triggered, so link to target axis.
  38464. if (srcAxisInfo !== tarAxisInfo && srcValItem) {
  38465. var val = srcValItem.value;
  38466. linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(
  38467. val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo)
  38468. )));
  38469. linkTriggers[tarAxisInfo.key] = val;
  38470. }
  38471. });
  38472. }
  38473. });
  38474. each$7(linkTriggers, function (val, tarKey) {
  38475. processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder);
  38476. });
  38477. updateModelActually(showValueMap, axesInfo, outputFinder);
  38478. dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);
  38479. dispatchHighDownActually(axesInfo, dispatchAction, api);
  38480. return outputFinder;
  38481. };
  38482. function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) {
  38483. var axis = axisInfo.axis;
  38484. if (axis.scale.isBlank() || !axis.containData(newValue)) {
  38485. return;
  38486. }
  38487. if (!axisInfo.involveSeries) {
  38488. updaters.showPointer(axisInfo, newValue);
  38489. return;
  38490. }
  38491. // Heavy calculation. So put it after axis.containData checking.
  38492. var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
  38493. var payloadBatch = payloadInfo.payloadBatch;
  38494. var snapToValue = payloadInfo.snapToValue;
  38495. // Fill content of event obj for echarts.connect.
  38496. // By defualt use the first involved series data as a sample to connect.
  38497. if (payloadBatch[0] && outputFinder.seriesIndex == null) {
  38498. extend(outputFinder, payloadBatch[0]);
  38499. }
  38500. // If no linkSource input, this process is for collecting link
  38501. // target, where snap should not be accepted.
  38502. if (!dontSnap && axisInfo.snap) {
  38503. if (axis.containData(snapToValue) && snapToValue != null) {
  38504. newValue = snapToValue;
  38505. }
  38506. }
  38507. updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder);
  38508. // Tooltip should always be snapToValue, otherwise there will be
  38509. // incorrect "axis value ~ series value" mapping displayed in tooltip.
  38510. updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
  38511. }
  38512. function buildPayloadsBySeries(value, axisInfo) {
  38513. var axis = axisInfo.axis;
  38514. var dim = axis.dim;
  38515. var snapToValue = value;
  38516. var payloadBatch = [];
  38517. var minDist = Number.MAX_VALUE;
  38518. var minDiff = -1;
  38519. each$7(axisInfo.seriesModels, function (series, idx) {
  38520. var dataDim = series.getData().mapDimension(dim, true);
  38521. var seriesNestestValue;
  38522. var dataIndices;
  38523. if (series.getAxisTooltipData) {
  38524. var result = series.getAxisTooltipData(dataDim, value, axis);
  38525. dataIndices = result.dataIndices;
  38526. seriesNestestValue = result.nestestValue;
  38527. }
  38528. else {
  38529. dataIndices = series.getData().indicesOfNearest(
  38530. dataDim[0],
  38531. value,
  38532. // Add a threshold to avoid find the wrong dataIndex
  38533. // when data length is not same.
  38534. // false,
  38535. axis.type === 'category' ? 0.5 : null
  38536. );
  38537. if (!dataIndices.length) {
  38538. return;
  38539. }
  38540. seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
  38541. }
  38542. if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
  38543. return;
  38544. }
  38545. var diff = value - seriesNestestValue;
  38546. var dist = Math.abs(diff);
  38547. // Consider category case
  38548. if (dist <= minDist) {
  38549. if (dist < minDist || (diff >= 0 && minDiff < 0)) {
  38550. minDist = dist;
  38551. minDiff = diff;
  38552. snapToValue = seriesNestestValue;
  38553. payloadBatch.length = 0;
  38554. }
  38555. each$7(dataIndices, function (dataIndex) {
  38556. payloadBatch.push({
  38557. seriesIndex: series.seriesIndex,
  38558. dataIndexInside: dataIndex,
  38559. dataIndex: series.getData().getRawIndex(dataIndex)
  38560. });
  38561. });
  38562. }
  38563. });
  38564. return {
  38565. payloadBatch: payloadBatch,
  38566. snapToValue: snapToValue
  38567. };
  38568. }
  38569. function showPointer(showValueMap, axisInfo, value, payloadBatch) {
  38570. showValueMap[axisInfo.key] = {value: value, payloadBatch: payloadBatch};
  38571. }
  38572. function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
  38573. var payloadBatch = payloadInfo.payloadBatch;
  38574. var axis = axisInfo.axis;
  38575. var axisModel = axis.model;
  38576. var axisPointerModel = axisInfo.axisPointerModel;
  38577. // If no data, do not create anything in dataByCoordSys,
  38578. // whose length will be used to judge whether dispatch action.
  38579. if (!axisInfo.triggerTooltip || !payloadBatch.length) {
  38580. return;
  38581. }
  38582. var coordSysModel = axisInfo.coordSys.model;
  38583. var coordSysKey = makeKey(coordSysModel);
  38584. var coordSysItem = dataByCoordSys.map[coordSysKey];
  38585. if (!coordSysItem) {
  38586. coordSysItem = dataByCoordSys.map[coordSysKey] = {
  38587. coordSysId: coordSysModel.id,
  38588. coordSysIndex: coordSysModel.componentIndex,
  38589. coordSysType: coordSysModel.type,
  38590. coordSysMainType: coordSysModel.mainType,
  38591. dataByAxis: []
  38592. };
  38593. dataByCoordSys.list.push(coordSysItem);
  38594. }
  38595. coordSysItem.dataByAxis.push({
  38596. axisDim: axis.dim,
  38597. axisIndex: axisModel.componentIndex,
  38598. axisType: axisModel.type,
  38599. axisId: axisModel.id,
  38600. value: value,
  38601. // Caustion: viewHelper.getValueLabel is actually on "view stage", which
  38602. // depends that all models have been updated. So it should not be performed
  38603. // here. Considering axisPointerModel used here is volatile, which is hard
  38604. // to be retrieve in TooltipView, we prepare parameters here.
  38605. valueLabelOpt: {
  38606. precision: axisPointerModel.get('label.precision'),
  38607. formatter: axisPointerModel.get('label.formatter')
  38608. },
  38609. seriesDataIndices: payloadBatch.slice()
  38610. });
  38611. }
  38612. function updateModelActually(showValueMap, axesInfo, outputFinder) {
  38613. var outputAxesInfo = outputFinder.axesInfo = [];
  38614. // Basic logic: If no 'show' required, 'hide' this axisPointer.
  38615. each$7(axesInfo, function (axisInfo, key) {
  38616. var option = axisInfo.axisPointerModel.option;
  38617. var valItem = showValueMap[key];
  38618. if (valItem) {
  38619. !axisInfo.useHandle && (option.status = 'show');
  38620. option.value = valItem.value;
  38621. // For label formatter param and highlight.
  38622. option.seriesDataIndices = (valItem.payloadBatch || []).slice();
  38623. }
  38624. // When always show (e.g., handle used), remain
  38625. // original value and status.
  38626. else {
  38627. // If hide, value still need to be set, consider
  38628. // click legend to toggle axis blank.
  38629. !axisInfo.useHandle && (option.status = 'hide');
  38630. }
  38631. // If status is 'hide', should be no info in payload.
  38632. option.status === 'show' && outputAxesInfo.push({
  38633. axisDim: axisInfo.axis.dim,
  38634. axisIndex: axisInfo.axis.model.componentIndex,
  38635. value: option.value
  38636. });
  38637. });
  38638. }
  38639. function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {
  38640. // Basic logic: If no showTip required, hideTip will be dispatched.
  38641. if (illegalPoint(point) || !dataByCoordSys.list.length) {
  38642. dispatchAction({type: 'hideTip'});
  38643. return;
  38644. }
  38645. // In most case only one axis (or event one series is used). It is
  38646. // convinient to fetch payload.seriesIndex and payload.dataIndex
  38647. // dirtectly. So put the first seriesIndex and dataIndex of the first
  38648. // axis on the payload.
  38649. var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
  38650. dispatchAction({
  38651. type: 'showTip',
  38652. escapeConnect: true,
  38653. x: point[0],
  38654. y: point[1],
  38655. tooltipOption: payload.tooltipOption,
  38656. position: payload.position,
  38657. dataIndexInside: sampleItem.dataIndexInside,
  38658. dataIndex: sampleItem.dataIndex,
  38659. seriesIndex: sampleItem.seriesIndex,
  38660. dataByCoordSys: dataByCoordSys.list
  38661. });
  38662. }
  38663. function dispatchHighDownActually(axesInfo, dispatchAction, api) {
  38664. // FIXME
  38665. // highlight status modification shoule be a stage of main process?
  38666. // (Consider confilct (e.g., legend and axisPointer) and setOption)
  38667. var zr = api.getZr();
  38668. var highDownKey = 'axisPointerLastHighlights';
  38669. var lastHighlights = inner$7(zr)[highDownKey] || {};
  38670. var newHighlights = inner$7(zr)[highDownKey] = {};
  38671. // Update highlight/downplay status according to axisPointer model.
  38672. // Build hash map and remove duplicate incidentally.
  38673. each$7(axesInfo, function (axisInfo, key) {
  38674. var option = axisInfo.axisPointerModel.option;
  38675. option.status === 'show' && each$7(option.seriesDataIndices, function (batchItem) {
  38676. var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
  38677. newHighlights[key] = batchItem;
  38678. });
  38679. });
  38680. // Diff.
  38681. var toHighlight = [];
  38682. var toDownplay = [];
  38683. each$1(lastHighlights, function (batchItem, key) {
  38684. !newHighlights[key] && toDownplay.push(batchItem);
  38685. });
  38686. each$1(newHighlights, function (batchItem, key) {
  38687. !lastHighlights[key] && toHighlight.push(batchItem);
  38688. });
  38689. toDownplay.length && api.dispatchAction({
  38690. type: 'downplay', escapeConnect: true, batch: toDownplay
  38691. });
  38692. toHighlight.length && api.dispatchAction({
  38693. type: 'highlight', escapeConnect: true, batch: toHighlight
  38694. });
  38695. }
  38696. function findInputAxisInfo(inputAxesInfo, axisInfo) {
  38697. for (var i = 0; i < (inputAxesInfo || []).length; i++) {
  38698. var inputAxisInfo = inputAxesInfo[i];
  38699. if (axisInfo.axis.dim === inputAxisInfo.axisDim
  38700. && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex
  38701. ) {
  38702. return inputAxisInfo;
  38703. }
  38704. }
  38705. }
  38706. function makeMapperParam(axisInfo) {
  38707. var axisModel = axisInfo.axis.model;
  38708. var item = {};
  38709. var dim = item.axisDim = axisInfo.axis.dim;
  38710. item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
  38711. item.axisName = item[dim + 'AxisName'] = axisModel.name;
  38712. item.axisId = item[dim + 'AxisId'] = axisModel.id;
  38713. return item;
  38714. }
  38715. function illegalPoint(point) {
  38716. return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
  38717. }
  38718. /*
  38719. * Licensed to the Apache Software Foundation (ASF) under one
  38720. * or more contributor license agreements. See the NOTICE file
  38721. * distributed with this work for additional information
  38722. * regarding copyright ownership. The ASF licenses this file
  38723. * to you under the Apache License, Version 2.0 (the
  38724. * "License"); you may not use this file except in compliance
  38725. * with the License. You may obtain a copy of the License at
  38726. *
  38727. * http://www.apache.org/licenses/LICENSE-2.0
  38728. *
  38729. * Unless required by applicable law or agreed to in writing,
  38730. * software distributed under the License is distributed on an
  38731. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38732. * KIND, either express or implied. See the License for the
  38733. * specific language governing permissions and limitations
  38734. * under the License.
  38735. */
  38736. var AxisPointerModel = extendComponentModel({
  38737. type: 'axisPointer',
  38738. coordSysAxesInfo: null,
  38739. defaultOption: {
  38740. // 'auto' means that show when triggered by tooltip or handle.
  38741. show: 'auto',
  38742. // 'click' | 'mousemove' | 'none'
  38743. triggerOn: null, // set default in AxisPonterView.js
  38744. zlevel: 0,
  38745. z: 50,
  38746. type: 'line', // 'line' 'shadow' 'cross' 'none'.
  38747. // axispointer triggered by tootip determine snap automatically,
  38748. // see `modelHelper`.
  38749. snap: false,
  38750. triggerTooltip: true,
  38751. value: null,
  38752. status: null, // Init value depends on whether handle is used.
  38753. // [group0, group1, ...]
  38754. // Each group can be: {
  38755. // mapper: function () {},
  38756. // singleTooltip: 'multiple', // 'multiple' or 'single'
  38757. // xAxisId: ...,
  38758. // yAxisName: ...,
  38759. // angleAxisIndex: ...
  38760. // }
  38761. // mapper: can be ignored.
  38762. // input: {axisInfo, value}
  38763. // output: {axisInfo, value}
  38764. link: [],
  38765. // Do not set 'auto' here, otherwise global animation: false
  38766. // will not effect at this axispointer.
  38767. animation: null,
  38768. animationDurationUpdate: 200,
  38769. lineStyle: {
  38770. color: '#aaa',
  38771. width: 1,
  38772. type: 'solid'
  38773. },
  38774. shadowStyle: {
  38775. color: 'rgba(150,150,150,0.3)'
  38776. },
  38777. label: {
  38778. show: true,
  38779. formatter: null, // string | Function
  38780. precision: 'auto', // Or a number like 0, 1, 2 ...
  38781. margin: 3,
  38782. color: '#fff',
  38783. padding: [5, 7, 5, 7],
  38784. backgroundColor: 'auto', // default: axis line color
  38785. borderColor: null,
  38786. borderWidth: 0,
  38787. shadowBlur: 3,
  38788. shadowColor: '#aaa'
  38789. // Considering applicability, common style should
  38790. // better not have shadowOffset.
  38791. // shadowOffsetX: 0,
  38792. // shadowOffsetY: 2
  38793. },
  38794. handle: {
  38795. show: false,
  38796. /* eslint-disable */
  38797. icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line
  38798. /* eslint-enable */
  38799. size: 45,
  38800. // handle margin is from symbol center to axis, which is stable when circular move.
  38801. margin: 50,
  38802. // color: '#1b8bbd'
  38803. // color: '#2f4554'
  38804. color: '#333',
  38805. shadowBlur: 3,
  38806. shadowColor: '#aaa',
  38807. shadowOffsetX: 0,
  38808. shadowOffsetY: 2,
  38809. // For mobile performance
  38810. throttle: 40
  38811. }
  38812. }
  38813. });
  38814. /*
  38815. * Licensed to the Apache Software Foundation (ASF) under one
  38816. * or more contributor license agreements. See the NOTICE file
  38817. * distributed with this work for additional information
  38818. * regarding copyright ownership. The ASF licenses this file
  38819. * to you under the Apache License, Version 2.0 (the
  38820. * "License"); you may not use this file except in compliance
  38821. * with the License. You may obtain a copy of the License at
  38822. *
  38823. * http://www.apache.org/licenses/LICENSE-2.0
  38824. *
  38825. * Unless required by applicable law or agreed to in writing,
  38826. * software distributed under the License is distributed on an
  38827. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38828. * KIND, either express or implied. See the License for the
  38829. * specific language governing permissions and limitations
  38830. * under the License.
  38831. */
  38832. var inner$8 = makeInner();
  38833. var each$8 = each$1;
  38834. /**
  38835. * @param {string} key
  38836. * @param {module:echarts/ExtensionAPI} api
  38837. * @param {Function} handler
  38838. * param: {string} currTrigger
  38839. * param: {Array.<number>} point
  38840. */
  38841. function register(key, api, handler) {
  38842. if (env$1.node) {
  38843. return;
  38844. }
  38845. var zr = api.getZr();
  38846. inner$8(zr).records || (inner$8(zr).records = {});
  38847. initGlobalListeners(zr, api);
  38848. var record = inner$8(zr).records[key] || (inner$8(zr).records[key] = {});
  38849. record.handler = handler;
  38850. }
  38851. function initGlobalListeners(zr, api) {
  38852. if (inner$8(zr).initialized) {
  38853. return;
  38854. }
  38855. inner$8(zr).initialized = true;
  38856. useHandler('click', curry(doEnter, 'click'));
  38857. useHandler('mousemove', curry(doEnter, 'mousemove'));
  38858. // useHandler('mouseout', onLeave);
  38859. useHandler('globalout', onLeave);
  38860. function useHandler(eventType, cb) {
  38861. zr.on(eventType, function (e) {
  38862. var dis = makeDispatchAction(api);
  38863. each$8(inner$8(zr).records, function (record) {
  38864. record && cb(record, e, dis.dispatchAction);
  38865. });
  38866. dispatchTooltipFinally(dis.pendings, api);
  38867. });
  38868. }
  38869. }
  38870. function dispatchTooltipFinally(pendings, api) {
  38871. var showLen = pendings.showTip.length;
  38872. var hideLen = pendings.hideTip.length;
  38873. var actuallyPayload;
  38874. if (showLen) {
  38875. actuallyPayload = pendings.showTip[showLen - 1];
  38876. }
  38877. else if (hideLen) {
  38878. actuallyPayload = pendings.hideTip[hideLen - 1];
  38879. }
  38880. if (actuallyPayload) {
  38881. actuallyPayload.dispatchAction = null;
  38882. api.dispatchAction(actuallyPayload);
  38883. }
  38884. }
  38885. function onLeave(record, e, dispatchAction) {
  38886. record.handler('leave', null, dispatchAction);
  38887. }
  38888. function doEnter(currTrigger, record, e, dispatchAction) {
  38889. record.handler(currTrigger, e, dispatchAction);
  38890. }
  38891. function makeDispatchAction(api) {
  38892. var pendings = {
  38893. showTip: [],
  38894. hideTip: []
  38895. };
  38896. // FIXME
  38897. // better approach?
  38898. // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
  38899. // which may be conflict, (axisPointer call showTip but tooltip call hideTip);
  38900. // So we have to add "final stage" to merge those dispatched actions.
  38901. var dispatchAction = function (payload) {
  38902. var pendingList = pendings[payload.type];
  38903. if (pendingList) {
  38904. pendingList.push(payload);
  38905. }
  38906. else {
  38907. payload.dispatchAction = dispatchAction;
  38908. api.dispatchAction(payload);
  38909. }
  38910. };
  38911. return {
  38912. dispatchAction: dispatchAction,
  38913. pendings: pendings
  38914. };
  38915. }
  38916. /**
  38917. * @param {string} key
  38918. * @param {module:echarts/ExtensionAPI} api
  38919. */
  38920. function unregister(key, api) {
  38921. if (env$1.node) {
  38922. return;
  38923. }
  38924. var zr = api.getZr();
  38925. var record = (inner$8(zr).records || {})[key];
  38926. if (record) {
  38927. inner$8(zr).records[key] = null;
  38928. }
  38929. }
  38930. /*
  38931. * Licensed to the Apache Software Foundation (ASF) under one
  38932. * or more contributor license agreements. See the NOTICE file
  38933. * distributed with this work for additional information
  38934. * regarding copyright ownership. The ASF licenses this file
  38935. * to you under the Apache License, Version 2.0 (the
  38936. * "License"); you may not use this file except in compliance
  38937. * with the License. You may obtain a copy of the License at
  38938. *
  38939. * http://www.apache.org/licenses/LICENSE-2.0
  38940. *
  38941. * Unless required by applicable law or agreed to in writing,
  38942. * software distributed under the License is distributed on an
  38943. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38944. * KIND, either express or implied. See the License for the
  38945. * specific language governing permissions and limitations
  38946. * under the License.
  38947. */
  38948. var AxisPointerView = extendComponentView({
  38949. type: 'axisPointer',
  38950. render: function (globalAxisPointerModel, ecModel, api) {
  38951. var globalTooltipModel = ecModel.getComponent('tooltip');
  38952. var triggerOn = globalAxisPointerModel.get('triggerOn')
  38953. || (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click');
  38954. // Register global listener in AxisPointerView to enable
  38955. // AxisPointerView to be independent to Tooltip.
  38956. register(
  38957. 'axisPointer',
  38958. api,
  38959. function (currTrigger, e, dispatchAction) {
  38960. // If 'none', it is not controlled by mouse totally.
  38961. if (triggerOn !== 'none'
  38962. && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)
  38963. ) {
  38964. dispatchAction({
  38965. type: 'updateAxisPointer',
  38966. currTrigger: currTrigger,
  38967. x: e && e.offsetX,
  38968. y: e && e.offsetY
  38969. });
  38970. }
  38971. }
  38972. );
  38973. },
  38974. /**
  38975. * @override
  38976. */
  38977. remove: function (ecModel, api) {
  38978. unregister(api.getZr(), 'axisPointer');
  38979. AxisPointerView.superApply(this._model, 'remove', arguments);
  38980. },
  38981. /**
  38982. * @override
  38983. */
  38984. dispose: function (ecModel, api) {
  38985. unregister('axisPointer', api);
  38986. AxisPointerView.superApply(this._model, 'dispose', arguments);
  38987. }
  38988. });
  38989. /*
  38990. * Licensed to the Apache Software Foundation (ASF) under one
  38991. * or more contributor license agreements. See the NOTICE file
  38992. * distributed with this work for additional information
  38993. * regarding copyright ownership. The ASF licenses this file
  38994. * to you under the Apache License, Version 2.0 (the
  38995. * "License"); you may not use this file except in compliance
  38996. * with the License. You may obtain a copy of the License at
  38997. *
  38998. * http://www.apache.org/licenses/LICENSE-2.0
  38999. *
  39000. * Unless required by applicable law or agreed to in writing,
  39001. * software distributed under the License is distributed on an
  39002. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39003. * KIND, either express or implied. See the License for the
  39004. * specific language governing permissions and limitations
  39005. * under the License.
  39006. */
  39007. var inner$9 = makeInner();
  39008. var clone$4 = clone;
  39009. var bind$1 = bind;
  39010. /**
  39011. * Base axis pointer class in 2D.
  39012. * Implemenents {module:echarts/component/axis/IAxisPointer}.
  39013. */
  39014. function BaseAxisPointer() {
  39015. }
  39016. BaseAxisPointer.prototype = {
  39017. /**
  39018. * @private
  39019. */
  39020. _group: null,
  39021. /**
  39022. * @private
  39023. */
  39024. _lastGraphicKey: null,
  39025. /**
  39026. * @private
  39027. */
  39028. _handle: null,
  39029. /**
  39030. * @private
  39031. */
  39032. _dragging: false,
  39033. /**
  39034. * @private
  39035. */
  39036. _lastValue: null,
  39037. /**
  39038. * @private
  39039. */
  39040. _lastStatus: null,
  39041. /**
  39042. * @private
  39043. */
  39044. _payloadInfo: null,
  39045. /**
  39046. * In px, arbitrary value. Do not set too small,
  39047. * no animation is ok for most cases.
  39048. * @protected
  39049. */
  39050. animationThreshold: 15,
  39051. /**
  39052. * @implement
  39053. */
  39054. render: function (axisModel, axisPointerModel, api, forceRender) {
  39055. var value = axisPointerModel.get('value');
  39056. var status = axisPointerModel.get('status');
  39057. // Bind them to `this`, not in closure, otherwise they will not
  39058. // be replaced when user calling setOption in not merge mode.
  39059. this._axisModel = axisModel;
  39060. this._axisPointerModel = axisPointerModel;
  39061. this._api = api;
  39062. // Optimize: `render` will be called repeatly during mouse move.
  39063. // So it is power consuming if performing `render` each time,
  39064. // especially on mobile device.
  39065. if (!forceRender
  39066. && this._lastValue === value
  39067. && this._lastStatus === status
  39068. ) {
  39069. return;
  39070. }
  39071. this._lastValue = value;
  39072. this._lastStatus = status;
  39073. var group = this._group;
  39074. var handle = this._handle;
  39075. if (!status || status === 'hide') {
  39076. // Do not clear here, for animation better.
  39077. group && group.hide();
  39078. handle && handle.hide();
  39079. return;
  39080. }
  39081. group && group.show();
  39082. handle && handle.show();
  39083. // Otherwise status is 'show'
  39084. var elOption = {};
  39085. this.makeElOption(elOption, value, axisModel, axisPointerModel, api);
  39086. // Enable change axis pointer type.
  39087. var graphicKey = elOption.graphicKey;
  39088. if (graphicKey !== this._lastGraphicKey) {
  39089. this.clear(api);
  39090. }
  39091. this._lastGraphicKey = graphicKey;
  39092. var moveAnimation = this._moveAnimation
  39093. = this.determineAnimation(axisModel, axisPointerModel);
  39094. if (!group) {
  39095. group = this._group = new Group();
  39096. this.createPointerEl(group, elOption, axisModel, axisPointerModel);
  39097. this.createLabelEl(group, elOption, axisModel, axisPointerModel);
  39098. api.getZr().add(group);
  39099. }
  39100. else {
  39101. var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);
  39102. this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel);
  39103. this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);
  39104. }
  39105. updateMandatoryProps(group, axisPointerModel, true);
  39106. this._renderHandle(value);
  39107. },
  39108. /**
  39109. * @implement
  39110. */
  39111. remove: function (api) {
  39112. this.clear(api);
  39113. },
  39114. /**
  39115. * @implement
  39116. */
  39117. dispose: function (api) {
  39118. this.clear(api);
  39119. },
  39120. /**
  39121. * @protected
  39122. */
  39123. determineAnimation: function (axisModel, axisPointerModel) {
  39124. var animation = axisPointerModel.get('animation');
  39125. var axis = axisModel.axis;
  39126. var isCategoryAxis = axis.type === 'category';
  39127. var useSnap = axisPointerModel.get('snap');
  39128. // Value axis without snap always do not snap.
  39129. if (!useSnap && !isCategoryAxis) {
  39130. return false;
  39131. }
  39132. if (animation === 'auto' || animation == null) {
  39133. var animationThreshold = this.animationThreshold;
  39134. if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {
  39135. return true;
  39136. }
  39137. // It is important to auto animation when snap used. Consider if there is
  39138. // a dataZoom, animation will be disabled when too many points exist, while
  39139. // it will be enabled for better visual effect when little points exist.
  39140. if (useSnap) {
  39141. var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;
  39142. var axisExtent = axis.getExtent();
  39143. // Approximate band width
  39144. return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;
  39145. }
  39146. return false;
  39147. }
  39148. return animation === true;
  39149. },
  39150. /**
  39151. * add {pointer, label, graphicKey} to elOption
  39152. * @protected
  39153. */
  39154. makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
  39155. // Shoule be implemenented by sub-class.
  39156. },
  39157. /**
  39158. * @protected
  39159. */
  39160. createPointerEl: function (group, elOption, axisModel, axisPointerModel) {
  39161. var pointerOption = elOption.pointer;
  39162. if (pointerOption) {
  39163. var pointerEl = inner$9(group).pointerEl = new graphic[pointerOption.type](
  39164. clone$4(elOption.pointer)
  39165. );
  39166. group.add(pointerEl);
  39167. }
  39168. },
  39169. /**
  39170. * @protected
  39171. */
  39172. createLabelEl: function (group, elOption, axisModel, axisPointerModel) {
  39173. if (elOption.label) {
  39174. var labelEl = inner$9(group).labelEl = new Rect(
  39175. clone$4(elOption.label)
  39176. );
  39177. group.add(labelEl);
  39178. updateLabelShowHide(labelEl, axisPointerModel);
  39179. }
  39180. },
  39181. /**
  39182. * @protected
  39183. */
  39184. updatePointerEl: function (group, elOption, updateProps$$1) {
  39185. var pointerEl = inner$9(group).pointerEl;
  39186. if (pointerEl) {
  39187. pointerEl.setStyle(elOption.pointer.style);
  39188. updateProps$$1(pointerEl, {shape: elOption.pointer.shape});
  39189. }
  39190. },
  39191. /**
  39192. * @protected
  39193. */
  39194. updateLabelEl: function (group, elOption, updateProps$$1, axisPointerModel) {
  39195. var labelEl = inner$9(group).labelEl;
  39196. if (labelEl) {
  39197. labelEl.setStyle(elOption.label.style);
  39198. updateProps$$1(labelEl, {
  39199. // Consider text length change in vertical axis, animation should
  39200. // be used on shape, otherwise the effect will be weird.
  39201. shape: elOption.label.shape,
  39202. position: elOption.label.position
  39203. });
  39204. updateLabelShowHide(labelEl, axisPointerModel);
  39205. }
  39206. },
  39207. /**
  39208. * @private
  39209. */
  39210. _renderHandle: function (value) {
  39211. if (this._dragging || !this.updateHandleTransform) {
  39212. return;
  39213. }
  39214. var axisPointerModel = this._axisPointerModel;
  39215. var zr = this._api.getZr();
  39216. var handle = this._handle;
  39217. var handleModel = axisPointerModel.getModel('handle');
  39218. var status = axisPointerModel.get('status');
  39219. if (!handleModel.get('show') || !status || status === 'hide') {
  39220. handle && zr.remove(handle);
  39221. this._handle = null;
  39222. return;
  39223. }
  39224. var isInit;
  39225. if (!this._handle) {
  39226. isInit = true;
  39227. handle = this._handle = createIcon(
  39228. handleModel.get('icon'),
  39229. {
  39230. cursor: 'move',
  39231. draggable: true,
  39232. onmousemove: function (e) {
  39233. // Fot mobile devicem, prevent screen slider on the button.
  39234. stop(e.event);
  39235. },
  39236. onmousedown: bind$1(this._onHandleDragMove, this, 0, 0),
  39237. drift: bind$1(this._onHandleDragMove, this),
  39238. ondragend: bind$1(this._onHandleDragEnd, this)
  39239. }
  39240. );
  39241. zr.add(handle);
  39242. }
  39243. updateMandatoryProps(handle, axisPointerModel, false);
  39244. // update style
  39245. var includeStyles = [
  39246. 'color', 'borderColor', 'borderWidth', 'opacity',
  39247. 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'
  39248. ];
  39249. handle.setStyle(handleModel.getItemStyle(null, includeStyles));
  39250. // update position
  39251. var handleSize = handleModel.get('size');
  39252. if (!isArray(handleSize)) {
  39253. handleSize = [handleSize, handleSize];
  39254. }
  39255. handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
  39256. createOrUpdate(
  39257. this,
  39258. '_doDispatchAxisPointer',
  39259. handleModel.get('throttle') || 0,
  39260. 'fixRate'
  39261. );
  39262. this._moveHandleToValue(value, isInit);
  39263. },
  39264. /**
  39265. * @private
  39266. */
  39267. _moveHandleToValue: function (value, isInit) {
  39268. updateProps$1(
  39269. this._axisPointerModel,
  39270. !isInit && this._moveAnimation,
  39271. this._handle,
  39272. getHandleTransProps(this.getHandleTransform(
  39273. value, this._axisModel, this._axisPointerModel
  39274. ))
  39275. );
  39276. },
  39277. /**
  39278. * @private
  39279. */
  39280. _onHandleDragMove: function (dx, dy) {
  39281. var handle = this._handle;
  39282. if (!handle) {
  39283. return;
  39284. }
  39285. this._dragging = true;
  39286. // Persistent for throttle.
  39287. var trans = this.updateHandleTransform(
  39288. getHandleTransProps(handle),
  39289. [dx, dy],
  39290. this._axisModel,
  39291. this._axisPointerModel
  39292. );
  39293. this._payloadInfo = trans;
  39294. handle.stopAnimation();
  39295. handle.attr(getHandleTransProps(trans));
  39296. inner$9(handle).lastProp = null;
  39297. this._doDispatchAxisPointer();
  39298. },
  39299. /**
  39300. * Throttled method.
  39301. * @private
  39302. */
  39303. _doDispatchAxisPointer: function () {
  39304. var handle = this._handle;
  39305. if (!handle) {
  39306. return;
  39307. }
  39308. var payloadInfo = this._payloadInfo;
  39309. var axisModel = this._axisModel;
  39310. this._api.dispatchAction({
  39311. type: 'updateAxisPointer',
  39312. x: payloadInfo.cursorPoint[0],
  39313. y: payloadInfo.cursorPoint[1],
  39314. tooltipOption: payloadInfo.tooltipOption,
  39315. axesInfo: [{
  39316. axisDim: axisModel.axis.dim,
  39317. axisIndex: axisModel.componentIndex
  39318. }]
  39319. });
  39320. },
  39321. /**
  39322. * @private
  39323. */
  39324. _onHandleDragEnd: function (moveAnimation) {
  39325. this._dragging = false;
  39326. var handle = this._handle;
  39327. if (!handle) {
  39328. return;
  39329. }
  39330. var value = this._axisPointerModel.get('value');
  39331. // Consider snap or categroy axis, handle may be not consistent with
  39332. // axisPointer. So move handle to align the exact value position when
  39333. // drag ended.
  39334. this._moveHandleToValue(value);
  39335. // For the effect: tooltip will be shown when finger holding on handle
  39336. // button, and will be hidden after finger left handle button.
  39337. this._api.dispatchAction({
  39338. type: 'hideTip'
  39339. });
  39340. },
  39341. /**
  39342. * Should be implemenented by sub-class if support `handle`.
  39343. * @protected
  39344. * @param {number} value
  39345. * @param {module:echarts/model/Model} axisModel
  39346. * @param {module:echarts/model/Model} axisPointerModel
  39347. * @return {Object} {position: [x, y], rotation: 0}
  39348. */
  39349. getHandleTransform: null,
  39350. /**
  39351. * * Should be implemenented by sub-class if support `handle`.
  39352. * @protected
  39353. * @param {Object} transform {position, rotation}
  39354. * @param {Array.<number>} delta [dx, dy]
  39355. * @param {module:echarts/model/Model} axisModel
  39356. * @param {module:echarts/model/Model} axisPointerModel
  39357. * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]}
  39358. */
  39359. updateHandleTransform: null,
  39360. /**
  39361. * @private
  39362. */
  39363. clear: function (api) {
  39364. this._lastValue = null;
  39365. this._lastStatus = null;
  39366. var zr = api.getZr();
  39367. var group = this._group;
  39368. var handle = this._handle;
  39369. if (zr && group) {
  39370. this._lastGraphicKey = null;
  39371. group && zr.remove(group);
  39372. handle && zr.remove(handle);
  39373. this._group = null;
  39374. this._handle = null;
  39375. this._payloadInfo = null;
  39376. }
  39377. },
  39378. /**
  39379. * @protected
  39380. */
  39381. doClear: function () {
  39382. // Implemented by sub-class if necessary.
  39383. },
  39384. /**
  39385. * @protected
  39386. * @param {Array.<number>} xy
  39387. * @param {Array.<number>} wh
  39388. * @param {number} [xDimIndex=0] or 1
  39389. */
  39390. buildLabel: function (xy, wh, xDimIndex) {
  39391. xDimIndex = xDimIndex || 0;
  39392. return {
  39393. x: xy[xDimIndex],
  39394. y: xy[1 - xDimIndex],
  39395. width: wh[xDimIndex],
  39396. height: wh[1 - xDimIndex]
  39397. };
  39398. }
  39399. };
  39400. BaseAxisPointer.prototype.constructor = BaseAxisPointer;
  39401. function updateProps$1(animationModel, moveAnimation, el, props) {
  39402. // Animation optimize.
  39403. if (!propsEqual(inner$9(el).lastProp, props)) {
  39404. inner$9(el).lastProp = props;
  39405. moveAnimation
  39406. ? updateProps(el, props, animationModel)
  39407. : (el.stopAnimation(), el.attr(props));
  39408. }
  39409. }
  39410. function propsEqual(lastProps, newProps) {
  39411. if (isObject$1(lastProps) && isObject$1(newProps)) {
  39412. var equals = true;
  39413. each$1(newProps, function (item, key) {
  39414. equals = equals && propsEqual(lastProps[key], item);
  39415. });
  39416. return !!equals;
  39417. }
  39418. else {
  39419. return lastProps === newProps;
  39420. }
  39421. }
  39422. function updateLabelShowHide(labelEl, axisPointerModel) {
  39423. labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide']();
  39424. }
  39425. function getHandleTransProps(trans) {
  39426. return {
  39427. position: trans.position.slice(),
  39428. rotation: trans.rotation || 0
  39429. };
  39430. }
  39431. function updateMandatoryProps(group, axisPointerModel, silent) {
  39432. var z = axisPointerModel.get('z');
  39433. var zlevel = axisPointerModel.get('zlevel');
  39434. group && group.traverse(function (el) {
  39435. if (el.type !== 'group') {
  39436. z != null && (el.z = z);
  39437. zlevel != null && (el.zlevel = zlevel);
  39438. el.silent = silent;
  39439. }
  39440. });
  39441. }
  39442. enableClassExtend(BaseAxisPointer);
  39443. /*
  39444. * Licensed to the Apache Software Foundation (ASF) under one
  39445. * or more contributor license agreements. See the NOTICE file
  39446. * distributed with this work for additional information
  39447. * regarding copyright ownership. The ASF licenses this file
  39448. * to you under the Apache License, Version 2.0 (the
  39449. * "License"); you may not use this file except in compliance
  39450. * with the License. You may obtain a copy of the License at
  39451. *
  39452. * http://www.apache.org/licenses/LICENSE-2.0
  39453. *
  39454. * Unless required by applicable law or agreed to in writing,
  39455. * software distributed under the License is distributed on an
  39456. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39457. * KIND, either express or implied. See the License for the
  39458. * specific language governing permissions and limitations
  39459. * under the License.
  39460. */
  39461. /**
  39462. * @param {module:echarts/model/Model} axisPointerModel
  39463. */
  39464. function buildElStyle(axisPointerModel) {
  39465. var axisPointerType = axisPointerModel.get('type');
  39466. var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');
  39467. var style;
  39468. if (axisPointerType === 'line') {
  39469. style = styleModel.getLineStyle();
  39470. style.fill = null;
  39471. }
  39472. else if (axisPointerType === 'shadow') {
  39473. style = styleModel.getAreaStyle();
  39474. style.stroke = null;
  39475. }
  39476. return style;
  39477. }
  39478. /**
  39479. * @param {Function} labelPos {align, verticalAlign, position}
  39480. */
  39481. function buildLabelElOption(
  39482. elOption, axisModel, axisPointerModel, api, labelPos
  39483. ) {
  39484. var value = axisPointerModel.get('value');
  39485. var text = getValueLabel(
  39486. value, axisModel.axis, axisModel.ecModel,
  39487. axisPointerModel.get('seriesDataIndices'),
  39488. {
  39489. precision: axisPointerModel.get('label.precision'),
  39490. formatter: axisPointerModel.get('label.formatter')
  39491. }
  39492. );
  39493. var labelModel = axisPointerModel.getModel('label');
  39494. var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);
  39495. var font = labelModel.getFont();
  39496. var textRect = getBoundingRect(text, font);
  39497. var position = labelPos.position;
  39498. var width = textRect.width + paddings[1] + paddings[3];
  39499. var height = textRect.height + paddings[0] + paddings[2];
  39500. // Adjust by align.
  39501. var align = labelPos.align;
  39502. align === 'right' && (position[0] -= width);
  39503. align === 'center' && (position[0] -= width / 2);
  39504. var verticalAlign = labelPos.verticalAlign;
  39505. verticalAlign === 'bottom' && (position[1] -= height);
  39506. verticalAlign === 'middle' && (position[1] -= height / 2);
  39507. // Not overflow ec container
  39508. confineInContainer(position, width, height, api);
  39509. var bgColor = labelModel.get('backgroundColor');
  39510. if (!bgColor || bgColor === 'auto') {
  39511. bgColor = axisModel.get('axisLine.lineStyle.color');
  39512. }
  39513. elOption.label = {
  39514. shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},
  39515. position: position.slice(),
  39516. // TODO: rich
  39517. style: {
  39518. text: text,
  39519. textFont: font,
  39520. textFill: labelModel.getTextColor(),
  39521. textPosition: 'inside',
  39522. fill: bgColor,
  39523. stroke: labelModel.get('borderColor') || 'transparent',
  39524. lineWidth: labelModel.get('borderWidth') || 0,
  39525. shadowBlur: labelModel.get('shadowBlur'),
  39526. shadowColor: labelModel.get('shadowColor'),
  39527. shadowOffsetX: labelModel.get('shadowOffsetX'),
  39528. shadowOffsetY: labelModel.get('shadowOffsetY')
  39529. },
  39530. // Lable should be over axisPointer.
  39531. z2: 10
  39532. };
  39533. }
  39534. // Do not overflow ec container
  39535. function confineInContainer(position, width, height, api) {
  39536. var viewWidth = api.getWidth();
  39537. var viewHeight = api.getHeight();
  39538. position[0] = Math.min(position[0] + width, viewWidth) - width;
  39539. position[1] = Math.min(position[1] + height, viewHeight) - height;
  39540. position[0] = Math.max(position[0], 0);
  39541. position[1] = Math.max(position[1], 0);
  39542. }
  39543. /**
  39544. * @param {number} value
  39545. * @param {module:echarts/coord/Axis} axis
  39546. * @param {module:echarts/model/Global} ecModel
  39547. * @param {Object} opt
  39548. * @param {Array.<Object>} seriesDataIndices
  39549. * @param {number|string} opt.precision 'auto' or a number
  39550. * @param {string|Function} opt.formatter label formatter
  39551. */
  39552. function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {
  39553. value = axis.scale.parse(value);
  39554. var text = axis.scale.getLabel(
  39555. // If `precision` is set, width can be fixed (like '12.00500'), which
  39556. // helps to debounce when when moving label.
  39557. value, {precision: opt.precision}
  39558. );
  39559. var formatter = opt.formatter;
  39560. if (formatter) {
  39561. var params = {
  39562. value: getAxisRawValue(axis, value),
  39563. seriesData: []
  39564. };
  39565. each$1(seriesDataIndices, function (idxItem) {
  39566. var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
  39567. var dataIndex = idxItem.dataIndexInside;
  39568. var dataParams = series && series.getDataParams(dataIndex);
  39569. dataParams && params.seriesData.push(dataParams);
  39570. });
  39571. if (isString(formatter)) {
  39572. text = formatter.replace('{value}', text);
  39573. }
  39574. else if (isFunction$1(formatter)) {
  39575. text = formatter(params);
  39576. }
  39577. }
  39578. return text;
  39579. }
  39580. /**
  39581. * @param {module:echarts/coord/Axis} axis
  39582. * @param {number} value
  39583. * @param {Object} layoutInfo {
  39584. * rotation, position, labelOffset, labelDirection, labelMargin
  39585. * }
  39586. */
  39587. function getTransformedPosition(axis, value, layoutInfo) {
  39588. var transform = create$1();
  39589. rotate(transform, transform, layoutInfo.rotation);
  39590. translate(transform, transform, layoutInfo.position);
  39591. return applyTransform$1([
  39592. axis.dataToCoord(value),
  39593. (layoutInfo.labelOffset || 0)
  39594. + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)
  39595. ], transform);
  39596. }
  39597. function buildCartesianSingleLabelElOption(
  39598. value, elOption, layoutInfo, axisModel, axisPointerModel, api
  39599. ) {
  39600. var textLayout = AxisBuilder.innerTextLayout(
  39601. layoutInfo.rotation, 0, layoutInfo.labelDirection
  39602. );
  39603. layoutInfo.labelMargin = axisPointerModel.get('label.margin');
  39604. buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
  39605. position: getTransformedPosition(axisModel.axis, value, layoutInfo),
  39606. align: textLayout.textAlign,
  39607. verticalAlign: textLayout.textVerticalAlign
  39608. });
  39609. }
  39610. /**
  39611. * @param {Array.<number>} p1
  39612. * @param {Array.<number>} p2
  39613. * @param {number} [xDimIndex=0] or 1
  39614. */
  39615. function makeLineShape(p1, p2, xDimIndex) {
  39616. xDimIndex = xDimIndex || 0;
  39617. return {
  39618. x1: p1[xDimIndex],
  39619. y1: p1[1 - xDimIndex],
  39620. x2: p2[xDimIndex],
  39621. y2: p2[1 - xDimIndex]
  39622. };
  39623. }
  39624. /**
  39625. * @param {Array.<number>} xy
  39626. * @param {Array.<number>} wh
  39627. * @param {number} [xDimIndex=0] or 1
  39628. */
  39629. function makeRectShape(xy, wh, xDimIndex) {
  39630. xDimIndex = xDimIndex || 0;
  39631. return {
  39632. x: xy[xDimIndex],
  39633. y: xy[1 - xDimIndex],
  39634. width: wh[xDimIndex],
  39635. height: wh[1 - xDimIndex]
  39636. };
  39637. }
  39638. /*
  39639. * Licensed to the Apache Software Foundation (ASF) under one
  39640. * or more contributor license agreements. See the NOTICE file
  39641. * distributed with this work for additional information
  39642. * regarding copyright ownership. The ASF licenses this file
  39643. * to you under the Apache License, Version 2.0 (the
  39644. * "License"); you may not use this file except in compliance
  39645. * with the License. You may obtain a copy of the License at
  39646. *
  39647. * http://www.apache.org/licenses/LICENSE-2.0
  39648. *
  39649. * Unless required by applicable law or agreed to in writing,
  39650. * software distributed under the License is distributed on an
  39651. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39652. * KIND, either express or implied. See the License for the
  39653. * specific language governing permissions and limitations
  39654. * under the License.
  39655. */
  39656. var CartesianAxisPointer = BaseAxisPointer.extend({
  39657. /**
  39658. * @override
  39659. */
  39660. makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
  39661. var axis = axisModel.axis;
  39662. var grid = axis.grid;
  39663. var axisPointerType = axisPointerModel.get('type');
  39664. var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
  39665. var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));
  39666. if (axisPointerType && axisPointerType !== 'none') {
  39667. var elStyle = buildElStyle(axisPointerModel);
  39668. var pointerOption = pointerShapeBuilder[axisPointerType](
  39669. axis, pixelValue, otherExtent, elStyle
  39670. );
  39671. pointerOption.style = elStyle;
  39672. elOption.graphicKey = pointerOption.type;
  39673. elOption.pointer = pointerOption;
  39674. }
  39675. var layoutInfo = layout$1(grid.model, axisModel);
  39676. buildCartesianSingleLabelElOption(
  39677. value, elOption, layoutInfo, axisModel, axisPointerModel, api
  39678. );
  39679. },
  39680. /**
  39681. * @override
  39682. */
  39683. getHandleTransform: function (value, axisModel, axisPointerModel) {
  39684. var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {
  39685. labelInside: false
  39686. });
  39687. layoutInfo.labelMargin = axisPointerModel.get('handle.margin');
  39688. return {
  39689. position: getTransformedPosition(axisModel.axis, value, layoutInfo),
  39690. rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
  39691. };
  39692. },
  39693. /**
  39694. * @override
  39695. */
  39696. updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) {
  39697. var axis = axisModel.axis;
  39698. var grid = axis.grid;
  39699. var axisExtent = axis.getGlobalExtent(true);
  39700. var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
  39701. var dimIndex = axis.dim === 'x' ? 0 : 1;
  39702. var currPosition = transform.position;
  39703. currPosition[dimIndex] += delta[dimIndex];
  39704. currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
  39705. currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
  39706. var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;
  39707. var cursorPoint = [cursorOtherValue, cursorOtherValue];
  39708. cursorPoint[dimIndex] = currPosition[dimIndex];
  39709. // Make tooltip do not overlap axisPointer and in the middle of the grid.
  39710. var tooltipOptions = [{verticalAlign: 'middle'}, {align: 'center'}];
  39711. return {
  39712. position: currPosition,
  39713. rotation: transform.rotation,
  39714. cursorPoint: cursorPoint,
  39715. tooltipOption: tooltipOptions[dimIndex]
  39716. };
  39717. }
  39718. });
  39719. function getCartesian(grid, axis) {
  39720. var opt = {};
  39721. opt[axis.dim + 'AxisIndex'] = axis.index;
  39722. return grid.getCartesian(opt);
  39723. }
  39724. var pointerShapeBuilder = {
  39725. line: function (axis, pixelValue, otherExtent, elStyle) {
  39726. var targetShape = makeLineShape(
  39727. [pixelValue, otherExtent[0]],
  39728. [pixelValue, otherExtent[1]],
  39729. getAxisDimIndex(axis)
  39730. );
  39731. subPixelOptimizeLine({
  39732. shape: targetShape,
  39733. style: elStyle
  39734. });
  39735. return {
  39736. type: 'Line',
  39737. shape: targetShape
  39738. };
  39739. },
  39740. shadow: function (axis, pixelValue, otherExtent, elStyle) {
  39741. var bandWidth = Math.max(1, axis.getBandWidth());
  39742. var span = otherExtent[1] - otherExtent[0];
  39743. return {
  39744. type: 'Rect',
  39745. shape: makeRectShape(
  39746. [pixelValue - bandWidth / 2, otherExtent[0]],
  39747. [bandWidth, span],
  39748. getAxisDimIndex(axis)
  39749. )
  39750. };
  39751. }
  39752. };
  39753. function getAxisDimIndex(axis) {
  39754. return axis.dim === 'x' ? 0 : 1;
  39755. }
  39756. AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);
  39757. /*
  39758. * Licensed to the Apache Software Foundation (ASF) under one
  39759. * or more contributor license agreements. See the NOTICE file
  39760. * distributed with this work for additional information
  39761. * regarding copyright ownership. The ASF licenses this file
  39762. * to you under the Apache License, Version 2.0 (the
  39763. * "License"); you may not use this file except in compliance
  39764. * with the License. You may obtain a copy of the License at
  39765. *
  39766. * http://www.apache.org/licenses/LICENSE-2.0
  39767. *
  39768. * Unless required by applicable law or agreed to in writing,
  39769. * software distributed under the License is distributed on an
  39770. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39771. * KIND, either express or implied. See the License for the
  39772. * specific language governing permissions and limitations
  39773. * under the License.
  39774. */
  39775. // CartesianAxisPointer is not supposed to be required here. But consider
  39776. // echarts.simple.js and online build tooltip, which only require gridSimple,
  39777. // CartesianAxisPointer should be able to required somewhere.
  39778. registerPreprocessor(function (option) {
  39779. // Always has a global axisPointerModel for default setting.
  39780. if (option) {
  39781. (!option.axisPointer || option.axisPointer.length === 0)
  39782. && (option.axisPointer = {});
  39783. var link = option.axisPointer.link;
  39784. // Normalize to array to avoid object mergin. But if link
  39785. // is not set, remain null/undefined, otherwise it will
  39786. // override existent link setting.
  39787. if (link && !isArray(link)) {
  39788. option.axisPointer.link = [link];
  39789. }
  39790. }
  39791. });
  39792. // This process should proformed after coordinate systems created
  39793. // and series data processed. So put it on statistic processing stage.
  39794. registerProcessor(PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {
  39795. // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  39796. // allAxesInfo should be updated when setOption performed.
  39797. ecModel.getComponent('axisPointer').coordSysAxesInfo
  39798. = collect(ecModel, api);
  39799. });
  39800. // Broadcast to all views.
  39801. registerAction({
  39802. type: 'updateAxisPointer',
  39803. event: 'updateAxisPointer',
  39804. update: ':updateAxisPointer'
  39805. }, axisTrigger);
  39806. /*
  39807. * Licensed to the Apache Software Foundation (ASF) under one
  39808. * or more contributor license agreements. See the NOTICE file
  39809. * distributed with this work for additional information
  39810. * regarding copyright ownership. The ASF licenses this file
  39811. * to you under the Apache License, Version 2.0 (the
  39812. * "License"); you may not use this file except in compliance
  39813. * with the License. You may obtain a copy of the License at
  39814. *
  39815. * http://www.apache.org/licenses/LICENSE-2.0
  39816. *
  39817. * Unless required by applicable law or agreed to in writing,
  39818. * software distributed under the License is distributed on an
  39819. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39820. * KIND, either express or implied. See the License for the
  39821. * specific language governing permissions and limitations
  39822. * under the License.
  39823. */
  39824. extendComponentModel({
  39825. type: 'tooltip',
  39826. dependencies: ['axisPointer'],
  39827. defaultOption: {
  39828. zlevel: 0,
  39829. z: 60,
  39830. show: true,
  39831. // tooltip主体内容
  39832. showContent: true,
  39833. // 'trigger' only works on coordinate system.
  39834. // 'item' | 'axis' | 'none'
  39835. trigger: 'item',
  39836. // 'click' | 'mousemove' | 'none'
  39837. triggerOn: 'mousemove|click',
  39838. alwaysShowContent: false,
  39839. displayMode: 'single', // 'single' | 'multipleByCoordSys'
  39840. renderMode: 'auto', // 'auto' | 'html' | 'richText'
  39841. // 'auto': use html by default, and use non-html if `document` is not defined
  39842. // 'html': use html for tooltip
  39843. // 'richText': use canvas, svg, and etc. for tooltip
  39844. // 位置 {Array} | {Function}
  39845. // position: null
  39846. // Consider triggered from axisPointer handle, verticalAlign should be 'middle'
  39847. // align: null,
  39848. // verticalAlign: null,
  39849. // 是否约束 content 在 viewRect 中。默认 false 是为了兼容以前版本。
  39850. confine: false,
  39851. // 内容格式器:{string}(Template) ¦ {Function}
  39852. // formatter: null
  39853. showDelay: 0,
  39854. // 隐藏延迟,单位ms
  39855. hideDelay: 100,
  39856. // 动画变换时间,单位s
  39857. transitionDuration: 0.4,
  39858. enterable: false,
  39859. // 提示背景颜色,默认为透明度为0.7的黑色
  39860. backgroundColor: 'rgba(50,50,50,0.7)',
  39861. // 提示边框颜色
  39862. borderColor: '#333',
  39863. // 提示边框圆角,单位px,默认为4
  39864. borderRadius: 4,
  39865. // 提示边框线宽,单位px,默认为0(无边框)
  39866. borderWidth: 0,
  39867. // 提示内边距,单位px,默认各方向内边距为5,
  39868. // 接受数组分别设定上右下左边距,同css
  39869. padding: 5,
  39870. // Extra css text
  39871. extraCssText: '',
  39872. // 坐标轴指示器,坐标轴触发有效
  39873. axisPointer: {
  39874. // 默认为直线
  39875. // 可选为:'line' | 'shadow' | 'cross'
  39876. type: 'line',
  39877. // type 为 line 的时候有效,指定 tooltip line 所在的轴,可选
  39878. // 可选 'x' | 'y' | 'angle' | 'radius' | 'auto'
  39879. // 默认 'auto',会选择类型为 category 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴
  39880. // 极坐标系会默认选择 angle 轴
  39881. axis: 'auto',
  39882. animation: 'auto',
  39883. animationDurationUpdate: 200,
  39884. animationEasingUpdate: 'exponentialOut',
  39885. crossStyle: {
  39886. color: '#999',
  39887. width: 1,
  39888. type: 'dashed',
  39889. // TODO formatter
  39890. textStyle: {}
  39891. }
  39892. // lineStyle and shadowStyle should not be specified here,
  39893. // otherwise it will always override those styles on option.axisPointer.
  39894. },
  39895. textStyle: {
  39896. color: '#fff',
  39897. fontSize: 14
  39898. }
  39899. }
  39900. });
  39901. /*
  39902. * Licensed to the Apache Software Foundation (ASF) under one
  39903. * or more contributor license agreements. See the NOTICE file
  39904. * distributed with this work for additional information
  39905. * regarding copyright ownership. The ASF licenses this file
  39906. * to you under the Apache License, Version 2.0 (the
  39907. * "License"); you may not use this file except in compliance
  39908. * with the License. You may obtain a copy of the License at
  39909. *
  39910. * http://www.apache.org/licenses/LICENSE-2.0
  39911. *
  39912. * Unless required by applicable law or agreed to in writing,
  39913. * software distributed under the License is distributed on an
  39914. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39915. * KIND, either express or implied. See the License for the
  39916. * specific language governing permissions and limitations
  39917. * under the License.
  39918. */
  39919. var each$10 = each$1;
  39920. var toCamelCase$1 = toCamelCase;
  39921. var vendors = ['', '-webkit-', '-moz-', '-o-'];
  39922. var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
  39923. /**
  39924. * @param {number} duration
  39925. * @return {string}
  39926. * @inner
  39927. */
  39928. function assembleTransition(duration) {
  39929. var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
  39930. var transitionText = 'left ' + duration + 's ' + transitionCurve + ','
  39931. + 'top ' + duration + 's ' + transitionCurve;
  39932. return map(vendors, function (vendorPrefix) {
  39933. return vendorPrefix + 'transition:' + transitionText;
  39934. }).join(';');
  39935. }
  39936. /**
  39937. * @param {Object} textStyle
  39938. * @return {string}
  39939. * @inner
  39940. */
  39941. function assembleFont(textStyleModel) {
  39942. var cssText = [];
  39943. var fontSize = textStyleModel.get('fontSize');
  39944. var color = textStyleModel.getTextColor();
  39945. color && cssText.push('color:' + color);
  39946. cssText.push('font:' + textStyleModel.getFont());
  39947. fontSize
  39948. && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');
  39949. each$10(['decoration', 'align'], function (name) {
  39950. var val = textStyleModel.get(name);
  39951. val && cssText.push('text-' + name + ':' + val);
  39952. });
  39953. return cssText.join(';');
  39954. }
  39955. /**
  39956. * @param {Object} tooltipModel
  39957. * @return {string}
  39958. * @inner
  39959. */
  39960. function assembleCssText(tooltipModel) {
  39961. var cssText = [];
  39962. var transitionDuration = tooltipModel.get('transitionDuration');
  39963. var backgroundColor = tooltipModel.get('backgroundColor');
  39964. var textStyleModel = tooltipModel.getModel('textStyle');
  39965. var padding = tooltipModel.get('padding');
  39966. // Animation transition. Do not animate when transitionDuration is 0.
  39967. transitionDuration
  39968. && cssText.push(assembleTransition(transitionDuration));
  39969. if (backgroundColor) {
  39970. if (env$1.canvasSupported) {
  39971. cssText.push('background-Color:' + backgroundColor);
  39972. }
  39973. else {
  39974. // for ie
  39975. cssText.push(
  39976. 'background-Color:#' + toHex(backgroundColor)
  39977. );
  39978. cssText.push('filter:alpha(opacity=70)');
  39979. }
  39980. }
  39981. // Border style
  39982. each$10(['width', 'color', 'radius'], function (name) {
  39983. var borderName = 'border-' + name;
  39984. var camelCase = toCamelCase$1(borderName);
  39985. var val = tooltipModel.get(camelCase);
  39986. val != null
  39987. && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));
  39988. });
  39989. // Text style
  39990. cssText.push(assembleFont(textStyleModel));
  39991. // Padding
  39992. if (padding != null) {
  39993. cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');
  39994. }
  39995. return cssText.join(';') + ';';
  39996. }
  39997. /**
  39998. * @alias module:echarts/component/tooltip/TooltipContent
  39999. * @constructor
  40000. */
  40001. function TooltipContent(container, api) {
  40002. if (env$1.wxa) {
  40003. return null;
  40004. }
  40005. var el = document.createElement('div');
  40006. var zr = this._zr = api.getZr();
  40007. this.el = el;
  40008. this._x = api.getWidth() / 2;
  40009. this._y = api.getHeight() / 2;
  40010. container.appendChild(el);
  40011. this._container = container;
  40012. this._show = false;
  40013. /**
  40014. * @private
  40015. */
  40016. this._hideTimeout;
  40017. var self = this;
  40018. el.onmouseenter = function () {
  40019. // clear the timeout in hideLater and keep showing tooltip
  40020. if (self._enterable) {
  40021. clearTimeout(self._hideTimeout);
  40022. self._show = true;
  40023. }
  40024. self._inContent = true;
  40025. };
  40026. el.onmousemove = function (e) {
  40027. e = e || window.event;
  40028. if (!self._enterable) {
  40029. // Try trigger zrender event to avoid mouse
  40030. // in and out shape too frequently
  40031. var handler = zr.handler;
  40032. normalizeEvent(container, e, true);
  40033. handler.dispatch('mousemove', e);
  40034. }
  40035. };
  40036. el.onmouseleave = function () {
  40037. if (self._enterable) {
  40038. if (self._show) {
  40039. self.hideLater(self._hideDelay);
  40040. }
  40041. }
  40042. self._inContent = false;
  40043. };
  40044. }
  40045. TooltipContent.prototype = {
  40046. constructor: TooltipContent,
  40047. /**
  40048. * @private
  40049. * @type {boolean}
  40050. */
  40051. _enterable: true,
  40052. /**
  40053. * Update when tooltip is rendered
  40054. */
  40055. update: function () {
  40056. // FIXME
  40057. // Move this logic to ec main?
  40058. var container = this._container;
  40059. var stl = container.currentStyle
  40060. || document.defaultView.getComputedStyle(container);
  40061. var domStyle = container.style;
  40062. if (domStyle.position !== 'absolute' && stl.position !== 'absolute') {
  40063. domStyle.position = 'relative';
  40064. }
  40065. // Hide the tooltip
  40066. // PENDING
  40067. // this.hide();
  40068. },
  40069. show: function (tooltipModel) {
  40070. clearTimeout(this._hideTimeout);
  40071. var el = this.el;
  40072. el.style.cssText = gCssText + assembleCssText(tooltipModel)
  40073. // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore
  40074. + ';left:' + this._x + 'px;top:' + this._y + 'px;'
  40075. + (tooltipModel.get('extraCssText') || '');
  40076. el.style.display = el.innerHTML ? 'block' : 'none';
  40077. // If mouse occsionally move over the tooltip, a mouseout event will be
  40078. // triggered by canvas, and cuase some unexpectable result like dragging
  40079. // stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve
  40080. // it. Although it is not suppored by IE8~IE10, fortunately it is a rare
  40081. // scenario.
  40082. el.style.pointerEvents = this._enterable ? 'auto' : 'none';
  40083. this._show = true;
  40084. },
  40085. setContent: function (content) {
  40086. this.el.innerHTML = content == null ? '' : content;
  40087. },
  40088. setEnterable: function (enterable) {
  40089. this._enterable = enterable;
  40090. },
  40091. getSize: function () {
  40092. var el = this.el;
  40093. return [el.clientWidth, el.clientHeight];
  40094. },
  40095. moveTo: function (x, y) {
  40096. // xy should be based on canvas root. But tooltipContent is
  40097. // the sibling of canvas root. So padding of ec container
  40098. // should be considered here.
  40099. var zr = this._zr;
  40100. var viewportRootOffset;
  40101. if (zr && zr.painter && (viewportRootOffset = zr.painter.getViewportRootOffset())) {
  40102. x += viewportRootOffset.offsetLeft;
  40103. y += viewportRootOffset.offsetTop;
  40104. }
  40105. var style = this.el.style;
  40106. style.left = x + 'px';
  40107. style.top = y + 'px';
  40108. this._x = x;
  40109. this._y = y;
  40110. },
  40111. hide: function () {
  40112. this.el.style.display = 'none';
  40113. this._show = false;
  40114. },
  40115. hideLater: function (time) {
  40116. if (this._show && !(this._inContent && this._enterable)) {
  40117. if (time) {
  40118. this._hideDelay = time;
  40119. // Set show false to avoid invoke hideLater mutiple times
  40120. this._show = false;
  40121. this._hideTimeout = setTimeout(bind(this.hide, this), time);
  40122. }
  40123. else {
  40124. this.hide();
  40125. }
  40126. }
  40127. },
  40128. isShow: function () {
  40129. return this._show;
  40130. },
  40131. getOuterSize: function () {
  40132. var width = this.el.clientWidth;
  40133. var height = this.el.clientHeight;
  40134. // Consider browser compatibility.
  40135. // IE8 does not support getComputedStyle.
  40136. if (document.defaultView && document.defaultView.getComputedStyle) {
  40137. var stl = document.defaultView.getComputedStyle(this.el);
  40138. if (stl) {
  40139. width += parseInt(stl.paddingLeft, 10) + parseInt(stl.paddingRight, 10)
  40140. + parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10);
  40141. height += parseInt(stl.paddingTop, 10) + parseInt(stl.paddingBottom, 10)
  40142. + parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10);
  40143. }
  40144. }
  40145. return {width: width, height: height};
  40146. }
  40147. };
  40148. /*
  40149. * Licensed to the Apache Software Foundation (ASF) under one
  40150. * or more contributor license agreements. See the NOTICE file
  40151. * distributed with this work for additional information
  40152. * regarding copyright ownership. The ASF licenses this file
  40153. * to you under the Apache License, Version 2.0 (the
  40154. * "License"); you may not use this file except in compliance
  40155. * with the License. You may obtain a copy of the License at
  40156. *
  40157. * http://www.apache.org/licenses/LICENSE-2.0
  40158. *
  40159. * Unless required by applicable law or agreed to in writing,
  40160. * software distributed under the License is distributed on an
  40161. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  40162. * KIND, either express or implied. See the License for the
  40163. * specific language governing permissions and limitations
  40164. * under the License.
  40165. */
  40166. // import Group from 'zrender/src/container/Group';
  40167. /**
  40168. * @alias module:echarts/component/tooltip/TooltipRichContent
  40169. * @constructor
  40170. */
  40171. function TooltipRichContent(api) {
  40172. this._zr = api.getZr();
  40173. this._show = false;
  40174. /**
  40175. * @private
  40176. */
  40177. this._hideTimeout;
  40178. }
  40179. TooltipRichContent.prototype = {
  40180. constructor: TooltipRichContent,
  40181. /**
  40182. * @private
  40183. * @type {boolean}
  40184. */
  40185. _enterable: true,
  40186. /**
  40187. * Update when tooltip is rendered
  40188. */
  40189. update: function () {
  40190. // noop
  40191. },
  40192. show: function (tooltipModel) {
  40193. if (this._hideTimeout) {
  40194. clearTimeout(this._hideTimeout);
  40195. }
  40196. this.el.attr('show', true);
  40197. this._show = true;
  40198. },
  40199. /**
  40200. * Set tooltip content
  40201. *
  40202. * @param {string} content rich text string of content
  40203. * @param {Object} markerRich rich text style
  40204. * @param {Object} tooltipModel tooltip model
  40205. */
  40206. setContent: function (content, markerRich, tooltipModel) {
  40207. if (this.el) {
  40208. this._zr.remove(this.el);
  40209. }
  40210. var markers = {};
  40211. var text = content;
  40212. var prefix = '{marker';
  40213. var suffix = '|}';
  40214. var startId = text.indexOf(prefix);
  40215. while (startId >= 0) {
  40216. var endId = text.indexOf(suffix);
  40217. var name = text.substr(startId + prefix.length, endId - startId - prefix.length);
  40218. if (name.indexOf('sub') > -1) {
  40219. markers['marker' + name] = {
  40220. textWidth: 4,
  40221. textHeight: 4,
  40222. textBorderRadius: 2,
  40223. textBackgroundColor: markerRich[name],
  40224. // TODO: textOffset is not implemented for rich text
  40225. textOffset: [3, 0]
  40226. };
  40227. }
  40228. else {
  40229. markers['marker' + name] = {
  40230. textWidth: 10,
  40231. textHeight: 10,
  40232. textBorderRadius: 5,
  40233. textBackgroundColor: markerRich[name]
  40234. };
  40235. }
  40236. text = text.substr(endId + 1);
  40237. startId = text.indexOf('{marker');
  40238. }
  40239. this.el = new Text({
  40240. style: {
  40241. rich: markers,
  40242. text: content,
  40243. textLineHeight: 20,
  40244. textBackgroundColor: tooltipModel.get('backgroundColor'),
  40245. textBorderRadius: tooltipModel.get('borderRadius'),
  40246. textFill: tooltipModel.get('textStyle.color'),
  40247. textPadding: tooltipModel.get('padding')
  40248. },
  40249. z: tooltipModel.get('z')
  40250. });
  40251. this._zr.add(this.el);
  40252. var self = this;
  40253. this.el.on('mouseover', function () {
  40254. // clear the timeout in hideLater and keep showing tooltip
  40255. if (self._enterable) {
  40256. clearTimeout(self._hideTimeout);
  40257. self._show = true;
  40258. }
  40259. self._inContent = true;
  40260. });
  40261. this.el.on('mouseout', function () {
  40262. if (self._enterable) {
  40263. if (self._show) {
  40264. self.hideLater(self._hideDelay);
  40265. }
  40266. }
  40267. self._inContent = false;
  40268. });
  40269. },
  40270. setEnterable: function (enterable) {
  40271. this._enterable = enterable;
  40272. },
  40273. getSize: function () {
  40274. var bounding = this.el.getBoundingRect();
  40275. return [bounding.width, bounding.height];
  40276. },
  40277. moveTo: function (x, y) {
  40278. if (this.el) {
  40279. this.el.attr('position', [x, y]);
  40280. }
  40281. },
  40282. hide: function () {
  40283. this.el.hide();
  40284. this._show = false;
  40285. },
  40286. hideLater: function (time) {
  40287. if (this._show && !(this._inContent && this._enterable)) {
  40288. if (time) {
  40289. this._hideDelay = time;
  40290. // Set show false to avoid invoke hideLater mutiple times
  40291. this._show = false;
  40292. this._hideTimeout = setTimeout(bind(this.hide, this), time);
  40293. }
  40294. else {
  40295. this.hide();
  40296. }
  40297. }
  40298. },
  40299. isShow: function () {
  40300. return this._show;
  40301. },
  40302. getOuterSize: function () {
  40303. return this.getSize();
  40304. }
  40305. };
  40306. /*
  40307. * Licensed to the Apache Software Foundation (ASF) under one
  40308. * or more contributor license agreements. See the NOTICE file
  40309. * distributed with this work for additional information
  40310. * regarding copyright ownership. The ASF licenses this file
  40311. * to you under the Apache License, Version 2.0 (the
  40312. * "License"); you may not use this file except in compliance
  40313. * with the License. You may obtain a copy of the License at
  40314. *
  40315. * http://www.apache.org/licenses/LICENSE-2.0
  40316. *
  40317. * Unless required by applicable law or agreed to in writing,
  40318. * software distributed under the License is distributed on an
  40319. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  40320. * KIND, either express or implied. See the License for the
  40321. * specific language governing permissions and limitations
  40322. * under the License.
  40323. */
  40324. var bind$2 = bind;
  40325. var each$9 = each$1;
  40326. var parsePercent$2 = parsePercent$1;
  40327. var proxyRect = new Rect({
  40328. shape: {x: -1, y: -1, width: 2, height: 2}
  40329. });
  40330. extendComponentView({
  40331. type: 'tooltip',
  40332. init: function (ecModel, api) {
  40333. if (env$1.node) {
  40334. return;
  40335. }
  40336. var tooltipModel = ecModel.getComponent('tooltip');
  40337. var renderMode = tooltipModel.get('renderMode');
  40338. this._renderMode = getTooltipRenderMode(renderMode);
  40339. var tooltipContent;
  40340. if (this._renderMode === 'html') {
  40341. tooltipContent = new TooltipContent(api.getDom(), api);
  40342. this._newLine = '<br/>';
  40343. }
  40344. else {
  40345. tooltipContent = new TooltipRichContent(api);
  40346. this._newLine = '\n';
  40347. }
  40348. this._tooltipContent = tooltipContent;
  40349. },
  40350. render: function (tooltipModel, ecModel, api) {
  40351. if (env$1.node) {
  40352. return;
  40353. }
  40354. // Reset
  40355. this.group.removeAll();
  40356. /**
  40357. * @private
  40358. * @type {module:echarts/component/tooltip/TooltipModel}
  40359. */
  40360. this._tooltipModel = tooltipModel;
  40361. /**
  40362. * @private
  40363. * @type {module:echarts/model/Global}
  40364. */
  40365. this._ecModel = ecModel;
  40366. /**
  40367. * @private
  40368. * @type {module:echarts/ExtensionAPI}
  40369. */
  40370. this._api = api;
  40371. /**
  40372. * Should be cleaned when render.
  40373. * @private
  40374. * @type {Array.<Array.<Object>>}
  40375. */
  40376. this._lastDataByCoordSys = null;
  40377. /**
  40378. * @private
  40379. * @type {boolean}
  40380. */
  40381. this._alwaysShowContent = tooltipModel.get('alwaysShowContent');
  40382. var tooltipContent = this._tooltipContent;
  40383. tooltipContent.update();
  40384. tooltipContent.setEnterable(tooltipModel.get('enterable'));
  40385. this._initGlobalListener();
  40386. this._keepShow();
  40387. },
  40388. _initGlobalListener: function () {
  40389. var tooltipModel = this._tooltipModel;
  40390. var triggerOn = tooltipModel.get('triggerOn');
  40391. register(
  40392. 'itemTooltip',
  40393. this._api,
  40394. bind$2(function (currTrigger, e, dispatchAction) {
  40395. // If 'none', it is not controlled by mouse totally.
  40396. if (triggerOn !== 'none') {
  40397. if (triggerOn.indexOf(currTrigger) >= 0) {
  40398. this._tryShow(e, dispatchAction);
  40399. }
  40400. else if (currTrigger === 'leave') {
  40401. this._hide(dispatchAction);
  40402. }
  40403. }
  40404. }, this)
  40405. );
  40406. },
  40407. _keepShow: function () {
  40408. var tooltipModel = this._tooltipModel;
  40409. var ecModel = this._ecModel;
  40410. var api = this._api;
  40411. // Try to keep the tooltip show when refreshing
  40412. if (this._lastX != null
  40413. && this._lastY != null
  40414. // When user is willing to control tooltip totally using API,
  40415. // self.manuallyShowTip({x, y}) might cause tooltip hide,
  40416. // which is not expected.
  40417. && tooltipModel.get('triggerOn') !== 'none'
  40418. ) {
  40419. var self = this;
  40420. clearTimeout(this._refreshUpdateTimeout);
  40421. this._refreshUpdateTimeout = setTimeout(function () {
  40422. // Show tip next tick after other charts are rendered
  40423. // In case highlight action has wrong result
  40424. // FIXME
  40425. self.manuallyShowTip(tooltipModel, ecModel, api, {
  40426. x: self._lastX,
  40427. y: self._lastY
  40428. });
  40429. });
  40430. }
  40431. },
  40432. /**
  40433. * Show tip manually by
  40434. * dispatchAction({
  40435. * type: 'showTip',
  40436. * x: 10,
  40437. * y: 10
  40438. * });
  40439. * Or
  40440. * dispatchAction({
  40441. * type: 'showTip',
  40442. * seriesIndex: 0,
  40443. * dataIndex or dataIndexInside or name
  40444. * });
  40445. *
  40446. * TODO Batch
  40447. */
  40448. manuallyShowTip: function (tooltipModel, ecModel, api, payload) {
  40449. if (payload.from === this.uid || env$1.node) {
  40450. return;
  40451. }
  40452. var dispatchAction = makeDispatchAction$1(payload, api);
  40453. // Reset ticket
  40454. this._ticket = '';
  40455. // When triggered from axisPointer.
  40456. var dataByCoordSys = payload.dataByCoordSys;
  40457. if (payload.tooltip && payload.x != null && payload.y != null) {
  40458. var el = proxyRect;
  40459. el.position = [payload.x, payload.y];
  40460. el.update();
  40461. el.tooltip = payload.tooltip;
  40462. // Manually show tooltip while view is not using zrender elements.
  40463. this._tryShow({
  40464. offsetX: payload.x,
  40465. offsetY: payload.y,
  40466. target: el
  40467. }, dispatchAction);
  40468. }
  40469. else if (dataByCoordSys) {
  40470. this._tryShow({
  40471. offsetX: payload.x,
  40472. offsetY: payload.y,
  40473. position: payload.position,
  40474. event: {},
  40475. dataByCoordSys: payload.dataByCoordSys,
  40476. tooltipOption: payload.tooltipOption
  40477. }, dispatchAction);
  40478. }
  40479. else if (payload.seriesIndex != null) {
  40480. if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {
  40481. return;
  40482. }
  40483. var pointInfo = findPointFromSeries(payload, ecModel);
  40484. var cx = pointInfo.point[0];
  40485. var cy = pointInfo.point[1];
  40486. if (cx != null && cy != null) {
  40487. this._tryShow({
  40488. offsetX: cx,
  40489. offsetY: cy,
  40490. position: payload.position,
  40491. target: pointInfo.el,
  40492. event: {}
  40493. }, dispatchAction);
  40494. }
  40495. }
  40496. else if (payload.x != null && payload.y != null) {
  40497. // FIXME
  40498. // should wrap dispatchAction like `axisPointer/globalListener` ?
  40499. api.dispatchAction({
  40500. type: 'updateAxisPointer',
  40501. x: payload.x,
  40502. y: payload.y
  40503. });
  40504. this._tryShow({
  40505. offsetX: payload.x,
  40506. offsetY: payload.y,
  40507. position: payload.position,
  40508. target: api.getZr().findHover(payload.x, payload.y).target,
  40509. event: {}
  40510. }, dispatchAction);
  40511. }
  40512. },
  40513. manuallyHideTip: function (tooltipModel, ecModel, api, payload) {
  40514. var tooltipContent = this._tooltipContent;
  40515. if (!this._alwaysShowContent && this._tooltipModel) {
  40516. tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));
  40517. }
  40518. this._lastX = this._lastY = null;
  40519. if (payload.from !== this.uid) {
  40520. this._hide(makeDispatchAction$1(payload, api));
  40521. }
  40522. },
  40523. // Be compatible with previous design, that is, when tooltip.type is 'axis' and
  40524. // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
  40525. // and tooltip.
  40526. _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) {
  40527. var seriesIndex = payload.seriesIndex;
  40528. var dataIndex = payload.dataIndex;
  40529. var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
  40530. if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {
  40531. return;
  40532. }
  40533. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  40534. if (!seriesModel) {
  40535. return;
  40536. }
  40537. var data = seriesModel.getData();
  40538. var tooltipModel = buildTooltipModel([
  40539. data.getItemModel(dataIndex),
  40540. seriesModel,
  40541. (seriesModel.coordinateSystem || {}).model,
  40542. tooltipModel
  40543. ]);
  40544. if (tooltipModel.get('trigger') !== 'axis') {
  40545. return;
  40546. }
  40547. api.dispatchAction({
  40548. type: 'updateAxisPointer',
  40549. seriesIndex: seriesIndex,
  40550. dataIndex: dataIndex,
  40551. position: payload.position
  40552. });
  40553. return true;
  40554. },
  40555. _tryShow: function (e, dispatchAction) {
  40556. var el = e.target;
  40557. var tooltipModel = this._tooltipModel;
  40558. if (!tooltipModel) {
  40559. return;
  40560. }
  40561. // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed
  40562. this._lastX = e.offsetX;
  40563. this._lastY = e.offsetY;
  40564. var dataByCoordSys = e.dataByCoordSys;
  40565. if (dataByCoordSys && dataByCoordSys.length) {
  40566. this._showAxisTooltip(dataByCoordSys, e);
  40567. }
  40568. // Always show item tooltip if mouse is on the element with dataIndex
  40569. else if (el && el.dataIndex != null) {
  40570. this._lastDataByCoordSys = null;
  40571. this._showSeriesItemTooltip(e, el, dispatchAction);
  40572. }
  40573. // Tooltip provided directly. Like legend.
  40574. else if (el && el.tooltip) {
  40575. this._lastDataByCoordSys = null;
  40576. this._showComponentItemTooltip(e, el, dispatchAction);
  40577. }
  40578. else {
  40579. this._lastDataByCoordSys = null;
  40580. this._hide(dispatchAction);
  40581. }
  40582. },
  40583. _showOrMove: function (tooltipModel, cb) {
  40584. // showDelay is used in this case: tooltip.enterable is set
  40585. // as true. User intent to move mouse into tooltip and click
  40586. // something. `showDelay` makes it easyer to enter the content
  40587. // but tooltip do not move immediately.
  40588. var delay = tooltipModel.get('showDelay');
  40589. cb = bind(cb, this);
  40590. clearTimeout(this._showTimout);
  40591. delay > 0
  40592. ? (this._showTimout = setTimeout(cb, delay))
  40593. : cb();
  40594. },
  40595. _showAxisTooltip: function (dataByCoordSys, e) {
  40596. var ecModel = this._ecModel;
  40597. var globalTooltipModel = this._tooltipModel;
  40598. var point = [e.offsetX, e.offsetY];
  40599. var singleDefaultHTML = [];
  40600. var singleParamsList = [];
  40601. var singleTooltipModel = buildTooltipModel([
  40602. e.tooltipOption,
  40603. globalTooltipModel
  40604. ]);
  40605. var renderMode = this._renderMode;
  40606. var newLine = this._newLine;
  40607. var markers = {};
  40608. each$9(dataByCoordSys, function (itemCoordSys) {
  40609. // var coordParamList = [];
  40610. // var coordDefaultHTML = [];
  40611. // var coordTooltipModel = buildTooltipModel([
  40612. // e.tooltipOption,
  40613. // itemCoordSys.tooltipOption,
  40614. // ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex),
  40615. // globalTooltipModel
  40616. // ]);
  40617. // var displayMode = coordTooltipModel.get('displayMode');
  40618. // var paramsList = displayMode === 'single' ? singleParamsList : [];
  40619. each$9(itemCoordSys.dataByAxis, function (item) {
  40620. var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex);
  40621. var axisValue = item.value;
  40622. var seriesDefaultHTML = [];
  40623. if (!axisModel || axisValue == null) {
  40624. return;
  40625. }
  40626. var valueLabel = getValueLabel(
  40627. axisValue, axisModel.axis, ecModel,
  40628. item.seriesDataIndices,
  40629. item.valueLabelOpt
  40630. );
  40631. each$1(item.seriesDataIndices, function (idxItem) {
  40632. var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
  40633. var dataIndex = idxItem.dataIndexInside;
  40634. var dataParams = series && series.getDataParams(dataIndex);
  40635. dataParams.axisDim = item.axisDim;
  40636. dataParams.axisIndex = item.axisIndex;
  40637. dataParams.axisType = item.axisType;
  40638. dataParams.axisId = item.axisId;
  40639. dataParams.axisValue = getAxisRawValue(axisModel.axis, axisValue);
  40640. dataParams.axisValueLabel = valueLabel;
  40641. if (dataParams) {
  40642. singleParamsList.push(dataParams);
  40643. var seriesTooltip = series.formatTooltip(dataIndex, true, null, renderMode);
  40644. var html;
  40645. if (isObject$1(seriesTooltip)) {
  40646. html = seriesTooltip.html;
  40647. var newMarkers = seriesTooltip.markers;
  40648. merge(markers, newMarkers);
  40649. }
  40650. else {
  40651. html = seriesTooltip;
  40652. }
  40653. seriesDefaultHTML.push(html);
  40654. }
  40655. });
  40656. // Default tooltip content
  40657. // FIXME
  40658. // (1) shold be the first data which has name?
  40659. // (2) themeRiver, firstDataIndex is array, and first line is unnecessary.
  40660. var firstLine = valueLabel;
  40661. if (renderMode !== 'html') {
  40662. singleDefaultHTML.push(seriesDefaultHTML.join(newLine));
  40663. }
  40664. else {
  40665. singleDefaultHTML.push(
  40666. (firstLine ? encodeHTML(firstLine) + newLine : '')
  40667. + seriesDefaultHTML.join(newLine)
  40668. );
  40669. }
  40670. });
  40671. }, this);
  40672. // In most case, the second axis is shown upper than the first one.
  40673. singleDefaultHTML.reverse();
  40674. singleDefaultHTML = singleDefaultHTML.join(this._newLine + this._newLine);
  40675. var positionExpr = e.position;
  40676. this._showOrMove(singleTooltipModel, function () {
  40677. if (this._updateContentNotChangedOnAxis(dataByCoordSys)) {
  40678. this._updatePosition(
  40679. singleTooltipModel,
  40680. positionExpr,
  40681. point[0], point[1],
  40682. this._tooltipContent,
  40683. singleParamsList
  40684. );
  40685. }
  40686. else {
  40687. this._showTooltipContent(
  40688. singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(),
  40689. point[0], point[1], positionExpr, undefined, markers
  40690. );
  40691. }
  40692. });
  40693. // Do not trigger events here, because this branch only be entered
  40694. // from dispatchAction.
  40695. },
  40696. _showSeriesItemTooltip: function (e, el, dispatchAction) {
  40697. var ecModel = this._ecModel;
  40698. // Use dataModel in element if possible
  40699. // Used when mouseover on a element like markPoint or edge
  40700. // In which case, the data is not main data in series.
  40701. var seriesIndex = el.seriesIndex;
  40702. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  40703. // For example, graph link.
  40704. var dataModel = el.dataModel || seriesModel;
  40705. var dataIndex = el.dataIndex;
  40706. var dataType = el.dataType;
  40707. var data = dataModel.getData();
  40708. var tooltipModel = buildTooltipModel([
  40709. data.getItemModel(dataIndex),
  40710. dataModel,
  40711. seriesModel && (seriesModel.coordinateSystem || {}).model,
  40712. this._tooltipModel
  40713. ]);
  40714. var tooltipTrigger = tooltipModel.get('trigger');
  40715. if (tooltipTrigger != null && tooltipTrigger !== 'item') {
  40716. return;
  40717. }
  40718. var params = dataModel.getDataParams(dataIndex, dataType);
  40719. var seriesTooltip = dataModel.formatTooltip(dataIndex, false, dataType, this._renderMode);
  40720. var defaultHtml;
  40721. var markers;
  40722. if (isObject$1(seriesTooltip)) {
  40723. defaultHtml = seriesTooltip.html;
  40724. markers = seriesTooltip.markers;
  40725. }
  40726. else {
  40727. defaultHtml = seriesTooltip;
  40728. markers = null;
  40729. }
  40730. var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;
  40731. this._showOrMove(tooltipModel, function () {
  40732. this._showTooltipContent(
  40733. tooltipModel, defaultHtml, params, asyncTicket,
  40734. e.offsetX, e.offsetY, e.position, e.target, markers
  40735. );
  40736. });
  40737. // FIXME
  40738. // duplicated showtip if manuallyShowTip is called from dispatchAction.
  40739. dispatchAction({
  40740. type: 'showTip',
  40741. dataIndexInside: dataIndex,
  40742. dataIndex: data.getRawIndex(dataIndex),
  40743. seriesIndex: seriesIndex,
  40744. from: this.uid
  40745. });
  40746. },
  40747. _showComponentItemTooltip: function (e, el, dispatchAction) {
  40748. var tooltipOpt = el.tooltip;
  40749. if (typeof tooltipOpt === 'string') {
  40750. var content = tooltipOpt;
  40751. tooltipOpt = {
  40752. content: content,
  40753. // Fixed formatter
  40754. formatter: content
  40755. };
  40756. }
  40757. var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
  40758. var defaultHtml = subTooltipModel.get('content');
  40759. var asyncTicket = Math.random();
  40760. // Do not check whether `trigger` is 'none' here, because `trigger`
  40761. // only works on cooridinate system. In fact, we have not found case
  40762. // that requires setting `trigger` nothing on component yet.
  40763. this._showOrMove(subTooltipModel, function () {
  40764. this._showTooltipContent(
  40765. subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {},
  40766. asyncTicket, e.offsetX, e.offsetY, e.position, el
  40767. );
  40768. });
  40769. // If not dispatch showTip, tip may be hide triggered by axis.
  40770. dispatchAction({
  40771. type: 'showTip',
  40772. from: this.uid
  40773. });
  40774. },
  40775. _showTooltipContent: function (
  40776. tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markers
  40777. ) {
  40778. // Reset ticket
  40779. this._ticket = '';
  40780. if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {
  40781. return;
  40782. }
  40783. var tooltipContent = this._tooltipContent;
  40784. var formatter = tooltipModel.get('formatter');
  40785. positionExpr = positionExpr || tooltipModel.get('position');
  40786. var html = defaultHtml;
  40787. if (formatter && typeof formatter === 'string') {
  40788. html = formatTpl(formatter, params, true);
  40789. }
  40790. else if (typeof formatter === 'function') {
  40791. var callback = bind$2(function (cbTicket, html) {
  40792. if (cbTicket === this._ticket) {
  40793. tooltipContent.setContent(html, markers, tooltipModel);
  40794. this._updatePosition(
  40795. tooltipModel, positionExpr, x, y, tooltipContent, params, el
  40796. );
  40797. }
  40798. }, this);
  40799. this._ticket = asyncTicket;
  40800. html = formatter(params, asyncTicket, callback);
  40801. }
  40802. tooltipContent.setContent(html, markers, tooltipModel);
  40803. tooltipContent.show(tooltipModel);
  40804. this._updatePosition(
  40805. tooltipModel, positionExpr, x, y, tooltipContent, params, el
  40806. );
  40807. },
  40808. /**
  40809. * @param {string|Function|Array.<number>|Object} positionExpr
  40810. * @param {number} x Mouse x
  40811. * @param {number} y Mouse y
  40812. * @param {boolean} confine Whether confine tooltip content in view rect.
  40813. * @param {Object|<Array.<Object>} params
  40814. * @param {module:zrender/Element} el target element
  40815. * @param {module:echarts/ExtensionAPI} api
  40816. * @return {Array.<number>}
  40817. */
  40818. _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) {
  40819. var viewWidth = this._api.getWidth();
  40820. var viewHeight = this._api.getHeight();
  40821. positionExpr = positionExpr || tooltipModel.get('position');
  40822. var contentSize = content.getSize();
  40823. var align = tooltipModel.get('align');
  40824. var vAlign = tooltipModel.get('verticalAlign');
  40825. var rect = el && el.getBoundingRect().clone();
  40826. el && rect.applyTransform(el.transform);
  40827. if (typeof positionExpr === 'function') {
  40828. // Callback of position can be an array or a string specify the position
  40829. positionExpr = positionExpr([x, y], params, content.el, rect, {
  40830. viewSize: [viewWidth, viewHeight],
  40831. contentSize: contentSize.slice()
  40832. });
  40833. }
  40834. if (isArray(positionExpr)) {
  40835. x = parsePercent$2(positionExpr[0], viewWidth);
  40836. y = parsePercent$2(positionExpr[1], viewHeight);
  40837. }
  40838. else if (isObject$1(positionExpr)) {
  40839. positionExpr.width = contentSize[0];
  40840. positionExpr.height = contentSize[1];
  40841. var layoutRect = getLayoutRect(
  40842. positionExpr, {width: viewWidth, height: viewHeight}
  40843. );
  40844. x = layoutRect.x;
  40845. y = layoutRect.y;
  40846. align = null;
  40847. // When positionExpr is left/top/right/bottom,
  40848. // align and verticalAlign will not work.
  40849. vAlign = null;
  40850. }
  40851. // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
  40852. else if (typeof positionExpr === 'string' && el) {
  40853. var pos = calcTooltipPosition(
  40854. positionExpr, rect, contentSize
  40855. );
  40856. x = pos[0];
  40857. y = pos[1];
  40858. }
  40859. else {
  40860. var pos = refixTooltipPosition(
  40861. x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20
  40862. );
  40863. x = pos[0];
  40864. y = pos[1];
  40865. }
  40866. align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);
  40867. vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);
  40868. if (tooltipModel.get('confine')) {
  40869. var pos = confineTooltipPosition(
  40870. x, y, content, viewWidth, viewHeight
  40871. );
  40872. x = pos[0];
  40873. y = pos[1];
  40874. }
  40875. content.moveTo(x, y);
  40876. },
  40877. // FIXME
  40878. // Should we remove this but leave this to user?
  40879. _updateContentNotChangedOnAxis: function (dataByCoordSys) {
  40880. var lastCoordSys = this._lastDataByCoordSys;
  40881. var contentNotChanged = !!lastCoordSys
  40882. && lastCoordSys.length === dataByCoordSys.length;
  40883. contentNotChanged && each$9(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {
  40884. var lastDataByAxis = lastItemCoordSys.dataByAxis || {};
  40885. var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};
  40886. var thisDataByAxis = thisItemCoordSys.dataByAxis || [];
  40887. contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length;
  40888. contentNotChanged && each$9(lastDataByAxis, function (lastItem, indexAxis) {
  40889. var thisItem = thisDataByAxis[indexAxis] || {};
  40890. var lastIndices = lastItem.seriesDataIndices || [];
  40891. var newIndices = thisItem.seriesDataIndices || [];
  40892. contentNotChanged
  40893. &= lastItem.value === thisItem.value
  40894. && lastItem.axisType === thisItem.axisType
  40895. && lastItem.axisId === thisItem.axisId
  40896. && lastIndices.length === newIndices.length;
  40897. contentNotChanged && each$9(lastIndices, function (lastIdxItem, j) {
  40898. var newIdxItem = newIndices[j];
  40899. contentNotChanged
  40900. &= lastIdxItem.seriesIndex === newIdxItem.seriesIndex
  40901. && lastIdxItem.dataIndex === newIdxItem.dataIndex;
  40902. });
  40903. });
  40904. });
  40905. this._lastDataByCoordSys = dataByCoordSys;
  40906. return !!contentNotChanged;
  40907. },
  40908. _hide: function (dispatchAction) {
  40909. // Do not directly hideLater here, because this behavior may be prevented
  40910. // in dispatchAction when showTip is dispatched.
  40911. // FIXME
  40912. // duplicated hideTip if manuallyHideTip is called from dispatchAction.
  40913. this._lastDataByCoordSys = null;
  40914. dispatchAction({
  40915. type: 'hideTip',
  40916. from: this.uid
  40917. });
  40918. },
  40919. dispose: function (ecModel, api) {
  40920. if (env$1.node) {
  40921. return;
  40922. }
  40923. this._tooltipContent.hide();
  40924. unregister('itemTooltip', api);
  40925. }
  40926. });
  40927. /**
  40928. * @param {Array.<Object|module:echarts/model/Model>} modelCascade
  40929. * From top to bottom. (the last one should be globalTooltipModel);
  40930. */
  40931. function buildTooltipModel(modelCascade) {
  40932. var resultModel = modelCascade.pop();
  40933. while (modelCascade.length) {
  40934. var tooltipOpt = modelCascade.pop();
  40935. if (tooltipOpt) {
  40936. if (Model.isInstance(tooltipOpt)) {
  40937. tooltipOpt = tooltipOpt.get('tooltip', true);
  40938. }
  40939. // In each data item tooltip can be simply write:
  40940. // {
  40941. // value: 10,
  40942. // tooltip: 'Something you need to know'
  40943. // }
  40944. if (typeof tooltipOpt === 'string') {
  40945. tooltipOpt = {formatter: tooltipOpt};
  40946. }
  40947. resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel);
  40948. }
  40949. }
  40950. return resultModel;
  40951. }
  40952. function makeDispatchAction$1(payload, api) {
  40953. return payload.dispatchAction || bind(api.dispatchAction, api);
  40954. }
  40955. function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) {
  40956. var size = content.getOuterSize();
  40957. var width = size.width;
  40958. var height = size.height;
  40959. if (gapH != null) {
  40960. if (x + width + gapH > viewWidth) {
  40961. x -= width + gapH;
  40962. }
  40963. else {
  40964. x += gapH;
  40965. }
  40966. }
  40967. if (gapV != null) {
  40968. if (y + height + gapV > viewHeight) {
  40969. y -= height + gapV;
  40970. }
  40971. else {
  40972. y += gapV;
  40973. }
  40974. }
  40975. return [x, y];
  40976. }
  40977. function confineTooltipPosition(x, y, content, viewWidth, viewHeight) {
  40978. var size = content.getOuterSize();
  40979. var width = size.width;
  40980. var height = size.height;
  40981. x = Math.min(x + width, viewWidth) - width;
  40982. y = Math.min(y + height, viewHeight) - height;
  40983. x = Math.max(x, 0);
  40984. y = Math.max(y, 0);
  40985. return [x, y];
  40986. }
  40987. function calcTooltipPosition(position, rect, contentSize) {
  40988. var domWidth = contentSize[0];
  40989. var domHeight = contentSize[1];
  40990. var gap = 5;
  40991. var x = 0;
  40992. var y = 0;
  40993. var rectWidth = rect.width;
  40994. var rectHeight = rect.height;
  40995. switch (position) {
  40996. case 'inside':
  40997. x = rect.x + rectWidth / 2 - domWidth / 2;
  40998. y = rect.y + rectHeight / 2 - domHeight / 2;
  40999. break;
  41000. case 'top':
  41001. x = rect.x + rectWidth / 2 - domWidth / 2;
  41002. y = rect.y - domHeight - gap;
  41003. break;
  41004. case 'bottom':
  41005. x = rect.x + rectWidth / 2 - domWidth / 2;
  41006. y = rect.y + rectHeight + gap;
  41007. break;
  41008. case 'left':
  41009. x = rect.x - domWidth - gap;
  41010. y = rect.y + rectHeight / 2 - domHeight / 2;
  41011. break;
  41012. case 'right':
  41013. x = rect.x + rectWidth + gap;
  41014. y = rect.y + rectHeight / 2 - domHeight / 2;
  41015. }
  41016. return [x, y];
  41017. }
  41018. function isCenterAlign(align) {
  41019. return align === 'center' || align === 'middle';
  41020. }
  41021. /*
  41022. * Licensed to the Apache Software Foundation (ASF) under one
  41023. * or more contributor license agreements. See the NOTICE file
  41024. * distributed with this work for additional information
  41025. * regarding copyright ownership. The ASF licenses this file
  41026. * to you under the Apache License, Version 2.0 (the
  41027. * "License"); you may not use this file except in compliance
  41028. * with the License. You may obtain a copy of the License at
  41029. *
  41030. * http://www.apache.org/licenses/LICENSE-2.0
  41031. *
  41032. * Unless required by applicable law or agreed to in writing,
  41033. * software distributed under the License is distributed on an
  41034. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41035. * KIND, either express or implied. See the License for the
  41036. * specific language governing permissions and limitations
  41037. * under the License.
  41038. */
  41039. // FIXME Better way to pack data in graphic element
  41040. /**
  41041. * @action
  41042. * @property {string} type
  41043. * @property {number} seriesIndex
  41044. * @property {number} dataIndex
  41045. * @property {number} [x]
  41046. * @property {number} [y]
  41047. */
  41048. registerAction(
  41049. {
  41050. type: 'showTip',
  41051. event: 'showTip',
  41052. update: 'tooltip:manuallyShowTip'
  41053. },
  41054. // noop
  41055. function () {}
  41056. );
  41057. registerAction(
  41058. {
  41059. type: 'hideTip',
  41060. event: 'hideTip',
  41061. update: 'tooltip:manuallyHideTip'
  41062. },
  41063. // noop
  41064. function () {}
  41065. );
  41066. /*
  41067. * Licensed to the Apache Software Foundation (ASF) under one
  41068. * or more contributor license agreements. See the NOTICE file
  41069. * distributed with this work for additional information
  41070. * regarding copyright ownership. The ASF licenses this file
  41071. * to you under the Apache License, Version 2.0 (the
  41072. * "License"); you may not use this file except in compliance
  41073. * with the License. You may obtain a copy of the License at
  41074. *
  41075. * http://www.apache.org/licenses/LICENSE-2.0
  41076. *
  41077. * Unless required by applicable law or agreed to in writing,
  41078. * software distributed under the License is distributed on an
  41079. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41080. * KIND, either express or implied. See the License for the
  41081. * specific language governing permissions and limitations
  41082. * under the License.
  41083. */
  41084. var LegendModel = extendComponentModel({
  41085. type: 'legend.plain',
  41086. dependencies: ['series'],
  41087. layoutMode: {
  41088. type: 'box',
  41089. // legend.width/height are maxWidth/maxHeight actually,
  41090. // whereas realy width/height is calculated by its content.
  41091. // (Setting {left: 10, right: 10} does not make sense).
  41092. // So consider the case:
  41093. // `setOption({legend: {left: 10});`
  41094. // then `setOption({legend: {right: 10});`
  41095. // The previous `left` should be cleared by setting `ignoreSize`.
  41096. ignoreSize: true
  41097. },
  41098. init: function (option, parentModel, ecModel) {
  41099. this.mergeDefaultAndTheme(option, ecModel);
  41100. option.selected = option.selected || {};
  41101. },
  41102. mergeOption: function (option) {
  41103. LegendModel.superCall(this, 'mergeOption', option);
  41104. },
  41105. optionUpdated: function () {
  41106. this._updateData(this.ecModel);
  41107. var legendData = this._data;
  41108. // If selectedMode is single, try to select one
  41109. if (legendData[0] && this.get('selectedMode') === 'single') {
  41110. var hasSelected = false;
  41111. // If has any selected in option.selected
  41112. for (var i = 0; i < legendData.length; i++) {
  41113. var name = legendData[i].get('name');
  41114. if (this.isSelected(name)) {
  41115. // Force to unselect others
  41116. this.select(name);
  41117. hasSelected = true;
  41118. break;
  41119. }
  41120. }
  41121. // Try select the first if selectedMode is single
  41122. !hasSelected && this.select(legendData[0].get('name'));
  41123. }
  41124. },
  41125. _updateData: function (ecModel) {
  41126. var potentialData = [];
  41127. var availableNames = [];
  41128. ecModel.eachRawSeries(function (seriesModel) {
  41129. var seriesName = seriesModel.name;
  41130. availableNames.push(seriesName);
  41131. var isPotential;
  41132. if (seriesModel.legendDataProvider) {
  41133. var data = seriesModel.legendDataProvider();
  41134. var names = data.mapArray(data.getName);
  41135. if (!ecModel.isSeriesFiltered(seriesModel)) {
  41136. availableNames = availableNames.concat(names);
  41137. }
  41138. if (names.length) {
  41139. potentialData = potentialData.concat(names);
  41140. }
  41141. else {
  41142. isPotential = true;
  41143. }
  41144. }
  41145. else {
  41146. isPotential = true;
  41147. }
  41148. if (isPotential && isNameSpecified(seriesModel)) {
  41149. potentialData.push(seriesModel.name);
  41150. }
  41151. });
  41152. /**
  41153. * @type {Array.<string>}
  41154. * @private
  41155. */
  41156. this._availableNames = availableNames;
  41157. // If legend.data not specified in option, use availableNames as data,
  41158. // which is convinient for user preparing option.
  41159. var rawData = this.get('data') || potentialData;
  41160. var legendData = map(rawData, function (dataItem) {
  41161. // Can be string or number
  41162. if (typeof dataItem === 'string' || typeof dataItem === 'number') {
  41163. dataItem = {
  41164. name: dataItem
  41165. };
  41166. }
  41167. return new Model(dataItem, this, this.ecModel);
  41168. }, this);
  41169. /**
  41170. * @type {Array.<module:echarts/model/Model>}
  41171. * @private
  41172. */
  41173. this._data = legendData;
  41174. },
  41175. /**
  41176. * @return {Array.<module:echarts/model/Model>}
  41177. */
  41178. getData: function () {
  41179. return this._data;
  41180. },
  41181. /**
  41182. * @param {string} name
  41183. */
  41184. select: function (name) {
  41185. var selected = this.option.selected;
  41186. var selectedMode = this.get('selectedMode');
  41187. if (selectedMode === 'single') {
  41188. var data = this._data;
  41189. each$1(data, function (dataItem) {
  41190. selected[dataItem.get('name')] = false;
  41191. });
  41192. }
  41193. selected[name] = true;
  41194. },
  41195. /**
  41196. * @param {string} name
  41197. */
  41198. unSelect: function (name) {
  41199. if (this.get('selectedMode') !== 'single') {
  41200. this.option.selected[name] = false;
  41201. }
  41202. },
  41203. /**
  41204. * @param {string} name
  41205. */
  41206. toggleSelected: function (name) {
  41207. var selected = this.option.selected;
  41208. // Default is true
  41209. if (!selected.hasOwnProperty(name)) {
  41210. selected[name] = true;
  41211. }
  41212. this[selected[name] ? 'unSelect' : 'select'](name);
  41213. },
  41214. /**
  41215. * @param {string} name
  41216. */
  41217. isSelected: function (name) {
  41218. var selected = this.option.selected;
  41219. return !(selected.hasOwnProperty(name) && !selected[name])
  41220. && indexOf(this._availableNames, name) >= 0;
  41221. },
  41222. defaultOption: {
  41223. // 一级层叠
  41224. zlevel: 0,
  41225. // 二级层叠
  41226. z: 4,
  41227. show: true,
  41228. // 布局方式,默认为水平布局,可选为:
  41229. // 'horizontal' | 'vertical'
  41230. orient: 'horizontal',
  41231. left: 'center',
  41232. // right: 'center',
  41233. top: 0,
  41234. // bottom: null,
  41235. // 水平对齐
  41236. // 'auto' | 'left' | 'right'
  41237. // 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐
  41238. align: 'auto',
  41239. backgroundColor: 'rgba(0,0,0,0)',
  41240. // 图例边框颜色
  41241. borderColor: '#ccc',
  41242. borderRadius: 0,
  41243. // 图例边框线宽,单位px,默认为0(无边框)
  41244. borderWidth: 0,
  41245. // 图例内边距,单位px,默认各方向内边距为5,
  41246. // 接受数组分别设定上右下左边距,同css
  41247. padding: 5,
  41248. // 各个item之间的间隔,单位px,默认为10,
  41249. // 横向布局时为水平间隔,纵向布局时为纵向间隔
  41250. itemGap: 10,
  41251. // 图例图形宽度
  41252. itemWidth: 25,
  41253. // 图例图形高度
  41254. itemHeight: 14,
  41255. // 图例关闭时候的颜色
  41256. inactiveColor: '#ccc',
  41257. textStyle: {
  41258. // 图例文字颜色
  41259. color: '#333'
  41260. },
  41261. // formatter: '',
  41262. // 选择模式,默认开启图例开关
  41263. selectedMode: true,
  41264. // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入
  41265. // selected: null,
  41266. // 图例内容(详见legend.data,数组中每一项代表一个item
  41267. // data: [],
  41268. // Tooltip 相关配置
  41269. tooltip: {
  41270. show: false
  41271. }
  41272. }
  41273. });
  41274. /*
  41275. * Licensed to the Apache Software Foundation (ASF) under one
  41276. * or more contributor license agreements. See the NOTICE file
  41277. * distributed with this work for additional information
  41278. * regarding copyright ownership. The ASF licenses this file
  41279. * to you under the Apache License, Version 2.0 (the
  41280. * "License"); you may not use this file except in compliance
  41281. * with the License. You may obtain a copy of the License at
  41282. *
  41283. * http://www.apache.org/licenses/LICENSE-2.0
  41284. *
  41285. * Unless required by applicable law or agreed to in writing,
  41286. * software distributed under the License is distributed on an
  41287. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41288. * KIND, either express or implied. See the License for the
  41289. * specific language governing permissions and limitations
  41290. * under the License.
  41291. */
  41292. function legendSelectActionHandler(methodName, payload, ecModel) {
  41293. var selectedMap = {};
  41294. var isToggleSelect = methodName === 'toggleSelected';
  41295. var isSelected;
  41296. // Update all legend components
  41297. ecModel.eachComponent('legend', function (legendModel) {
  41298. if (isToggleSelect && isSelected != null) {
  41299. // Force other legend has same selected status
  41300. // Or the first is toggled to true and other are toggled to false
  41301. // In the case one legend has some item unSelected in option. And if other legend
  41302. // doesn't has the item, they will assume it is selected.
  41303. legendModel[isSelected ? 'select' : 'unSelect'](payload.name);
  41304. }
  41305. else {
  41306. legendModel[methodName](payload.name);
  41307. isSelected = legendModel.isSelected(payload.name);
  41308. }
  41309. var legendData = legendModel.getData();
  41310. each$1(legendData, function (model) {
  41311. var name = model.get('name');
  41312. // Wrap element
  41313. if (name === '\n' || name === '') {
  41314. return;
  41315. }
  41316. var isItemSelected = legendModel.isSelected(name);
  41317. if (selectedMap.hasOwnProperty(name)) {
  41318. // Unselected if any legend is unselected
  41319. selectedMap[name] = selectedMap[name] && isItemSelected;
  41320. }
  41321. else {
  41322. selectedMap[name] = isItemSelected;
  41323. }
  41324. });
  41325. });
  41326. // Return the event explicitly
  41327. return {
  41328. name: payload.name,
  41329. selected: selectedMap
  41330. };
  41331. }
  41332. /**
  41333. * @event legendToggleSelect
  41334. * @type {Object}
  41335. * @property {string} type 'legendToggleSelect'
  41336. * @property {string} [from]
  41337. * @property {string} name Series name or data item name
  41338. */
  41339. registerAction(
  41340. 'legendToggleSelect', 'legendselectchanged',
  41341. curry(legendSelectActionHandler, 'toggleSelected')
  41342. );
  41343. /**
  41344. * @event legendSelect
  41345. * @type {Object}
  41346. * @property {string} type 'legendSelect'
  41347. * @property {string} name Series name or data item name
  41348. */
  41349. registerAction(
  41350. 'legendSelect', 'legendselected',
  41351. curry(legendSelectActionHandler, 'select')
  41352. );
  41353. /**
  41354. * @event legendUnSelect
  41355. * @type {Object}
  41356. * @property {string} type 'legendUnSelect'
  41357. * @property {string} name Series name or data item name
  41358. */
  41359. registerAction(
  41360. 'legendUnSelect', 'legendunselected',
  41361. curry(legendSelectActionHandler, 'unSelect')
  41362. );
  41363. /*
  41364. * Licensed to the Apache Software Foundation (ASF) under one
  41365. * or more contributor license agreements. See the NOTICE file
  41366. * distributed with this work for additional information
  41367. * regarding copyright ownership. The ASF licenses this file
  41368. * to you under the Apache License, Version 2.0 (the
  41369. * "License"); you may not use this file except in compliance
  41370. * with the License. You may obtain a copy of the License at
  41371. *
  41372. * http://www.apache.org/licenses/LICENSE-2.0
  41373. *
  41374. * Unless required by applicable law or agreed to in writing,
  41375. * software distributed under the License is distributed on an
  41376. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41377. * KIND, either express or implied. See the License for the
  41378. * specific language governing permissions and limitations
  41379. * under the License.
  41380. */
  41381. /**
  41382. * Layout list like component.
  41383. * It will box layout each items in group of component and then position the whole group in the viewport
  41384. * @param {module:zrender/group/Group} group
  41385. * @param {module:echarts/model/Component} componentModel
  41386. * @param {module:echarts/ExtensionAPI}
  41387. */
  41388. function layout$2(group, componentModel, api) {
  41389. var boxLayoutParams = componentModel.getBoxLayoutParams();
  41390. var padding = componentModel.get('padding');
  41391. var viewportSize = {width: api.getWidth(), height: api.getHeight()};
  41392. var rect = getLayoutRect(
  41393. boxLayoutParams,
  41394. viewportSize,
  41395. padding
  41396. );
  41397. box(
  41398. componentModel.get('orient'),
  41399. group,
  41400. componentModel.get('itemGap'),
  41401. rect.width,
  41402. rect.height
  41403. );
  41404. positionElement(
  41405. group,
  41406. boxLayoutParams,
  41407. viewportSize,
  41408. padding
  41409. );
  41410. }
  41411. function makeBackground(rect, componentModel) {
  41412. var padding = normalizeCssArray$1(
  41413. componentModel.get('padding')
  41414. );
  41415. var style = componentModel.getItemStyle(['color', 'opacity']);
  41416. style.fill = componentModel.get('backgroundColor');
  41417. var rect = new Rect({
  41418. shape: {
  41419. x: rect.x - padding[3],
  41420. y: rect.y - padding[0],
  41421. width: rect.width + padding[1] + padding[3],
  41422. height: rect.height + padding[0] + padding[2],
  41423. r: componentModel.get('borderRadius')
  41424. },
  41425. style: style,
  41426. silent: true,
  41427. z2: -1
  41428. });
  41429. // FIXME
  41430. // `subPixelOptimizeRect` may bring some gap between edge of viewpart
  41431. // and background rect when setting like `left: 0`, `top: 0`.
  41432. // graphic.subPixelOptimizeRect(rect);
  41433. return rect;
  41434. }
  41435. /*
  41436. * Licensed to the Apache Software Foundation (ASF) under one
  41437. * or more contributor license agreements. See the NOTICE file
  41438. * distributed with this work for additional information
  41439. * regarding copyright ownership. The ASF licenses this file
  41440. * to you under the Apache License, Version 2.0 (the
  41441. * "License"); you may not use this file except in compliance
  41442. * with the License. You may obtain a copy of the License at
  41443. *
  41444. * http://www.apache.org/licenses/LICENSE-2.0
  41445. *
  41446. * Unless required by applicable law or agreed to in writing,
  41447. * software distributed under the License is distributed on an
  41448. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41449. * KIND, either express or implied. See the License for the
  41450. * specific language governing permissions and limitations
  41451. * under the License.
  41452. */
  41453. var curry$3 = curry;
  41454. var each$11 = each$1;
  41455. var Group$2 = Group;
  41456. var LegendView = extendComponentView({
  41457. type: 'legend.plain',
  41458. newlineDisabled: false,
  41459. /**
  41460. * @override
  41461. */
  41462. init: function () {
  41463. /**
  41464. * @private
  41465. * @type {module:zrender/container/Group}
  41466. */
  41467. this.group.add(this._contentGroup = new Group$2());
  41468. /**
  41469. * @private
  41470. * @type {module:zrender/Element}
  41471. */
  41472. this._backgroundEl;
  41473. /**
  41474. * If first rendering, `contentGroup.position` is [0, 0], which
  41475. * does not make sense and may cause unexepcted animation if adopted.
  41476. * @private
  41477. * @type {boolean}
  41478. */
  41479. this._isFirstRender = true;
  41480. },
  41481. /**
  41482. * @protected
  41483. */
  41484. getContentGroup: function () {
  41485. return this._contentGroup;
  41486. },
  41487. /**
  41488. * @override
  41489. */
  41490. render: function (legendModel, ecModel, api) {
  41491. var isFirstRender = this._isFirstRender;
  41492. this._isFirstRender = false;
  41493. this.resetInner();
  41494. if (!legendModel.get('show', true)) {
  41495. return;
  41496. }
  41497. var itemAlign = legendModel.get('align');
  41498. if (!itemAlign || itemAlign === 'auto') {
  41499. itemAlign = (
  41500. legendModel.get('left') === 'right'
  41501. && legendModel.get('orient') === 'vertical'
  41502. ) ? 'right' : 'left';
  41503. }
  41504. this.renderInner(itemAlign, legendModel, ecModel, api);
  41505. // Perform layout.
  41506. var positionInfo = legendModel.getBoxLayoutParams();
  41507. var viewportSize = {width: api.getWidth(), height: api.getHeight()};
  41508. var padding = legendModel.get('padding');
  41509. var maxSize = getLayoutRect(positionInfo, viewportSize, padding);
  41510. var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender);
  41511. // Place mainGroup, based on the calculated `mainRect`.
  41512. var layoutRect = getLayoutRect(
  41513. defaults({width: mainRect.width, height: mainRect.height}, positionInfo),
  41514. viewportSize,
  41515. padding
  41516. );
  41517. this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]);
  41518. // Render background after group is layout.
  41519. this.group.add(
  41520. this._backgroundEl = makeBackground(mainRect, legendModel)
  41521. );
  41522. },
  41523. /**
  41524. * @protected
  41525. */
  41526. resetInner: function () {
  41527. this.getContentGroup().removeAll();
  41528. this._backgroundEl && this.group.remove(this._backgroundEl);
  41529. },
  41530. /**
  41531. * @protected
  41532. */
  41533. renderInner: function (itemAlign, legendModel, ecModel, api) {
  41534. var contentGroup = this.getContentGroup();
  41535. var legendDrawnMap = createHashMap();
  41536. var selectMode = legendModel.get('selectedMode');
  41537. var excludeSeriesId = [];
  41538. ecModel.eachRawSeries(function (seriesModel) {
  41539. !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id);
  41540. });
  41541. each$11(legendModel.getData(), function (itemModel, dataIndex) {
  41542. var name = itemModel.get('name');
  41543. // Use empty string or \n as a newline string
  41544. if (!this.newlineDisabled && (name === '' || name === '\n')) {
  41545. contentGroup.add(new Group$2({
  41546. newline: true
  41547. }));
  41548. return;
  41549. }
  41550. // Representitive series.
  41551. var seriesModel = ecModel.getSeriesByName(name)[0];
  41552. if (legendDrawnMap.get(name)) {
  41553. // Have been drawed
  41554. return;
  41555. }
  41556. // Series legend
  41557. if (seriesModel) {
  41558. var data = seriesModel.getData();
  41559. var color = data.getVisual('color');
  41560. // If color is a callback function
  41561. if (typeof color === 'function') {
  41562. // Use the first data
  41563. color = color(seriesModel.getDataParams(0));
  41564. }
  41565. // Using rect symbol defaultly
  41566. var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect';
  41567. var symbolType = data.getVisual('symbol');
  41568. var itemGroup = this._createItem(
  41569. name, dataIndex, itemModel, legendModel,
  41570. legendSymbolType, symbolType,
  41571. itemAlign, color,
  41572. selectMode
  41573. );
  41574. itemGroup.on('click', curry$3(dispatchSelectAction, name, api))
  41575. .on('mouseover', curry$3(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId))
  41576. .on('mouseout', curry$3(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId));
  41577. legendDrawnMap.set(name, true);
  41578. }
  41579. else {
  41580. // Data legend of pie, funnel
  41581. ecModel.eachRawSeries(function (seriesModel) {
  41582. // In case multiple series has same data name
  41583. if (legendDrawnMap.get(name)) {
  41584. return;
  41585. }
  41586. if (seriesModel.legendDataProvider) {
  41587. var data = seriesModel.legendDataProvider();
  41588. var idx = data.indexOfName(name);
  41589. if (idx < 0) {
  41590. return;
  41591. }
  41592. var color = data.getItemVisual(idx, 'color');
  41593. var legendSymbolType = 'roundRect';
  41594. var itemGroup = this._createItem(
  41595. name, dataIndex, itemModel, legendModel,
  41596. legendSymbolType, null,
  41597. itemAlign, color,
  41598. selectMode
  41599. );
  41600. // FIXME: consider different series has items with the same name.
  41601. itemGroup.on('click', curry$3(dispatchSelectAction, name, api))
  41602. // Should not specify the series name, consider legend controls
  41603. // more than one pie series.
  41604. .on('mouseover', curry$3(dispatchHighlightAction, null, name, api, excludeSeriesId))
  41605. .on('mouseout', curry$3(dispatchDownplayAction, null, name, api, excludeSeriesId));
  41606. legendDrawnMap.set(name, true);
  41607. }
  41608. }, this);
  41609. }
  41610. if (__DEV__) {
  41611. if (!legendDrawnMap.get(name)) {
  41612. console.warn(
  41613. name + ' series not exists. Legend data should be same with series name or data name.'
  41614. );
  41615. }
  41616. }
  41617. }, this);
  41618. },
  41619. _createItem: function (
  41620. name, dataIndex, itemModel, legendModel,
  41621. legendSymbolType, symbolType,
  41622. itemAlign, color, selectMode
  41623. ) {
  41624. var itemWidth = legendModel.get('itemWidth');
  41625. var itemHeight = legendModel.get('itemHeight');
  41626. var inactiveColor = legendModel.get('inactiveColor');
  41627. var symbolKeepAspect = legendModel.get('symbolKeepAspect');
  41628. var isSelected = legendModel.isSelected(name);
  41629. var itemGroup = new Group$2();
  41630. var textStyleModel = itemModel.getModel('textStyle');
  41631. var itemIcon = itemModel.get('icon');
  41632. var tooltipModel = itemModel.getModel('tooltip');
  41633. var legendGlobalTooltipModel = tooltipModel.parentModel;
  41634. // Use user given icon first
  41635. legendSymbolType = itemIcon || legendSymbolType;
  41636. itemGroup.add(createSymbol(
  41637. legendSymbolType,
  41638. 0,
  41639. 0,
  41640. itemWidth,
  41641. itemHeight,
  41642. isSelected ? color : inactiveColor,
  41643. // symbolKeepAspect default true for legend
  41644. symbolKeepAspect == null ? true : symbolKeepAspect
  41645. ));
  41646. // Compose symbols
  41647. // PENDING
  41648. if (!itemIcon && symbolType
  41649. // At least show one symbol, can't be all none
  41650. && ((symbolType !== legendSymbolType) || symbolType === 'none')
  41651. ) {
  41652. var size = itemHeight * 0.8;
  41653. if (symbolType === 'none') {
  41654. symbolType = 'circle';
  41655. }
  41656. // Put symbol in the center
  41657. itemGroup.add(createSymbol(
  41658. symbolType,
  41659. (itemWidth - size) / 2,
  41660. (itemHeight - size) / 2,
  41661. size,
  41662. size,
  41663. isSelected ? color : inactiveColor,
  41664. // symbolKeepAspect default true for legend
  41665. symbolKeepAspect == null ? true : symbolKeepAspect
  41666. ));
  41667. }
  41668. var textX = itemAlign === 'left' ? itemWidth + 5 : -5;
  41669. var textAlign = itemAlign;
  41670. var formatter = legendModel.get('formatter');
  41671. var content = name;
  41672. if (typeof formatter === 'string' && formatter) {
  41673. content = formatter.replace('{name}', name != null ? name : '');
  41674. }
  41675. else if (typeof formatter === 'function') {
  41676. content = formatter(name);
  41677. }
  41678. itemGroup.add(new Text({
  41679. style: setTextStyle({}, textStyleModel, {
  41680. text: content,
  41681. x: textX,
  41682. y: itemHeight / 2,
  41683. textFill: isSelected ? textStyleModel.getTextColor() : inactiveColor,
  41684. textAlign: textAlign,
  41685. textVerticalAlign: 'middle'
  41686. })
  41687. }));
  41688. // Add a invisible rect to increase the area of mouse hover
  41689. var hitRect = new Rect({
  41690. shape: itemGroup.getBoundingRect(),
  41691. invisible: true,
  41692. tooltip: tooltipModel.get('show') ? extend({
  41693. content: name,
  41694. // Defaul formatter
  41695. formatter: legendGlobalTooltipModel.get('formatter', true) || function () {
  41696. return name;
  41697. },
  41698. formatterParams: {
  41699. componentType: 'legend',
  41700. legendIndex: legendModel.componentIndex,
  41701. name: name,
  41702. $vars: ['name']
  41703. }
  41704. }, tooltipModel.option) : null
  41705. });
  41706. itemGroup.add(hitRect);
  41707. itemGroup.eachChild(function (child) {
  41708. child.silent = true;
  41709. });
  41710. hitRect.silent = !selectMode;
  41711. this.getContentGroup().add(itemGroup);
  41712. setHoverStyle(itemGroup);
  41713. itemGroup.__legendDataIndex = dataIndex;
  41714. return itemGroup;
  41715. },
  41716. /**
  41717. * @protected
  41718. */
  41719. layoutInner: function (legendModel, itemAlign, maxSize) {
  41720. var contentGroup = this.getContentGroup();
  41721. // Place items in contentGroup.
  41722. box(
  41723. legendModel.get('orient'),
  41724. contentGroup,
  41725. legendModel.get('itemGap'),
  41726. maxSize.width,
  41727. maxSize.height
  41728. );
  41729. var contentRect = contentGroup.getBoundingRect();
  41730. contentGroup.attr('position', [-contentRect.x, -contentRect.y]);
  41731. return this.group.getBoundingRect();
  41732. },
  41733. /**
  41734. * @protected
  41735. */
  41736. remove: function () {
  41737. this.getContentGroup().removeAll();
  41738. this._isFirstRender = true;
  41739. }
  41740. });
  41741. function dispatchSelectAction(name, api) {
  41742. api.dispatchAction({
  41743. type: 'legendToggleSelect',
  41744. name: name
  41745. });
  41746. }
  41747. function dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId) {
  41748. // If element hover will move to a hoverLayer.
  41749. var el = api.getZr().storage.getDisplayList()[0];
  41750. if (!(el && el.useHoverLayer)) {
  41751. api.dispatchAction({
  41752. type: 'highlight',
  41753. seriesName: seriesName,
  41754. name: dataName,
  41755. excludeSeriesId: excludeSeriesId
  41756. });
  41757. }
  41758. }
  41759. function dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId) {
  41760. // If element hover will move to a hoverLayer.
  41761. var el = api.getZr().storage.getDisplayList()[0];
  41762. if (!(el && el.useHoverLayer)) {
  41763. api.dispatchAction({
  41764. type: 'downplay',
  41765. seriesName: seriesName,
  41766. name: dataName,
  41767. excludeSeriesId: excludeSeriesId
  41768. });
  41769. }
  41770. }
  41771. /*
  41772. * Licensed to the Apache Software Foundation (ASF) under one
  41773. * or more contributor license agreements. See the NOTICE file
  41774. * distributed with this work for additional information
  41775. * regarding copyright ownership. The ASF licenses this file
  41776. * to you under the Apache License, Version 2.0 (the
  41777. * "License"); you may not use this file except in compliance
  41778. * with the License. You may obtain a copy of the License at
  41779. *
  41780. * http://www.apache.org/licenses/LICENSE-2.0
  41781. *
  41782. * Unless required by applicable law or agreed to in writing,
  41783. * software distributed under the License is distributed on an
  41784. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41785. * KIND, either express or implied. See the License for the
  41786. * specific language governing permissions and limitations
  41787. * under the License.
  41788. */
  41789. var legendFilter = function (ecModel) {
  41790. var legendModels = ecModel.findComponents({
  41791. mainType: 'legend'
  41792. });
  41793. if (legendModels && legendModels.length) {
  41794. ecModel.filterSeries(function (series) {
  41795. // If in any legend component the status is not selected.
  41796. // Because in legend series is assumed selected when it is not in the legend data.
  41797. for (var i = 0; i < legendModels.length; i++) {
  41798. if (!legendModels[i].isSelected(series.name)) {
  41799. return false;
  41800. }
  41801. }
  41802. return true;
  41803. });
  41804. }
  41805. };
  41806. /*
  41807. * Licensed to the Apache Software Foundation (ASF) under one
  41808. * or more contributor license agreements. See the NOTICE file
  41809. * distributed with this work for additional information
  41810. * regarding copyright ownership. The ASF licenses this file
  41811. * to you under the Apache License, Version 2.0 (the
  41812. * "License"); you may not use this file except in compliance
  41813. * with the License. You may obtain a copy of the License at
  41814. *
  41815. * http://www.apache.org/licenses/LICENSE-2.0
  41816. *
  41817. * Unless required by applicable law or agreed to in writing,
  41818. * software distributed under the License is distributed on an
  41819. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41820. * KIND, either express or implied. See the License for the
  41821. * specific language governing permissions and limitations
  41822. * under the License.
  41823. */
  41824. // Do not contain scrollable legend, for sake of file size.
  41825. // Series Filter
  41826. registerProcessor(legendFilter);
  41827. ComponentModel.registerSubTypeDefaulter('legend', function () {
  41828. // Default 'plain' when no type specified.
  41829. return 'plain';
  41830. });
  41831. /*
  41832. * Licensed to the Apache Software Foundation (ASF) under one
  41833. * or more contributor license agreements. See the NOTICE file
  41834. * distributed with this work for additional information
  41835. * regarding copyright ownership. The ASF licenses this file
  41836. * to you under the Apache License, Version 2.0 (the
  41837. * "License"); you may not use this file except in compliance
  41838. * with the License. You may obtain a copy of the License at
  41839. *
  41840. * http://www.apache.org/licenses/LICENSE-2.0
  41841. *
  41842. * Unless required by applicable law or agreed to in writing,
  41843. * software distributed under the License is distributed on an
  41844. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41845. * KIND, either express or implied. See the License for the
  41846. * specific language governing permissions and limitations
  41847. * under the License.
  41848. */
  41849. var ScrollableLegendModel = LegendModel.extend({
  41850. type: 'legend.scroll',
  41851. /**
  41852. * @param {number} scrollDataIndex
  41853. */
  41854. setScrollDataIndex: function (scrollDataIndex) {
  41855. this.option.scrollDataIndex = scrollDataIndex;
  41856. },
  41857. defaultOption: {
  41858. scrollDataIndex: 0,
  41859. pageButtonItemGap: 5,
  41860. pageButtonGap: null,
  41861. pageButtonPosition: 'end', // 'start' or 'end'
  41862. pageFormatter: '{current}/{total}', // If null/undefined, do not show page.
  41863. pageIcons: {
  41864. horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],
  41865. vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']
  41866. },
  41867. pageIconColor: '#2f4554',
  41868. pageIconInactiveColor: '#aaa',
  41869. pageIconSize: 15, // Can be [10, 3], which represents [width, height]
  41870. pageTextStyle: {
  41871. color: '#333'
  41872. },
  41873. animationDurationUpdate: 800
  41874. },
  41875. /**
  41876. * @override
  41877. */
  41878. init: function (option, parentModel, ecModel, extraOpt) {
  41879. var inputPositionParams = getLayoutParams(option);
  41880. ScrollableLegendModel.superCall(this, 'init', option, parentModel, ecModel, extraOpt);
  41881. mergeAndNormalizeLayoutParams(this, option, inputPositionParams);
  41882. },
  41883. /**
  41884. * @override
  41885. */
  41886. mergeOption: function (option, extraOpt) {
  41887. ScrollableLegendModel.superCall(this, 'mergeOption', option, extraOpt);
  41888. mergeAndNormalizeLayoutParams(this, this.option, option);
  41889. },
  41890. getOrient: function () {
  41891. return this.get('orient') === 'vertical'
  41892. ? {index: 1, name: 'vertical'}
  41893. : {index: 0, name: 'horizontal'};
  41894. }
  41895. });
  41896. // Do not `ignoreSize` to enable setting {left: 10, right: 10}.
  41897. function mergeAndNormalizeLayoutParams(legendModel, target, raw) {
  41898. var orient = legendModel.getOrient();
  41899. var ignoreSize = [1, 1];
  41900. ignoreSize[orient.index] = 0;
  41901. mergeLayoutParam(target, raw, {
  41902. type: 'box', ignoreSize: ignoreSize
  41903. });
  41904. }
  41905. /*
  41906. * Licensed to the Apache Software Foundation (ASF) under one
  41907. * or more contributor license agreements. See the NOTICE file
  41908. * distributed with this work for additional information
  41909. * regarding copyright ownership. The ASF licenses this file
  41910. * to you under the Apache License, Version 2.0 (the
  41911. * "License"); you may not use this file except in compliance
  41912. * with the License. You may obtain a copy of the License at
  41913. *
  41914. * http://www.apache.org/licenses/LICENSE-2.0
  41915. *
  41916. * Unless required by applicable law or agreed to in writing,
  41917. * software distributed under the License is distributed on an
  41918. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41919. * KIND, either express or implied. See the License for the
  41920. * specific language governing permissions and limitations
  41921. * under the License.
  41922. */
  41923. /**
  41924. * Separate legend and scrollable legend to reduce package size.
  41925. */
  41926. var Group$3 = Group;
  41927. var WH = ['width', 'height'];
  41928. var XY = ['x', 'y'];
  41929. var ScrollableLegendView = LegendView.extend({
  41930. type: 'legend.scroll',
  41931. newlineDisabled: true,
  41932. init: function () {
  41933. ScrollableLegendView.superCall(this, 'init');
  41934. /**
  41935. * @private
  41936. * @type {number} For `scroll`.
  41937. */
  41938. this._currentIndex = 0;
  41939. /**
  41940. * @private
  41941. * @type {module:zrender/container/Group}
  41942. */
  41943. this.group.add(this._containerGroup = new Group$3());
  41944. this._containerGroup.add(this.getContentGroup());
  41945. /**
  41946. * @private
  41947. * @type {module:zrender/container/Group}
  41948. */
  41949. this.group.add(this._controllerGroup = new Group$3());
  41950. /**
  41951. *
  41952. * @private
  41953. */
  41954. this._showController;
  41955. },
  41956. /**
  41957. * @override
  41958. */
  41959. resetInner: function () {
  41960. ScrollableLegendView.superCall(this, 'resetInner');
  41961. this._controllerGroup.removeAll();
  41962. this._containerGroup.removeClipPath();
  41963. this._containerGroup.__rectSize = null;
  41964. },
  41965. /**
  41966. * @override
  41967. */
  41968. renderInner: function (itemAlign, legendModel, ecModel, api) {
  41969. var me = this;
  41970. // Render content items.
  41971. ScrollableLegendView.superCall(this, 'renderInner', itemAlign, legendModel, ecModel, api);
  41972. var controllerGroup = this._controllerGroup;
  41973. // FIXME: support be 'auto' adapt to size number text length,
  41974. // e.g., '3/12345' should not overlap with the control arrow button.
  41975. var pageIconSize = legendModel.get('pageIconSize', true);
  41976. if (!isArray(pageIconSize)) {
  41977. pageIconSize = [pageIconSize, pageIconSize];
  41978. }
  41979. createPageButton('pagePrev', 0);
  41980. var pageTextStyleModel = legendModel.getModel('pageTextStyle');
  41981. controllerGroup.add(new Text({
  41982. name: 'pageText',
  41983. style: {
  41984. textFill: pageTextStyleModel.getTextColor(),
  41985. font: pageTextStyleModel.getFont(),
  41986. textVerticalAlign: 'middle',
  41987. textAlign: 'center'
  41988. },
  41989. silent: true
  41990. }));
  41991. createPageButton('pageNext', 1);
  41992. function createPageButton(name, iconIdx) {
  41993. var pageDataIndexName = name + 'DataIndex';
  41994. var icon = createIcon(
  41995. legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx],
  41996. {
  41997. // Buttons will be created in each render, so we do not need
  41998. // to worry about avoiding using legendModel kept in scope.
  41999. onclick: bind(
  42000. me._pageGo, me, pageDataIndexName, legendModel, api
  42001. )
  42002. },
  42003. {
  42004. x: -pageIconSize[0] / 2,
  42005. y: -pageIconSize[1] / 2,
  42006. width: pageIconSize[0],
  42007. height: pageIconSize[1]
  42008. }
  42009. );
  42010. icon.name = name;
  42011. controllerGroup.add(icon);
  42012. }
  42013. },
  42014. /**
  42015. * @override
  42016. */
  42017. layoutInner: function (legendModel, itemAlign, maxSize, isFirstRender) {
  42018. var contentGroup = this.getContentGroup();
  42019. var containerGroup = this._containerGroup;
  42020. var controllerGroup = this._controllerGroup;
  42021. var orientIdx = legendModel.getOrient().index;
  42022. var wh = WH[orientIdx];
  42023. var hw = WH[1 - orientIdx];
  42024. var yx = XY[1 - orientIdx];
  42025. // Place items in contentGroup.
  42026. box(
  42027. legendModel.get('orient'),
  42028. contentGroup,
  42029. legendModel.get('itemGap'),
  42030. !orientIdx ? null : maxSize.width,
  42031. orientIdx ? null : maxSize.height
  42032. );
  42033. box(
  42034. // Buttons in controller are layout always horizontally.
  42035. 'horizontal',
  42036. controllerGroup,
  42037. legendModel.get('pageButtonItemGap', true)
  42038. );
  42039. var contentRect = contentGroup.getBoundingRect();
  42040. var controllerRect = controllerGroup.getBoundingRect();
  42041. var showController = this._showController = contentRect[wh] > maxSize[wh];
  42042. var contentPos = [-contentRect.x, -contentRect.y];
  42043. // Remain contentPos when scroll animation perfroming.
  42044. // If first rendering, `contentGroup.position` is [0, 0], which
  42045. // does not make sense and may cause unexepcted animation if adopted.
  42046. if (!isFirstRender) {
  42047. contentPos[orientIdx] = contentGroup.position[orientIdx];
  42048. }
  42049. // Layout container group based on 0.
  42050. var containerPos = [0, 0];
  42051. var controllerPos = [-controllerRect.x, -controllerRect.y];
  42052. var pageButtonGap = retrieve2(
  42053. legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)
  42054. );
  42055. // Place containerGroup and controllerGroup and contentGroup.
  42056. if (showController) {
  42057. var pageButtonPosition = legendModel.get('pageButtonPosition', true);
  42058. // controller is on the right / bottom.
  42059. if (pageButtonPosition === 'end') {
  42060. controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];
  42061. }
  42062. // controller is on the left / top.
  42063. else {
  42064. containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;
  42065. }
  42066. }
  42067. // Always align controller to content as 'middle'.
  42068. controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
  42069. contentGroup.attr('position', contentPos);
  42070. containerGroup.attr('position', containerPos);
  42071. controllerGroup.attr('position', controllerPos);
  42072. // Calculate `mainRect` and set `clipPath`.
  42073. // mainRect should not be calculated by `this.group.getBoundingRect()`
  42074. // for sake of the overflow.
  42075. var mainRect = this.group.getBoundingRect();
  42076. var mainRect = {x: 0, y: 0};
  42077. // Consider content may be overflow (should be clipped).
  42078. mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
  42079. mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]);
  42080. // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.
  42081. mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);
  42082. containerGroup.__rectSize = maxSize[wh];
  42083. if (showController) {
  42084. var clipShape = {x: 0, y: 0};
  42085. clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);
  42086. clipShape[hw] = mainRect[hw];
  42087. containerGroup.setClipPath(new Rect({shape: clipShape}));
  42088. // Consider content may be larger than container, container rect
  42089. // can not be obtained from `containerGroup.getBoundingRect()`.
  42090. containerGroup.__rectSize = clipShape[wh];
  42091. }
  42092. else {
  42093. // Do not remove or ignore controller. Keep them set as place holders.
  42094. controllerGroup.eachChild(function (child) {
  42095. child.attr({invisible: true, silent: true});
  42096. });
  42097. }
  42098. // Content translate animation.
  42099. var pageInfo = this._getPageInfo(legendModel);
  42100. pageInfo.pageIndex != null && updateProps(
  42101. contentGroup,
  42102. {position: pageInfo.contentPosition},
  42103. // When switch from "show controller" to "not show controller", view should be
  42104. // updated immediately without animation, otherwise causes weird efffect.
  42105. showController ? legendModel : false
  42106. );
  42107. this._updatePageInfoView(legendModel, pageInfo);
  42108. return mainRect;
  42109. },
  42110. _pageGo: function (to, legendModel, api) {
  42111. var scrollDataIndex = this._getPageInfo(legendModel)[to];
  42112. scrollDataIndex != null && api.dispatchAction({
  42113. type: 'legendScroll',
  42114. scrollDataIndex: scrollDataIndex,
  42115. legendId: legendModel.id
  42116. });
  42117. },
  42118. _updatePageInfoView: function (legendModel, pageInfo) {
  42119. var controllerGroup = this._controllerGroup;
  42120. each$1(['pagePrev', 'pageNext'], function (name) {
  42121. var canJump = pageInfo[name + 'DataIndex'] != null;
  42122. var icon = controllerGroup.childOfName(name);
  42123. if (icon) {
  42124. icon.setStyle(
  42125. 'fill',
  42126. canJump
  42127. ? legendModel.get('pageIconColor', true)
  42128. : legendModel.get('pageIconInactiveColor', true)
  42129. );
  42130. icon.cursor = canJump ? 'pointer' : 'default';
  42131. }
  42132. });
  42133. var pageText = controllerGroup.childOfName('pageText');
  42134. var pageFormatter = legendModel.get('pageFormatter');
  42135. var pageIndex = pageInfo.pageIndex;
  42136. var current = pageIndex != null ? pageIndex + 1 : 0;
  42137. var total = pageInfo.pageCount;
  42138. pageText && pageFormatter && pageText.setStyle(
  42139. 'text',
  42140. isString(pageFormatter)
  42141. ? pageFormatter.replace('{current}', current).replace('{total}', total)
  42142. : pageFormatter({current: current, total: total})
  42143. );
  42144. },
  42145. /**
  42146. * @param {module:echarts/model/Model} legendModel
  42147. * @return {Object} {
  42148. * contentPosition: Array.<number>, null when data item not found.
  42149. * pageIndex: number, null when data item not found.
  42150. * pageCount: number, always be a number, can be 0.
  42151. * pagePrevDataIndex: number, null when no next page.
  42152. * pageNextDataIndex: number, null when no previous page.
  42153. * }
  42154. */
  42155. _getPageInfo: function (legendModel) {
  42156. var scrollDataIndex = legendModel.get('scrollDataIndex', true);
  42157. var contentGroup = this.getContentGroup();
  42158. var containerRectSize = this._containerGroup.__rectSize;
  42159. var orientIdx = legendModel.getOrient().index;
  42160. var wh = WH[orientIdx];
  42161. var xy = XY[orientIdx];
  42162. var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);
  42163. var children = contentGroup.children();
  42164. var targetItem = children[targetItemIndex];
  42165. var itemCount = children.length;
  42166. var pCount = !itemCount ? 0 : 1;
  42167. var result = {
  42168. contentPosition: contentGroup.position.slice(),
  42169. pageCount: pCount,
  42170. pageIndex: pCount - 1,
  42171. pagePrevDataIndex: null,
  42172. pageNextDataIndex: null
  42173. };
  42174. if (!targetItem) {
  42175. return result;
  42176. }
  42177. var targetItemInfo = getItemInfo(targetItem);
  42178. result.contentPosition[orientIdx] = -targetItemInfo.s;
  42179. // Strategy:
  42180. // (1) Always align based on the left/top most item.
  42181. // (2) It is user-friendly that the last item shown in the
  42182. // current window is shown at the begining of next window.
  42183. // Otherwise if half of the last item is cut by the window,
  42184. // it will have no chance to display entirely.
  42185. // (3) Consider that item size probably be different, we
  42186. // have calculate pageIndex by size rather than item index,
  42187. // and we can not get page index directly by division.
  42188. // (4) The window is to narrow to contain more than
  42189. // one item, we should make sure that the page can be fliped.
  42190. for (var i = targetItemIndex + 1,
  42191. winStartItemInfo = targetItemInfo,
  42192. winEndItemInfo = targetItemInfo,
  42193. currItemInfo = null;
  42194. i <= itemCount;
  42195. ++i
  42196. ) {
  42197. currItemInfo = getItemInfo(children[i]);
  42198. if (
  42199. // Half of the last item is out of the window.
  42200. (!currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize)
  42201. // If the current item does not intersect with the window, the new page
  42202. // can be started at the current item or the last item.
  42203. || (currItemInfo && !intersect(currItemInfo, winStartItemInfo.s))
  42204. ) {
  42205. if (winEndItemInfo.i > winStartItemInfo.i) {
  42206. winStartItemInfo = winEndItemInfo;
  42207. }
  42208. else { // e.g., when page size is smaller than item size.
  42209. winStartItemInfo = currItemInfo;
  42210. }
  42211. if (winStartItemInfo) {
  42212. if (result.pageNextDataIndex == null) {
  42213. result.pageNextDataIndex = winStartItemInfo.i;
  42214. }
  42215. ++result.pageCount;
  42216. }
  42217. }
  42218. winEndItemInfo = currItemInfo;
  42219. }
  42220. for (var i = targetItemIndex - 1,
  42221. winStartItemInfo = targetItemInfo,
  42222. winEndItemInfo = targetItemInfo,
  42223. currItemInfo = null;
  42224. i >= -1;
  42225. --i
  42226. ) {
  42227. currItemInfo = getItemInfo(children[i]);
  42228. if (
  42229. // If the the end item does not intersect with the window started
  42230. // from the current item, a page can be settled.
  42231. (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s))
  42232. // e.g., when page size is smaller than item size.
  42233. && winStartItemInfo.i < winEndItemInfo.i
  42234. ) {
  42235. winEndItemInfo = winStartItemInfo;
  42236. if (result.pagePrevDataIndex == null) {
  42237. result.pagePrevDataIndex = winStartItemInfo.i;
  42238. }
  42239. ++result.pageCount;
  42240. ++result.pageIndex;
  42241. }
  42242. winStartItemInfo = currItemInfo;
  42243. }
  42244. return result;
  42245. function getItemInfo(el) {
  42246. if (el) {
  42247. var itemRect = el.getBoundingRect();
  42248. var start = itemRect[xy] + el.position[orientIdx];
  42249. return {
  42250. s: start,
  42251. e: start + itemRect[wh],
  42252. i: el.__legendDataIndex
  42253. };
  42254. }
  42255. }
  42256. function intersect(itemInfo, winStart) {
  42257. return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;
  42258. }
  42259. },
  42260. _findTargetItemIndex: function (targetDataIndex) {
  42261. var index;
  42262. var contentGroup = this.getContentGroup();
  42263. if (this._showController) {
  42264. contentGroup.eachChild(function (child, idx) {
  42265. if (child.__legendDataIndex === targetDataIndex) {
  42266. index = idx;
  42267. }
  42268. });
  42269. }
  42270. else {
  42271. index = 0;
  42272. }
  42273. return index;
  42274. }
  42275. });
  42276. /*
  42277. * Licensed to the Apache Software Foundation (ASF) under one
  42278. * or more contributor license agreements. See the NOTICE file
  42279. * distributed with this work for additional information
  42280. * regarding copyright ownership. The ASF licenses this file
  42281. * to you under the Apache License, Version 2.0 (the
  42282. * "License"); you may not use this file except in compliance
  42283. * with the License. You may obtain a copy of the License at
  42284. *
  42285. * http://www.apache.org/licenses/LICENSE-2.0
  42286. *
  42287. * Unless required by applicable law or agreed to in writing,
  42288. * software distributed under the License is distributed on an
  42289. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42290. * KIND, either express or implied. See the License for the
  42291. * specific language governing permissions and limitations
  42292. * under the License.
  42293. */
  42294. /**
  42295. * @event legendScroll
  42296. * @type {Object}
  42297. * @property {string} type 'legendScroll'
  42298. * @property {string} scrollDataIndex
  42299. */
  42300. registerAction(
  42301. 'legendScroll', 'legendscroll',
  42302. function (payload, ecModel) {
  42303. var scrollDataIndex = payload.scrollDataIndex;
  42304. scrollDataIndex != null && ecModel.eachComponent(
  42305. {mainType: 'legend', subType: 'scroll', query: payload},
  42306. function (legendModel) {
  42307. legendModel.setScrollDataIndex(scrollDataIndex);
  42308. }
  42309. );
  42310. }
  42311. );
  42312. /*
  42313. * Licensed to the Apache Software Foundation (ASF) under one
  42314. * or more contributor license agreements. See the NOTICE file
  42315. * distributed with this work for additional information
  42316. * regarding copyright ownership. The ASF licenses this file
  42317. * to you under the Apache License, Version 2.0 (the
  42318. * "License"); you may not use this file except in compliance
  42319. * with the License. You may obtain a copy of the License at
  42320. *
  42321. * http://www.apache.org/licenses/LICENSE-2.0
  42322. *
  42323. * Unless required by applicable law or agreed to in writing,
  42324. * software distributed under the License is distributed on an
  42325. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42326. * KIND, either express or implied. See the License for the
  42327. * specific language governing permissions and limitations
  42328. * under the License.
  42329. */
  42330. /**
  42331. * Legend component entry file8
  42332. */
  42333. /*
  42334. * Licensed to the Apache Software Foundation (ASF) under one
  42335. * or more contributor license agreements. See the NOTICE file
  42336. * distributed with this work for additional information
  42337. * regarding copyright ownership. The ASF licenses this file
  42338. * to you under the Apache License, Version 2.0 (the
  42339. * "License"); you may not use this file except in compliance
  42340. * with the License. You may obtain a copy of the License at
  42341. *
  42342. * http://www.apache.org/licenses/LICENSE-2.0
  42343. *
  42344. * Unless required by applicable law or agreed to in writing,
  42345. * software distributed under the License is distributed on an
  42346. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42347. * KIND, either express or implied. See the License for the
  42348. * specific language governing permissions and limitations
  42349. * under the License.
  42350. */
  42351. /*
  42352. * Licensed to the Apache Software Foundation (ASF) under one
  42353. * or more contributor license agreements. See the NOTICE file
  42354. * distributed with this work for additional information
  42355. * regarding copyright ownership. The ASF licenses this file
  42356. * to you under the Apache License, Version 2.0 (the
  42357. * "License"); you may not use this file except in compliance
  42358. * with the License. You may obtain a copy of the License at
  42359. *
  42360. * http://www.apache.org/licenses/LICENSE-2.0
  42361. *
  42362. * Unless required by applicable law or agreed to in writing,
  42363. * software distributed under the License is distributed on an
  42364. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42365. * KIND, either express or implied. See the License for the
  42366. * specific language governing permissions and limitations
  42367. * under the License.
  42368. */
  42369. // Model
  42370. extendComponentModel({
  42371. type: 'title',
  42372. layoutMode: {type: 'box', ignoreSize: true},
  42373. defaultOption: {
  42374. // 一级层叠
  42375. zlevel: 0,
  42376. // 二级层叠
  42377. z: 6,
  42378. show: true,
  42379. text: '',
  42380. // 超链接跳转
  42381. // link: null,
  42382. // 仅支持self | blank
  42383. target: 'blank',
  42384. subtext: '',
  42385. // 超链接跳转
  42386. // sublink: null,
  42387. // 仅支持self | blank
  42388. subtarget: 'blank',
  42389. // 'center' ¦ 'left' ¦ 'right'
  42390. // ¦ {number}(x坐标,单位px)
  42391. left: 0,
  42392. // 'top' ¦ 'bottom' ¦ 'center'
  42393. // ¦ {number}(y坐标,单位px)
  42394. top: 0,
  42395. // 水平对齐
  42396. // 'auto' | 'left' | 'right' | 'center'
  42397. // 默认根据 left 的位置判断是左对齐还是右对齐
  42398. // textAlign: null
  42399. //
  42400. // 垂直对齐
  42401. // 'auto' | 'top' | 'bottom' | 'middle'
  42402. // 默认根据 top 位置判断是上对齐还是下对齐
  42403. // textBaseline: null
  42404. backgroundColor: 'rgba(0,0,0,0)',
  42405. // 标题边框颜色
  42406. borderColor: '#ccc',
  42407. // 标题边框线宽,单位px,默认为0(无边框)
  42408. borderWidth: 0,
  42409. // 标题内边距,单位px,默认各方向内边距为5,
  42410. // 接受数组分别设定上右下左边距,同css
  42411. padding: 5,
  42412. // 主副标题纵向间隔,单位px,默认为10,
  42413. itemGap: 10,
  42414. textStyle: {
  42415. fontSize: 18,
  42416. fontWeight: 'bolder',
  42417. color: '#333'
  42418. },
  42419. subtextStyle: {
  42420. color: '#aaa'
  42421. }
  42422. }
  42423. });
  42424. // View
  42425. extendComponentView({
  42426. type: 'title',
  42427. render: function (titleModel, ecModel, api) {
  42428. this.group.removeAll();
  42429. if (!titleModel.get('show')) {
  42430. return;
  42431. }
  42432. var group = this.group;
  42433. var textStyleModel = titleModel.getModel('textStyle');
  42434. var subtextStyleModel = titleModel.getModel('subtextStyle');
  42435. var textAlign = titleModel.get('textAlign');
  42436. var textBaseline = titleModel.get('textBaseline');
  42437. var textEl = new Text({
  42438. style: setTextStyle({}, textStyleModel, {
  42439. text: titleModel.get('text'),
  42440. textFill: textStyleModel.getTextColor()
  42441. }, {disableBox: true}),
  42442. z2: 10
  42443. });
  42444. var textRect = textEl.getBoundingRect();
  42445. var subText = titleModel.get('subtext');
  42446. var subTextEl = new Text({
  42447. style: setTextStyle({}, subtextStyleModel, {
  42448. text: subText,
  42449. textFill: subtextStyleModel.getTextColor(),
  42450. y: textRect.height + titleModel.get('itemGap'),
  42451. textVerticalAlign: 'top'
  42452. }, {disableBox: true}),
  42453. z2: 10
  42454. });
  42455. var link = titleModel.get('link');
  42456. var sublink = titleModel.get('sublink');
  42457. var triggerEvent = titleModel.get('triggerEvent', true);
  42458. textEl.silent = !link && !triggerEvent;
  42459. subTextEl.silent = !sublink && !triggerEvent;
  42460. if (link) {
  42461. textEl.on('click', function () {
  42462. window.open(link, '_' + titleModel.get('target'));
  42463. });
  42464. }
  42465. if (sublink) {
  42466. subTextEl.on('click', function () {
  42467. window.open(sublink, '_' + titleModel.get('subtarget'));
  42468. });
  42469. }
  42470. textEl.eventData = subTextEl.eventData = triggerEvent
  42471. ? {
  42472. componentType: 'title',
  42473. componentIndex: titleModel.componentIndex
  42474. }
  42475. : null;
  42476. group.add(textEl);
  42477. subText && group.add(subTextEl);
  42478. // If no subText, but add subTextEl, there will be an empty line.
  42479. var groupRect = group.getBoundingRect();
  42480. var layoutOption = titleModel.getBoxLayoutParams();
  42481. layoutOption.width = groupRect.width;
  42482. layoutOption.height = groupRect.height;
  42483. var layoutRect = getLayoutRect(
  42484. layoutOption, {
  42485. width: api.getWidth(),
  42486. height: api.getHeight()
  42487. }, titleModel.get('padding')
  42488. );
  42489. // Adjust text align based on position
  42490. if (!textAlign) {
  42491. // Align left if title is on the left. center and right is same
  42492. textAlign = titleModel.get('left') || titleModel.get('right');
  42493. if (textAlign === 'middle') {
  42494. textAlign = 'center';
  42495. }
  42496. // Adjust layout by text align
  42497. if (textAlign === 'right') {
  42498. layoutRect.x += layoutRect.width;
  42499. }
  42500. else if (textAlign === 'center') {
  42501. layoutRect.x += layoutRect.width / 2;
  42502. }
  42503. }
  42504. if (!textBaseline) {
  42505. textBaseline = titleModel.get('top') || titleModel.get('bottom');
  42506. if (textBaseline === 'center') {
  42507. textBaseline = 'middle';
  42508. }
  42509. if (textBaseline === 'bottom') {
  42510. layoutRect.y += layoutRect.height;
  42511. }
  42512. else if (textBaseline === 'middle') {
  42513. layoutRect.y += layoutRect.height / 2;
  42514. }
  42515. textBaseline = textBaseline || 'top';
  42516. }
  42517. group.attr('position', [layoutRect.x, layoutRect.y]);
  42518. var alignStyle = {
  42519. textAlign: textAlign,
  42520. textVerticalAlign: textBaseline
  42521. };
  42522. textEl.setStyle(alignStyle);
  42523. subTextEl.setStyle(alignStyle);
  42524. // Render background
  42525. // Get groupRect again because textAlign has been changed
  42526. groupRect = group.getBoundingRect();
  42527. var padding = layoutRect.margin;
  42528. var style = titleModel.getItemStyle(['color', 'opacity']);
  42529. style.fill = titleModel.get('backgroundColor');
  42530. var rect = new Rect({
  42531. shape: {
  42532. x: groupRect.x - padding[3],
  42533. y: groupRect.y - padding[0],
  42534. width: groupRect.width + padding[1] + padding[3],
  42535. height: groupRect.height + padding[0] + padding[2],
  42536. r: titleModel.get('borderRadius')
  42537. },
  42538. style: style,
  42539. silent: true
  42540. });
  42541. subPixelOptimizeRect(rect);
  42542. group.add(rect);
  42543. }
  42544. });
  42545. /*
  42546. * Licensed to the Apache Software Foundation (ASF) under one
  42547. * or more contributor license agreements. See the NOTICE file
  42548. * distributed with this work for additional information
  42549. * regarding copyright ownership. The ASF licenses this file
  42550. * to you under the Apache License, Version 2.0 (the
  42551. * "License"); you may not use this file except in compliance
  42552. * with the License. You may obtain a copy of the License at
  42553. *
  42554. * http://www.apache.org/licenses/LICENSE-2.0
  42555. *
  42556. * Unless required by applicable law or agreed to in writing,
  42557. * software distributed under the License is distributed on an
  42558. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42559. * KIND, either express or implied. See the License for the
  42560. * specific language governing permissions and limitations
  42561. * under the License.
  42562. */
  42563. var addCommas$1 = addCommas;
  42564. var encodeHTML$1 = encodeHTML;
  42565. function fillLabel(opt) {
  42566. defaultEmphasis(opt, 'label', ['show']);
  42567. }
  42568. var MarkerModel = extendComponentModel({
  42569. type: 'marker',
  42570. dependencies: ['series', 'grid', 'polar', 'geo'],
  42571. /**
  42572. * @overrite
  42573. */
  42574. init: function (option, parentModel, ecModel, extraOpt) {
  42575. if (__DEV__) {
  42576. if (this.type === 'marker') {
  42577. throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');
  42578. }
  42579. }
  42580. this.mergeDefaultAndTheme(option, ecModel);
  42581. this.mergeOption(option, ecModel, extraOpt.createdBySelf, true);
  42582. },
  42583. /**
  42584. * @return {boolean}
  42585. */
  42586. isAnimationEnabled: function () {
  42587. if (env$1.node) {
  42588. return false;
  42589. }
  42590. var hostSeries = this.__hostSeries;
  42591. return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();
  42592. },
  42593. mergeOption: function (newOpt, ecModel, createdBySelf, isInit) {
  42594. var MarkerModel = this.constructor;
  42595. var modelPropName = this.mainType + 'Model';
  42596. if (!createdBySelf) {
  42597. ecModel.eachSeries(function (seriesModel) {
  42598. var markerOpt = seriesModel.get(this.mainType, true);
  42599. var markerModel = seriesModel[modelPropName];
  42600. if (!markerOpt || !markerOpt.data) {
  42601. seriesModel[modelPropName] = null;
  42602. return;
  42603. }
  42604. if (!markerModel) {
  42605. if (isInit) {
  42606. // Default label emphasis `position` and `show`
  42607. fillLabel(markerOpt);
  42608. }
  42609. each$1(markerOpt.data, function (item) {
  42610. // FIXME Overwrite fillLabel method ?
  42611. if (item instanceof Array) {
  42612. fillLabel(item[0]);
  42613. fillLabel(item[1]);
  42614. }
  42615. else {
  42616. fillLabel(item);
  42617. }
  42618. });
  42619. markerModel = new MarkerModel(
  42620. markerOpt, this, ecModel
  42621. );
  42622. extend(markerModel, {
  42623. mainType: this.mainType,
  42624. // Use the same series index and name
  42625. seriesIndex: seriesModel.seriesIndex,
  42626. name: seriesModel.name,
  42627. createdBySelf: true
  42628. });
  42629. markerModel.__hostSeries = seriesModel;
  42630. }
  42631. else {
  42632. markerModel.mergeOption(markerOpt, ecModel, true);
  42633. }
  42634. seriesModel[modelPropName] = markerModel;
  42635. }, this);
  42636. }
  42637. },
  42638. formatTooltip: function (dataIndex) {
  42639. var data = this.getData();
  42640. var value = this.getRawValue(dataIndex);
  42641. var formattedValue = isArray(value)
  42642. ? map(value, addCommas$1).join(', ') : addCommas$1(value);
  42643. var name = data.getName(dataIndex);
  42644. var html = encodeHTML$1(this.name);
  42645. if (value != null || name) {
  42646. html += '<br />';
  42647. }
  42648. if (name) {
  42649. html += encodeHTML$1(name);
  42650. if (value != null) {
  42651. html += ' : ';
  42652. }
  42653. }
  42654. if (value != null) {
  42655. html += encodeHTML$1(formattedValue);
  42656. }
  42657. return html;
  42658. },
  42659. getData: function () {
  42660. return this._data;
  42661. },
  42662. setData: function (data) {
  42663. this._data = data;
  42664. }
  42665. });
  42666. mixin(MarkerModel, dataFormatMixin);
  42667. /*
  42668. * Licensed to the Apache Software Foundation (ASF) under one
  42669. * or more contributor license agreements. See the NOTICE file
  42670. * distributed with this work for additional information
  42671. * regarding copyright ownership. The ASF licenses this file
  42672. * to you under the Apache License, Version 2.0 (the
  42673. * "License"); you may not use this file except in compliance
  42674. * with the License. You may obtain a copy of the License at
  42675. *
  42676. * http://www.apache.org/licenses/LICENSE-2.0
  42677. *
  42678. * Unless required by applicable law or agreed to in writing,
  42679. * software distributed under the License is distributed on an
  42680. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42681. * KIND, either express or implied. See the License for the
  42682. * specific language governing permissions and limitations
  42683. * under the License.
  42684. */
  42685. MarkerModel.extend({
  42686. type: 'markPoint',
  42687. defaultOption: {
  42688. zlevel: 0,
  42689. z: 5,
  42690. symbol: 'pin',
  42691. symbolSize: 50,
  42692. //symbolRotate: 0,
  42693. //symbolOffset: [0, 0]
  42694. tooltip: {
  42695. trigger: 'item'
  42696. },
  42697. label: {
  42698. show: true,
  42699. position: 'inside'
  42700. },
  42701. itemStyle: {
  42702. borderWidth: 2
  42703. },
  42704. emphasis: {
  42705. label: {
  42706. show: true
  42707. }
  42708. }
  42709. }
  42710. });
  42711. /*
  42712. * Licensed to the Apache Software Foundation (ASF) under one
  42713. * or more contributor license agreements. See the NOTICE file
  42714. * distributed with this work for additional information
  42715. * regarding copyright ownership. The ASF licenses this file
  42716. * to you under the Apache License, Version 2.0 (the
  42717. * "License"); you may not use this file except in compliance
  42718. * with the License. You may obtain a copy of the License at
  42719. *
  42720. * http://www.apache.org/licenses/LICENSE-2.0
  42721. *
  42722. * Unless required by applicable law or agreed to in writing,
  42723. * software distributed under the License is distributed on an
  42724. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42725. * KIND, either express or implied. See the License for the
  42726. * specific language governing permissions and limitations
  42727. * under the License.
  42728. */
  42729. var indexOf$1 = indexOf;
  42730. function hasXOrY(item) {
  42731. return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));
  42732. }
  42733. function hasXAndY(item) {
  42734. return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));
  42735. }
  42736. // Make it simple, do not visit all stacked value to count precision.
  42737. // function getPrecision(data, valueAxisDim, dataIndex) {
  42738. // var precision = -1;
  42739. // var stackedDim = data.mapDimension(valueAxisDim);
  42740. // do {
  42741. // precision = Math.max(
  42742. // numberUtil.getPrecision(data.get(stackedDim, dataIndex)),
  42743. // precision
  42744. // );
  42745. // var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');
  42746. // if (stackedOnSeries) {
  42747. // var byValue = data.get(data.getCalculationInfo('stackedByDimension'), dataIndex);
  42748. // data = stackedOnSeries.getData();
  42749. // dataIndex = data.indexOf(data.getCalculationInfo('stackedByDimension'), byValue);
  42750. // stackedDim = data.getCalculationInfo('stackedDimension');
  42751. // }
  42752. // else {
  42753. // data = null;
  42754. // }
  42755. // } while (data);
  42756. // return precision;
  42757. // }
  42758. function markerTypeCalculatorWithExtent(
  42759. mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex
  42760. ) {
  42761. var coordArr = [];
  42762. var stacked = isDimensionStacked(data, targetDataDim /*, otherDataDim*/);
  42763. var calcDataDim = stacked
  42764. ? data.getCalculationInfo('stackResultDimension')
  42765. : targetDataDim;
  42766. var value = numCalculate(data, calcDataDim, mlType);
  42767. var dataIndex = data.indicesOfNearest(calcDataDim, value)[0];
  42768. coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex);
  42769. coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex);
  42770. // Make it simple, do not visit all stacked value to count precision.
  42771. var precision = getPrecision(data.get(targetDataDim, dataIndex));
  42772. precision = Math.min(precision, 20);
  42773. if (precision >= 0) {
  42774. coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);
  42775. }
  42776. return coordArr;
  42777. }
  42778. var curry$4 = curry;
  42779. // TODO Specified percent
  42780. var markerTypeCalculator = {
  42781. /**
  42782. * @method
  42783. * @param {module:echarts/data/List} data
  42784. * @param {string} baseAxisDim
  42785. * @param {string} valueAxisDim
  42786. */
  42787. min: curry$4(markerTypeCalculatorWithExtent, 'min'),
  42788. /**
  42789. * @method
  42790. * @param {module:echarts/data/List} data
  42791. * @param {string} baseAxisDim
  42792. * @param {string} valueAxisDim
  42793. */
  42794. max: curry$4(markerTypeCalculatorWithExtent, 'max'),
  42795. /**
  42796. * @method
  42797. * @param {module:echarts/data/List} data
  42798. * @param {string} baseAxisDim
  42799. * @param {string} valueAxisDim
  42800. */
  42801. average: curry$4(markerTypeCalculatorWithExtent, 'average')
  42802. };
  42803. /**
  42804. * Transform markPoint data item to format used in List by do the following
  42805. * 1. Calculate statistic like `max`, `min`, `average`
  42806. * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array
  42807. * @param {module:echarts/model/Series} seriesModel
  42808. * @param {module:echarts/coord/*} [coordSys]
  42809. * @param {Object} item
  42810. * @return {Object}
  42811. */
  42812. function dataTransform(seriesModel, item) {
  42813. var data = seriesModel.getData();
  42814. var coordSys = seriesModel.coordinateSystem;
  42815. // 1. If not specify the position with pixel directly
  42816. // 2. If `coord` is not a data array. Which uses `xAxis`,
  42817. // `yAxis` to specify the coord on each dimension
  42818. // parseFloat first because item.x and item.y can be percent string like '20%'
  42819. if (item && !hasXAndY(item) && !isArray(item.coord) && coordSys) {
  42820. var dims = coordSys.dimensions;
  42821. var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);
  42822. // Clone the option
  42823. // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value
  42824. item = clone(item);
  42825. if (item.type
  42826. && markerTypeCalculator[item.type]
  42827. && axisInfo.baseAxis && axisInfo.valueAxis
  42828. ) {
  42829. var otherCoordIndex = indexOf$1(dims, axisInfo.baseAxis.dim);
  42830. var targetCoordIndex = indexOf$1(dims, axisInfo.valueAxis.dim);
  42831. item.coord = markerTypeCalculator[item.type](
  42832. data, axisInfo.baseDataDim, axisInfo.valueDataDim,
  42833. otherCoordIndex, targetCoordIndex
  42834. );
  42835. // Force to use the value of calculated value.
  42836. item.value = item.coord[targetCoordIndex];
  42837. }
  42838. else {
  42839. // FIXME Only has one of xAxis and yAxis.
  42840. var coord = [
  42841. item.xAxis != null ? item.xAxis : item.radiusAxis,
  42842. item.yAxis != null ? item.yAxis : item.angleAxis
  42843. ];
  42844. // Each coord support max, min, average
  42845. for (var i = 0; i < 2; i++) {
  42846. if (markerTypeCalculator[coord[i]]) {
  42847. coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]);
  42848. }
  42849. }
  42850. item.coord = coord;
  42851. }
  42852. }
  42853. return item;
  42854. }
  42855. function getAxisInfo$1(item, data, coordSys, seriesModel) {
  42856. var ret = {};
  42857. if (item.valueIndex != null || item.valueDim != null) {
  42858. ret.valueDataDim = item.valueIndex != null
  42859. ? data.getDimension(item.valueIndex) : item.valueDim;
  42860. ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim));
  42861. ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);
  42862. ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);
  42863. }
  42864. else {
  42865. ret.baseAxis = seriesModel.getBaseAxis();
  42866. ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);
  42867. ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);
  42868. ret.valueDataDim = data.mapDimension(ret.valueAxis.dim);
  42869. }
  42870. return ret;
  42871. }
  42872. function dataDimToCoordDim(seriesModel, dataDim) {
  42873. var data = seriesModel.getData();
  42874. var dimensions = data.dimensions;
  42875. dataDim = data.getDimension(dataDim);
  42876. for (var i = 0; i < dimensions.length; i++) {
  42877. var dimItem = data.getDimensionInfo(dimensions[i]);
  42878. if (dimItem.name === dataDim) {
  42879. return dimItem.coordDim;
  42880. }
  42881. }
  42882. }
  42883. /**
  42884. * Filter data which is out of coordinateSystem range
  42885. * [dataFilter description]
  42886. * @param {module:echarts/coord/*} [coordSys]
  42887. * @param {Object} item
  42888. * @return {boolean}
  42889. */
  42890. function dataFilter$1(coordSys, item) {
  42891. // Alwalys return true if there is no coordSys
  42892. return (coordSys && coordSys.containData && item.coord && !hasXOrY(item))
  42893. ? coordSys.containData(item.coord) : true;
  42894. }
  42895. function dimValueGetter(item, dimName, dataIndex, dimIndex) {
  42896. // x, y, radius, angle
  42897. if (dimIndex < 2) {
  42898. return item.coord && item.coord[dimIndex];
  42899. }
  42900. return item.value;
  42901. }
  42902. function numCalculate(data, valueDataDim, type) {
  42903. if (type === 'average') {
  42904. var sum = 0;
  42905. var count = 0;
  42906. data.each(valueDataDim, function (val, idx) {
  42907. if (!isNaN(val)) {
  42908. sum += val;
  42909. count++;
  42910. }
  42911. });
  42912. return sum / count;
  42913. }
  42914. else if (type === 'median') {
  42915. return data.getMedian(valueDataDim);
  42916. }
  42917. else {
  42918. // max & min
  42919. return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0];
  42920. }
  42921. }
  42922. /*
  42923. * Licensed to the Apache Software Foundation (ASF) under one
  42924. * or more contributor license agreements. See the NOTICE file
  42925. * distributed with this work for additional information
  42926. * regarding copyright ownership. The ASF licenses this file
  42927. * to you under the Apache License, Version 2.0 (the
  42928. * "License"); you may not use this file except in compliance
  42929. * with the License. You may obtain a copy of the License at
  42930. *
  42931. * http://www.apache.org/licenses/LICENSE-2.0
  42932. *
  42933. * Unless required by applicable law or agreed to in writing,
  42934. * software distributed under the License is distributed on an
  42935. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42936. * KIND, either express or implied. See the License for the
  42937. * specific language governing permissions and limitations
  42938. * under the License.
  42939. */
  42940. var MarkerView = extendComponentView({
  42941. type: 'marker',
  42942. init: function () {
  42943. /**
  42944. * Markline grouped by series
  42945. * @private
  42946. * @type {module:zrender/core/util.HashMap}
  42947. */
  42948. this.markerGroupMap = createHashMap();
  42949. },
  42950. render: function (markerModel, ecModel, api) {
  42951. var markerGroupMap = this.markerGroupMap;
  42952. markerGroupMap.each(function (item) {
  42953. item.__keep = false;
  42954. });
  42955. var markerModelKey = this.type + 'Model';
  42956. ecModel.eachSeries(function (seriesModel) {
  42957. var markerModel = seriesModel[markerModelKey];
  42958. markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api);
  42959. }, this);
  42960. markerGroupMap.each(function (item) {
  42961. !item.__keep && this.group.remove(item.group);
  42962. }, this);
  42963. },
  42964. renderSeries: function () {}
  42965. });
  42966. /*
  42967. * Licensed to the Apache Software Foundation (ASF) under one
  42968. * or more contributor license agreements. See the NOTICE file
  42969. * distributed with this work for additional information
  42970. * regarding copyright ownership. The ASF licenses this file
  42971. * to you under the Apache License, Version 2.0 (the
  42972. * "License"); you may not use this file except in compliance
  42973. * with the License. You may obtain a copy of the License at
  42974. *
  42975. * http://www.apache.org/licenses/LICENSE-2.0
  42976. *
  42977. * Unless required by applicable law or agreed to in writing,
  42978. * software distributed under the License is distributed on an
  42979. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  42980. * KIND, either express or implied. See the License for the
  42981. * specific language governing permissions and limitations
  42982. * under the License.
  42983. */
  42984. function updateMarkerLayout(mpData, seriesModel, api) {
  42985. var coordSys = seriesModel.coordinateSystem;
  42986. mpData.each(function (idx) {
  42987. var itemModel = mpData.getItemModel(idx);
  42988. var point;
  42989. var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());
  42990. var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());
  42991. if (!isNaN(xPx) && !isNaN(yPx)) {
  42992. point = [xPx, yPx];
  42993. }
  42994. // Chart like bar may have there own marker positioning logic
  42995. else if (seriesModel.getMarkerPosition) {
  42996. // Use the getMarkerPoisition
  42997. point = seriesModel.getMarkerPosition(
  42998. mpData.getValues(mpData.dimensions, idx)
  42999. );
  43000. }
  43001. else if (coordSys) {
  43002. var x = mpData.get(coordSys.dimensions[0], idx);
  43003. var y = mpData.get(coordSys.dimensions[1], idx);
  43004. point = coordSys.dataToPoint([x, y]);
  43005. }
  43006. // Use x, y if has any
  43007. if (!isNaN(xPx)) {
  43008. point[0] = xPx;
  43009. }
  43010. if (!isNaN(yPx)) {
  43011. point[1] = yPx;
  43012. }
  43013. mpData.setItemLayout(idx, point);
  43014. });
  43015. }
  43016. MarkerView.extend({
  43017. type: 'markPoint',
  43018. // updateLayout: function (markPointModel, ecModel, api) {
  43019. // ecModel.eachSeries(function (seriesModel) {
  43020. // var mpModel = seriesModel.markPointModel;
  43021. // if (mpModel) {
  43022. // updateMarkerLayout(mpModel.getData(), seriesModel, api);
  43023. // this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel);
  43024. // }
  43025. // }, this);
  43026. // },
  43027. updateTransform: function (markPointModel, ecModel, api) {
  43028. ecModel.eachSeries(function (seriesModel) {
  43029. var mpModel = seriesModel.markPointModel;
  43030. if (mpModel) {
  43031. updateMarkerLayout(mpModel.getData(), seriesModel, api);
  43032. this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel);
  43033. }
  43034. }, this);
  43035. },
  43036. renderSeries: function (seriesModel, mpModel, ecModel, api) {
  43037. var coordSys = seriesModel.coordinateSystem;
  43038. var seriesId = seriesModel.id;
  43039. var seriesData = seriesModel.getData();
  43040. var symbolDrawMap = this.markerGroupMap;
  43041. var symbolDraw = symbolDrawMap.get(seriesId)
  43042. || symbolDrawMap.set(seriesId, new SymbolDraw());
  43043. var mpData = createList$1(coordSys, seriesModel, mpModel);
  43044. // FIXME
  43045. mpModel.setData(mpData);
  43046. updateMarkerLayout(mpModel.getData(), seriesModel, api);
  43047. mpData.each(function (idx) {
  43048. var itemModel = mpData.getItemModel(idx);
  43049. var symbolSize = itemModel.getShallow('symbolSize');
  43050. if (typeof symbolSize === 'function') {
  43051. // FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据?
  43052. symbolSize = symbolSize(
  43053. mpModel.getRawValue(idx), mpModel.getDataParams(idx)
  43054. );
  43055. }
  43056. mpData.setItemVisual(idx, {
  43057. symbolSize: symbolSize,
  43058. color: itemModel.get('itemStyle.color')
  43059. || seriesData.getVisual('color'),
  43060. symbol: itemModel.getShallow('symbol')
  43061. });
  43062. });
  43063. // TODO Text are wrong
  43064. symbolDraw.updateData(mpData);
  43065. this.group.add(symbolDraw.group);
  43066. // Set host model for tooltip
  43067. // FIXME
  43068. mpData.eachItemGraphicEl(function (el) {
  43069. el.traverse(function (child) {
  43070. child.dataModel = mpModel;
  43071. });
  43072. });
  43073. symbolDraw.__keep = true;
  43074. symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');
  43075. }
  43076. });
  43077. /**
  43078. * @inner
  43079. * @param {module:echarts/coord/*} [coordSys]
  43080. * @param {module:echarts/model/Series} seriesModel
  43081. * @param {module:echarts/model/Model} mpModel
  43082. */
  43083. function createList$1(coordSys, seriesModel, mpModel) {
  43084. var coordDimsInfos;
  43085. if (coordSys) {
  43086. coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
  43087. var info = seriesModel.getData().getDimensionInfo(
  43088. seriesModel.getData().mapDimension(coordDim)
  43089. ) || {};
  43090. // In map series data don't have lng and lat dimension. Fallback to same with coordSys
  43091. return defaults({name: coordDim}, info);
  43092. });
  43093. }
  43094. else {
  43095. coordDimsInfos = [{
  43096. name: 'value',
  43097. type: 'float'
  43098. }];
  43099. }
  43100. var mpData = new List(coordDimsInfos, mpModel);
  43101. var dataOpt = map(mpModel.get('data'), curry(
  43102. dataTransform, seriesModel
  43103. ));
  43104. if (coordSys) {
  43105. dataOpt = filter(
  43106. dataOpt, curry(dataFilter$1, coordSys)
  43107. );
  43108. }
  43109. mpData.initData(dataOpt, null,
  43110. coordSys ? dimValueGetter : function (item) {
  43111. return item.value;
  43112. }
  43113. );
  43114. return mpData;
  43115. }
  43116. /*
  43117. * Licensed to the Apache Software Foundation (ASF) under one
  43118. * or more contributor license agreements. See the NOTICE file
  43119. * distributed with this work for additional information
  43120. * regarding copyright ownership. The ASF licenses this file
  43121. * to you under the Apache License, Version 2.0 (the
  43122. * "License"); you may not use this file except in compliance
  43123. * with the License. You may obtain a copy of the License at
  43124. *
  43125. * http://www.apache.org/licenses/LICENSE-2.0
  43126. *
  43127. * Unless required by applicable law or agreed to in writing,
  43128. * software distributed under the License is distributed on an
  43129. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43130. * KIND, either express or implied. See the License for the
  43131. * specific language governing permissions and limitations
  43132. * under the License.
  43133. */
  43134. // HINT Markpoint can't be used too much
  43135. registerPreprocessor(function (opt) {
  43136. // Make sure markPoint component is enabled
  43137. opt.markPoint = opt.markPoint || {};
  43138. });
  43139. /*
  43140. * Licensed to the Apache Software Foundation (ASF) under one
  43141. * or more contributor license agreements. See the NOTICE file
  43142. * distributed with this work for additional information
  43143. * regarding copyright ownership. The ASF licenses this file
  43144. * to you under the Apache License, Version 2.0 (the
  43145. * "License"); you may not use this file except in compliance
  43146. * with the License. You may obtain a copy of the License at
  43147. *
  43148. * http://www.apache.org/licenses/LICENSE-2.0
  43149. *
  43150. * Unless required by applicable law or agreed to in writing,
  43151. * software distributed under the License is distributed on an
  43152. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43153. * KIND, either express or implied. See the License for the
  43154. * specific language governing permissions and limitations
  43155. * under the License.
  43156. */
  43157. MarkerModel.extend({
  43158. type: 'markLine',
  43159. defaultOption: {
  43160. zlevel: 0,
  43161. z: 5,
  43162. symbol: ['circle', 'arrow'],
  43163. symbolSize: [8, 16],
  43164. //symbolRotate: 0,
  43165. precision: 2,
  43166. tooltip: {
  43167. trigger: 'item'
  43168. },
  43169. label: {
  43170. show: true,
  43171. position: 'end'
  43172. },
  43173. lineStyle: {
  43174. type: 'dashed'
  43175. },
  43176. emphasis: {
  43177. label: {
  43178. show: true
  43179. },
  43180. lineStyle: {
  43181. width: 3
  43182. }
  43183. },
  43184. animationEasing: 'linear'
  43185. }
  43186. });
  43187. /*
  43188. * Licensed to the Apache Software Foundation (ASF) under one
  43189. * or more contributor license agreements. See the NOTICE file
  43190. * distributed with this work for additional information
  43191. * regarding copyright ownership. The ASF licenses this file
  43192. * to you under the Apache License, Version 2.0 (the
  43193. * "License"); you may not use this file except in compliance
  43194. * with the License. You may obtain a copy of the License at
  43195. *
  43196. * http://www.apache.org/licenses/LICENSE-2.0
  43197. *
  43198. * Unless required by applicable law or agreed to in writing,
  43199. * software distributed under the License is distributed on an
  43200. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43201. * KIND, either express or implied. See the License for the
  43202. * specific language governing permissions and limitations
  43203. * under the License.
  43204. */
  43205. /**
  43206. * Line path for bezier and straight line draw
  43207. */
  43208. var straightLineProto = Line.prototype;
  43209. var bezierCurveProto = BezierCurve.prototype;
  43210. function isLine(shape) {
  43211. return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);
  43212. }
  43213. var LinePath = extendShape({
  43214. type: 'ec-line',
  43215. style: {
  43216. stroke: '#000',
  43217. fill: null
  43218. },
  43219. shape: {
  43220. x1: 0,
  43221. y1: 0,
  43222. x2: 0,
  43223. y2: 0,
  43224. percent: 1,
  43225. cpx1: null,
  43226. cpy1: null
  43227. },
  43228. buildPath: function (ctx, shape) {
  43229. (isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape);
  43230. },
  43231. pointAt: function (t) {
  43232. return isLine(this.shape)
  43233. ? straightLineProto.pointAt.call(this, t)
  43234. : bezierCurveProto.pointAt.call(this, t);
  43235. },
  43236. tangentAt: function (t) {
  43237. var shape = this.shape;
  43238. var p = isLine(shape)
  43239. ? [shape.x2 - shape.x1, shape.y2 - shape.y1]
  43240. : bezierCurveProto.tangentAt.call(this, t);
  43241. return normalize(p, p);
  43242. }
  43243. });
  43244. /*
  43245. * Licensed to the Apache Software Foundation (ASF) under one
  43246. * or more contributor license agreements. See the NOTICE file
  43247. * distributed with this work for additional information
  43248. * regarding copyright ownership. The ASF licenses this file
  43249. * to you under the Apache License, Version 2.0 (the
  43250. * "License"); you may not use this file except in compliance
  43251. * with the License. You may obtain a copy of the License at
  43252. *
  43253. * http://www.apache.org/licenses/LICENSE-2.0
  43254. *
  43255. * Unless required by applicable law or agreed to in writing,
  43256. * software distributed under the License is distributed on an
  43257. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43258. * KIND, either express or implied. See the License for the
  43259. * specific language governing permissions and limitations
  43260. * under the License.
  43261. */
  43262. /**
  43263. * @module echarts/chart/helper/Line
  43264. */
  43265. var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];
  43266. function makeSymbolTypeKey(symbolCategory) {
  43267. return '_' + symbolCategory + 'Type';
  43268. }
  43269. /**
  43270. * @inner
  43271. */
  43272. function createSymbol$1(name, lineData, idx) {
  43273. var color = lineData.getItemVisual(idx, 'color');
  43274. var symbolType = lineData.getItemVisual(idx, name);
  43275. var symbolSize = lineData.getItemVisual(idx, name + 'Size');
  43276. if (!symbolType || symbolType === 'none') {
  43277. return;
  43278. }
  43279. if (!isArray(symbolSize)) {
  43280. symbolSize = [symbolSize, symbolSize];
  43281. }
  43282. var symbolPath = createSymbol(
  43283. symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2,
  43284. symbolSize[0], symbolSize[1], color
  43285. );
  43286. symbolPath.name = name;
  43287. return symbolPath;
  43288. }
  43289. function createLine(points) {
  43290. var line = new LinePath({
  43291. name: 'line'
  43292. });
  43293. setLinePoints(line.shape, points);
  43294. return line;
  43295. }
  43296. function setLinePoints(targetShape, points) {
  43297. var p1 = points[0];
  43298. var p2 = points[1];
  43299. var cp1 = points[2];
  43300. targetShape.x1 = p1[0];
  43301. targetShape.y1 = p1[1];
  43302. targetShape.x2 = p2[0];
  43303. targetShape.y2 = p2[1];
  43304. targetShape.percent = 1;
  43305. if (cp1) {
  43306. targetShape.cpx1 = cp1[0];
  43307. targetShape.cpy1 = cp1[1];
  43308. }
  43309. else {
  43310. targetShape.cpx1 = NaN;
  43311. targetShape.cpy1 = NaN;
  43312. }
  43313. }
  43314. function updateSymbolAndLabelBeforeLineUpdate() {
  43315. var lineGroup = this;
  43316. var symbolFrom = lineGroup.childOfName('fromSymbol');
  43317. var symbolTo = lineGroup.childOfName('toSymbol');
  43318. var label = lineGroup.childOfName('label');
  43319. // Quick reject
  43320. if (!symbolFrom && !symbolTo && label.ignore) {
  43321. return;
  43322. }
  43323. var invScale = 1;
  43324. var parentNode = this.parent;
  43325. while (parentNode) {
  43326. if (parentNode.scale) {
  43327. invScale /= parentNode.scale[0];
  43328. }
  43329. parentNode = parentNode.parent;
  43330. }
  43331. var line = lineGroup.childOfName('line');
  43332. // If line not changed
  43333. // FIXME Parent scale changed
  43334. if (!this.__dirty && !line.__dirty) {
  43335. return;
  43336. }
  43337. var percent = line.shape.percent;
  43338. var fromPos = line.pointAt(0);
  43339. var toPos = line.pointAt(percent);
  43340. var d = sub([], toPos, fromPos);
  43341. normalize(d, d);
  43342. if (symbolFrom) {
  43343. symbolFrom.attr('position', fromPos);
  43344. var tangent = line.tangentAt(0);
  43345. symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
  43346. tangent[1], tangent[0]
  43347. ));
  43348. symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
  43349. }
  43350. if (symbolTo) {
  43351. symbolTo.attr('position', toPos);
  43352. var tangent = line.tangentAt(1);
  43353. symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
  43354. tangent[1], tangent[0]
  43355. ));
  43356. symbolTo.attr('scale', [invScale * percent, invScale * percent]);
  43357. }
  43358. if (!label.ignore) {
  43359. label.attr('position', toPos);
  43360. var textPosition;
  43361. var textAlign;
  43362. var textVerticalAlign;
  43363. var distance$$1 = 5 * invScale;
  43364. // End
  43365. if (label.__position === 'end') {
  43366. textPosition = [d[0] * distance$$1 + toPos[0], d[1] * distance$$1 + toPos[1]];
  43367. textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
  43368. textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
  43369. }
  43370. // Middle
  43371. else if (label.__position === 'middle') {
  43372. var halfPercent = percent / 2;
  43373. var tangent = line.tangentAt(halfPercent);
  43374. var n = [tangent[1], -tangent[0]];
  43375. var cp = line.pointAt(halfPercent);
  43376. if (n[1] > 0) {
  43377. n[0] = -n[0];
  43378. n[1] = -n[1];
  43379. }
  43380. textPosition = [cp[0] + n[0] * distance$$1, cp[1] + n[1] * distance$$1];
  43381. textAlign = 'center';
  43382. textVerticalAlign = 'bottom';
  43383. var rotation = -Math.atan2(tangent[1], tangent[0]);
  43384. if (toPos[0] < fromPos[0]) {
  43385. rotation = Math.PI + rotation;
  43386. }
  43387. label.attr('rotation', rotation);
  43388. }
  43389. // Start
  43390. else {
  43391. textPosition = [-d[0] * distance$$1 + fromPos[0], -d[1] * distance$$1 + fromPos[1]];
  43392. textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
  43393. textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
  43394. }
  43395. label.attr({
  43396. style: {
  43397. // Use the user specified text align and baseline first
  43398. textVerticalAlign: label.__verticalAlign || textVerticalAlign,
  43399. textAlign: label.__textAlign || textAlign
  43400. },
  43401. position: textPosition,
  43402. scale: [invScale, invScale]
  43403. });
  43404. }
  43405. }
  43406. /**
  43407. * @constructor
  43408. * @extends {module:zrender/graphic/Group}
  43409. * @alias {module:echarts/chart/helper/Line}
  43410. */
  43411. function Line$1(lineData, idx, seriesScope) {
  43412. Group.call(this);
  43413. this._createLine(lineData, idx, seriesScope);
  43414. }
  43415. var lineProto = Line$1.prototype;
  43416. // Update symbol position and rotation
  43417. lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
  43418. lineProto._createLine = function (lineData, idx, seriesScope) {
  43419. var seriesModel = lineData.hostModel;
  43420. var linePoints = lineData.getItemLayout(idx);
  43421. var line = createLine(linePoints);
  43422. line.shape.percent = 0;
  43423. initProps(line, {
  43424. shape: {
  43425. percent: 1
  43426. }
  43427. }, seriesModel, idx);
  43428. this.add(line);
  43429. var label = new Text({
  43430. name: 'label',
  43431. // FIXME
  43432. // Temporary solution for `focusNodeAdjacency`.
  43433. // line label do not use the opacity of lineStyle.
  43434. lineLabelOriginalOpacity: 1
  43435. });
  43436. this.add(label);
  43437. each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
  43438. var symbol = createSymbol$1(symbolCategory, lineData, idx);
  43439. // symbols must added after line to make sure
  43440. // it will be updated after line#update.
  43441. // Or symbol position and rotation update in line#beforeUpdate will be one frame slow
  43442. this.add(symbol);
  43443. this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
  43444. }, this);
  43445. this._updateCommonStl(lineData, idx, seriesScope);
  43446. };
  43447. lineProto.updateData = function (lineData, idx, seriesScope) {
  43448. var seriesModel = lineData.hostModel;
  43449. var line = this.childOfName('line');
  43450. var linePoints = lineData.getItemLayout(idx);
  43451. var target = {
  43452. shape: {}
  43453. };
  43454. setLinePoints(target.shape, linePoints);
  43455. updateProps(line, target, seriesModel, idx);
  43456. each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
  43457. var symbolType = lineData.getItemVisual(idx, symbolCategory);
  43458. var key = makeSymbolTypeKey(symbolCategory);
  43459. // Symbol changed
  43460. if (this[key] !== symbolType) {
  43461. this.remove(this.childOfName(symbolCategory));
  43462. var symbol = createSymbol$1(symbolCategory, lineData, idx);
  43463. this.add(symbol);
  43464. }
  43465. this[key] = symbolType;
  43466. }, this);
  43467. this._updateCommonStl(lineData, idx, seriesScope);
  43468. };
  43469. lineProto._updateCommonStl = function (lineData, idx, seriesScope) {
  43470. var seriesModel = lineData.hostModel;
  43471. var line = this.childOfName('line');
  43472. var lineStyle = seriesScope && seriesScope.lineStyle;
  43473. var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
  43474. var labelModel = seriesScope && seriesScope.labelModel;
  43475. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
  43476. // Optimization for large dataset
  43477. if (!seriesScope || lineData.hasItemOption) {
  43478. var itemModel = lineData.getItemModel(idx);
  43479. lineStyle = itemModel.getModel('lineStyle').getLineStyle();
  43480. hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();
  43481. labelModel = itemModel.getModel('label');
  43482. hoverLabelModel = itemModel.getModel('emphasis.label');
  43483. }
  43484. var visualColor = lineData.getItemVisual(idx, 'color');
  43485. var visualOpacity = retrieve3(
  43486. lineData.getItemVisual(idx, 'opacity'),
  43487. lineStyle.opacity,
  43488. 1
  43489. );
  43490. line.useStyle(defaults(
  43491. {
  43492. strokeNoScale: true,
  43493. fill: 'none',
  43494. stroke: visualColor,
  43495. opacity: visualOpacity
  43496. },
  43497. lineStyle
  43498. ));
  43499. line.hoverStyle = hoverLineStyle;
  43500. // Update symbol
  43501. each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
  43502. var symbol = this.childOfName(symbolCategory);
  43503. if (symbol) {
  43504. symbol.setColor(visualColor);
  43505. symbol.setStyle({
  43506. opacity: visualOpacity
  43507. });
  43508. }
  43509. }, this);
  43510. var showLabel = labelModel.getShallow('show');
  43511. var hoverShowLabel = hoverLabelModel.getShallow('show');
  43512. var label = this.childOfName('label');
  43513. var defaultLabelColor;
  43514. var baseText;
  43515. // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`.
  43516. if (showLabel || hoverShowLabel) {
  43517. defaultLabelColor = visualColor || '#000';
  43518. baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType);
  43519. if (baseText == null) {
  43520. var rawVal = seriesModel.getRawValue(idx);
  43521. baseText = rawVal == null
  43522. ? lineData.getName(idx)
  43523. : isFinite(rawVal)
  43524. ? round$2(rawVal)
  43525. : rawVal;
  43526. }
  43527. }
  43528. var normalText = showLabel ? baseText : null;
  43529. var emphasisText = hoverShowLabel
  43530. ? retrieve2(
  43531. seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
  43532. baseText
  43533. )
  43534. : null;
  43535. var labelStyle = label.style;
  43536. // Always set `textStyle` even if `normalStyle.text` is null, because default
  43537. // values have to be set on `normalStyle`.
  43538. if (normalText != null || emphasisText != null) {
  43539. setTextStyle(label.style, labelModel, {
  43540. text: normalText
  43541. }, {
  43542. autoColor: defaultLabelColor
  43543. });
  43544. label.__textAlign = labelStyle.textAlign;
  43545. label.__verticalAlign = labelStyle.textVerticalAlign;
  43546. // 'start', 'middle', 'end'
  43547. label.__position = labelModel.get('position') || 'middle';
  43548. }
  43549. if (emphasisText != null) {
  43550. // Only these properties supported in this emphasis style here.
  43551. label.hoverStyle = {
  43552. text: emphasisText,
  43553. textFill: hoverLabelModel.getTextColor(true),
  43554. // For merging hover style to normal style, do not use
  43555. // `hoverLabelModel.getFont()` here.
  43556. fontStyle: hoverLabelModel.getShallow('fontStyle'),
  43557. fontWeight: hoverLabelModel.getShallow('fontWeight'),
  43558. fontSize: hoverLabelModel.getShallow('fontSize'),
  43559. fontFamily: hoverLabelModel.getShallow('fontFamily')
  43560. };
  43561. }
  43562. else {
  43563. label.hoverStyle = {
  43564. text: null
  43565. };
  43566. }
  43567. label.ignore = !showLabel && !hoverShowLabel;
  43568. setHoverStyle(this);
  43569. };
  43570. lineProto.highlight = function () {
  43571. this.trigger('emphasis');
  43572. };
  43573. lineProto.downplay = function () {
  43574. this.trigger('normal');
  43575. };
  43576. lineProto.updateLayout = function (lineData, idx) {
  43577. this.setLinePoints(lineData.getItemLayout(idx));
  43578. };
  43579. lineProto.setLinePoints = function (points) {
  43580. var linePath = this.childOfName('line');
  43581. setLinePoints(linePath.shape, points);
  43582. linePath.dirty();
  43583. };
  43584. inherits(Line$1, Group);
  43585. /*
  43586. * Licensed to the Apache Software Foundation (ASF) under one
  43587. * or more contributor license agreements. See the NOTICE file
  43588. * distributed with this work for additional information
  43589. * regarding copyright ownership. The ASF licenses this file
  43590. * to you under the Apache License, Version 2.0 (the
  43591. * "License"); you may not use this file except in compliance
  43592. * with the License. You may obtain a copy of the License at
  43593. *
  43594. * http://www.apache.org/licenses/LICENSE-2.0
  43595. *
  43596. * Unless required by applicable law or agreed to in writing,
  43597. * software distributed under the License is distributed on an
  43598. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43599. * KIND, either express or implied. See the License for the
  43600. * specific language governing permissions and limitations
  43601. * under the License.
  43602. */
  43603. /**
  43604. * @module echarts/chart/helper/LineDraw
  43605. */
  43606. // import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
  43607. /**
  43608. * @alias module:echarts/component/marker/LineDraw
  43609. * @constructor
  43610. */
  43611. function LineDraw(ctor) {
  43612. this._ctor = ctor || Line$1;
  43613. this.group = new Group();
  43614. }
  43615. var lineDrawProto = LineDraw.prototype;
  43616. lineDrawProto.isPersistent = function () {
  43617. return true;
  43618. };
  43619. /**
  43620. * @param {module:echarts/data/List} lineData
  43621. */
  43622. lineDrawProto.updateData = function (lineData) {
  43623. var lineDraw = this;
  43624. var group = lineDraw.group;
  43625. var oldLineData = lineDraw._lineData;
  43626. lineDraw._lineData = lineData;
  43627. // There is no oldLineData only when first rendering or switching from
  43628. // stream mode to normal mode, where previous elements should be removed.
  43629. if (!oldLineData) {
  43630. group.removeAll();
  43631. }
  43632. var seriesScope = makeSeriesScope$1(lineData);
  43633. lineData.diff(oldLineData)
  43634. .add(function (idx) {
  43635. doAdd(lineDraw, lineData, idx, seriesScope);
  43636. })
  43637. .update(function (newIdx, oldIdx) {
  43638. doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope);
  43639. })
  43640. .remove(function (idx) {
  43641. group.remove(oldLineData.getItemGraphicEl(idx));
  43642. })
  43643. .execute();
  43644. };
  43645. function doAdd(lineDraw, lineData, idx, seriesScope) {
  43646. var itemLayout = lineData.getItemLayout(idx);
  43647. if (!lineNeedsDraw(itemLayout)) {
  43648. return;
  43649. }
  43650. var el = new lineDraw._ctor(lineData, idx, seriesScope);
  43651. lineData.setItemGraphicEl(idx, el);
  43652. lineDraw.group.add(el);
  43653. }
  43654. function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) {
  43655. var itemEl = oldLineData.getItemGraphicEl(oldIdx);
  43656. if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {
  43657. lineDraw.group.remove(itemEl);
  43658. return;
  43659. }
  43660. if (!itemEl) {
  43661. itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope);
  43662. }
  43663. else {
  43664. itemEl.updateData(newLineData, newIdx, seriesScope);
  43665. }
  43666. newLineData.setItemGraphicEl(newIdx, itemEl);
  43667. lineDraw.group.add(itemEl);
  43668. }
  43669. lineDrawProto.updateLayout = function () {
  43670. var lineData = this._lineData;
  43671. // Do not support update layout in incremental mode.
  43672. if (!lineData) {
  43673. return;
  43674. }
  43675. lineData.eachItemGraphicEl(function (el, idx) {
  43676. el.updateLayout(lineData, idx);
  43677. }, this);
  43678. };
  43679. lineDrawProto.incrementalPrepareUpdate = function (lineData) {
  43680. this._seriesScope = makeSeriesScope$1(lineData);
  43681. this._lineData = null;
  43682. this.group.removeAll();
  43683. };
  43684. lineDrawProto.incrementalUpdate = function (taskParams, lineData) {
  43685. function updateIncrementalAndHover(el) {
  43686. if (!el.isGroup) {
  43687. el.incremental = el.useHoverLayer = true;
  43688. }
  43689. }
  43690. for (var idx = taskParams.start; idx < taskParams.end; idx++) {
  43691. var itemLayout = lineData.getItemLayout(idx);
  43692. if (lineNeedsDraw(itemLayout)) {
  43693. var el = new this._ctor(lineData, idx, this._seriesScope);
  43694. el.traverse(updateIncrementalAndHover);
  43695. this.group.add(el);
  43696. lineData.setItemGraphicEl(idx, el);
  43697. }
  43698. }
  43699. };
  43700. function makeSeriesScope$1(lineData) {
  43701. var hostModel = lineData.hostModel;
  43702. return {
  43703. lineStyle: hostModel.getModel('lineStyle').getLineStyle(),
  43704. hoverLineStyle: hostModel.getModel('emphasis.lineStyle').getLineStyle(),
  43705. labelModel: hostModel.getModel('label'),
  43706. hoverLabelModel: hostModel.getModel('emphasis.label')
  43707. };
  43708. }
  43709. lineDrawProto.remove = function () {
  43710. this._clearIncremental();
  43711. this._incremental = null;
  43712. this.group.removeAll();
  43713. };
  43714. lineDrawProto._clearIncremental = function () {
  43715. var incremental = this._incremental;
  43716. if (incremental) {
  43717. incremental.clearDisplaybles();
  43718. }
  43719. };
  43720. function isPointNaN(pt) {
  43721. return isNaN(pt[0]) || isNaN(pt[1]);
  43722. }
  43723. function lineNeedsDraw(pts) {
  43724. return !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
  43725. }
  43726. /*
  43727. * Licensed to the Apache Software Foundation (ASF) under one
  43728. * or more contributor license agreements. See the NOTICE file
  43729. * distributed with this work for additional information
  43730. * regarding copyright ownership. The ASF licenses this file
  43731. * to you under the Apache License, Version 2.0 (the
  43732. * "License"); you may not use this file except in compliance
  43733. * with the License. You may obtain a copy of the License at
  43734. *
  43735. * http://www.apache.org/licenses/LICENSE-2.0
  43736. *
  43737. * Unless required by applicable law or agreed to in writing,
  43738. * software distributed under the License is distributed on an
  43739. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  43740. * KIND, either express or implied. See the License for the
  43741. * specific language governing permissions and limitations
  43742. * under the License.
  43743. */
  43744. var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
  43745. var data = seriesModel.getData();
  43746. // Special type markLine like 'min', 'max', 'average', 'median'
  43747. var mlType = item.type;
  43748. if (!isArray(item)
  43749. && (
  43750. mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median'
  43751. // In case
  43752. // data: [{
  43753. // yAxis: 10
  43754. // }]
  43755. || (item.xAxis != null || item.yAxis != null)
  43756. )
  43757. ) {
  43758. var valueAxis;
  43759. var valueDataDim;
  43760. var value;
  43761. if (item.yAxis != null || item.xAxis != null) {
  43762. valueDataDim = item.yAxis != null ? 'y' : 'x';
  43763. valueAxis = coordSys.getAxis(valueDataDim);
  43764. value = retrieve(item.yAxis, item.xAxis);
  43765. }
  43766. else {
  43767. var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);
  43768. valueDataDim = axisInfo.valueDataDim;
  43769. valueAxis = axisInfo.valueAxis;
  43770. value = numCalculate(data, valueDataDim, mlType);
  43771. }
  43772. var valueIndex = valueDataDim === 'x' ? 0 : 1;
  43773. var baseIndex = 1 - valueIndex;
  43774. var mlFrom = clone(item);
  43775. var mlTo = {};
  43776. mlFrom.type = null;
  43777. mlFrom.coord = [];
  43778. mlTo.coord = [];
  43779. mlFrom.coord[baseIndex] = -Infinity;
  43780. mlTo.coord[baseIndex] = Infinity;
  43781. var precision = mlModel.get('precision');
  43782. if (precision >= 0 && typeof value === 'number') {
  43783. value = +value.toFixed(Math.min(precision, 20));
  43784. }
  43785. mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;
  43786. item = [mlFrom, mlTo, { // Extra option for tooltip and label
  43787. type: mlType,
  43788. valueIndex: item.valueIndex,
  43789. // Force to use the value of calculated value.
  43790. value: value
  43791. }];
  43792. }
  43793. item = [
  43794. dataTransform(seriesModel, item[0]),
  43795. dataTransform(seriesModel, item[1]),
  43796. extend({}, item[2])
  43797. ];
  43798. // Avoid line data type is extended by from(to) data type
  43799. item[2].type = item[2].type || '';
  43800. // Merge from option and to option into line option
  43801. merge(item[2], item[0]);
  43802. merge(item[2], item[1]);
  43803. return item;
  43804. };
  43805. function isInifinity(val) {
  43806. return !isNaN(val) && !isFinite(val);
  43807. }
  43808. // If a markLine has one dim
  43809. function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
  43810. var otherDimIndex = 1 - dimIndex;
  43811. var dimName = coordSys.dimensions[dimIndex];
  43812. return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
  43813. && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
  43814. }
  43815. function markLineFilter(coordSys, item) {
  43816. if (coordSys.type === 'cartesian2d') {
  43817. var fromCoord = item[0].coord;
  43818. var toCoord = item[1].coord;
  43819. // In case
  43820. // {
  43821. // markLine: {
  43822. // data: [{ yAxis: 2 }]
  43823. // }
  43824. // }
  43825. if (
  43826. fromCoord && toCoord
  43827. && (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
  43828. || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
  43829. ) {
  43830. return true;
  43831. }
  43832. }
  43833. return dataFilter$1(coordSys, item[0])
  43834. && dataFilter$1(coordSys, item[1]);
  43835. }
  43836. function updateSingleMarkerEndLayout(
  43837. data, idx, isFrom, seriesModel, api
  43838. ) {
  43839. var coordSys = seriesModel.coordinateSystem;
  43840. var itemModel = data.getItemModel(idx);
  43841. var point;
  43842. var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());
  43843. var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());
  43844. if (!isNaN(xPx) && !isNaN(yPx)) {
  43845. point = [xPx, yPx];
  43846. }
  43847. else {
  43848. // Chart like bar may have there own marker positioning logic
  43849. if (seriesModel.getMarkerPosition) {
  43850. // Use the getMarkerPoisition
  43851. point = seriesModel.getMarkerPosition(
  43852. data.getValues(data.dimensions, idx)
  43853. );
  43854. }
  43855. else {
  43856. var dims = coordSys.dimensions;
  43857. var x = data.get(dims[0], idx);
  43858. var y = data.get(dims[1], idx);
  43859. point = coordSys.dataToPoint([x, y]);
  43860. }
  43861. // Expand line to the edge of grid if value on one axis is Inifnity
  43862. // In case
  43863. // markLine: {
  43864. // data: [{
  43865. // yAxis: 2
  43866. // // or
  43867. // type: 'average'
  43868. // }]
  43869. // }
  43870. if (coordSys.type === 'cartesian2d') {
  43871. var xAxis = coordSys.getAxis('x');
  43872. var yAxis = coordSys.getAxis('y');
  43873. var dims = coordSys.dimensions;
  43874. if (isInifinity(data.get(dims[0], idx))) {
  43875. point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
  43876. }
  43877. else if (isInifinity(data.get(dims[1], idx))) {
  43878. point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
  43879. }
  43880. }
  43881. // Use x, y if has any
  43882. if (!isNaN(xPx)) {
  43883. point[0] = xPx;
  43884. }
  43885. if (!isNaN(yPx)) {
  43886. point[1] = yPx;
  43887. }
  43888. }
  43889. data.setItemLayout(idx, point);
  43890. }
  43891. MarkerView.extend({
  43892. type: 'markLine',
  43893. // updateLayout: function (markLineModel, ecModel, api) {
  43894. // ecModel.eachSeries(function (seriesModel) {
  43895. // var mlModel = seriesModel.markLineModel;
  43896. // if (mlModel) {
  43897. // var mlData = mlModel.getData();
  43898. // var fromData = mlModel.__from;
  43899. // var toData = mlModel.__to;
  43900. // // Update visual and layout of from symbol and to symbol
  43901. // fromData.each(function (idx) {
  43902. // updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
  43903. // updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
  43904. // });
  43905. // // Update layout of line
  43906. // mlData.each(function (idx) {
  43907. // mlData.setItemLayout(idx, [
  43908. // fromData.getItemLayout(idx),
  43909. // toData.getItemLayout(idx)
  43910. // ]);
  43911. // });
  43912. // this.markerGroupMap.get(seriesModel.id).updateLayout();
  43913. // }
  43914. // }, this);
  43915. // },
  43916. updateTransform: function (markLineModel, ecModel, api) {
  43917. ecModel.eachSeries(function (seriesModel) {
  43918. var mlModel = seriesModel.markLineModel;
  43919. if (mlModel) {
  43920. var mlData = mlModel.getData();
  43921. var fromData = mlModel.__from;
  43922. var toData = mlModel.__to;
  43923. // Update visual and layout of from symbol and to symbol
  43924. fromData.each(function (idx) {
  43925. updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
  43926. updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
  43927. });
  43928. // Update layout of line
  43929. mlData.each(function (idx) {
  43930. mlData.setItemLayout(idx, [
  43931. fromData.getItemLayout(idx),
  43932. toData.getItemLayout(idx)
  43933. ]);
  43934. });
  43935. this.markerGroupMap.get(seriesModel.id).updateLayout();
  43936. }
  43937. }, this);
  43938. },
  43939. renderSeries: function (seriesModel, mlModel, ecModel, api) {
  43940. var coordSys = seriesModel.coordinateSystem;
  43941. var seriesId = seriesModel.id;
  43942. var seriesData = seriesModel.getData();
  43943. var lineDrawMap = this.markerGroupMap;
  43944. var lineDraw = lineDrawMap.get(seriesId)
  43945. || lineDrawMap.set(seriesId, new LineDraw());
  43946. this.group.add(lineDraw.group);
  43947. var mlData = createList$2(coordSys, seriesModel, mlModel);
  43948. var fromData = mlData.from;
  43949. var toData = mlData.to;
  43950. var lineData = mlData.line;
  43951. mlModel.__from = fromData;
  43952. mlModel.__to = toData;
  43953. // Line data for tooltip and formatter
  43954. mlModel.setData(lineData);
  43955. var symbolType = mlModel.get('symbol');
  43956. var symbolSize = mlModel.get('symbolSize');
  43957. if (!isArray(symbolType)) {
  43958. symbolType = [symbolType, symbolType];
  43959. }
  43960. if (typeof symbolSize === 'number') {
  43961. symbolSize = [symbolSize, symbolSize];
  43962. }
  43963. // Update visual and layout of from symbol and to symbol
  43964. mlData.from.each(function (idx) {
  43965. updateDataVisualAndLayout(fromData, idx, true);
  43966. updateDataVisualAndLayout(toData, idx, false);
  43967. });
  43968. // Update visual and layout of line
  43969. lineData.each(function (idx) {
  43970. var lineColor = lineData.getItemModel(idx).get('lineStyle.color');
  43971. lineData.setItemVisual(idx, {
  43972. color: lineColor || fromData.getItemVisual(idx, 'color')
  43973. });
  43974. lineData.setItemLayout(idx, [
  43975. fromData.getItemLayout(idx),
  43976. toData.getItemLayout(idx)
  43977. ]);
  43978. lineData.setItemVisual(idx, {
  43979. 'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
  43980. 'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
  43981. 'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
  43982. 'toSymbol': toData.getItemVisual(idx, 'symbol')
  43983. });
  43984. });
  43985. lineDraw.updateData(lineData);
  43986. // Set host model for tooltip
  43987. // FIXME
  43988. mlData.line.eachItemGraphicEl(function (el, idx) {
  43989. el.traverse(function (child) {
  43990. child.dataModel = mlModel;
  43991. });
  43992. });
  43993. function updateDataVisualAndLayout(data, idx, isFrom) {
  43994. var itemModel = data.getItemModel(idx);
  43995. updateSingleMarkerEndLayout(
  43996. data, idx, isFrom, seriesModel, api
  43997. );
  43998. data.setItemVisual(idx, {
  43999. symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
  44000. symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
  44001. color: itemModel.get('itemStyle.color') || seriesData.getVisual('color')
  44002. });
  44003. }
  44004. lineDraw.__keep = true;
  44005. lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');
  44006. }
  44007. });
  44008. /**
  44009. * @inner
  44010. * @param {module:echarts/coord/*} coordSys
  44011. * @param {module:echarts/model/Series} seriesModel
  44012. * @param {module:echarts/model/Model} mpModel
  44013. */
  44014. function createList$2(coordSys, seriesModel, mlModel) {
  44015. var coordDimsInfos;
  44016. if (coordSys) {
  44017. coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
  44018. var info = seriesModel.getData().getDimensionInfo(
  44019. seriesModel.getData().mapDimension(coordDim)
  44020. ) || {};
  44021. // In map series data don't have lng and lat dimension. Fallback to same with coordSys
  44022. return defaults({name: coordDim}, info);
  44023. });
  44024. }
  44025. else {
  44026. coordDimsInfos = [{
  44027. name: 'value',
  44028. type: 'float'
  44029. }];
  44030. }
  44031. var fromData = new List(coordDimsInfos, mlModel);
  44032. var toData = new List(coordDimsInfos, mlModel);
  44033. // No dimensions
  44034. var lineData = new List([], mlModel);
  44035. var optData = map(mlModel.get('data'), curry(
  44036. markLineTransform, seriesModel, coordSys, mlModel
  44037. ));
  44038. if (coordSys) {
  44039. optData = filter(
  44040. optData, curry(markLineFilter, coordSys)
  44041. );
  44042. }
  44043. var dimValueGetter$$1 = coordSys ? dimValueGetter : function (item) {
  44044. return item.value;
  44045. };
  44046. fromData.initData(
  44047. map(optData, function (item) {
  44048. return item[0];
  44049. }),
  44050. null,
  44051. dimValueGetter$$1
  44052. );
  44053. toData.initData(
  44054. map(optData, function (item) {
  44055. return item[1];
  44056. }),
  44057. null,
  44058. dimValueGetter$$1
  44059. );
  44060. lineData.initData(
  44061. map(optData, function (item) {
  44062. return item[2];
  44063. })
  44064. );
  44065. lineData.hasItemOption = true;
  44066. return {
  44067. from: fromData,
  44068. to: toData,
  44069. line: lineData
  44070. };
  44071. }
  44072. /*
  44073. * Licensed to the Apache Software Foundation (ASF) under one
  44074. * or more contributor license agreements. See the NOTICE file
  44075. * distributed with this work for additional information
  44076. * regarding copyright ownership. The ASF licenses this file
  44077. * to you under the Apache License, Version 2.0 (the
  44078. * "License"); you may not use this file except in compliance
  44079. * with the License. You may obtain a copy of the License at
  44080. *
  44081. * http://www.apache.org/licenses/LICENSE-2.0
  44082. *
  44083. * Unless required by applicable law or agreed to in writing,
  44084. * software distributed under the License is distributed on an
  44085. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44086. * KIND, either express or implied. See the License for the
  44087. * specific language governing permissions and limitations
  44088. * under the License.
  44089. */
  44090. registerPreprocessor(function (opt) {
  44091. // Make sure markLine component is enabled
  44092. opt.markLine = opt.markLine || {};
  44093. });
  44094. /*
  44095. * Licensed to the Apache Software Foundation (ASF) under one
  44096. * or more contributor license agreements. See the NOTICE file
  44097. * distributed with this work for additional information
  44098. * regarding copyright ownership. The ASF licenses this file
  44099. * to you under the Apache License, Version 2.0 (the
  44100. * "License"); you may not use this file except in compliance
  44101. * with the License. You may obtain a copy of the License at
  44102. *
  44103. * http://www.apache.org/licenses/LICENSE-2.0
  44104. *
  44105. * Unless required by applicable law or agreed to in writing,
  44106. * software distributed under the License is distributed on an
  44107. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44108. * KIND, either express or implied. See the License for the
  44109. * specific language governing permissions and limitations
  44110. * under the License.
  44111. */
  44112. MarkerModel.extend({
  44113. type: 'markArea',
  44114. defaultOption: {
  44115. zlevel: 0,
  44116. // PENDING
  44117. z: 1,
  44118. tooltip: {
  44119. trigger: 'item'
  44120. },
  44121. // markArea should fixed on the coordinate system
  44122. animation: false,
  44123. label: {
  44124. show: true,
  44125. position: 'top'
  44126. },
  44127. itemStyle: {
  44128. // color and borderColor default to use color from series
  44129. // color: 'auto'
  44130. // borderColor: 'auto'
  44131. borderWidth: 0
  44132. },
  44133. emphasis: {
  44134. label: {
  44135. show: true,
  44136. position: 'top'
  44137. }
  44138. }
  44139. }
  44140. });
  44141. /*
  44142. * Licensed to the Apache Software Foundation (ASF) under one
  44143. * or more contributor license agreements. See the NOTICE file
  44144. * distributed with this work for additional information
  44145. * regarding copyright ownership. The ASF licenses this file
  44146. * to you under the Apache License, Version 2.0 (the
  44147. * "License"); you may not use this file except in compliance
  44148. * with the License. You may obtain a copy of the License at
  44149. *
  44150. * http://www.apache.org/licenses/LICENSE-2.0
  44151. *
  44152. * Unless required by applicable law or agreed to in writing,
  44153. * software distributed under the License is distributed on an
  44154. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44155. * KIND, either express or implied. See the License for the
  44156. * specific language governing permissions and limitations
  44157. * under the License.
  44158. */
  44159. // TODO Better on polar
  44160. var markAreaTransform = function (seriesModel, coordSys, maModel, item) {
  44161. var lt = dataTransform(seriesModel, item[0]);
  44162. var rb = dataTransform(seriesModel, item[1]);
  44163. var retrieve$$1 = retrieve;
  44164. // FIXME make sure lt is less than rb
  44165. var ltCoord = lt.coord;
  44166. var rbCoord = rb.coord;
  44167. ltCoord[0] = retrieve$$1(ltCoord[0], -Infinity);
  44168. ltCoord[1] = retrieve$$1(ltCoord[1], -Infinity);
  44169. rbCoord[0] = retrieve$$1(rbCoord[0], Infinity);
  44170. rbCoord[1] = retrieve$$1(rbCoord[1], Infinity);
  44171. // Merge option into one
  44172. var result = mergeAll([{}, lt, rb]);
  44173. result.coord = [
  44174. lt.coord, rb.coord
  44175. ];
  44176. result.x0 = lt.x;
  44177. result.y0 = lt.y;
  44178. result.x1 = rb.x;
  44179. result.y1 = rb.y;
  44180. return result;
  44181. };
  44182. function isInifinity$1(val) {
  44183. return !isNaN(val) && !isFinite(val);
  44184. }
  44185. // If a markArea has one dim
  44186. function ifMarkLineHasOnlyDim$1(dimIndex, fromCoord, toCoord, coordSys) {
  44187. var otherDimIndex = 1 - dimIndex;
  44188. return isInifinity$1(fromCoord[otherDimIndex]) && isInifinity$1(toCoord[otherDimIndex]);
  44189. }
  44190. function markAreaFilter(coordSys, item) {
  44191. var fromCoord = item.coord[0];
  44192. var toCoord = item.coord[1];
  44193. if (coordSys.type === 'cartesian2d') {
  44194. // In case
  44195. // {
  44196. // markArea: {
  44197. // data: [{ yAxis: 2 }]
  44198. // }
  44199. // }
  44200. if (
  44201. fromCoord && toCoord
  44202. && (ifMarkLineHasOnlyDim$1(1, fromCoord, toCoord, coordSys)
  44203. || ifMarkLineHasOnlyDim$1(0, fromCoord, toCoord, coordSys))
  44204. ) {
  44205. return true;
  44206. }
  44207. }
  44208. return dataFilter$1(coordSys, {
  44209. coord: fromCoord,
  44210. x: item.x0,
  44211. y: item.y0
  44212. })
  44213. || dataFilter$1(coordSys, {
  44214. coord: toCoord,
  44215. x: item.x1,
  44216. y: item.y1
  44217. });
  44218. }
  44219. // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']
  44220. function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {
  44221. var coordSys = seriesModel.coordinateSystem;
  44222. var itemModel = data.getItemModel(idx);
  44223. var point;
  44224. var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth());
  44225. var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight());
  44226. if (!isNaN(xPx) && !isNaN(yPx)) {
  44227. point = [xPx, yPx];
  44228. }
  44229. else {
  44230. // Chart like bar may have there own marker positioning logic
  44231. if (seriesModel.getMarkerPosition) {
  44232. // Use the getMarkerPoisition
  44233. point = seriesModel.getMarkerPosition(
  44234. data.getValues(dims, idx)
  44235. );
  44236. }
  44237. else {
  44238. var x = data.get(dims[0], idx);
  44239. var y = data.get(dims[1], idx);
  44240. var pt = [x, y];
  44241. coordSys.clampData && coordSys.clampData(pt, pt);
  44242. point = coordSys.dataToPoint(pt, true);
  44243. }
  44244. if (coordSys.type === 'cartesian2d') {
  44245. var xAxis = coordSys.getAxis('x');
  44246. var yAxis = coordSys.getAxis('y');
  44247. var x = data.get(dims[0], idx);
  44248. var y = data.get(dims[1], idx);
  44249. if (isInifinity$1(x)) {
  44250. point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);
  44251. }
  44252. else if (isInifinity$1(y)) {
  44253. point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);
  44254. }
  44255. }
  44256. // Use x, y if has any
  44257. if (!isNaN(xPx)) {
  44258. point[0] = xPx;
  44259. }
  44260. if (!isNaN(yPx)) {
  44261. point[1] = yPx;
  44262. }
  44263. }
  44264. return point;
  44265. }
  44266. var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];
  44267. MarkerView.extend({
  44268. type: 'markArea',
  44269. // updateLayout: function (markAreaModel, ecModel, api) {
  44270. // ecModel.eachSeries(function (seriesModel) {
  44271. // var maModel = seriesModel.markAreaModel;
  44272. // if (maModel) {
  44273. // var areaData = maModel.getData();
  44274. // areaData.each(function (idx) {
  44275. // var points = zrUtil.map(dimPermutations, function (dim) {
  44276. // return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
  44277. // });
  44278. // // Layout
  44279. // areaData.setItemLayout(idx, points);
  44280. // var el = areaData.getItemGraphicEl(idx);
  44281. // el.setShape('points', points);
  44282. // });
  44283. // }
  44284. // }, this);
  44285. // },
  44286. updateTransform: function (markAreaModel, ecModel, api) {
  44287. ecModel.eachSeries(function (seriesModel) {
  44288. var maModel = seriesModel.markAreaModel;
  44289. if (maModel) {
  44290. var areaData = maModel.getData();
  44291. areaData.each(function (idx) {
  44292. var points = map(dimPermutations, function (dim) {
  44293. return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
  44294. });
  44295. // Layout
  44296. areaData.setItemLayout(idx, points);
  44297. var el = areaData.getItemGraphicEl(idx);
  44298. el.setShape('points', points);
  44299. });
  44300. }
  44301. }, this);
  44302. },
  44303. renderSeries: function (seriesModel, maModel, ecModel, api) {
  44304. var coordSys = seriesModel.coordinateSystem;
  44305. var seriesId = seriesModel.id;
  44306. var seriesData = seriesModel.getData();
  44307. var areaGroupMap = this.markerGroupMap;
  44308. var polygonGroup = areaGroupMap.get(seriesId)
  44309. || areaGroupMap.set(seriesId, {group: new Group()});
  44310. this.group.add(polygonGroup.group);
  44311. polygonGroup.__keep = true;
  44312. var areaData = createList$3(coordSys, seriesModel, maModel);
  44313. // Line data for tooltip and formatter
  44314. maModel.setData(areaData);
  44315. // Update visual and layout of line
  44316. areaData.each(function (idx) {
  44317. // Layout
  44318. areaData.setItemLayout(idx, map(dimPermutations, function (dim) {
  44319. return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
  44320. }));
  44321. // Visual
  44322. areaData.setItemVisual(idx, {
  44323. color: seriesData.getVisual('color')
  44324. });
  44325. });
  44326. areaData.diff(polygonGroup.__data)
  44327. .add(function (idx) {
  44328. var polygon = new Polygon({
  44329. shape: {
  44330. points: areaData.getItemLayout(idx)
  44331. }
  44332. });
  44333. areaData.setItemGraphicEl(idx, polygon);
  44334. polygonGroup.group.add(polygon);
  44335. })
  44336. .update(function (newIdx, oldIdx) {
  44337. var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx);
  44338. updateProps(polygon, {
  44339. shape: {
  44340. points: areaData.getItemLayout(newIdx)
  44341. }
  44342. }, maModel, newIdx);
  44343. polygonGroup.group.add(polygon);
  44344. areaData.setItemGraphicEl(newIdx, polygon);
  44345. })
  44346. .remove(function (idx) {
  44347. var polygon = polygonGroup.__data.getItemGraphicEl(idx);
  44348. polygonGroup.group.remove(polygon);
  44349. })
  44350. .execute();
  44351. areaData.eachItemGraphicEl(function (polygon, idx) {
  44352. var itemModel = areaData.getItemModel(idx);
  44353. var labelModel = itemModel.getModel('label');
  44354. var labelHoverModel = itemModel.getModel('emphasis.label');
  44355. var color = areaData.getItemVisual(idx, 'color');
  44356. polygon.useStyle(
  44357. defaults(
  44358. itemModel.getModel('itemStyle').getItemStyle(),
  44359. {
  44360. fill: modifyAlpha(color, 0.4),
  44361. stroke: color
  44362. }
  44363. )
  44364. );
  44365. polygon.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
  44366. setLabelStyle(
  44367. polygon.style, polygon.hoverStyle, labelModel, labelHoverModel,
  44368. {
  44369. labelFetcher: maModel,
  44370. labelDataIndex: idx,
  44371. defaultText: areaData.getName(idx) || '',
  44372. isRectText: true,
  44373. autoColor: color
  44374. }
  44375. );
  44376. setHoverStyle(polygon, {});
  44377. polygon.dataModel = maModel;
  44378. });
  44379. polygonGroup.__data = areaData;
  44380. polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');
  44381. }
  44382. });
  44383. /**
  44384. * @inner
  44385. * @param {module:echarts/coord/*} coordSys
  44386. * @param {module:echarts/model/Series} seriesModel
  44387. * @param {module:echarts/model/Model} mpModel
  44388. */
  44389. function createList$3(coordSys, seriesModel, maModel) {
  44390. var coordDimsInfos;
  44391. var areaData;
  44392. var dims = ['x0', 'y0', 'x1', 'y1'];
  44393. if (coordSys) {
  44394. coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
  44395. var data = seriesModel.getData();
  44396. var info = data.getDimensionInfo(
  44397. data.mapDimension(coordDim)
  44398. ) || {};
  44399. // In map series data don't have lng and lat dimension. Fallback to same with coordSys
  44400. return defaults({name: coordDim}, info);
  44401. });
  44402. areaData = new List(map(dims, function (dim, idx) {
  44403. return {
  44404. name: dim,
  44405. type: coordDimsInfos[idx % 2].type
  44406. };
  44407. }), maModel);
  44408. }
  44409. else {
  44410. coordDimsInfos = [{
  44411. name: 'value',
  44412. type: 'float'
  44413. }];
  44414. areaData = new List(coordDimsInfos, maModel);
  44415. }
  44416. var optData = map(maModel.get('data'), curry(
  44417. markAreaTransform, seriesModel, coordSys, maModel
  44418. ));
  44419. if (coordSys) {
  44420. optData = filter(
  44421. optData, curry(markAreaFilter, coordSys)
  44422. );
  44423. }
  44424. var dimValueGetter$$1 = coordSys ? function (item, dimName, dataIndex, dimIndex) {
  44425. return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];
  44426. } : function (item) {
  44427. return item.value;
  44428. };
  44429. areaData.initData(optData, null, dimValueGetter$$1);
  44430. areaData.hasItemOption = true;
  44431. return areaData;
  44432. }
  44433. /*
  44434. * Licensed to the Apache Software Foundation (ASF) under one
  44435. * or more contributor license agreements. See the NOTICE file
  44436. * distributed with this work for additional information
  44437. * regarding copyright ownership. The ASF licenses this file
  44438. * to you under the Apache License, Version 2.0 (the
  44439. * "License"); you may not use this file except in compliance
  44440. * with the License. You may obtain a copy of the License at
  44441. *
  44442. * http://www.apache.org/licenses/LICENSE-2.0
  44443. *
  44444. * Unless required by applicable law or agreed to in writing,
  44445. * software distributed under the License is distributed on an
  44446. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44447. * KIND, either express or implied. See the License for the
  44448. * specific language governing permissions and limitations
  44449. * under the License.
  44450. */
  44451. registerPreprocessor(function (opt) {
  44452. // Make sure markArea component is enabled
  44453. opt.markArea = opt.markArea || {};
  44454. });
  44455. /*
  44456. * Licensed to the Apache Software Foundation (ASF) under one
  44457. * or more contributor license agreements. See the NOTICE file
  44458. * distributed with this work for additional information
  44459. * regarding copyright ownership. The ASF licenses this file
  44460. * to you under the Apache License, Version 2.0 (the
  44461. * "License"); you may not use this file except in compliance
  44462. * with the License. You may obtain a copy of the License at
  44463. *
  44464. * http://www.apache.org/licenses/LICENSE-2.0
  44465. *
  44466. * Unless required by applicable law or agreed to in writing,
  44467. * software distributed under the License is distributed on an
  44468. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44469. * KIND, either express or implied. See the License for the
  44470. * specific language governing permissions and limitations
  44471. * under the License.
  44472. */
  44473. ComponentModel.registerSubTypeDefaulter('dataZoom', function () {
  44474. // Default 'slider' when no type specified.
  44475. return 'slider';
  44476. });
  44477. /*
  44478. * Licensed to the Apache Software Foundation (ASF) under one
  44479. * or more contributor license agreements. See the NOTICE file
  44480. * distributed with this work for additional information
  44481. * regarding copyright ownership. The ASF licenses this file
  44482. * to you under the Apache License, Version 2.0 (the
  44483. * "License"); you may not use this file except in compliance
  44484. * with the License. You may obtain a copy of the License at
  44485. *
  44486. * http://www.apache.org/licenses/LICENSE-2.0
  44487. *
  44488. * Unless required by applicable law or agreed to in writing,
  44489. * software distributed under the License is distributed on an
  44490. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44491. * KIND, either express or implied. See the License for the
  44492. * specific language governing permissions and limitations
  44493. * under the License.
  44494. */
  44495. var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle', 'single'];
  44496. // Supported coords.
  44497. var COORDS = ['cartesian2d', 'polar', 'singleAxis'];
  44498. /**
  44499. * @param {string} coordType
  44500. * @return {boolean}
  44501. */
  44502. function isCoordSupported(coordType) {
  44503. return indexOf(COORDS, coordType) >= 0;
  44504. }
  44505. /**
  44506. * Create "each" method to iterate names.
  44507. *
  44508. * @pubilc
  44509. * @param {Array.<string>} names
  44510. * @param {Array.<string>=} attrs
  44511. * @return {Function}
  44512. */
  44513. function createNameEach(names, attrs) {
  44514. names = names.slice();
  44515. var capitalNames = map(names, capitalFirst);
  44516. attrs = (attrs || []).slice();
  44517. var capitalAttrs = map(attrs, capitalFirst);
  44518. return function (callback, context) {
  44519. each$1(names, function (name, index) {
  44520. var nameObj = {name: name, capital: capitalNames[index]};
  44521. for (var j = 0; j < attrs.length; j++) {
  44522. nameObj[attrs[j]] = name + capitalAttrs[j];
  44523. }
  44524. callback.call(context, nameObj);
  44525. });
  44526. };
  44527. }
  44528. /**
  44529. * Iterate each dimension name.
  44530. *
  44531. * @public
  44532. * @param {Function} callback The parameter is like:
  44533. * {
  44534. * name: 'angle',
  44535. * capital: 'Angle',
  44536. * axis: 'angleAxis',
  44537. * axisIndex: 'angleAixs',
  44538. * index: 'angleIndex'
  44539. * }
  44540. * @param {Object} context
  44541. */
  44542. var eachAxisDim$1 = createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']);
  44543. /**
  44544. * If tow dataZoomModels has the same axis controlled, we say that they are 'linked'.
  44545. * dataZoomModels and 'links' make up one or more graphics.
  44546. * This function finds the graphic where the source dataZoomModel is in.
  44547. *
  44548. * @public
  44549. * @param {Function} forEachNode Node iterator.
  44550. * @param {Function} forEachEdgeType edgeType iterator
  44551. * @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id.
  44552. * @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}}
  44553. */
  44554. function createLinkedNodesFinder(forEachNode, forEachEdgeType, edgeIdGetter) {
  44555. return function (sourceNode) {
  44556. var result = {
  44557. nodes: [],
  44558. records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean).
  44559. };
  44560. forEachEdgeType(function (edgeType) {
  44561. result.records[edgeType.name] = {};
  44562. });
  44563. if (!sourceNode) {
  44564. return result;
  44565. }
  44566. absorb(sourceNode, result);
  44567. var existsLink;
  44568. do {
  44569. existsLink = false;
  44570. forEachNode(processSingleNode);
  44571. }
  44572. while (existsLink);
  44573. function processSingleNode(node) {
  44574. if (!isNodeAbsorded(node, result) && isLinked(node, result)) {
  44575. absorb(node, result);
  44576. existsLink = true;
  44577. }
  44578. }
  44579. return result;
  44580. };
  44581. function isNodeAbsorded(node, result) {
  44582. return indexOf(result.nodes, node) >= 0;
  44583. }
  44584. function isLinked(node, result) {
  44585. var hasLink = false;
  44586. forEachEdgeType(function (edgeType) {
  44587. each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) {
  44588. result.records[edgeType.name][edgeId] && (hasLink = true);
  44589. });
  44590. });
  44591. return hasLink;
  44592. }
  44593. function absorb(node, result) {
  44594. result.nodes.push(node);
  44595. forEachEdgeType(function (edgeType) {
  44596. each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) {
  44597. result.records[edgeType.name][edgeId] = true;
  44598. });
  44599. });
  44600. }
  44601. }
  44602. /*
  44603. * Licensed to the Apache Software Foundation (ASF) under one
  44604. * or more contributor license agreements. See the NOTICE file
  44605. * distributed with this work for additional information
  44606. * regarding copyright ownership. The ASF licenses this file
  44607. * to you under the Apache License, Version 2.0 (the
  44608. * "License"); you may not use this file except in compliance
  44609. * with the License. You may obtain a copy of the License at
  44610. *
  44611. * http://www.apache.org/licenses/LICENSE-2.0
  44612. *
  44613. * Unless required by applicable law or agreed to in writing,
  44614. * software distributed under the License is distributed on an
  44615. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  44616. * KIND, either express or implied. See the License for the
  44617. * specific language governing permissions and limitations
  44618. * under the License.
  44619. */
  44620. var each$13 = each$1;
  44621. var asc$1 = asc;
  44622. /**
  44623. * Operate single axis.
  44624. * One axis can only operated by one axis operator.
  44625. * Different dataZoomModels may be defined to operate the same axis.
  44626. * (i.e. 'inside' data zoom and 'slider' data zoom components)
  44627. * So dataZoomModels share one axisProxy in that case.
  44628. *
  44629. * @class
  44630. */
  44631. var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) {
  44632. /**
  44633. * @private
  44634. * @type {string}
  44635. */
  44636. this._dimName = dimName;
  44637. /**
  44638. * @private
  44639. */
  44640. this._axisIndex = axisIndex;
  44641. /**
  44642. * @private
  44643. * @type {Array.<number>}
  44644. */
  44645. this._valueWindow;
  44646. /**
  44647. * @private
  44648. * @type {Array.<number>}
  44649. */
  44650. this._percentWindow;
  44651. /**
  44652. * @private
  44653. * @type {Array.<number>}
  44654. */
  44655. this._dataExtent;
  44656. /**
  44657. * {minSpan, maxSpan, minValueSpan, maxValueSpan}
  44658. * @private
  44659. * @type {Object}
  44660. */
  44661. this._minMaxSpan;
  44662. /**
  44663. * @readOnly
  44664. * @type {module: echarts/model/Global}
  44665. */
  44666. this.ecModel = ecModel;
  44667. /**
  44668. * @private
  44669. * @type {module: echarts/component/dataZoom/DataZoomModel}
  44670. */
  44671. this._dataZoomModel = dataZoomModel;
  44672. // /**
  44673. // * @readOnly
  44674. // * @private
  44675. // */
  44676. // this.hasSeriesStacked;
  44677. };
  44678. AxisProxy.prototype = {
  44679. constructor: AxisProxy,
  44680. /**
  44681. * Whether the axisProxy is hosted by dataZoomModel.
  44682. *
  44683. * @public
  44684. * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
  44685. * @return {boolean}
  44686. */
  44687. hostedBy: function (dataZoomModel) {
  44688. return this._dataZoomModel === dataZoomModel;
  44689. },
  44690. /**
  44691. * @return {Array.<number>} Value can only be NaN or finite value.
  44692. */
  44693. getDataValueWindow: function () {
  44694. return this._valueWindow.slice();
  44695. },
  44696. /**
  44697. * @return {Array.<number>}
  44698. */
  44699. getDataPercentWindow: function () {
  44700. return this._percentWindow.slice();
  44701. },
  44702. /**
  44703. * @public
  44704. * @param {number} axisIndex
  44705. * @return {Array} seriesModels
  44706. */
  44707. getTargetSeriesModels: function () {
  44708. var seriesModels = [];
  44709. var ecModel = this.ecModel;
  44710. ecModel.eachSeries(function (seriesModel) {
  44711. if (isCoordSupported(seriesModel.get('coordinateSystem'))) {
  44712. var dimName = this._dimName;
  44713. var axisModel = ecModel.queryComponents({
  44714. mainType: dimName + 'Axis',
  44715. index: seriesModel.get(dimName + 'AxisIndex'),
  44716. id: seriesModel.get(dimName + 'AxisId')
  44717. })[0];
  44718. if (this._axisIndex === (axisModel && axisModel.componentIndex)) {
  44719. seriesModels.push(seriesModel);
  44720. }
  44721. }
  44722. }, this);
  44723. return seriesModels;
  44724. },
  44725. getAxisModel: function () {
  44726. return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);
  44727. },
  44728. getOtherAxisModel: function () {
  44729. var axisDim = this._dimName;
  44730. var ecModel = this.ecModel;
  44731. var axisModel = this.getAxisModel();
  44732. var isCartesian = axisDim === 'x' || axisDim === 'y';
  44733. var otherAxisDim;
  44734. var coordSysIndexName;
  44735. if (isCartesian) {
  44736. coordSysIndexName = 'gridIndex';
  44737. otherAxisDim = axisDim === 'x' ? 'y' : 'x';
  44738. }
  44739. else {
  44740. coordSysIndexName = 'polarIndex';
  44741. otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle';
  44742. }
  44743. var foundOtherAxisModel;
  44744. ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) {
  44745. if ((otherAxisModel.get(coordSysIndexName) || 0)
  44746. === (axisModel.get(coordSysIndexName) || 0)
  44747. ) {
  44748. foundOtherAxisModel = otherAxisModel;
  44749. }
  44750. });
  44751. return foundOtherAxisModel;
  44752. },
  44753. getMinMaxSpan: function () {
  44754. return clone(this._minMaxSpan);
  44755. },
  44756. /**
  44757. * Only calculate by given range and this._dataExtent, do not change anything.
  44758. *
  44759. * @param {Object} opt
  44760. * @param {number} [opt.start]
  44761. * @param {number} [opt.end]
  44762. * @param {number} [opt.startValue]
  44763. * @param {number} [opt.endValue]
  44764. */
  44765. calculateDataWindow: function (opt) {
  44766. var dataExtent = this._dataExtent;
  44767. var axisModel = this.getAxisModel();
  44768. var scale = axisModel.axis.scale;
  44769. var rangePropMode = this._dataZoomModel.getRangePropMode();
  44770. var percentExtent = [0, 100];
  44771. var percentWindow = [
  44772. opt.start,
  44773. opt.end
  44774. ];
  44775. var valueWindow = [];
  44776. each$13(['startValue', 'endValue'], function (prop) {
  44777. valueWindow.push(opt[prop] != null ? scale.parse(opt[prop]) : null);
  44778. });
  44779. // Normalize bound.
  44780. each$13([0, 1], function (idx) {
  44781. var boundValue = valueWindow[idx];
  44782. var boundPercent = percentWindow[idx];
  44783. // Notice: dataZoom is based either on `percentProp` ('start', 'end') or
  44784. // on `valueProp` ('startValue', 'endValue'). The former one is suitable
  44785. // for cases that a dataZoom component controls multiple axes with different
  44786. // unit or extent, and the latter one is suitable for accurate zoom by pixel
  44787. // (e.g., in dataZoomSelect). `valueProp` can be calculated from `percentProp`,
  44788. // but it is awkward that `percentProp` can not be obtained from `valueProp`
  44789. // accurately (because all of values that are overflow the `dataExtent` will
  44790. // be calculated to percent '100%'). So we have to use
  44791. // `dataZoom.getRangePropMode()` to mark which prop is used.
  44792. // `rangePropMode` is updated only when setOption or dispatchAction, otherwise
  44793. // it remains its original value.
  44794. if (rangePropMode[idx] === 'percent') {
  44795. if (boundPercent == null) {
  44796. boundPercent = percentExtent[idx];
  44797. }
  44798. // Use scale.parse to math round for category or time axis.
  44799. boundValue = scale.parse(linearMap(
  44800. boundPercent, percentExtent, dataExtent, true
  44801. ));
  44802. }
  44803. else {
  44804. // Calculating `percent` from `value` may be not accurate, because
  44805. // This calculation can not be inversed, because all of values that
  44806. // are overflow the `dataExtent` will be calculated to percent '100%'
  44807. boundPercent = linearMap(
  44808. boundValue, dataExtent, percentExtent, true
  44809. );
  44810. }
  44811. // valueWindow[idx] = round(boundValue);
  44812. // percentWindow[idx] = round(boundPercent);
  44813. valueWindow[idx] = boundValue;
  44814. percentWindow[idx] = boundPercent;
  44815. });
  44816. return {
  44817. valueWindow: asc$1(valueWindow),
  44818. percentWindow: asc$1(percentWindow)
  44819. };
  44820. },
  44821. /**
  44822. * Notice: reset should not be called before series.restoreData() called,
  44823. * so it is recommanded to be called in "process stage" but not "model init
  44824. * stage".
  44825. *
  44826. * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
  44827. */
  44828. reset: function (dataZoomModel) {
  44829. if (dataZoomModel !== this._dataZoomModel) {
  44830. return;
  44831. }
  44832. var targetSeries = this.getTargetSeriesModels();
  44833. // Culculate data window and data extent, and record them.
  44834. this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries);
  44835. // this.hasSeriesStacked = false;
  44836. // each(targetSeries, function (series) {
  44837. // var data = series.getData();
  44838. // var dataDim = data.mapDimension(this._dimName);
  44839. // var stackedDimension = data.getCalculationInfo('stackedDimension');
  44840. // if (stackedDimension && stackedDimension === dataDim) {
  44841. // this.hasSeriesStacked = true;
  44842. // }
  44843. // }, this);
  44844. var dataWindow = this.calculateDataWindow(dataZoomModel.option);
  44845. this._valueWindow = dataWindow.valueWindow;
  44846. this._percentWindow = dataWindow.percentWindow;
  44847. setMinMaxSpan(this);
  44848. // Update axis setting then.
  44849. setAxisModel(this);
  44850. },
  44851. /**
  44852. * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
  44853. */
  44854. restore: function (dataZoomModel) {
  44855. if (dataZoomModel !== this._dataZoomModel) {
  44856. return;
  44857. }
  44858. this._valueWindow = this._percentWindow = null;
  44859. setAxisModel(this, true);
  44860. },
  44861. /**
  44862. * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
  44863. */
  44864. filterData: function (dataZoomModel, api) {
  44865. if (dataZoomModel !== this._dataZoomModel) {
  44866. return;
  44867. }
  44868. var axisDim = this._dimName;
  44869. var seriesModels = this.getTargetSeriesModels();
  44870. var filterMode = dataZoomModel.get('filterMode');
  44871. var valueWindow = this._valueWindow;
  44872. if (filterMode === 'none') {
  44873. return;
  44874. }
  44875. // FIXME
  44876. // Toolbox may has dataZoom injected. And if there are stacked bar chart
  44877. // with NaN data, NaN will be filtered and stack will be wrong.
  44878. // So we need to force the mode to be set empty.
  44879. // In fect, it is not a big deal that do not support filterMode-'filter'
  44880. // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis
  44881. // selection" some day, which might need "adapt to data extent on the
  44882. // otherAxis", which is disabled by filterMode-'empty'.
  44883. // But currently, stack has been fixed to based on value but not index,
  44884. // so this is not an issue any more.
  44885. // var otherAxisModel = this.getOtherAxisModel();
  44886. // if (dataZoomModel.get('$fromToolbox')
  44887. // && otherAxisModel
  44888. // && otherAxisModel.hasSeriesStacked
  44889. // ) {
  44890. // filterMode = 'empty';
  44891. // }
  44892. // TODO
  44893. // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.
  44894. each$13(seriesModels, function (seriesModel) {
  44895. var seriesData = seriesModel.getData();
  44896. var dataDims = seriesData.mapDimension(axisDim, true);
  44897. if (!dataDims.length) {
  44898. return;
  44899. }
  44900. if (filterMode === 'weakFilter') {
  44901. seriesData.filterSelf(function (dataIndex) {
  44902. var leftOut;
  44903. var rightOut;
  44904. var hasValue;
  44905. for (var i = 0; i < dataDims.length; i++) {
  44906. var value = seriesData.get(dataDims[i], dataIndex);
  44907. var thisHasValue = !isNaN(value);
  44908. var thisLeftOut = value < valueWindow[0];
  44909. var thisRightOut = value > valueWindow[1];
  44910. if (thisHasValue && !thisLeftOut && !thisRightOut) {
  44911. return true;
  44912. }
  44913. thisHasValue && (hasValue = true);
  44914. thisLeftOut && (leftOut = true);
  44915. thisRightOut && (rightOut = true);
  44916. }
  44917. // If both left out and right out, do not filter.
  44918. return hasValue && leftOut && rightOut;
  44919. });
  44920. }
  44921. else {
  44922. each$13(dataDims, function (dim) {
  44923. if (filterMode === 'empty') {
  44924. seriesModel.setData(
  44925. seriesData.map(dim, function (value) {
  44926. return !isInWindow(value) ? NaN : value;
  44927. })
  44928. );
  44929. }
  44930. else {
  44931. var range = {};
  44932. range[dim] = valueWindow;
  44933. // console.time('select');
  44934. seriesData.selectRange(range);
  44935. // console.timeEnd('select');
  44936. }
  44937. });
  44938. }
  44939. each$13(dataDims, function (dim) {
  44940. seriesData.setApproximateExtent(valueWindow, dim);
  44941. });
  44942. });
  44943. function isInWindow(value) {
  44944. return value >= valueWindow[0] && value <= valueWindow[1];
  44945. }
  44946. }
  44947. };
  44948. function calculateDataExtent(axisProxy, axisDim, seriesModels) {
  44949. var dataExtent = [Infinity, -Infinity];
  44950. each$13(seriesModels, function (seriesModel) {
  44951. var seriesData = seriesModel.getData();
  44952. if (seriesData) {
  44953. each$13(seriesData.mapDimension(axisDim, true), function (dim) {
  44954. var seriesExtent = seriesData.getApproximateExtent(dim);
  44955. seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);
  44956. seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);
  44957. });
  44958. }
  44959. });
  44960. if (dataExtent[1] < dataExtent[0]) {
  44961. dataExtent = [NaN, NaN];
  44962. }
  44963. // It is important to get "consistent" extent when more then one axes is
  44964. // controlled by a `dataZoom`, otherwise those axes will not be synchronized
  44965. // when zooming. But it is difficult to know what is "consistent", considering
  44966. // axes have different type or even different meanings (For example, two
  44967. // time axes are used to compare data of the same date in different years).
  44968. // So basically dataZoom just obtains extent by series.data (in category axis
  44969. // extent can be obtained from axis.data).
  44970. // Nevertheless, user can set min/max/scale on axes to make extent of axes
  44971. // consistent.
  44972. fixExtentByAxis(axisProxy, dataExtent);
  44973. return dataExtent;
  44974. }
  44975. function fixExtentByAxis(axisProxy, dataExtent) {
  44976. var axisModel = axisProxy.getAxisModel();
  44977. var min = axisModel.getMin(true);
  44978. // For category axis, if min/max/scale are not set, extent is determined
  44979. // by axis.data by default.
  44980. var isCategoryAxis = axisModel.get('type') === 'category';
  44981. var axisDataLen = isCategoryAxis && axisModel.getCategories().length;
  44982. if (min != null && min !== 'dataMin' && typeof min !== 'function') {
  44983. dataExtent[0] = min;
  44984. }
  44985. else if (isCategoryAxis) {
  44986. dataExtent[0] = axisDataLen > 0 ? 0 : NaN;
  44987. }
  44988. var max = axisModel.getMax(true);
  44989. if (max != null && max !== 'dataMax' && typeof max !== 'function') {
  44990. dataExtent[1] = max;
  44991. }
  44992. else if (isCategoryAxis) {
  44993. dataExtent[1] = axisDataLen > 0 ? axisDataLen - 1 : NaN;
  44994. }
  44995. if (!axisModel.get('scale', true)) {
  44996. dataExtent[0] > 0 && (dataExtent[0] = 0);
  44997. dataExtent[1] < 0 && (dataExtent[1] = 0);
  44998. }
  44999. // For value axis, if min/max/scale are not set, we just use the extent obtained
  45000. // by series data, which may be a little different from the extent calculated by
  45001. // `axisHelper.getScaleExtent`. But the different just affects the experience a
  45002. // little when zooming. So it will not be fixed until some users require it strongly.
  45003. return dataExtent;
  45004. }
  45005. function setAxisModel(axisProxy, isRestore) {
  45006. var axisModel = axisProxy.getAxisModel();
  45007. var percentWindow = axisProxy._percentWindow;
  45008. var valueWindow = axisProxy._valueWindow;
  45009. if (!percentWindow) {
  45010. return;
  45011. }
  45012. // [0, 500]: arbitrary value, guess axis extent.
  45013. var precision = getPixelPrecision(valueWindow, [0, 500]);
  45014. precision = Math.min(precision, 20);
  45015. // isRestore or isFull
  45016. var useOrigin = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100);
  45017. axisModel.setRange(
  45018. useOrigin ? null : +valueWindow[0].toFixed(precision),
  45019. useOrigin ? null : +valueWindow[1].toFixed(precision)
  45020. );
  45021. }
  45022. function setMinMaxSpan(axisProxy) {
  45023. var minMaxSpan = axisProxy._minMaxSpan = {};
  45024. var dataZoomModel = axisProxy._dataZoomModel;
  45025. each$13(['min', 'max'], function (minMax) {
  45026. minMaxSpan[minMax + 'Span'] = dataZoomModel.get(minMax + 'Span');
  45027. // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan
  45028. var valueSpan = dataZoomModel.get(minMax + 'ValueSpan');
  45029. if (valueSpan != null) {
  45030. minMaxSpan[minMax + 'ValueSpan'] = valueSpan;
  45031. valueSpan = axisProxy.getAxisModel().axis.scale.parse(valueSpan);
  45032. if (valueSpan != null) {
  45033. var dataExtent = axisProxy._dataExtent;
  45034. minMaxSpan[minMax + 'Span'] = linearMap(
  45035. dataExtent[0] + valueSpan, dataExtent, [0, 100], true
  45036. );
  45037. }
  45038. }
  45039. });
  45040. }
  45041. /*
  45042. * Licensed to the Apache Software Foundation (ASF) under one
  45043. * or more contributor license agreements. See the NOTICE file
  45044. * distributed with this work for additional information
  45045. * regarding copyright ownership. The ASF licenses this file
  45046. * to you under the Apache License, Version 2.0 (the
  45047. * "License"); you may not use this file except in compliance
  45048. * with the License. You may obtain a copy of the License at
  45049. *
  45050. * http://www.apache.org/licenses/LICENSE-2.0
  45051. *
  45052. * Unless required by applicable law or agreed to in writing,
  45053. * software distributed under the License is distributed on an
  45054. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45055. * KIND, either express or implied. See the License for the
  45056. * specific language governing permissions and limitations
  45057. * under the License.
  45058. */
  45059. var each$12 = each$1;
  45060. var eachAxisDim = eachAxisDim$1;
  45061. var DataZoomModel = extendComponentModel({
  45062. type: 'dataZoom',
  45063. dependencies: [
  45064. 'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'
  45065. ],
  45066. /**
  45067. * @protected
  45068. */
  45069. defaultOption: {
  45070. zlevel: 0,
  45071. z: 4, // Higher than normal component (z: 2).
  45072. orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
  45073. xAxisIndex: null, // Default the first horizontal category axis.
  45074. yAxisIndex: null, // Default the first vertical category axis.
  45075. filterMode: 'filter', // Possible values: 'filter' or 'empty' or 'weakFilter'.
  45076. // 'filter': data items which are out of window will be removed. This option is
  45077. // applicable when filtering outliers. For each data item, it will be
  45078. // filtered if one of the relevant dimensions is out of the window.
  45079. // 'weakFilter': data items which are out of window will be removed. This option
  45080. // is applicable when filtering outliers. For each data item, it will be
  45081. // filtered only if all of the relevant dimensions are out of the same
  45082. // side of the window.
  45083. // 'empty': data items which are out of window will be set to empty.
  45084. // This option is applicable when user should not neglect
  45085. // that there are some data items out of window.
  45086. // 'none': Do not filter.
  45087. // Taking line chart as an example, line will be broken in
  45088. // the filtered points when filterModel is set to 'empty', but
  45089. // be connected when set to 'filter'.
  45090. throttle: null, // Dispatch action by the fixed rate, avoid frequency.
  45091. // default 100. Do not throttle when use null/undefined.
  45092. // If animation === true and animationDurationUpdate > 0,
  45093. // default value is 100, otherwise 20.
  45094. start: 0, // Start percent. 0 ~ 100
  45095. end: 100, // End percent. 0 ~ 100
  45096. startValue: null, // Start value. If startValue specified, start is ignored.
  45097. endValue: null, // End value. If endValue specified, end is ignored.
  45098. minSpan: null, // 0 ~ 100
  45099. maxSpan: null, // 0 ~ 100
  45100. minValueSpan: null, // The range of dataZoom can not be smaller than that.
  45101. maxValueSpan: null, // The range of dataZoom can not be larger than that.
  45102. rangeMode: null // Array, can be 'value' or 'percent'.
  45103. },
  45104. /**
  45105. * @override
  45106. */
  45107. init: function (option, parentModel, ecModel) {
  45108. /**
  45109. * key like x_0, y_1
  45110. * @private
  45111. * @type {Object}
  45112. */
  45113. this._dataIntervalByAxis = {};
  45114. /**
  45115. * @private
  45116. */
  45117. this._dataInfo = {};
  45118. /**
  45119. * key like x_0, y_1
  45120. * @private
  45121. */
  45122. this._axisProxies = {};
  45123. /**
  45124. * @readOnly
  45125. */
  45126. this.textStyleModel;
  45127. /**
  45128. * @private
  45129. */
  45130. this._autoThrottle = true;
  45131. /**
  45132. * 'percent' or 'value'
  45133. * @private
  45134. */
  45135. this._rangePropMode = ['percent', 'percent'];
  45136. var rawOption = retrieveRaw(option);
  45137. this.mergeDefaultAndTheme(option, ecModel);
  45138. this.doInit(rawOption);
  45139. },
  45140. /**
  45141. * @override
  45142. */
  45143. mergeOption: function (newOption) {
  45144. var rawOption = retrieveRaw(newOption);
  45145. //FIX #2591
  45146. merge(this.option, newOption, true);
  45147. this.doInit(rawOption);
  45148. },
  45149. /**
  45150. * @protected
  45151. */
  45152. doInit: function (rawOption) {
  45153. var thisOption = this.option;
  45154. // Disable realtime view update if canvas is not supported.
  45155. if (!env$1.canvasSupported) {
  45156. thisOption.realtime = false;
  45157. }
  45158. this._setDefaultThrottle(rawOption);
  45159. updateRangeUse(this, rawOption);
  45160. each$12([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
  45161. // start/end has higher priority over startValue/endValue if they
  45162. // both set, but we should make chart.setOption({endValue: 1000})
  45163. // effective, rather than chart.setOption({endValue: 1000, end: null}).
  45164. if (this._rangePropMode[index] === 'value') {
  45165. thisOption[names[0]] = null;
  45166. }
  45167. // Otherwise do nothing and use the merge result.
  45168. }, this);
  45169. this.textStyleModel = this.getModel('textStyle');
  45170. this._resetTarget();
  45171. this._giveAxisProxies();
  45172. },
  45173. /**
  45174. * @private
  45175. */
  45176. _giveAxisProxies: function () {
  45177. var axisProxies = this._axisProxies;
  45178. this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
  45179. var axisModel = this.dependentModels[dimNames.axis][axisIndex];
  45180. // If exists, share axisProxy with other dataZoomModels.
  45181. var axisProxy = axisModel.__dzAxisProxy || (
  45182. // Use the first dataZoomModel as the main model of axisProxy.
  45183. axisModel.__dzAxisProxy = new AxisProxy(
  45184. dimNames.name, axisIndex, this, ecModel
  45185. )
  45186. );
  45187. // FIXME
  45188. // dispose __dzAxisProxy
  45189. axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
  45190. }, this);
  45191. },
  45192. /**
  45193. * @private
  45194. */
  45195. _resetTarget: function () {
  45196. var thisOption = this.option;
  45197. var autoMode = this._judgeAutoMode();
  45198. eachAxisDim(function (dimNames) {
  45199. var axisIndexName = dimNames.axisIndex;
  45200. thisOption[axisIndexName] = normalizeToArray(
  45201. thisOption[axisIndexName]
  45202. );
  45203. }, this);
  45204. if (autoMode === 'axisIndex') {
  45205. this._autoSetAxisIndex();
  45206. }
  45207. else if (autoMode === 'orient') {
  45208. this._autoSetOrient();
  45209. }
  45210. },
  45211. /**
  45212. * @private
  45213. */
  45214. _judgeAutoMode: function () {
  45215. // Auto set only works for setOption at the first time.
  45216. // The following is user's reponsibility. So using merged
  45217. // option is OK.
  45218. var thisOption = this.option;
  45219. var hasIndexSpecified = false;
  45220. eachAxisDim(function (dimNames) {
  45221. // When user set axisIndex as a empty array, we think that user specify axisIndex
  45222. // but do not want use auto mode. Because empty array may be encountered when
  45223. // some error occured.
  45224. if (thisOption[dimNames.axisIndex] != null) {
  45225. hasIndexSpecified = true;
  45226. }
  45227. }, this);
  45228. var orient = thisOption.orient;
  45229. if (orient == null && hasIndexSpecified) {
  45230. return 'orient';
  45231. }
  45232. else if (!hasIndexSpecified) {
  45233. if (orient == null) {
  45234. thisOption.orient = 'horizontal';
  45235. }
  45236. return 'axisIndex';
  45237. }
  45238. },
  45239. /**
  45240. * @private
  45241. */
  45242. _autoSetAxisIndex: function () {
  45243. var autoAxisIndex = true;
  45244. var orient = this.get('orient', true);
  45245. var thisOption = this.option;
  45246. var dependentModels = this.dependentModels;
  45247. if (autoAxisIndex) {
  45248. // Find axis that parallel to dataZoom as default.
  45249. var dimName = orient === 'vertical' ? 'y' : 'x';
  45250. if (dependentModels[dimName + 'Axis'].length) {
  45251. thisOption[dimName + 'AxisIndex'] = [0];
  45252. autoAxisIndex = false;
  45253. }
  45254. else {
  45255. each$12(dependentModels.singleAxis, function (singleAxisModel) {
  45256. if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
  45257. thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
  45258. autoAxisIndex = false;
  45259. }
  45260. });
  45261. }
  45262. }
  45263. if (autoAxisIndex) {
  45264. // Find the first category axis as default. (consider polar)
  45265. eachAxisDim(function (dimNames) {
  45266. if (!autoAxisIndex) {
  45267. return;
  45268. }
  45269. var axisIndices = [];
  45270. var axisModels = this.dependentModels[dimNames.axis];
  45271. if (axisModels.length && !axisIndices.length) {
  45272. for (var i = 0, len = axisModels.length; i < len; i++) {
  45273. if (axisModels[i].get('type') === 'category') {
  45274. axisIndices.push(i);
  45275. }
  45276. }
  45277. }
  45278. thisOption[dimNames.axisIndex] = axisIndices;
  45279. if (axisIndices.length) {
  45280. autoAxisIndex = false;
  45281. }
  45282. }, this);
  45283. }
  45284. if (autoAxisIndex) {
  45285. // FIXME
  45286. // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
  45287. // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?
  45288. // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
  45289. // dataZoom component auto adopts series that reference to
  45290. // both xAxis and yAxis which type is 'value'.
  45291. this.ecModel.eachSeries(function (seriesModel) {
  45292. if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
  45293. eachAxisDim(function (dimNames) {
  45294. var axisIndices = thisOption[dimNames.axisIndex];
  45295. var axisIndex = seriesModel.get(dimNames.axisIndex);
  45296. var axisId = seriesModel.get(dimNames.axisId);
  45297. var axisModel = seriesModel.ecModel.queryComponents({
  45298. mainType: dimNames.axis,
  45299. index: axisIndex,
  45300. id: axisId
  45301. })[0];
  45302. if (__DEV__) {
  45303. if (!axisModel) {
  45304. throw new Error(
  45305. dimNames.axis + ' "' + retrieve(
  45306. axisIndex,
  45307. axisId,
  45308. 0
  45309. ) + '" not found'
  45310. );
  45311. }
  45312. }
  45313. axisIndex = axisModel.componentIndex;
  45314. if (indexOf(axisIndices, axisIndex) < 0) {
  45315. axisIndices.push(axisIndex);
  45316. }
  45317. });
  45318. }
  45319. }, this);
  45320. }
  45321. },
  45322. /**
  45323. * @private
  45324. */
  45325. _autoSetOrient: function () {
  45326. var dim;
  45327. // Find the first axis
  45328. this.eachTargetAxis(function (dimNames) {
  45329. !dim && (dim = dimNames.name);
  45330. }, this);
  45331. this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
  45332. },
  45333. /**
  45334. * @private
  45335. */
  45336. _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) {
  45337. // FIXME
  45338. // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
  45339. // 例如series.type === scatter时。
  45340. var is = true;
  45341. eachAxisDim(function (dimNames) {
  45342. var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
  45343. var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];
  45344. if (!axisModel || axisModel.get('type') !== axisType) {
  45345. is = false;
  45346. }
  45347. }, this);
  45348. return is;
  45349. },
  45350. /**
  45351. * @private
  45352. */
  45353. _setDefaultThrottle: function (rawOption) {
  45354. // When first time user set throttle, auto throttle ends.
  45355. if (rawOption.hasOwnProperty('throttle')) {
  45356. this._autoThrottle = false;
  45357. }
  45358. if (this._autoThrottle) {
  45359. var globalOption = this.ecModel.option;
  45360. this.option.throttle
  45361. = (globalOption.animation && globalOption.animationDurationUpdate > 0)
  45362. ? 100 : 20;
  45363. }
  45364. },
  45365. /**
  45366. * @public
  45367. */
  45368. getFirstTargetAxisModel: function () {
  45369. var firstAxisModel;
  45370. eachAxisDim(function (dimNames) {
  45371. if (firstAxisModel == null) {
  45372. var indices = this.get(dimNames.axisIndex);
  45373. if (indices.length) {
  45374. firstAxisModel = this.dependentModels[dimNames.axis][indices[0]];
  45375. }
  45376. }
  45377. }, this);
  45378. return firstAxisModel;
  45379. },
  45380. /**
  45381. * @public
  45382. * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
  45383. */
  45384. eachTargetAxis: function (callback, context) {
  45385. var ecModel = this.ecModel;
  45386. eachAxisDim(function (dimNames) {
  45387. each$12(
  45388. this.get(dimNames.axisIndex),
  45389. function (axisIndex) {
  45390. callback.call(context, dimNames, axisIndex, this, ecModel);
  45391. },
  45392. this
  45393. );
  45394. }, this);
  45395. },
  45396. /**
  45397. * @param {string} dimName
  45398. * @param {number} axisIndex
  45399. * @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined.
  45400. */
  45401. getAxisProxy: function (dimName, axisIndex) {
  45402. return this._axisProxies[dimName + '_' + axisIndex];
  45403. },
  45404. /**
  45405. * @param {string} dimName
  45406. * @param {number} axisIndex
  45407. * @return {module:echarts/model/Model} If not found, return null/undefined.
  45408. */
  45409. getAxisModel: function (dimName, axisIndex) {
  45410. var axisProxy = this.getAxisProxy(dimName, axisIndex);
  45411. return axisProxy && axisProxy.getAxisModel();
  45412. },
  45413. /**
  45414. * If not specified, set to undefined.
  45415. *
  45416. * @public
  45417. * @param {Object} opt
  45418. * @param {number} [opt.start]
  45419. * @param {number} [opt.end]
  45420. * @param {number} [opt.startValue]
  45421. * @param {number} [opt.endValue]
  45422. * @param {boolean} [ignoreUpdateRangeUsg=false]
  45423. */
  45424. setRawRange: function (opt, ignoreUpdateRangeUsg) {
  45425. var option = this.option;
  45426. each$12([['start', 'startValue'], ['end', 'endValue']], function (names) {
  45427. // If only one of 'start' and 'startValue' is not null/undefined, the other
  45428. // should be cleared, which enable clear the option.
  45429. // If both of them are not set, keep option with the original value, which
  45430. // enable use only set start but not set end when calling `dispatchAction`.
  45431. // The same as 'end' and 'endValue'.
  45432. if (opt[names[0]] != null || opt[names[1]] != null) {
  45433. option[names[0]] = opt[names[0]];
  45434. option[names[1]] = opt[names[1]];
  45435. }
  45436. }, this);
  45437. !ignoreUpdateRangeUsg && updateRangeUse(this, opt);
  45438. },
  45439. /**
  45440. * @public
  45441. * @return {Array.<number>} [startPercent, endPercent]
  45442. */
  45443. getPercentRange: function () {
  45444. var axisProxy = this.findRepresentativeAxisProxy();
  45445. if (axisProxy) {
  45446. return axisProxy.getDataPercentWindow();
  45447. }
  45448. },
  45449. /**
  45450. * @public
  45451. * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
  45452. *
  45453. * @param {string} [axisDimName]
  45454. * @param {number} [axisIndex]
  45455. * @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number.
  45456. */
  45457. getValueRange: function (axisDimName, axisIndex) {
  45458. if (axisDimName == null && axisIndex == null) {
  45459. var axisProxy = this.findRepresentativeAxisProxy();
  45460. if (axisProxy) {
  45461. return axisProxy.getDataValueWindow();
  45462. }
  45463. }
  45464. else {
  45465. return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
  45466. }
  45467. },
  45468. /**
  45469. * @public
  45470. * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy
  45471. * corresponding to the axisModel
  45472. * @return {module:echarts/component/dataZoom/AxisProxy}
  45473. */
  45474. findRepresentativeAxisProxy: function (axisModel) {
  45475. if (axisModel) {
  45476. return axisModel.__dzAxisProxy;
  45477. }
  45478. // Find the first hosted axisProxy
  45479. var axisProxies = this._axisProxies;
  45480. for (var key in axisProxies) {
  45481. if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
  45482. return axisProxies[key];
  45483. }
  45484. }
  45485. // If no hosted axis find not hosted axisProxy.
  45486. // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
  45487. // and the option.start or option.end settings are different. The percentRange
  45488. // should follow axisProxy.
  45489. // (We encounter this problem in toolbox data zoom.)
  45490. for (var key in axisProxies) {
  45491. if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
  45492. return axisProxies[key];
  45493. }
  45494. }
  45495. },
  45496. /**
  45497. * @return {Array.<string>}
  45498. */
  45499. getRangePropMode: function () {
  45500. return this._rangePropMode.slice();
  45501. }
  45502. });
  45503. function retrieveRaw(option) {
  45504. var ret = {};
  45505. each$12(
  45506. ['start', 'end', 'startValue', 'endValue', 'throttle'],
  45507. function (name) {
  45508. option.hasOwnProperty(name) && (ret[name] = option[name]);
  45509. }
  45510. );
  45511. return ret;
  45512. }
  45513. function updateRangeUse(dataZoomModel, rawOption) {
  45514. var rangePropMode = dataZoomModel._rangePropMode;
  45515. var rangeModeInOption = dataZoomModel.get('rangeMode');
  45516. each$12([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
  45517. var percentSpecified = rawOption[names[0]] != null;
  45518. var valueSpecified = rawOption[names[1]] != null;
  45519. if (percentSpecified && !valueSpecified) {
  45520. rangePropMode[index] = 'percent';
  45521. }
  45522. else if (!percentSpecified && valueSpecified) {
  45523. rangePropMode[index] = 'value';
  45524. }
  45525. else if (rangeModeInOption) {
  45526. rangePropMode[index] = rangeModeInOption[index];
  45527. }
  45528. else if (percentSpecified) { // percentSpecified && valueSpecified
  45529. rangePropMode[index] = 'percent';
  45530. }
  45531. // else remain its original setting.
  45532. });
  45533. }
  45534. /*
  45535. * Licensed to the Apache Software Foundation (ASF) under one
  45536. * or more contributor license agreements. See the NOTICE file
  45537. * distributed with this work for additional information
  45538. * regarding copyright ownership. The ASF licenses this file
  45539. * to you under the Apache License, Version 2.0 (the
  45540. * "License"); you may not use this file except in compliance
  45541. * with the License. You may obtain a copy of the License at
  45542. *
  45543. * http://www.apache.org/licenses/LICENSE-2.0
  45544. *
  45545. * Unless required by applicable law or agreed to in writing,
  45546. * software distributed under the License is distributed on an
  45547. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45548. * KIND, either express or implied. See the License for the
  45549. * specific language governing permissions and limitations
  45550. * under the License.
  45551. */
  45552. var DataZoomView = Component$1.extend({
  45553. type: 'dataZoom',
  45554. render: function (dataZoomModel, ecModel, api, payload) {
  45555. this.dataZoomModel = dataZoomModel;
  45556. this.ecModel = ecModel;
  45557. this.api = api;
  45558. },
  45559. /**
  45560. * Find the first target coordinate system.
  45561. *
  45562. * @protected
  45563. * @return {Object} {
  45564. * grid: [
  45565. * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},
  45566. * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},
  45567. * ...
  45568. * ], // cartesians must not be null/undefined.
  45569. * polar: [
  45570. * {model: coord0, axisModels: [axis4], coordIndex: 0},
  45571. * ...
  45572. * ], // polars must not be null/undefined.
  45573. * singleAxis: [
  45574. * {model: coord0, axisModels: [], coordIndex: 0}
  45575. * ]
  45576. */
  45577. getTargetCoordInfo: function () {
  45578. var dataZoomModel = this.dataZoomModel;
  45579. var ecModel = this.ecModel;
  45580. var coordSysLists = {};
  45581. dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
  45582. var axisModel = ecModel.getComponent(dimNames.axis, axisIndex);
  45583. if (axisModel) {
  45584. var coordModel = axisModel.getCoordSysModel();
  45585. coordModel && save(
  45586. coordModel,
  45587. axisModel,
  45588. coordSysLists[coordModel.mainType] || (coordSysLists[coordModel.mainType] = []),
  45589. coordModel.componentIndex
  45590. );
  45591. }
  45592. }, this);
  45593. function save(coordModel, axisModel, store, coordIndex) {
  45594. var item;
  45595. for (var i = 0; i < store.length; i++) {
  45596. if (store[i].model === coordModel) {
  45597. item = store[i];
  45598. break;
  45599. }
  45600. }
  45601. if (!item) {
  45602. store.push(item = {
  45603. model: coordModel, axisModels: [], coordIndex: coordIndex
  45604. });
  45605. }
  45606. item.axisModels.push(axisModel);
  45607. }
  45608. return coordSysLists;
  45609. }
  45610. });
  45611. /*
  45612. * Licensed to the Apache Software Foundation (ASF) under one
  45613. * or more contributor license agreements. See the NOTICE file
  45614. * distributed with this work for additional information
  45615. * regarding copyright ownership. The ASF licenses this file
  45616. * to you under the Apache License, Version 2.0 (the
  45617. * "License"); you may not use this file except in compliance
  45618. * with the License. You may obtain a copy of the License at
  45619. *
  45620. * http://www.apache.org/licenses/LICENSE-2.0
  45621. *
  45622. * Unless required by applicable law or agreed to in writing,
  45623. * software distributed under the License is distributed on an
  45624. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45625. * KIND, either express or implied. See the License for the
  45626. * specific language governing permissions and limitations
  45627. * under the License.
  45628. */
  45629. var SliderZoomModel = DataZoomModel.extend({
  45630. type: 'dataZoom.slider',
  45631. layoutMode: 'box',
  45632. /**
  45633. * @protected
  45634. */
  45635. defaultOption: {
  45636. show: true,
  45637. // ph => placeholder. Using placehoder here because
  45638. // deault value can only be drived in view stage.
  45639. right: 'ph', // Default align to grid rect.
  45640. top: 'ph', // Default align to grid rect.
  45641. width: 'ph', // Default align to grid rect.
  45642. height: 'ph', // Default align to grid rect.
  45643. left: null, // Default align to grid rect.
  45644. bottom: null, // Default align to grid rect.
  45645. backgroundColor: 'rgba(47,69,84,0)', // Background of slider zoom component.
  45646. // dataBackgroundColor: '#ddd', // Background coor of data shadow and border of box,
  45647. // highest priority, remain for compatibility of
  45648. // previous version, but not recommended any more.
  45649. dataBackground: {
  45650. lineStyle: {
  45651. color: '#2f4554',
  45652. width: 0.5,
  45653. opacity: 0.3
  45654. },
  45655. areaStyle: {
  45656. color: 'rgba(47,69,84,0.3)',
  45657. opacity: 0.3
  45658. }
  45659. },
  45660. borderColor: '#ddd', // border color of the box. For compatibility,
  45661. // if dataBackgroundColor is set, borderColor
  45662. // is ignored.
  45663. fillerColor: 'rgba(167,183,204,0.4)', // Color of selected area.
  45664. // handleColor: 'rgba(89,170,216,0.95)', // Color of handle.
  45665. // handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.6,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z',
  45666. /* eslint-disable */
  45667. handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z',
  45668. /* eslint-enable */
  45669. // Percent of the slider height
  45670. handleSize: '100%',
  45671. handleStyle: {
  45672. color: '#a7b7cc'
  45673. },
  45674. labelPrecision: null,
  45675. labelFormatter: null,
  45676. showDetail: true,
  45677. showDataShadow: 'auto', // Default auto decision.
  45678. realtime: true,
  45679. zoomLock: false, // Whether disable zoom.
  45680. textStyle: {
  45681. color: '#333'
  45682. }
  45683. }
  45684. });
  45685. /*
  45686. * Licensed to the Apache Software Foundation (ASF) under one
  45687. * or more contributor license agreements. See the NOTICE file
  45688. * distributed with this work for additional information
  45689. * regarding copyright ownership. The ASF licenses this file
  45690. * to you under the Apache License, Version 2.0 (the
  45691. * "License"); you may not use this file except in compliance
  45692. * with the License. You may obtain a copy of the License at
  45693. *
  45694. * http://www.apache.org/licenses/LICENSE-2.0
  45695. *
  45696. * Unless required by applicable law or agreed to in writing,
  45697. * software distributed under the License is distributed on an
  45698. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45699. * KIND, either express or implied. See the License for the
  45700. * specific language governing permissions and limitations
  45701. * under the License.
  45702. */
  45703. /**
  45704. * Calculate slider move result.
  45705. * Usage:
  45706. * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as
  45707. * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.
  45708. * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.
  45709. *
  45710. * @param {number} delta Move length.
  45711. * @param {Array.<number>} handleEnds handleEnds[0] can be bigger then handleEnds[1].
  45712. * handleEnds will be modified in this method.
  45713. * @param {Array.<number>} extent handleEnds is restricted by extent.
  45714. * extent[0] should less or equals than extent[1].
  45715. * @param {number|string} handleIndex Can be 'all', means that both move the two handleEnds,
  45716. * where the input minSpan and maxSpan will not work.
  45717. * @param {number} [minSpan] The range of dataZoom can not be smaller than that.
  45718. * If not set, handle0 and cross handle1. If set as a non-negative
  45719. * number (including `0`), handles will push each other when reaching
  45720. * the minSpan.
  45721. * @param {number} [maxSpan] The range of dataZoom can not be larger than that.
  45722. * @return {Array.<number>} The input handleEnds.
  45723. */
  45724. var sliderMove = function (delta, handleEnds, extent, handleIndex, minSpan, maxSpan) {
  45725. // Normalize firstly.
  45726. handleEnds[0] = restrict(handleEnds[0], extent);
  45727. handleEnds[1] = restrict(handleEnds[1], extent);
  45728. delta = delta || 0;
  45729. var extentSpan = extent[1] - extent[0];
  45730. // Notice maxSpan and minSpan can be null/undefined.
  45731. if (minSpan != null) {
  45732. minSpan = restrict(minSpan, [0, extentSpan]);
  45733. }
  45734. if (maxSpan != null) {
  45735. maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0);
  45736. }
  45737. if (handleIndex === 'all') {
  45738. minSpan = maxSpan = Math.abs(handleEnds[1] - handleEnds[0]);
  45739. handleIndex = 0;
  45740. }
  45741. var originalDistSign = getSpanSign(handleEnds, handleIndex);
  45742. handleEnds[handleIndex] += delta;
  45743. // Restrict in extent.
  45744. var extentMinSpan = minSpan || 0;
  45745. var realExtent = extent.slice();
  45746. originalDistSign.sign < 0 ? (realExtent[0] += extentMinSpan) : (realExtent[1] -= extentMinSpan);
  45747. handleEnds[handleIndex] = restrict(handleEnds[handleIndex], realExtent);
  45748. // Expand span.
  45749. var currDistSign = getSpanSign(handleEnds, handleIndex);
  45750. if (minSpan != null && (
  45751. currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan
  45752. )) {
  45753. // If minSpan exists, 'cross' is forbinden.
  45754. handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan;
  45755. }
  45756. // Shrink span.
  45757. var currDistSign = getSpanSign(handleEnds, handleIndex);
  45758. if (maxSpan != null && currDistSign.span > maxSpan) {
  45759. handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan;
  45760. }
  45761. return handleEnds;
  45762. };
  45763. function getSpanSign(handleEnds, handleIndex) {
  45764. var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex];
  45765. // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]
  45766. // is at left of handleEnds[1] for non-cross case.
  45767. return {span: Math.abs(dist), sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1};
  45768. }
  45769. function restrict(value, extend) {
  45770. return Math.min(extend[1], Math.max(extend[0], value));
  45771. }
  45772. /*
  45773. * Licensed to the Apache Software Foundation (ASF) under one
  45774. * or more contributor license agreements. See the NOTICE file
  45775. * distributed with this work for additional information
  45776. * regarding copyright ownership. The ASF licenses this file
  45777. * to you under the Apache License, Version 2.0 (the
  45778. * "License"); you may not use this file except in compliance
  45779. * with the License. You may obtain a copy of the License at
  45780. *
  45781. * http://www.apache.org/licenses/LICENSE-2.0
  45782. *
  45783. * Unless required by applicable law or agreed to in writing,
  45784. * software distributed under the License is distributed on an
  45785. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45786. * KIND, either express or implied. See the License for the
  45787. * specific language governing permissions and limitations
  45788. * under the License.
  45789. */
  45790. var Rect$1 = Rect;
  45791. var linearMap$1 = linearMap;
  45792. var asc$2 = asc;
  45793. var bind$3 = bind;
  45794. var each$14 = each$1;
  45795. // Constants
  45796. var DEFAULT_LOCATION_EDGE_GAP = 7;
  45797. var DEFAULT_FRAME_BORDER_WIDTH = 1;
  45798. var DEFAULT_FILLER_SIZE = 30;
  45799. var HORIZONTAL = 'horizontal';
  45800. var VERTICAL = 'vertical';
  45801. var LABEL_GAP = 5;
  45802. var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];
  45803. var SliderZoomView = DataZoomView.extend({
  45804. type: 'dataZoom.slider',
  45805. init: function (ecModel, api) {
  45806. /**
  45807. * @private
  45808. * @type {Object}
  45809. */
  45810. this._displayables = {};
  45811. /**
  45812. * @private
  45813. * @type {string}
  45814. */
  45815. this._orient;
  45816. /**
  45817. * [0, 100]
  45818. * @private
  45819. */
  45820. this._range;
  45821. /**
  45822. * [coord of the first handle, coord of the second handle]
  45823. * @private
  45824. */
  45825. this._handleEnds;
  45826. /**
  45827. * [length, thick]
  45828. * @private
  45829. * @type {Array.<number>}
  45830. */
  45831. this._size;
  45832. /**
  45833. * @private
  45834. * @type {number}
  45835. */
  45836. this._handleWidth;
  45837. /**
  45838. * @private
  45839. * @type {number}
  45840. */
  45841. this._handleHeight;
  45842. /**
  45843. * @private
  45844. */
  45845. this._location;
  45846. /**
  45847. * @private
  45848. */
  45849. this._dragging;
  45850. /**
  45851. * @private
  45852. */
  45853. this._dataShadowInfo;
  45854. this.api = api;
  45855. },
  45856. /**
  45857. * @override
  45858. */
  45859. render: function (dataZoomModel, ecModel, api, payload) {
  45860. SliderZoomView.superApply(this, 'render', arguments);
  45861. createOrUpdate(
  45862. this,
  45863. '_dispatchZoomAction',
  45864. this.dataZoomModel.get('throttle'),
  45865. 'fixRate'
  45866. );
  45867. this._orient = dataZoomModel.get('orient');
  45868. if (this.dataZoomModel.get('show') === false) {
  45869. this.group.removeAll();
  45870. return;
  45871. }
  45872. // Notice: this._resetInterval() should not be executed when payload.type
  45873. // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'
  45874. // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,
  45875. if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {
  45876. this._buildView();
  45877. }
  45878. this._updateView();
  45879. },
  45880. /**
  45881. * @override
  45882. */
  45883. remove: function () {
  45884. SliderZoomView.superApply(this, 'remove', arguments);
  45885. clear(this, '_dispatchZoomAction');
  45886. },
  45887. /**
  45888. * @override
  45889. */
  45890. dispose: function () {
  45891. SliderZoomView.superApply(this, 'dispose', arguments);
  45892. clear(this, '_dispatchZoomAction');
  45893. },
  45894. _buildView: function () {
  45895. var thisGroup = this.group;
  45896. thisGroup.removeAll();
  45897. this._resetLocation();
  45898. this._resetInterval();
  45899. var barGroup = this._displayables.barGroup = new Group();
  45900. this._renderBackground();
  45901. this._renderHandle();
  45902. this._renderDataShadow();
  45903. thisGroup.add(barGroup);
  45904. this._positionGroup();
  45905. },
  45906. /**
  45907. * @private
  45908. */
  45909. _resetLocation: function () {
  45910. var dataZoomModel = this.dataZoomModel;
  45911. var api = this.api;
  45912. // If some of x/y/width/height are not specified,
  45913. // auto-adapt according to target grid.
  45914. var coordRect = this._findCoordRect();
  45915. var ecSize = {width: api.getWidth(), height: api.getHeight()};
  45916. // Default align by coordinate system rect.
  45917. var positionInfo = this._orient === HORIZONTAL
  45918. ? {
  45919. // Why using 'right', because right should be used in vertical,
  45920. // and it is better to be consistent for dealing with position param merge.
  45921. right: ecSize.width - coordRect.x - coordRect.width,
  45922. top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP),
  45923. width: coordRect.width,
  45924. height: DEFAULT_FILLER_SIZE
  45925. }
  45926. : { // vertical
  45927. right: DEFAULT_LOCATION_EDGE_GAP,
  45928. top: coordRect.y,
  45929. width: DEFAULT_FILLER_SIZE,
  45930. height: coordRect.height
  45931. };
  45932. // Do not write back to option and replace value 'ph', because
  45933. // the 'ph' value should be recalculated when resize.
  45934. var layoutParams = getLayoutParams(dataZoomModel.option);
  45935. // Replace the placeholder value.
  45936. each$1(['right', 'top', 'width', 'height'], function (name) {
  45937. if (layoutParams[name] === 'ph') {
  45938. layoutParams[name] = positionInfo[name];
  45939. }
  45940. });
  45941. var layoutRect = getLayoutRect(
  45942. layoutParams,
  45943. ecSize,
  45944. dataZoomModel.padding
  45945. );
  45946. this._location = {x: layoutRect.x, y: layoutRect.y};
  45947. this._size = [layoutRect.width, layoutRect.height];
  45948. this._orient === VERTICAL && this._size.reverse();
  45949. },
  45950. /**
  45951. * @private
  45952. */
  45953. _positionGroup: function () {
  45954. var thisGroup = this.group;
  45955. var location = this._location;
  45956. var orient = this._orient;
  45957. // Just use the first axis to determine mapping.
  45958. var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();
  45959. var inverse = targetAxisModel && targetAxisModel.get('inverse');
  45960. var barGroup = this._displayables.barGroup;
  45961. var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse;
  45962. // Transform barGroup.
  45963. barGroup.attr(
  45964. (orient === HORIZONTAL && !inverse)
  45965. ? {scale: otherAxisInverse ? [1, 1] : [1, -1]}
  45966. : (orient === HORIZONTAL && inverse)
  45967. ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]}
  45968. : (orient === VERTICAL && !inverse)
  45969. ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2}
  45970. // Dont use Math.PI, considering shadow direction.
  45971. : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2}
  45972. );
  45973. // Position barGroup
  45974. var rect = thisGroup.getBoundingRect([barGroup]);
  45975. thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]);
  45976. },
  45977. /**
  45978. * @private
  45979. */
  45980. _getViewExtent: function () {
  45981. return [0, this._size[0]];
  45982. },
  45983. _renderBackground: function () {
  45984. var dataZoomModel = this.dataZoomModel;
  45985. var size = this._size;
  45986. var barGroup = this._displayables.barGroup;
  45987. barGroup.add(new Rect$1({
  45988. silent: true,
  45989. shape: {
  45990. x: 0, y: 0, width: size[0], height: size[1]
  45991. },
  45992. style: {
  45993. fill: dataZoomModel.get('backgroundColor')
  45994. },
  45995. z2: -40
  45996. }));
  45997. // Click panel, over shadow, below handles.
  45998. barGroup.add(new Rect$1({
  45999. shape: {
  46000. x: 0, y: 0, width: size[0], height: size[1]
  46001. },
  46002. style: {
  46003. fill: 'transparent'
  46004. },
  46005. z2: 0,
  46006. onclick: bind(this._onClickPanelClick, this)
  46007. }));
  46008. },
  46009. _renderDataShadow: function () {
  46010. var info = this._dataShadowInfo = this._prepareDataShadowInfo();
  46011. if (!info) {
  46012. return;
  46013. }
  46014. var size = this._size;
  46015. var seriesModel = info.series;
  46016. var data = seriesModel.getRawData();
  46017. var otherDim = seriesModel.getShadowDim
  46018. ? seriesModel.getShadowDim() // @see candlestick
  46019. : info.otherDim;
  46020. if (otherDim == null) {
  46021. return;
  46022. }
  46023. var otherDataExtent = data.getDataExtent(otherDim);
  46024. // Nice extent.
  46025. var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3;
  46026. otherDataExtent = [
  46027. otherDataExtent[0] - otherOffset,
  46028. otherDataExtent[1] + otherOffset
  46029. ];
  46030. var otherShadowExtent = [0, size[1]];
  46031. var thisShadowExtent = [0, size[0]];
  46032. var areaPoints = [[size[0], 0], [0, 0]];
  46033. var linePoints = [];
  46034. var step = thisShadowExtent[1] / (data.count() - 1);
  46035. var thisCoord = 0;
  46036. // Optimize for large data shadow
  46037. var stride = Math.round(data.count() / size[0]);
  46038. var lastIsEmpty;
  46039. data.each([otherDim], function (value, index) {
  46040. if (stride > 0 && (index % stride)) {
  46041. thisCoord += step;
  46042. return;
  46043. }
  46044. // FIXME
  46045. // Should consider axis.min/axis.max when drawing dataShadow.
  46046. // FIXME
  46047. // 应该使用统一的空判断?还是在list里进行空判断?
  46048. var isEmpty = value == null || isNaN(value) || value === '';
  46049. // See #4235.
  46050. var otherCoord = isEmpty
  46051. ? 0 : linearMap$1(value, otherDataExtent, otherShadowExtent, true);
  46052. // Attempt to draw data shadow precisely when there are empty value.
  46053. if (isEmpty && !lastIsEmpty && index) {
  46054. areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]);
  46055. linePoints.push([linePoints[linePoints.length - 1][0], 0]);
  46056. }
  46057. else if (!isEmpty && lastIsEmpty) {
  46058. areaPoints.push([thisCoord, 0]);
  46059. linePoints.push([thisCoord, 0]);
  46060. }
  46061. areaPoints.push([thisCoord, otherCoord]);
  46062. linePoints.push([thisCoord, otherCoord]);
  46063. thisCoord += step;
  46064. lastIsEmpty = isEmpty;
  46065. });
  46066. var dataZoomModel = this.dataZoomModel;
  46067. // var dataBackgroundModel = dataZoomModel.getModel('dataBackground');
  46068. this._displayables.barGroup.add(new Polygon({
  46069. shape: {points: areaPoints},
  46070. style: defaults(
  46071. {fill: dataZoomModel.get('dataBackgroundColor')},
  46072. dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle()
  46073. ),
  46074. silent: true,
  46075. z2: -20
  46076. }));
  46077. this._displayables.barGroup.add(new Polyline({
  46078. shape: {points: linePoints},
  46079. style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(),
  46080. silent: true,
  46081. z2: -19
  46082. }));
  46083. },
  46084. _prepareDataShadowInfo: function () {
  46085. var dataZoomModel = this.dataZoomModel;
  46086. var showDataShadow = dataZoomModel.get('showDataShadow');
  46087. if (showDataShadow === false) {
  46088. return;
  46089. }
  46090. // Find a representative series.
  46091. var result;
  46092. var ecModel = this.ecModel;
  46093. dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
  46094. var seriesModels = dataZoomModel
  46095. .getAxisProxy(dimNames.name, axisIndex)
  46096. .getTargetSeriesModels();
  46097. each$1(seriesModels, function (seriesModel) {
  46098. if (result) {
  46099. return;
  46100. }
  46101. if (showDataShadow !== true && indexOf(
  46102. SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')
  46103. ) < 0
  46104. ) {
  46105. return;
  46106. }
  46107. var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis;
  46108. var otherDim = getOtherDim(dimNames.name);
  46109. var otherAxisInverse;
  46110. var coordSys = seriesModel.coordinateSystem;
  46111. if (otherDim != null && coordSys.getOtherAxis) {
  46112. otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
  46113. }
  46114. otherDim = seriesModel.getData().mapDimension(otherDim);
  46115. result = {
  46116. thisAxis: thisAxis,
  46117. series: seriesModel,
  46118. thisDim: dimNames.name,
  46119. otherDim: otherDim,
  46120. otherAxisInverse: otherAxisInverse
  46121. };
  46122. }, this);
  46123. }, this);
  46124. return result;
  46125. },
  46126. _renderHandle: function () {
  46127. var displaybles = this._displayables;
  46128. var handles = displaybles.handles = [];
  46129. var handleLabels = displaybles.handleLabels = [];
  46130. var barGroup = this._displayables.barGroup;
  46131. var size = this._size;
  46132. var dataZoomModel = this.dataZoomModel;
  46133. barGroup.add(displaybles.filler = new Rect$1({
  46134. draggable: true,
  46135. cursor: getCursor(this._orient),
  46136. drift: bind$3(this._onDragMove, this, 'all'),
  46137. onmousemove: function (e) {
  46138. // Fot mobile devicem, prevent screen slider on the button.
  46139. stop(e.event);
  46140. },
  46141. ondragstart: bind$3(this._showDataInfo, this, true),
  46142. ondragend: bind$3(this._onDragEnd, this),
  46143. onmouseover: bind$3(this._showDataInfo, this, true),
  46144. onmouseout: bind$3(this._showDataInfo, this, false),
  46145. style: {
  46146. fill: dataZoomModel.get('fillerColor'),
  46147. textPosition: 'inside'
  46148. }
  46149. }));
  46150. // Frame border.
  46151. barGroup.add(new Rect$1(subPixelOptimizeRect({
  46152. silent: true,
  46153. shape: {
  46154. x: 0,
  46155. y: 0,
  46156. width: size[0],
  46157. height: size[1]
  46158. },
  46159. style: {
  46160. stroke: dataZoomModel.get('dataBackgroundColor')
  46161. || dataZoomModel.get('borderColor'),
  46162. lineWidth: DEFAULT_FRAME_BORDER_WIDTH,
  46163. fill: 'rgba(0,0,0,0)'
  46164. }
  46165. })));
  46166. each$14([0, 1], function (handleIndex) {
  46167. var path = createIcon(
  46168. dataZoomModel.get('handleIcon'),
  46169. {
  46170. cursor: getCursor(this._orient),
  46171. draggable: true,
  46172. drift: bind$3(this._onDragMove, this, handleIndex),
  46173. onmousemove: function (e) {
  46174. // Fot mobile devicem, prevent screen slider on the button.
  46175. stop(e.event);
  46176. },
  46177. ondragend: bind$3(this._onDragEnd, this),
  46178. onmouseover: bind$3(this._showDataInfo, this, true),
  46179. onmouseout: bind$3(this._showDataInfo, this, false)
  46180. },
  46181. {x: -1, y: 0, width: 2, height: 2}
  46182. );
  46183. var bRect = path.getBoundingRect();
  46184. this._handleHeight = parsePercent$1(dataZoomModel.get('handleSize'), this._size[1]);
  46185. this._handleWidth = bRect.width / bRect.height * this._handleHeight;
  46186. path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());
  46187. var handleColor = dataZoomModel.get('handleColor');
  46188. // Compatitable with previous version
  46189. if (handleColor != null) {
  46190. path.style.fill = handleColor;
  46191. }
  46192. barGroup.add(handles[handleIndex] = path);
  46193. var textStyleModel = dataZoomModel.textStyleModel;
  46194. this.group.add(
  46195. handleLabels[handleIndex] = new Text({
  46196. silent: true,
  46197. invisible: true,
  46198. style: {
  46199. x: 0, y: 0, text: '',
  46200. textVerticalAlign: 'middle',
  46201. textAlign: 'center',
  46202. textFill: textStyleModel.getTextColor(),
  46203. textFont: textStyleModel.getFont()
  46204. },
  46205. z2: 10
  46206. }));
  46207. }, this);
  46208. },
  46209. /**
  46210. * @private
  46211. */
  46212. _resetInterval: function () {
  46213. var range = this._range = this.dataZoomModel.getPercentRange();
  46214. var viewExtent = this._getViewExtent();
  46215. this._handleEnds = [
  46216. linearMap$1(range[0], [0, 100], viewExtent, true),
  46217. linearMap$1(range[1], [0, 100], viewExtent, true)
  46218. ];
  46219. },
  46220. /**
  46221. * @private
  46222. * @param {(number|string)} handleIndex 0 or 1 or 'all'
  46223. * @param {number} delta
  46224. * @return {boolean} changed
  46225. */
  46226. _updateInterval: function (handleIndex, delta) {
  46227. var dataZoomModel = this.dataZoomModel;
  46228. var handleEnds = this._handleEnds;
  46229. var viewExtend = this._getViewExtent();
  46230. var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
  46231. var percentExtent = [0, 100];
  46232. sliderMove(
  46233. delta,
  46234. handleEnds,
  46235. viewExtend,
  46236. dataZoomModel.get('zoomLock') ? 'all' : handleIndex,
  46237. minMaxSpan.minSpan != null
  46238. ? linearMap$1(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null,
  46239. minMaxSpan.maxSpan != null
  46240. ? linearMap$1(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null
  46241. );
  46242. var lastRange = this._range;
  46243. var range = this._range = asc$2([
  46244. linearMap$1(handleEnds[0], viewExtend, percentExtent, true),
  46245. linearMap$1(handleEnds[1], viewExtend, percentExtent, true)
  46246. ]);
  46247. return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];
  46248. },
  46249. /**
  46250. * @private
  46251. */
  46252. _updateView: function (nonRealtime) {
  46253. var displaybles = this._displayables;
  46254. var handleEnds = this._handleEnds;
  46255. var handleInterval = asc$2(handleEnds.slice());
  46256. var size = this._size;
  46257. each$14([0, 1], function (handleIndex) {
  46258. // Handles
  46259. var handle = displaybles.handles[handleIndex];
  46260. var handleHeight = this._handleHeight;
  46261. handle.attr({
  46262. scale: [handleHeight / 2, handleHeight / 2],
  46263. position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2]
  46264. });
  46265. }, this);
  46266. // Filler
  46267. displaybles.filler.setShape({
  46268. x: handleInterval[0],
  46269. y: 0,
  46270. width: handleInterval[1] - handleInterval[0],
  46271. height: size[1]
  46272. });
  46273. this._updateDataInfo(nonRealtime);
  46274. },
  46275. /**
  46276. * @private
  46277. */
  46278. _updateDataInfo: function (nonRealtime) {
  46279. var dataZoomModel = this.dataZoomModel;
  46280. var displaybles = this._displayables;
  46281. var handleLabels = displaybles.handleLabels;
  46282. var orient = this._orient;
  46283. var labelTexts = ['', ''];
  46284. // FIXME
  46285. // date型,支持formatter,autoformatter(ec2 date.getAutoFormatter)
  46286. if (dataZoomModel.get('showDetail')) {
  46287. var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
  46288. if (axisProxy) {
  46289. var axis = axisProxy.getAxisModel().axis;
  46290. var range = this._range;
  46291. var dataInterval = nonRealtime
  46292. // See #4434, data and axis are not processed and reset yet in non-realtime mode.
  46293. ? axisProxy.calculateDataWindow({
  46294. start: range[0], end: range[1]
  46295. }).valueWindow
  46296. : axisProxy.getDataValueWindow();
  46297. labelTexts = [
  46298. this._formatLabel(dataInterval[0], axis),
  46299. this._formatLabel(dataInterval[1], axis)
  46300. ];
  46301. }
  46302. }
  46303. var orderedHandleEnds = asc$2(this._handleEnds.slice());
  46304. setLabel.call(this, 0);
  46305. setLabel.call(this, 1);
  46306. function setLabel(handleIndex) {
  46307. // Label
  46308. // Text should not transform by barGroup.
  46309. // Ignore handlers transform
  46310. var barTransform = getTransform(
  46311. displaybles.handles[handleIndex].parent, this.group
  46312. );
  46313. var direction = transformDirection(
  46314. handleIndex === 0 ? 'right' : 'left', barTransform
  46315. );
  46316. var offset = this._handleWidth / 2 + LABEL_GAP;
  46317. var textPoint = applyTransform$1(
  46318. [
  46319. orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset),
  46320. this._size[1] / 2
  46321. ],
  46322. barTransform
  46323. );
  46324. handleLabels[handleIndex].setStyle({
  46325. x: textPoint[0],
  46326. y: textPoint[1],
  46327. textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction,
  46328. textAlign: orient === HORIZONTAL ? direction : 'center',
  46329. text: labelTexts[handleIndex]
  46330. });
  46331. }
  46332. },
  46333. /**
  46334. * @private
  46335. */
  46336. _formatLabel: function (value, axis) {
  46337. var dataZoomModel = this.dataZoomModel;
  46338. var labelFormatter = dataZoomModel.get('labelFormatter');
  46339. var labelPrecision = dataZoomModel.get('labelPrecision');
  46340. if (labelPrecision == null || labelPrecision === 'auto') {
  46341. labelPrecision = axis.getPixelPrecision();
  46342. }
  46343. var valueStr = (value == null || isNaN(value))
  46344. ? ''
  46345. // FIXME Glue code
  46346. : (axis.type === 'category' || axis.type === 'time')
  46347. ? axis.scale.getLabel(Math.round(value))
  46348. // param of toFixed should less then 20.
  46349. : value.toFixed(Math.min(labelPrecision, 20));
  46350. return isFunction$1(labelFormatter)
  46351. ? labelFormatter(value, valueStr)
  46352. : isString(labelFormatter)
  46353. ? labelFormatter.replace('{value}', valueStr)
  46354. : valueStr;
  46355. },
  46356. /**
  46357. * @private
  46358. * @param {boolean} showOrHide true: show, false: hide
  46359. */
  46360. _showDataInfo: function (showOrHide) {
  46361. // Always show when drgging.
  46362. showOrHide = this._dragging || showOrHide;
  46363. var handleLabels = this._displayables.handleLabels;
  46364. handleLabels[0].attr('invisible', !showOrHide);
  46365. handleLabels[1].attr('invisible', !showOrHide);
  46366. },
  46367. _onDragMove: function (handleIndex, dx, dy) {
  46368. this._dragging = true;
  46369. // Transform dx, dy to bar coordination.
  46370. var barTransform = this._displayables.barGroup.getLocalTransform();
  46371. var vertex = applyTransform$1([dx, dy], barTransform, true);
  46372. var changed = this._updateInterval(handleIndex, vertex[0]);
  46373. var realtime = this.dataZoomModel.get('realtime');
  46374. this._updateView(!realtime);
  46375. // Avoid dispatch dataZoom repeatly but range not changed,
  46376. // which cause bad visual effect when progressive enabled.
  46377. changed && realtime && this._dispatchZoomAction();
  46378. },
  46379. _onDragEnd: function () {
  46380. this._dragging = false;
  46381. this._showDataInfo(false);
  46382. // While in realtime mode and stream mode, dispatch action when
  46383. // drag end will cause the whole view rerender, which is unnecessary.
  46384. var realtime = this.dataZoomModel.get('realtime');
  46385. !realtime && this._dispatchZoomAction();
  46386. },
  46387. _onClickPanelClick: function (e) {
  46388. var size = this._size;
  46389. var localPoint = this._displayables.barGroup.transformCoordToLocal(e.offsetX, e.offsetY);
  46390. if (localPoint[0] < 0 || localPoint[0] > size[0]
  46391. || localPoint[1] < 0 || localPoint[1] > size[1]
  46392. ) {
  46393. return;
  46394. }
  46395. var handleEnds = this._handleEnds;
  46396. var center = (handleEnds[0] + handleEnds[1]) / 2;
  46397. var changed = this._updateInterval('all', localPoint[0] - center);
  46398. this._updateView();
  46399. changed && this._dispatchZoomAction();
  46400. },
  46401. /**
  46402. * This action will be throttled.
  46403. * @private
  46404. */
  46405. _dispatchZoomAction: function () {
  46406. var range = this._range;
  46407. this.api.dispatchAction({
  46408. type: 'dataZoom',
  46409. from: this.uid,
  46410. dataZoomId: this.dataZoomModel.id,
  46411. start: range[0],
  46412. end: range[1]
  46413. });
  46414. },
  46415. /**
  46416. * @private
  46417. */
  46418. _findCoordRect: function () {
  46419. // Find the grid coresponding to the first axis referred by dataZoom.
  46420. var rect;
  46421. each$14(this.getTargetCoordInfo(), function (coordInfoList) {
  46422. if (!rect && coordInfoList.length) {
  46423. var coordSys = coordInfoList[0].model.coordinateSystem;
  46424. rect = coordSys.getRect && coordSys.getRect();
  46425. }
  46426. });
  46427. if (!rect) {
  46428. var width = this.api.getWidth();
  46429. var height = this.api.getHeight();
  46430. rect = {
  46431. x: width * 0.2,
  46432. y: height * 0.2,
  46433. width: width * 0.6,
  46434. height: height * 0.6
  46435. };
  46436. }
  46437. return rect;
  46438. }
  46439. });
  46440. function getOtherDim(thisDim) {
  46441. // FIXME
  46442. // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好
  46443. var map$$1 = {x: 'y', y: 'x', radius: 'angle', angle: 'radius'};
  46444. return map$$1[thisDim];
  46445. }
  46446. function getCursor(orient) {
  46447. return orient === 'vertical' ? 'ns-resize' : 'ew-resize';
  46448. }
  46449. /*
  46450. * Licensed to the Apache Software Foundation (ASF) under one
  46451. * or more contributor license agreements. See the NOTICE file
  46452. * distributed with this work for additional information
  46453. * regarding copyright ownership. The ASF licenses this file
  46454. * to you under the Apache License, Version 2.0 (the
  46455. * "License"); you may not use this file except in compliance
  46456. * with the License. You may obtain a copy of the License at
  46457. *
  46458. * http://www.apache.org/licenses/LICENSE-2.0
  46459. *
  46460. * Unless required by applicable law or agreed to in writing,
  46461. * software distributed under the License is distributed on an
  46462. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  46463. * KIND, either express or implied. See the License for the
  46464. * specific language governing permissions and limitations
  46465. * under the License.
  46466. */
  46467. DataZoomModel.extend({
  46468. type: 'dataZoom.inside',
  46469. /**
  46470. * @protected
  46471. */
  46472. defaultOption: {
  46473. disabled: false, // Whether disable this inside zoom.
  46474. zoomLock: false, // Whether disable zoom but only pan.
  46475. zoomOnMouseWheel: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46476. moveOnMouseMove: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46477. moveOnMouseWheel: false, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46478. preventDefaultMouseMove: true
  46479. }
  46480. });
  46481. /*
  46482. * Licensed to the Apache Software Foundation (ASF) under one
  46483. * or more contributor license agreements. See the NOTICE file
  46484. * distributed with this work for additional information
  46485. * regarding copyright ownership. The ASF licenses this file
  46486. * to you under the Apache License, Version 2.0 (the
  46487. * "License"); you may not use this file except in compliance
  46488. * with the License. You may obtain a copy of the License at
  46489. *
  46490. * http://www.apache.org/licenses/LICENSE-2.0
  46491. *
  46492. * Unless required by applicable law or agreed to in writing,
  46493. * software distributed under the License is distributed on an
  46494. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  46495. * KIND, either express or implied. See the License for the
  46496. * specific language governing permissions and limitations
  46497. * under the License.
  46498. */
  46499. var ATTR$1 = '\0_ec_interaction_mutex';
  46500. function take(zr, resourceKey, userKey) {
  46501. var store = getStore(zr);
  46502. store[resourceKey] = userKey;
  46503. }
  46504. function release(zr, resourceKey, userKey) {
  46505. var store = getStore(zr);
  46506. var uKey = store[resourceKey];
  46507. if (uKey === userKey) {
  46508. store[resourceKey] = null;
  46509. }
  46510. }
  46511. function isTaken(zr, resourceKey) {
  46512. return !!getStore(zr)[resourceKey];
  46513. }
  46514. function getStore(zr) {
  46515. return zr[ATTR$1] || (zr[ATTR$1] = {});
  46516. }
  46517. /**
  46518. * payload: {
  46519. * type: 'takeGlobalCursor',
  46520. * key: 'dataZoomSelect', or 'brush', or ...,
  46521. * If no userKey, release global cursor.
  46522. * }
  46523. */
  46524. registerAction(
  46525. {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'},
  46526. function () {}
  46527. );
  46528. /*
  46529. * Licensed to the Apache Software Foundation (ASF) under one
  46530. * or more contributor license agreements. See the NOTICE file
  46531. * distributed with this work for additional information
  46532. * regarding copyright ownership. The ASF licenses this file
  46533. * to you under the Apache License, Version 2.0 (the
  46534. * "License"); you may not use this file except in compliance
  46535. * with the License. You may obtain a copy of the License at
  46536. *
  46537. * http://www.apache.org/licenses/LICENSE-2.0
  46538. *
  46539. * Unless required by applicable law or agreed to in writing,
  46540. * software distributed under the License is distributed on an
  46541. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  46542. * KIND, either express or implied. See the License for the
  46543. * specific language governing permissions and limitations
  46544. * under the License.
  46545. */
  46546. /**
  46547. * @alias module:echarts/component/helper/RoamController
  46548. * @constructor
  46549. * @mixin {module:zrender/mixin/Eventful}
  46550. *
  46551. * @param {module:zrender/zrender~ZRender} zr
  46552. */
  46553. function RoamController(zr) {
  46554. /**
  46555. * @type {Function}
  46556. */
  46557. this.pointerChecker;
  46558. /**
  46559. * @type {module:zrender}
  46560. */
  46561. this._zr = zr;
  46562. /**
  46563. * @type {Object}
  46564. */
  46565. this._opt = {};
  46566. // Avoid two roamController bind the same handler
  46567. var bind$$1 = bind;
  46568. var mousedownHandler = bind$$1(mousedown, this);
  46569. var mousemoveHandler = bind$$1(mousemove, this);
  46570. var mouseupHandler = bind$$1(mouseup, this);
  46571. var mousewheelHandler = bind$$1(mousewheel, this);
  46572. var pinchHandler = bind$$1(pinch, this);
  46573. Eventful.call(this);
  46574. /**
  46575. * @param {Function} pointerChecker
  46576. * input: x, y
  46577. * output: boolean
  46578. */
  46579. this.setPointerChecker = function (pointerChecker) {
  46580. this.pointerChecker = pointerChecker;
  46581. };
  46582. /**
  46583. * Notice: only enable needed types. For example, if 'zoom'
  46584. * is not needed, 'zoom' should not be enabled, otherwise
  46585. * default mousewheel behaviour (scroll page) will be disabled.
  46586. *
  46587. * @param {boolean|string} [controlType=true] Specify the control type,
  46588. * which can be null/undefined or true/false
  46589. * or 'pan/move' or 'zoom'/'scale'
  46590. * @param {Object} [opt]
  46591. * @param {Object} [opt.zoomOnMouseWheel=true] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46592. * @param {Object} [opt.moveOnMouseMove=true] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46593. * @param {Object} [opt.moveOnMouseWheel=false] The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46594. * @param {Object} [opt.preventDefaultMouseMove=true] When pan.
  46595. */
  46596. this.enable = function (controlType, opt) {
  46597. // Disable previous first
  46598. this.disable();
  46599. this._opt = defaults(clone(opt) || {}, {
  46600. zoomOnMouseWheel: true,
  46601. moveOnMouseMove: true,
  46602. // By default, wheel do not trigger move.
  46603. moveOnMouseWheel: false,
  46604. preventDefaultMouseMove: true
  46605. });
  46606. if (controlType == null) {
  46607. controlType = true;
  46608. }
  46609. if (controlType === true || (controlType === 'move' || controlType === 'pan')) {
  46610. zr.on('mousedown', mousedownHandler);
  46611. zr.on('mousemove', mousemoveHandler);
  46612. zr.on('mouseup', mouseupHandler);
  46613. }
  46614. if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) {
  46615. zr.on('mousewheel', mousewheelHandler);
  46616. zr.on('pinch', pinchHandler);
  46617. }
  46618. };
  46619. this.disable = function () {
  46620. zr.off('mousedown', mousedownHandler);
  46621. zr.off('mousemove', mousemoveHandler);
  46622. zr.off('mouseup', mouseupHandler);
  46623. zr.off('mousewheel', mousewheelHandler);
  46624. zr.off('pinch', pinchHandler);
  46625. };
  46626. this.dispose = this.disable;
  46627. this.isDragging = function () {
  46628. return this._dragging;
  46629. };
  46630. this.isPinching = function () {
  46631. return this._pinching;
  46632. };
  46633. }
  46634. mixin(RoamController, Eventful);
  46635. function mousedown(e) {
  46636. if (isMiddleOrRightButtonOnMouseUpDown(e)
  46637. || (e.target && e.target.draggable)
  46638. ) {
  46639. return;
  46640. }
  46641. var x = e.offsetX;
  46642. var y = e.offsetY;
  46643. // Only check on mosedown, but not mousemove.
  46644. // Mouse can be out of target when mouse moving.
  46645. if (this.pointerChecker && this.pointerChecker(e, x, y)) {
  46646. this._x = x;
  46647. this._y = y;
  46648. this._dragging = true;
  46649. }
  46650. }
  46651. function mousemove(e) {
  46652. if (!this._dragging
  46653. || !isAvailableBehavior('moveOnMouseMove', e, this._opt)
  46654. || e.gestureEvent === 'pinch'
  46655. || isTaken(this._zr, 'globalPan')
  46656. ) {
  46657. return;
  46658. }
  46659. var x = e.offsetX;
  46660. var y = e.offsetY;
  46661. var oldX = this._x;
  46662. var oldY = this._y;
  46663. var dx = x - oldX;
  46664. var dy = y - oldY;
  46665. this._x = x;
  46666. this._y = y;
  46667. this._opt.preventDefaultMouseMove && stop(e.event);
  46668. trigger(this, 'pan', 'moveOnMouseMove', e, {
  46669. dx: dx, dy: dy, oldX: oldX, oldY: oldY, newX: x, newY: y
  46670. });
  46671. }
  46672. function mouseup(e) {
  46673. if (!isMiddleOrRightButtonOnMouseUpDown(e)) {
  46674. this._dragging = false;
  46675. }
  46676. }
  46677. function mousewheel(e) {
  46678. var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt);
  46679. var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt);
  46680. var wheelDelta = e.wheelDelta;
  46681. var absWheelDeltaDelta = Math.abs(wheelDelta);
  46682. var originX = e.offsetX;
  46683. var originY = e.offsetY;
  46684. // wheelDelta maybe -0 in chrome mac.
  46685. if (wheelDelta === 0 || (!shouldZoom && !shouldMove)) {
  46686. return;
  46687. }
  46688. // If both `shouldZoom` and `shouldMove` is true, trigger
  46689. // their event both, and the final behavior is determined
  46690. // by event listener themselves.
  46691. if (shouldZoom) {
  46692. // Convenience:
  46693. // Mac and VM Windows on Mac: scroll up: zoom out.
  46694. // Windows: scroll up: zoom in.
  46695. // FIXME: Should do more test in different environment.
  46696. // wheelDelta is too complicated in difference nvironment
  46697. // (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),
  46698. // although it has been normallized by zrender.
  46699. // wheelDelta of mouse wheel is bigger than touch pad.
  46700. var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1;
  46701. var scale = wheelDelta > 0 ? factor : 1 / factor;
  46702. checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {
  46703. scale: scale, originX: originX, originY: originY
  46704. });
  46705. }
  46706. if (shouldMove) {
  46707. // FIXME: Should do more test in different environment.
  46708. var absDelta = Math.abs(wheelDelta);
  46709. // wheelDelta of mouse wheel is bigger than touch pad.
  46710. var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05);
  46711. checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, {
  46712. scrollDelta: scrollDelta, originX: originX, originY: originY
  46713. });
  46714. }
  46715. }
  46716. function pinch(e) {
  46717. if (isTaken(this._zr, 'globalPan')) {
  46718. return;
  46719. }
  46720. var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1;
  46721. checkPointerAndTrigger(this, 'zoom', null, e, {
  46722. scale: scale, originX: e.pinchX, originY: e.pinchY
  46723. });
  46724. }
  46725. function checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) {
  46726. if (controller.pointerChecker
  46727. && controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)
  46728. ) {
  46729. // When mouse is out of roamController rect,
  46730. // default befavoius should not be be disabled, otherwise
  46731. // page sliding is disabled, contrary to expectation.
  46732. stop(e.event);
  46733. trigger(controller, eventName, behaviorToCheck, e, contollerEvent);
  46734. }
  46735. }
  46736. function trigger(controller, eventName, behaviorToCheck, e, contollerEvent) {
  46737. // Also provide behavior checker for event listener, for some case that
  46738. // multiple components share one listener.
  46739. contollerEvent.isAvailableBehavior = bind(isAvailableBehavior, null, behaviorToCheck, e);
  46740. controller.trigger(eventName, contollerEvent);
  46741. }
  46742. // settings: {
  46743. // zoomOnMouseWheel
  46744. // moveOnMouseMove
  46745. // moveOnMouseWheel
  46746. // }
  46747. // The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
  46748. function isAvailableBehavior(behaviorToCheck, e, settings) {
  46749. var setting = settings[behaviorToCheck];
  46750. return !behaviorToCheck || (
  46751. setting && (!isString(setting) || e.event[setting + 'Key'])
  46752. );
  46753. }
  46754. /*
  46755. * Licensed to the Apache Software Foundation (ASF) under one
  46756. * or more contributor license agreements. See the NOTICE file
  46757. * distributed with this work for additional information
  46758. * regarding copyright ownership. The ASF licenses this file
  46759. * to you under the Apache License, Version 2.0 (the
  46760. * "License"); you may not use this file except in compliance
  46761. * with the License. You may obtain a copy of the License at
  46762. *
  46763. * http://www.apache.org/licenses/LICENSE-2.0
  46764. *
  46765. * Unless required by applicable law or agreed to in writing,
  46766. * software distributed under the License is distributed on an
  46767. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  46768. * KIND, either express or implied. See the License for the
  46769. * specific language governing permissions and limitations
  46770. * under the License.
  46771. */
  46772. // Only create one roam controller for each coordinate system.
  46773. // one roam controller might be refered by two inside data zoom
  46774. // components (for example, one for x and one for y). When user
  46775. // pan or zoom, only dispatch one action for those data zoom
  46776. // components.
  46777. var ATTR = '\0_ec_dataZoom_roams';
  46778. /**
  46779. * @public
  46780. * @param {module:echarts/ExtensionAPI} api
  46781. * @param {Object} dataZoomInfo
  46782. * @param {string} dataZoomInfo.coordId
  46783. * @param {Function} dataZoomInfo.containsPoint
  46784. * @param {Array.<string>} dataZoomInfo.allCoordIds
  46785. * @param {string} dataZoomInfo.dataZoomId
  46786. * @param {Object} dataZoomInfo.getRange
  46787. * @param {Function} dataZoomInfo.getRange.pan
  46788. * @param {Function} dataZoomInfo.getRange.zoom
  46789. * @param {Function} dataZoomInfo.getRange.scrollMove
  46790. * @param {boolean} dataZoomInfo.dataZoomModel
  46791. */
  46792. function register$1(api, dataZoomInfo) {
  46793. var store = giveStore(api);
  46794. var theDataZoomId = dataZoomInfo.dataZoomId;
  46795. var theCoordId = dataZoomInfo.coordId;
  46796. // Do clean when a dataZoom changes its target coordnate system.
  46797. // Avoid memory leak, dispose all not-used-registered.
  46798. each$1(store, function (record, coordId) {
  46799. var dataZoomInfos = record.dataZoomInfos;
  46800. if (dataZoomInfos[theDataZoomId]
  46801. && indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0
  46802. ) {
  46803. delete dataZoomInfos[theDataZoomId];
  46804. record.count--;
  46805. }
  46806. });
  46807. cleanStore(store);
  46808. var record = store[theCoordId];
  46809. // Create if needed.
  46810. if (!record) {
  46811. record = store[theCoordId] = {
  46812. coordId: theCoordId,
  46813. dataZoomInfos: {},
  46814. count: 0
  46815. };
  46816. record.controller = createController(api, record);
  46817. record.dispatchAction = curry(dispatchAction, api);
  46818. }
  46819. // Update reference of dataZoom.
  46820. !(record.dataZoomInfos[theDataZoomId]) && record.count++;
  46821. record.dataZoomInfos[theDataZoomId] = dataZoomInfo;
  46822. var controllerParams = mergeControllerParams(record.dataZoomInfos);
  46823. record.controller.enable(controllerParams.controlType, controllerParams.opt);
  46824. // Consider resize, area should be always updated.
  46825. record.controller.setPointerChecker(dataZoomInfo.containsPoint);
  46826. // Update throttle.
  46827. createOrUpdate(
  46828. record,
  46829. 'dispatchAction',
  46830. dataZoomInfo.dataZoomModel.get('throttle', true),
  46831. 'fixRate'
  46832. );
  46833. }
  46834. /**
  46835. * @public
  46836. * @param {module:echarts/ExtensionAPI} api
  46837. * @param {string} dataZoomId
  46838. */
  46839. function unregister$1(api, dataZoomId) {
  46840. var store = giveStore(api);
  46841. each$1(store, function (record) {
  46842. record.controller.dispose();
  46843. var dataZoomInfos = record.dataZoomInfos;
  46844. if (dataZoomInfos[dataZoomId]) {
  46845. delete dataZoomInfos[dataZoomId];
  46846. record.count--;
  46847. }
  46848. });
  46849. cleanStore(store);
  46850. }
  46851. /**
  46852. * @public
  46853. */
  46854. function generateCoordId(coordModel) {
  46855. return coordModel.type + '\0_' + coordModel.id;
  46856. }
  46857. /**
  46858. * Key: coordId, value: {dataZoomInfos: [], count, controller}
  46859. * @type {Array.<Object>}
  46860. */
  46861. function giveStore(api) {
  46862. // Mount store on zrender instance, so that we do not
  46863. // need to worry about dispose.
  46864. var zr = api.getZr();
  46865. return zr[ATTR] || (zr[ATTR] = {});
  46866. }
  46867. function createController(api, newRecord) {
  46868. var controller = new RoamController(api.getZr());
  46869. each$1(['pan', 'zoom', 'scrollMove'], function (eventName) {
  46870. controller.on(eventName, function (event) {
  46871. var batch = [];
  46872. each$1(newRecord.dataZoomInfos, function (info) {
  46873. // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,
  46874. // moveOnMouseWheel, ...) enabled.
  46875. if (!event.isAvailableBehavior(info.dataZoomModel.option)) {
  46876. return;
  46877. }
  46878. var method = (info.getRange || {})[eventName];
  46879. var range = method && method(newRecord.controller, event);
  46880. !info.dataZoomModel.get('disabled', true) && range && batch.push({
  46881. dataZoomId: info.dataZoomId,
  46882. start: range[0],
  46883. end: range[1]
  46884. });
  46885. });
  46886. batch.length && newRecord.dispatchAction(batch);
  46887. });
  46888. });
  46889. return controller;
  46890. }
  46891. function cleanStore(store) {
  46892. each$1(store, function (record, coordId) {
  46893. if (!record.count) {
  46894. record.controller.dispose();
  46895. delete store[coordId];
  46896. }
  46897. });
  46898. }
  46899. /**
  46900. * This action will be throttled.
  46901. */
  46902. function dispatchAction(api, batch) {
  46903. api.dispatchAction({
  46904. type: 'dataZoom',
  46905. batch: batch
  46906. });
  46907. }
  46908. /**
  46909. * Merge roamController settings when multiple dataZooms share one roamController.
  46910. */
  46911. function mergeControllerParams(dataZoomInfos) {
  46912. var controlType;
  46913. // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated
  46914. // as string, it is probably revert to reserved word by compress tool. See #7411.
  46915. var prefix = 'type_';
  46916. var typePriority = {
  46917. 'type_true': 2,
  46918. 'type_move': 1,
  46919. 'type_false': 0,
  46920. 'type_undefined': -1
  46921. };
  46922. var preventDefaultMouseMove = true;
  46923. each$1(dataZoomInfos, function (dataZoomInfo) {
  46924. var dataZoomModel = dataZoomInfo.dataZoomModel;
  46925. var oneType = dataZoomModel.get('disabled', true)
  46926. ? false
  46927. : dataZoomModel.get('zoomLock', true)
  46928. ? 'move'
  46929. : true;
  46930. if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {
  46931. controlType = oneType;
  46932. }
  46933. // Prevent default move event by default. If one false, do not prevent. Otherwise
  46934. // users may be confused why it does not work when multiple insideZooms exist.
  46935. preventDefaultMouseMove &= dataZoomModel.get('preventDefaultMouseMove', true);
  46936. });
  46937. return {
  46938. controlType: controlType,
  46939. opt: {
  46940. // RoamController will enable all of these functionalities,
  46941. // and the final behavior is determined by its event listener
  46942. // provided by each inside zoom.
  46943. zoomOnMouseWheel: true,
  46944. moveOnMouseMove: true,
  46945. moveOnMouseWheel: true,
  46946. preventDefaultMouseMove: !!preventDefaultMouseMove
  46947. }
  46948. };
  46949. }
  46950. /*
  46951. * Licensed to the Apache Software Foundation (ASF) under one
  46952. * or more contributor license agreements. See the NOTICE file
  46953. * distributed with this work for additional information
  46954. * regarding copyright ownership. The ASF licenses this file
  46955. * to you under the Apache License, Version 2.0 (the
  46956. * "License"); you may not use this file except in compliance
  46957. * with the License. You may obtain a copy of the License at
  46958. *
  46959. * http://www.apache.org/licenses/LICENSE-2.0
  46960. *
  46961. * Unless required by applicable law or agreed to in writing,
  46962. * software distributed under the License is distributed on an
  46963. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  46964. * KIND, either express or implied. See the License for the
  46965. * specific language governing permissions and limitations
  46966. * under the License.
  46967. */
  46968. var bind$4 = bind;
  46969. var InsideZoomView = DataZoomView.extend({
  46970. type: 'dataZoom.inside',
  46971. /**
  46972. * @override
  46973. */
  46974. init: function (ecModel, api) {
  46975. /**
  46976. * 'throttle' is used in this.dispatchAction, so we save range
  46977. * to avoid missing some 'pan' info.
  46978. * @private
  46979. * @type {Array.<number>}
  46980. */
  46981. this._range;
  46982. },
  46983. /**
  46984. * @override
  46985. */
  46986. render: function (dataZoomModel, ecModel, api, payload) {
  46987. InsideZoomView.superApply(this, 'render', arguments);
  46988. // Hance the `throttle` util ensures to preserve command order,
  46989. // here simply updating range all the time will not cause missing
  46990. // any of the the roam change.
  46991. this._range = dataZoomModel.getPercentRange();
  46992. // Reset controllers.
  46993. each$1(this.getTargetCoordInfo(), function (coordInfoList, coordSysName) {
  46994. var allCoordIds = map(coordInfoList, function (coordInfo) {
  46995. return generateCoordId(coordInfo.model);
  46996. });
  46997. each$1(coordInfoList, function (coordInfo) {
  46998. var coordModel = coordInfo.model;
  46999. var getRange = {};
  47000. each$1(['pan', 'zoom', 'scrollMove'], function (eventName) {
  47001. getRange[eventName] = bind$4(roamHandlers[eventName], this, coordInfo, coordSysName);
  47002. }, this);
  47003. register$1(
  47004. api,
  47005. {
  47006. coordId: generateCoordId(coordModel),
  47007. allCoordIds: allCoordIds,
  47008. containsPoint: function (e, x, y) {
  47009. return coordModel.coordinateSystem.containPoint([x, y]);
  47010. },
  47011. dataZoomId: dataZoomModel.id,
  47012. dataZoomModel: dataZoomModel,
  47013. getRange: getRange
  47014. }
  47015. );
  47016. }, this);
  47017. }, this);
  47018. },
  47019. /**
  47020. * @override
  47021. */
  47022. dispose: function () {
  47023. unregister$1(this.api, this.dataZoomModel.id);
  47024. InsideZoomView.superApply(this, 'dispose', arguments);
  47025. this._range = null;
  47026. }
  47027. });
  47028. var roamHandlers = {
  47029. /**
  47030. * @this {module:echarts/component/dataZoom/InsideZoomView}
  47031. */
  47032. zoom: function (coordInfo, coordSysName, controller, e) {
  47033. var lastRange = this._range;
  47034. var range = lastRange.slice();
  47035. // Calculate transform by the first axis.
  47036. var axisModel = coordInfo.axisModels[0];
  47037. if (!axisModel) {
  47038. return;
  47039. }
  47040. var directionInfo = getDirectionInfo[coordSysName](
  47041. null, [e.originX, e.originY], axisModel, controller, coordInfo
  47042. );
  47043. var percentPoint = (
  47044. directionInfo.signal > 0
  47045. ? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel)
  47046. : (directionInfo.pixel - directionInfo.pixelStart)
  47047. ) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];
  47048. var scale = Math.max(1 / e.scale, 0);
  47049. range[0] = (range[0] - percentPoint) * scale + percentPoint;
  47050. range[1] = (range[1] - percentPoint) * scale + percentPoint;
  47051. // Restrict range.
  47052. var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
  47053. sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
  47054. this._range = range;
  47055. if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
  47056. return range;
  47057. }
  47058. },
  47059. /**
  47060. * @this {module:echarts/component/dataZoom/InsideZoomView}
  47061. */
  47062. pan: makeMover(function (range, axisModel, coordInfo, coordSysName, controller, e) {
  47063. var directionInfo = getDirectionInfo[coordSysName](
  47064. [e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordInfo
  47065. );
  47066. return directionInfo.signal
  47067. * (range[1] - range[0])
  47068. * directionInfo.pixel / directionInfo.pixelLength;
  47069. }),
  47070. /**
  47071. * @this {module:echarts/component/dataZoom/InsideZoomView}
  47072. */
  47073. scrollMove: makeMover(function (range, axisModel, coordInfo, coordSysName, controller, e) {
  47074. var directionInfo = getDirectionInfo[coordSysName](
  47075. [0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordInfo
  47076. );
  47077. return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta;
  47078. })
  47079. };
  47080. function makeMover(getPercentDelta) {
  47081. return function (coordInfo, coordSysName, controller, e) {
  47082. var lastRange = this._range;
  47083. var range = lastRange.slice();
  47084. // Calculate transform by the first axis.
  47085. var axisModel = coordInfo.axisModels[0];
  47086. if (!axisModel) {
  47087. return;
  47088. }
  47089. var percentDelta = getPercentDelta(
  47090. range, axisModel, coordInfo, coordSysName, controller, e
  47091. );
  47092. sliderMove(percentDelta, range, [0, 100], 'all');
  47093. this._range = range;
  47094. if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
  47095. return range;
  47096. }
  47097. };
  47098. }
  47099. var getDirectionInfo = {
  47100. grid: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
  47101. var axis = axisModel.axis;
  47102. var ret = {};
  47103. var rect = coordInfo.model.coordinateSystem.getRect();
  47104. oldPoint = oldPoint || [0, 0];
  47105. if (axis.dim === 'x') {
  47106. ret.pixel = newPoint[0] - oldPoint[0];
  47107. ret.pixelLength = rect.width;
  47108. ret.pixelStart = rect.x;
  47109. ret.signal = axis.inverse ? 1 : -1;
  47110. }
  47111. else { // axis.dim === 'y'
  47112. ret.pixel = newPoint[1] - oldPoint[1];
  47113. ret.pixelLength = rect.height;
  47114. ret.pixelStart = rect.y;
  47115. ret.signal = axis.inverse ? -1 : 1;
  47116. }
  47117. return ret;
  47118. },
  47119. polar: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
  47120. var axis = axisModel.axis;
  47121. var ret = {};
  47122. var polar = coordInfo.model.coordinateSystem;
  47123. var radiusExtent = polar.getRadiusAxis().getExtent();
  47124. var angleExtent = polar.getAngleAxis().getExtent();
  47125. oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];
  47126. newPoint = polar.pointToCoord(newPoint);
  47127. if (axisModel.mainType === 'radiusAxis') {
  47128. ret.pixel = newPoint[0] - oldPoint[0];
  47129. // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);
  47130. // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);
  47131. ret.pixelLength = radiusExtent[1] - radiusExtent[0];
  47132. ret.pixelStart = radiusExtent[0];
  47133. ret.signal = axis.inverse ? 1 : -1;
  47134. }
  47135. else { // 'angleAxis'
  47136. ret.pixel = newPoint[1] - oldPoint[1];
  47137. // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);
  47138. // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);
  47139. ret.pixelLength = angleExtent[1] - angleExtent[0];
  47140. ret.pixelStart = angleExtent[0];
  47141. ret.signal = axis.inverse ? -1 : 1;
  47142. }
  47143. return ret;
  47144. },
  47145. singleAxis: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
  47146. var axis = axisModel.axis;
  47147. var rect = coordInfo.model.coordinateSystem.getRect();
  47148. var ret = {};
  47149. oldPoint = oldPoint || [0, 0];
  47150. if (axis.orient === 'horizontal') {
  47151. ret.pixel = newPoint[0] - oldPoint[0];
  47152. ret.pixelLength = rect.width;
  47153. ret.pixelStart = rect.x;
  47154. ret.signal = axis.inverse ? 1 : -1;
  47155. }
  47156. else { // 'vertical'
  47157. ret.pixel = newPoint[1] - oldPoint[1];
  47158. ret.pixelLength = rect.height;
  47159. ret.pixelStart = rect.y;
  47160. ret.signal = axis.inverse ? -1 : 1;
  47161. }
  47162. return ret;
  47163. }
  47164. };
  47165. /*
  47166. * Licensed to the Apache Software Foundation (ASF) under one
  47167. * or more contributor license agreements. See the NOTICE file
  47168. * distributed with this work for additional information
  47169. * regarding copyright ownership. The ASF licenses this file
  47170. * to you under the Apache License, Version 2.0 (the
  47171. * "License"); you may not use this file except in compliance
  47172. * with the License. You may obtain a copy of the License at
  47173. *
  47174. * http://www.apache.org/licenses/LICENSE-2.0
  47175. *
  47176. * Unless required by applicable law or agreed to in writing,
  47177. * software distributed under the License is distributed on an
  47178. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47179. * KIND, either express or implied. See the License for the
  47180. * specific language governing permissions and limitations
  47181. * under the License.
  47182. */
  47183. registerProcessor({
  47184. // `dataZoomProcessor` will only be performed in needed series. Consider if
  47185. // there is a line series and a pie series, it is better not to update the
  47186. // line series if only pie series is needed to be updated.
  47187. getTargetSeries: function (ecModel) {
  47188. var seriesModelMap = createHashMap();
  47189. ecModel.eachComponent('dataZoom', function (dataZoomModel) {
  47190. dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
  47191. var axisProxy = dataZoomModel.getAxisProxy(dimNames.name, axisIndex);
  47192. each$1(axisProxy.getTargetSeriesModels(), function (seriesModel) {
  47193. seriesModelMap.set(seriesModel.uid, seriesModel);
  47194. });
  47195. });
  47196. });
  47197. return seriesModelMap;
  47198. },
  47199. modifyOutputEnd: true,
  47200. // Consider appendData, where filter should be performed. Because data process is
  47201. // in block mode currently, it is not need to worry about that the overallProgress
  47202. // execute every frame.
  47203. overallReset: function (ecModel, api) {
  47204. ecModel.eachComponent('dataZoom', function (dataZoomModel) {
  47205. // We calculate window and reset axis here but not in model
  47206. // init stage and not after action dispatch handler, because
  47207. // reset should be called after seriesData.restoreData.
  47208. dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
  47209. dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel, api);
  47210. });
  47211. // Caution: data zoom filtering is order sensitive when using
  47212. // percent range and no min/max/scale set on axis.
  47213. // For example, we have dataZoom definition:
  47214. // [
  47215. // {xAxisIndex: 0, start: 30, end: 70},
  47216. // {yAxisIndex: 0, start: 20, end: 80}
  47217. // ]
  47218. // In this case, [20, 80] of y-dataZoom should be based on data
  47219. // that have filtered by x-dataZoom using range of [30, 70],
  47220. // but should not be based on full raw data. Thus sliding
  47221. // x-dataZoom will change both ranges of xAxis and yAxis,
  47222. // while sliding y-dataZoom will only change the range of yAxis.
  47223. // So we should filter x-axis after reset x-axis immediately,
  47224. // and then reset y-axis and filter y-axis.
  47225. dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
  47226. dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel, api);
  47227. });
  47228. });
  47229. ecModel.eachComponent('dataZoom', function (dataZoomModel) {
  47230. // Fullfill all of the range props so that user
  47231. // is able to get them from chart.getOption().
  47232. var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
  47233. var percentRange = axisProxy.getDataPercentWindow();
  47234. var valueRange = axisProxy.getDataValueWindow();
  47235. dataZoomModel.setRawRange({
  47236. start: percentRange[0],
  47237. end: percentRange[1],
  47238. startValue: valueRange[0],
  47239. endValue: valueRange[1]
  47240. }, true);
  47241. });
  47242. }
  47243. });
  47244. /*
  47245. * Licensed to the Apache Software Foundation (ASF) under one
  47246. * or more contributor license agreements. See the NOTICE file
  47247. * distributed with this work for additional information
  47248. * regarding copyright ownership. The ASF licenses this file
  47249. * to you under the Apache License, Version 2.0 (the
  47250. * "License"); you may not use this file except in compliance
  47251. * with the License. You may obtain a copy of the License at
  47252. *
  47253. * http://www.apache.org/licenses/LICENSE-2.0
  47254. *
  47255. * Unless required by applicable law or agreed to in writing,
  47256. * software distributed under the License is distributed on an
  47257. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47258. * KIND, either express or implied. See the License for the
  47259. * specific language governing permissions and limitations
  47260. * under the License.
  47261. */
  47262. registerAction('dataZoom', function (payload, ecModel) {
  47263. var linkedNodesFinder = createLinkedNodesFinder(
  47264. bind(ecModel.eachComponent, ecModel, 'dataZoom'),
  47265. eachAxisDim$1,
  47266. function (model, dimNames) {
  47267. return model.get(dimNames.axisIndex);
  47268. }
  47269. );
  47270. var effectedModels = [];
  47271. ecModel.eachComponent(
  47272. {mainType: 'dataZoom', query: payload},
  47273. function (model, index) {
  47274. effectedModels.push.apply(
  47275. effectedModels, linkedNodesFinder(model).nodes
  47276. );
  47277. }
  47278. );
  47279. each$1(effectedModels, function (dataZoomModel, index) {
  47280. dataZoomModel.setRawRange({
  47281. start: payload.start,
  47282. end: payload.end,
  47283. startValue: payload.startValue,
  47284. endValue: payload.endValue
  47285. });
  47286. });
  47287. });
  47288. /*
  47289. * Licensed to the Apache Software Foundation (ASF) under one
  47290. * or more contributor license agreements. See the NOTICE file
  47291. * distributed with this work for additional information
  47292. * regarding copyright ownership. The ASF licenses this file
  47293. * to you under the Apache License, Version 2.0 (the
  47294. * "License"); you may not use this file except in compliance
  47295. * with the License. You may obtain a copy of the License at
  47296. *
  47297. * http://www.apache.org/licenses/LICENSE-2.0
  47298. *
  47299. * Unless required by applicable law or agreed to in writing,
  47300. * software distributed under the License is distributed on an
  47301. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47302. * KIND, either express or implied. See the License for the
  47303. * specific language governing permissions and limitations
  47304. * under the License.
  47305. */
  47306. /**
  47307. * DataZoom component entry
  47308. */
  47309. /*
  47310. * Licensed to the Apache Software Foundation (ASF) under one
  47311. * or more contributor license agreements. See the NOTICE file
  47312. * distributed with this work for additional information
  47313. * regarding copyright ownership. The ASF licenses this file
  47314. * to you under the Apache License, Version 2.0 (the
  47315. * "License"); you may not use this file except in compliance
  47316. * with the License. You may obtain a copy of the License at
  47317. *
  47318. * http://www.apache.org/licenses/LICENSE-2.0
  47319. *
  47320. * Unless required by applicable law or agreed to in writing,
  47321. * software distributed under the License is distributed on an
  47322. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47323. * KIND, either express or implied. See the License for the
  47324. * specific language governing permissions and limitations
  47325. * under the License.
  47326. */
  47327. var features = {};
  47328. function register$2(name, ctor) {
  47329. features[name] = ctor;
  47330. }
  47331. function get$1(name) {
  47332. return features[name];
  47333. }
  47334. /*
  47335. * Licensed to the Apache Software Foundation (ASF) under one
  47336. * or more contributor license agreements. See the NOTICE file
  47337. * distributed with this work for additional information
  47338. * regarding copyright ownership. The ASF licenses this file
  47339. * to you under the Apache License, Version 2.0 (the
  47340. * "License"); you may not use this file except in compliance
  47341. * with the License. You may obtain a copy of the License at
  47342. *
  47343. * http://www.apache.org/licenses/LICENSE-2.0
  47344. *
  47345. * Unless required by applicable law or agreed to in writing,
  47346. * software distributed under the License is distributed on an
  47347. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47348. * KIND, either express or implied. See the License for the
  47349. * specific language governing permissions and limitations
  47350. * under the License.
  47351. */
  47352. var ToolboxModel = extendComponentModel({
  47353. type: 'toolbox',
  47354. layoutMode: {
  47355. type: 'box',
  47356. ignoreSize: true
  47357. },
  47358. optionUpdated: function () {
  47359. ToolboxModel.superApply(this, 'optionUpdated', arguments);
  47360. each$1(this.option.feature, function (featureOpt, featureName) {
  47361. var Feature = get$1(featureName);
  47362. Feature && merge(featureOpt, Feature.defaultOption);
  47363. });
  47364. },
  47365. defaultOption: {
  47366. show: true,
  47367. z: 6,
  47368. zlevel: 0,
  47369. orient: 'horizontal',
  47370. left: 'right',
  47371. top: 'top',
  47372. // right
  47373. // bottom
  47374. backgroundColor: 'transparent',
  47375. borderColor: '#ccc',
  47376. borderRadius: 0,
  47377. borderWidth: 0,
  47378. padding: 5,
  47379. itemSize: 15,
  47380. itemGap: 8,
  47381. showTitle: true,
  47382. iconStyle: {
  47383. borderColor: '#666',
  47384. color: 'none'
  47385. },
  47386. emphasis: {
  47387. iconStyle: {
  47388. borderColor: '#3E98C5'
  47389. }
  47390. }
  47391. // textStyle: {},
  47392. // feature
  47393. }
  47394. });
  47395. /*
  47396. * Licensed to the Apache Software Foundation (ASF) under one
  47397. * or more contributor license agreements. See the NOTICE file
  47398. * distributed with this work for additional information
  47399. * regarding copyright ownership. The ASF licenses this file
  47400. * to you under the Apache License, Version 2.0 (the
  47401. * "License"); you may not use this file except in compliance
  47402. * with the License. You may obtain a copy of the License at
  47403. *
  47404. * http://www.apache.org/licenses/LICENSE-2.0
  47405. *
  47406. * Unless required by applicable law or agreed to in writing,
  47407. * software distributed under the License is distributed on an
  47408. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47409. * KIND, either express or implied. See the License for the
  47410. * specific language governing permissions and limitations
  47411. * under the License.
  47412. */
  47413. extendComponentView({
  47414. type: 'toolbox',
  47415. render: function (toolboxModel, ecModel, api, payload) {
  47416. var group = this.group;
  47417. group.removeAll();
  47418. if (!toolboxModel.get('show')) {
  47419. return;
  47420. }
  47421. var itemSize = +toolboxModel.get('itemSize');
  47422. var featureOpts = toolboxModel.get('feature') || {};
  47423. var features = this._features || (this._features = {});
  47424. var featureNames = [];
  47425. each$1(featureOpts, function (opt, name) {
  47426. featureNames.push(name);
  47427. });
  47428. (new DataDiffer(this._featureNames || [], featureNames))
  47429. .add(processFeature)
  47430. .update(processFeature)
  47431. .remove(curry(processFeature, null))
  47432. .execute();
  47433. // Keep for diff.
  47434. this._featureNames = featureNames;
  47435. function processFeature(newIndex, oldIndex) {
  47436. var featureName = featureNames[newIndex];
  47437. var oldName = featureNames[oldIndex];
  47438. var featureOpt = featureOpts[featureName];
  47439. var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);
  47440. var feature;
  47441. if (featureName && !oldName) { // Create
  47442. if (isUserFeatureName(featureName)) {
  47443. feature = {
  47444. model: featureModel,
  47445. onclick: featureModel.option.onclick,
  47446. featureName: featureName
  47447. };
  47448. }
  47449. else {
  47450. var Feature = get$1(featureName);
  47451. if (!Feature) {
  47452. return;
  47453. }
  47454. feature = new Feature(featureModel, ecModel, api);
  47455. }
  47456. features[featureName] = feature;
  47457. }
  47458. else {
  47459. feature = features[oldName];
  47460. // If feature does not exsit.
  47461. if (!feature) {
  47462. return;
  47463. }
  47464. feature.model = featureModel;
  47465. feature.ecModel = ecModel;
  47466. feature.api = api;
  47467. }
  47468. if (!featureName && oldName) {
  47469. feature.dispose && feature.dispose(ecModel, api);
  47470. return;
  47471. }
  47472. if (!featureModel.get('show') || feature.unusable) {
  47473. feature.remove && feature.remove(ecModel, api);
  47474. return;
  47475. }
  47476. createIconPaths(featureModel, feature, featureName);
  47477. featureModel.setIconStatus = function (iconName, status) {
  47478. var option = this.option;
  47479. var iconPaths = this.iconPaths;
  47480. option.iconStatus = option.iconStatus || {};
  47481. option.iconStatus[iconName] = status;
  47482. // FIXME
  47483. iconPaths[iconName] && iconPaths[iconName].trigger(status);
  47484. };
  47485. if (feature.render) {
  47486. feature.render(featureModel, ecModel, api, payload);
  47487. }
  47488. }
  47489. function createIconPaths(featureModel, feature, featureName) {
  47490. var iconStyleModel = featureModel.getModel('iconStyle');
  47491. var iconStyleEmphasisModel = featureModel.getModel('emphasis.iconStyle');
  47492. // If one feature has mutiple icon. they are orginaized as
  47493. // {
  47494. // icon: {
  47495. // foo: '',
  47496. // bar: ''
  47497. // },
  47498. // title: {
  47499. // foo: '',
  47500. // bar: ''
  47501. // }
  47502. // }
  47503. var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon');
  47504. var titles = featureModel.get('title') || {};
  47505. if (typeof icons === 'string') {
  47506. var icon = icons;
  47507. var title = titles;
  47508. icons = {};
  47509. titles = {};
  47510. icons[featureName] = icon;
  47511. titles[featureName] = title;
  47512. }
  47513. var iconPaths = featureModel.iconPaths = {};
  47514. each$1(icons, function (iconStr, iconName) {
  47515. var path = createIcon(
  47516. iconStr,
  47517. {},
  47518. {
  47519. x: -itemSize / 2,
  47520. y: -itemSize / 2,
  47521. width: itemSize,
  47522. height: itemSize
  47523. }
  47524. );
  47525. path.setStyle(iconStyleModel.getItemStyle());
  47526. path.hoverStyle = iconStyleEmphasisModel.getItemStyle();
  47527. setHoverStyle(path);
  47528. if (toolboxModel.get('showTitle')) {
  47529. path.__title = titles[iconName];
  47530. path.on('mouseover', function () {
  47531. // Should not reuse above hoverStyle, which might be modified.
  47532. var hoverStyle = iconStyleEmphasisModel.getItemStyle();
  47533. path.setStyle({
  47534. text: titles[iconName],
  47535. textPosition: hoverStyle.textPosition || 'bottom',
  47536. textFill: hoverStyle.fill || hoverStyle.stroke || '#000',
  47537. textAlign: hoverStyle.textAlign || 'center'
  47538. });
  47539. })
  47540. .on('mouseout', function () {
  47541. path.setStyle({
  47542. textFill: null
  47543. });
  47544. });
  47545. }
  47546. path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal');
  47547. group.add(path);
  47548. path.on('click', bind(
  47549. feature.onclick, feature, ecModel, api, iconName
  47550. ));
  47551. iconPaths[iconName] = path;
  47552. });
  47553. }
  47554. layout$2(group, toolboxModel, api);
  47555. // Render background after group is layout
  47556. // FIXME
  47557. group.add(makeBackground(group.getBoundingRect(), toolboxModel));
  47558. // Adjust icon title positions to avoid them out of screen
  47559. group.eachChild(function (icon) {
  47560. var titleText = icon.__title;
  47561. var hoverStyle = icon.hoverStyle;
  47562. // May be background element
  47563. if (hoverStyle && titleText) {
  47564. var rect = getBoundingRect(
  47565. titleText, makeFont(hoverStyle)
  47566. );
  47567. var offsetX = icon.position[0] + group.position[0];
  47568. var offsetY = icon.position[1] + group.position[1] + itemSize;
  47569. var needPutOnTop = false;
  47570. if (offsetY + rect.height > api.getHeight()) {
  47571. hoverStyle.textPosition = 'top';
  47572. needPutOnTop = true;
  47573. }
  47574. var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8);
  47575. if (offsetX + rect.width / 2 > api.getWidth()) {
  47576. hoverStyle.textPosition = ['100%', topOffset];
  47577. hoverStyle.textAlign = 'right';
  47578. }
  47579. else if (offsetX - rect.width / 2 < 0) {
  47580. hoverStyle.textPosition = [0, topOffset];
  47581. hoverStyle.textAlign = 'left';
  47582. }
  47583. }
  47584. });
  47585. },
  47586. updateView: function (toolboxModel, ecModel, api, payload) {
  47587. each$1(this._features, function (feature) {
  47588. feature.updateView && feature.updateView(feature.model, ecModel, api, payload);
  47589. });
  47590. },
  47591. // updateLayout: function (toolboxModel, ecModel, api, payload) {
  47592. // zrUtil.each(this._features, function (feature) {
  47593. // feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);
  47594. // });
  47595. // },
  47596. remove: function (ecModel, api) {
  47597. each$1(this._features, function (feature) {
  47598. feature.remove && feature.remove(ecModel, api);
  47599. });
  47600. this.group.removeAll();
  47601. },
  47602. dispose: function (ecModel, api) {
  47603. each$1(this._features, function (feature) {
  47604. feature.dispose && feature.dispose(ecModel, api);
  47605. });
  47606. }
  47607. });
  47608. function isUserFeatureName(featureName) {
  47609. return featureName.indexOf('my') === 0;
  47610. }
  47611. /*
  47612. * Licensed to the Apache Software Foundation (ASF) under one
  47613. * or more contributor license agreements. See the NOTICE file
  47614. * distributed with this work for additional information
  47615. * regarding copyright ownership. The ASF licenses this file
  47616. * to you under the Apache License, Version 2.0 (the
  47617. * "License"); you may not use this file except in compliance
  47618. * with the License. You may obtain a copy of the License at
  47619. *
  47620. * http://www.apache.org/licenses/LICENSE-2.0
  47621. *
  47622. * Unless required by applicable law or agreed to in writing,
  47623. * software distributed under the License is distributed on an
  47624. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47625. * KIND, either express or implied. See the License for the
  47626. * specific language governing permissions and limitations
  47627. * under the License.
  47628. */
  47629. /* global Uint8Array */
  47630. var saveAsImageLang = lang.toolbox.saveAsImage;
  47631. function SaveAsImage(model) {
  47632. this.model = model;
  47633. }
  47634. SaveAsImage.defaultOption = {
  47635. show: true,
  47636. icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',
  47637. title: saveAsImageLang.title,
  47638. type: 'png',
  47639. // Default use option.backgroundColor
  47640. // backgroundColor: '#fff',
  47641. name: '',
  47642. excludeComponents: ['toolbox'],
  47643. pixelRatio: 1,
  47644. lang: saveAsImageLang.lang.slice()
  47645. };
  47646. SaveAsImage.prototype.unusable = !env$1.canvasSupported;
  47647. var proto$2 = SaveAsImage.prototype;
  47648. proto$2.onclick = function (ecModel, api) {
  47649. var model = this.model;
  47650. var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';
  47651. var $a = document.createElement('a');
  47652. var type = model.get('type', true) || 'png';
  47653. $a.download = title + '.' + type;
  47654. $a.target = '_blank';
  47655. var url = api.getConnectedDataURL({
  47656. type: type,
  47657. backgroundColor: model.get('backgroundColor', true)
  47658. || ecModel.get('backgroundColor') || '#fff',
  47659. excludeComponents: model.get('excludeComponents'),
  47660. pixelRatio: model.get('pixelRatio')
  47661. });
  47662. $a.href = url;
  47663. // Chrome and Firefox
  47664. if (typeof MouseEvent === 'function' && !env$1.browser.ie && !env$1.browser.edge) {
  47665. var evt = new MouseEvent('click', {
  47666. view: window,
  47667. bubbles: true,
  47668. cancelable: false
  47669. });
  47670. $a.dispatchEvent(evt);
  47671. }
  47672. // IE
  47673. else {
  47674. if (window.navigator.msSaveOrOpenBlob) {
  47675. var bstr = atob(url.split(',')[1]);
  47676. var n = bstr.length;
  47677. var u8arr = new Uint8Array(n);
  47678. while (n--) {
  47679. u8arr[n] = bstr.charCodeAt(n);
  47680. }
  47681. var blob = new Blob([u8arr]);
  47682. window.navigator.msSaveOrOpenBlob(blob, title + '.' + type);
  47683. }
  47684. else {
  47685. var lang$$1 = model.get('lang');
  47686. var html = ''
  47687. + '<body style="margin:0;">'
  47688. + '<img src="' + url + '" style="max-width:100%;" title="' + ((lang$$1 && lang$$1[0]) || '') + '" />'
  47689. + '</body>';
  47690. var tab = window.open();
  47691. tab.document.write(html);
  47692. }
  47693. }
  47694. };
  47695. register$2(
  47696. 'saveAsImage', SaveAsImage
  47697. );
  47698. /*
  47699. * Licensed to the Apache Software Foundation (ASF) under one
  47700. * or more contributor license agreements. See the NOTICE file
  47701. * distributed with this work for additional information
  47702. * regarding copyright ownership. The ASF licenses this file
  47703. * to you under the Apache License, Version 2.0 (the
  47704. * "License"); you may not use this file except in compliance
  47705. * with the License. You may obtain a copy of the License at
  47706. *
  47707. * http://www.apache.org/licenses/LICENSE-2.0
  47708. *
  47709. * Unless required by applicable law or agreed to in writing,
  47710. * software distributed under the License is distributed on an
  47711. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47712. * KIND, either express or implied. See the License for the
  47713. * specific language governing permissions and limitations
  47714. * under the License.
  47715. */
  47716. var magicTypeLang = lang.toolbox.magicType;
  47717. function MagicType(model) {
  47718. this.model = model;
  47719. }
  47720. MagicType.defaultOption = {
  47721. show: true,
  47722. type: [],
  47723. // Icon group
  47724. icon: {
  47725. /* eslint-disable */
  47726. line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',
  47727. bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',
  47728. stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line
  47729. tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z'
  47730. /* eslint-enable */
  47731. },
  47732. // `line`, `bar`, `stack`, `tiled`
  47733. title: clone(magicTypeLang.title),
  47734. option: {},
  47735. seriesIndex: {}
  47736. };
  47737. var proto$3 = MagicType.prototype;
  47738. proto$3.getIcons = function () {
  47739. var model = this.model;
  47740. var availableIcons = model.get('icon');
  47741. var icons = {};
  47742. each$1(model.get('type'), function (type) {
  47743. if (availableIcons[type]) {
  47744. icons[type] = availableIcons[type];
  47745. }
  47746. });
  47747. return icons;
  47748. };
  47749. var seriesOptGenreator = {
  47750. 'line': function (seriesType, seriesId, seriesModel, model) {
  47751. if (seriesType === 'bar') {
  47752. return merge({
  47753. id: seriesId,
  47754. type: 'line',
  47755. // Preserve data related option
  47756. data: seriesModel.get('data'),
  47757. stack: seriesModel.get('stack'),
  47758. markPoint: seriesModel.get('markPoint'),
  47759. markLine: seriesModel.get('markLine')
  47760. }, model.get('option.line') || {}, true);
  47761. }
  47762. },
  47763. 'bar': function (seriesType, seriesId, seriesModel, model) {
  47764. if (seriesType === 'line') {
  47765. return merge({
  47766. id: seriesId,
  47767. type: 'bar',
  47768. // Preserve data related option
  47769. data: seriesModel.get('data'),
  47770. stack: seriesModel.get('stack'),
  47771. markPoint: seriesModel.get('markPoint'),
  47772. markLine: seriesModel.get('markLine')
  47773. }, model.get('option.bar') || {}, true);
  47774. }
  47775. },
  47776. 'stack': function (seriesType, seriesId, seriesModel, model) {
  47777. if (seriesType === 'line' || seriesType === 'bar') {
  47778. return merge({
  47779. id: seriesId,
  47780. stack: '__ec_magicType_stack__'
  47781. }, model.get('option.stack') || {}, true);
  47782. }
  47783. },
  47784. 'tiled': function (seriesType, seriesId, seriesModel, model) {
  47785. if (seriesType === 'line' || seriesType === 'bar') {
  47786. return merge({
  47787. id: seriesId,
  47788. stack: ''
  47789. }, model.get('option.tiled') || {}, true);
  47790. }
  47791. }
  47792. };
  47793. var radioTypes = [
  47794. ['line', 'bar'],
  47795. ['stack', 'tiled']
  47796. ];
  47797. proto$3.onclick = function (ecModel, api, type) {
  47798. var model = this.model;
  47799. var seriesIndex = model.get('seriesIndex.' + type);
  47800. // Not supported magicType
  47801. if (!seriesOptGenreator[type]) {
  47802. return;
  47803. }
  47804. var newOption = {
  47805. series: []
  47806. };
  47807. var generateNewSeriesTypes = function (seriesModel) {
  47808. var seriesType = seriesModel.subType;
  47809. var seriesId = seriesModel.id;
  47810. var newSeriesOpt = seriesOptGenreator[type](
  47811. seriesType, seriesId, seriesModel, model
  47812. );
  47813. if (newSeriesOpt) {
  47814. // PENDING If merge original option?
  47815. defaults(newSeriesOpt, seriesModel.option);
  47816. newOption.series.push(newSeriesOpt);
  47817. }
  47818. // Modify boundaryGap
  47819. var coordSys = seriesModel.coordinateSystem;
  47820. if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {
  47821. var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
  47822. if (categoryAxis) {
  47823. var axisDim = categoryAxis.dim;
  47824. var axisType = axisDim + 'Axis';
  47825. var axisModel = ecModel.queryComponents({
  47826. mainType: axisType,
  47827. index: seriesModel.get(name + 'Index'),
  47828. id: seriesModel.get(name + 'Id')
  47829. })[0];
  47830. var axisIndex = axisModel.componentIndex;
  47831. newOption[axisType] = newOption[axisType] || [];
  47832. for (var i = 0; i <= axisIndex; i++) {
  47833. newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};
  47834. }
  47835. newOption[axisType][axisIndex].boundaryGap = type === 'bar';
  47836. }
  47837. }
  47838. };
  47839. each$1(radioTypes, function (radio) {
  47840. if (indexOf(radio, type) >= 0) {
  47841. each$1(radio, function (item) {
  47842. model.setIconStatus(item, 'normal');
  47843. });
  47844. }
  47845. });
  47846. model.setIconStatus(type, 'emphasis');
  47847. ecModel.eachComponent(
  47848. {
  47849. mainType: 'series',
  47850. query: seriesIndex == null ? null : {
  47851. seriesIndex: seriesIndex
  47852. }
  47853. }, generateNewSeriesTypes
  47854. );
  47855. api.dispatchAction({
  47856. type: 'changeMagicType',
  47857. currentType: type,
  47858. newOption: newOption
  47859. });
  47860. };
  47861. registerAction({
  47862. type: 'changeMagicType',
  47863. event: 'magicTypeChanged',
  47864. update: 'prepareAndUpdate'
  47865. }, function (payload, ecModel) {
  47866. ecModel.mergeOption(payload.newOption);
  47867. });
  47868. register$2('magicType', MagicType);
  47869. /*
  47870. * Licensed to the Apache Software Foundation (ASF) under one
  47871. * or more contributor license agreements. See the NOTICE file
  47872. * distributed with this work for additional information
  47873. * regarding copyright ownership. The ASF licenses this file
  47874. * to you under the Apache License, Version 2.0 (the
  47875. * "License"); you may not use this file except in compliance
  47876. * with the License. You may obtain a copy of the License at
  47877. *
  47878. * http://www.apache.org/licenses/LICENSE-2.0
  47879. *
  47880. * Unless required by applicable law or agreed to in writing,
  47881. * software distributed under the License is distributed on an
  47882. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  47883. * KIND, either express or implied. See the License for the
  47884. * specific language governing permissions and limitations
  47885. * under the License.
  47886. */
  47887. var dataViewLang = lang.toolbox.dataView;
  47888. var BLOCK_SPLITER = new Array(60).join('-');
  47889. var ITEM_SPLITER = '\t';
  47890. /**
  47891. * Group series into two types
  47892. * 1. on category axis, like line, bar
  47893. * 2. others, like scatter, pie
  47894. * @param {module:echarts/model/Global} ecModel
  47895. * @return {Object}
  47896. * @inner
  47897. */
  47898. function groupSeries(ecModel) {
  47899. var seriesGroupByCategoryAxis = {};
  47900. var otherSeries = [];
  47901. var meta = [];
  47902. ecModel.eachRawSeries(function (seriesModel) {
  47903. var coordSys = seriesModel.coordinateSystem;
  47904. if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {
  47905. var baseAxis = coordSys.getBaseAxis();
  47906. if (baseAxis.type === 'category') {
  47907. var key = baseAxis.dim + '_' + baseAxis.index;
  47908. if (!seriesGroupByCategoryAxis[key]) {
  47909. seriesGroupByCategoryAxis[key] = {
  47910. categoryAxis: baseAxis,
  47911. valueAxis: coordSys.getOtherAxis(baseAxis),
  47912. series: []
  47913. };
  47914. meta.push({
  47915. axisDim: baseAxis.dim,
  47916. axisIndex: baseAxis.index
  47917. });
  47918. }
  47919. seriesGroupByCategoryAxis[key].series.push(seriesModel);
  47920. }
  47921. else {
  47922. otherSeries.push(seriesModel);
  47923. }
  47924. }
  47925. else {
  47926. otherSeries.push(seriesModel);
  47927. }
  47928. });
  47929. return {
  47930. seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,
  47931. other: otherSeries,
  47932. meta: meta
  47933. };
  47934. }
  47935. /**
  47936. * Assemble content of series on cateogory axis
  47937. * @param {Array.<module:echarts/model/Series>} series
  47938. * @return {string}
  47939. * @inner
  47940. */
  47941. function assembleSeriesWithCategoryAxis(series) {
  47942. var tables = [];
  47943. each$1(series, function (group, key) {
  47944. var categoryAxis = group.categoryAxis;
  47945. var valueAxis = group.valueAxis;
  47946. var valueAxisDim = valueAxis.dim;
  47947. var headers = [' '].concat(map(group.series, function (series) {
  47948. return series.name;
  47949. }));
  47950. var columns = [categoryAxis.model.getCategories()];
  47951. each$1(group.series, function (series) {
  47952. columns.push(series.getRawData().mapArray(valueAxisDim, function (val) {
  47953. return val;
  47954. }));
  47955. });
  47956. // Assemble table content
  47957. var lines = [headers.join(ITEM_SPLITER)];
  47958. for (var i = 0; i < columns[0].length; i++) {
  47959. var items = [];
  47960. for (var j = 0; j < columns.length; j++) {
  47961. items.push(columns[j][i]);
  47962. }
  47963. lines.push(items.join(ITEM_SPLITER));
  47964. }
  47965. tables.push(lines.join('\n'));
  47966. });
  47967. return tables.join('\n\n' + BLOCK_SPLITER + '\n\n');
  47968. }
  47969. /**
  47970. * Assemble content of other series
  47971. * @param {Array.<module:echarts/model/Series>} series
  47972. * @return {string}
  47973. * @inner
  47974. */
  47975. function assembleOtherSeries(series) {
  47976. return map(series, function (series) {
  47977. var data = series.getRawData();
  47978. var lines = [series.name];
  47979. var vals = [];
  47980. data.each(data.dimensions, function () {
  47981. var argLen = arguments.length;
  47982. var dataIndex = arguments[argLen - 1];
  47983. var name = data.getName(dataIndex);
  47984. for (var i = 0; i < argLen - 1; i++) {
  47985. vals[i] = arguments[i];
  47986. }
  47987. lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER));
  47988. });
  47989. return lines.join('\n');
  47990. }).join('\n\n' + BLOCK_SPLITER + '\n\n');
  47991. }
  47992. /**
  47993. * @param {module:echarts/model/Global}
  47994. * @return {Object}
  47995. * @inner
  47996. */
  47997. function getContentFromModel(ecModel) {
  47998. var result = groupSeries(ecModel);
  47999. return {
  48000. value: filter([
  48001. assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis),
  48002. assembleOtherSeries(result.other)
  48003. ], function (str) {
  48004. return str.replace(/[\n\t\s]/g, '');
  48005. }).join('\n\n' + BLOCK_SPLITER + '\n\n'),
  48006. meta: result.meta
  48007. };
  48008. }
  48009. function trim$1(str) {
  48010. return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  48011. }
  48012. /**
  48013. * If a block is tsv format
  48014. */
  48015. function isTSVFormat(block) {
  48016. // Simple method to find out if a block is tsv format
  48017. var firstLine = block.slice(0, block.indexOf('\n'));
  48018. if (firstLine.indexOf(ITEM_SPLITER) >= 0) {
  48019. return true;
  48020. }
  48021. }
  48022. var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');
  48023. /**
  48024. * @param {string} tsv
  48025. * @return {Object}
  48026. */
  48027. function parseTSVContents(tsv) {
  48028. var tsvLines = tsv.split(/\n+/g);
  48029. var headers = trim$1(tsvLines.shift()).split(itemSplitRegex);
  48030. var categories = [];
  48031. var series = map(headers, function (header) {
  48032. return {
  48033. name: header,
  48034. data: []
  48035. };
  48036. });
  48037. for (var i = 0; i < tsvLines.length; i++) {
  48038. var items = trim$1(tsvLines[i]).split(itemSplitRegex);
  48039. categories.push(items.shift());
  48040. for (var j = 0; j < items.length; j++) {
  48041. series[j] && (series[j].data[i] = items[j]);
  48042. }
  48043. }
  48044. return {
  48045. series: series,
  48046. categories: categories
  48047. };
  48048. }
  48049. /**
  48050. * @param {string} str
  48051. * @return {Array.<Object>}
  48052. * @inner
  48053. */
  48054. function parseListContents(str) {
  48055. var lines = str.split(/\n+/g);
  48056. var seriesName = trim$1(lines.shift());
  48057. var data = [];
  48058. for (var i = 0; i < lines.length; i++) {
  48059. var items = trim$1(lines[i]).split(itemSplitRegex);
  48060. var name = '';
  48061. var value;
  48062. var hasName = false;
  48063. if (isNaN(items[0])) { // First item is name
  48064. hasName = true;
  48065. name = items[0];
  48066. items = items.slice(1);
  48067. data[i] = {
  48068. name: name,
  48069. value: []
  48070. };
  48071. value = data[i].value;
  48072. }
  48073. else {
  48074. value = data[i] = [];
  48075. }
  48076. for (var j = 0; j < items.length; j++) {
  48077. value.push(+items[j]);
  48078. }
  48079. if (value.length === 1) {
  48080. hasName ? (data[i].value = value[0]) : (data[i] = value[0]);
  48081. }
  48082. }
  48083. return {
  48084. name: seriesName,
  48085. data: data
  48086. };
  48087. }
  48088. /**
  48089. * @param {string} str
  48090. * @param {Array.<Object>} blockMetaList
  48091. * @return {Object}
  48092. * @inner
  48093. */
  48094. function parseContents(str, blockMetaList) {
  48095. var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g'));
  48096. var newOption = {
  48097. series: []
  48098. };
  48099. each$1(blocks, function (block, idx) {
  48100. if (isTSVFormat(block)) {
  48101. var result = parseTSVContents(block);
  48102. var blockMeta = blockMetaList[idx];
  48103. var axisKey = blockMeta.axisDim + 'Axis';
  48104. if (blockMeta) {
  48105. newOption[axisKey] = newOption[axisKey] || [];
  48106. newOption[axisKey][blockMeta.axisIndex] = {
  48107. data: result.categories
  48108. };
  48109. newOption.series = newOption.series.concat(result.series);
  48110. }
  48111. }
  48112. else {
  48113. var result = parseListContents(block);
  48114. newOption.series.push(result);
  48115. }
  48116. });
  48117. return newOption;
  48118. }
  48119. /**
  48120. * @alias {module:echarts/component/toolbox/feature/DataView}
  48121. * @constructor
  48122. * @param {module:echarts/model/Model} model
  48123. */
  48124. function DataView(model) {
  48125. this._dom = null;
  48126. this.model = model;
  48127. }
  48128. DataView.defaultOption = {
  48129. show: true,
  48130. readOnly: false,
  48131. optionToContent: null,
  48132. contentToOption: null,
  48133. icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',
  48134. title: clone(dataViewLang.title),
  48135. lang: clone(dataViewLang.lang),
  48136. backgroundColor: '#fff',
  48137. textColor: '#000',
  48138. textareaColor: '#fff',
  48139. textareaBorderColor: '#333',
  48140. buttonColor: '#c23531',
  48141. buttonTextColor: '#fff'
  48142. };
  48143. DataView.prototype.onclick = function (ecModel, api) {
  48144. var container = api.getDom();
  48145. var model = this.model;
  48146. if (this._dom) {
  48147. container.removeChild(this._dom);
  48148. }
  48149. var root = document.createElement('div');
  48150. root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;';
  48151. root.style.backgroundColor = model.get('backgroundColor') || '#fff';
  48152. // Create elements
  48153. var header = document.createElement('h4');
  48154. var lang$$1 = model.get('lang') || [];
  48155. header.innerHTML = lang$$1[0] || model.get('title');
  48156. header.style.cssText = 'margin: 10px 20px;';
  48157. header.style.color = model.get('textColor');
  48158. var viewMain = document.createElement('div');
  48159. var textarea = document.createElement('textarea');
  48160. viewMain.style.cssText = 'display:block;width:100%;overflow:auto;';
  48161. var optionToContent = model.get('optionToContent');
  48162. var contentToOption = model.get('contentToOption');
  48163. var result = getContentFromModel(ecModel);
  48164. if (typeof optionToContent === 'function') {
  48165. var htmlOrDom = optionToContent(api.getOption());
  48166. if (typeof htmlOrDom === 'string') {
  48167. viewMain.innerHTML = htmlOrDom;
  48168. }
  48169. else if (isDom(htmlOrDom)) {
  48170. viewMain.appendChild(htmlOrDom);
  48171. }
  48172. }
  48173. else {
  48174. // Use default textarea
  48175. viewMain.appendChild(textarea);
  48176. textarea.readOnly = model.get('readOnly');
  48177. textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;';
  48178. textarea.style.color = model.get('textColor');
  48179. textarea.style.borderColor = model.get('textareaBorderColor');
  48180. textarea.style.backgroundColor = model.get('textareaColor');
  48181. textarea.value = result.value;
  48182. }
  48183. var blockMetaList = result.meta;
  48184. var buttonContainer = document.createElement('div');
  48185. buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;';
  48186. var buttonStyle = 'float:right;margin-right:20px;border:none;'
  48187. + 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';
  48188. var closeButton = document.createElement('div');
  48189. var refreshButton = document.createElement('div');
  48190. buttonStyle += ';background-color:' + model.get('buttonColor');
  48191. buttonStyle += ';color:' + model.get('buttonTextColor');
  48192. var self = this;
  48193. function close() {
  48194. container.removeChild(root);
  48195. self._dom = null;
  48196. }
  48197. addEventListener(closeButton, 'click', close);
  48198. addEventListener(refreshButton, 'click', function () {
  48199. var newOption;
  48200. try {
  48201. if (typeof contentToOption === 'function') {
  48202. newOption = contentToOption(viewMain, api.getOption());
  48203. }
  48204. else {
  48205. newOption = parseContents(textarea.value, blockMetaList);
  48206. }
  48207. }
  48208. catch (e) {
  48209. close();
  48210. throw new Error('Data view format error ' + e);
  48211. }
  48212. if (newOption) {
  48213. api.dispatchAction({
  48214. type: 'changeDataView',
  48215. newOption: newOption
  48216. });
  48217. }
  48218. close();
  48219. });
  48220. closeButton.innerHTML = lang$$1[1];
  48221. refreshButton.innerHTML = lang$$1[2];
  48222. refreshButton.style.cssText = buttonStyle;
  48223. closeButton.style.cssText = buttonStyle;
  48224. !model.get('readOnly') && buttonContainer.appendChild(refreshButton);
  48225. buttonContainer.appendChild(closeButton);
  48226. // http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea
  48227. addEventListener(textarea, 'keydown', function (e) {
  48228. if ((e.keyCode || e.which) === 9) {
  48229. // get caret position/selection
  48230. var val = this.value;
  48231. var start = this.selectionStart;
  48232. var end = this.selectionEnd;
  48233. // set textarea value to: text before caret + tab + text after caret
  48234. this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end);
  48235. // put caret at right position again
  48236. this.selectionStart = this.selectionEnd = start + 1;
  48237. // prevent the focus lose
  48238. stop(e);
  48239. }
  48240. });
  48241. root.appendChild(header);
  48242. root.appendChild(viewMain);
  48243. root.appendChild(buttonContainer);
  48244. viewMain.style.height = (container.clientHeight - 80) + 'px';
  48245. container.appendChild(root);
  48246. this._dom = root;
  48247. };
  48248. DataView.prototype.remove = function (ecModel, api) {
  48249. this._dom && api.getDom().removeChild(this._dom);
  48250. };
  48251. DataView.prototype.dispose = function (ecModel, api) {
  48252. this.remove(ecModel, api);
  48253. };
  48254. /**
  48255. * @inner
  48256. */
  48257. function tryMergeDataOption(newData, originalData) {
  48258. return map(newData, function (newVal, idx) {
  48259. var original = originalData && originalData[idx];
  48260. if (isObject$1(original) && !isArray(original)) {
  48261. if (isObject$1(newVal) && !isArray(newVal)) {
  48262. newVal = newVal.value;
  48263. }
  48264. // Original data has option
  48265. return defaults({
  48266. value: newVal
  48267. }, original);
  48268. }
  48269. else {
  48270. return newVal;
  48271. }
  48272. });
  48273. }
  48274. register$2('dataView', DataView);
  48275. registerAction({
  48276. type: 'changeDataView',
  48277. event: 'dataViewChanged',
  48278. update: 'prepareAndUpdate'
  48279. }, function (payload, ecModel) {
  48280. var newSeriesOptList = [];
  48281. each$1(payload.newOption.series, function (seriesOpt) {
  48282. var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];
  48283. if (!seriesModel) {
  48284. // New created series
  48285. // Geuss the series type
  48286. newSeriesOptList.push(extend({
  48287. // Default is scatter
  48288. type: 'scatter'
  48289. }, seriesOpt));
  48290. }
  48291. else {
  48292. var originalData = seriesModel.get('data');
  48293. newSeriesOptList.push({
  48294. name: seriesOpt.name,
  48295. data: tryMergeDataOption(seriesOpt.data, originalData)
  48296. });
  48297. }
  48298. });
  48299. ecModel.mergeOption(defaults({
  48300. series: newSeriesOptList
  48301. }, payload.newOption));
  48302. });
  48303. /*
  48304. * Licensed to the Apache Software Foundation (ASF) under one
  48305. * or more contributor license agreements. See the NOTICE file
  48306. * distributed with this work for additional information
  48307. * regarding copyright ownership. The ASF licenses this file
  48308. * to you under the Apache License, Version 2.0 (the
  48309. * "License"); you may not use this file except in compliance
  48310. * with the License. You may obtain a copy of the License at
  48311. *
  48312. * http://www.apache.org/licenses/LICENSE-2.0
  48313. *
  48314. * Unless required by applicable law or agreed to in writing,
  48315. * software distributed under the License is distributed on an
  48316. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  48317. * KIND, either express or implied. See the License for the
  48318. * specific language governing permissions and limitations
  48319. * under the License.
  48320. */
  48321. var curry$5 = curry;
  48322. var each$16 = each$1;
  48323. var map$2 = map;
  48324. var mathMin$4 = Math.min;
  48325. var mathMax$4 = Math.max;
  48326. var mathPow$2 = Math.pow;
  48327. var COVER_Z = 10000;
  48328. var UNSELECT_THRESHOLD = 6;
  48329. var MIN_RESIZE_LINE_WIDTH = 6;
  48330. var MUTEX_RESOURCE_KEY = 'globalPan';
  48331. var DIRECTION_MAP = {
  48332. w: [0, 0],
  48333. e: [0, 1],
  48334. n: [1, 0],
  48335. s: [1, 1]
  48336. };
  48337. var CURSOR_MAP = {
  48338. w: 'ew',
  48339. e: 'ew',
  48340. n: 'ns',
  48341. s: 'ns',
  48342. ne: 'nesw',
  48343. sw: 'nesw',
  48344. nw: 'nwse',
  48345. se: 'nwse'
  48346. };
  48347. var DEFAULT_BRUSH_OPT = {
  48348. brushStyle: {
  48349. lineWidth: 2,
  48350. stroke: 'rgba(0,0,0,0.3)',
  48351. fill: 'rgba(0,0,0,0.1)'
  48352. },
  48353. transformable: true,
  48354. brushMode: 'single',
  48355. removeOnClick: false
  48356. };
  48357. var baseUID = 0;
  48358. /**
  48359. * @alias module:echarts/component/helper/BrushController
  48360. * @constructor
  48361. * @mixin {module:zrender/mixin/Eventful}
  48362. * @event module:echarts/component/helper/BrushController#brush
  48363. * params:
  48364. * areas: Array.<Array>, coord relates to container group,
  48365. * If no container specified, to global.
  48366. * opt {
  48367. * isEnd: boolean,
  48368. * removeOnClick: boolean
  48369. * }
  48370. *
  48371. * @param {module:zrender/zrender~ZRender} zr
  48372. */
  48373. function BrushController(zr) {
  48374. if (__DEV__) {
  48375. assert$1(zr);
  48376. }
  48377. Eventful.call(this);
  48378. /**
  48379. * @type {module:zrender/zrender~ZRender}
  48380. * @private
  48381. */
  48382. this._zr = zr;
  48383. /**
  48384. * @type {module:zrender/container/Group}
  48385. * @readOnly
  48386. */
  48387. this.group = new Group();
  48388. /**
  48389. * Only for drawing (after enabledBrush).
  48390. * 'line', 'rect', 'polygon' or false
  48391. * If passing false/null/undefined, disable brush.
  48392. * If passing 'auto', determined by panel.defaultBrushType
  48393. * @private
  48394. * @type {string}
  48395. */
  48396. this._brushType;
  48397. /**
  48398. * Only for drawing (after enabledBrush).
  48399. *
  48400. * @private
  48401. * @type {Object}
  48402. */
  48403. this._brushOption;
  48404. /**
  48405. * @private
  48406. * @type {Object}
  48407. */
  48408. this._panels;
  48409. /**
  48410. * @private
  48411. * @type {Array.<nubmer>}
  48412. */
  48413. this._track = [];
  48414. /**
  48415. * @private
  48416. * @type {boolean}
  48417. */
  48418. this._dragging;
  48419. /**
  48420. * @private
  48421. * @type {Array}
  48422. */
  48423. this._covers = [];
  48424. /**
  48425. * @private
  48426. * @type {moudule:zrender/container/Group}
  48427. */
  48428. this._creatingCover;
  48429. /**
  48430. * `true` means global panel
  48431. * @private
  48432. * @type {module:zrender/container/Group|boolean}
  48433. */
  48434. this._creatingPanel;
  48435. /**
  48436. * @private
  48437. * @type {boolean}
  48438. */
  48439. this._enableGlobalPan;
  48440. /**
  48441. * @private
  48442. * @type {boolean}
  48443. */
  48444. if (__DEV__) {
  48445. this._mounted;
  48446. }
  48447. /**
  48448. * @private
  48449. * @type {string}
  48450. */
  48451. this._uid = 'brushController_' + baseUID++;
  48452. /**
  48453. * @private
  48454. * @type {Object}
  48455. */
  48456. this._handlers = {};
  48457. each$16(mouseHandlers, function (handler, eventName) {
  48458. this._handlers[eventName] = bind(handler, this);
  48459. }, this);
  48460. }
  48461. BrushController.prototype = {
  48462. constructor: BrushController,
  48463. /**
  48464. * If set to null/undefined/false, select disabled.
  48465. * @param {Object} brushOption
  48466. * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
  48467. * If passing false/null/undefined, disable brush.
  48468. * If passing 'auto', determined by panel.defaultBrushType.
  48469. * ('auto' can not be used in global panel)
  48470. * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
  48471. * @param {boolean} [brushOption.transformable=true]
  48472. * @param {boolean} [brushOption.removeOnClick=false]
  48473. * @param {Object} [brushOption.brushStyle]
  48474. * @param {number} [brushOption.brushStyle.width]
  48475. * @param {number} [brushOption.brushStyle.lineWidth]
  48476. * @param {string} [brushOption.brushStyle.stroke]
  48477. * @param {string} [brushOption.brushStyle.fill]
  48478. * @param {number} [brushOption.z]
  48479. */
  48480. enableBrush: function (brushOption) {
  48481. if (__DEV__) {
  48482. assert$1(this._mounted);
  48483. }
  48484. this._brushType && doDisableBrush(this);
  48485. brushOption.brushType && doEnableBrush(this, brushOption);
  48486. return this;
  48487. },
  48488. /**
  48489. * @param {Array.<Object>} panelOpts If not pass, it is global brush.
  48490. * Each items: {
  48491. * panelId, // mandatory.
  48492. * clipPath, // mandatory. function.
  48493. * isTargetByCursor, // mandatory. function.
  48494. * defaultBrushType, // optional, only used when brushType is 'auto'.
  48495. * getLinearBrushOtherExtent, // optional. function.
  48496. * }
  48497. */
  48498. setPanels: function (panelOpts) {
  48499. if (panelOpts && panelOpts.length) {
  48500. var panels = this._panels = {};
  48501. each$1(panelOpts, function (panelOpts) {
  48502. panels[panelOpts.panelId] = clone(panelOpts);
  48503. });
  48504. }
  48505. else {
  48506. this._panels = null;
  48507. }
  48508. return this;
  48509. },
  48510. /**
  48511. * @param {Object} [opt]
  48512. * @return {boolean} [opt.enableGlobalPan=false]
  48513. */
  48514. mount: function (opt) {
  48515. opt = opt || {};
  48516. if (__DEV__) {
  48517. this._mounted = true; // should be at first.
  48518. }
  48519. this._enableGlobalPan = opt.enableGlobalPan;
  48520. var thisGroup = this.group;
  48521. this._zr.add(thisGroup);
  48522. thisGroup.attr({
  48523. position: opt.position || [0, 0],
  48524. rotation: opt.rotation || 0,
  48525. scale: opt.scale || [1, 1]
  48526. });
  48527. this._transform = thisGroup.getLocalTransform();
  48528. return this;
  48529. },
  48530. eachCover: function (cb, context) {
  48531. each$16(this._covers, cb, context);
  48532. },
  48533. /**
  48534. * Update covers.
  48535. * @param {Array.<Object>} brushOptionList Like:
  48536. * [
  48537. * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
  48538. * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
  48539. * ...
  48540. * ]
  48541. * `brushType` is required in each cover info. (can not be 'auto')
  48542. * `id` is not mandatory.
  48543. * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
  48544. * If brushOptionList is null/undefined, all covers removed.
  48545. */
  48546. updateCovers: function (brushOptionList) {
  48547. if (__DEV__) {
  48548. assert$1(this._mounted);
  48549. }
  48550. brushOptionList = map(brushOptionList, function (brushOption) {
  48551. return merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);
  48552. });
  48553. var tmpIdPrefix = '\0-brush-index-';
  48554. var oldCovers = this._covers;
  48555. var newCovers = this._covers = [];
  48556. var controller = this;
  48557. var creatingCover = this._creatingCover;
  48558. (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey))
  48559. .add(addOrUpdate)
  48560. .update(addOrUpdate)
  48561. .remove(remove)
  48562. .execute();
  48563. return this;
  48564. function getKey(brushOption, index) {
  48565. return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index)
  48566. + '-' + brushOption.brushType;
  48567. }
  48568. function oldGetKey(cover, index) {
  48569. return getKey(cover.__brushOption, index);
  48570. }
  48571. function addOrUpdate(newIndex, oldIndex) {
  48572. var newBrushOption = brushOptionList[newIndex];
  48573. // Consider setOption in event listener of brushSelect,
  48574. // where updating cover when creating should be forbiden.
  48575. if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
  48576. newCovers[newIndex] = oldCovers[oldIndex];
  48577. }
  48578. else {
  48579. var cover = newCovers[newIndex] = oldIndex != null
  48580. ? (
  48581. oldCovers[oldIndex].__brushOption = newBrushOption,
  48582. oldCovers[oldIndex]
  48583. )
  48584. : endCreating(controller, createCover(controller, newBrushOption));
  48585. updateCoverAfterCreation(controller, cover);
  48586. }
  48587. }
  48588. function remove(oldIndex) {
  48589. if (oldCovers[oldIndex] !== creatingCover) {
  48590. controller.group.remove(oldCovers[oldIndex]);
  48591. }
  48592. }
  48593. },
  48594. unmount: function () {
  48595. if (__DEV__) {
  48596. if (!this._mounted) {
  48597. return;
  48598. }
  48599. }
  48600. this.enableBrush(false);
  48601. // container may 'removeAll' outside.
  48602. clearCovers(this);
  48603. this._zr.remove(this.group);
  48604. if (__DEV__) {
  48605. this._mounted = false; // should be at last.
  48606. }
  48607. return this;
  48608. },
  48609. dispose: function () {
  48610. this.unmount();
  48611. this.off();
  48612. }
  48613. };
  48614. mixin(BrushController, Eventful);
  48615. function doEnableBrush(controller, brushOption) {
  48616. var zr = controller._zr;
  48617. // Consider roam, which takes globalPan too.
  48618. if (!controller._enableGlobalPan) {
  48619. take(zr, MUTEX_RESOURCE_KEY, controller._uid);
  48620. }
  48621. each$16(controller._handlers, function (handler, eventName) {
  48622. zr.on(eventName, handler);
  48623. });
  48624. controller._brushType = brushOption.brushType;
  48625. controller._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);
  48626. }
  48627. function doDisableBrush(controller) {
  48628. var zr = controller._zr;
  48629. release(zr, MUTEX_RESOURCE_KEY, controller._uid);
  48630. each$16(controller._handlers, function (handler, eventName) {
  48631. zr.off(eventName, handler);
  48632. });
  48633. controller._brushType = controller._brushOption = null;
  48634. }
  48635. function createCover(controller, brushOption) {
  48636. var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
  48637. cover.__brushOption = brushOption;
  48638. updateZ$1(cover, brushOption);
  48639. controller.group.add(cover);
  48640. return cover;
  48641. }
  48642. function endCreating(controller, creatingCover) {
  48643. var coverRenderer = getCoverRenderer(creatingCover);
  48644. if (coverRenderer.endCreating) {
  48645. coverRenderer.endCreating(controller, creatingCover);
  48646. updateZ$1(creatingCover, creatingCover.__brushOption);
  48647. }
  48648. return creatingCover;
  48649. }
  48650. function updateCoverShape(controller, cover) {
  48651. var brushOption = cover.__brushOption;
  48652. getCoverRenderer(cover).updateCoverShape(
  48653. controller, cover, brushOption.range, brushOption
  48654. );
  48655. }
  48656. function updateZ$1(cover, brushOption) {
  48657. var z = brushOption.z;
  48658. z == null && (z = COVER_Z);
  48659. cover.traverse(function (el) {
  48660. el.z = z;
  48661. el.z2 = z; // Consider in given container.
  48662. });
  48663. }
  48664. function updateCoverAfterCreation(controller, cover) {
  48665. getCoverRenderer(cover).updateCommon(controller, cover);
  48666. updateCoverShape(controller, cover);
  48667. }
  48668. function getCoverRenderer(cover) {
  48669. return coverRenderers[cover.__brushOption.brushType];
  48670. }
  48671. // return target panel or `true` (means global panel)
  48672. function getPanelByPoint(controller, e, localCursorPoint) {
  48673. var panels = controller._panels;
  48674. if (!panels) {
  48675. return true; // Global panel
  48676. }
  48677. var panel;
  48678. var transform = controller._transform;
  48679. each$16(panels, function (pn) {
  48680. pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);
  48681. });
  48682. return panel;
  48683. }
  48684. // Return a panel or true
  48685. function getPanelByCover(controller, cover) {
  48686. var panels = controller._panels;
  48687. if (!panels) {
  48688. return true; // Global panel
  48689. }
  48690. var panelId = cover.__brushOption.panelId;
  48691. // User may give cover without coord sys info,
  48692. // which is then treated as global panel.
  48693. return panelId != null ? panels[panelId] : true;
  48694. }
  48695. function clearCovers(controller) {
  48696. var covers = controller._covers;
  48697. var originalLength = covers.length;
  48698. each$16(covers, function (cover) {
  48699. controller.group.remove(cover);
  48700. }, controller);
  48701. covers.length = 0;
  48702. return !!originalLength;
  48703. }
  48704. function trigger$1(controller, opt) {
  48705. var areas = map$2(controller._covers, function (cover) {
  48706. var brushOption = cover.__brushOption;
  48707. var range = clone(brushOption.range);
  48708. return {
  48709. brushType: brushOption.brushType,
  48710. panelId: brushOption.panelId,
  48711. range: range
  48712. };
  48713. });
  48714. controller.trigger('brush', areas, {
  48715. isEnd: !!opt.isEnd,
  48716. removeOnClick: !!opt.removeOnClick
  48717. });
  48718. }
  48719. function shouldShowCover(controller) {
  48720. var track = controller._track;
  48721. if (!track.length) {
  48722. return false;
  48723. }
  48724. var p2 = track[track.length - 1];
  48725. var p1 = track[0];
  48726. var dx = p2[0] - p1[0];
  48727. var dy = p2[1] - p1[1];
  48728. var dist = mathPow$2(dx * dx + dy * dy, 0.5);
  48729. return dist > UNSELECT_THRESHOLD;
  48730. }
  48731. function getTrackEnds(track) {
  48732. var tail = track.length - 1;
  48733. tail < 0 && (tail = 0);
  48734. return [track[0], track[tail]];
  48735. }
  48736. function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
  48737. var cover = new Group();
  48738. cover.add(new Rect({
  48739. name: 'main',
  48740. style: makeStyle(brushOption),
  48741. silent: true,
  48742. draggable: true,
  48743. cursor: 'move',
  48744. drift: curry$5(doDrift, controller, cover, 'nswe'),
  48745. ondragend: curry$5(trigger$1, controller, {isEnd: true})
  48746. }));
  48747. each$16(
  48748. edgeNames,
  48749. function (name) {
  48750. cover.add(new Rect({
  48751. name: name,
  48752. style: {opacity: 0},
  48753. draggable: true,
  48754. silent: true,
  48755. invisible: true,
  48756. drift: curry$5(doDrift, controller, cover, name),
  48757. ondragend: curry$5(trigger$1, controller, {isEnd: true})
  48758. }));
  48759. }
  48760. );
  48761. return cover;
  48762. }
  48763. function updateBaseRect(controller, cover, localRange, brushOption) {
  48764. var lineWidth = brushOption.brushStyle.lineWidth || 0;
  48765. var handleSize = mathMax$4(lineWidth, MIN_RESIZE_LINE_WIDTH);
  48766. var x = localRange[0][0];
  48767. var y = localRange[1][0];
  48768. var xa = x - lineWidth / 2;
  48769. var ya = y - lineWidth / 2;
  48770. var x2 = localRange[0][1];
  48771. var y2 = localRange[1][1];
  48772. var x2a = x2 - handleSize + lineWidth / 2;
  48773. var y2a = y2 - handleSize + lineWidth / 2;
  48774. var width = x2 - x;
  48775. var height = y2 - y;
  48776. var widtha = width + lineWidth;
  48777. var heighta = height + lineWidth;
  48778. updateRectShape(controller, cover, 'main', x, y, width, height);
  48779. if (brushOption.transformable) {
  48780. updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
  48781. updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
  48782. updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
  48783. updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);
  48784. updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
  48785. updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
  48786. updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
  48787. updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
  48788. }
  48789. }
  48790. function updateCommon(controller, cover) {
  48791. var brushOption = cover.__brushOption;
  48792. var transformable = brushOption.transformable;
  48793. var mainEl = cover.childAt(0);
  48794. mainEl.useStyle(makeStyle(brushOption));
  48795. mainEl.attr({
  48796. silent: !transformable,
  48797. cursor: transformable ? 'move' : 'default'
  48798. });
  48799. each$16(
  48800. ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'],
  48801. function (name) {
  48802. var el = cover.childOfName(name);
  48803. var globalDir = getGlobalDirection(controller, name);
  48804. el && el.attr({
  48805. silent: !transformable,
  48806. invisible: !transformable,
  48807. cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null
  48808. });
  48809. }
  48810. );
  48811. }
  48812. function updateRectShape(controller, cover, name, x, y, w, h) {
  48813. var el = cover.childOfName(name);
  48814. el && el.setShape(pointsToRect(
  48815. clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
  48816. ));
  48817. }
  48818. function makeStyle(brushOption) {
  48819. return defaults({strokeNoScale: true}, brushOption.brushStyle);
  48820. }
  48821. function formatRectRange(x, y, x2, y2) {
  48822. var min = [mathMin$4(x, x2), mathMin$4(y, y2)];
  48823. var max = [mathMax$4(x, x2), mathMax$4(y, y2)];
  48824. return [
  48825. [min[0], max[0]], // x range
  48826. [min[1], max[1]] // y range
  48827. ];
  48828. }
  48829. function getTransform$1(controller) {
  48830. return getTransform(controller.group);
  48831. }
  48832. function getGlobalDirection(controller, localDirection) {
  48833. if (localDirection.length > 1) {
  48834. localDirection = localDirection.split('');
  48835. var globalDir = [
  48836. getGlobalDirection(controller, localDirection[0]),
  48837. getGlobalDirection(controller, localDirection[1])
  48838. ];
  48839. (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
  48840. return globalDir.join('');
  48841. }
  48842. else {
  48843. var map$$1 = {w: 'left', e: 'right', n: 'top', s: 'bottom'};
  48844. var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'};
  48845. var globalDir = transformDirection(
  48846. map$$1[localDirection], getTransform$1(controller)
  48847. );
  48848. return inverseMap[globalDir];
  48849. }
  48850. }
  48851. function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
  48852. var brushOption = cover.__brushOption;
  48853. var rectRange = toRectRange(brushOption.range);
  48854. var localDelta = toLocalDelta(controller, dx, dy);
  48855. each$16(name.split(''), function (namePart) {
  48856. var ind = DIRECTION_MAP[namePart];
  48857. rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
  48858. });
  48859. brushOption.range = fromRectRange(formatRectRange(
  48860. rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]
  48861. ));
  48862. updateCoverAfterCreation(controller, cover);
  48863. trigger$1(controller, {isEnd: false});
  48864. }
  48865. function driftPolygon(controller, cover, dx, dy, e) {
  48866. var range = cover.__brushOption.range;
  48867. var localDelta = toLocalDelta(controller, dx, dy);
  48868. each$16(range, function (point) {
  48869. point[0] += localDelta[0];
  48870. point[1] += localDelta[1];
  48871. });
  48872. updateCoverAfterCreation(controller, cover);
  48873. trigger$1(controller, {isEnd: false});
  48874. }
  48875. function toLocalDelta(controller, dx, dy) {
  48876. var thisGroup = controller.group;
  48877. var localD = thisGroup.transformCoordToLocal(dx, dy);
  48878. var localZero = thisGroup.transformCoordToLocal(0, 0);
  48879. return [localD[0] - localZero[0], localD[1] - localZero[1]];
  48880. }
  48881. function clipByPanel(controller, cover, data) {
  48882. var panel = getPanelByCover(controller, cover);
  48883. return (panel && panel !== true)
  48884. ? panel.clipPath(data, controller._transform)
  48885. : clone(data);
  48886. }
  48887. function pointsToRect(points) {
  48888. var xmin = mathMin$4(points[0][0], points[1][0]);
  48889. var ymin = mathMin$4(points[0][1], points[1][1]);
  48890. var xmax = mathMax$4(points[0][0], points[1][0]);
  48891. var ymax = mathMax$4(points[0][1], points[1][1]);
  48892. return {
  48893. x: xmin,
  48894. y: ymin,
  48895. width: xmax - xmin,
  48896. height: ymax - ymin
  48897. };
  48898. }
  48899. function resetCursor(controller, e, localCursorPoint) {
  48900. // Check active
  48901. if (!controller._brushType) {
  48902. return;
  48903. }
  48904. var zr = controller._zr;
  48905. var covers = controller._covers;
  48906. var currPanel = getPanelByPoint(controller, e, localCursorPoint);
  48907. // Check whether in covers.
  48908. if (!controller._dragging) {
  48909. for (var i = 0; i < covers.length; i++) {
  48910. var brushOption = covers[i].__brushOption;
  48911. if (currPanel
  48912. && (currPanel === true || brushOption.panelId === currPanel.panelId)
  48913. && coverRenderers[brushOption.brushType].contain(
  48914. covers[i], localCursorPoint[0], localCursorPoint[1]
  48915. )
  48916. ) {
  48917. // Use cursor style set on cover.
  48918. return;
  48919. }
  48920. }
  48921. }
  48922. currPanel && zr.setCursorStyle('crosshair');
  48923. }
  48924. function preventDefault(e) {
  48925. var rawE = e.event;
  48926. rawE.preventDefault && rawE.preventDefault();
  48927. }
  48928. function mainShapeContain(cover, x, y) {
  48929. return cover.childOfName('main').contain(x, y);
  48930. }
  48931. function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
  48932. var creatingCover = controller._creatingCover;
  48933. var panel = controller._creatingPanel;
  48934. var thisBrushOption = controller._brushOption;
  48935. var eventParams;
  48936. controller._track.push(localCursorPoint.slice());
  48937. if (shouldShowCover(controller) || creatingCover) {
  48938. if (panel && !creatingCover) {
  48939. thisBrushOption.brushMode === 'single' && clearCovers(controller);
  48940. var brushOption = clone(thisBrushOption);
  48941. brushOption.brushType = determineBrushType(brushOption.brushType, panel);
  48942. brushOption.panelId = panel === true ? null : panel.panelId;
  48943. creatingCover = controller._creatingCover = createCover(controller, brushOption);
  48944. controller._covers.push(creatingCover);
  48945. }
  48946. if (creatingCover) {
  48947. var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
  48948. var coverBrushOption = creatingCover.__brushOption;
  48949. coverBrushOption.range = coverRenderer.getCreatingRange(
  48950. clipByPanel(controller, creatingCover, controller._track)
  48951. );
  48952. if (isEnd) {
  48953. endCreating(controller, creatingCover);
  48954. coverRenderer.updateCommon(controller, creatingCover);
  48955. }
  48956. updateCoverShape(controller, creatingCover);
  48957. eventParams = {isEnd: isEnd};
  48958. }
  48959. }
  48960. else if (
  48961. isEnd
  48962. && thisBrushOption.brushMode === 'single'
  48963. && thisBrushOption.removeOnClick
  48964. ) {
  48965. // Help user to remove covers easily, only by a tiny drag, in 'single' mode.
  48966. // But a single click do not clear covers, because user may have casual
  48967. // clicks (for example, click on other component and do not expect covers
  48968. // disappear).
  48969. // Only some cover removed, trigger action, but not every click trigger action.
  48970. if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {
  48971. eventParams = {isEnd: isEnd, removeOnClick: true};
  48972. }
  48973. }
  48974. return eventParams;
  48975. }
  48976. function determineBrushType(brushType, panel) {
  48977. if (brushType === 'auto') {
  48978. if (__DEV__) {
  48979. assert$1(
  48980. panel && panel.defaultBrushType,
  48981. 'MUST have defaultBrushType when brushType is "atuo"'
  48982. );
  48983. }
  48984. return panel.defaultBrushType;
  48985. }
  48986. return brushType;
  48987. }
  48988. var mouseHandlers = {
  48989. mousedown: function (e) {
  48990. if (this._dragging) {
  48991. // In case some browser do not support globalOut,
  48992. // and release mose out side the browser.
  48993. handleDragEnd.call(this, e);
  48994. }
  48995. else if (!e.target || !e.target.draggable) {
  48996. preventDefault(e);
  48997. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  48998. this._creatingCover = null;
  48999. var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);
  49000. if (panel) {
  49001. this._dragging = true;
  49002. this._track = [localCursorPoint.slice()];
  49003. }
  49004. }
  49005. },
  49006. mousemove: function (e) {
  49007. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  49008. resetCursor(this, e, localCursorPoint);
  49009. if (this._dragging) {
  49010. preventDefault(e);
  49011. var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);
  49012. eventParams && trigger$1(this, eventParams);
  49013. }
  49014. },
  49015. mouseup: handleDragEnd //,
  49016. // FIXME
  49017. // in tooltip, globalout should not be triggered.
  49018. // globalout: handleDragEnd
  49019. };
  49020. function handleDragEnd(e) {
  49021. if (this._dragging) {
  49022. preventDefault(e);
  49023. var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
  49024. var eventParams = updateCoverByMouse(this, e, localCursorPoint, true);
  49025. this._dragging = false;
  49026. this._track = [];
  49027. this._creatingCover = null;
  49028. // trigger event shoule be at final, after procedure will be nested.
  49029. eventParams && trigger$1(this, eventParams);
  49030. }
  49031. }
  49032. /**
  49033. * key: brushType
  49034. * @type {Object}
  49035. */
  49036. var coverRenderers = {
  49037. lineX: getLineRenderer(0),
  49038. lineY: getLineRenderer(1),
  49039. rect: {
  49040. createCover: function (controller, brushOption) {
  49041. return createBaseRectCover(
  49042. curry$5(
  49043. driftRect,
  49044. function (range) {
  49045. return range;
  49046. },
  49047. function (range) {
  49048. return range;
  49049. }
  49050. ),
  49051. controller,
  49052. brushOption,
  49053. ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']
  49054. );
  49055. },
  49056. getCreatingRange: function (localTrack) {
  49057. var ends = getTrackEnds(localTrack);
  49058. return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
  49059. },
  49060. updateCoverShape: function (controller, cover, localRange, brushOption) {
  49061. updateBaseRect(controller, cover, localRange, brushOption);
  49062. },
  49063. updateCommon: updateCommon,
  49064. contain: mainShapeContain
  49065. },
  49066. polygon: {
  49067. createCover: function (controller, brushOption) {
  49068. var cover = new Group();
  49069. // Do not use graphic.Polygon because graphic.Polyline do not close the
  49070. // border of the shape when drawing, which is a better experience for user.
  49071. cover.add(new Polyline({
  49072. name: 'main',
  49073. style: makeStyle(brushOption),
  49074. silent: true
  49075. }));
  49076. return cover;
  49077. },
  49078. getCreatingRange: function (localTrack) {
  49079. return localTrack;
  49080. },
  49081. endCreating: function (controller, cover) {
  49082. cover.remove(cover.childAt(0));
  49083. // Use graphic.Polygon close the shape.
  49084. cover.add(new Polygon({
  49085. name: 'main',
  49086. draggable: true,
  49087. drift: curry$5(driftPolygon, controller, cover),
  49088. ondragend: curry$5(trigger$1, controller, {isEnd: true})
  49089. }));
  49090. },
  49091. updateCoverShape: function (controller, cover, localRange, brushOption) {
  49092. cover.childAt(0).setShape({
  49093. points: clipByPanel(controller, cover, localRange)
  49094. });
  49095. },
  49096. updateCommon: updateCommon,
  49097. contain: mainShapeContain
  49098. }
  49099. };
  49100. function getLineRenderer(xyIndex) {
  49101. return {
  49102. createCover: function (controller, brushOption) {
  49103. return createBaseRectCover(
  49104. curry$5(
  49105. driftRect,
  49106. function (range) {
  49107. var rectRange = [range, [0, 100]];
  49108. xyIndex && rectRange.reverse();
  49109. return rectRange;
  49110. },
  49111. function (rectRange) {
  49112. return rectRange[xyIndex];
  49113. }
  49114. ),
  49115. controller,
  49116. brushOption,
  49117. [['w', 'e'], ['n', 's']][xyIndex]
  49118. );
  49119. },
  49120. getCreatingRange: function (localTrack) {
  49121. var ends = getTrackEnds(localTrack);
  49122. var min = mathMin$4(ends[0][xyIndex], ends[1][xyIndex]);
  49123. var max = mathMax$4(ends[0][xyIndex], ends[1][xyIndex]);
  49124. return [min, max];
  49125. },
  49126. updateCoverShape: function (controller, cover, localRange, brushOption) {
  49127. var otherExtent;
  49128. // If brushWidth not specified, fit the panel.
  49129. var panel = getPanelByCover(controller, cover);
  49130. if (panel !== true && panel.getLinearBrushOtherExtent) {
  49131. otherExtent = panel.getLinearBrushOtherExtent(
  49132. xyIndex, controller._transform
  49133. );
  49134. }
  49135. else {
  49136. var zr = controller._zr;
  49137. otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];
  49138. }
  49139. var rectRange = [localRange, otherExtent];
  49140. xyIndex && rectRange.reverse();
  49141. updateBaseRect(controller, cover, rectRange, brushOption);
  49142. },
  49143. updateCommon: updateCommon,
  49144. contain: mainShapeContain
  49145. };
  49146. }
  49147. /*
  49148. * Licensed to the Apache Software Foundation (ASF) under one
  49149. * or more contributor license agreements. See the NOTICE file
  49150. * distributed with this work for additional information
  49151. * regarding copyright ownership. The ASF licenses this file
  49152. * to you under the Apache License, Version 2.0 (the
  49153. * "License"); you may not use this file except in compliance
  49154. * with the License. You may obtain a copy of the License at
  49155. *
  49156. * http://www.apache.org/licenses/LICENSE-2.0
  49157. *
  49158. * Unless required by applicable law or agreed to in writing,
  49159. * software distributed under the License is distributed on an
  49160. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49161. * KIND, either express or implied. See the License for the
  49162. * specific language governing permissions and limitations
  49163. * under the License.
  49164. */
  49165. var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1};
  49166. /**
  49167. * Avoid that: mouse click on a elements that is over geo or graph,
  49168. * but roam is triggered.
  49169. */
  49170. function onIrrelevantElement(e, api, targetCoordSysModel) {
  49171. var model = api.getComponentByElement(e.topTarget);
  49172. // If model is axisModel, it works only if it is injected with coordinateSystem.
  49173. var coordSys = model && model.coordinateSystem;
  49174. return model
  49175. && model !== targetCoordSysModel
  49176. && !IRRELEVANT_EXCLUDES[model.mainType]
  49177. && (coordSys && coordSys.model !== targetCoordSysModel);
  49178. }
  49179. /*
  49180. * Licensed to the Apache Software Foundation (ASF) under one
  49181. * or more contributor license agreements. See the NOTICE file
  49182. * distributed with this work for additional information
  49183. * regarding copyright ownership. The ASF licenses this file
  49184. * to you under the Apache License, Version 2.0 (the
  49185. * "License"); you may not use this file except in compliance
  49186. * with the License. You may obtain a copy of the License at
  49187. *
  49188. * http://www.apache.org/licenses/LICENSE-2.0
  49189. *
  49190. * Unless required by applicable law or agreed to in writing,
  49191. * software distributed under the License is distributed on an
  49192. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49193. * KIND, either express or implied. See the License for the
  49194. * specific language governing permissions and limitations
  49195. * under the License.
  49196. */
  49197. function makeRectPanelClipPath(rect) {
  49198. rect = normalizeRect(rect);
  49199. return function (localPoints, transform) {
  49200. return clipPointsByRect(localPoints, rect);
  49201. };
  49202. }
  49203. function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {
  49204. rect = normalizeRect(rect);
  49205. return function (xyIndex) {
  49206. var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;
  49207. var brushWidth = idx ? rect.width : rect.height;
  49208. var base = idx ? rect.x : rect.y;
  49209. return [base, base + (brushWidth || 0)];
  49210. };
  49211. }
  49212. function makeRectIsTargetByCursor(rect, api, targetModel) {
  49213. rect = normalizeRect(rect);
  49214. return function (e, localCursorPoint, transform) {
  49215. return rect.contain(localCursorPoint[0], localCursorPoint[1])
  49216. && !onIrrelevantElement(e, api, targetModel);
  49217. };
  49218. }
  49219. // Consider width/height is negative.
  49220. function normalizeRect(rect) {
  49221. return BoundingRect.create(rect);
  49222. }
  49223. /*
  49224. * Licensed to the Apache Software Foundation (ASF) under one
  49225. * or more contributor license agreements. See the NOTICE file
  49226. * distributed with this work for additional information
  49227. * regarding copyright ownership. The ASF licenses this file
  49228. * to you under the Apache License, Version 2.0 (the
  49229. * "License"); you may not use this file except in compliance
  49230. * with the License. You may obtain a copy of the License at
  49231. *
  49232. * http://www.apache.org/licenses/LICENSE-2.0
  49233. *
  49234. * Unless required by applicable law or agreed to in writing,
  49235. * software distributed under the License is distributed on an
  49236. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49237. * KIND, either express or implied. See the License for the
  49238. * specific language governing permissions and limitations
  49239. * under the License.
  49240. */
  49241. var each$17 = each$1;
  49242. var indexOf$2 = indexOf;
  49243. var curry$6 = curry;
  49244. var COORD_CONVERTS = ['dataToPoint', 'pointToData'];
  49245. // FIXME
  49246. // how to genarialize to more coordinate systems.
  49247. var INCLUDE_FINDER_MAIN_TYPES = [
  49248. 'grid', 'xAxis', 'yAxis', 'geo', 'graph',
  49249. 'polar', 'radiusAxis', 'angleAxis', 'bmap'
  49250. ];
  49251. /**
  49252. * [option in constructor]:
  49253. * {
  49254. * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
  49255. * }
  49256. *
  49257. *
  49258. * [targetInfo]:
  49259. *
  49260. * There can be multiple axes in a single targetInfo. Consider the case
  49261. * of `grid` component, a targetInfo represents a grid which contains one or more
  49262. * cartesian and one or more axes. And consider the case of parallel system,
  49263. * which has multiple axes in a coordinate system.
  49264. * Can be {
  49265. * panelId: ...,
  49266. * coordSys: <a representitive cartesian in grid (first cartesian by default)>,
  49267. * coordSyses: all cartesians.
  49268. * gridModel: <grid component>
  49269. * xAxes: correspond to coordSyses on index
  49270. * yAxes: correspond to coordSyses on index
  49271. * }
  49272. * or {
  49273. * panelId: ...,
  49274. * coordSys: <geo coord sys>
  49275. * coordSyses: [<geo coord sys>]
  49276. * geoModel: <geo component>
  49277. * }
  49278. *
  49279. *
  49280. * [panelOpt]:
  49281. *
  49282. * Make from targetInfo. Input to BrushController.
  49283. * {
  49284. * panelId: ...,
  49285. * rect: ...
  49286. * }
  49287. *
  49288. *
  49289. * [area]:
  49290. *
  49291. * Generated by BrushController or user input.
  49292. * {
  49293. * panelId: Used to locate coordInfo directly. If user inpput, no panelId.
  49294. * brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y').
  49295. * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
  49296. * range: pixel range.
  49297. * coordRange: representitive coord range (the first one of coordRanges).
  49298. * coordRanges: <Array> coord ranges, used in multiple cartesian in one grid.
  49299. * }
  49300. */
  49301. /**
  49302. * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid
  49303. * Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
  49304. * @param {module:echarts/model/Global} ecModel
  49305. * @param {Object} [opt]
  49306. * @param {Array.<string>} [opt.include] include coordinate system types.
  49307. */
  49308. function BrushTargetManager(option, ecModel, opt) {
  49309. /**
  49310. * @private
  49311. * @type {Array.<Object>}
  49312. */
  49313. var targetInfoList = this._targetInfoList = [];
  49314. var info = {};
  49315. var foundCpts = parseFinder$1(ecModel, option);
  49316. each$17(targetInfoBuilders, function (builder, type) {
  49317. if (!opt || !opt.include || indexOf$2(opt.include, type) >= 0) {
  49318. builder(foundCpts, targetInfoList, info);
  49319. }
  49320. });
  49321. }
  49322. var proto$5 = BrushTargetManager.prototype;
  49323. proto$5.setOutputRanges = function (areas, ecModel) {
  49324. this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
  49325. (area.coordRanges || (area.coordRanges = [])).push(coordRange);
  49326. // area.coordRange is the first of area.coordRanges
  49327. if (!area.coordRange) {
  49328. area.coordRange = coordRange;
  49329. // In 'category' axis, coord to pixel is not reversible, so we can not
  49330. // rebuild range by coordRange accrately, which may bring trouble when
  49331. // brushing only one item. So we use __rangeOffset to rebuilding range
  49332. // by coordRange. And this it only used in brush component so it is no
  49333. // need to be adapted to coordRanges.
  49334. var result = coordConvert[area.brushType](0, coordSys, coordRange);
  49335. area.__rangeOffset = {
  49336. offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
  49337. xyMinMax: result.xyMinMax
  49338. };
  49339. }
  49340. });
  49341. };
  49342. proto$5.matchOutputRanges = function (areas, ecModel, cb) {
  49343. each$17(areas, function (area) {
  49344. var targetInfo = this.findTargetInfo(area, ecModel);
  49345. if (targetInfo && targetInfo !== true) {
  49346. each$1(
  49347. targetInfo.coordSyses,
  49348. function (coordSys) {
  49349. var result = coordConvert[area.brushType](1, coordSys, area.range);
  49350. cb(area, result.values, coordSys, ecModel);
  49351. }
  49352. );
  49353. }
  49354. }, this);
  49355. };
  49356. proto$5.setInputRanges = function (areas, ecModel) {
  49357. each$17(areas, function (area) {
  49358. var targetInfo = this.findTargetInfo(area, ecModel);
  49359. if (__DEV__) {
  49360. assert$1(
  49361. !targetInfo || targetInfo === true || area.coordRange,
  49362. 'coordRange must be specified when coord index specified.'
  49363. );
  49364. assert$1(
  49365. !targetInfo || targetInfo !== true || area.range,
  49366. 'range must be specified in global brush.'
  49367. );
  49368. }
  49369. area.range = area.range || [];
  49370. // convert coordRange to global range and set panelId.
  49371. if (targetInfo && targetInfo !== true) {
  49372. area.panelId = targetInfo.panelId;
  49373. // (1) area.range shoule always be calculate from coordRange but does
  49374. // not keep its original value, for the sake of the dataZoom scenario,
  49375. // where area.coordRange remains unchanged but area.range may be changed.
  49376. // (2) Only support converting one coordRange to pixel range in brush
  49377. // component. So do not consider `coordRanges`.
  49378. // (3) About __rangeOffset, see comment above.
  49379. var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
  49380. var rangeOffset = area.__rangeOffset;
  49381. area.range = rangeOffset
  49382. ? diffProcessor[area.brushType](
  49383. result.values,
  49384. rangeOffset.offset,
  49385. getScales(result.xyMinMax, rangeOffset.xyMinMax)
  49386. )
  49387. : result.values;
  49388. }
  49389. }, this);
  49390. };
  49391. proto$5.makePanelOpts = function (api, getDefaultBrushType) {
  49392. return map(this._targetInfoList, function (targetInfo) {
  49393. var rect = targetInfo.getPanelRect();
  49394. return {
  49395. panelId: targetInfo.panelId,
  49396. defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo),
  49397. clipPath: makeRectPanelClipPath(rect),
  49398. isTargetByCursor: makeRectIsTargetByCursor(
  49399. rect, api, targetInfo.coordSysModel
  49400. ),
  49401. getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)
  49402. };
  49403. });
  49404. };
  49405. proto$5.controlSeries = function (area, seriesModel, ecModel) {
  49406. // Check whether area is bound in coord, and series do not belong to that coord.
  49407. // If do not do this check, some brush (like lineX) will controll all axes.
  49408. var targetInfo = this.findTargetInfo(area, ecModel);
  49409. return targetInfo === true || (
  49410. targetInfo && indexOf$2(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0
  49411. );
  49412. };
  49413. /**
  49414. * If return Object, a coord found.
  49415. * If reutrn true, global found.
  49416. * Otherwise nothing found.
  49417. *
  49418. * @param {Object} area
  49419. * @param {Array} targetInfoList
  49420. * @return {Object|boolean}
  49421. */
  49422. proto$5.findTargetInfo = function (area, ecModel) {
  49423. var targetInfoList = this._targetInfoList;
  49424. var foundCpts = parseFinder$1(ecModel, area);
  49425. for (var i = 0; i < targetInfoList.length; i++) {
  49426. var targetInfo = targetInfoList[i];
  49427. var areaPanelId = area.panelId;
  49428. if (areaPanelId) {
  49429. if (targetInfo.panelId === areaPanelId) {
  49430. return targetInfo;
  49431. }
  49432. }
  49433. else {
  49434. for (var i = 0; i < targetInfoMatchers.length; i++) {
  49435. if (targetInfoMatchers[i](foundCpts, targetInfo)) {
  49436. return targetInfo;
  49437. }
  49438. }
  49439. }
  49440. }
  49441. return true;
  49442. };
  49443. function formatMinMax(minMax) {
  49444. minMax[0] > minMax[1] && minMax.reverse();
  49445. return minMax;
  49446. }
  49447. function parseFinder$1(ecModel, option) {
  49448. return parseFinder(
  49449. ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES}
  49450. );
  49451. }
  49452. var targetInfoBuilders = {
  49453. grid: function (foundCpts, targetInfoList) {
  49454. var xAxisModels = foundCpts.xAxisModels;
  49455. var yAxisModels = foundCpts.yAxisModels;
  49456. var gridModels = foundCpts.gridModels;
  49457. // Remove duplicated.
  49458. var gridModelMap = createHashMap();
  49459. var xAxesHas = {};
  49460. var yAxesHas = {};
  49461. if (!xAxisModels && !yAxisModels && !gridModels) {
  49462. return;
  49463. }
  49464. each$17(xAxisModels, function (axisModel) {
  49465. var gridModel = axisModel.axis.grid.model;
  49466. gridModelMap.set(gridModel.id, gridModel);
  49467. xAxesHas[gridModel.id] = true;
  49468. });
  49469. each$17(yAxisModels, function (axisModel) {
  49470. var gridModel = axisModel.axis.grid.model;
  49471. gridModelMap.set(gridModel.id, gridModel);
  49472. yAxesHas[gridModel.id] = true;
  49473. });
  49474. each$17(gridModels, function (gridModel) {
  49475. gridModelMap.set(gridModel.id, gridModel);
  49476. xAxesHas[gridModel.id] = true;
  49477. yAxesHas[gridModel.id] = true;
  49478. });
  49479. gridModelMap.each(function (gridModel) {
  49480. var grid = gridModel.coordinateSystem;
  49481. var cartesians = [];
  49482. each$17(grid.getCartesians(), function (cartesian, index) {
  49483. if (indexOf$2(xAxisModels, cartesian.getAxis('x').model) >= 0
  49484. || indexOf$2(yAxisModels, cartesian.getAxis('y').model) >= 0
  49485. ) {
  49486. cartesians.push(cartesian);
  49487. }
  49488. });
  49489. targetInfoList.push({
  49490. panelId: 'grid--' + gridModel.id,
  49491. gridModel: gridModel,
  49492. coordSysModel: gridModel,
  49493. // Use the first one as the representitive coordSys.
  49494. coordSys: cartesians[0],
  49495. coordSyses: cartesians,
  49496. getPanelRect: panelRectBuilder.grid,
  49497. xAxisDeclared: xAxesHas[gridModel.id],
  49498. yAxisDeclared: yAxesHas[gridModel.id]
  49499. });
  49500. });
  49501. },
  49502. geo: function (foundCpts, targetInfoList) {
  49503. each$17(foundCpts.geoModels, function (geoModel) {
  49504. var coordSys = geoModel.coordinateSystem;
  49505. targetInfoList.push({
  49506. panelId: 'geo--' + geoModel.id,
  49507. geoModel: geoModel,
  49508. coordSysModel: geoModel,
  49509. coordSys: coordSys,
  49510. coordSyses: [coordSys],
  49511. getPanelRect: panelRectBuilder.geo
  49512. });
  49513. });
  49514. }
  49515. };
  49516. var targetInfoMatchers = [
  49517. // grid
  49518. function (foundCpts, targetInfo) {
  49519. var xAxisModel = foundCpts.xAxisModel;
  49520. var yAxisModel = foundCpts.yAxisModel;
  49521. var gridModel = foundCpts.gridModel;
  49522. !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);
  49523. !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);
  49524. return gridModel && gridModel === targetInfo.gridModel;
  49525. },
  49526. // geo
  49527. function (foundCpts, targetInfo) {
  49528. var geoModel = foundCpts.geoModel;
  49529. return geoModel && geoModel === targetInfo.geoModel;
  49530. }
  49531. ];
  49532. var panelRectBuilder = {
  49533. grid: function () {
  49534. // grid is not Transformable.
  49535. return this.coordSys.grid.getRect().clone();
  49536. },
  49537. geo: function () {
  49538. var coordSys = this.coordSys;
  49539. var rect = coordSys.getBoundingRect().clone();
  49540. // geo roam and zoom transform
  49541. rect.applyTransform(getTransform(coordSys));
  49542. return rect;
  49543. }
  49544. };
  49545. var coordConvert = {
  49546. lineX: curry$6(axisConvert, 0),
  49547. lineY: curry$6(axisConvert, 1),
  49548. rect: function (to, coordSys, rangeOrCoordRange) {
  49549. var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]);
  49550. var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]);
  49551. var values = [
  49552. formatMinMax([xminymin[0], xmaxymax[0]]),
  49553. formatMinMax([xminymin[1], xmaxymax[1]])
  49554. ];
  49555. return {values: values, xyMinMax: values};
  49556. },
  49557. polygon: function (to, coordSys, rangeOrCoordRange) {
  49558. var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];
  49559. var values = map(rangeOrCoordRange, function (item) {
  49560. var p = coordSys[COORD_CONVERTS[to]](item);
  49561. xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);
  49562. xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);
  49563. xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);
  49564. xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);
  49565. return p;
  49566. });
  49567. return {values: values, xyMinMax: xyMinMax};
  49568. }
  49569. };
  49570. function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
  49571. if (__DEV__) {
  49572. assert$1(
  49573. coordSys.type === 'cartesian2d',
  49574. 'lineX/lineY brush is available only in cartesian2d.'
  49575. );
  49576. }
  49577. var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);
  49578. var values = formatMinMax(map([0, 1], function (i) {
  49579. return to
  49580. ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]))
  49581. : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));
  49582. }));
  49583. var xyMinMax = [];
  49584. xyMinMax[axisNameIndex] = values;
  49585. xyMinMax[1 - axisNameIndex] = [NaN, NaN];
  49586. return {values: values, xyMinMax: xyMinMax};
  49587. }
  49588. var diffProcessor = {
  49589. lineX: curry$6(axisDiffProcessor, 0),
  49590. lineY: curry$6(axisDiffProcessor, 1),
  49591. rect: function (values, refer, scales) {
  49592. return [
  49593. [values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]],
  49594. [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]
  49595. ];
  49596. },
  49597. polygon: function (values, refer, scales) {
  49598. return map(values, function (item, idx) {
  49599. return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];
  49600. });
  49601. }
  49602. };
  49603. function axisDiffProcessor(axisNameIndex, values, refer, scales) {
  49604. return [
  49605. values[0] - scales[axisNameIndex] * refer[0],
  49606. values[1] - scales[axisNameIndex] * refer[1]
  49607. ];
  49608. }
  49609. // We have to process scale caused by dataZoom manually,
  49610. // although it might be not accurate.
  49611. function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
  49612. var sizeCurr = getSize(xyMinMaxCurr);
  49613. var sizeOrigin = getSize(xyMinMaxOrigin);
  49614. var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
  49615. isNaN(scales[0]) && (scales[0] = 1);
  49616. isNaN(scales[1]) && (scales[1] = 1);
  49617. return scales;
  49618. }
  49619. function getSize(xyMinMax) {
  49620. return xyMinMax
  49621. ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]]
  49622. : [NaN, NaN];
  49623. }
  49624. /*
  49625. * Licensed to the Apache Software Foundation (ASF) under one
  49626. * or more contributor license agreements. See the NOTICE file
  49627. * distributed with this work for additional information
  49628. * regarding copyright ownership. The ASF licenses this file
  49629. * to you under the Apache License, Version 2.0 (the
  49630. * "License"); you may not use this file except in compliance
  49631. * with the License. You may obtain a copy of the License at
  49632. *
  49633. * http://www.apache.org/licenses/LICENSE-2.0
  49634. *
  49635. * Unless required by applicable law or agreed to in writing,
  49636. * software distributed under the License is distributed on an
  49637. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49638. * KIND, either express or implied. See the License for the
  49639. * specific language governing permissions and limitations
  49640. * under the License.
  49641. */
  49642. var each$18 = each$1;
  49643. var ATTR$2 = '\0_ec_hist_store';
  49644. /**
  49645. * @param {module:echarts/model/Global} ecModel
  49646. * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]}
  49647. */
  49648. function push(ecModel, newSnapshot) {
  49649. var store = giveStore$1(ecModel);
  49650. // If previous dataZoom can not be found,
  49651. // complete an range with current range.
  49652. each$18(newSnapshot, function (batchItem, dataZoomId) {
  49653. var i = store.length - 1;
  49654. for (; i >= 0; i--) {
  49655. var snapshot = store[i];
  49656. if (snapshot[dataZoomId]) {
  49657. break;
  49658. }
  49659. }
  49660. if (i < 0) {
  49661. // No origin range set, create one by current range.
  49662. var dataZoomModel = ecModel.queryComponents(
  49663. {mainType: 'dataZoom', subType: 'select', id: dataZoomId}
  49664. )[0];
  49665. if (dataZoomModel) {
  49666. var percentRange = dataZoomModel.getPercentRange();
  49667. store[0][dataZoomId] = {
  49668. dataZoomId: dataZoomId,
  49669. start: percentRange[0],
  49670. end: percentRange[1]
  49671. };
  49672. }
  49673. }
  49674. });
  49675. store.push(newSnapshot);
  49676. }
  49677. /**
  49678. * @param {module:echarts/model/Global} ecModel
  49679. * @return {Object} snapshot
  49680. */
  49681. function pop(ecModel) {
  49682. var store = giveStore$1(ecModel);
  49683. var head = store[store.length - 1];
  49684. store.length > 1 && store.pop();
  49685. // Find top for all dataZoom.
  49686. var snapshot = {};
  49687. each$18(head, function (batchItem, dataZoomId) {
  49688. for (var i = store.length - 1; i >= 0; i--) {
  49689. var batchItem = store[i][dataZoomId];
  49690. if (batchItem) {
  49691. snapshot[dataZoomId] = batchItem;
  49692. break;
  49693. }
  49694. }
  49695. });
  49696. return snapshot;
  49697. }
  49698. /**
  49699. * @param {module:echarts/model/Global} ecModel
  49700. */
  49701. function clear$1(ecModel) {
  49702. ecModel[ATTR$2] = null;
  49703. }
  49704. /**
  49705. * @param {module:echarts/model/Global} ecModel
  49706. * @return {number} records. always >= 1.
  49707. */
  49708. function count(ecModel) {
  49709. return giveStore$1(ecModel).length;
  49710. }
  49711. /**
  49712. * [{key: dataZoomId, value: {dataZoomId, range}}, ...]
  49713. * History length of each dataZoom may be different.
  49714. * this._history[0] is used to store origin range.
  49715. * @type {Array.<Object>}
  49716. */
  49717. function giveStore$1(ecModel) {
  49718. var store = ecModel[ATTR$2];
  49719. if (!store) {
  49720. store = ecModel[ATTR$2] = [{}];
  49721. }
  49722. return store;
  49723. }
  49724. /*
  49725. * Licensed to the Apache Software Foundation (ASF) under one
  49726. * or more contributor license agreements. See the NOTICE file
  49727. * distributed with this work for additional information
  49728. * regarding copyright ownership. The ASF licenses this file
  49729. * to you under the Apache License, Version 2.0 (the
  49730. * "License"); you may not use this file except in compliance
  49731. * with the License. You may obtain a copy of the License at
  49732. *
  49733. * http://www.apache.org/licenses/LICENSE-2.0
  49734. *
  49735. * Unless required by applicable law or agreed to in writing,
  49736. * software distributed under the License is distributed on an
  49737. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49738. * KIND, either express or implied. See the License for the
  49739. * specific language governing permissions and limitations
  49740. * under the License.
  49741. */
  49742. DataZoomModel.extend({
  49743. type: 'dataZoom.select'
  49744. });
  49745. /*
  49746. * Licensed to the Apache Software Foundation (ASF) under one
  49747. * or more contributor license agreements. See the NOTICE file
  49748. * distributed with this work for additional information
  49749. * regarding copyright ownership. The ASF licenses this file
  49750. * to you under the Apache License, Version 2.0 (the
  49751. * "License"); you may not use this file except in compliance
  49752. * with the License. You may obtain a copy of the License at
  49753. *
  49754. * http://www.apache.org/licenses/LICENSE-2.0
  49755. *
  49756. * Unless required by applicable law or agreed to in writing,
  49757. * software distributed under the License is distributed on an
  49758. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49759. * KIND, either express or implied. See the License for the
  49760. * specific language governing permissions and limitations
  49761. * under the License.
  49762. */
  49763. DataZoomView.extend({
  49764. type: 'dataZoom.select'
  49765. });
  49766. /*
  49767. * Licensed to the Apache Software Foundation (ASF) under one
  49768. * or more contributor license agreements. See the NOTICE file
  49769. * distributed with this work for additional information
  49770. * regarding copyright ownership. The ASF licenses this file
  49771. * to you under the Apache License, Version 2.0 (the
  49772. * "License"); you may not use this file except in compliance
  49773. * with the License. You may obtain a copy of the License at
  49774. *
  49775. * http://www.apache.org/licenses/LICENSE-2.0
  49776. *
  49777. * Unless required by applicable law or agreed to in writing,
  49778. * software distributed under the License is distributed on an
  49779. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49780. * KIND, either express or implied. See the License for the
  49781. * specific language governing permissions and limitations
  49782. * under the License.
  49783. */
  49784. /**
  49785. * DataZoom component entry
  49786. */
  49787. /*
  49788. * Licensed to the Apache Software Foundation (ASF) under one
  49789. * or more contributor license agreements. See the NOTICE file
  49790. * distributed with this work for additional information
  49791. * regarding copyright ownership. The ASF licenses this file
  49792. * to you under the Apache License, Version 2.0 (the
  49793. * "License"); you may not use this file except in compliance
  49794. * with the License. You may obtain a copy of the License at
  49795. *
  49796. * http://www.apache.org/licenses/LICENSE-2.0
  49797. *
  49798. * Unless required by applicable law or agreed to in writing,
  49799. * software distributed under the License is distributed on an
  49800. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  49801. * KIND, either express or implied. See the License for the
  49802. * specific language governing permissions and limitations
  49803. * under the License.
  49804. */
  49805. // Use dataZoomSelect
  49806. var dataZoomLang = lang.toolbox.dataZoom;
  49807. var each$15 = each$1;
  49808. // Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId
  49809. var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_';
  49810. function DataZoom(model, ecModel, api) {
  49811. /**
  49812. * @private
  49813. * @type {module:echarts/component/helper/BrushController}
  49814. */
  49815. (this._brushController = new BrushController(api.getZr()))
  49816. .on('brush', bind(this._onBrush, this))
  49817. .mount();
  49818. /**
  49819. * @private
  49820. * @type {boolean}
  49821. */
  49822. this._isZoomActive;
  49823. }
  49824. DataZoom.defaultOption = {
  49825. show: true,
  49826. // Icon group
  49827. icon: {
  49828. zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',
  49829. back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'
  49830. },
  49831. // `zoom`, `back`
  49832. title: clone(dataZoomLang.title)
  49833. };
  49834. var proto$4 = DataZoom.prototype;
  49835. proto$4.render = function (featureModel, ecModel, api, payload) {
  49836. this.model = featureModel;
  49837. this.ecModel = ecModel;
  49838. this.api = api;
  49839. updateZoomBtnStatus(featureModel, ecModel, this, payload, api);
  49840. updateBackBtnStatus(featureModel, ecModel);
  49841. };
  49842. proto$4.onclick = function (ecModel, api, type) {
  49843. handlers[type].call(this);
  49844. };
  49845. proto$4.remove = function (ecModel, api) {
  49846. this._brushController.unmount();
  49847. };
  49848. proto$4.dispose = function (ecModel, api) {
  49849. this._brushController.dispose();
  49850. };
  49851. /**
  49852. * @private
  49853. */
  49854. var handlers = {
  49855. zoom: function () {
  49856. var nextActive = !this._isZoomActive;
  49857. this.api.dispatchAction({
  49858. type: 'takeGlobalCursor',
  49859. key: 'dataZoomSelect',
  49860. dataZoomSelectActive: nextActive
  49861. });
  49862. },
  49863. back: function () {
  49864. this._dispatchZoomAction(pop(this.ecModel));
  49865. }
  49866. };
  49867. /**
  49868. * @private
  49869. */
  49870. proto$4._onBrush = function (areas, opt) {
  49871. if (!opt.isEnd || !areas.length) {
  49872. return;
  49873. }
  49874. var snapshot = {};
  49875. var ecModel = this.ecModel;
  49876. this._brushController.updateCovers([]); // remove cover
  49877. var brushTargetManager = new BrushTargetManager(
  49878. retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']}
  49879. );
  49880. brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
  49881. if (coordSys.type !== 'cartesian2d') {
  49882. return;
  49883. }
  49884. var brushType = area.brushType;
  49885. if (brushType === 'rect') {
  49886. setBatch('x', coordSys, coordRange[0]);
  49887. setBatch('y', coordSys, coordRange[1]);
  49888. }
  49889. else {
  49890. setBatch(({lineX: 'x', lineY: 'y'})[brushType], coordSys, coordRange);
  49891. }
  49892. });
  49893. push(ecModel, snapshot);
  49894. this._dispatchZoomAction(snapshot);
  49895. function setBatch(dimName, coordSys, minMax) {
  49896. var axis = coordSys.getAxis(dimName);
  49897. var axisModel = axis.model;
  49898. var dataZoomModel = findDataZoom(dimName, axisModel, ecModel);
  49899. // Restrict range.
  49900. var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();
  49901. if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {
  49902. minMax = sliderMove(
  49903. 0, minMax.slice(), axis.scale.getExtent(), 0,
  49904. minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan
  49905. );
  49906. }
  49907. dataZoomModel && (snapshot[dataZoomModel.id] = {
  49908. dataZoomId: dataZoomModel.id,
  49909. startValue: minMax[0],
  49910. endValue: minMax[1]
  49911. });
  49912. }
  49913. function findDataZoom(dimName, axisModel, ecModel) {
  49914. var found;
  49915. ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) {
  49916. var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);
  49917. has && (found = dzModel);
  49918. });
  49919. return found;
  49920. }
  49921. };
  49922. /**
  49923. * @private
  49924. */
  49925. proto$4._dispatchZoomAction = function (snapshot) {
  49926. var batch = [];
  49927. // Convert from hash map to array.
  49928. each$15(snapshot, function (batchItem, dataZoomId) {
  49929. batch.push(clone(batchItem));
  49930. });
  49931. batch.length && this.api.dispatchAction({
  49932. type: 'dataZoom',
  49933. from: this.uid,
  49934. batch: batch
  49935. });
  49936. };
  49937. function retrieveAxisSetting(option) {
  49938. var setting = {};
  49939. // Compatible with previous setting: null => all axis, false => no axis.
  49940. each$1(['xAxisIndex', 'yAxisIndex'], function (name) {
  49941. setting[name] = option[name];
  49942. setting[name] == null && (setting[name] = 'all');
  49943. (setting[name] === false || setting[name] === 'none') && (setting[name] = []);
  49944. });
  49945. return setting;
  49946. }
  49947. function updateBackBtnStatus(featureModel, ecModel) {
  49948. featureModel.setIconStatus(
  49949. 'back',
  49950. count(ecModel) > 1 ? 'emphasis' : 'normal'
  49951. );
  49952. }
  49953. function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {
  49954. var zoomActive = view._isZoomActive;
  49955. if (payload && payload.type === 'takeGlobalCursor') {
  49956. zoomActive = payload.key === 'dataZoomSelect'
  49957. ? payload.dataZoomSelectActive : false;
  49958. }
  49959. view._isZoomActive = zoomActive;
  49960. featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');
  49961. var brushTargetManager = new BrushTargetManager(
  49962. retrieveAxisSetting(featureModel.option), ecModel, {include: ['grid']}
  49963. );
  49964. view._brushController
  49965. .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) {
  49966. return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared)
  49967. ? 'lineX'
  49968. : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared)
  49969. ? 'lineY'
  49970. : 'rect';
  49971. }))
  49972. .enableBrush(
  49973. zoomActive
  49974. ? {
  49975. brushType: 'auto',
  49976. brushStyle: {
  49977. // FIXME user customized?
  49978. lineWidth: 0,
  49979. fill: 'rgba(0,0,0,0.2)'
  49980. }
  49981. }
  49982. : false
  49983. );
  49984. }
  49985. register$2('dataZoom', DataZoom);
  49986. // Create special dataZoom option for select
  49987. // FIXME consider the case of merge option, where axes options are not exists.
  49988. registerPreprocessor(function (option) {
  49989. if (!option) {
  49990. return;
  49991. }
  49992. var dataZoomOpts = option.dataZoom || (option.dataZoom = []);
  49993. if (!isArray(dataZoomOpts)) {
  49994. option.dataZoom = dataZoomOpts = [dataZoomOpts];
  49995. }
  49996. var toolboxOpt = option.toolbox;
  49997. if (toolboxOpt) {
  49998. // Assume there is only one toolbox
  49999. if (isArray(toolboxOpt)) {
  50000. toolboxOpt = toolboxOpt[0];
  50001. }
  50002. if (toolboxOpt && toolboxOpt.feature) {
  50003. var dataZoomOpt = toolboxOpt.feature.dataZoom;
  50004. // FIXME: If add dataZoom when setOption in merge mode,
  50005. // no axis info to be added. See `test/dataZoom-extreme.html`
  50006. addForAxis('xAxis', dataZoomOpt);
  50007. addForAxis('yAxis', dataZoomOpt);
  50008. }
  50009. }
  50010. function addForAxis(axisName, dataZoomOpt) {
  50011. if (!dataZoomOpt) {
  50012. return;
  50013. }
  50014. // Try not to modify model, because it is not merged yet.
  50015. var axisIndicesName = axisName + 'Index';
  50016. var givenAxisIndices = dataZoomOpt[axisIndicesName];
  50017. if (givenAxisIndices != null
  50018. && givenAxisIndices !== 'all'
  50019. && !isArray(givenAxisIndices)
  50020. ) {
  50021. givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices];
  50022. }
  50023. forEachComponent(axisName, function (axisOpt, axisIndex) {
  50024. if (givenAxisIndices != null
  50025. && givenAxisIndices !== 'all'
  50026. && indexOf(givenAxisIndices, axisIndex) === -1
  50027. ) {
  50028. return;
  50029. }
  50030. var newOpt = {
  50031. type: 'select',
  50032. $fromToolbox: true,
  50033. // Id for merge mapping.
  50034. id: DATA_ZOOM_ID_BASE + axisName + axisIndex
  50035. };
  50036. // FIXME
  50037. // Only support one axis now.
  50038. newOpt[axisIndicesName] = axisIndex;
  50039. dataZoomOpts.push(newOpt);
  50040. });
  50041. }
  50042. function forEachComponent(mainType, cb) {
  50043. var opts = option[mainType];
  50044. if (!isArray(opts)) {
  50045. opts = opts ? [opts] : [];
  50046. }
  50047. each$15(opts, cb);
  50048. }
  50049. });
  50050. /*
  50051. * Licensed to the Apache Software Foundation (ASF) under one
  50052. * or more contributor license agreements. See the NOTICE file
  50053. * distributed with this work for additional information
  50054. * regarding copyright ownership. The ASF licenses this file
  50055. * to you under the Apache License, Version 2.0 (the
  50056. * "License"); you may not use this file except in compliance
  50057. * with the License. You may obtain a copy of the License at
  50058. *
  50059. * http://www.apache.org/licenses/LICENSE-2.0
  50060. *
  50061. * Unless required by applicable law or agreed to in writing,
  50062. * software distributed under the License is distributed on an
  50063. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  50064. * KIND, either express or implied. See the License for the
  50065. * specific language governing permissions and limitations
  50066. * under the License.
  50067. */
  50068. var restoreLang = lang.toolbox.restore;
  50069. function Restore(model) {
  50070. this.model = model;
  50071. }
  50072. Restore.defaultOption = {
  50073. show: true,
  50074. /* eslint-disable */
  50075. icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',
  50076. /* eslint-enable */
  50077. title: restoreLang.title
  50078. };
  50079. var proto$6 = Restore.prototype;
  50080. proto$6.onclick = function (ecModel, api, type) {
  50081. clear$1(ecModel);
  50082. api.dispatchAction({
  50083. type: 'restore',
  50084. from: this.uid
  50085. });
  50086. };
  50087. register$2('restore', Restore);
  50088. registerAction(
  50089. {type: 'restore', event: 'restore', update: 'prepareAndUpdate'},
  50090. function (payload, ecModel) {
  50091. ecModel.resetOption('recreate');
  50092. }
  50093. );
  50094. /*
  50095. * Licensed to the Apache Software Foundation (ASF) under one
  50096. * or more contributor license agreements. See the NOTICE file
  50097. * distributed with this work for additional information
  50098. * regarding copyright ownership. The ASF licenses this file
  50099. * to you under the Apache License, Version 2.0 (the
  50100. * "License"); you may not use this file except in compliance
  50101. * with the License. You may obtain a copy of the License at
  50102. *
  50103. * http://www.apache.org/licenses/LICENSE-2.0
  50104. *
  50105. * Unless required by applicable law or agreed to in writing,
  50106. * software distributed under the License is distributed on an
  50107. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  50108. * KIND, either express or implied. See the License for the
  50109. * specific language governing permissions and limitations
  50110. * under the License.
  50111. */
  50112. var urn = 'urn:schemas-microsoft-com:vml';
  50113. var win = typeof window === 'undefined' ? null : window;
  50114. var vmlInited = false;
  50115. var doc = win && win.document;
  50116. function createNode(tagName) {
  50117. return doCreateNode(tagName);
  50118. }
  50119. // Avoid assign to an exported variable, for transforming to cjs.
  50120. var doCreateNode;
  50121. if (doc && !env$1.canvasSupported) {
  50122. try {
  50123. !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn);
  50124. doCreateNode = function (tagName) {
  50125. return doc.createElement('<zrvml:' + tagName + ' class="zrvml">');
  50126. };
  50127. }
  50128. catch (e) {
  50129. doCreateNode = function (tagName) {
  50130. return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">');
  50131. };
  50132. }
  50133. }
  50134. // From raphael
  50135. function initVML() {
  50136. if (vmlInited || !doc) {
  50137. return;
  50138. }
  50139. vmlInited = true;
  50140. var styleSheets = doc.styleSheets;
  50141. if (styleSheets.length < 31) {
  50142. doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)');
  50143. }
  50144. else {
  50145. // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
  50146. styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)');
  50147. }
  50148. }
  50149. // http://www.w3.org/TR/NOTE-VML
  50150. // TODO Use proxy like svg instead of overwrite brush methods
  50151. var CMD$3 = PathProxy.CMD;
  50152. var round$3 = Math.round;
  50153. var sqrt = Math.sqrt;
  50154. var abs$1 = Math.abs;
  50155. var cos = Math.cos;
  50156. var sin = Math.sin;
  50157. var mathMax$5 = Math.max;
  50158. if (!env$1.canvasSupported) {
  50159. var comma = ',';
  50160. var imageTransformPrefix = 'progid:DXImageTransform.Microsoft';
  50161. var Z = 21600;
  50162. var Z2 = Z / 2;
  50163. var ZLEVEL_BASE = 100000;
  50164. var Z_BASE = 1000;
  50165. var initRootElStyle = function (el) {
  50166. el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;';
  50167. el.coordsize = Z + ',' + Z;
  50168. el.coordorigin = '0,0';
  50169. };
  50170. var encodeHtmlAttribute = function (s) {
  50171. return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
  50172. };
  50173. var rgb2Str = function (r, g, b) {
  50174. return 'rgb(' + [r, g, b].join(',') + ')';
  50175. };
  50176. var append = function (parent, child) {
  50177. if (child && parent && child.parentNode !== parent) {
  50178. parent.appendChild(child);
  50179. }
  50180. };
  50181. var remove = function (parent, child) {
  50182. if (child && parent && child.parentNode === parent) {
  50183. parent.removeChild(child);
  50184. }
  50185. };
  50186. var getZIndex = function (zlevel, z, z2) {
  50187. // z 的取值范围为 [0, 1000]
  50188. return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2;
  50189. };
  50190. var parsePercent$3 = function (value, maxValue) {
  50191. if (typeof value === 'string') {
  50192. if (value.lastIndexOf('%') >= 0) {
  50193. return parseFloat(value) / 100 * maxValue;
  50194. }
  50195. return parseFloat(value);
  50196. }
  50197. return value;
  50198. };
  50199. /***************************************************
  50200. * PATH
  50201. **************************************************/
  50202. var setColorAndOpacity = function (el, color, opacity) {
  50203. var colorArr = parse(color);
  50204. opacity = +opacity;
  50205. if (isNaN(opacity)) {
  50206. opacity = 1;
  50207. }
  50208. if (colorArr) {
  50209. el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]);
  50210. el.opacity = opacity * colorArr[3];
  50211. }
  50212. };
  50213. var getColorAndAlpha = function (color) {
  50214. var colorArr = parse(color);
  50215. return [
  50216. rgb2Str(colorArr[0], colorArr[1], colorArr[2]),
  50217. colorArr[3]
  50218. ];
  50219. };
  50220. var updateFillNode = function (el, style, zrEl) {
  50221. // TODO pattern
  50222. var fill = style.fill;
  50223. if (fill != null) {
  50224. // Modified from excanvas
  50225. if (fill instanceof Gradient) {
  50226. var gradientType;
  50227. var angle = 0;
  50228. var focus = [0, 0];
  50229. // additional offset
  50230. var shift = 0;
  50231. // scale factor for offset
  50232. var expansion = 1;
  50233. var rect = zrEl.getBoundingRect();
  50234. var rectWidth = rect.width;
  50235. var rectHeight = rect.height;
  50236. if (fill.type === 'linear') {
  50237. gradientType = 'gradient';
  50238. var transform = zrEl.transform;
  50239. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  50240. var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight];
  50241. if (transform) {
  50242. applyTransform(p0, p0, transform);
  50243. applyTransform(p1, p1, transform);
  50244. }
  50245. var dx = p1[0] - p0[0];
  50246. var dy = p1[1] - p0[1];
  50247. angle = Math.atan2(dx, dy) * 180 / Math.PI;
  50248. // The angle should be a non-negative number.
  50249. if (angle < 0) {
  50250. angle += 360;
  50251. }
  50252. // Very small angles produce an unexpected result because they are
  50253. // converted to a scientific notation string.
  50254. if (angle < 1e-6) {
  50255. angle = 0;
  50256. }
  50257. }
  50258. else {
  50259. gradientType = 'gradientradial';
  50260. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  50261. var transform = zrEl.transform;
  50262. var scale$$1 = zrEl.scale;
  50263. var width = rectWidth;
  50264. var height = rectHeight;
  50265. focus = [
  50266. // Percent in bounding rect
  50267. (p0[0] - rect.x) / width,
  50268. (p0[1] - rect.y) / height
  50269. ];
  50270. if (transform) {
  50271. applyTransform(p0, p0, transform);
  50272. }
  50273. width /= scale$$1[0] * Z;
  50274. height /= scale$$1[1] * Z;
  50275. var dimension = mathMax$5(width, height);
  50276. shift = 2 * 0 / dimension;
  50277. expansion = 2 * fill.r / dimension - shift;
  50278. }
  50279. // We need to sort the color stops in ascending order by offset,
  50280. // otherwise IE won't interpret it correctly.
  50281. var stops = fill.colorStops.slice();
  50282. stops.sort(function (cs1, cs2) {
  50283. return cs1.offset - cs2.offset;
  50284. });
  50285. var length$$1 = stops.length;
  50286. // Color and alpha list of first and last stop
  50287. var colorAndAlphaList = [];
  50288. var colors = [];
  50289. for (var i = 0; i < length$$1; i++) {
  50290. var stop = stops[i];
  50291. var colorAndAlpha = getColorAndAlpha(stop.color);
  50292. colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]);
  50293. if (i === 0 || i === length$$1 - 1) {
  50294. colorAndAlphaList.push(colorAndAlpha);
  50295. }
  50296. }
  50297. if (length$$1 >= 2) {
  50298. var color1 = colorAndAlphaList[0][0];
  50299. var color2 = colorAndAlphaList[1][0];
  50300. var opacity1 = colorAndAlphaList[0][1] * style.opacity;
  50301. var opacity2 = colorAndAlphaList[1][1] * style.opacity;
  50302. el.type = gradientType;
  50303. el.method = 'none';
  50304. el.focus = '100%';
  50305. el.angle = angle;
  50306. el.color = color1;
  50307. el.color2 = color2;
  50308. el.colors = colors.join(',');
  50309. // When colors attribute is used, the meanings of opacity and o:opacity2
  50310. // are reversed.
  50311. el.opacity = opacity2;
  50312. // FIXME g_o_:opacity ?
  50313. el.opacity2 = opacity1;
  50314. }
  50315. if (gradientType === 'radial') {
  50316. el.focusposition = focus.join(',');
  50317. }
  50318. }
  50319. else {
  50320. // FIXME Change from Gradient fill to color fill
  50321. setColorAndOpacity(el, fill, style.opacity);
  50322. }
  50323. }
  50324. };
  50325. var updateStrokeNode = function (el, style) {
  50326. // if (style.lineJoin != null) {
  50327. // el.joinstyle = style.lineJoin;
  50328. // }
  50329. // if (style.miterLimit != null) {
  50330. // el.miterlimit = style.miterLimit * Z;
  50331. // }
  50332. // if (style.lineCap != null) {
  50333. // el.endcap = style.lineCap;
  50334. // }
  50335. if (style.lineDash != null) {
  50336. el.dashstyle = style.lineDash.join(' ');
  50337. }
  50338. if (style.stroke != null && !(style.stroke instanceof Gradient)) {
  50339. setColorAndOpacity(el, style.stroke, style.opacity);
  50340. }
  50341. };
  50342. var updateFillAndStroke = function (vmlEl, type, style, zrEl) {
  50343. var isFill = type === 'fill';
  50344. var el = vmlEl.getElementsByTagName(type)[0];
  50345. // Stroke must have lineWidth
  50346. if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) {
  50347. vmlEl[isFill ? 'filled' : 'stroked'] = 'true';
  50348. // FIXME Remove before updating, or set `colors` will throw error
  50349. if (style[type] instanceof Gradient) {
  50350. remove(vmlEl, el);
  50351. }
  50352. if (!el) {
  50353. el = createNode(type);
  50354. }
  50355. isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style);
  50356. append(vmlEl, el);
  50357. }
  50358. else {
  50359. vmlEl[isFill ? 'filled' : 'stroked'] = 'false';
  50360. remove(vmlEl, el);
  50361. }
  50362. };
  50363. var points$1 = [[], [], []];
  50364. var pathDataToString = function (path, m) {
  50365. var M = CMD$3.M;
  50366. var C = CMD$3.C;
  50367. var L = CMD$3.L;
  50368. var A = CMD$3.A;
  50369. var Q = CMD$3.Q;
  50370. var str = [];
  50371. var nPoint;
  50372. var cmdStr;
  50373. var cmd;
  50374. var i;
  50375. var xi;
  50376. var yi;
  50377. var data = path.data;
  50378. var dataLength = path.len();
  50379. for (i = 0; i < dataLength;) {
  50380. cmd = data[i++];
  50381. cmdStr = '';
  50382. nPoint = 0;
  50383. switch (cmd) {
  50384. case M:
  50385. cmdStr = ' m ';
  50386. nPoint = 1;
  50387. xi = data[i++];
  50388. yi = data[i++];
  50389. points$1[0][0] = xi;
  50390. points$1[0][1] = yi;
  50391. break;
  50392. case L:
  50393. cmdStr = ' l ';
  50394. nPoint = 1;
  50395. xi = data[i++];
  50396. yi = data[i++];
  50397. points$1[0][0] = xi;
  50398. points$1[0][1] = yi;
  50399. break;
  50400. case Q:
  50401. case C:
  50402. cmdStr = ' c ';
  50403. nPoint = 3;
  50404. var x1 = data[i++];
  50405. var y1 = data[i++];
  50406. var x2 = data[i++];
  50407. var y2 = data[i++];
  50408. var x3;
  50409. var y3;
  50410. if (cmd === Q) {
  50411. // Convert quadratic to cubic using degree elevation
  50412. x3 = x2;
  50413. y3 = y2;
  50414. x2 = (x2 + 2 * x1) / 3;
  50415. y2 = (y2 + 2 * y1) / 3;
  50416. x1 = (xi + 2 * x1) / 3;
  50417. y1 = (yi + 2 * y1) / 3;
  50418. }
  50419. else {
  50420. x3 = data[i++];
  50421. y3 = data[i++];
  50422. }
  50423. points$1[0][0] = x1;
  50424. points$1[0][1] = y1;
  50425. points$1[1][0] = x2;
  50426. points$1[1][1] = y2;
  50427. points$1[2][0] = x3;
  50428. points$1[2][1] = y3;
  50429. xi = x3;
  50430. yi = y3;
  50431. break;
  50432. case A:
  50433. var x = 0;
  50434. var y = 0;
  50435. var sx = 1;
  50436. var sy = 1;
  50437. var angle = 0;
  50438. if (m) {
  50439. // Extract SRT from matrix
  50440. x = m[4];
  50441. y = m[5];
  50442. sx = sqrt(m[0] * m[0] + m[1] * m[1]);
  50443. sy = sqrt(m[2] * m[2] + m[3] * m[3]);
  50444. angle = Math.atan2(-m[1] / sy, m[0] / sx);
  50445. }
  50446. var cx = data[i++];
  50447. var cy = data[i++];
  50448. var rx = data[i++];
  50449. var ry = data[i++];
  50450. var startAngle = data[i++] + angle;
  50451. var endAngle = data[i++] + startAngle + angle;
  50452. // FIXME
  50453. // var psi = data[i++];
  50454. i++;
  50455. var clockwise = data[i++];
  50456. var x0 = cx + cos(startAngle) * rx;
  50457. var y0 = cy + sin(startAngle) * ry;
  50458. var x1 = cx + cos(endAngle) * rx;
  50459. var y1 = cy + sin(endAngle) * ry;
  50460. var type = clockwise ? ' wa ' : ' at ';
  50461. if (Math.abs(x0 - x1) < 1e-4) {
  50462. // IE won't render arches drawn counter clockwise if x0 == x1.
  50463. if (Math.abs(endAngle - startAngle) > 1e-2) {
  50464. // Offset x0 by 1/80 of a pixel. Use something
  50465. // that can be represented in binary
  50466. if (clockwise) {
  50467. x0 += 270 / Z;
  50468. }
  50469. }
  50470. else {
  50471. // Avoid case draw full circle
  50472. if (Math.abs(y0 - cy) < 1e-4) {
  50473. if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) {
  50474. y1 -= 270 / Z;
  50475. }
  50476. else {
  50477. y1 += 270 / Z;
  50478. }
  50479. }
  50480. else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) {
  50481. x1 += 270 / Z;
  50482. }
  50483. else {
  50484. x1 -= 270 / Z;
  50485. }
  50486. }
  50487. }
  50488. str.push(
  50489. type,
  50490. round$3(((cx - rx) * sx + x) * Z - Z2), comma,
  50491. round$3(((cy - ry) * sy + y) * Z - Z2), comma,
  50492. round$3(((cx + rx) * sx + x) * Z - Z2), comma,
  50493. round$3(((cy + ry) * sy + y) * Z - Z2), comma,
  50494. round$3((x0 * sx + x) * Z - Z2), comma,
  50495. round$3((y0 * sy + y) * Z - Z2), comma,
  50496. round$3((x1 * sx + x) * Z - Z2), comma,
  50497. round$3((y1 * sy + y) * Z - Z2)
  50498. );
  50499. xi = x1;
  50500. yi = y1;
  50501. break;
  50502. case CMD$3.R:
  50503. var p0 = points$1[0];
  50504. var p1 = points$1[1];
  50505. // x0, y0
  50506. p0[0] = data[i++];
  50507. p0[1] = data[i++];
  50508. // x1, y1
  50509. p1[0] = p0[0] + data[i++];
  50510. p1[1] = p0[1] + data[i++];
  50511. if (m) {
  50512. applyTransform(p0, p0, m);
  50513. applyTransform(p1, p1, m);
  50514. }
  50515. p0[0] = round$3(p0[0] * Z - Z2);
  50516. p1[0] = round$3(p1[0] * Z - Z2);
  50517. p0[1] = round$3(p0[1] * Z - Z2);
  50518. p1[1] = round$3(p1[1] * Z - Z2);
  50519. str.push(
  50520. // x0, y0
  50521. ' m ', p0[0], comma, p0[1],
  50522. // x1, y0
  50523. ' l ', p1[0], comma, p0[1],
  50524. // x1, y1
  50525. ' l ', p1[0], comma, p1[1],
  50526. // x0, y1
  50527. ' l ', p0[0], comma, p1[1]
  50528. );
  50529. break;
  50530. case CMD$3.Z:
  50531. // FIXME Update xi, yi
  50532. str.push(' x ');
  50533. }
  50534. if (nPoint > 0) {
  50535. str.push(cmdStr);
  50536. for (var k = 0; k < nPoint; k++) {
  50537. var p = points$1[k];
  50538. m && applyTransform(p, p, m);
  50539. // 不 round 会非常慢
  50540. str.push(
  50541. round$3(p[0] * Z - Z2), comma, round$3(p[1] * Z - Z2),
  50542. k < nPoint - 1 ? comma : ''
  50543. );
  50544. }
  50545. }
  50546. }
  50547. return str.join('');
  50548. };
  50549. // Rewrite the original path method
  50550. Path.prototype.brushVML = function (vmlRoot) {
  50551. var style = this.style;
  50552. var vmlEl = this._vmlEl;
  50553. if (!vmlEl) {
  50554. vmlEl = createNode('shape');
  50555. initRootElStyle(vmlEl);
  50556. this._vmlEl = vmlEl;
  50557. }
  50558. updateFillAndStroke(vmlEl, 'fill', style, this);
  50559. updateFillAndStroke(vmlEl, 'stroke', style, this);
  50560. var m = this.transform;
  50561. var needTransform = m != null;
  50562. var strokeEl = vmlEl.getElementsByTagName('stroke')[0];
  50563. if (strokeEl) {
  50564. var lineWidth = style.lineWidth;
  50565. // Get the line scale.
  50566. // Determinant of this.m_ means how much the area is enlarged by the
  50567. // transformation. So its square root can be used as a scale factor
  50568. // for width.
  50569. if (needTransform && !style.strokeNoScale) {
  50570. var det = m[0] * m[3] - m[1] * m[2];
  50571. lineWidth *= sqrt(abs$1(det));
  50572. }
  50573. strokeEl.weight = lineWidth + 'px';
  50574. }
  50575. var path = this.path || (this.path = new PathProxy());
  50576. if (this.__dirtyPath) {
  50577. path.beginPath();
  50578. path.subPixelOptimize = false;
  50579. this.buildPath(path, this.shape);
  50580. path.toStatic();
  50581. this.__dirtyPath = false;
  50582. }
  50583. vmlEl.path = pathDataToString(path, this.transform);
  50584. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  50585. // Append to root
  50586. append(vmlRoot, vmlEl);
  50587. // Text
  50588. if (style.text != null) {
  50589. this.drawRectText(vmlRoot, this.getBoundingRect());
  50590. }
  50591. else {
  50592. this.removeRectText(vmlRoot);
  50593. }
  50594. };
  50595. Path.prototype.onRemove = function (vmlRoot) {
  50596. remove(vmlRoot, this._vmlEl);
  50597. this.removeRectText(vmlRoot);
  50598. };
  50599. Path.prototype.onAdd = function (vmlRoot) {
  50600. append(vmlRoot, this._vmlEl);
  50601. this.appendRectText(vmlRoot);
  50602. };
  50603. /***************************************************
  50604. * IMAGE
  50605. **************************************************/
  50606. var isImage = function (img) {
  50607. // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错
  50608. return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG';
  50609. // return img instanceof Image;
  50610. };
  50611. // Rewrite the original path method
  50612. ZImage.prototype.brushVML = function (vmlRoot) {
  50613. var style = this.style;
  50614. var image = style.image;
  50615. // Image original width, height
  50616. var ow;
  50617. var oh;
  50618. if (isImage(image)) {
  50619. var src = image.src;
  50620. if (src === this._imageSrc) {
  50621. ow = this._imageWidth;
  50622. oh = this._imageHeight;
  50623. }
  50624. else {
  50625. var imageRuntimeStyle = image.runtimeStyle;
  50626. var oldRuntimeWidth = imageRuntimeStyle.width;
  50627. var oldRuntimeHeight = imageRuntimeStyle.height;
  50628. imageRuntimeStyle.width = 'auto';
  50629. imageRuntimeStyle.height = 'auto';
  50630. // get the original size
  50631. ow = image.width;
  50632. oh = image.height;
  50633. // and remove overides
  50634. imageRuntimeStyle.width = oldRuntimeWidth;
  50635. imageRuntimeStyle.height = oldRuntimeHeight;
  50636. // Caching image original width, height and src
  50637. this._imageSrc = src;
  50638. this._imageWidth = ow;
  50639. this._imageHeight = oh;
  50640. }
  50641. image = src;
  50642. }
  50643. else {
  50644. if (image === this._imageSrc) {
  50645. ow = this._imageWidth;
  50646. oh = this._imageHeight;
  50647. }
  50648. }
  50649. if (!image) {
  50650. return;
  50651. }
  50652. var x = style.x || 0;
  50653. var y = style.y || 0;
  50654. var dw = style.width;
  50655. var dh = style.height;
  50656. var sw = style.sWidth;
  50657. var sh = style.sHeight;
  50658. var sx = style.sx || 0;
  50659. var sy = style.sy || 0;
  50660. var hasCrop = sw && sh;
  50661. var vmlEl = this._vmlEl;
  50662. if (!vmlEl) {
  50663. // FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。
  50664. // vmlEl = vmlCore.createNode('group');
  50665. vmlEl = doc.createElement('div');
  50666. initRootElStyle(vmlEl);
  50667. this._vmlEl = vmlEl;
  50668. }
  50669. var vmlElStyle = vmlEl.style;
  50670. var hasRotation = false;
  50671. var m;
  50672. var scaleX = 1;
  50673. var scaleY = 1;
  50674. if (this.transform) {
  50675. m = this.transform;
  50676. scaleX = sqrt(m[0] * m[0] + m[1] * m[1]);
  50677. scaleY = sqrt(m[2] * m[2] + m[3] * m[3]);
  50678. hasRotation = m[1] || m[2];
  50679. }
  50680. if (hasRotation) {
  50681. // If filters are necessary (rotation exists), create them
  50682. // filters are bog-slow, so only create them if abbsolutely necessary
  50683. // The following check doesn't account for skews (which don't exist
  50684. // in the canvas spec (yet) anyway.
  50685. // From excanvas
  50686. var p0 = [x, y];
  50687. var p1 = [x + dw, y];
  50688. var p2 = [x, y + dh];
  50689. var p3 = [x + dw, y + dh];
  50690. applyTransform(p0, p0, m);
  50691. applyTransform(p1, p1, m);
  50692. applyTransform(p2, p2, m);
  50693. applyTransform(p3, p3, m);
  50694. var maxX = mathMax$5(p0[0], p1[0], p2[0], p3[0]);
  50695. var maxY = mathMax$5(p0[1], p1[1], p2[1], p3[1]);
  50696. var transformFilter = [];
  50697. transformFilter.push('M11=', m[0] / scaleX, comma,
  50698. 'M12=', m[2] / scaleY, comma,
  50699. 'M21=', m[1] / scaleX, comma,
  50700. 'M22=', m[3] / scaleY, comma,
  50701. 'Dx=', round$3(x * scaleX + m[4]), comma,
  50702. 'Dy=', round$3(y * scaleY + m[5]));
  50703. vmlElStyle.padding = '0 ' + round$3(maxX) + 'px ' + round$3(maxY) + 'px 0';
  50704. // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用
  50705. vmlElStyle.filter = imageTransformPrefix + '.Matrix('
  50706. + transformFilter.join('') + ', SizingMethod=clip)';
  50707. }
  50708. else {
  50709. if (m) {
  50710. x = x * scaleX + m[4];
  50711. y = y * scaleY + m[5];
  50712. }
  50713. vmlElStyle.filter = '';
  50714. vmlElStyle.left = round$3(x) + 'px';
  50715. vmlElStyle.top = round$3(y) + 'px';
  50716. }
  50717. var imageEl = this._imageEl;
  50718. var cropEl = this._cropEl;
  50719. if (!imageEl) {
  50720. imageEl = doc.createElement('div');
  50721. this._imageEl = imageEl;
  50722. }
  50723. var imageELStyle = imageEl.style;
  50724. if (hasCrop) {
  50725. // Needs know image original width and height
  50726. if (!(ow && oh)) {
  50727. var tmpImage = new Image();
  50728. var self = this;
  50729. tmpImage.onload = function () {
  50730. tmpImage.onload = null;
  50731. ow = tmpImage.width;
  50732. oh = tmpImage.height;
  50733. // Adjust image width and height to fit the ratio destinationSize / sourceSize
  50734. imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px';
  50735. imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px';
  50736. // Caching image original width, height and src
  50737. self._imageWidth = ow;
  50738. self._imageHeight = oh;
  50739. self._imageSrc = image;
  50740. };
  50741. tmpImage.src = image;
  50742. }
  50743. else {
  50744. imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px';
  50745. imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px';
  50746. }
  50747. if (!cropEl) {
  50748. cropEl = doc.createElement('div');
  50749. cropEl.style.overflow = 'hidden';
  50750. this._cropEl = cropEl;
  50751. }
  50752. var cropElStyle = cropEl.style;
  50753. cropElStyle.width = round$3((dw + sx * dw / sw) * scaleX);
  50754. cropElStyle.height = round$3((dh + sy * dh / sh) * scaleY);
  50755. cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx='
  50756. + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')';
  50757. if (!cropEl.parentNode) {
  50758. vmlEl.appendChild(cropEl);
  50759. }
  50760. if (imageEl.parentNode !== cropEl) {
  50761. cropEl.appendChild(imageEl);
  50762. }
  50763. }
  50764. else {
  50765. imageELStyle.width = round$3(scaleX * dw) + 'px';
  50766. imageELStyle.height = round$3(scaleY * dh) + 'px';
  50767. vmlEl.appendChild(imageEl);
  50768. if (cropEl && cropEl.parentNode) {
  50769. vmlEl.removeChild(cropEl);
  50770. this._cropEl = null;
  50771. }
  50772. }
  50773. var filterStr = '';
  50774. var alpha = style.opacity;
  50775. if (alpha < 1) {
  50776. filterStr += '.Alpha(opacity=' + round$3(alpha * 100) + ') ';
  50777. }
  50778. filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)';
  50779. imageELStyle.filter = filterStr;
  50780. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  50781. // Append to root
  50782. append(vmlRoot, vmlEl);
  50783. // Text
  50784. if (style.text != null) {
  50785. this.drawRectText(vmlRoot, this.getBoundingRect());
  50786. }
  50787. };
  50788. ZImage.prototype.onRemove = function (vmlRoot) {
  50789. remove(vmlRoot, this._vmlEl);
  50790. this._vmlEl = null;
  50791. this._cropEl = null;
  50792. this._imageEl = null;
  50793. this.removeRectText(vmlRoot);
  50794. };
  50795. ZImage.prototype.onAdd = function (vmlRoot) {
  50796. append(vmlRoot, this._vmlEl);
  50797. this.appendRectText(vmlRoot);
  50798. };
  50799. /***************************************************
  50800. * TEXT
  50801. **************************************************/
  50802. var DEFAULT_STYLE_NORMAL = 'normal';
  50803. var fontStyleCache = {};
  50804. var fontStyleCacheCount = 0;
  50805. var MAX_FONT_CACHE_SIZE = 100;
  50806. var fontEl = document.createElement('div');
  50807. var getFontStyle = function (fontString) {
  50808. var fontStyle = fontStyleCache[fontString];
  50809. if (!fontStyle) {
  50810. // Clear cache
  50811. if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) {
  50812. fontStyleCacheCount = 0;
  50813. fontStyleCache = {};
  50814. }
  50815. var style = fontEl.style;
  50816. var fontFamily;
  50817. try {
  50818. style.font = fontString;
  50819. fontFamily = style.fontFamily.split(',')[0];
  50820. }
  50821. catch (e) {
  50822. }
  50823. fontStyle = {
  50824. style: style.fontStyle || DEFAULT_STYLE_NORMAL,
  50825. variant: style.fontVariant || DEFAULT_STYLE_NORMAL,
  50826. weight: style.fontWeight || DEFAULT_STYLE_NORMAL,
  50827. size: parseFloat(style.fontSize || 12) | 0,
  50828. family: fontFamily || 'Microsoft YaHei'
  50829. };
  50830. fontStyleCache[fontString] = fontStyle;
  50831. fontStyleCacheCount++;
  50832. }
  50833. return fontStyle;
  50834. };
  50835. var textMeasureEl;
  50836. // Overwrite measure text method
  50837. $override$1('measureText', function (text, textFont) {
  50838. var doc$$1 = doc;
  50839. if (!textMeasureEl) {
  50840. textMeasureEl = doc$$1.createElement('div');
  50841. textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;'
  50842. + 'padding:0;margin:0;border:none;white-space:pre;';
  50843. doc.body.appendChild(textMeasureEl);
  50844. }
  50845. try {
  50846. textMeasureEl.style.font = textFont;
  50847. }
  50848. catch (ex) {
  50849. // Ignore failures to set to invalid font.
  50850. }
  50851. textMeasureEl.innerHTML = '';
  50852. // Don't use innerHTML or innerText because they allow markup/whitespace.
  50853. textMeasureEl.appendChild(doc$$1.createTextNode(text));
  50854. return {
  50855. width: textMeasureEl.offsetWidth
  50856. };
  50857. });
  50858. var tmpRect$2 = new BoundingRect();
  50859. var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) {
  50860. var style = this.style;
  50861. // Optimize, avoid normalize every time.
  50862. this.__dirty && normalizeTextStyle(style, true);
  50863. var text = style.text;
  50864. // Convert to string
  50865. text != null && (text += '');
  50866. if (!text) {
  50867. return;
  50868. }
  50869. // Convert rich text to plain text. Rich text is not supported in
  50870. // IE8-, but tags in rich text template will be removed.
  50871. if (style.rich) {
  50872. var contentBlock = parseRichText(text, style);
  50873. text = [];
  50874. for (var i = 0; i < contentBlock.lines.length; i++) {
  50875. var tokens = contentBlock.lines[i].tokens;
  50876. var textLine = [];
  50877. for (var j = 0; j < tokens.length; j++) {
  50878. textLine.push(tokens[j].text);
  50879. }
  50880. text.push(textLine.join(''));
  50881. }
  50882. text = text.join('\n');
  50883. }
  50884. var x;
  50885. var y;
  50886. var align = style.textAlign;
  50887. var verticalAlign = style.textVerticalAlign;
  50888. var fontStyle = getFontStyle(style.font);
  50889. // FIXME encodeHtmlAttribute ?
  50890. var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' '
  50891. + fontStyle.size + 'px "' + fontStyle.family + '"';
  50892. textRect = textRect || getBoundingRect(
  50893. text, font, align, verticalAlign, style.textPadding, style.textLineHeight
  50894. );
  50895. // Transform rect to view space
  50896. var m = this.transform;
  50897. // Ignore transform for text in other element
  50898. if (m && !fromTextEl) {
  50899. tmpRect$2.copy(rect);
  50900. tmpRect$2.applyTransform(m);
  50901. rect = tmpRect$2;
  50902. }
  50903. if (!fromTextEl) {
  50904. var textPosition = style.textPosition;
  50905. var distance$$1 = style.textDistance;
  50906. // Text position represented by coord
  50907. if (textPosition instanceof Array) {
  50908. x = rect.x + parsePercent$3(textPosition[0], rect.width);
  50909. y = rect.y + parsePercent$3(textPosition[1], rect.height);
  50910. align = align || 'left';
  50911. }
  50912. else {
  50913. var res = adjustTextPositionOnRect(
  50914. textPosition, rect, distance$$1
  50915. );
  50916. x = res.x;
  50917. y = res.y;
  50918. // Default align and baseline when has textPosition
  50919. align = align || res.textAlign;
  50920. verticalAlign = verticalAlign || res.textVerticalAlign;
  50921. }
  50922. }
  50923. else {
  50924. x = rect.x;
  50925. y = rect.y;
  50926. }
  50927. x = adjustTextX(x, textRect.width, align);
  50928. y = adjustTextY(y, textRect.height, verticalAlign);
  50929. // Force baseline 'middle'
  50930. y += textRect.height / 2;
  50931. // var fontSize = fontStyle.size;
  50932. // 1.75 is an arbitrary number, as there is no info about the text baseline
  50933. // switch (baseline) {
  50934. // case 'hanging':
  50935. // case 'top':
  50936. // y += fontSize / 1.75;
  50937. // break;
  50938. // case 'middle':
  50939. // break;
  50940. // default:
  50941. // // case null:
  50942. // // case 'alphabetic':
  50943. // // case 'ideographic':
  50944. // // case 'bottom':
  50945. // y -= fontSize / 2.25;
  50946. // break;
  50947. // }
  50948. // switch (align) {
  50949. // case 'left':
  50950. // break;
  50951. // case 'center':
  50952. // x -= textRect.width / 2;
  50953. // break;
  50954. // case 'right':
  50955. // x -= textRect.width;
  50956. // break;
  50957. // case 'end':
  50958. // align = elementStyle.direction == 'ltr' ? 'right' : 'left';
  50959. // break;
  50960. // case 'start':
  50961. // align = elementStyle.direction == 'rtl' ? 'right' : 'left';
  50962. // break;
  50963. // default:
  50964. // align = 'left';
  50965. // }
  50966. var createNode$$1 = createNode;
  50967. var textVmlEl = this._textVmlEl;
  50968. var pathEl;
  50969. var textPathEl;
  50970. var skewEl;
  50971. if (!textVmlEl) {
  50972. textVmlEl = createNode$$1('line');
  50973. pathEl = createNode$$1('path');
  50974. textPathEl = createNode$$1('textpath');
  50975. skewEl = createNode$$1('skew');
  50976. // FIXME Why here is not cammel case
  50977. // Align 'center' seems wrong
  50978. textPathEl.style['v-text-align'] = 'left';
  50979. initRootElStyle(textVmlEl);
  50980. pathEl.textpathok = true;
  50981. textPathEl.on = true;
  50982. textVmlEl.from = '0 0';
  50983. textVmlEl.to = '1000 0.05';
  50984. append(textVmlEl, skewEl);
  50985. append(textVmlEl, pathEl);
  50986. append(textVmlEl, textPathEl);
  50987. this._textVmlEl = textVmlEl;
  50988. }
  50989. else {
  50990. // 这里是在前面 appendChild 保证顺序的前提下
  50991. skewEl = textVmlEl.firstChild;
  50992. pathEl = skewEl.nextSibling;
  50993. textPathEl = pathEl.nextSibling;
  50994. }
  50995. var coords = [x, y];
  50996. var textVmlElStyle = textVmlEl.style;
  50997. // Ignore transform for text in other element
  50998. if (m && fromTextEl) {
  50999. applyTransform(coords, coords, m);
  51000. skewEl.on = true;
  51001. skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma
  51002. + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0';
  51003. // Text position
  51004. skewEl.offset = (round$3(coords[0]) || 0) + ',' + (round$3(coords[1]) || 0);
  51005. // Left top point as origin
  51006. skewEl.origin = '0 0';
  51007. textVmlElStyle.left = '0px';
  51008. textVmlElStyle.top = '0px';
  51009. }
  51010. else {
  51011. skewEl.on = false;
  51012. textVmlElStyle.left = round$3(x) + 'px';
  51013. textVmlElStyle.top = round$3(y) + 'px';
  51014. }
  51015. textPathEl.string = encodeHtmlAttribute(text);
  51016. // TODO
  51017. try {
  51018. textPathEl.style.font = font;
  51019. }
  51020. // Error font format
  51021. catch (e) {}
  51022. updateFillAndStroke(textVmlEl, 'fill', {
  51023. fill: style.textFill,
  51024. opacity: style.opacity
  51025. }, this);
  51026. updateFillAndStroke(textVmlEl, 'stroke', {
  51027. stroke: style.textStroke,
  51028. opacity: style.opacity,
  51029. lineDash: style.lineDash
  51030. }, this);
  51031. textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  51032. // Attached to root
  51033. append(vmlRoot, textVmlEl);
  51034. };
  51035. var removeRectText = function (vmlRoot) {
  51036. remove(vmlRoot, this._textVmlEl);
  51037. this._textVmlEl = null;
  51038. };
  51039. var appendRectText = function (vmlRoot) {
  51040. append(vmlRoot, this._textVmlEl);
  51041. };
  51042. var list = [RectText, Displayable, ZImage, Path, Text];
  51043. // In case Displayable has been mixed in RectText
  51044. for (var i$1 = 0; i$1 < list.length; i$1++) {
  51045. var proto$7 = list[i$1].prototype;
  51046. proto$7.drawRectText = drawRectText;
  51047. proto$7.removeRectText = removeRectText;
  51048. proto$7.appendRectText = appendRectText;
  51049. }
  51050. Text.prototype.brushVML = function (vmlRoot) {
  51051. var style = this.style;
  51052. if (style.text != null) {
  51053. this.drawRectText(vmlRoot, {
  51054. x: style.x || 0, y: style.y || 0,
  51055. width: 0, height: 0
  51056. }, this.getBoundingRect(), true);
  51057. }
  51058. else {
  51059. this.removeRectText(vmlRoot);
  51060. }
  51061. };
  51062. Text.prototype.onRemove = function (vmlRoot) {
  51063. this.removeRectText(vmlRoot);
  51064. };
  51065. Text.prototype.onAdd = function (vmlRoot) {
  51066. this.appendRectText(vmlRoot);
  51067. };
  51068. }
  51069. /**
  51070. * VML Painter.
  51071. *
  51072. * @module zrender/vml/Painter
  51073. */
  51074. function parseInt10$1(val) {
  51075. return parseInt(val, 10);
  51076. }
  51077. /**
  51078. * @alias module:zrender/vml/Painter
  51079. */
  51080. function VMLPainter(root, storage) {
  51081. initVML();
  51082. this.root = root;
  51083. this.storage = storage;
  51084. var vmlViewport = document.createElement('div');
  51085. var vmlRoot = document.createElement('div');
  51086. vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;';
  51087. vmlRoot.style.cssText = 'position:absolute;left:0;top:0;';
  51088. root.appendChild(vmlViewport);
  51089. this._vmlRoot = vmlRoot;
  51090. this._vmlViewport = vmlViewport;
  51091. this.resize();
  51092. // Modify storage
  51093. var oldDelFromStorage = storage.delFromStorage;
  51094. var oldAddToStorage = storage.addToStorage;
  51095. storage.delFromStorage = function (el) {
  51096. oldDelFromStorage.call(storage, el);
  51097. if (el) {
  51098. el.onRemove && el.onRemove(vmlRoot);
  51099. }
  51100. };
  51101. storage.addToStorage = function (el) {
  51102. // Displayable already has a vml node
  51103. el.onAdd && el.onAdd(vmlRoot);
  51104. oldAddToStorage.call(storage, el);
  51105. };
  51106. this._firstPaint = true;
  51107. }
  51108. VMLPainter.prototype = {
  51109. constructor: VMLPainter,
  51110. getType: function () {
  51111. return 'vml';
  51112. },
  51113. /**
  51114. * @return {HTMLDivElement}
  51115. */
  51116. getViewportRoot: function () {
  51117. return this._vmlViewport;
  51118. },
  51119. getViewportRootOffset: function () {
  51120. var viewportRoot = this.getViewportRoot();
  51121. if (viewportRoot) {
  51122. return {
  51123. offsetLeft: viewportRoot.offsetLeft || 0,
  51124. offsetTop: viewportRoot.offsetTop || 0
  51125. };
  51126. }
  51127. },
  51128. /**
  51129. * 刷新
  51130. */
  51131. refresh: function () {
  51132. var list = this.storage.getDisplayList(true, true);
  51133. this._paintList(list);
  51134. },
  51135. _paintList: function (list) {
  51136. var vmlRoot = this._vmlRoot;
  51137. for (var i = 0; i < list.length; i++) {
  51138. var el = list[i];
  51139. if (el.invisible || el.ignore) {
  51140. if (!el.__alreadyNotVisible) {
  51141. el.onRemove(vmlRoot);
  51142. }
  51143. // Set as already invisible
  51144. el.__alreadyNotVisible = true;
  51145. }
  51146. else {
  51147. if (el.__alreadyNotVisible) {
  51148. el.onAdd(vmlRoot);
  51149. }
  51150. el.__alreadyNotVisible = false;
  51151. if (el.__dirty) {
  51152. el.beforeBrush && el.beforeBrush();
  51153. (el.brushVML || el.brush).call(el, vmlRoot);
  51154. el.afterBrush && el.afterBrush();
  51155. }
  51156. }
  51157. el.__dirty = false;
  51158. }
  51159. if (this._firstPaint) {
  51160. // Detached from document at first time
  51161. // to avoid page refreshing too many times
  51162. // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变
  51163. this._vmlViewport.appendChild(vmlRoot);
  51164. this._firstPaint = false;
  51165. }
  51166. },
  51167. resize: function (width, height) {
  51168. var width = width == null ? this._getWidth() : width;
  51169. var height = height == null ? this._getHeight() : height;
  51170. if (this._width !== width || this._height !== height) {
  51171. this._width = width;
  51172. this._height = height;
  51173. var vmlViewportStyle = this._vmlViewport.style;
  51174. vmlViewportStyle.width = width + 'px';
  51175. vmlViewportStyle.height = height + 'px';
  51176. }
  51177. },
  51178. dispose: function () {
  51179. this.root.innerHTML = '';
  51180. this._vmlRoot =
  51181. this._vmlViewport =
  51182. this.storage = null;
  51183. },
  51184. getWidth: function () {
  51185. return this._width;
  51186. },
  51187. getHeight: function () {
  51188. return this._height;
  51189. },
  51190. clear: function () {
  51191. if (this._vmlViewport) {
  51192. this.root.removeChild(this._vmlViewport);
  51193. }
  51194. },
  51195. _getWidth: function () {
  51196. var root = this.root;
  51197. var stl = root.currentStyle;
  51198. return ((root.clientWidth || parseInt10$1(stl.width))
  51199. - parseInt10$1(stl.paddingLeft)
  51200. - parseInt10$1(stl.paddingRight)) | 0;
  51201. },
  51202. _getHeight: function () {
  51203. var root = this.root;
  51204. var stl = root.currentStyle;
  51205. return ((root.clientHeight || parseInt10$1(stl.height))
  51206. - parseInt10$1(stl.paddingTop)
  51207. - parseInt10$1(stl.paddingBottom)) | 0;
  51208. }
  51209. };
  51210. // Not supported methods
  51211. function createMethodNotSupport(method) {
  51212. return function () {
  51213. zrLog('In IE8.0 VML mode painter not support method "' + method + '"');
  51214. };
  51215. }
  51216. // Unsupported methods
  51217. each$1([
  51218. 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers',
  51219. 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'
  51220. ], function (name) {
  51221. VMLPainter.prototype[name] = createMethodNotSupport(name);
  51222. });
  51223. registerPainter('vml', VMLPainter);
  51224. var svgURI = 'http://www.w3.org/2000/svg';
  51225. function createElement(name) {
  51226. return document.createElementNS(svgURI, name);
  51227. }
  51228. // TODO
  51229. // 1. shadow
  51230. // 2. Image: sx, sy, sw, sh
  51231. var CMD$4 = PathProxy.CMD;
  51232. var arrayJoin = Array.prototype.join;
  51233. var NONE = 'none';
  51234. var mathRound = Math.round;
  51235. var mathSin$3 = Math.sin;
  51236. var mathCos$3 = Math.cos;
  51237. var PI$3 = Math.PI;
  51238. var PI2$5 = Math.PI * 2;
  51239. var degree = 180 / PI$3;
  51240. var EPSILON$4 = 1e-4;
  51241. function round4(val) {
  51242. return mathRound(val * 1e4) / 1e4;
  51243. }
  51244. function isAroundZero$1(val) {
  51245. return val < EPSILON$4 && val > -EPSILON$4;
  51246. }
  51247. function pathHasFill(style, isText) {
  51248. var fill = isText ? style.textFill : style.fill;
  51249. return fill != null && fill !== NONE;
  51250. }
  51251. function pathHasStroke(style, isText) {
  51252. var stroke = isText ? style.textStroke : style.stroke;
  51253. return stroke != null && stroke !== NONE;
  51254. }
  51255. function setTransform(svgEl, m) {
  51256. if (m) {
  51257. attr(svgEl, 'transform', 'matrix(' + arrayJoin.call(m, ',') + ')');
  51258. }
  51259. }
  51260. function attr(el, key, val) {
  51261. if (!val || val.type !== 'linear' && val.type !== 'radial') {
  51262. // Don't set attribute for gradient, since it need new dom nodes
  51263. el.setAttribute(key, val);
  51264. }
  51265. }
  51266. function attrXLink(el, key, val) {
  51267. el.setAttributeNS('http://www.w3.org/1999/xlink', key, val);
  51268. }
  51269. function bindStyle(svgEl, style, isText, el) {
  51270. if (pathHasFill(style, isText)) {
  51271. var fill = isText ? style.textFill : style.fill;
  51272. fill = fill === 'transparent' ? NONE : fill;
  51273. /**
  51274. * FIXME:
  51275. * This is a temporary fix for Chrome's clipping bug
  51276. * that happens when a clip-path is referring another one.
  51277. * This fix should be used before Chrome's bug is fixed.
  51278. * For an element that has clip-path, and fill is none,
  51279. * set it to be "rgba(0, 0, 0, 0.002)" will hide the element.
  51280. * Otherwise, it will show black fill color.
  51281. * 0.002 is used because this won't work for alpha values smaller
  51282. * than 0.002.
  51283. *
  51284. * See
  51285. * https://bugs.chromium.org/p/chromium/issues/detail?id=659790
  51286. * for more information.
  51287. */
  51288. if (svgEl.getAttribute('clip-path') !== 'none' && fill === NONE) {
  51289. fill = 'rgba(0, 0, 0, 0.002)';
  51290. }
  51291. attr(svgEl, 'fill', fill);
  51292. attr(svgEl, 'fill-opacity', style.fillOpacity != null ? style.fillOpacity * style.opacity : style.opacity);
  51293. }
  51294. else {
  51295. attr(svgEl, 'fill', NONE);
  51296. }
  51297. if (pathHasStroke(style, isText)) {
  51298. var stroke = isText ? style.textStroke : style.stroke;
  51299. stroke = stroke === 'transparent' ? NONE : stroke;
  51300. attr(svgEl, 'stroke', stroke);
  51301. var strokeWidth = isText
  51302. ? style.textStrokeWidth
  51303. : style.lineWidth;
  51304. var strokeScale = !isText && style.strokeNoScale
  51305. ? el.getLineScale()
  51306. : 1;
  51307. attr(svgEl, 'stroke-width', strokeWidth / strokeScale);
  51308. // stroke then fill for text; fill then stroke for others
  51309. attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill');
  51310. attr(svgEl, 'stroke-opacity', style.strokeOpacity != null ? style.strokeOpacity : style.opacity);
  51311. var lineDash = style.lineDash;
  51312. if (lineDash) {
  51313. attr(svgEl, 'stroke-dasharray', style.lineDash.join(','));
  51314. attr(svgEl, 'stroke-dashoffset', mathRound(style.lineDashOffset || 0));
  51315. }
  51316. else {
  51317. attr(svgEl, 'stroke-dasharray', '');
  51318. }
  51319. // PENDING
  51320. style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap);
  51321. style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin);
  51322. style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit);
  51323. }
  51324. else {
  51325. attr(svgEl, 'stroke', NONE);
  51326. }
  51327. }
  51328. /***************************************************
  51329. * PATH
  51330. **************************************************/
  51331. function pathDataToString$1(path) {
  51332. var str = [];
  51333. var data = path.data;
  51334. var dataLength = path.len();
  51335. for (var i = 0; i < dataLength;) {
  51336. var cmd = data[i++];
  51337. var cmdStr = '';
  51338. var nData = 0;
  51339. switch (cmd) {
  51340. case CMD$4.M:
  51341. cmdStr = 'M';
  51342. nData = 2;
  51343. break;
  51344. case CMD$4.L:
  51345. cmdStr = 'L';
  51346. nData = 2;
  51347. break;
  51348. case CMD$4.Q:
  51349. cmdStr = 'Q';
  51350. nData = 4;
  51351. break;
  51352. case CMD$4.C:
  51353. cmdStr = 'C';
  51354. nData = 6;
  51355. break;
  51356. case CMD$4.A:
  51357. var cx = data[i++];
  51358. var cy = data[i++];
  51359. var rx = data[i++];
  51360. var ry = data[i++];
  51361. var theta = data[i++];
  51362. var dTheta = data[i++];
  51363. var psi = data[i++];
  51364. var clockwise = data[i++];
  51365. var dThetaPositive = Math.abs(dTheta);
  51366. var isCircle = isAroundZero$1(dThetaPositive - PI2$5)
  51367. && !isAroundZero$1(dThetaPositive);
  51368. var large = false;
  51369. if (dThetaPositive >= PI2$5) {
  51370. large = true;
  51371. }
  51372. else if (isAroundZero$1(dThetaPositive)) {
  51373. large = false;
  51374. }
  51375. else {
  51376. large = (dTheta > -PI$3 && dTheta < 0 || dTheta > PI$3)
  51377. === !!clockwise;
  51378. }
  51379. var x0 = round4(cx + rx * mathCos$3(theta));
  51380. var y0 = round4(cy + ry * mathSin$3(theta));
  51381. // It will not draw if start point and end point are exactly the same
  51382. // We need to shift the end point with a small value
  51383. // FIXME A better way to draw circle ?
  51384. if (isCircle) {
  51385. if (clockwise) {
  51386. dTheta = PI2$5 - 1e-4;
  51387. }
  51388. else {
  51389. dTheta = -PI2$5 + 1e-4;
  51390. }
  51391. large = true;
  51392. if (i === 9) {
  51393. // Move to (x0, y0) only when CMD.A comes at the
  51394. // first position of a shape.
  51395. // For instance, when drawing a ring, CMD.A comes
  51396. // after CMD.M, so it's unnecessary to move to
  51397. // (x0, y0).
  51398. str.push('M', x0, y0);
  51399. }
  51400. }
  51401. var x = round4(cx + rx * mathCos$3(theta + dTheta));
  51402. var y = round4(cy + ry * mathSin$3(theta + dTheta));
  51403. // FIXME Ellipse
  51404. str.push('A', round4(rx), round4(ry),
  51405. mathRound(psi * degree), +large, +clockwise, x, y);
  51406. break;
  51407. case CMD$4.Z:
  51408. cmdStr = 'Z';
  51409. break;
  51410. case CMD$4.R:
  51411. var x = round4(data[i++]);
  51412. var y = round4(data[i++]);
  51413. var w = round4(data[i++]);
  51414. var h = round4(data[i++]);
  51415. str.push(
  51416. 'M', x, y,
  51417. 'L', x + w, y,
  51418. 'L', x + w, y + h,
  51419. 'L', x, y + h,
  51420. 'L', x, y
  51421. );
  51422. break;
  51423. }
  51424. cmdStr && str.push(cmdStr);
  51425. for (var j = 0; j < nData; j++) {
  51426. // PENDING With scale
  51427. str.push(round4(data[i++]));
  51428. }
  51429. }
  51430. return str.join(' ');
  51431. }
  51432. var svgPath = {};
  51433. svgPath.brush = function (el) {
  51434. var style = el.style;
  51435. var svgEl = el.__svgEl;
  51436. if (!svgEl) {
  51437. svgEl = createElement('path');
  51438. el.__svgEl = svgEl;
  51439. }
  51440. if (!el.path) {
  51441. el.createPathProxy();
  51442. }
  51443. var path = el.path;
  51444. if (el.__dirtyPath) {
  51445. path.beginPath();
  51446. path.subPixelOptimize = false;
  51447. el.buildPath(path, el.shape);
  51448. el.__dirtyPath = false;
  51449. var pathStr = pathDataToString$1(path);
  51450. if (pathStr.indexOf('NaN') < 0) {
  51451. // Ignore illegal path, which may happen such in out-of-range
  51452. // data in Calendar series.
  51453. attr(svgEl, 'd', pathStr);
  51454. }
  51455. }
  51456. bindStyle(svgEl, style, false, el);
  51457. setTransform(svgEl, el.transform);
  51458. if (style.text != null) {
  51459. svgTextDrawRectText(el, el.getBoundingRect());
  51460. }
  51461. };
  51462. /***************************************************
  51463. * IMAGE
  51464. **************************************************/
  51465. var svgImage = {};
  51466. svgImage.brush = function (el) {
  51467. var style = el.style;
  51468. var image = style.image;
  51469. if (image instanceof HTMLImageElement) {
  51470. var src = image.src;
  51471. image = src;
  51472. }
  51473. if (!image) {
  51474. return;
  51475. }
  51476. var x = style.x || 0;
  51477. var y = style.y || 0;
  51478. var dw = style.width;
  51479. var dh = style.height;
  51480. var svgEl = el.__svgEl;
  51481. if (!svgEl) {
  51482. svgEl = createElement('image');
  51483. el.__svgEl = svgEl;
  51484. }
  51485. if (image !== el.__imageSrc) {
  51486. attrXLink(svgEl, 'href', image);
  51487. // Caching image src
  51488. el.__imageSrc = image;
  51489. }
  51490. attr(svgEl, 'width', dw);
  51491. attr(svgEl, 'height', dh);
  51492. attr(svgEl, 'x', x);
  51493. attr(svgEl, 'y', y);
  51494. setTransform(svgEl, el.transform);
  51495. if (style.text != null) {
  51496. svgTextDrawRectText(el, el.getBoundingRect());
  51497. }
  51498. };
  51499. /***************************************************
  51500. * TEXT
  51501. **************************************************/
  51502. var svgText = {};
  51503. var tmpRect$3 = new BoundingRect();
  51504. var svgTextDrawRectText = function (el, rect, textRect) {
  51505. var style = el.style;
  51506. el.__dirty && normalizeTextStyle(style, true);
  51507. var text = style.text;
  51508. // Convert to string
  51509. if (text == null) {
  51510. // Draw no text only when text is set to null, but not ''
  51511. return;
  51512. }
  51513. else {
  51514. text += '';
  51515. }
  51516. var textSvgEl = el.__textSvgEl;
  51517. if (!textSvgEl) {
  51518. textSvgEl = createElement('text');
  51519. el.__textSvgEl = textSvgEl;
  51520. }
  51521. var x;
  51522. var y;
  51523. var textPosition = style.textPosition;
  51524. var distance = style.textDistance;
  51525. var align = style.textAlign || 'left';
  51526. if (typeof style.fontSize === 'number') {
  51527. style.fontSize += 'px';
  51528. }
  51529. var font = style.font
  51530. || [
  51531. style.fontStyle || '',
  51532. style.fontWeight || '',
  51533. style.fontSize || '',
  51534. style.fontFamily || ''
  51535. ].join(' ')
  51536. || DEFAULT_FONT$1;
  51537. var verticalAlign = getVerticalAlignForSvg(style.textVerticalAlign);
  51538. textRect = getBoundingRect(
  51539. text, font, align,
  51540. verticalAlign, style.textPadding, style.textLineHeight
  51541. );
  51542. var lineHeight = textRect.lineHeight;
  51543. // Text position represented by coord
  51544. if (textPosition instanceof Array) {
  51545. x = rect.x + textPosition[0];
  51546. y = rect.y + textPosition[1];
  51547. }
  51548. else {
  51549. var newPos = adjustTextPositionOnRect(
  51550. textPosition, rect, distance
  51551. );
  51552. x = newPos.x;
  51553. y = newPos.y;
  51554. verticalAlign = getVerticalAlignForSvg(newPos.textVerticalAlign);
  51555. align = newPos.textAlign;
  51556. }
  51557. attr(textSvgEl, 'alignment-baseline', verticalAlign);
  51558. if (font) {
  51559. textSvgEl.style.font = font;
  51560. }
  51561. var textPadding = style.textPadding;
  51562. // Make baseline top
  51563. attr(textSvgEl, 'x', x);
  51564. attr(textSvgEl, 'y', y);
  51565. bindStyle(textSvgEl, style, true, el);
  51566. if (el instanceof Text || el.style.transformText) {
  51567. // Transform text with element
  51568. setTransform(textSvgEl, el.transform);
  51569. }
  51570. else {
  51571. if (el.transform) {
  51572. tmpRect$3.copy(rect);
  51573. tmpRect$3.applyTransform(el.transform);
  51574. rect = tmpRect$3;
  51575. }
  51576. else {
  51577. var pos = el.transformCoordToGlobal(rect.x, rect.y);
  51578. rect.x = pos[0];
  51579. rect.y = pos[1];
  51580. el.transform = identity(create$1());
  51581. }
  51582. // Text rotation, but no element transform
  51583. var origin = style.textOrigin;
  51584. if (origin === 'center') {
  51585. x = textRect.width / 2 + x;
  51586. y = textRect.height / 2 + y;
  51587. }
  51588. else if (origin) {
  51589. x = origin[0] + x;
  51590. y = origin[1] + y;
  51591. }
  51592. var rotate$$1 = -style.textRotation || 0;
  51593. var transform = create$1();
  51594. // Apply textRotate to element matrix
  51595. rotate(transform, transform, rotate$$1);
  51596. var pos = [el.transform[4], el.transform[5]];
  51597. translate(transform, transform, pos);
  51598. setTransform(textSvgEl, transform);
  51599. }
  51600. var textLines = text.split('\n');
  51601. var nTextLines = textLines.length;
  51602. var textAnchor = align;
  51603. // PENDING
  51604. if (textAnchor === 'left') {
  51605. textAnchor = 'start';
  51606. textPadding && (x += textPadding[3]);
  51607. }
  51608. else if (textAnchor === 'right') {
  51609. textAnchor = 'end';
  51610. textPadding && (x -= textPadding[1]);
  51611. }
  51612. else if (textAnchor === 'center') {
  51613. textAnchor = 'middle';
  51614. textPadding && (x += (textPadding[3] - textPadding[1]) / 2);
  51615. }
  51616. var dy = 0;
  51617. if (verticalAlign === 'after-edge') {
  51618. dy = -textRect.height + lineHeight;
  51619. textPadding && (dy -= textPadding[2]);
  51620. }
  51621. else if (verticalAlign === 'middle') {
  51622. dy = (-textRect.height + lineHeight) / 2;
  51623. textPadding && (y += (textPadding[0] - textPadding[2]) / 2);
  51624. }
  51625. else {
  51626. textPadding && (dy += textPadding[0]);
  51627. }
  51628. // Font may affect position of each tspan elements
  51629. if (el.__text !== text || el.__textFont !== font) {
  51630. var tspanList = el.__tspanList || [];
  51631. el.__tspanList = tspanList;
  51632. for (var i = 0; i < nTextLines; i++) {
  51633. // Using cached tspan elements
  51634. var tspan = tspanList[i];
  51635. if (!tspan) {
  51636. tspan = tspanList[i] = createElement('tspan');
  51637. textSvgEl.appendChild(tspan);
  51638. attr(tspan, 'alignment-baseline', verticalAlign);
  51639. attr(tspan, 'text-anchor', textAnchor);
  51640. }
  51641. else {
  51642. tspan.innerHTML = '';
  51643. }
  51644. attr(tspan, 'x', x);
  51645. attr(tspan, 'y', y + i * lineHeight + dy);
  51646. tspan.appendChild(document.createTextNode(textLines[i]));
  51647. }
  51648. // Remove unsed tspan elements
  51649. for (; i < tspanList.length; i++) {
  51650. textSvgEl.removeChild(tspanList[i]);
  51651. }
  51652. tspanList.length = nTextLines;
  51653. el.__text = text;
  51654. el.__textFont = font;
  51655. }
  51656. else if (el.__tspanList.length) {
  51657. // Update span x and y
  51658. var len = el.__tspanList.length;
  51659. for (var i = 0; i < len; ++i) {
  51660. var tspan = el.__tspanList[i];
  51661. if (tspan) {
  51662. attr(tspan, 'x', x);
  51663. attr(tspan, 'y', y + i * lineHeight + dy);
  51664. }
  51665. }
  51666. }
  51667. };
  51668. function getVerticalAlignForSvg(verticalAlign) {
  51669. if (verticalAlign === 'middle') {
  51670. return 'middle';
  51671. }
  51672. else if (verticalAlign === 'bottom') {
  51673. return 'after-edge';
  51674. }
  51675. else {
  51676. return 'hanging';
  51677. }
  51678. }
  51679. svgText.drawRectText = svgTextDrawRectText;
  51680. svgText.brush = function (el) {
  51681. var style = el.style;
  51682. if (style.text != null) {
  51683. // 强制设置 textPosition
  51684. style.textPosition = [0, 0];
  51685. svgTextDrawRectText(el, {
  51686. x: style.x || 0, y: style.y || 0,
  51687. width: 0, height: 0
  51688. }, el.getBoundingRect());
  51689. }
  51690. };
  51691. // Myers' Diff Algorithm
  51692. // Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js
  51693. function Diff() {}
  51694. Diff.prototype = {
  51695. diff: function (oldArr, newArr, equals) {
  51696. if (!equals) {
  51697. equals = function (a, b) {
  51698. return a === b;
  51699. };
  51700. }
  51701. this.equals = equals;
  51702. var self = this;
  51703. oldArr = oldArr.slice();
  51704. newArr = newArr.slice();
  51705. // Allow subclasses to massage the input prior to running
  51706. var newLen = newArr.length;
  51707. var oldLen = oldArr.length;
  51708. var editLength = 1;
  51709. var maxEditLength = newLen + oldLen;
  51710. var bestPath = [{ newPos: -1, components: [] }];
  51711. // Seed editLength = 0, i.e. the content starts with the same values
  51712. var oldPos = this.extractCommon(bestPath[0], newArr, oldArr, 0);
  51713. if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
  51714. var indices = [];
  51715. for (var i = 0; i < newArr.length; i++) {
  51716. indices.push(i);
  51717. }
  51718. // Identity per the equality and tokenizer
  51719. return [{
  51720. indices: indices, count: newArr.length
  51721. }];
  51722. }
  51723. // Main worker method. checks all permutations of a given edit length for acceptance.
  51724. function execEditLength() {
  51725. for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
  51726. var basePath;
  51727. var addPath = bestPath[diagonalPath - 1];
  51728. var removePath = bestPath[diagonalPath + 1];
  51729. var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
  51730. if (addPath) {
  51731. // No one else is going to attempt to use this value, clear it
  51732. bestPath[diagonalPath - 1] = undefined;
  51733. }
  51734. var canAdd = addPath && addPath.newPos + 1 < newLen;
  51735. var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
  51736. if (!canAdd && !canRemove) {
  51737. // If this path is a terminal then prune
  51738. bestPath[diagonalPath] = undefined;
  51739. continue;
  51740. }
  51741. // Select the diagonal that we want to branch from. We select the prior
  51742. // path whose position in the new string is the farthest from the origin
  51743. // and does not pass the bounds of the diff graph
  51744. if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
  51745. basePath = clonePath(removePath);
  51746. self.pushComponent(basePath.components, undefined, true);
  51747. }
  51748. else {
  51749. basePath = addPath; // No need to clone, we've pulled it from the list
  51750. basePath.newPos++;
  51751. self.pushComponent(basePath.components, true, undefined);
  51752. }
  51753. oldPos = self.extractCommon(basePath, newArr, oldArr, diagonalPath);
  51754. // If we have hit the end of both strings, then we are done
  51755. if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
  51756. return buildValues(self, basePath.components, newArr, oldArr);
  51757. }
  51758. else {
  51759. // Otherwise track this path as a potential candidate and continue.
  51760. bestPath[diagonalPath] = basePath;
  51761. }
  51762. }
  51763. editLength++;
  51764. }
  51765. while (editLength <= maxEditLength) {
  51766. var ret = execEditLength();
  51767. if (ret) {
  51768. return ret;
  51769. }
  51770. }
  51771. },
  51772. pushComponent: function (components, added, removed) {
  51773. var last = components[components.length - 1];
  51774. if (last && last.added === added && last.removed === removed) {
  51775. // We need to clone here as the component clone operation is just
  51776. // as shallow array clone
  51777. components[components.length - 1] = {count: last.count + 1, added: added, removed: removed };
  51778. }
  51779. else {
  51780. components.push({count: 1, added: added, removed: removed });
  51781. }
  51782. },
  51783. extractCommon: function (basePath, newArr, oldArr, diagonalPath) {
  51784. var newLen = newArr.length;
  51785. var oldLen = oldArr.length;
  51786. var newPos = basePath.newPos;
  51787. var oldPos = newPos - diagonalPath;
  51788. var commonCount = 0;
  51789. while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newArr[newPos + 1], oldArr[oldPos + 1])) {
  51790. newPos++;
  51791. oldPos++;
  51792. commonCount++;
  51793. }
  51794. if (commonCount) {
  51795. basePath.components.push({count: commonCount});
  51796. }
  51797. basePath.newPos = newPos;
  51798. return oldPos;
  51799. },
  51800. tokenize: function (value) {
  51801. return value.slice();
  51802. },
  51803. join: function (value) {
  51804. return value.slice();
  51805. }
  51806. };
  51807. function buildValues(diff, components, newArr, oldArr) {
  51808. var componentPos = 0;
  51809. var componentLen = components.length;
  51810. var newPos = 0;
  51811. var oldPos = 0;
  51812. for (; componentPos < componentLen; componentPos++) {
  51813. var component = components[componentPos];
  51814. if (!component.removed) {
  51815. var indices = [];
  51816. for (var i = newPos; i < newPos + component.count; i++) {
  51817. indices.push(i);
  51818. }
  51819. component.indices = indices;
  51820. newPos += component.count;
  51821. // Common case
  51822. if (!component.added) {
  51823. oldPos += component.count;
  51824. }
  51825. }
  51826. else {
  51827. var indices = [];
  51828. for (var i = oldPos; i < oldPos + component.count; i++) {
  51829. indices.push(i);
  51830. }
  51831. component.indices = indices;
  51832. oldPos += component.count;
  51833. }
  51834. }
  51835. return components;
  51836. }
  51837. function clonePath(path) {
  51838. return { newPos: path.newPos, components: path.components.slice(0) };
  51839. }
  51840. var arrayDiff = new Diff();
  51841. var arrayDiff$1 = function (oldArr, newArr, callback) {
  51842. return arrayDiff.diff(oldArr, newArr, callback);
  51843. };
  51844. /**
  51845. * @file Manages elements that can be defined in <defs> in SVG,
  51846. * e.g., gradients, clip path, etc.
  51847. * @author Zhang Wenli
  51848. */
  51849. var MARK_UNUSED = '0';
  51850. var MARK_USED = '1';
  51851. /**
  51852. * Manages elements that can be defined in <defs> in SVG,
  51853. * e.g., gradients, clip path, etc.
  51854. *
  51855. * @class
  51856. * @param {number} zrId zrender instance id
  51857. * @param {SVGElement} svgRoot root of SVG document
  51858. * @param {string|string[]} tagNames possible tag names
  51859. * @param {string} markLabel label name to make if the element
  51860. * is used
  51861. */
  51862. function Definable(
  51863. zrId,
  51864. svgRoot,
  51865. tagNames,
  51866. markLabel,
  51867. domName
  51868. ) {
  51869. this._zrId = zrId;
  51870. this._svgRoot = svgRoot;
  51871. this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames;
  51872. this._markLabel = markLabel;
  51873. this._domName = domName || '_dom';
  51874. this.nextId = 0;
  51875. }
  51876. Definable.prototype.createElement = createElement;
  51877. /**
  51878. * Get the <defs> tag for svgRoot; optionally creates one if not exists.
  51879. *
  51880. * @param {boolean} isForceCreating if need to create when not exists
  51881. * @return {SVGDefsElement} SVG <defs> element, null if it doesn't
  51882. * exist and isForceCreating is false
  51883. */
  51884. Definable.prototype.getDefs = function (isForceCreating) {
  51885. var svgRoot = this._svgRoot;
  51886. var defs = this._svgRoot.getElementsByTagName('defs');
  51887. if (defs.length === 0) {
  51888. // Not exist
  51889. if (isForceCreating) {
  51890. defs = svgRoot.insertBefore(
  51891. this.createElement('defs'), // Create new tag
  51892. svgRoot.firstChild // Insert in the front of svg
  51893. );
  51894. if (!defs.contains) {
  51895. // IE doesn't support contains method
  51896. defs.contains = function (el) {
  51897. var children = defs.children;
  51898. if (!children) {
  51899. return false;
  51900. }
  51901. for (var i = children.length - 1; i >= 0; --i) {
  51902. if (children[i] === el) {
  51903. return true;
  51904. }
  51905. }
  51906. return false;
  51907. };
  51908. }
  51909. return defs;
  51910. }
  51911. else {
  51912. return null;
  51913. }
  51914. }
  51915. else {
  51916. return defs[0];
  51917. }
  51918. };
  51919. /**
  51920. * Update DOM element if necessary.
  51921. *
  51922. * @param {Object|string} element style element. e.g., for gradient,
  51923. * it may be '#ccc' or {type: 'linear', ...}
  51924. * @param {Function|undefined} onUpdate update callback
  51925. */
  51926. Definable.prototype.update = function (element, onUpdate) {
  51927. if (!element) {
  51928. return;
  51929. }
  51930. var defs = this.getDefs(false);
  51931. if (element[this._domName] && defs.contains(element[this._domName])) {
  51932. // Update DOM
  51933. if (typeof onUpdate === 'function') {
  51934. onUpdate(element);
  51935. }
  51936. }
  51937. else {
  51938. // No previous dom, create new
  51939. var dom = this.add(element);
  51940. if (dom) {
  51941. element[this._domName] = dom;
  51942. }
  51943. }
  51944. };
  51945. /**
  51946. * Add gradient dom to defs
  51947. *
  51948. * @param {SVGElement} dom DOM to be added to <defs>
  51949. */
  51950. Definable.prototype.addDom = function (dom) {
  51951. var defs = this.getDefs(true);
  51952. defs.appendChild(dom);
  51953. };
  51954. /**
  51955. * Remove DOM of a given element.
  51956. *
  51957. * @param {SVGElement} element element to remove dom
  51958. */
  51959. Definable.prototype.removeDom = function (element) {
  51960. var defs = this.getDefs(false);
  51961. if (defs && element[this._domName]) {
  51962. defs.removeChild(element[this._domName]);
  51963. element[this._domName] = null;
  51964. }
  51965. };
  51966. /**
  51967. * Get DOMs of this element.
  51968. *
  51969. * @return {HTMLDomElement} doms of this defineable elements in <defs>
  51970. */
  51971. Definable.prototype.getDoms = function () {
  51972. var defs = this.getDefs(false);
  51973. if (!defs) {
  51974. // No dom when defs is not defined
  51975. return [];
  51976. }
  51977. var doms = [];
  51978. each$1(this._tagNames, function (tagName) {
  51979. var tags = defs.getElementsByTagName(tagName);
  51980. // Note that tags is HTMLCollection, which is array-like
  51981. // rather than real array.
  51982. // So `doms.concat(tags)` add tags as one object.
  51983. doms = doms.concat([].slice.call(tags));
  51984. });
  51985. return doms;
  51986. };
  51987. /**
  51988. * Mark DOMs to be unused before painting, and clear unused ones at the end
  51989. * of the painting.
  51990. */
  51991. Definable.prototype.markAllUnused = function () {
  51992. var doms = this.getDoms();
  51993. var that = this;
  51994. each$1(doms, function (dom) {
  51995. dom[that._markLabel] = MARK_UNUSED;
  51996. });
  51997. };
  51998. /**
  51999. * Mark a single DOM to be used.
  52000. *
  52001. * @param {SVGElement} dom DOM to mark
  52002. */
  52003. Definable.prototype.markUsed = function (dom) {
  52004. if (dom) {
  52005. dom[this._markLabel] = MARK_USED;
  52006. }
  52007. };
  52008. /**
  52009. * Remove unused DOMs defined in <defs>
  52010. */
  52011. Definable.prototype.removeUnused = function () {
  52012. var defs = this.getDefs(false);
  52013. if (!defs) {
  52014. // Nothing to remove
  52015. return;
  52016. }
  52017. var doms = this.getDoms();
  52018. var that = this;
  52019. each$1(doms, function (dom) {
  52020. if (dom[that._markLabel] !== MARK_USED) {
  52021. // Remove gradient
  52022. defs.removeChild(dom);
  52023. }
  52024. });
  52025. };
  52026. /**
  52027. * Get SVG proxy.
  52028. *
  52029. * @param {Displayable} displayable displayable element
  52030. * @return {Path|Image|Text} svg proxy of given element
  52031. */
  52032. Definable.prototype.getSvgProxy = function (displayable) {
  52033. if (displayable instanceof Path) {
  52034. return svgPath;
  52035. }
  52036. else if (displayable instanceof ZImage) {
  52037. return svgImage;
  52038. }
  52039. else if (displayable instanceof Text) {
  52040. return svgText;
  52041. }
  52042. else {
  52043. return svgPath;
  52044. }
  52045. };
  52046. /**
  52047. * Get text SVG element.
  52048. *
  52049. * @param {Displayable} displayable displayable element
  52050. * @return {SVGElement} SVG element of text
  52051. */
  52052. Definable.prototype.getTextSvgElement = function (displayable) {
  52053. return displayable.__textSvgEl;
  52054. };
  52055. /**
  52056. * Get SVG element.
  52057. *
  52058. * @param {Displayable} displayable displayable element
  52059. * @return {SVGElement} SVG element
  52060. */
  52061. Definable.prototype.getSvgElement = function (displayable) {
  52062. return displayable.__svgEl;
  52063. };
  52064. /**
  52065. * @file Manages SVG gradient elements.
  52066. * @author Zhang Wenli
  52067. */
  52068. /**
  52069. * Manages SVG gradient elements.
  52070. *
  52071. * @class
  52072. * @extends Definable
  52073. * @param {number} zrId zrender instance id
  52074. * @param {SVGElement} svgRoot root of SVG document
  52075. */
  52076. function GradientManager(zrId, svgRoot) {
  52077. Definable.call(
  52078. this,
  52079. zrId,
  52080. svgRoot,
  52081. ['linearGradient', 'radialGradient'],
  52082. '__gradient_in_use__'
  52083. );
  52084. }
  52085. inherits(GradientManager, Definable);
  52086. /**
  52087. * Create new gradient DOM for fill or stroke if not exist,
  52088. * but will not update gradient if exists.
  52089. *
  52090. * @param {SvgElement} svgElement SVG element to paint
  52091. * @param {Displayable} displayable zrender displayable element
  52092. */
  52093. GradientManager.prototype.addWithoutUpdate = function (
  52094. svgElement,
  52095. displayable
  52096. ) {
  52097. if (displayable && displayable.style) {
  52098. var that = this;
  52099. each$1(['fill', 'stroke'], function (fillOrStroke) {
  52100. if (displayable.style[fillOrStroke]
  52101. && (displayable.style[fillOrStroke].type === 'linear'
  52102. || displayable.style[fillOrStroke].type === 'radial')
  52103. ) {
  52104. var gradient = displayable.style[fillOrStroke];
  52105. var defs = that.getDefs(true);
  52106. // Create dom in <defs> if not exists
  52107. var dom;
  52108. if (gradient._dom) {
  52109. // Gradient exists
  52110. dom = gradient._dom;
  52111. if (!defs.contains(gradient._dom)) {
  52112. // _dom is no longer in defs, recreate
  52113. that.addDom(dom);
  52114. }
  52115. }
  52116. else {
  52117. // New dom
  52118. dom = that.add(gradient);
  52119. }
  52120. that.markUsed(displayable);
  52121. var id = dom.getAttribute('id');
  52122. svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')');
  52123. }
  52124. });
  52125. }
  52126. };
  52127. /**
  52128. * Add a new gradient tag in <defs>
  52129. *
  52130. * @param {Gradient} gradient zr gradient instance
  52131. * @return {SVGLinearGradientElement | SVGRadialGradientElement}
  52132. * created DOM
  52133. */
  52134. GradientManager.prototype.add = function (gradient) {
  52135. var dom;
  52136. if (gradient.type === 'linear') {
  52137. dom = this.createElement('linearGradient');
  52138. }
  52139. else if (gradient.type === 'radial') {
  52140. dom = this.createElement('radialGradient');
  52141. }
  52142. else {
  52143. zrLog('Illegal gradient type.');
  52144. return null;
  52145. }
  52146. // Set dom id with gradient id, since each gradient instance
  52147. // will have no more than one dom element.
  52148. // id may exists before for those dirty elements, in which case
  52149. // id should remain the same, and other attributes should be
  52150. // updated.
  52151. gradient.id = gradient.id || this.nextId++;
  52152. dom.setAttribute('id', 'zr' + this._zrId
  52153. + '-gradient-' + gradient.id);
  52154. this.updateDom(gradient, dom);
  52155. this.addDom(dom);
  52156. return dom;
  52157. };
  52158. /**
  52159. * Update gradient.
  52160. *
  52161. * @param {Gradient} gradient zr gradient instance
  52162. */
  52163. GradientManager.prototype.update = function (gradient) {
  52164. var that = this;
  52165. Definable.prototype.update.call(this, gradient, function () {
  52166. var type = gradient.type;
  52167. var tagName = gradient._dom.tagName;
  52168. if (type === 'linear' && tagName === 'linearGradient'
  52169. || type === 'radial' && tagName === 'radialGradient'
  52170. ) {
  52171. // Gradient type is not changed, update gradient
  52172. that.updateDom(gradient, gradient._dom);
  52173. }
  52174. else {
  52175. // Remove and re-create if type is changed
  52176. that.removeDom(gradient);
  52177. that.add(gradient);
  52178. }
  52179. });
  52180. };
  52181. /**
  52182. * Update gradient dom
  52183. *
  52184. * @param {Gradient} gradient zr gradient instance
  52185. * @param {SVGLinearGradientElement | SVGRadialGradientElement} dom
  52186. * DOM to update
  52187. */
  52188. GradientManager.prototype.updateDom = function (gradient, dom) {
  52189. if (gradient.type === 'linear') {
  52190. dom.setAttribute('x1', gradient.x);
  52191. dom.setAttribute('y1', gradient.y);
  52192. dom.setAttribute('x2', gradient.x2);
  52193. dom.setAttribute('y2', gradient.y2);
  52194. }
  52195. else if (gradient.type === 'radial') {
  52196. dom.setAttribute('cx', gradient.x);
  52197. dom.setAttribute('cy', gradient.y);
  52198. dom.setAttribute('r', gradient.r);
  52199. }
  52200. else {
  52201. zrLog('Illegal gradient type.');
  52202. return;
  52203. }
  52204. if (gradient.global) {
  52205. // x1, x2, y1, y2 in range of 0 to canvas width or height
  52206. dom.setAttribute('gradientUnits', 'userSpaceOnUse');
  52207. }
  52208. else {
  52209. // x1, x2, y1, y2 in range of 0 to 1
  52210. dom.setAttribute('gradientUnits', 'objectBoundingBox');
  52211. }
  52212. // Remove color stops if exists
  52213. dom.innerHTML = '';
  52214. // Add color stops
  52215. var colors = gradient.colorStops;
  52216. for (var i = 0, len = colors.length; i < len; ++i) {
  52217. var stop = this.createElement('stop');
  52218. stop.setAttribute('offset', colors[i].offset * 100 + '%');
  52219. var color = colors[i].color;
  52220. if (color.indexOf('rgba' > -1)) {
  52221. // Fix Safari bug that stop-color not recognizing alpha #9014
  52222. var opacity = parse(color)[3];
  52223. var hex = toHex(color);
  52224. // stop-color cannot be color, since:
  52225. // The opacity value used for the gradient calculation is the
  52226. // *product* of the value of stop-opacity and the opacity of the
  52227. // value of stop-color.
  52228. // See https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty
  52229. stop.setAttribute('stop-color', '#' + hex);
  52230. stop.setAttribute('stop-opacity', opacity);
  52231. }
  52232. else {
  52233. stop.setAttribute('stop-color', colors[i].color);
  52234. }
  52235. dom.appendChild(stop);
  52236. }
  52237. // Store dom element in gradient, to avoid creating multiple
  52238. // dom instances for the same gradient element
  52239. gradient._dom = dom;
  52240. };
  52241. /**
  52242. * Mark a single gradient to be used
  52243. *
  52244. * @param {Displayable} displayable displayable element
  52245. */
  52246. GradientManager.prototype.markUsed = function (displayable) {
  52247. if (displayable.style) {
  52248. var gradient = displayable.style.fill;
  52249. if (gradient && gradient._dom) {
  52250. Definable.prototype.markUsed.call(this, gradient._dom);
  52251. }
  52252. gradient = displayable.style.stroke;
  52253. if (gradient && gradient._dom) {
  52254. Definable.prototype.markUsed.call(this, gradient._dom);
  52255. }
  52256. }
  52257. };
  52258. /**
  52259. * @file Manages SVG clipPath elements.
  52260. * @author Zhang Wenli
  52261. */
  52262. /**
  52263. * Manages SVG clipPath elements.
  52264. *
  52265. * @class
  52266. * @extends Definable
  52267. * @param {number} zrId zrender instance id
  52268. * @param {SVGElement} svgRoot root of SVG document
  52269. */
  52270. function ClippathManager(zrId, svgRoot) {
  52271. Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__');
  52272. }
  52273. inherits(ClippathManager, Definable);
  52274. /**
  52275. * Update clipPath.
  52276. *
  52277. * @param {Displayable} displayable displayable element
  52278. */
  52279. ClippathManager.prototype.update = function (displayable) {
  52280. var svgEl = this.getSvgElement(displayable);
  52281. if (svgEl) {
  52282. this.updateDom(svgEl, displayable.__clipPaths, false);
  52283. }
  52284. var textEl = this.getTextSvgElement(displayable);
  52285. if (textEl) {
  52286. // Make another clipPath for text, since it's transform
  52287. // matrix is not the same with svgElement
  52288. this.updateDom(textEl, displayable.__clipPaths, true);
  52289. }
  52290. this.markUsed(displayable);
  52291. };
  52292. /**
  52293. * Create an SVGElement of displayable and create a <clipPath> of its
  52294. * clipPath
  52295. *
  52296. * @param {Displayable} parentEl parent element
  52297. * @param {ClipPath[]} clipPaths clipPaths of parent element
  52298. * @param {boolean} isText if parent element is Text
  52299. */
  52300. ClippathManager.prototype.updateDom = function (
  52301. parentEl,
  52302. clipPaths,
  52303. isText
  52304. ) {
  52305. if (clipPaths && clipPaths.length > 0) {
  52306. // Has clipPath, create <clipPath> with the first clipPath
  52307. var defs = this.getDefs(true);
  52308. var clipPath = clipPaths[0];
  52309. var clipPathEl;
  52310. var id;
  52311. var dom = isText ? '_textDom' : '_dom';
  52312. if (clipPath[dom]) {
  52313. // Use a dom that is already in <defs>
  52314. id = clipPath[dom].getAttribute('id');
  52315. clipPathEl = clipPath[dom];
  52316. // Use a dom that is already in <defs>
  52317. if (!defs.contains(clipPathEl)) {
  52318. // This happens when set old clipPath that has
  52319. // been previously removed
  52320. defs.appendChild(clipPathEl);
  52321. }
  52322. }
  52323. else {
  52324. // New <clipPath>
  52325. id = 'zr' + this._zrId + '-clip-' + this.nextId;
  52326. ++this.nextId;
  52327. clipPathEl = this.createElement('clipPath');
  52328. clipPathEl.setAttribute('id', id);
  52329. defs.appendChild(clipPathEl);
  52330. clipPath[dom] = clipPathEl;
  52331. }
  52332. // Build path and add to <clipPath>
  52333. var svgProxy = this.getSvgProxy(clipPath);
  52334. if (clipPath.transform
  52335. && clipPath.parent.invTransform
  52336. && !isText
  52337. ) {
  52338. /**
  52339. * If a clipPath has a parent with transform, the transform
  52340. * of parent should not be considered when setting transform
  52341. * of clipPath. So we need to transform back from parent's
  52342. * transform, which is done by multiplying parent's inverse
  52343. * transform.
  52344. */
  52345. // Store old transform
  52346. var transform = Array.prototype.slice.call(
  52347. clipPath.transform
  52348. );
  52349. // Transform back from parent, and brush path
  52350. mul$1(
  52351. clipPath.transform,
  52352. clipPath.parent.invTransform,
  52353. clipPath.transform
  52354. );
  52355. svgProxy.brush(clipPath);
  52356. // Set back transform of clipPath
  52357. clipPath.transform = transform;
  52358. }
  52359. else {
  52360. svgProxy.brush(clipPath);
  52361. }
  52362. var pathEl = this.getSvgElement(clipPath);
  52363. clipPathEl.innerHTML = '';
  52364. /**
  52365. * Use `cloneNode()` here to appendChild to multiple parents,
  52366. * which may happend when Text and other shapes are using the same
  52367. * clipPath. Since Text will create an extra clipPath DOM due to
  52368. * different transform rules.
  52369. */
  52370. clipPathEl.appendChild(pathEl.cloneNode());
  52371. parentEl.setAttribute('clip-path', 'url(#' + id + ')');
  52372. if (clipPaths.length > 1) {
  52373. // Make the other clipPaths recursively
  52374. this.updateDom(clipPathEl, clipPaths.slice(1), isText);
  52375. }
  52376. }
  52377. else {
  52378. // No clipPath
  52379. if (parentEl) {
  52380. parentEl.setAttribute('clip-path', 'none');
  52381. }
  52382. }
  52383. };
  52384. /**
  52385. * Mark a single clipPath to be used
  52386. *
  52387. * @param {Displayable} displayable displayable element
  52388. */
  52389. ClippathManager.prototype.markUsed = function (displayable) {
  52390. var that = this;
  52391. if (displayable.__clipPaths && displayable.__clipPaths.length > 0) {
  52392. each$1(displayable.__clipPaths, function (clipPath) {
  52393. if (clipPath._dom) {
  52394. Definable.prototype.markUsed.call(that, clipPath._dom);
  52395. }
  52396. if (clipPath._textDom) {
  52397. Definable.prototype.markUsed.call(that, clipPath._textDom);
  52398. }
  52399. });
  52400. }
  52401. };
  52402. /**
  52403. * @file Manages SVG shadow elements.
  52404. * @author Zhang Wenli
  52405. */
  52406. /**
  52407. * Manages SVG shadow elements.
  52408. *
  52409. * @class
  52410. * @extends Definable
  52411. * @param {number} zrId zrender instance id
  52412. * @param {SVGElement} svgRoot root of SVG document
  52413. */
  52414. function ShadowManager(zrId, svgRoot) {
  52415. Definable.call(
  52416. this,
  52417. zrId,
  52418. svgRoot,
  52419. ['filter'],
  52420. '__filter_in_use__',
  52421. '_shadowDom'
  52422. );
  52423. }
  52424. inherits(ShadowManager, Definable);
  52425. /**
  52426. * Create new shadow DOM for fill or stroke if not exist,
  52427. * but will not update shadow if exists.
  52428. *
  52429. * @param {SvgElement} svgElement SVG element to paint
  52430. * @param {Displayable} displayable zrender displayable element
  52431. */
  52432. ShadowManager.prototype.addWithoutUpdate = function (
  52433. svgElement,
  52434. displayable
  52435. ) {
  52436. if (displayable && hasShadow(displayable.style)) {
  52437. var style = displayable.style;
  52438. // Create dom in <defs> if not exists
  52439. var dom;
  52440. if (style._shadowDom) {
  52441. // Gradient exists
  52442. dom = style._shadowDom;
  52443. var defs = this.getDefs(true);
  52444. if (!defs.contains(style._shadowDom)) {
  52445. // _shadowDom is no longer in defs, recreate
  52446. this.addDom(dom);
  52447. }
  52448. }
  52449. else {
  52450. // New dom
  52451. dom = this.add(displayable);
  52452. }
  52453. this.markUsed(displayable);
  52454. var id = dom.getAttribute('id');
  52455. svgElement.style.filter = 'url(#' + id + ')';
  52456. }
  52457. };
  52458. /**
  52459. * Add a new shadow tag in <defs>
  52460. *
  52461. * @param {Displayable} displayable zrender displayable element
  52462. * @return {SVGFilterElement} created DOM
  52463. */
  52464. ShadowManager.prototype.add = function (displayable) {
  52465. var dom = this.createElement('filter');
  52466. var style = displayable.style;
  52467. // Set dom id with shadow id, since each shadow instance
  52468. // will have no more than one dom element.
  52469. // id may exists before for those dirty elements, in which case
  52470. // id should remain the same, and other attributes should be
  52471. // updated.
  52472. style._shadowDomId = style._shadowDomId || this.nextId++;
  52473. dom.setAttribute('id', 'zr' + this._zrId
  52474. + '-shadow-' + style._shadowDomId);
  52475. this.updateDom(displayable, dom);
  52476. this.addDom(dom);
  52477. return dom;
  52478. };
  52479. /**
  52480. * Update shadow.
  52481. *
  52482. * @param {Displayable} displayable zrender displayable element
  52483. */
  52484. ShadowManager.prototype.update = function (svgElement, displayable) {
  52485. var style = displayable.style;
  52486. if (hasShadow(style)) {
  52487. var that = this;
  52488. Definable.prototype.update.call(this, displayable, function (style) {
  52489. that.updateDom(displayable, style._shadowDom);
  52490. });
  52491. }
  52492. else {
  52493. // Remove shadow
  52494. this.remove(svgElement, style);
  52495. }
  52496. };
  52497. /**
  52498. * Remove DOM and clear parent filter
  52499. */
  52500. ShadowManager.prototype.remove = function (svgElement, style) {
  52501. if (style._shadowDomId != null) {
  52502. this.removeDom(style);
  52503. svgElement.style.filter = '';
  52504. }
  52505. };
  52506. /**
  52507. * Update shadow dom
  52508. *
  52509. * @param {Displayable} displayable zrender displayable element
  52510. * @param {SVGFilterElement} dom DOM to update
  52511. */
  52512. ShadowManager.prototype.updateDom = function (displayable, dom) {
  52513. var domChild = dom.getElementsByTagName('feDropShadow');
  52514. if (domChild.length === 0) {
  52515. domChild = this.createElement('feDropShadow');
  52516. }
  52517. else {
  52518. domChild = domChild[0];
  52519. }
  52520. var style = displayable.style;
  52521. var scaleX = displayable.scale ? (displayable.scale[0] || 1) : 1;
  52522. var scaleY = displayable.scale ? (displayable.scale[1] || 1) : 1;
  52523. // TODO: textBoxShadowBlur is not supported yet
  52524. var offsetX, offsetY, blur, color;
  52525. if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) {
  52526. offsetX = style.shadowOffsetX || 0;
  52527. offsetY = style.shadowOffsetY || 0;
  52528. blur = style.shadowBlur;
  52529. color = style.shadowColor;
  52530. }
  52531. else if (style.textShadowBlur) {
  52532. offsetX = style.textShadowOffsetX || 0;
  52533. offsetY = style.textShadowOffsetY || 0;
  52534. blur = style.textShadowBlur;
  52535. color = style.textShadowColor;
  52536. }
  52537. else {
  52538. // Remove shadow
  52539. this.removeDom(dom, style);
  52540. return;
  52541. }
  52542. domChild.setAttribute('dx', offsetX / scaleX);
  52543. domChild.setAttribute('dy', offsetY / scaleY);
  52544. domChild.setAttribute('flood-color', color);
  52545. // Divide by two here so that it looks the same as in canvas
  52546. // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur
  52547. var stdDx = blur / 2 / scaleX;
  52548. var stdDy = blur / 2 / scaleY;
  52549. var stdDeviation = stdDx + ' ' + stdDy;
  52550. domChild.setAttribute('stdDeviation', stdDeviation);
  52551. // Fix filter clipping problem
  52552. dom.setAttribute('x', '-100%');
  52553. dom.setAttribute('y', '-100%');
  52554. dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%');
  52555. dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%');
  52556. dom.appendChild(domChild);
  52557. // Store dom element in shadow, to avoid creating multiple
  52558. // dom instances for the same shadow element
  52559. style._shadowDom = dom;
  52560. };
  52561. /**
  52562. * Mark a single shadow to be used
  52563. *
  52564. * @param {Displayable} displayable displayable element
  52565. */
  52566. ShadowManager.prototype.markUsed = function (displayable) {
  52567. var style = displayable.style;
  52568. if (style && style._shadowDom) {
  52569. Definable.prototype.markUsed.call(this, style._shadowDom);
  52570. }
  52571. };
  52572. function hasShadow(style) {
  52573. // TODO: textBoxShadowBlur is not supported yet
  52574. return style
  52575. && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY
  52576. || style.textShadowBlur || style.textShadowOffsetX
  52577. || style.textShadowOffsetY);
  52578. }
  52579. /**
  52580. * SVG Painter
  52581. * @module zrender/svg/Painter
  52582. */
  52583. function parseInt10$2(val) {
  52584. return parseInt(val, 10);
  52585. }
  52586. function getSvgProxy(el) {
  52587. if (el instanceof Path) {
  52588. return svgPath;
  52589. }
  52590. else if (el instanceof ZImage) {
  52591. return svgImage;
  52592. }
  52593. else if (el instanceof Text) {
  52594. return svgText;
  52595. }
  52596. else {
  52597. return svgPath;
  52598. }
  52599. }
  52600. function checkParentAvailable(parent, child) {
  52601. return child && parent && child.parentNode !== parent;
  52602. }
  52603. function insertAfter(parent, child, prevSibling) {
  52604. if (checkParentAvailable(parent, child) && prevSibling) {
  52605. var nextSibling = prevSibling.nextSibling;
  52606. nextSibling ? parent.insertBefore(child, nextSibling)
  52607. : parent.appendChild(child);
  52608. }
  52609. }
  52610. function prepend(parent, child) {
  52611. if (checkParentAvailable(parent, child)) {
  52612. var firstChild = parent.firstChild;
  52613. firstChild ? parent.insertBefore(child, firstChild)
  52614. : parent.appendChild(child);
  52615. }
  52616. }
  52617. function remove$1(parent, child) {
  52618. if (child && parent && child.parentNode === parent) {
  52619. parent.removeChild(child);
  52620. }
  52621. }
  52622. function getTextSvgElement(displayable) {
  52623. return displayable.__textSvgEl;
  52624. }
  52625. function getSvgElement(displayable) {
  52626. return displayable.__svgEl;
  52627. }
  52628. /**
  52629. * @alias module:zrender/svg/Painter
  52630. * @constructor
  52631. * @param {HTMLElement} root 绘图容器
  52632. * @param {module:zrender/Storage} storage
  52633. * @param {Object} opts
  52634. */
  52635. var SVGPainter = function (root, storage, opts, zrId) {
  52636. this.root = root;
  52637. this.storage = storage;
  52638. this._opts = opts = extend({}, opts || {});
  52639. var svgRoot = createElement('svg');
  52640. svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
  52641. svgRoot.setAttribute('version', '1.1');
  52642. svgRoot.setAttribute('baseProfile', 'full');
  52643. svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';
  52644. this.gradientManager = new GradientManager(zrId, svgRoot);
  52645. this.clipPathManager = new ClippathManager(zrId, svgRoot);
  52646. this.shadowManager = new ShadowManager(zrId, svgRoot);
  52647. var viewport = document.createElement('div');
  52648. viewport.style.cssText = 'overflow:hidden;position:relative';
  52649. this._svgRoot = svgRoot;
  52650. this._viewport = viewport;
  52651. root.appendChild(viewport);
  52652. viewport.appendChild(svgRoot);
  52653. this.resize(opts.width, opts.height);
  52654. this._visibleList = [];
  52655. };
  52656. SVGPainter.prototype = {
  52657. constructor: SVGPainter,
  52658. getType: function () {
  52659. return 'svg';
  52660. },
  52661. getViewportRoot: function () {
  52662. return this._viewport;
  52663. },
  52664. getViewportRootOffset: function () {
  52665. var viewportRoot = this.getViewportRoot();
  52666. if (viewportRoot) {
  52667. return {
  52668. offsetLeft: viewportRoot.offsetLeft || 0,
  52669. offsetTop: viewportRoot.offsetTop || 0
  52670. };
  52671. }
  52672. },
  52673. refresh: function () {
  52674. var list = this.storage.getDisplayList(true);
  52675. this._paintList(list);
  52676. },
  52677. setBackgroundColor: function (backgroundColor) {
  52678. // TODO gradient
  52679. this._viewport.style.background = backgroundColor;
  52680. },
  52681. _paintList: function (list) {
  52682. this.gradientManager.markAllUnused();
  52683. this.clipPathManager.markAllUnused();
  52684. this.shadowManager.markAllUnused();
  52685. var svgRoot = this._svgRoot;
  52686. var visibleList = this._visibleList;
  52687. var listLen = list.length;
  52688. var newVisibleList = [];
  52689. var i;
  52690. for (i = 0; i < listLen; i++) {
  52691. var displayable = list[i];
  52692. var svgProxy = getSvgProxy(displayable);
  52693. var svgElement = getSvgElement(displayable)
  52694. || getTextSvgElement(displayable);
  52695. if (!displayable.invisible) {
  52696. if (displayable.__dirty) {
  52697. svgProxy && svgProxy.brush(displayable);
  52698. // Update clipPath
  52699. this.clipPathManager.update(displayable);
  52700. // Update gradient and shadow
  52701. if (displayable.style) {
  52702. this.gradientManager
  52703. .update(displayable.style.fill);
  52704. this.gradientManager
  52705. .update(displayable.style.stroke);
  52706. this.shadowManager
  52707. .update(svgElement, displayable);
  52708. }
  52709. displayable.__dirty = false;
  52710. }
  52711. newVisibleList.push(displayable);
  52712. }
  52713. }
  52714. var diff = arrayDiff$1(visibleList, newVisibleList);
  52715. var prevSvgElement;
  52716. // First do remove, in case element moved to the head and do remove
  52717. // after add
  52718. for (i = 0; i < diff.length; i++) {
  52719. var item = diff[i];
  52720. if (item.removed) {
  52721. for (var k = 0; k < item.count; k++) {
  52722. var displayable = visibleList[item.indices[k]];
  52723. var svgElement = getSvgElement(displayable);
  52724. var textSvgElement = getTextSvgElement(displayable);
  52725. remove$1(svgRoot, svgElement);
  52726. remove$1(svgRoot, textSvgElement);
  52727. }
  52728. }
  52729. }
  52730. for (i = 0; i < diff.length; i++) {
  52731. var item = diff[i];
  52732. if (item.added) {
  52733. for (var k = 0; k < item.count; k++) {
  52734. var displayable = newVisibleList[item.indices[k]];
  52735. var svgElement = getSvgElement(displayable);
  52736. var textSvgElement = getTextSvgElement(displayable);
  52737. prevSvgElement
  52738. ? insertAfter(svgRoot, svgElement, prevSvgElement)
  52739. : prepend(svgRoot, svgElement);
  52740. if (svgElement) {
  52741. insertAfter(svgRoot, textSvgElement, svgElement);
  52742. }
  52743. else if (prevSvgElement) {
  52744. insertAfter(
  52745. svgRoot, textSvgElement, prevSvgElement
  52746. );
  52747. }
  52748. else {
  52749. prepend(svgRoot, textSvgElement);
  52750. }
  52751. // Insert text
  52752. insertAfter(svgRoot, textSvgElement, svgElement);
  52753. prevSvgElement = textSvgElement || svgElement
  52754. || prevSvgElement;
  52755. this.gradientManager
  52756. .addWithoutUpdate(svgElement, displayable);
  52757. this.shadowManager
  52758. .addWithoutUpdate(prevSvgElement, displayable);
  52759. this.clipPathManager.markUsed(displayable);
  52760. }
  52761. }
  52762. else if (!item.removed) {
  52763. for (var k = 0; k < item.count; k++) {
  52764. var displayable = newVisibleList[item.indices[k]];
  52765. prevSvgElement =
  52766. svgElement =
  52767. getTextSvgElement(displayable)
  52768. || getSvgElement(displayable)
  52769. || prevSvgElement;
  52770. this.gradientManager.markUsed(displayable);
  52771. this.gradientManager
  52772. .addWithoutUpdate(svgElement, displayable);
  52773. this.shadowManager.markUsed(displayable);
  52774. this.shadowManager
  52775. .addWithoutUpdate(svgElement, displayable);
  52776. this.clipPathManager.markUsed(displayable);
  52777. }
  52778. }
  52779. }
  52780. this.gradientManager.removeUnused();
  52781. this.clipPathManager.removeUnused();
  52782. this.shadowManager.removeUnused();
  52783. this._visibleList = newVisibleList;
  52784. },
  52785. _getDefs: function (isForceCreating) {
  52786. var svgRoot = this._svgRoot;
  52787. var defs = this._svgRoot.getElementsByTagName('defs');
  52788. if (defs.length === 0) {
  52789. // Not exist
  52790. if (isForceCreating) {
  52791. var defs = svgRoot.insertBefore(
  52792. createElement('defs'), // Create new tag
  52793. svgRoot.firstChild // Insert in the front of svg
  52794. );
  52795. if (!defs.contains) {
  52796. // IE doesn't support contains method
  52797. defs.contains = function (el) {
  52798. var children = defs.children;
  52799. if (!children) {
  52800. return false;
  52801. }
  52802. for (var i = children.length - 1; i >= 0; --i) {
  52803. if (children[i] === el) {
  52804. return true;
  52805. }
  52806. }
  52807. return false;
  52808. };
  52809. }
  52810. return defs;
  52811. }
  52812. else {
  52813. return null;
  52814. }
  52815. }
  52816. else {
  52817. return defs[0];
  52818. }
  52819. },
  52820. resize: function (width, height) {
  52821. var viewport = this._viewport;
  52822. // FIXME Why ?
  52823. viewport.style.display = 'none';
  52824. // Save input w/h
  52825. var opts = this._opts;
  52826. width != null && (opts.width = width);
  52827. height != null && (opts.height = height);
  52828. width = this._getSize(0);
  52829. height = this._getSize(1);
  52830. viewport.style.display = '';
  52831. if (this._width !== width || this._height !== height) {
  52832. this._width = width;
  52833. this._height = height;
  52834. var viewportStyle = viewport.style;
  52835. viewportStyle.width = width + 'px';
  52836. viewportStyle.height = height + 'px';
  52837. var svgRoot = this._svgRoot;
  52838. // Set width by 'svgRoot.width = width' is invalid
  52839. svgRoot.setAttribute('width', width);
  52840. svgRoot.setAttribute('height', height);
  52841. }
  52842. },
  52843. /**
  52844. * 获取绘图区域宽度
  52845. */
  52846. getWidth: function () {
  52847. return this._width;
  52848. },
  52849. /**
  52850. * 获取绘图区域高度
  52851. */
  52852. getHeight: function () {
  52853. return this._height;
  52854. },
  52855. _getSize: function (whIdx) {
  52856. var opts = this._opts;
  52857. var wh = ['width', 'height'][whIdx];
  52858. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  52859. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  52860. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  52861. if (opts[wh] != null && opts[wh] !== 'auto') {
  52862. return parseFloat(opts[wh]);
  52863. }
  52864. var root = this.root;
  52865. // IE8 does not support getComputedStyle, but it use VML.
  52866. var stl = document.defaultView.getComputedStyle(root);
  52867. return (
  52868. (root[cwh] || parseInt10$2(stl[wh]) || parseInt10$2(root.style[wh]))
  52869. - (parseInt10$2(stl[plt]) || 0)
  52870. - (parseInt10$2(stl[prb]) || 0)
  52871. ) | 0;
  52872. },
  52873. dispose: function () {
  52874. this.root.innerHTML = '';
  52875. this._svgRoot =
  52876. this._viewport =
  52877. this.storage =
  52878. null;
  52879. },
  52880. clear: function () {
  52881. if (this._viewport) {
  52882. this.root.removeChild(this._viewport);
  52883. }
  52884. },
  52885. pathToDataUrl: function () {
  52886. this.refresh();
  52887. var html = this._svgRoot.outerHTML;
  52888. return 'data:image/svg+xml;charset=UTF-8,' + html;
  52889. }
  52890. };
  52891. // Not supported methods
  52892. function createMethodNotSupport$1(method) {
  52893. return function () {
  52894. zrLog('In SVG mode painter not support method "' + method + '"');
  52895. };
  52896. }
  52897. // Unsuppoted methods
  52898. each$1([
  52899. 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer',
  52900. 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer',
  52901. 'toDataURL', 'pathToImage'
  52902. ], function (name) {
  52903. SVGPainter.prototype[name] = createMethodNotSupport$1(name);
  52904. });
  52905. registerPainter('svg', SVGPainter);
  52906. /*
  52907. * Licensed to the Apache Software Foundation (ASF) under one
  52908. * or more contributor license agreements. See the NOTICE file
  52909. * distributed with this work for additional information
  52910. * regarding copyright ownership. The ASF licenses this file
  52911. * to you under the Apache License, Version 2.0 (the
  52912. * "License"); you may not use this file except in compliance
  52913. * with the License. You may obtain a copy of the License at
  52914. *
  52915. * http://www.apache.org/licenses/LICENSE-2.0
  52916. *
  52917. * Unless required by applicable law or agreed to in writing,
  52918. * software distributed under the License is distributed on an
  52919. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  52920. * KIND, either express or implied. See the License for the
  52921. * specific language governing permissions and limitations
  52922. * under the License.
  52923. */
  52924. exports.version = version;
  52925. exports.dependencies = dependencies;
  52926. exports.PRIORITY = PRIORITY;
  52927. exports.init = init;
  52928. exports.connect = connect;
  52929. exports.disConnect = disConnect;
  52930. exports.disconnect = disconnect;
  52931. exports.dispose = dispose;
  52932. exports.getInstanceByDom = getInstanceByDom;
  52933. exports.getInstanceById = getInstanceById;
  52934. exports.registerTheme = registerTheme;
  52935. exports.registerPreprocessor = registerPreprocessor;
  52936. exports.registerProcessor = registerProcessor;
  52937. exports.registerPostUpdate = registerPostUpdate;
  52938. exports.registerAction = registerAction;
  52939. exports.registerCoordinateSystem = registerCoordinateSystem;
  52940. exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
  52941. exports.registerLayout = registerLayout;
  52942. exports.registerVisual = registerVisual;
  52943. exports.registerLoading = registerLoading;
  52944. exports.extendComponentModel = extendComponentModel;
  52945. exports.extendComponentView = extendComponentView;
  52946. exports.extendSeriesModel = extendSeriesModel;
  52947. exports.extendChartView = extendChartView;
  52948. exports.setCanvasCreator = setCanvasCreator;
  52949. exports.registerMap = registerMap;
  52950. exports.getMap = getMap;
  52951. exports.dataTool = dataTool;
  52952. exports.zrender = zrender;
  52953. exports.number = number;
  52954. exports.format = format;
  52955. exports.throttle = throttle;
  52956. exports.helper = helper;
  52957. exports.matrix = matrix;
  52958. exports.vector = vector;
  52959. exports.color = color;
  52960. exports.parseGeoJSON = parseGeoJSON;
  52961. exports.parseGeoJson = parseGeoJson;
  52962. exports.util = ecUtil;
  52963. exports.graphic = graphic$1;
  52964. exports.List = List;
  52965. exports.Model = Model;
  52966. exports.Axis = Axis;
  52967. exports.env = env$1;
  52968. })));