12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxGraph
- *
- * Extends <mxEventSource> to implement a graph component for
- * the browser. This is the main class of the package. To activate
- * panning and connections use <setPanning> and <setConnectable>.
- * For rubberband selection you must create a new instance of
- * <mxRubberband>. The following listeners are added to
- * <mouseListeners> by default:
- *
- * - <tooltipHandler>: <mxTooltipHandler> that displays tooltips
- * - <panningHandler>: <mxPanningHandler> for panning and popup menus
- * - <connectionHandler>: <mxConnectionHandler> for creating connections
- * - <graphHandler>: <mxGraphHandler> for moving and cloning cells
- *
- * These listeners will be called in the above order if they are enabled.
- *
- * Background Images:
- *
- * To display a background image, set the image, image width and
- * image height using <setBackgroundImage>. If one of the
- * above values has changed then the <view>'s <mxGraphView.validate>
- * should be invoked.
- *
- * Cell Images:
- *
- * To use images in cells, a shape must be specified in the default
- * vertex style (or any named style). Possible shapes are
- * <mxConstants.SHAPE_IMAGE> and <mxConstants.SHAPE_LABEL>.
- * The code to change the shape used in the default vertex style,
- * the following code is used:
- *
- * (code)
- * var style = graph.getStylesheet().getDefaultVertexStyle();
- * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
- * (end)
- *
- * For the default vertex style, the image to be displayed can be
- * specified in a cell's style using the <mxConstants.STYLE_IMAGE>
- * key and the image URL as a value, for example:
- *
- * (code)
- * image=http://www.example.com/image.gif
- * (end)
- *
- * For a named style, the the stylename must be the first element
- * of the cell style:
- *
- * (code)
- * stylename;image=http://www.example.com/image.gif
- * (end)
- *
- * A cell style can have any number of key=value pairs added, divided
- * by a semicolon as follows:
- *
- * (code)
- * [stylename;|key=value;]
- * (end)
- *
- * Labels:
- *
- * The cell labels are defined by <getLabel> which uses <convertValueToString>
- * if <labelsVisible> is true. If a label must be rendered as HTML markup, then
- * <isHtmlLabel> should return true for the respective cell. If all labels
- * contain HTML markup, <htmlLabels> can be set to true. NOTE: Enabling HTML
- * labels carries a possible security risk (see the section on security in
- * the manual).
- *
- * If wrapping is needed for a label, then <isHtmlLabel> and <isWrapping> must
- * return true for the cell whose label should be wrapped. See <isWrapping> for
- * an example.
- *
- * If clipping is needed to keep the rendering of a HTML label inside the
- * bounds of its vertex, then <isClipping> should return true for the
- * respective cell.
- *
- * By default, edge labels are movable and vertex labels are fixed. This can be
- * changed by setting <edgeLabelsMovable> and <vertexLabelsMovable>, or by
- * overriding <isLabelMovable>.
- *
- * In-place Editing:
- *
- * In-place editing is started with a doubleclick or by typing F2.
- * Programmatically, <edit> is used to check if the cell is editable
- * (<isCellEditable>) and call <startEditingAtCell>, which invokes
- * <mxCellEditor.startEditing>. The editor uses the value returned
- * by <getEditingValue> as the editing value.
- *
- * After in-place editing, <labelChanged> is called, which invokes
- * <mxGraphModel.setValue>, which in turn calls
- * <mxGraphModel.valueForCellChanged> via <mxValueChange>.
- *
- * The event that triggers in-place editing is passed through to the
- * <cellEditor>, which may take special actions depending on the type of the
- * event or mouse location, and is also passed to <getEditingValue>. The event
- * is then passed back to the event processing functions which can perform
- * specific actions based on the trigger event.
- *
- * Tooltips:
- *
- * Tooltips are implemented by <getTooltip>, which calls <getTooltipForCell>
- * if a cell is under the mousepointer. The default implementation checks if
- * the cell has a getTooltip function and calls it if it exists. Hence, in order
- * to provide custom tooltips, the cell must provide a getTooltip function, or
- * one of the two above functions must be overridden.
- *
- * Typically, for custom cell tooltips, the latter function is overridden as
- * follows:
- *
- * (code)
- * graph.getTooltipForCell = function(cell)
- * {
- * var label = this.convertValueToString(cell);
- * return 'Tooltip for '+label;
- * }
- * (end)
- *
- * When using a config file, the function is overridden in the mxGraph section
- * using the following entry:
- *
- * (code)
- * <add as="getTooltipForCell"><![CDATA[
- * function(cell)
- * {
- * var label = this.convertValueToString(cell);
- * return 'Tooltip for '+label;
- * }
- * ]]></add>
- * (end)
- *
- * "this" refers to the graph in the implementation, so for example to check if
- * a cell is an edge, you use this.getModel().isEdge(cell)
- *
- * For replacing the default implementation of <getTooltipForCell> (rather than
- * replacing the function on a specific instance), the following code should be
- * used after loading the JavaScript files, but before creating a new mxGraph
- * instance using <mxGraph>:
- *
- * (code)
- * mxGraph.prototype.getTooltipForCell = function(cell)
- * {
- * var label = this.convertValueToString(cell);
- * return 'Tooltip for '+label;
- * }
- * (end)
- *
- * Shapes & Styles:
- *
- * The implementation of new shapes is demonstrated in the examples. We'll assume
- * that we have implemented a custom shape with the name BoxShape which we want
- * to use for drawing vertices. To use this shape, it must first be registered in
- * the cell renderer as follows:
- *
- * (code)
- * mxCellRenderer.registerShape('box', BoxShape);
- * (end)
- *
- * The code registers the BoxShape constructor under the name box in the cell
- * renderer of the graph. The shape can now be referenced using the shape-key in
- * a style definition. (The cell renderer contains a set of additional shapes,
- * namely one for each constant with a SHAPE-prefix in <mxConstants>.)
- *
- * Styles are a collection of key, value pairs and a stylesheet is a collection
- * of named styles. The names are referenced by the cellstyle, which is stored
- * in <mxCell.style> with the following format: [stylename;|key=value;]. The
- * string is resolved to a collection of key, value pairs, where the keys are
- * overridden with the values in the string.
- *
- * When introducing a new shape, the name under which the shape is registered
- * must be used in the stylesheet. There are three ways of doing this:
- *
- * - By changing the default style, so that all vertices will use the new
- * shape
- * - By defining a new style, so that only vertices with the respective
- * cellstyle will use the new shape
- * - By using shape=box in the cellstyle's optional list of key, value pairs
- * to be overridden
- *
- * In the first case, the code to fetch and modify the default style for
- * vertices is as follows:
- *
- * (code)
- * var style = graph.getStylesheet().getDefaultVertexStyle();
- * style[mxConstants.STYLE_SHAPE] = 'box';
- * (end)
- *
- * The code takes the default vertex style, which is used for all vertices that
- * do not have a specific cellstyle, and modifies the value for the shape-key
- * in-place to use the new BoxShape for drawing vertices. This is done by
- * assigning the box value in the second line, which refers to the name of the
- * BoxShape in the cell renderer.
- *
- * In the second case, a collection of key, value pairs is created and then
- * added to the stylesheet under a new name. In order to distinguish the
- * shapename and the stylename we'll use boxstyle for the stylename:
- *
- * (code)
- * var style = new Object();
- * style[mxConstants.STYLE_SHAPE] = 'box';
- * style[mxConstants.STYLE_STROKECOLOR] = '#000000';
- * style[mxConstants.STYLE_FONTCOLOR] = '#000000';
- * graph.getStylesheet().putCellStyle('boxstyle', style);
- * (end)
- *
- * The code adds a new style with the name boxstyle to the stylesheet. To use
- * this style with a cell, it must be referenced from the cellstyle as follows:
- *
- * (code)
- * var vertex = graph.insertVertex(parent, null, 'Hello, World!', 20, 20, 80, 20,
- * 'boxstyle');
- * (end)
- *
- * To summarize, each new shape must be registered in the <mxCellRenderer> with
- * a unique name. That name is then used as the value of the shape-key in a
- * default or custom style. If there are multiple custom shapes, then there
- * should be a separate style for each shape.
- *
- * Inheriting Styles:
- *
- * For fill-, stroke-, gradient-, font- and indicatorColors special keywords
- * can be used. The inherit keyword for one of these colors will inherit the
- * color for the same key from the parent cell. The swimlane keyword does the
- * same, but inherits from the nearest swimlane in the ancestor hierarchy.
- * Finally, the indicated keyword will use the color of the indicator as the
- * color for the given key.
- *
- * Scrollbars:
- *
- * The <containers> overflow CSS property defines if scrollbars are used to
- * display the graph. For values of 'auto' or 'scroll', the scrollbars will
- * be shown. Note that the <resizeContainer> flag is normally not used
- * together with scrollbars, as it will resize the container to match the
- * size of the graph after each change.
- *
- * Multiplicities and Validation:
- *
- * To control the possible connections in mxGraph, <getEdgeValidationError> is
- * used. The default implementation of the function uses <multiplicities>,
- * which is an array of <mxMultiplicity>. Using this class allows to establish
- * simple multiplicities, which are enforced by the graph.
- *
- * The <mxMultiplicity> uses <mxCell.is> to determine for which terminals it
- * applies. The default implementation of <mxCell.is> works with DOM nodes (XML
- * nodes) and checks if the given type parameter matches the nodeName of the
- * node (case insensitive). Optionally, an attributename and value can be
- * specified which are also checked.
- *
- * <getEdgeValidationError> is called whenever the connectivity of an edge
- * changes. It returns an empty string or an error message if the edge is
- * invalid or null if the edge is valid. If the returned string is not empty
- * then it is displayed as an error message.
- *
- * <mxMultiplicity> allows to specify the multiplicity between a terminal and
- * its possible neighbors. For example, if any rectangle may only be connected
- * to, say, a maximum of two circles you can add the following rule to
- * <multiplicities>:
- *
- * (code)
- * graph.multiplicities.push(new mxMultiplicity(
- * true, 'rectangle', null, null, 0, 2, ['circle'],
- * 'Only 2 targets allowed',
- * 'Only shape targets allowed'));
- * (end)
- *
- * This will display the first error message whenever a rectangle is connected
- * to more than two circles and the second error message if a rectangle is
- * connected to anything but a circle.
- *
- * For certain multiplicities, such as a minimum of 1 connection, which cannot
- * be enforced at cell creation time (unless the cell is created together with
- * the connection), mxGraph offers <validate> which checks all multiplicities
- * for all cells and displays the respective error messages in an overlay icon
- * on the cells.
- *
- * If a cell is collapsed and contains validation errors, a respective warning
- * icon is attached to the collapsed cell.
- *
- * Auto-Layout:
- *
- * For automatic layout, the <getLayout> hook is provided in <mxLayoutManager>.
- * It can be overridden to return a layout algorithm for the children of a
- * given cell.
- *
- * Unconnected edges:
- *
- * The default values for all switches are designed to meet the requirements of
- * general diagram drawing applications. A very typical set of settings to
- * avoid edges that are not connected is the following:
- *
- * (code)
- * graph.setAllowDanglingEdges(false);
- * graph.setDisconnectOnMove(false);
- * (end)
- *
- * Setting the <cloneInvalidEdges> switch to true is optional. This switch
- * controls if edges are inserted after a copy, paste or clone-drag if they are
- * invalid. For example, edges are invalid if copied or control-dragged without
- * having selected the corresponding terminals and allowDanglingEdges is
- * false, in which case the edges will not be cloned if the switch is false.
- *
- * Output:
- *
- * To produce an XML representation for a diagram, the following code can be
- * used.
- *
- * (code)
- * var enc = new mxCodec(mxUtils.createXmlDocument());
- * var node = enc.encode(graph.getModel());
- * (end)
- *
- * This will produce an XML node than can be handled using the DOM API or
- * turned into a string representation using the following code:
- *
- * (code)
- * var xml = mxUtils.getXml(node);
- * (end)
- *
- * To obtain a formatted string, mxUtils.getPrettyXml can be used instead.
- *
- * This string can now be stored in a local persistent storage (for example
- * using Google Gears) or it can be passed to a backend using mxUtils.post as
- * follows. The url variable is the URL of the Java servlet, PHP page or HTTP
- * handler, depending on the server.
- *
- * (code)
- * var xmlString = encodeURIComponent(mxUtils.getXml(node));
- * mxUtils.post(url, 'xml='+xmlString, function(req)
- * {
- * // Process server response using req of type mxXmlRequest
- * });
- * (end)
- *
- * Input:
- *
- * To load an XML representation of a diagram into an existing graph object
- * mxUtils.load can be used as follows. The url variable is the URL of the Java
- * servlet, PHP page or HTTP handler that produces the XML string.
- *
- * (code)
- * var xmlDoc = mxUtils.load(url).getXml();
- * var node = xmlDoc.documentElement;
- * var dec = new mxCodec(node.ownerDocument);
- * dec.decode(node, graph.getModel());
- * (end)
- *
- * For creating a page that loads the client and a diagram using a single
- * request please refer to the deployment examples in the backends.
- *
- * Functional dependencies:
- *
- * (see images/callgraph.png)
- *
- * Resources:
- *
- * resources/graph - Language resources for mxGraph
- *
- * Group: Events
- *
- * Event: mxEvent.ROOT
- *
- * Fires if the root in the model has changed. This event has no properties.
- *
- * Event: mxEvent.ALIGN_CELLS
- *
- * Fires between begin- and endUpdate in <alignCells>. The <code>cells</code>
- * and <code>align</code> properties contain the respective arguments that were
- * passed to <alignCells>.
- *
- * Event: mxEvent.FLIP_EDGE
- *
- * Fires between begin- and endUpdate in <flipEdge>. The <code>edge</code>
- * property contains the edge passed to <flipEdge>.
- *
- * Event: mxEvent.ORDER_CELLS
- *
- * Fires between begin- and endUpdate in <orderCells>. The <code>cells</code>
- * and <code>back</code> properties contain the respective arguments that were
- * passed to <orderCells>.
- *
- * Event: mxEvent.CELLS_ORDERED
- *
- * Fires between begin- and endUpdate in <cellsOrdered>. The <code>cells</code>
- * and <code>back</code> arguments contain the respective arguments that were
- * passed to <cellsOrdered>.
- *
- * Event: mxEvent.GROUP_CELLS
- *
- * Fires between begin- and endUpdate in <groupCells>. The <code>group</code>,
- * <code>cells</code> and <code>border</code> arguments contain the respective
- * arguments that were passed to <groupCells>.
- *
- * Event: mxEvent.UNGROUP_CELLS
- *
- * Fires between begin- and endUpdate in <ungroupCells>. The <code>cells</code>
- * property contains the array of cells that was passed to <ungroupCells>.
- *
- * Event: mxEvent.REMOVE_CELLS_FROM_PARENT
- *
- * Fires between begin- and endUpdate in <removeCellsFromParent>. The
- * <code>cells</code> property contains the array of cells that was passed to
- * <removeCellsFromParent>.
- *
- * Event: mxEvent.ADD_CELLS
- *
- * Fires between begin- and endUpdate in <addCells>. The <code>cells</code>,
- * <code>parent</code>, <code>index</code>, <code>source</code> and
- * <code>target</code> properties contain the respective arguments that were
- * passed to <addCells>.
- *
- * Event: mxEvent.CELLS_ADDED
- *
- * Fires between begin- and endUpdate in <cellsAdded>. The <code>cells</code>,
- * <code>parent</code>, <code>index</code>, <code>source</code>,
- * <code>target</code> and <code>absolute</code> properties contain the
- * respective arguments that were passed to <cellsAdded>.
- *
- * Event: mxEvent.REMOVE_CELLS
- *
- * Fires between begin- and endUpdate in <removeCells>. The <code>cells</code>
- * and <code>includeEdges</code> arguments contain the respective arguments
- * that were passed to <removeCells>.
- *
- * Event: mxEvent.CELLS_REMOVED
- *
- * Fires between begin- and endUpdate in <cellsRemoved>. The <code>cells</code>
- * argument contains the array of cells that was removed.
- *
- * Event: mxEvent.SPLIT_EDGE
- *
- * Fires between begin- and endUpdate in <splitEdge>. The <code>edge</code>
- * property contains the edge to be splitted, the <code>cells</code>,
- * <code>newEdge</code>, <code>dx</code> and <code>dy</code> properties contain
- * the respective arguments that were passed to <splitEdge>.
- *
- * Event: mxEvent.TOGGLE_CELLS
- *
- * Fires between begin- and endUpdate in <toggleCells>. The <code>show</code>,
- * <code>cells</code> and <code>includeEdges</code> properties contain the
- * respective arguments that were passed to <toggleCells>.
- *
- * Event: mxEvent.FOLD_CELLS
- *
- * Fires between begin- and endUpdate in <foldCells>. The
- * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
- * properties contain the respective arguments that were passed to <foldCells>.
- *
- * Event: mxEvent.CELLS_FOLDED
- *
- * Fires between begin- and endUpdate in cellsFolded. The
- * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
- * properties contain the respective arguments that were passed to
- * <cellsFolded>.
- *
- * Event: mxEvent.UPDATE_CELL_SIZE
- *
- * Fires between begin- and endUpdate in <updateCellSize>. The
- * <code>cell</code> and <code>ignoreChildren</code> properties contain the
- * respective arguments that were passed to <updateCellSize>.
- *
- * Event: mxEvent.RESIZE_CELLS
- *
- * Fires between begin- and endUpdate in <resizeCells>. The <code>cells</code>
- * and <code>bounds</code> properties contain the respective arguments that
- * were passed to <resizeCells>.
- *
- * Event: mxEvent.CELLS_RESIZED
- *
- * Fires between begin- and endUpdate in <cellsResized>. The <code>cells</code>
- * and <code>bounds</code> properties contain the respective arguments that
- * were passed to <cellsResized>.
- *
- * Event: mxEvent.MOVE_CELLS
- *
- * Fires between begin- and endUpdate in <moveCells>. The <code>cells</code>,
- * <code>dx</code>, <code>dy</code>, <code>clone</code>, <code>target</code>
- * and <code>event</code> properties contain the respective arguments that
- * were passed to <moveCells>.
- *
- * Event: mxEvent.CELLS_MOVED
- *
- * Fires between begin- and endUpdate in <cellsMoved>. The <code>cells</code>,
- * <code>dx</code>, <code>dy</code> and <code>disconnect</code> properties
- * contain the respective arguments that were passed to <cellsMoved>.
- *
- * Event: mxEvent.CONNECT_CELL
- *
- * Fires between begin- and endUpdate in <connectCell>. The <code>edge</code>,
- * <code>terminal</code> and <code>source</code> properties contain the
- * respective arguments that were passed to <connectCell>.
- *
- * Event: mxEvent.CELL_CONNECTED
- *
- * Fires between begin- and endUpdate in <cellConnected>. The
- * <code>edge</code>, <code>terminal</code> and <code>source</code> properties
- * contain the respective arguments that were passed to <cellConnected>.
- *
- * Event: mxEvent.REFRESH
- *
- * Fires after <refresh> was executed. This event has no properties.
- *
- * Event: mxEvent.CLICK
- *
- * Fires in <click> after a click event. The <code>event</code> property
- * contains the original mouse event and <code>cell</code> property contains
- * the cell under the mouse or null if the background was clicked.
- *
- * Event: mxEvent.DOUBLE_CLICK
- *
- * Fires in <dblClick> after a double click. The <code>event</code> property
- * contains the original mouse event and the <code>cell</code> property
- * contains the cell under the mouse or null if the background was clicked.
- *
- * Event: mxEvent.GESTURE
- *
- * Fires in <fireGestureEvent> after a touch gesture. The <code>event</code>
- * property contains the original gesture end event and the <code>cell</code>
- * property contains the optional cell associated with the gesture.
- *
- * Event: mxEvent.TAP_AND_HOLD
- *
- * Fires in <tapAndHold> if a tap and hold event was detected. The <code>event</code>
- * property contains the initial touch event and the <code>cell</code> property
- * contains the cell under the mouse or null if the background was clicked.
- *
- * Event: mxEvent.FIRE_MOUSE_EVENT
- *
- * Fires in <fireMouseEvent> before the mouse listeners are invoked. The
- * <code>eventName</code> property contains the event name and the
- * <code>event</code> property contains the <mxMouseEvent>.
- *
- * Event: mxEvent.SIZE
- *
- * Fires after <sizeDidChange> was executed. The <code>bounds</code> property
- * contains the new graph bounds.
- *
- * Event: mxEvent.START_EDITING
- *
- * Fires before the in-place editor starts in <startEditingAtCell>. The
- * <code>cell</code> property contains the cell that is being edited and the
- * <code>event</code> property contains the optional event argument that was
- * passed to <startEditingAtCell>.
- *
- * Event: mxEvent.EDITING_STARTED
- *
- * Fires after the in-place editor starts in <startEditingAtCell>. The
- * <code>cell</code> property contains the cell that is being edited and the
- * <code>event</code> property contains the optional event argument that was
- * passed to <startEditingAtCell>.
- *
- * Event: mxEvent.EDITING_STOPPED
- *
- * Fires after the in-place editor stops in <stopEditing>.
- *
- * Event: mxEvent.LABEL_CHANGED
- *
- * Fires between begin- and endUpdate in <cellLabelChanged>. The
- * <code>cell</code> property contains the cell, the <code>value</code>
- * property contains the new value for the cell, the <code>old</code> property
- * contains the old value and the optional <code>event</code> property contains
- * the mouse event that started the edit.
- *
- * Event: mxEvent.ADD_OVERLAY
- *
- * Fires after an overlay is added in <addCellOverlay>. The <code>cell</code>
- * property contains the cell and the <code>overlay</code> property contains
- * the <mxCellOverlay> that was added.
- *
- * Event: mxEvent.REMOVE_OVERLAY
- *
- * Fires after an overlay is removed in <removeCellOverlay> and
- * <removeCellOverlays>. The <code>cell</code> property contains the cell and
- * the <code>overlay</code> property contains the <mxCellOverlay> that was
- * removed.
- *
- * Constructor: mxGraph
- *
- * Constructs a new mxGraph in the specified container. Model is an optional
- * mxGraphModel. If no model is provided, a new mxGraphModel instance is
- * used as the model. The container must have a valid owner document prior
- * to calling this function in Internet Explorer. RenderHint is a string to
- * affect the display performance and rendering in IE, but not in SVG-based
- * browsers. The parameter is mapped to <dialect>, which may
- * be one of <mxConstants.DIALECT_SVG> for SVG-based browsers,
- * <mxConstants.DIALECT_STRICTHTML> for fastest display mode,
- * <mxConstants.DIALECT_PREFERHTML> for faster display mode,
- * <mxConstants.DIALECT_MIXEDHTML> for fast and <mxConstants.DIALECT_VML>
- * for exact display mode (slowest). The dialects are defined in mxConstants.
- * The default values are DIALECT_SVG for SVG-based browsers and
- * DIALECT_MIXED for IE.
- *
- * The possible values for the renderingHint parameter are explained below:
- *
- * fast - The parameter is based on the fact that the display performance is
- * highly improved in IE if the VML is not contained within a VML group
- * element. The lack of a group element only slightly affects the display while
- * panning, but improves the performance by almost a factor of 2, while keeping
- * the display sufficiently accurate. This also allows to render certain shapes as HTML
- * if the display accuracy is not affected, which is implemented by
- * <mxShape.isMixedModeHtml>. This is the default setting and is mapped to
- * DIALECT_MIXEDHTML.
- * faster - Same as fast, but more expensive shapes are avoided. This is
- * controlled by <mxShape.preferModeHtml>. The default implementation will
- * avoid gradients and rounded rectangles, but more significant shapes, such
- * as rhombus, ellipse, actor and cylinder will be rendered accurately. This
- * setting is mapped to DIALECT_PREFERHTML.
- * fastest - Almost anything will be rendered in Html. This allows for
- * rectangles, labels and images. This setting is mapped to
- * DIALECT_STRICTHTML.
- * exact - If accurate panning is required and if the diagram is small (up
- * to 100 cells), then this value should be used. In this mode, a group is
- * created that contains the VML. This allows for accurate panning and is
- * mapped to DIALECT_VML.
- *
- * Example:
- *
- * To create a graph inside a DOM node with an id of graph:
- * (code)
- * var container = document.getElementById('graph');
- * var graph = new mxGraph(container);
- * (end)
- *
- * Parameters:
- *
- * container - Optional DOM node that acts as a container for the graph.
- * If this is null then the container can be initialized later using
- * <init>.
- * model - Optional <mxGraphModel> that constitutes the graph data.
- * renderHint - Optional string that specifies the display accuracy and
- * performance. Default is mxConstants.DIALECT_MIXEDHTML (for IE).
- * stylesheet - Optional <mxStylesheet> to be used in the graph.
- */
- function mxGraph(container, model, renderHint, stylesheet)
- {
- // Initializes the variable in case the prototype has been
- // modified to hold some listeners (which is possible because
- // the createHandlers call is executed regardless of the
- // arguments passed into the ctor).
- this.mouseListeners = null;
-
- // Converts the renderHint into a dialect
- this.renderHint = renderHint;
- if (mxClient.IS_SVG)
- {
- this.dialect = mxConstants.DIALECT_SVG;
- }
- else if (renderHint == mxConstants.RENDERING_HINT_EXACT && mxClient.IS_VML)
- {
- this.dialect = mxConstants.DIALECT_VML;
- }
- else if (renderHint == mxConstants.RENDERING_HINT_FASTEST)
- {
- this.dialect = mxConstants.DIALECT_STRICTHTML;
- }
- else if (renderHint == mxConstants.RENDERING_HINT_FASTER)
- {
- this.dialect = mxConstants.DIALECT_PREFERHTML;
- }
- else // default for VML
- {
- this.dialect = mxConstants.DIALECT_MIXEDHTML;
- }
-
- // Initializes the main members that do not require a container
- this.model = (model != null) ? model : new mxGraphModel();
- this.multiplicities = [];
- this.imageBundles = [];
- this.cellRenderer = this.createCellRenderer();
- this.setSelectionModel(this.createSelectionModel());
- this.setStylesheet((stylesheet != null) ? stylesheet : this.createStylesheet());
- this.view = this.createGraphView();
-
- // Adds a graph model listener to update the view
- this.graphModelChangeListener = mxUtils.bind(this, function(sender, evt)
- {
- this.graphModelChanged(evt.getProperty('edit').changes);
- });
-
- this.model.addListener(mxEvent.CHANGE, this.graphModelChangeListener);
- // Installs basic event handlers with disabled default settings.
- this.createHandlers();
-
- // Initializes the display if a container was specified
- if (container != null)
- {
- this.init(container);
- }
-
- this.view.revalidate();
- };
- /**
- * Installs the required language resources at class
- * loading time.
- */
- if (mxLoadResources)
- {
- mxResources.add(mxClient.basePath + '/resources/graph');
- }
- else
- {
- mxClient.defaultBundles.push(mxClient.basePath + '/resources/graph');
- }
- /**
- * Extends mxEventSource.
- */
- mxGraph.prototype = new mxEventSource();
- mxGraph.prototype.constructor = mxGraph;
- /**
- * Group: Variables
- */
- /**
- * Variable: mouseListeners
- *
- * Holds the mouse event listeners. See <fireMouseEvent>.
- */
- mxGraph.prototype.mouseListeners = null;
- /**
- * Variable: isMouseDown
- *
- * Holds the state of the mouse button.
- */
- mxGraph.prototype.isMouseDown = false;
- /**
- * Variable: model
- *
- * Holds the <mxGraphModel> that contains the cells to be displayed.
- */
- mxGraph.prototype.model = null;
- /**
- * Variable: view
- *
- * Holds the <mxGraphView> that caches the <mxCellStates> for the cells.
- */
- mxGraph.prototype.view = null;
- /**
- * Variable: stylesheet
- *
- * Holds the <mxStylesheet> that defines the appearance of the cells.
- *
- *
- * Example:
- *
- * Use the following code to read a stylesheet into an existing graph.
- *
- * (code)
- * var req = mxUtils.load('stylesheet.xml');
- * var root = req.getDocumentElement();
- * var dec = new mxCodec(root.ownerDocument);
- * dec.decode(root, graph.stylesheet);
- * (end)
- */
- mxGraph.prototype.stylesheet = null;
-
- /**
- * Variable: selectionModel
- *
- * Holds the <mxGraphSelectionModel> that models the current selection.
- */
- mxGraph.prototype.selectionModel = null;
- /**
- * Variable: cellEditor
- *
- * Holds the <mxCellEditor> that is used as the in-place editing.
- */
- mxGraph.prototype.cellEditor = null;
- /**
- * Variable: cellRenderer
- *
- * Holds the <mxCellRenderer> for rendering the cells in the graph.
- */
- mxGraph.prototype.cellRenderer = null;
- /**
- * Variable: multiplicities
- *
- * An array of <mxMultiplicities> describing the allowed
- * connections in a graph.
- */
- mxGraph.prototype.multiplicities = null;
- /**
- * Variable: renderHint
- *
- * RenderHint as it was passed to the constructor.
- */
- mxGraph.prototype.renderHint = null;
- /**
- * Variable: dialect
- *
- * Dialect to be used for drawing the graph. Possible values are all
- * constants in <mxConstants> with a DIALECT-prefix.
- */
- mxGraph.prototype.dialect = null;
- /**
- * Variable: gridSize
- *
- * Specifies the grid size. Default is 10.
- */
- mxGraph.prototype.gridSize = 10;
-
- /**
- * Variable: gridEnabled
- *
- * Specifies if the grid is enabled. This is used in <snap>. Default is
- * true.
- */
- mxGraph.prototype.gridEnabled = true;
- /**
- * Variable: portsEnabled
- *
- * Specifies if ports are enabled. This is used in <cellConnected> to update
- * the respective style. Default is true.
- */
- mxGraph.prototype.portsEnabled = true;
- /**
- * Variable: nativeDoubleClickEnabled
- *
- * Specifies if native double click events should be detected. Default is true.
- */
- mxGraph.prototype.nativeDblClickEnabled = true;
- /**
- * Variable: doubleTapEnabled
- *
- * Specifies if double taps on touch-based devices should be handled as a
- * double click. Default is true.
- */
- mxGraph.prototype.doubleTapEnabled = true;
- /**
- * Variable: doubleTapTimeout
- *
- * Specifies the timeout for double taps and non-native double clicks. Default
- * is 500 ms.
- */
- mxGraph.prototype.doubleTapTimeout = 500;
- /**
- * Variable: doubleTapTolerance
- *
- * Specifies the tolerance for double taps and double clicks in quirks mode.
- * Default is 25 pixels.
- */
- mxGraph.prototype.doubleTapTolerance = 25;
- /**
- * Variable: lastTouchX
- *
- * Holds the x-coordinate of the last touch event for double tap detection.
- */
- mxGraph.prototype.lastTouchY = 0;
- /**
- * Variable: lastTouchX
- *
- * Holds the y-coordinate of the last touch event for double tap detection.
- */
- mxGraph.prototype.lastTouchY = 0;
- /**
- * Variable: lastTouchTime
- *
- * Holds the time of the last touch event for double click detection.
- */
- mxGraph.prototype.lastTouchTime = 0;
- /**
- * Variable: tapAndHoldEnabled
- *
- * Specifies if tap and hold should be used for starting connections on touch-based
- * devices. Default is true.
- */
- mxGraph.prototype.tapAndHoldEnabled = true;
- /**
- * Variable: tapAndHoldDelay
- *
- * Specifies the time for a tap and hold. Default is 500 ms.
- */
- mxGraph.prototype.tapAndHoldDelay = 500;
- /**
- * Variable: tapAndHoldInProgress
- *
- * True if the timer for tap and hold events is running.
- */
- mxGraph.prototype.tapAndHoldInProgress = false;
- /**
- * Variable: tapAndHoldValid
- *
- * True as long as the timer is running and the touch events
- * stay within the given <tapAndHoldTolerance>.
- */
- mxGraph.prototype.tapAndHoldValid = false;
- /**
- * Variable: initialTouchX
- *
- * Holds the x-coordinate of the intial touch event for tap and hold.
- */
- mxGraph.prototype.initialTouchX = 0;
- /**
- * Variable: initialTouchY
- *
- * Holds the y-coordinate of the intial touch event for tap and hold.
- */
- mxGraph.prototype.initialTouchY = 0;
- /**
- * Variable: tolerance
- *
- * Tolerance for a move to be handled as a single click.
- * Default is 4 pixels.
- */
- mxGraph.prototype.tolerance = 4;
- /**
- * Variable: defaultOverlap
- *
- * Value returned by <getOverlap> if <isAllowOverlapParent> returns
- * true for the given cell. <getOverlap> is used in <constrainChild> if
- * <isConstrainChild> returns true. The value specifies the
- * portion of the child which is allowed to overlap the parent.
- */
- mxGraph.prototype.defaultOverlap = 0.5;
- /**
- * Variable: defaultParent
- *
- * Specifies the default parent to be used to insert new cells.
- * This is used in <getDefaultParent>. Default is null.
- */
- mxGraph.prototype.defaultParent = null;
- /**
- * Variable: alternateEdgeStyle
- *
- * Specifies the alternate edge style to be used if the main control point
- * on an edge is being doubleclicked. Default is null.
- */
- mxGraph.prototype.alternateEdgeStyle = null;
- /**
- * Variable: backgroundImage
- *
- * Specifies the <mxImage> to be returned by <getBackgroundImage>. Default
- * is null.
- *
- * Example:
- *
- * (code)
- * var img = new mxImage('http://www.example.com/maps/examplemap.jpg', 1024, 768);
- * graph.setBackgroundImage(img);
- * graph.view.validate();
- * (end)
- */
- mxGraph.prototype.backgroundImage = null;
- /**
- * Variable: pageVisible
- *
- * Specifies if the background page should be visible. Default is false.
- * Not yet implemented.
- */
- mxGraph.prototype.pageVisible = false;
- /**
- * Variable: pageBreaksVisible
- *
- * Specifies if a dashed line should be drawn between multiple pages. Default
- * is false. If you change this value while a graph is being displayed then you
- * should call <sizeDidChange> to force an update of the display.
- */
- mxGraph.prototype.pageBreaksVisible = false;
- /**
- * Variable: pageBreakColor
- *
- * Specifies the color for page breaks. Default is 'gray'.
- */
- mxGraph.prototype.pageBreakColor = 'gray';
- /**
- * Variable: pageBreakDashed
- *
- * Specifies the page breaks should be dashed. Default is true.
- */
- mxGraph.prototype.pageBreakDashed = true;
- /**
- * Variable: minPageBreakDist
- *
- * Specifies the minimum distance for page breaks to be visible. Default is
- * 20 (in pixels).
- */
- mxGraph.prototype.minPageBreakDist = 20;
- /**
- * Variable: preferPageSize
- *
- * Specifies if the graph size should be rounded to the next page number in
- * <sizeDidChange>. This is only used if the graph container has scrollbars.
- * Default is false.
- */
- mxGraph.prototype.preferPageSize = false;
- /**
- * Variable: pageFormat
- *
- * Specifies the page format for the background page. Default is
- * <mxConstants.PAGE_FORMAT_A4_PORTRAIT>. This is used as the default in
- * <mxPrintPreview> and for painting the background page if <pageVisible> is
- * true and the pagebreaks if <pageBreaksVisible> is true.
- */
- mxGraph.prototype.pageFormat = mxConstants.PAGE_FORMAT_A4_PORTRAIT;
- /**
- * Variable: pageScale
- *
- * Specifies the scale of the background page. Default is 1.5.
- * Not yet implemented.
- */
- mxGraph.prototype.pageScale = 1.5;
- /**
- * Variable: enabled
- *
- * Specifies the return value for <isEnabled>. Default is true.
- */
- mxGraph.prototype.enabled = true;
- /**
- * Variable: escapeEnabled
- *
- * Specifies if <mxKeyHandler> should invoke <escape> when the escape key
- * is pressed. Default is true.
- */
- mxGraph.prototype.escapeEnabled = true;
- /**
- * Variable: invokesStopCellEditing
- *
- * If true, when editing is to be stopped by way of selection changing,
- * data in diagram changing or other means stopCellEditing is invoked, and
- * changes are saved. This is implemented in a focus handler in
- * <mxCellEditor>. Default is true.
- */
- mxGraph.prototype.invokesStopCellEditing = true;
- /**
- * Variable: enterStopsCellEditing
- *
- * If true, pressing the enter key without pressing control or shift will stop
- * editing and accept the new value. This is used in <mxCellEditor> to stop
- * cell editing. Note: You can always use F2 and escape to stop editing.
- * Default is false.
- */
- mxGraph.prototype.enterStopsCellEditing = false;
- /**
- * Variable: useScrollbarsForPanning
- *
- * Specifies if scrollbars should be used for panning in <panGraph> if
- * any scrollbars are available. If scrollbars are enabled in CSS, but no
- * scrollbars appear because the graph is smaller than the container size,
- * then no panning occurs if this is true. Default is true.
- */
- mxGraph.prototype.useScrollbarsForPanning = true;
- /**
- * Variable: exportEnabled
- *
- * Specifies the return value for <canExportCell>. Default is true.
- */
- mxGraph.prototype.exportEnabled = true;
- /**
- * Variable: importEnabled
- *
- * Specifies the return value for <canImportCell>. Default is true.
- */
- mxGraph.prototype.importEnabled = true;
- /**
- * Variable: cellsLocked
- *
- * Specifies the return value for <isCellLocked>. Default is false.
- */
- mxGraph.prototype.cellsLocked = false;
- /**
- * Variable: cellsCloneable
- *
- * Specifies the return value for <isCellCloneable>. Default is true.
- */
- mxGraph.prototype.cellsCloneable = true;
- /**
- * Variable: foldingEnabled
- *
- * Specifies if folding (collapse and expand via an image icon in the graph
- * should be enabled). Default is true.
- */
- mxGraph.prototype.foldingEnabled = true;
- /**
- * Variable: cellsEditable
- *
- * Specifies the return value for <isCellEditable>. Default is true.
- */
- mxGraph.prototype.cellsEditable = true;
-
- /**
- * Variable: cellsDeletable
- *
- * Specifies the return value for <isCellDeletable>. Default is true.
- */
- mxGraph.prototype.cellsDeletable = true;
- /**
- * Variable: cellsMovable
- *
- * Specifies the return value for <isCellMovable>. Default is true.
- */
- mxGraph.prototype.cellsMovable = true;
-
- /**
- * Variable: edgeLabelsMovable
- *
- * Specifies the return value for edges in <isLabelMovable>. Default is true.
- */
- mxGraph.prototype.edgeLabelsMovable = true;
-
- /**
- * Variable: vertexLabelsMovable
- *
- * Specifies the return value for vertices in <isLabelMovable>. Default is false.
- */
- mxGraph.prototype.vertexLabelsMovable = false;
- /**
- * Variable: dropEnabled
- *
- * Specifies the return value for <isDropEnabled>. Default is false.
- */
- mxGraph.prototype.dropEnabled = false;
- /**
- * Variable: splitEnabled
- *
- * Specifies if dropping onto edges should be enabled. This is ignored if
- * <dropEnabled> is false. If enabled, it will call <splitEdge> to carry
- * out the drop operation. Default is true.
- */
- mxGraph.prototype.splitEnabled = true;
- /**
- * Variable: cellsResizable
- *
- * Specifies the return value for <isCellResizable>. Default is true.
- */
- mxGraph.prototype.cellsResizable = true;
- /**
- * Variable: cellsBendable
- *
- * Specifies the return value for <isCellsBendable>. Default is true.
- */
- mxGraph.prototype.cellsBendable = true;
- /**
- * Variable: cellsSelectable
- *
- * Specifies the return value for <isCellSelectable>. Default is true.
- */
- mxGraph.prototype.cellsSelectable = true;
- /**
- * Variable: cellsDisconnectable
- *
- * Specifies the return value for <isCellDisconntable>. Default is true.
- */
- mxGraph.prototype.cellsDisconnectable = true;
- /**
- * Variable: autoSizeCells
- *
- * Specifies if the graph should automatically update the cell size after an
- * edit. This is used in <isAutoSizeCell>. Default is false.
- */
- mxGraph.prototype.autoSizeCells = false;
- /**
- * Variable: autoSizeCellsOnAdd
- *
- * Specifies if autoSize style should be applied when cells are added. Default is false.
- */
- mxGraph.prototype.autoSizeCellsOnAdd = false;
- /**
- * Variable: autoScroll
- *
- * Specifies if the graph should automatically scroll if the mouse goes near
- * the container edge while dragging. This is only taken into account if the
- * container has scrollbars. Default is true.
- *
- * If you need this to work without scrollbars then set <ignoreScrollbars> to
- * true. Please consult the <ignoreScrollbars> for details. In general, with
- * no scrollbars, the use of <allowAutoPanning> is recommended.
- */
- mxGraph.prototype.autoScroll = true;
- /**
- * Variable: ignoreScrollbars
- *
- * Specifies if the graph should automatically scroll regardless of the
- * scrollbars. This will scroll the container using positive values for
- * scroll positions (ie usually only rightwards and downwards). To avoid
- * possible conflicts with panning, set <translateToScrollPosition> to true.
- */
- mxGraph.prototype.ignoreScrollbars = false;
- /**
- * Variable: translateToScrollPosition
- *
- * Specifies if the graph should automatically convert the current scroll
- * position to a translate in the graph view when a mouseUp event is received.
- * This can be used to avoid conflicts when using <autoScroll> and
- * <ignoreScrollbars> with no scrollbars in the container.
- */
- mxGraph.prototype.translateToScrollPosition = false;
- /**
- * Variable: timerAutoScroll
- *
- * Specifies if autoscrolling should be carried out via mxPanningManager even
- * if the container has scrollbars. This disables <scrollPointToVisible> and
- * uses <mxPanningManager> instead. If this is true then <autoExtend> is
- * disabled. It should only be used with a scroll buffer or when scollbars
- * are visible and scrollable in all directions. Default is false.
- */
- mxGraph.prototype.timerAutoScroll = false;
- /**
- * Variable: allowAutoPanning
- *
- * Specifies if panning via <panGraph> should be allowed to implement autoscroll
- * if no scrollbars are available in <scrollPointToVisible>. To enable panning
- * inside the container, near the edge, set <mxPanningManager.border> to a
- * positive value. Default is false.
- */
- mxGraph.prototype.allowAutoPanning = false;
- /**
- * Variable: autoExtend
- *
- * Specifies if the size of the graph should be automatically extended if the
- * mouse goes near the container edge while dragging. This is only taken into
- * account if the container has scrollbars. Default is true. See <autoScroll>.
- */
- mxGraph.prototype.autoExtend = true;
- /**
- * Variable: maximumGraphBounds
- *
- * <mxRectangle> that specifies the area in which all cells in the diagram
- * should be placed. Uses in <getMaximumGraphBounds>. Use a width or height of
- * 0 if you only want to give a upper, left corner.
- */
- mxGraph.prototype.maximumGraphBounds = null;
- /**
- * Variable: minimumGraphSize
- *
- * <mxRectangle> that specifies the minimum size of the graph. This is ignored
- * if the graph container has no scrollbars. Default is null.
- */
- mxGraph.prototype.minimumGraphSize = null;
- /**
- * Variable: minimumContainerSize
- *
- * <mxRectangle> that specifies the minimum size of the <container> if
- * <resizeContainer> is true.
- */
- mxGraph.prototype.minimumContainerSize = null;
-
- /**
- * Variable: maximumContainerSize
- *
- * <mxRectangle> that specifies the maximum size of the container if
- * <resizeContainer> is true.
- */
- mxGraph.prototype.maximumContainerSize = null;
- /**
- * Variable: resizeContainer
- *
- * Specifies if the container should be resized to the graph size when
- * the graph size has changed. Default is false.
- */
- mxGraph.prototype.resizeContainer = false;
- /**
- * Variable: border
- *
- * Border to be added to the bottom and right side when the container is
- * being resized after the graph has been changed. Default is 0.
- */
- mxGraph.prototype.border = 0;
-
- /**
- * Variable: keepEdgesInForeground
- *
- * Specifies if edges should appear in the foreground regardless of their order
- * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
- * both true then the normal order is applied. Default is false.
- */
- mxGraph.prototype.keepEdgesInForeground = false;
- /**
- * Variable: keepEdgesInBackground
- *
- * Specifies if edges should appear in the background regardless of their order
- * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
- * both true then the normal order is applied. Default is false.
- */
- mxGraph.prototype.keepEdgesInBackground = false;
- /**
- * Variable: allowNegativeCoordinates
- *
- * Specifies if negative coordinates for vertices are allowed. Default is true.
- */
- mxGraph.prototype.allowNegativeCoordinates = true;
- /**
- * Variable: constrainChildren
- *
- * Specifies if a child should be constrained inside the parent bounds after a
- * move or resize of the child. Default is true.
- */
- mxGraph.prototype.constrainChildren = true;
- /**
- * Variable: constrainRelativeChildren
- *
- * Specifies if child cells with relative geometries should be constrained
- * inside the parent bounds, if <constrainChildren> is true, and/or the
- * <maximumGraphBounds>. Default is false.
- */
- mxGraph.prototype.constrainRelativeChildren = false;
- /**
- * Variable: extendParents
- *
- * Specifies if a parent should contain the child bounds after a resize of
- * the child. Default is true. This has precedence over <constrainChildren>.
- */
- mxGraph.prototype.extendParents = true;
- /**
- * Variable: extendParentsOnAdd
- *
- * Specifies if parents should be extended according to the <extendParents>
- * switch if cells are added. Default is true.
- */
- mxGraph.prototype.extendParentsOnAdd = true;
- /**
- * Variable: extendParentsOnAdd
- *
- * Specifies if parents should be extended according to the <extendParents>
- * switch if cells are added. Default is false for backwards compatiblity.
- */
- mxGraph.prototype.extendParentsOnMove = false;
- /**
- * Variable: recursiveResize
- *
- * Specifies the return value for <isRecursiveResize>. Default is
- * false for backwards compatiblity.
- */
- mxGraph.prototype.recursiveResize = false;
- /**
- * Variable: collapseToPreferredSize
- *
- * Specifies if the cell size should be changed to the preferred size when
- * a cell is first collapsed. Default is true.
- */
- mxGraph.prototype.collapseToPreferredSize = true;
- /**
- * Variable: zoomFactor
- *
- * Specifies the factor used for <zoomIn> and <zoomOut>. Default is 1.2
- * (120%).
- */
- mxGraph.prototype.zoomFactor = 1.2;
- /**
- * Variable: keepSelectionVisibleOnZoom
- *
- * Specifies if the viewport should automatically contain the selection cells
- * after a zoom operation. Default is false.
- */
- mxGraph.prototype.keepSelectionVisibleOnZoom = false;
- /**
- * Variable: centerZoom
- *
- * Specifies if the zoom operations should go into the center of the actual
- * diagram rather than going from top, left. Default is true.
- */
- mxGraph.prototype.centerZoom = true;
- /**
- * Variable: resetViewOnRootChange
- *
- * Specifies if the scale and translate should be reset if the root changes in
- * the model. Default is true.
- */
- mxGraph.prototype.resetViewOnRootChange = true;
- /**
- * Variable: resetEdgesOnResize
- *
- * Specifies if edge control points should be reset after the resize of a
- * connected cell. Default is false.
- */
- mxGraph.prototype.resetEdgesOnResize = false;
- /**
- * Variable: resetEdgesOnMove
- *
- * Specifies if edge control points should be reset after the move of a
- * connected cell. Default is false.
- */
- mxGraph.prototype.resetEdgesOnMove = false;
- /**
- * Variable: resetEdgesOnConnect
- *
- * Specifies if edge control points should be reset after the the edge has been
- * reconnected. Default is true.
- */
- mxGraph.prototype.resetEdgesOnConnect = true;
- /**
- * Variable: allowLoops
- *
- * Specifies if loops (aka self-references) are allowed. Default is false.
- */
- mxGraph.prototype.allowLoops = false;
-
- /**
- * Variable: defaultLoopStyle
- *
- * <mxEdgeStyle> to be used for loops. This is a fallback for loops if the
- * <mxConstants.STYLE_LOOP> is undefined. Default is <mxEdgeStyle.Loop>.
- */
- mxGraph.prototype.defaultLoopStyle = mxEdgeStyle.Loop;
- /**
- * Variable: multigraph
- *
- * Specifies if multiple edges in the same direction between the same pair of
- * vertices are allowed. Default is true.
- */
- mxGraph.prototype.multigraph = true;
- /**
- * Variable: connectableEdges
- *
- * Specifies if edges are connectable. Default is false. This overrides the
- * connectable field in edges.
- */
- mxGraph.prototype.connectableEdges = false;
- /**
- * Variable: allowDanglingEdges
- *
- * Specifies if edges with disconnected terminals are allowed in the graph.
- * Default is true.
- */
- mxGraph.prototype.allowDanglingEdges = true;
- /**
- * Variable: cloneInvalidEdges
- *
- * Specifies if edges that are cloned should be validated and only inserted
- * if they are valid. Default is true.
- */
- mxGraph.prototype.cloneInvalidEdges = false;
- /**
- * Variable: disconnectOnMove
- *
- * Specifies if edges should be disconnected from their terminals when they
- * are moved. Default is true.
- */
- mxGraph.prototype.disconnectOnMove = true;
- /**
- * Variable: labelsVisible
- *
- * Specifies if labels should be visible. This is used in <getLabel>. Default
- * is true.
- */
- mxGraph.prototype.labelsVisible = true;
-
- /**
- * Variable: htmlLabels
- *
- * Specifies the return value for <isHtmlLabel>. Default is false.
- */
- mxGraph.prototype.htmlLabels = false;
- /**
- * Variable: swimlaneSelectionEnabled
- *
- * Specifies if swimlanes should be selectable via the content if the
- * mouse is released. Default is true.
- */
- mxGraph.prototype.swimlaneSelectionEnabled = true;
- /**
- * Variable: swimlaneNesting
- *
- * Specifies if nesting of swimlanes is allowed. Default is true.
- */
- mxGraph.prototype.swimlaneNesting = true;
-
- /**
- * Variable: swimlaneIndicatorColorAttribute
- *
- * The attribute used to find the color for the indicator if the indicator
- * color is set to 'swimlane'. Default is <mxConstants.STYLE_FILLCOLOR>.
- */
- mxGraph.prototype.swimlaneIndicatorColorAttribute = mxConstants.STYLE_FILLCOLOR;
- /**
- * Variable: imageBundles
- *
- * Holds the list of image bundles.
- */
- mxGraph.prototype.imageBundles = null;
- /**
- * Variable: minFitScale
- *
- * Specifies the minimum scale to be applied in <fit>. Default is 0.1. Set this
- * to null to allow any value.
- */
- mxGraph.prototype.minFitScale = 0.1;
- /**
- * Variable: maxFitScale
- *
- * Specifies the maximum scale to be applied in <fit>. Default is 8. Set this
- * to null to allow any value.
- */
- mxGraph.prototype.maxFitScale = 8;
- /**
- * Variable: panDx
- *
- * Current horizontal panning value. Default is 0.
- */
- mxGraph.prototype.panDx = 0;
- /**
- * Variable: panDy
- *
- * Current vertical panning value. Default is 0.
- */
- mxGraph.prototype.panDy = 0;
- /**
- * Variable: collapsedImage
- *
- * Specifies the <mxImage> to indicate a collapsed state.
- * Default value is mxClient.imageBasePath + '/collapsed.gif'
- */
- mxGraph.prototype.collapsedImage = new mxImage(mxClient.imageBasePath + '/collapsed.gif', 9, 9);
- /**
- * Variable: expandedImage
- *
- * Specifies the <mxImage> to indicate a expanded state.
- * Default value is mxClient.imageBasePath + '/expanded.gif'
- */
- mxGraph.prototype.expandedImage = new mxImage(mxClient.imageBasePath + '/expanded.gif', 9, 9);
- /**
- * Variable: warningImage
- *
- * Specifies the <mxImage> for the image to be used to display a warning
- * overlay. See <setCellWarning>. Default value is mxClient.imageBasePath +
- * '/warning'. The extension for the image depends on the platform. It is
- * '.png' on the Mac and '.gif' on all other platforms.
- */
- mxGraph.prototype.warningImage = new mxImage(mxClient.imageBasePath + '/warning'+
- ((mxClient.IS_MAC) ? '.png' : '.gif'), 16, 16);
- /**
- * Variable: alreadyConnectedResource
- *
- * Specifies the resource key for the error message to be displayed in
- * non-multigraphs when two vertices are already connected. If the resource
- * for this key does not exist then the value is used as the error message.
- * Default is 'alreadyConnected'.
- */
- mxGraph.prototype.alreadyConnectedResource = (mxClient.language != 'none') ? 'alreadyConnected' : '';
- /**
- * Variable: containsValidationErrorsResource
- *
- * Specifies the resource key for the warning message to be displayed when
- * a collapsed cell contains validation errors. If the resource for this
- * key does not exist then the value is used as the warning message.
- * Default is 'containsValidationErrors'.
- */
- mxGraph.prototype.containsValidationErrorsResource = (mxClient.language != 'none') ? 'containsValidationErrors' : '';
- /**
- * Variable: collapseExpandResource
- *
- * Specifies the resource key for the tooltip on the collapse/expand icon.
- * If the resource for this key does not exist then the value is used as
- * the tooltip. Default is 'collapse-expand'.
- */
- mxGraph.prototype.collapseExpandResource = (mxClient.language != 'none') ? 'collapse-expand' : '';
- /**
- * Function: init
- *
- * Initializes the <container> and creates the respective datastructures.
- *
- * Parameters:
- *
- * container - DOM node that will contain the graph display.
- */
- mxGraph.prototype.init = function(container)
- {
- this.container = container;
-
- // Initializes the in-place editor
- this.cellEditor = this.createCellEditor();
- // Initializes the container using the view
- this.view.init();
-
- // Updates the size of the container for the current graph
- this.sizeDidChange();
-
- // Hides tooltips and resets tooltip timer if mouse leaves container
- mxEvent.addListener(container, 'mouseleave', mxUtils.bind(this, function(evt)
- {
- if (this.tooltipHandler != null && this.tooltipHandler.div != null &&
- this.tooltipHandler.div != evt.relatedTarget)
- {
- this.tooltipHandler.hide();
- }
- }));
- // Automatic deallocation of memory
- if (mxClient.IS_IE)
- {
- mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
- {
- this.destroy();
- }));
-
- // Disable shift-click for text
- mxEvent.addListener(container, 'selectstart',
- mxUtils.bind(this, function(evt)
- {
- return this.isEditing() || (!this.isMouseDown && !mxEvent.isShiftDown(evt));
- })
- );
- }
-
- // Workaround for missing last shape and connect preview in IE8 standards
- // mode if no initial graph displayed or no label for shape defined
- if (document.documentMode == 8)
- {
- container.insertAdjacentHTML('beforeend', '<' + mxClient.VML_PREFIX + ':group' +
- ' style="DISPLAY: none;"></' + mxClient.VML_PREFIX + ':group>');
- }
- };
- /**
- * Function: createHandlers
- *
- * Creates the tooltip-, panning-, connection- and graph-handler (in this
- * order). This is called in the constructor before <init> is called.
- */
- mxGraph.prototype.createHandlers = function()
- {
- this.tooltipHandler = this.createTooltipHandler();
- this.tooltipHandler.setEnabled(false);
- this.selectionCellsHandler = this.createSelectionCellsHandler();
- this.connectionHandler = this.createConnectionHandler();
- this.connectionHandler.setEnabled(false);
- this.graphHandler = this.createGraphHandler();
- this.panningHandler = this.createPanningHandler();
- this.panningHandler.panningEnabled = false;
- this.popupMenuHandler = this.createPopupMenuHandler();
- };
- /**
- * Function: createTooltipHandler
- *
- * Creates and returns a new <mxTooltipHandler> to be used in this graph.
- */
- mxGraph.prototype.createTooltipHandler = function()
- {
- return new mxTooltipHandler(this);
- };
- /**
- * Function: createSelectionCellsHandler
- *
- * Creates and returns a new <mxTooltipHandler> to be used in this graph.
- */
- mxGraph.prototype.createSelectionCellsHandler = function()
- {
- return new mxSelectionCellsHandler(this);
- };
- /**
- * Function: createConnectionHandler
- *
- * Creates and returns a new <mxConnectionHandler> to be used in this graph.
- */
- mxGraph.prototype.createConnectionHandler = function()
- {
- return new mxConnectionHandler(this);
- };
- /**
- * Function: createGraphHandler
- *
- * Creates and returns a new <mxGraphHandler> to be used in this graph.
- */
- mxGraph.prototype.createGraphHandler = function()
- {
- return new mxGraphHandler(this);
- };
- /**
- * Function: createPanningHandler
- *
- * Creates and returns a new <mxPanningHandler> to be used in this graph.
- */
- mxGraph.prototype.createPanningHandler = function()
- {
- return new mxPanningHandler(this);
- };
- /**
- * Function: createPopupMenuHandler
- *
- * Creates and returns a new <mxPopupMenuHandler> to be used in this graph.
- */
- mxGraph.prototype.createPopupMenuHandler = function()
- {
- return new mxPopupMenuHandler(this);
- };
- /**
- * Function: createSelectionModel
- *
- * Creates a new <mxGraphSelectionModel> to be used in this graph.
- */
- mxGraph.prototype.createSelectionModel = function()
- {
- return new mxGraphSelectionModel(this);
- };
- /**
- * Function: createStylesheet
- *
- * Creates a new <mxGraphSelectionModel> to be used in this graph.
- */
- mxGraph.prototype.createStylesheet = function()
- {
- return new mxStylesheet();
- };
- /**
- * Function: createGraphView
- *
- * Creates a new <mxGraphView> to be used in this graph.
- */
- mxGraph.prototype.createGraphView = function()
- {
- return new mxGraphView(this);
- };
-
- /**
- * Function: createCellRenderer
- *
- * Creates a new <mxCellRenderer> to be used in this graph.
- */
- mxGraph.prototype.createCellRenderer = function()
- {
- return new mxCellRenderer();
- };
- /**
- * Function: createCellEditor
- *
- * Creates a new <mxCellEditor> to be used in this graph.
- */
- mxGraph.prototype.createCellEditor = function()
- {
- return new mxCellEditor(this);
- };
- /**
- * Function: getModel
- *
- * Returns the <mxGraphModel> that contains the cells.
- */
- mxGraph.prototype.getModel = function()
- {
- return this.model;
- };
- /**
- * Function: getView
- *
- * Returns the <mxGraphView> that contains the <mxCellStates>.
- */
- mxGraph.prototype.getView = function()
- {
- return this.view;
- };
- /**
- * Function: getStylesheet
- *
- * Returns the <mxStylesheet> that defines the style.
- */
- mxGraph.prototype.getStylesheet = function()
- {
- return this.stylesheet;
- };
- /**
- * Function: setStylesheet
- *
- * Sets the <mxStylesheet> that defines the style.
- */
- mxGraph.prototype.setStylesheet = function(stylesheet)
- {
- this.stylesheet = stylesheet;
- };
- /**
- * Function: getSelectionModel
- *
- * Returns the <mxGraphSelectionModel> that contains the selection.
- */
- mxGraph.prototype.getSelectionModel = function()
- {
- return this.selectionModel;
- };
- /**
- * Function: setSelectionModel
- *
- * Sets the <mxSelectionModel> that contains the selection.
- */
- mxGraph.prototype.setSelectionModel = function(selectionModel)
- {
- this.selectionModel = selectionModel;
- };
- /**
- * Function: getSelectionCellsForChanges
- *
- * Returns the cells to be selected for the given array of changes.
- *
- * Parameters:
- *
- * ignoreFn - Optional function that takes a change and returns true if the
- * change should be ignored.
- *
- */
- mxGraph.prototype.getSelectionCellsForChanges = function(changes, ignoreFn)
- {
- var dict = new mxDictionary();
- var cells = [];
-
- var addCell = mxUtils.bind(this, function(cell)
- {
- if (!dict.get(cell) && this.model.contains(cell))
- {
- if (this.model.isEdge(cell) || this.model.isVertex(cell))
- {
- dict.put(cell, true);
- cells.push(cell);
- }
- else
- {
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- addCell(this.model.getChildAt(cell, i));
- }
- }
- }
- });
- for (var i = 0; i < changes.length; i++)
- {
- var change = changes[i];
-
- if (change.constructor != mxRootChange &&
- (ignoreFn == null || !ignoreFn(change)))
- {
- var cell = null;
- if (change instanceof mxChildChange)
- {
- cell = change.child;
- }
- else if (change.cell != null &&
- change.cell instanceof mxCell)
- {
- cell = change.cell;
- }
-
- if (cell != null)
- {
- addCell(cell);
- }
- }
- }
-
- return cells;
- };
- /**
- * Function: graphModelChanged
- *
- * Called when the graph model changes. Invokes <processChange> on each
- * item of the given array to update the view accordingly.
- *
- * Parameters:
- *
- * changes - Array that contains the individual changes.
- */
- mxGraph.prototype.graphModelChanged = function(changes)
- {
- for (var i = 0; i < changes.length; i++)
- {
- this.processChange(changes[i]);
- }
- this.updateSelection();
- this.view.validate();
- this.sizeDidChange();
- };
- /**
- * Function: updateSelection
- *
- * Removes selection cells that are not in the model from the selection.
- */
- mxGraph.prototype.updateSelection = function()
- {
- var cells = this.getSelectionCells();
- var removed = [];
-
- for (var i = 0; i < cells.length; i++)
- {
- if (!this.model.contains(cells[i]) || !this.isCellVisible(cells[i]))
- {
- removed.push(cells[i]);
- }
- else
- {
- var par = this.model.getParent(cells[i]);
-
- while (par != null && par != this.view.currentRoot)
- {
- if (this.isCellCollapsed(par) || !this.isCellVisible(par))
- {
- removed.push(cells[i]);
- break;
- }
-
- par = this.model.getParent(par);
- }
- }
- }
-
- this.removeSelectionCells(removed);
- };
- /**
- * Function: processChange
- *
- * Processes the given change and invalidates the respective cached data
- * in <view>. This fires a <root> event if the root has changed in the
- * model.
- *
- * Parameters:
- *
- * change - Object that represents the change on the model.
- */
- mxGraph.prototype.processChange = function(change)
- {
- // Resets the view settings, removes all cells and clears
- // the selection if the root changes.
- if (change instanceof mxRootChange)
- {
- this.clearSelection();
- this.setDefaultParent(null);
- this.removeStateForCell(change.previous);
-
- if (this.resetViewOnRootChange)
- {
- this.view.scale = 1;
- this.view.translate.x = 0;
- this.view.translate.y = 0;
- }
- this.fireEvent(new mxEventObject(mxEvent.ROOT));
- }
-
- // Adds or removes a child to the view by online invaliding
- // the minimal required portions of the cache, namely, the
- // old and new parent and the child.
- else if (change instanceof mxChildChange)
- {
- var newParent = this.model.getParent(change.child);
- this.view.invalidate(change.child, true, true);
-
- if (!this.model.contains(newParent) || this.isCellCollapsed(newParent))
- {
- this.view.invalidate(change.child, true, true);
- this.removeStateForCell(change.child);
-
- // Handles special case of current root of view being removed
- if (this.view.currentRoot == change.child)
- {
- this.home();
- }
- }
-
- if (newParent != change.previous)
- {
- // Refreshes the collapse/expand icons on the parents
- if (newParent != null)
- {
- this.view.invalidate(newParent, false, false);
- }
-
- if (change.previous != null)
- {
- this.view.invalidate(change.previous, false, false);
- }
- }
- }
- // Handles two special cases where the shape does not need to be
- // recreated from scratch, it only needs to be invalidated.
- else if (change instanceof mxTerminalChange || change instanceof mxGeometryChange)
- {
- // Checks if the geometry has changed to avoid unnessecary revalidation
- if (change instanceof mxTerminalChange || ((change.previous == null && change.geometry != null) ||
- (change.previous != null && !change.previous.equals(change.geometry))))
- {
- this.view.invalidate(change.cell);
- }
- }
- // Handles two special cases where only the shape, but no
- // descendants need to be recreated
- else if (change instanceof mxValueChange)
- {
- this.view.invalidate(change.cell, false, false);
- }
-
- // Requires a new mxShape in JavaScript
- else if (change instanceof mxStyleChange)
- {
- this.view.invalidate(change.cell, true, true);
- var state = this.view.getState(change.cell);
-
- if (state != null)
- {
- state.invalidStyle = true;
- }
- }
-
- // Removes the state from the cache by default
- else if (change.cell != null && change.cell instanceof mxCell)
- {
- this.removeStateForCell(change.cell);
- }
- };
- /**
- * Function: removeStateForCell
- *
- * Removes all cached information for the given cell and its descendants.
- * This is called when a cell was removed from the model.
- *
- * Paramters:
- *
- * cell - <mxCell> that was removed from the model.
- */
- mxGraph.prototype.removeStateForCell = function(cell)
- {
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- this.removeStateForCell(this.model.getChildAt(cell, i));
- }
- this.view.invalidate(cell, false, true);
- this.view.removeState(cell);
- };
- /**
- * Group: Overlays
- */
- /**
- * Function: addCellOverlay
- *
- * Adds an <mxCellOverlay> for the specified cell. This method fires an
- * <addoverlay> event and returns the new <mxCellOverlay>.
- *
- * Parameters:
- *
- * cell - <mxCell> to add the overlay for.
- * overlay - <mxCellOverlay> to be added for the cell.
- */
- mxGraph.prototype.addCellOverlay = function(cell, overlay)
- {
- if (cell.overlays == null)
- {
- cell.overlays = [];
- }
-
- cell.overlays.push(overlay);
- var state = this.view.getState(cell);
- // Immediately updates the cell display if the state exists
- if (state != null)
- {
- this.cellRenderer.redraw(state);
- }
-
- this.fireEvent(new mxEventObject(mxEvent.ADD_OVERLAY,
- 'cell', cell, 'overlay', overlay));
-
- return overlay;
- };
- /**
- * Function: getCellOverlays
- *
- * Returns the array of <mxCellOverlays> for the given cell or null, if
- * no overlays are defined.
- *
- * Parameters:
- *
- * cell - <mxCell> whose overlays should be returned.
- */
- mxGraph.prototype.getCellOverlays = function(cell)
- {
- return cell.overlays;
- };
- /**
- * Function: removeCellOverlay
- *
- * Removes and returns the given <mxCellOverlay> from the given cell. This
- * method fires a <removeoverlay> event. If no overlay is given, then all
- * overlays are removed using <removeOverlays>.
- *
- * Parameters:
- *
- * cell - <mxCell> whose overlay should be removed.
- * overlay - Optional <mxCellOverlay> to be removed.
- */
- mxGraph.prototype.removeCellOverlay = function(cell, overlay)
- {
- if (overlay == null)
- {
- this.removeCellOverlays(cell);
- }
- else
- {
- var index = mxUtils.indexOf(cell.overlays, overlay);
-
- if (index >= 0)
- {
- cell.overlays.splice(index, 1);
-
- if (cell.overlays.length == 0)
- {
- cell.overlays = null;
- }
-
- // Immediately updates the cell display if the state exists
- var state = this.view.getState(cell);
-
- if (state != null)
- {
- this.cellRenderer.redraw(state);
- }
-
- this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
- 'cell', cell, 'overlay', overlay));
- }
- else
- {
- overlay = null;
- }
- }
-
- return overlay;
- };
- /**
- * Function: removeCellOverlays
- *
- * Removes all <mxCellOverlays> from the given cell. This method
- * fires a <removeoverlay> event for each <mxCellOverlay> and returns
- * the array of <mxCellOverlays> that was removed from the cell.
- *
- * Parameters:
- *
- * cell - <mxCell> whose overlays should be removed
- */
- mxGraph.prototype.removeCellOverlays = function(cell)
- {
- var overlays = cell.overlays;
-
- if (overlays != null)
- {
- cell.overlays = null;
-
- // Immediately updates the cell display if the state exists
- var state = this.view.getState(cell);
-
- if (state != null)
- {
- this.cellRenderer.redraw(state);
- }
-
- for (var i = 0; i < overlays.length; i++)
- {
- this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
- 'cell', cell, 'overlay', overlays[i]));
- }
- }
-
- return overlays;
- };
- /**
- * Function: clearCellOverlays
- *
- * Removes all <mxCellOverlays> in the graph for the given cell and all its
- * descendants. If no cell is specified then all overlays are removed from
- * the graph. This implementation uses <removeCellOverlays> to remove the
- * overlays from the individual cells.
- *
- * Parameters:
- *
- * cell - Optional <mxCell> that represents the root of the subtree to
- * remove the overlays from. Default is the root in the model.
- */
- mxGraph.prototype.clearCellOverlays = function(cell)
- {
- cell = (cell != null) ? cell : this.model.getRoot();
- this.removeCellOverlays(cell);
-
- // Recursively removes all overlays from the children
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- var child = this.model.getChildAt(cell, i);
- this.clearCellOverlays(child); // recurse
- }
- };
- /**
- * Function: setCellWarning
- *
- * Creates an overlay for the given cell using the warning and image or
- * <warningImage> and returns the new <mxCellOverlay>. The warning is
- * displayed as a tooltip in a red font and may contain HTML markup. If
- * the warning is null or a zero length string, then all overlays are
- * removed from the cell.
- *
- * Example:
- *
- * (code)
- * graph.setCellWarning(cell, '<b>Warning:</b>: Hello, World!');
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> whose warning should be set.
- * warning - String that represents the warning to be displayed.
- * img - Optional <mxImage> to be used for the overlay. Default is
- * <warningImage>.
- * isSelect - Optional boolean indicating if a click on the overlay
- * should select the corresponding cell. Default is false.
- */
- mxGraph.prototype.setCellWarning = function(cell, warning, img, isSelect)
- {
- if (warning != null && warning.length > 0)
- {
- img = (img != null) ? img : this.warningImage;
-
- // Creates the overlay with the image and warning
- var overlay = new mxCellOverlay(img,
- '<font color=red>'+warning+'</font>');
-
- // Adds a handler for single mouseclicks to select the cell
- if (isSelect)
- {
- overlay.addListener(mxEvent.CLICK,
- mxUtils.bind(this, function(sender, evt)
- {
- if (this.isEnabled())
- {
- this.setSelectionCell(cell);
- }
- })
- );
- }
-
- // Sets and returns the overlay in the graph
- return this.addCellOverlay(cell, overlay);
- }
- else
- {
- this.removeCellOverlays(cell);
- }
-
- return null;
- };
- /**
- * Group: In-place editing
- */
- /**
- * Function: startEditing
- *
- * Calls <startEditingAtCell> using the given cell or the first selection
- * cell.
- *
- * Parameters:
- *
- * evt - Optional mouse event that triggered the editing.
- */
- mxGraph.prototype.startEditing = function(evt)
- {
- this.startEditingAtCell(null, evt);
- };
- /**
- * Function: startEditingAtCell
- *
- * Fires a <startEditing> event and invokes <mxCellEditor.startEditing>
- * on <editor>. After editing was started, a <editingStarted> event is
- * fired.
- *
- * Parameters:
- *
- * cell - <mxCell> to start the in-place editor for.
- * evt - Optional mouse event that triggered the editing.
- */
- mxGraph.prototype.startEditingAtCell = function(cell, evt)
- {
- if (evt == null || !mxEvent.isMultiTouchEvent(evt))
- {
- if (cell == null)
- {
- cell = this.getSelectionCell();
-
- if (cell != null && !this.isCellEditable(cell))
- {
- cell = null;
- }
- }
-
- if (cell != null)
- {
- this.fireEvent(new mxEventObject(mxEvent.START_EDITING,
- 'cell', cell, 'event', evt));
- this.cellEditor.startEditing(cell, evt);
- this.fireEvent(new mxEventObject(mxEvent.EDITING_STARTED,
- 'cell', cell, 'event', evt));
- }
- }
- };
- /**
- * Function: getEditingValue
- *
- * Returns the initial value for in-place editing. This implementation
- * returns <convertValueToString> for the given cell. If this function is
- * overridden, then <mxGraphModel.valueForCellChanged> should take care
- * of correctly storing the actual new value inside the user object.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the initial editing value should be returned.
- * evt - Optional mouse event that triggered the editor.
- */
- mxGraph.prototype.getEditingValue = function(cell, evt)
- {
- return this.convertValueToString(cell);
- };
- /**
- * Function: stopEditing
- *
- * Stops the current editing and fires a <editingStopped> event.
- *
- * Parameters:
- *
- * cancel - Boolean that specifies if the current editing value
- * should be stored.
- */
- mxGraph.prototype.stopEditing = function(cancel)
- {
- this.cellEditor.stopEditing(cancel);
- this.fireEvent(new mxEventObject(mxEvent.EDITING_STOPPED, 'cancel', cancel));
- };
- /**
- * Function: labelChanged
- *
- * Sets the label of the specified cell to the given value using
- * <cellLabelChanged> and fires <mxEvent.LABEL_CHANGED> while the
- * transaction is in progress. Returns the cell whose label was changed.
- *
- * Parameters:
- *
- * cell - <mxCell> whose label should be changed.
- * value - New label to be assigned.
- * evt - Optional event that triggered the change.
- */
- mxGraph.prototype.labelChanged = function(cell, value, evt)
- {
- this.model.beginUpdate();
- try
- {
- var old = cell.value;
- this.cellLabelChanged(cell, value, this.isAutoSizeCell(cell));
- this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED,
- 'cell', cell, 'value', value, 'old', old, 'event', evt));
- }
- finally
- {
- this.model.endUpdate();
- }
-
- return cell;
- };
- /**
- * Function: cellLabelChanged
- *
- * Sets the new label for a cell. If autoSize is true then
- * <cellSizeUpdated> will be called.
- *
- * In the following example, the function is extended to map changes to
- * attributes in an XML node, as shown in <convertValueToString>.
- * Alternatively, the handling of this can be implemented as shown in
- * <mxGraphModel.valueForCellChanged> without the need to clone the
- * user object.
- *
- * (code)
- * var graphCellLabelChanged = graph.cellLabelChanged;
- * graph.cellLabelChanged = function(cell, newValue, autoSize)
- * {
- * // Cloned for correct undo/redo
- * var elt = cell.value.cloneNode(true);
- * elt.setAttribute('label', newValue);
- *
- * newValue = elt;
- * graphCellLabelChanged.apply(this, arguments);
- * };
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> whose label should be changed.
- * value - New label to be assigned.
- * autoSize - Boolean that specifies if <cellSizeUpdated> should be called.
- */
- mxGraph.prototype.cellLabelChanged = function(cell, value, autoSize)
- {
- this.model.beginUpdate();
- try
- {
- this.model.setValue(cell, value);
-
- if (autoSize)
- {
- this.cellSizeUpdated(cell, false);
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- };
- /**
- * Group: Event processing
- */
- /**
- * Function: escape
- *
- * Processes an escape keystroke.
- *
- * Parameters:
- *
- * evt - Mouseevent that represents the keystroke.
- */
- mxGraph.prototype.escape = function(evt)
- {
- this.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
- };
- /**
- * Function: click
- *
- * Processes a singleclick on an optional cell and fires a <click> event.
- * The click event is fired initially. If the graph is enabled and the
- * event has not been consumed, then the cell is selected using
- * <selectCellForEvent> or the selection is cleared using
- * <clearSelection>. The events consumed state is set to true if the
- * corresponding <mxMouseEvent> has been consumed.
- *
- * To handle a click event, use the following code.
- *
- * (code)
- * graph.addListener(mxEvent.CLICK, function(sender, evt)
- * {
- * var e = evt.getProperty('event'); // mouse event
- * var cell = evt.getProperty('cell'); // cell may be null
- *
- * if (cell != null)
- * {
- * // Do something useful with cell and consume the event
- * evt.consume();
- * }
- * });
- * (end)
- *
- * Parameters:
- *
- * me - <mxMouseEvent> that represents the single click.
- */
- mxGraph.prototype.click = function(me)
- {
- var evt = me.getEvent();
- var cell = me.getCell();
- var mxe = new mxEventObject(mxEvent.CLICK, 'event', evt, 'cell', cell);
-
- if (me.isConsumed())
- {
- mxe.consume();
- }
-
- this.fireEvent(mxe);
-
- if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
- {
- if (cell != null)
- {
- if (this.isTransparentClickEvent(evt))
- {
- var active = false;
-
- var tmp = this.getCellAt(me.graphX, me.graphY, null, null, null,
- mxUtils.bind(this, function(state)
- {
- var selected = this.isCellSelected(state.cell);
- active = active || selected;
-
- return !active || selected || (state.cell != cell &&
- this.model.isAncestor(state.cell, cell));
- }));
-
- if (tmp != null)
- {
- cell = tmp;
- }
- }
- }
- else if (this.isSwimlaneSelectionEnabled())
- {
- cell = this.getSwimlaneAt(me.getGraphX(), me.getGraphY());
-
- if (cell != null && (!this.isToggleEvent(evt) ||
- !mxEvent.isAltDown(evt)))
- {
- var temp = cell;
- var swimlanes = [];
-
- while (temp != null)
- {
- temp = this.model.getParent(temp);
- var state = this.view.getState(temp);
-
- if (this.isSwimlane(temp) && state != null)
- {
- swimlanes.push(temp);
- }
- }
-
- // Selects ancestors for selected swimlanes
- if (swimlanes.length > 0)
- {
- swimlanes = swimlanes.reverse();
- swimlanes.splice(0, 0, cell);
- swimlanes.push(cell);
-
- for (var i = 0; i < swimlanes.length - 1; i++)
- {
- if (this.isCellSelected(swimlanes[i]))
- {
- cell = swimlanes[(this.isToggleEvent(evt)) ?
- i : i + 1];
- }
- }
- }
- }
- }
-
- if (cell != null)
- {
- this.selectCellForEvent(cell, evt);
- }
- else if (!this.isToggleEvent(evt))
- {
- this.clearSelection();
- }
- }
- };
- /**
- * Function: isSiblingSelected
- *
- * Returns true if any sibling of the given cell is selected.
- */
- mxGraph.prototype.isSiblingSelected = function(cell)
- {
- var model = this.model;
- var parent = model.getParent(cell);
- var childCount = model.getChildCount(parent);
-
- for (var i = 0; i < childCount; i++)
- {
- var child = model.getChildAt(parent, i);
-
- if (cell != child && this.isCellSelected(child))
- {
- return true;
- }
- }
-
- return false;
- };
- /**
- * Function: dblClick
- *
- * Processes a doubleclick on an optional cell and fires a <dblclick>
- * event. The event is fired initially. If the graph is enabled and the
- * event has not been consumed, then <edit> is called with the given
- * cell. The event is ignored if no cell was specified.
- *
- * Example for overriding this method.
- *
- * (code)
- * graph.dblClick = function(evt, cell)
- * {
- * var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
- * this.fireEvent(mxe);
- *
- * if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
- * {
- * mxUtils.alert('Hello, World!');
- * mxe.consume();
- * }
- * }
- * (end)
- *
- * Example listener for this event.
- *
- * (code)
- * graph.addListener(mxEvent.DOUBLE_CLICK, function(sender, evt)
- * {
- * var cell = evt.getProperty('cell');
- * // do something with the cell and consume the
- * // event to prevent in-place editing from start
- * });
- * (end)
- *
- * Parameters:
- *
- * evt - Mouseevent that represents the doubleclick.
- * cell - Optional <mxCell> under the mousepointer.
- */
- mxGraph.prototype.dblClick = function(evt, cell)
- {
- var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
- this.fireEvent(mxe);
-
- // Handles the event if it has not been consumed
- if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() &&
- cell != null && this.isCellEditable(cell) && !this.isEditing(cell))
- {
- this.startEditingAtCell(cell, evt);
- mxEvent.consume(evt);
- }
- };
- /**
- * Function: tapAndHold
- *
- * Handles the <mxMouseEvent> by highlighting the <mxCellState>.
- *
- * Parameters:
- *
- * me - <mxMouseEvent> that represents the touch event.
- * state - Optional <mxCellState> that is associated with the event.
- */
- mxGraph.prototype.tapAndHold = function(me)
- {
- var evt = me.getEvent();
- var mxe = new mxEventObject(mxEvent.TAP_AND_HOLD, 'event', evt, 'cell', me.getCell());
- // LATER: Check if event should be consumed if me is consumed
- this.fireEvent(mxe);
- if (mxe.isConsumed())
- {
- // Resets the state of the panning handler
- this.panningHandler.panningTrigger = false;
- }
-
- // Handles the event if it has not been consumed
- if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() && this.connectionHandler.isEnabled())
- {
- var state = this.view.getState(this.connectionHandler.marker.getCell(me));
- if (state != null)
- {
- this.connectionHandler.marker.currentColor = this.connectionHandler.marker.validColor;
- this.connectionHandler.marker.markedState = state;
- this.connectionHandler.marker.mark();
-
- this.connectionHandler.first = new mxPoint(me.getGraphX(), me.getGraphY());
- this.connectionHandler.edgeState = this.connectionHandler.createEdgeState(me);
- this.connectionHandler.previous = state;
- this.connectionHandler.fireEvent(new mxEventObject(mxEvent.START, 'state', this.connectionHandler.previous));
- }
- }
- };
- /**
- * Function: scrollPointToVisible
- *
- * Scrolls the graph to the given point, extending the graph container if
- * specified.
- */
- mxGraph.prototype.scrollPointToVisible = function(x, y, extend, border)
- {
- if (!this.timerAutoScroll && (this.ignoreScrollbars || mxUtils.hasScrollbars(this.container)))
- {
- var c = this.container;
- border = (border != null) ? border : 20;
-
- if (x >= c.scrollLeft && y >= c.scrollTop && x <= c.scrollLeft + c.clientWidth &&
- y <= c.scrollTop + c.clientHeight)
- {
- var dx = c.scrollLeft + c.clientWidth - x;
-
- if (dx < border)
- {
- var old = c.scrollLeft;
- c.scrollLeft += border - dx;
- // Automatically extends the canvas size to the bottom, right
- // if the event is outside of the canvas and the edge of the
- // canvas has been reached. Notes: Needs fix for IE.
- if (extend && old == c.scrollLeft)
- {
- if (this.dialect == mxConstants.DIALECT_SVG)
- {
- var root = this.view.getDrawPane().ownerSVGElement;
- var width = this.container.scrollWidth + border - dx;
-
- // Updates the clipping region. This is an expensive
- // operation that should not be executed too often.
- root.style.width = width + 'px';
- }
- else
- {
- var width = Math.max(c.clientWidth, c.scrollWidth) + border - dx;
- var canvas = this.view.getCanvas();
- canvas.style.width = width + 'px';
- }
-
- c.scrollLeft += border - dx;
- }
- }
- else
- {
- dx = x - c.scrollLeft;
-
- if (dx < border)
- {
- c.scrollLeft -= border - dx;
- }
- }
-
- var dy = c.scrollTop + c.clientHeight - y;
-
- if (dy < border)
- {
- var old = c.scrollTop;
- c.scrollTop += border - dy;
- if (old == c.scrollTop && extend)
- {
- if (this.dialect == mxConstants.DIALECT_SVG)
- {
- var root = this.view.getDrawPane().ownerSVGElement;
- var height = this.container.scrollHeight + border - dy;
-
- // Updates the clipping region. This is an expensive
- // operation that should not be executed too often.
- root.style.height = height + 'px';
- }
- else
- {
- var height = Math.max(c.clientHeight, c.scrollHeight) + border - dy;
- var canvas = this.view.getCanvas();
- canvas.style.height = height + 'px';
- }
-
- c.scrollTop += border - dy;
- }
- }
- else
- {
- dy = y - c.scrollTop;
-
- if (dy < border)
- {
- c.scrollTop -= border - dy;
- }
- }
- }
- }
- else if (this.allowAutoPanning && !this.panningHandler.isActive())
- {
- if (this.panningManager == null)
- {
- this.panningManager = this.createPanningManager();
- }
- this.panningManager.panTo(x + this.panDx, y + this.panDy);
- }
- };
- /**
- * Function: createPanningManager
- *
- * Creates and returns an <mxPanningManager>.
- */
- mxGraph.prototype.createPanningManager = function()
- {
- return new mxPanningManager(this);
- };
- /**
- * Function: getBorderSizes
- *
- * Returns the size of the border and padding on all four sides of the
- * container. The left, top, right and bottom borders are stored in the x, y,
- * width and height of the returned <mxRectangle>, respectively.
- */
- mxGraph.prototype.getBorderSizes = function()
- {
- var css = mxUtils.getCurrentStyle(this.container);
-
- return new mxRectangle(mxUtils.parseCssNumber(css.paddingLeft) +
- ((css.borderLeftStyle != 'none') ? mxUtils.parseCssNumber(css.borderLeftWidth) : 0),
- mxUtils.parseCssNumber(css.paddingTop) +
- ((css.borderTopStyle != 'none') ? mxUtils.parseCssNumber(css.borderTopWidth) : 0),
- mxUtils.parseCssNumber(css.paddingRight) +
- ((css.borderRightStyle != 'none') ? mxUtils.parseCssNumber(css.borderRightWidth) : 0),
- mxUtils.parseCssNumber(css.paddingBottom) +
- ((css.borderBottomStyle != 'none') ? mxUtils.parseCssNumber(css.borderBottomWidth) : 0));
- };
- /**
- * Function: getPreferredPageSize
- *
- * Returns the preferred size of the background page if <preferPageSize> is true.
- */
- mxGraph.prototype.getPreferredPageSize = function(bounds, width, height)
- {
- var scale = this.view.scale;
- var tr = this.view.translate;
- var fmt = this.pageFormat;
- var ps = this.pageScale;
- var page = new mxRectangle(0, 0, Math.ceil(fmt.width * ps), Math.ceil(fmt.height * ps));
-
- var hCount = (this.pageBreaksVisible) ? Math.ceil(width / page.width) : 1;
- var vCount = (this.pageBreaksVisible) ? Math.ceil(height / page.height) : 1;
-
- return new mxRectangle(0, 0, hCount * page.width + 2 + tr.x, vCount * page.height + 2 + tr.y);
- };
- /**
- * Function: fit
- *
- * Scales the graph such that the complete diagram fits into <container> and
- * returns the current scale in the view. To fit an initial graph prior to
- * rendering, set <mxGraphView.rendering> to false prior to changing the model
- * and execute the following after changing the model.
- *
- * (code)
- * graph.fit();
- * graph.view.rendering = true;
- * graph.refresh();
- * (end)
- *
- * To fit and center the graph, the following code can be used.
- *
- * (code)
- * var margin = 2;
- * var max = 3;
- *
- * var bounds = graph.getGraphBounds();
- * var cw = graph.container.clientWidth - margin;
- * var ch = graph.container.clientHeight - margin;
- * var w = bounds.width / graph.view.scale;
- * var h = bounds.height / graph.view.scale;
- * var s = Math.min(max, Math.min(cw / w, ch / h));
- *
- * graph.view.scaleAndTranslate(s,
- * (margin + cw - w * s) / (2 * s) - bounds.x / graph.view.scale,
- * (margin + ch - h * s) / (2 * s) - bounds.y / graph.view.scale);
- * (end)
- *
- * Parameters:
- *
- * border - Optional number that specifies the border. Default is <border>.
- * keepOrigin - Optional boolean that specifies if the translate should be
- * changed. Default is false.
- * margin - Optional margin in pixels. Default is 0.
- * enabled - Optional boolean that specifies if the scale should be set or
- * just returned. Default is true.
- * ignoreWidth - Optional boolean that specifies if the width should be
- * ignored. Default is false.
- * ignoreHeight - Optional boolean that specifies if the height should be
- * ignored. Default is false.
- * maxHeight - Optional maximum height.
- */
- mxGraph.prototype.fit = function(border, keepOrigin, margin, enabled, ignoreWidth, ignoreHeight, maxHeight)
- {
- if (this.container != null)
- {
- border = (border != null) ? border : this.getBorder();
- keepOrigin = (keepOrigin != null) ? keepOrigin : false;
- margin = (margin != null) ? margin : 0;
- enabled = (enabled != null) ? enabled : true;
- ignoreWidth = (ignoreWidth != null) ? ignoreWidth : false;
- ignoreHeight = (ignoreHeight != null) ? ignoreHeight : false;
-
- // Adds spacing and border from css
- var cssBorder = this.getBorderSizes();
- var w1 = this.container.offsetWidth - cssBorder.x - cssBorder.width - 1;
- var h1 = (maxHeight != null) ? maxHeight : this.container.offsetHeight - cssBorder.y - cssBorder.height - 1;
- var bounds = this.view.getGraphBounds();
-
- if (bounds.width > 0 && bounds.height > 0)
- {
- if (keepOrigin && bounds.x != null && bounds.y != null)
- {
- bounds = bounds.clone();
- bounds.width += bounds.x;
- bounds.height += bounds.y;
- bounds.x = 0;
- bounds.y = 0;
- }
-
- // LATER: Use unscaled bounding boxes to fix rounding errors
- var s = this.view.scale;
- var w2 = bounds.width / s;
- var h2 = bounds.height / s;
-
- // Fits to the size of the background image if required
- if (this.backgroundImage != null)
- {
- w2 = Math.max(w2, this.backgroundImage.width - bounds.x / s);
- h2 = Math.max(h2, this.backgroundImage.height - bounds.y / s);
- }
-
- var b = ((keepOrigin) ? border : 2 * border) + margin + 1;
- w1 -= b;
- h1 -= b;
-
- var s2 = (((ignoreWidth) ? h1 / h2 : (ignoreHeight) ? w1 / w2 :
- Math.min(w1 / w2, h1 / h2)));
-
- if (this.minFitScale != null)
- {
- s2 = Math.max(s2, this.minFitScale);
- }
-
- if (this.maxFitScale != null)
- {
- s2 = Math.min(s2, this.maxFitScale);
- }
-
- if (enabled)
- {
- if (!keepOrigin)
- {
- if (!mxUtils.hasScrollbars(this.container))
- {
- var x0 = (bounds.x != null) ? Math.floor(this.view.translate.x - bounds.x / s + border / s2 + margin / 2) : border;
- var y0 = (bounds.y != null) ? Math.floor(this.view.translate.y - bounds.y / s + border / s2 + margin / 2) : border;
- this.view.scaleAndTranslate(s2, x0, y0);
- }
- else
- {
- this.view.setScale(s2);
- var b2 = this.getGraphBounds();
-
- if (b2.x != null)
- {
- this.container.scrollLeft = b2.x;
- }
-
- if (b2.y != null)
- {
- this.container.scrollTop = b2.y;
- }
- }
- }
- else if (this.view.scale != s2)
- {
- this.view.setScale(s2);
- }
- }
- else
- {
- return s2;
- }
- }
- }
- return this.view.scale;
- };
- /**
- * Function: sizeDidChange
- *
- * Called when the size of the graph has changed. This implementation fires
- * a <size> event after updating the clipping region of the SVG element in
- * SVG-bases browsers.
- */
- mxGraph.prototype.sizeDidChange = function()
- {
- var bounds = this.getGraphBounds();
-
- if (this.container != null)
- {
- var border = this.getBorder();
-
- var width = Math.max(0, bounds.x) + bounds.width + 2 * border;
- var height = Math.max(0, bounds.y) + bounds.height + 2 * border;
-
- if (this.minimumContainerSize != null)
- {
- width = Math.max(width, this.minimumContainerSize.width);
- height = Math.max(height, this.minimumContainerSize.height);
- }
- if (this.resizeContainer)
- {
- this.doResizeContainer(width, height);
- }
- if (this.preferPageSize || (!mxClient.IS_IE && this.pageVisible))
- {
- var size = this.getPreferredPageSize(bounds, Math.max(1, width), Math.max(1, height));
-
- if (size != null)
- {
- width = size.width * this.view.scale;
- height = size.height * this.view.scale;
- }
- }
-
- if (this.minimumGraphSize != null)
- {
- width = Math.max(width, this.minimumGraphSize.width * this.view.scale);
- height = Math.max(height, this.minimumGraphSize.height * this.view.scale);
- }
- width = Math.ceil(width);
- height = Math.ceil(height);
- if (this.dialect == mxConstants.DIALECT_SVG)
- {
- var root = this.view.getDrawPane().ownerSVGElement;
-
- if (root != null)
- {
- root.style.minWidth = Math.max(1, width) + 'px';
- root.style.minHeight = Math.max(1, height) + 'px';
- root.style.width = '100%';
- root.style.height = '100%';
- }
- }
- else
- {
- if (mxClient.IS_QUIRKS)
- {
- // Quirks mode does not support minWidth/-Height
- this.view.updateHtmlCanvasSize(Math.max(1, width), Math.max(1, height));
- }
- else
- {
- this.view.canvas.style.minWidth = Math.max(1, width) + 'px';
- this.view.canvas.style.minHeight = Math.max(1, height) + 'px';
- }
- }
-
- this.updatePageBreaks(this.pageBreaksVisible, width, height);
- }
- this.fireEvent(new mxEventObject(mxEvent.SIZE, 'bounds', bounds));
- };
- /**
- * Function: doResizeContainer
- *
- * Resizes the container for the given graph width and height.
- */
- mxGraph.prototype.doResizeContainer = function(width, height)
- {
- if (this.maximumContainerSize != null)
- {
- width = Math.min(this.maximumContainerSize.width, width);
- height = Math.min(this.maximumContainerSize.height, height);
- }
- this.container.style.width = Math.ceil(width) + 'px';
- this.container.style.height = Math.ceil(height) + 'px';
- };
- /**
- * Function: updatePageBreaks
- *
- * Invokes from <sizeDidChange> to redraw the page breaks.
- *
- * Parameters:
- *
- * visible - Boolean that specifies if page breaks should be shown.
- * width - Specifies the width of the container in pixels.
- * height - Specifies the height of the container in pixels.
- */
- mxGraph.prototype.updatePageBreaks = function(visible, width, height)
- {
- var scale = this.view.scale;
- var tr = this.view.translate;
- var fmt = this.pageFormat;
- var ps = scale * this.pageScale;
- var bounds = new mxRectangle(0, 0, fmt.width * ps, fmt.height * ps);
- var gb = mxRectangle.fromRectangle(this.getGraphBounds());
- gb.width = Math.max(1, gb.width);
- gb.height = Math.max(1, gb.height);
-
- bounds.x = Math.floor((gb.x - tr.x * scale) / bounds.width) * bounds.width + tr.x * scale;
- bounds.y = Math.floor((gb.y - tr.y * scale) / bounds.height) * bounds.height + tr.y * scale;
-
- gb.width = Math.ceil((gb.width + (gb.x - bounds.x)) / bounds.width) * bounds.width;
- gb.height = Math.ceil((gb.height + (gb.y - bounds.y)) / bounds.height) * bounds.height;
-
- // Does not show page breaks if the scale is too small
- visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
- var horizontalCount = (visible) ? Math.ceil(gb.height / bounds.height) + 1 : 0;
- var verticalCount = (visible) ? Math.ceil(gb.width / bounds.width) + 1 : 0;
- var right = (verticalCount - 1) * bounds.width;
- var bottom = (horizontalCount - 1) * bounds.height;
-
- if (this.horizontalPageBreaks == null && horizontalCount > 0)
- {
- this.horizontalPageBreaks = [];
- }
- if (this.verticalPageBreaks == null && verticalCount > 0)
- {
- this.verticalPageBreaks = [];
- }
-
- var drawPageBreaks = mxUtils.bind(this, function(breaks)
- {
- if (breaks != null)
- {
- var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount;
-
- for (var i = 0; i <= count; i++)
- {
- var pts = (breaks == this.horizontalPageBreaks) ?
- [new mxPoint(Math.round(bounds.x), Math.round(bounds.y + i * bounds.height)),
- new mxPoint(Math.round(bounds.x + right), Math.round(bounds.y + i * bounds.height))] :
- [new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y)),
- new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y + bottom))];
- if (breaks[i] != null)
- {
- breaks[i].points = pts;
- breaks[i].redraw();
- }
- else
- {
- var pageBreak = new mxPolyline(pts, this.pageBreakColor);
- pageBreak.dialect = this.dialect;
- pageBreak.pointerEvents = false;
- pageBreak.isDashed = this.pageBreakDashed;
- pageBreak.init(this.view.backgroundPane);
- pageBreak.redraw();
-
- breaks[i] = pageBreak;
- }
- }
-
- for (var i = count; i < breaks.length; i++)
- {
- breaks[i].destroy();
- }
-
- breaks.splice(count, breaks.length - count);
- }
- });
-
- drawPageBreaks(this.horizontalPageBreaks);
- drawPageBreaks(this.verticalPageBreaks);
- };
- /**
- * Group: Cell styles
- */
- /**
- * Function: getCurrentCellStyle
- *
- * Returns the style for the given cell from the cell state, if one exists,
- * or using <getCellStyle>.
- *
- * Parameters:
- *
- * cell - <mxCell> whose style should be returned as an array.
- * ignoreState - Optional boolean that specifies if the cell state should be ignored.
- */
- mxGraph.prototype.getCurrentCellStyle = function(cell, ignoreState)
- {
- var state = (ignoreState) ? null : this.view.getState(cell);
-
- return (state != null) ? state.style : this.getCellStyle(cell);
- };
- /**
- * Function: getCellStyle
- *
- * Returns an array of key, value pairs representing the cell style for the
- * given cell. If no string is defined in the model that specifies the
- * style, then the default style for the cell is returned or an empty object,
- * if no style can be found. Note: You should try and get the cell state
- * for the given cell and use the cached style in the state before using
- * this method.
- *
- * Parameters:
- *
- * cell - <mxCell> whose style should be returned as an array.
- */
- mxGraph.prototype.getCellStyle = function(cell)
- {
- var stylename = this.model.getStyle(cell);
- var style = null;
-
- // Gets the default style for the cell
- if (this.model.isEdge(cell))
- {
- style = this.stylesheet.getDefaultEdgeStyle();
- }
- else
- {
- style = this.stylesheet.getDefaultVertexStyle();
- }
-
- // Resolves the stylename using the above as the default
- if (stylename != null)
- {
- style = this.postProcessCellStyle(this.stylesheet.getCellStyle(stylename, style));
- }
-
- // Returns a non-null value if no style can be found
- if (style == null)
- {
- style = new Object();
- }
-
- return style;
- };
- /**
- * Function: postProcessCellStyle
- *
- * Tries to resolve the value for the image style in the image bundles and
- * turns short data URIs as defined in mxImageBundle to data URIs as
- * defined in RFC 2397 of the IETF.
- */
- mxGraph.prototype.postProcessCellStyle = function(style)
- {
- if (style != null)
- {
- var key = style[mxConstants.STYLE_IMAGE];
- var image = this.getImageFromBundles(key);
- if (image != null)
- {
- style[mxConstants.STYLE_IMAGE] = image;
- }
- else
- {
- image = key;
- }
-
- // Converts short data uris to normal data uris
- if (image != null && image.substring(0, 11) == 'data:image/')
- {
- if (image.substring(0, 20) == 'data:image/svg+xml,<')
- {
- // Required for FF and IE11
- image = image.substring(0, 19) + encodeURIComponent(image.substring(19));
- }
- else if (image.substring(0, 22) != 'data:image/svg+xml,%3C')
- {
- var comma = image.indexOf(',');
-
- // Adds base64 encoding prefix if needed
- if (comma > 0 && image.substring(comma - 7, comma + 1) != ';base64,')
- {
- image = image.substring(0, comma) + ';base64,'
- + image.substring(comma + 1);
- }
- }
-
- style[mxConstants.STYLE_IMAGE] = image;
- }
- }
- return style;
- };
- /**
- * Function: setCellStyle
- *
- * Sets the style of the specified cells. If no cells are given, then the
- * selection cells are changed.
- *
- * Parameters:
- *
- * style - String representing the new style of the cells.
- * cells - Optional array of <mxCells> to set the style for. Default is the
- * selection cells.
- */
- mxGraph.prototype.setCellStyle = function(style, cells)
- {
- cells = cells || this.getSelectionCells();
-
- if (cells != null)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- this.model.setStyle(cells[i], style);
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: toggleCellStyle
- *
- * Toggles the boolean value for the given key in the style of the given cell
- * and returns the new value as 0 or 1. If no cell is specified then the
- * selection cell is used.
- *
- * Parameter:
- *
- * key - String representing the key for the boolean value to be toggled.
- * defaultValue - Optional boolean default value if no value is defined.
- * Default is false.
- * cell - Optional <mxCell> whose style should be modified. Default is
- * the selection cell.
- */
- mxGraph.prototype.toggleCellStyle = function(key, defaultValue, cell)
- {
- cell = cell || this.getSelectionCell();
-
- return this.toggleCellStyles(key, defaultValue, [cell]);
- };
- /**
- * Function: toggleCellStyles
- *
- * Toggles the boolean value for the given key in the style of the given cells
- * and returns the new value as 0 or 1. If no cells are specified, then the
- * selection cells are used. For example, this can be used to toggle
- * <mxConstants.STYLE_ROUNDED> or any other style with a boolean value.
- *
- * Parameter:
- *
- * key - String representing the key for the boolean value to be toggled.
- * defaultValue - Optional boolean default value if no value is defined.
- * Default is false.
- * cells - Optional array of <mxCells> whose styles should be modified.
- * Default is the selection cells.
- */
- mxGraph.prototype.toggleCellStyles = function(key, defaultValue, cells)
- {
- defaultValue = (defaultValue != null) ? defaultValue : false;
- cells = cells || this.getSelectionCells();
- var value = null;
-
- if (cells != null && cells.length > 0)
- {
- var style = this.getCurrentCellStyle(cells[0]);
- value = (mxUtils.getValue(style, key, defaultValue)) ? 0 : 1;
- this.setCellStyles(key, value, cells);
- }
-
- return value;
- };
- /**
- * Function: setCellStyles
- *
- * Sets the key to value in the styles of the given cells. This will modify
- * the existing cell styles in-place and override any existing assignment
- * for the given key. If no cells are specified, then the selection cells
- * are changed. If no value is specified, then the respective key is
- * removed from the styles.
- *
- * Parameters:
- *
- * key - String representing the key to be assigned.
- * value - String representing the new value for the key.
- * cells - Optional array of <mxCells> to change the style for. Default is
- * the selection cells.
- */
- mxGraph.prototype.setCellStyles = function(key, value, cells)
- {
- cells = cells || this.getSelectionCells();
- mxUtils.setCellStyles(this.model, cells, key, value);
- };
- /**
- * Function: toggleCellStyleFlags
- *
- * Toggles the given bit for the given key in the styles of the specified
- * cells.
- *
- * Parameters:
- *
- * key - String representing the key to toggle the flag in.
- * flag - Integer that represents the bit to be toggled.
- * cells - Optional array of <mxCells> to change the style for. Default is
- * the selection cells.
- */
- mxGraph.prototype.toggleCellStyleFlags = function(key, flag, cells)
- {
- this.setCellStyleFlags(key, flag, null, cells);
- };
- /**
- * Function: setCellStyleFlags
- *
- * Sets or toggles the given bit for the given key in the styles of the
- * specified cells.
- *
- * Parameters:
- *
- * key - String representing the key to toggle the flag in.
- * flag - Integer that represents the bit to be toggled.
- * value - Boolean value to be used or null if the value should be toggled.
- * cells - Optional array of <mxCells> to change the style for. Default is
- * the selection cells.
- */
- mxGraph.prototype.setCellStyleFlags = function(key, flag, value, cells)
- {
- cells = cells || this.getSelectionCells();
-
- if (cells != null && cells.length > 0)
- {
- if (value == null)
- {
- var style = this.getCurrentCellStyle(cells[0]);
- var current = parseInt(style[key] || 0);
- value = !((current & flag) == flag);
- }
- mxUtils.setCellStyleFlags(this.model, cells, key, flag, value);
- }
- };
- /**
- * Group: Cell alignment and orientation
- */
- /**
- * Function: alignCells
- *
- * Aligns the given cells vertically or horizontally according to the given
- * alignment using the optional parameter as the coordinate.
- *
- * Parameters:
- *
- * align - Specifies the alignment. Possible values are all constants in
- * mxConstants with an ALIGN prefix.
- * cells - Array of <mxCells> to be aligned.
- * param - Optional coordinate for the alignment.
- */
- mxGraph.prototype.alignCells = function(align, cells, param)
- {
- if (cells == null)
- {
- cells = this.getSelectionCells();
- }
-
- if (cells != null && cells.length > 1)
- {
- // Finds the required coordinate for the alignment
- if (param == null)
- {
- for (var i = 0; i < cells.length; i++)
- {
- var state = this.view.getState(cells[i]);
-
- if (state != null && !this.model.isEdge(cells[i]))
- {
- if (param == null)
- {
- if (align == mxConstants.ALIGN_CENTER)
- {
- param = state.x + state.width / 2;
- break;
- }
- else if (align == mxConstants.ALIGN_RIGHT)
- {
- param = state.x + state.width;
- }
- else if (align == mxConstants.ALIGN_TOP)
- {
- param = state.y;
- }
- else if (align == mxConstants.ALIGN_MIDDLE)
- {
- param = state.y + state.height / 2;
- break;
- }
- else if (align == mxConstants.ALIGN_BOTTOM)
- {
- param = state.y + state.height;
- }
- else
- {
- param = state.x;
- }
- }
- else
- {
- if (align == mxConstants.ALIGN_RIGHT)
- {
- param = Math.max(param, state.x + state.width);
- }
- else if (align == mxConstants.ALIGN_TOP)
- {
- param = Math.min(param, state.y);
- }
- else if (align == mxConstants.ALIGN_BOTTOM)
- {
- param = Math.max(param, state.y + state.height);
- }
- else
- {
- param = Math.min(param, state.x);
- }
- }
- }
- }
- }
- // Aligns the cells to the coordinate
- if (param != null)
- {
- var s = this.view.scale;
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- var state = this.view.getState(cells[i]);
-
- if (state != null)
- {
- var geo = this.getCellGeometry(cells[i]);
-
- if (geo != null && !this.model.isEdge(cells[i]))
- {
- geo = geo.clone();
-
- if (align == mxConstants.ALIGN_CENTER)
- {
- geo.x += (param - state.x - state.width / 2) / s;
- }
- else if (align == mxConstants.ALIGN_RIGHT)
- {
- geo.x += (param - state.x - state.width) / s;
- }
- else if (align == mxConstants.ALIGN_TOP)
- {
- geo.y += (param - state.y) / s;
- }
- else if (align == mxConstants.ALIGN_MIDDLE)
- {
- geo.y += (param - state.y - state.height / 2) / s;
- }
- else if (align == mxConstants.ALIGN_BOTTOM)
- {
- geo.y += (param - state.y - state.height) / s;
- }
- else
- {
- geo.x += (param - state.x) / s;
- }
-
- this.resizeCell(cells[i], geo);
- }
- }
- }
-
- this.fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS,
- 'align', align, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- }
-
- return cells;
- };
- /**
- * Function: flipEdge
- *
- * Toggles the style of the given edge between null (or empty) and
- * <alternateEdgeStyle>. This method fires <mxEvent.FLIP_EDGE> while the
- * transaction is in progress. Returns the edge that was flipped.
- *
- * Here is an example that overrides this implementation to invert the
- * value of <mxConstants.STYLE_ELBOW> without removing any existing styles.
- *
- * (code)
- * graph.flipEdge = function(edge)
- * {
- * if (edge != null)
- * {
- * var style = this.getCurrentCellStyle(edge);
- * var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW,
- * mxConstants.ELBOW_HORIZONTAL);
- * var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ?
- * mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL;
- * this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]);
- * }
- * };
- * (end)
- *
- * Parameters:
- *
- * edge - <mxCell> whose style should be changed.
- */
- mxGraph.prototype.flipEdge = function(edge)
- {
- if (edge != null &&
- this.alternateEdgeStyle != null)
- {
- this.model.beginUpdate();
- try
- {
- var style = this.model.getStyle(edge);
- if (style == null || style.length == 0)
- {
- this.model.setStyle(edge, this.alternateEdgeStyle);
- }
- else
- {
- this.model.setStyle(edge, null);
- }
- // Removes all existing control points
- this.resetEdge(edge);
- this.fireEvent(new mxEventObject(mxEvent.FLIP_EDGE, 'edge', edge));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- return edge;
- };
- /**
- * Function: addImageBundle
- *
- * Adds the specified <mxImageBundle>.
- */
- mxGraph.prototype.addImageBundle = function(bundle)
- {
- this.imageBundles.push(bundle);
- };
- /**
- * Function: removeImageBundle
- *
- * Removes the specified <mxImageBundle>.
- */
- mxGraph.prototype.removeImageBundle = function(bundle)
- {
- var tmp = [];
-
- for (var i = 0; i < this.imageBundles.length; i++)
- {
- if (this.imageBundles[i] != bundle)
- {
- tmp.push(this.imageBundles[i]);
- }
- }
-
- this.imageBundles = tmp;
- };
- /**
- * Function: getImageFromBundles
- *
- * Searches all <imageBundles> for the specified key and returns the value
- * for the first match or null if the key is not found.
- */
- mxGraph.prototype.getImageFromBundles = function(key)
- {
- if (key != null)
- {
- for (var i = 0; i < this.imageBundles.length; i++)
- {
- var image = this.imageBundles[i].getImage(key);
-
- if (image != null)
- {
- return image;
- }
- }
- }
-
- return null;
- };
- /**
- * Group: Order
- */
- /**
- * Function: orderCells
- *
- * Moves the given cells to the front or back. The change is carried out
- * using <cellsOrdered>. This method fires <mxEvent.ORDER_CELLS> while the
- * transaction is in progress.
- *
- * Parameters:
- *
- * back - Boolean that specifies if the cells should be moved to back.
- * cells - Array of <mxCells> to move to the background. If null is
- * specified then the selection cells are used.
- */
- mxGraph.prototype.orderCells = function(back, cells)
- {
- if (cells == null)
- {
- cells = mxUtils.sortCells(this.getSelectionCells(), true);
- }
- this.model.beginUpdate();
- try
- {
- this.cellsOrdered(cells, back);
- this.fireEvent(new mxEventObject(mxEvent.ORDER_CELLS,
- 'back', back, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: cellsOrdered
- *
- * Moves the given cells to the front or back. This method fires
- * <mxEvent.CELLS_ORDERED> while the transaction is in progress.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose order should be changed.
- * back - Boolean that specifies if the cells should be moved to back.
- */
- mxGraph.prototype.cellsOrdered = function(cells, back)
- {
- if (cells != null)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- var parent = this.model.getParent(cells[i]);
- if (back)
- {
- this.model.add(parent, cells[i], i);
- }
- else
- {
- this.model.add(parent, cells[i],
- this.model.getChildCount(parent) - 1);
- }
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_ORDERED,
- 'back', back, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Group: Grouping
- */
- /**
- * Function: groupCells
- *
- * Adds the cells into the given group. The change is carried out using
- * <cellsAdded>, <cellsMoved> and <cellsResized>. This method fires
- * <mxEvent.GROUP_CELLS> while the transaction is in progress. Returns the
- * new group. A group is only created if there is at least one entry in the
- * given array of cells.
- *
- * Parameters:
- *
- * group - <mxCell> that represents the target group. If null is specified
- * then a new group is created using <createGroupCell>.
- * border - Optional integer that specifies the border between the child
- * area and the group bounds. Default is 0.
- * cells - Optional array of <mxCells> to be grouped. If null is specified
- * then the selection cells are used.
- */
- mxGraph.prototype.groupCells = function(group, border, cells)
- {
- if (cells == null)
- {
- cells = mxUtils.sortCells(this.getSelectionCells(), true);
- }
- cells = this.getCellsForGroup(cells);
- if (group == null)
- {
- group = this.createGroupCell(cells);
- }
- var bounds = this.getBoundsForGroup(group, cells, border);
- if (cells.length > 1 && bounds != null)
- {
- // Uses parent of group or previous parent of first child
- var parent = this.model.getParent(group);
-
- if (parent == null)
- {
- parent = this.model.getParent(cells[0]);
- }
- this.model.beginUpdate();
- try
- {
- // Checks if the group has a geometry and
- // creates one if one does not exist
- if (this.getCellGeometry(group) == null)
- {
- this.model.setGeometry(group, new mxGeometry());
- }
- // Adds the group into the parent
- var index = this.model.getChildCount(parent);
- this.cellsAdded([group], parent, index, null, null, false, false, false);
- // Adds the children into the group and moves
- index = this.model.getChildCount(group);
- this.cellsAdded(cells, group, index, null, null, false, false, false);
- this.cellsMoved(cells, -bounds.x, -bounds.y, false, false, false);
- // Resizes the group
- this.cellsResized([group], [bounds], false);
- this.fireEvent(new mxEventObject(mxEvent.GROUP_CELLS,
- 'group', group, 'border', border, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- return group;
- };
- /**
- * Function: getCellsForGroup
- *
- * Returns the cells with the same parent as the first cell
- * in the given array.
- */
- mxGraph.prototype.getCellsForGroup = function(cells)
- {
- var result = [];
- if (cells != null && cells.length > 0)
- {
- var parent = this.model.getParent(cells[0]);
- result.push(cells[0]);
- // Filters selection cells with the same parent
- for (var i = 1; i < cells.length; i++)
- {
- if (this.model.getParent(cells[i]) == parent)
- {
- result.push(cells[i]);
- }
- }
- }
- return result;
- };
- /**
- * Function: getBoundsForGroup
- *
- * Returns the bounds to be used for the given group and children.
- */
- mxGraph.prototype.getBoundsForGroup = function(group, children, border)
- {
- var result = this.getBoundingBoxFromGeometry(children, true);
-
- if (result != null)
- {
- if (this.isSwimlane(group))
- {
- var size = this.getStartSize(group);
-
- result.x -= size.width;
- result.y -= size.height;
- result.width += size.width;
- result.height += size.height;
- }
-
- // Adds the border
- if (border != null)
- {
- result.x -= border;
- result.y -= border;
- result.width += 2 * border;
- result.height += 2 * border;
- }
- }
-
- return result;
- };
- /**
- * Function: createGroupCell
- *
- * Hook for creating the group cell to hold the given array of <mxCells> if
- * no group cell was given to the <group> function.
- *
- * The following code can be used to set the style of new group cells.
- *
- * (code)
- * var graphCreateGroupCell = graph.createGroupCell;
- * graph.createGroupCell = function(cells)
- * {
- * var group = graphCreateGroupCell.apply(this, arguments);
- * group.setStyle('group');
- *
- * return group;
- * };
- */
- mxGraph.prototype.createGroupCell = function(cells)
- {
- var group = new mxCell('');
- group.setVertex(true);
- group.setConnectable(false);
-
- return group;
- };
- /**
- * Function: ungroupCells
- *
- * Ungroups the given cells by moving the children the children to their
- * parents parent and removing the empty groups. Returns the children that
- * have been removed from the groups.
- *
- * Parameters:
- *
- * cells - Array of cells to be ungrouped. If null is specified then the
- * selection cells are used.
- */
- mxGraph.prototype.ungroupCells = function(cells)
- {
- var result = [];
-
- if (cells == null)
- {
- cells = this.getCellsForUngroup();
- }
-
- if (cells != null && cells.length > 0)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- var children = this.model.getChildren(cells[i]);
-
- if (children != null && children.length > 0)
- {
- children = children.slice();
- var parent = this.model.getParent(cells[i]);
- var index = this.model.getChildCount(parent);
- this.cellsAdded(children, parent, index, null, null, true);
- result = result.concat(children);
-
- // Fix relative child cells
- for (var j = 0; j < children.length; j++)
- {
- var state = this.view.getState(children[j]);
- var geo = this.getCellGeometry(children[j]);
-
- if (state != null && geo != null && geo.relative)
- {
- geo = geo.clone();
- geo.x = state.origin.x;
- geo.y = state.origin.y;
- geo.relative = false;
-
- this.model.setGeometry(children[j], geo);
- }
- }
- }
- }
- this.removeCellsAfterUngroup(cells);
- this.fireEvent(new mxEventObject(mxEvent.UNGROUP_CELLS, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
-
- return result;
- };
- /**
- * Function: getCellsForUngroup
- *
- * Returns the selection cells that can be ungrouped.
- */
- mxGraph.prototype.getCellsForUngroup = function()
- {
- var cells = this.getSelectionCells();
- // Finds the cells with children
- var tmp = [];
-
- for (var i = 0; i < cells.length; i++)
- {
- if (this.model.isVertex(cells[i]) &&
- this.model.getChildCount(cells[i]) > 0)
- {
- tmp.push(cells[i]);
- }
- }
- return tmp;
- };
- /**
- * Function: removeCellsAfterUngroup
- *
- * Hook to remove the groups after <ungroupCells>.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> that were ungrouped.
- */
- mxGraph.prototype.removeCellsAfterUngroup = function(cells)
- {
- this.cellsRemoved(this.addAllEdges(cells));
- };
- /**
- * Function: removeCellsFromParent
- *
- * Removes the specified cells from their parents and adds them to the
- * default parent. Returns the cells that were removed from their parents.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be removed from their parents.
- */
- mxGraph.prototype.removeCellsFromParent = function(cells)
- {
- if (cells == null)
- {
- cells = this.getSelectionCells();
- }
-
- this.model.beginUpdate();
- try
- {
- var parent = this.getDefaultParent();
- var index = this.model.getChildCount(parent);
- this.cellsAdded(cells, parent, index, null, null, true);
- this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS_FROM_PARENT, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: updateGroupBounds
- *
- * Updates the bounds of the given groups to include all children and returns
- * the passed-in cells. Call this with the groups in parent to child order,
- * top-most group first, the cells are processed in reverse order and cells
- * with no children are ignored.
- *
- * Parameters:
- *
- * cells - The groups whose bounds should be updated. If this is null, then
- * the selection cells are used.
- * border - Optional border to be added in the group. Default is 0.
- * moveGroup - Optional boolean that allows the group to be moved. Default
- * is false.
- * topBorder - Optional top border to be added in the group. Default is 0.
- * rightBorder - Optional top border to be added in the group. Default is 0.
- * bottomBorder - Optional top border to be added in the group. Default is 0.
- * leftBorder - Optional top border to be added in the group. Default is 0.
- */
- mxGraph.prototype.updateGroupBounds = function(cells, border, moveGroup, topBorder, rightBorder, bottomBorder, leftBorder)
- {
- if (cells == null)
- {
- cells = this.getSelectionCells();
- }
-
- border = (border != null) ? border : 0;
- moveGroup = (moveGroup != null) ? moveGroup : false;
- topBorder = (topBorder != null) ? topBorder : 0;
- rightBorder = (rightBorder != null) ? rightBorder : 0;
- bottomBorder = (bottomBorder != null) ? bottomBorder : 0;
- leftBorder = (leftBorder != null) ? leftBorder : 0;
- this.model.beginUpdate();
- try
- {
- for (var i = cells.length - 1; i >= 0; i--)
- {
- var geo = this.getCellGeometry(cells[i]);
-
- if (geo != null)
- {
- var children = this.getChildCells(cells[i]);
-
- if (children != null && children.length > 0)
- {
- var bounds = this.getBoundingBoxFromGeometry(children, true);
-
- if (bounds != null && bounds.width > 0 && bounds.height > 0)
- {
- // Adds the size of the title area for swimlanes
- var size = (this.isSwimlane(cells[i])) ?
- this.getActualStartSize(cells[i], true) : new mxRectangle();
- geo = geo.clone();
-
- if (moveGroup)
- {
- geo.x = Math.round(geo.x + bounds.x - border - size.x - leftBorder);
- geo.y = Math.round(geo.y + bounds.y - border - size.y - topBorder);
- }
-
- geo.width = Math.round(bounds.width + 2 * border + size.x + leftBorder + rightBorder + size.width);
- geo.height = Math.round(bounds.height + 2 * border + size.y + topBorder + bottomBorder + size.height);
-
- this.model.setGeometry(cells[i], geo);
- this.moveCells(children, border + size.x - bounds.x + leftBorder,
- border + size.y - bounds.y + topBorder);
- }
- }
- }
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: getBoundingBox
- *
- * Returns the bounding box for the given array of <mxCells>. The bounding box for
- * each cell and its descendants is computed using <mxGraphView.getBoundingBox>.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose bounding box should be returned.
- */
- mxGraph.prototype.getBoundingBox = function(cells)
- {
- var result = null;
-
- if (cells != null && cells.length > 0)
- {
- for (var i = 0; i < cells.length; i++)
- {
- if (this.model.isVertex(cells[i]) || this.model.isEdge(cells[i]))
- {
- var bbox = this.view.getBoundingBox(this.view.getState(cells[i]), true);
-
- if (bbox != null)
- {
- if (result == null)
- {
- result = mxRectangle.fromRectangle(bbox);
- }
- else
- {
- result.add(bbox);
- }
- }
- }
- }
- }
-
- return result;
- };
- /**
- * Group: Cell cloning, insertion and removal
- */
- /**
- * Function: cloneCell
- *
- * Returns the clone for the given cell. Uses <cloneCells>.
- *
- * Parameters:
- *
- * cell - <mxCell> to be cloned.
- * allowInvalidEdges - Optional boolean that specifies if invalid edges
- * should be cloned. Default is true.
- * mapping - Optional mapping for existing clones.
- * keepPosition - Optional boolean indicating if the position of the cells should
- * be updated to reflect the lost parent cell. Default is false.
- */
- mxGraph.prototype.cloneCell = function(cell, allowInvalidEdges, mapping, keepPosition)
- {
- return this.cloneCells([cell], allowInvalidEdges, mapping, keepPosition)[0];
- };
- /**
- * Function: cloneCells
- *
- * Returns the clones for the given cells. The clones are created recursively
- * using <mxGraphModel.cloneCells>. If the terminal of an edge is not in the
- * given array, then the respective end is assigned a terminal point and the
- * terminal is removed.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be cloned.
- * allowInvalidEdges - Optional boolean that specifies if invalid edges
- * should be cloned. Default is true.
- * mapping - Optional mapping for existing clones.
- * keepPosition - Optional boolean indicating if the position of the cells should
- * be updated to reflect the lost parent cell. Default is false.
- */
- mxGraph.prototype.cloneCells = function(cells, allowInvalidEdges, mapping, keepPosition)
- {
- allowInvalidEdges = (allowInvalidEdges != null) ? allowInvalidEdges : true;
- var clones = null;
-
- if (cells != null)
- {
- // Creates a dictionary for fast lookups
- var dict = new mxDictionary();
- var tmp = [];
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- tmp.push(cells[i]);
- }
-
- if (tmp.length > 0)
- {
- var scale = this.view.scale;
- var trans = this.view.translate;
- clones = this.model.cloneCells(cells, true, mapping);
-
- for (var i = 0; i < cells.length; i++)
- {
- if (!allowInvalidEdges && this.model.isEdge(clones[i]) &&
- this.getEdgeValidationError(clones[i],
- this.model.getTerminal(clones[i], true),
- this.model.getTerminal(clones[i], false)) != null)
- {
- clones[i] = null;
- }
- else
- {
- var g = this.model.getGeometry(clones[i]);
-
- if (g != null)
- {
- var state = this.view.getState(cells[i]);
- var pstate = this.view.getState(this.model.getParent(cells[i]));
-
- if (state != null && pstate != null)
- {
- var dx = (keepPosition) ? 0 : pstate.origin.x;
- var dy = (keepPosition) ? 0 : pstate.origin.y;
-
- if (this.model.isEdge(clones[i]))
- {
- var pts = state.absolutePoints;
-
- if (pts != null)
- {
- // Checks if the source is cloned or sets the terminal point
- var src = this.model.getTerminal(cells[i], true);
-
- while (src != null && !dict.get(src))
- {
- src = this.model.getParent(src);
- }
-
- if (src == null && pts[0] != null)
- {
- g.setTerminalPoint(
- new mxPoint(pts[0].x / scale - trans.x,
- pts[0].y / scale - trans.y), true);
- }
-
- // Checks if the target is cloned or sets the terminal point
- var trg = this.model.getTerminal(cells[i], false);
-
- while (trg != null && !dict.get(trg))
- {
- trg = this.model.getParent(trg);
- }
- var n = pts.length - 1;
-
- if (trg == null && pts[n] != null)
- {
- g.setTerminalPoint(
- new mxPoint(pts[n].x / scale - trans.x,
- pts[n].y / scale - trans.y), false);
- }
-
- // Translates the control points
- var points = g.points;
-
- if (points != null)
- {
- for (var j = 0; j < points.length; j++)
- {
- points[j].x += dx;
- points[j].y += dy;
- }
- }
- }
- }
- else
- {
- g.translate(dx, dy);
- }
- }
- }
- }
- }
- }
- else
- {
- clones = [];
- }
- }
-
- return clones;
- };
- /**
- * Function: insertVertex
- *
- * Adds a new vertex into the given parent <mxCell> using value as the user
- * object and the given coordinates as the <mxGeometry> of the new vertex.
- * The id and style are used for the respective properties of the new
- * <mxCell>, which is returned.
- *
- * When adding new vertices from a mouse event, one should take into
- * account the offset of the graph container and the scale and translation
- * of the view in order to find the correct unscaled, untranslated
- * coordinates using <mxGraph.getPointForEvent> as follows:
- *
- * (code)
- * var pt = graph.getPointForEvent(evt);
- * var parent = graph.getDefaultParent();
- * graph.insertVertex(parent, null,
- * 'Hello, World!', x, y, 220, 30);
- * (end)
- *
- * For adding image cells, the style parameter can be assigned as
- *
- * (code)
- * stylename;image=imageUrl
- * (end)
- *
- * See <mxGraph> for more information on using images.
- *
- * Parameters:
- *
- * parent - <mxCell> that specifies the parent of the new vertex.
- * id - Optional string that defines the Id of the new vertex.
- * value - Object to be used as the user object.
- * x - Integer that defines the x coordinate of the vertex.
- * y - Integer that defines the y coordinate of the vertex.
- * width - Integer that defines the width of the vertex.
- * height - Integer that defines the height of the vertex.
- * style - Optional string that defines the cell style.
- * relative - Optional boolean that specifies if the geometry is relative.
- * Default is false.
- */
- mxGraph.prototype.insertVertex = function(parent, id, value,
- x, y, width, height, style, relative)
- {
- var vertex = this.createVertex(parent, id, value, x, y, width, height, style, relative);
- return this.addCell(vertex, parent);
- };
- /**
- * Function: createVertex
- *
- * Hook method that creates the new vertex for <insertVertex>.
- */
- mxGraph.prototype.createVertex = function(parent, id, value,
- x, y, width, height, style, relative)
- {
- // Creates the geometry for the vertex
- var geometry = new mxGeometry(x, y, width, height);
- geometry.relative = (relative != null) ? relative : false;
-
- // Creates the vertex
- var vertex = new mxCell(value, geometry, style);
- vertex.setId(id);
- vertex.setVertex(true);
- vertex.setConnectable(true);
-
- return vertex;
- };
-
- /**
- * Function: insertEdge
- *
- * Adds a new edge into the given parent <mxCell> using value as the user
- * object and the given source and target as the terminals of the new edge.
- * The id and style are used for the respective properties of the new
- * <mxCell>, which is returned.
- *
- * Parameters:
- *
- * parent - <mxCell> that specifies the parent of the new edge.
- * id - Optional string that defines the Id of the new edge.
- * value - JavaScript object to be used as the user object.
- * source - <mxCell> that defines the source of the edge.
- * target - <mxCell> that defines the target of the edge.
- * style - Optional string that defines the cell style.
- */
- mxGraph.prototype.insertEdge = function(parent, id, value, source, target, style)
- {
- var edge = this.createEdge(parent, id, value, source, target, style);
-
- return this.addEdge(edge, parent, source, target);
- };
- /**
- * Function: createEdge
- *
- * Hook method that creates the new edge for <insertEdge>. This
- * implementation does not set the source and target of the edge, these
- * are set when the edge is added to the model.
- *
- */
- mxGraph.prototype.createEdge = function(parent, id, value, source, target, style)
- {
- // Creates the edge
- var edge = new mxCell(value, new mxGeometry(), style);
- edge.setId(id);
- edge.setEdge(true);
- edge.geometry.relative = true;
-
- return edge;
- };
- /**
- * Function: addEdge
- *
- * Adds the edge to the parent and connects it to the given source and
- * target terminals. This is a shortcut method. Returns the edge that was
- * added.
- *
- * Parameters:
- *
- * edge - <mxCell> to be inserted into the given parent.
- * parent - <mxCell> that represents the new parent. If no parent is
- * given then the default parent is used.
- * source - Optional <mxCell> that represents the source terminal.
- * target - Optional <mxCell> that represents the target terminal.
- * index - Optional index to insert the cells at. Default is to append.
- */
- mxGraph.prototype.addEdge = function(edge, parent, source, target, index)
- {
- return this.addCell(edge, parent, index, source, target);
- };
- /**
- * Function: addCell
- *
- * Adds the cell to the parent and connects it to the given source and
- * target terminals. This is a shortcut method. Returns the cell that was
- * added.
- *
- * Parameters:
- *
- * cell - <mxCell> to be inserted into the given parent.
- * parent - <mxCell> that represents the new parent. If no parent is
- * given then the default parent is used.
- * index - Optional index to insert the cells at. Default is to append.
- * source - Optional <mxCell> that represents the source terminal.
- * target - Optional <mxCell> that represents the target terminal.
- */
- mxGraph.prototype.addCell = function(cell, parent, index, source, target)
- {
- return this.addCells([cell], parent, index, source, target)[0];
- };
- /**
- * Function: addCells
- *
- * Adds the cells to the parent at the given index, connecting each cell to
- * the optional source and target terminal. The change is carried out using
- * <cellsAdded>. This method fires <mxEvent.ADD_CELLS> while the
- * transaction is in progress. Returns the cells that were added.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be inserted.
- * parent - <mxCell> that represents the new parent. If no parent is
- * given then the default parent is used.
- * index - Optional index to insert the cells at. Default is to append.
- * source - Optional source <mxCell> for all inserted cells.
- * target - Optional target <mxCell> for all inserted cells.
- * absolute - Optional boolean indicating of cells should be kept at
- * their absolute position. Default is false.
- */
- mxGraph.prototype.addCells = function(cells, parent, index, source, target, absolute)
- {
- if (parent == null)
- {
- parent = this.getDefaultParent();
- }
-
- if (index == null)
- {
- index = this.model.getChildCount(parent);
- }
-
- this.model.beginUpdate();
- try
- {
- this.cellsAdded(cells, parent, index, source, target, (absolute != null) ? absolute : false, true);
- this.fireEvent(new mxEventObject(mxEvent.ADD_CELLS, 'cells', cells,
- 'parent', parent, 'index', index, 'source', source, 'target', target));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: cellsAdded
- *
- * Adds the specified cells to the given parent. This method fires
- * <mxEvent.CELLS_ADDED> while the transaction is in progress.
- */
- mxGraph.prototype.cellsAdded = function(cells, parent, index, source, target, absolute, constrain, extend)
- {
- if (cells != null && parent != null && index != null)
- {
- this.model.beginUpdate();
- try
- {
- var parentState = (absolute) ? this.view.getState(parent) : null;
- var o1 = (parentState != null) ? parentState.origin : null;
- var zero = new mxPoint(0, 0);
- for (var i = 0; i < cells.length; i++)
- {
- if (cells[i] == null)
- {
- index--;
- }
- else
- {
- var previous = this.model.getParent(cells[i]);
-
- // Keeps the cell at its absolute location
- if (o1 != null && cells[i] != parent && parent != previous)
- {
- var oldState = this.view.getState(previous);
- var o2 = (oldState != null) ? oldState.origin : zero;
- var geo = this.model.getGeometry(cells[i]);
-
- if (geo != null)
- {
- var dx = o2.x - o1.x;
- var dy = o2.y - o1.y;
-
- // FIXME: Cells should always be inserted first before any other edit
- // to avoid forward references in sessions.
- geo = geo.clone();
- geo.translate(dx, dy);
-
- if (!geo.relative && this.model.isVertex(cells[i]) &&
- !this.isAllowNegativeCoordinates())
- {
- geo.x = Math.max(0, geo.x);
- geo.y = Math.max(0, geo.y);
- }
-
- this.model.setGeometry(cells[i], geo);
- }
- }
-
- // Decrements all following indices
- // if cell is already in parent
- if (parent == previous && index + i > this.model.getChildCount(parent))
- {
- index--;
- }
- this.model.add(parent, cells[i], index + i);
-
- if (this.autoSizeCellsOnAdd)
- {
- this.autoSizeCell(cells[i], true);
- }
- // Extends the parent or constrains the child
- if ((extend == null || extend) &&
- this.isExtendParentsOnAdd(cells[i]) && this.isExtendParent(cells[i]))
- {
- this.extendParent(cells[i]);
- }
-
- // Additionally constrains the child after extending the parent
- if (constrain == null || constrain)
- {
- this.constrainChild(cells[i]);
- }
-
- // Sets the source terminal
- if (source != null)
- {
- this.cellConnected(cells[i], source, true);
- }
-
- // Sets the target terminal
- if (target != null)
- {
- this.cellConnected(cells[i], target, false);
- }
- }
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_ADDED, 'cells', cells,
- 'parent', parent, 'index', index, 'source', source, 'target', target,
- 'absolute', absolute));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: autoSizeCell
- *
- * Resizes the specified cell to just fit around the its label and/or children
- *
- * Parameters:
- *
- * cell - <mxCells> to be resized.
- * recurse - Optional boolean which specifies if all descendants should be
- * autosized. Default is true.
- */
- mxGraph.prototype.autoSizeCell = function(cell, recurse)
- {
- recurse = (recurse != null) ? recurse : true;
-
- if (recurse)
- {
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- this.autoSizeCell(this.model.getChildAt(cell, i));
- }
- }
- if (this.getModel().isVertex(cell) && this.isAutoSizeCell(cell))
- {
- this.updateCellSize(cell);
- }
- };
- /**
- * Function: removeCells
- *
- * Removes the given cells from the graph including all connected edges if
- * includeEdges is true. The change is carried out using <cellsRemoved>.
- * This method fires <mxEvent.REMOVE_CELLS> while the transaction is in
- * progress. The removed cells are returned as an array.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to remove. If null is specified then the
- * selection cells which are deletable are used.
- * includeEdges - Optional boolean which specifies if all connected edges
- * should be removed as well. Default is true.
- */
- mxGraph.prototype.removeCells = function(cells, includeEdges)
- {
- includeEdges = (includeEdges != null) ? includeEdges : true;
-
- if (cells == null)
- {
- cells = this.getDeletableCells(this.getSelectionCells());
- }
- // Adds all edges to the cells
- if (includeEdges)
- {
- // FIXME: Remove duplicate cells in result or do not add if
- // in cells or descendant of cells
- cells = this.getDeletableCells(this.addAllEdges(cells));
- }
- else
- {
- cells = cells.slice();
-
- // Removes edges that are currently not
- // visible as those cannot be updated
- var edges = this.getDeletableCells(this.getAllEdges(cells));
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- for (var i = 0; i < edges.length; i++)
- {
- if (this.view.getState(edges[i]) == null &&
- !dict.get(edges[i]))
- {
- dict.put(edges[i], true);
- cells.push(edges[i]);
- }
- }
- }
- this.model.beginUpdate();
- try
- {
- this.cellsRemoved(cells);
- this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS,
- 'cells', cells, 'includeEdges', includeEdges));
- }
- finally
- {
- this.model.endUpdate();
- }
-
- return cells;
- };
- /**
- * Function: cellsRemoved
- *
- * Removes the given cells from the model. This method fires
- * <mxEvent.CELLS_REMOVED> while the transaction is in progress.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to remove.
- */
- mxGraph.prototype.cellsRemoved = function(cells)
- {
- if (cells != null && cells.length > 0)
- {
- var scale = this.view.scale;
- var tr = this.view.translate;
-
- this.model.beginUpdate();
- try
- {
- // Creates hashtable for faster lookup
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- for (var i = 0; i < cells.length; i++)
- {
- // Disconnects edges which are not being removed
- var edges = this.getAllEdges([cells[i]]);
-
- var disconnectTerminal = mxUtils.bind(this, function(edge, source)
- {
- var geo = this.model.getGeometry(edge);
- if (geo != null)
- {
- // Checks if terminal is being removed
- var terminal = this.model.getTerminal(edge, source);
- var connected = false;
- var tmp = terminal;
-
- while (tmp != null)
- {
- if (cells[i] == tmp)
- {
- connected = true;
- break;
- }
-
- tmp = this.model.getParent(tmp);
- }
- if (connected)
- {
- geo = geo.clone();
- var state = this.view.getState(edge);
- if (state != null && state.absolutePoints != null)
- {
- var pts = state.absolutePoints;
- var n = (source) ? 0 : pts.length - 1;
- geo.setTerminalPoint(new mxPoint(
- pts[n].x / scale - tr.x - state.origin.x,
- pts[n].y / scale - tr.y - state.origin.y), source);
- }
- else
- {
- // Fallback to center of terminal if routing
- // points are not available to add new point
- // KNOWN: Should recurse to find parent offset
- // of edge for nested groups but invisible edges
- // should be removed in removeCells step
- var tstate = this.view.getState(terminal);
-
- if (tstate != null)
- {
- geo.setTerminalPoint(new mxPoint(
- tstate.getCenterX() / scale - tr.x,
- tstate.getCenterY() / scale - tr.y), source);
- }
- }
- this.model.setGeometry(edge, geo);
- this.model.setTerminal(edge, null, source);
- }
- }
- });
-
- for (var j = 0; j < edges.length; j++)
- {
- if (!dict.get(edges[j]))
- {
- dict.put(edges[j], true);
- disconnectTerminal(edges[j], true);
- disconnectTerminal(edges[j], false);
- }
- }
- this.model.remove(cells[i]);
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: splitEdge
- *
- * Splits the given edge by adding the newEdge between the previous source
- * and the given cell and reconnecting the source of the given edge to the
- * given cell. This method fires <mxEvent.SPLIT_EDGE> while the transaction
- * is in progress. Returns the new edge that was inserted.
- *
- * Parameters:
- *
- * edge - <mxCell> that represents the edge to be splitted.
- * cells - <mxCells> that represents the cells to insert into the edge.
- * newEdge - <mxCell> that represents the edge to be inserted.
- * dx - Optional integer that specifies the vector to move the cells.
- * dy - Optional integer that specifies the vector to move the cells.
- * x - Integer that specifies the x-coordinate of the drop location.
- * y - Integer that specifies the y-coordinate of the drop location.
- * parent - Optional parent to insert the cell. If null the parent of
- * the edge is used.
- */
- mxGraph.prototype.splitEdge = function(edge, cells, newEdge, dx, dy, x, y, parent)
- {
- dx = dx || 0;
- dy = dy || 0;
- parent = (parent != null) ? parent : this.model.getParent(edge);
- var source = this.model.getTerminal(edge, true);
-
- this.model.beginUpdate();
- try
- {
- if (newEdge == null)
- {
- newEdge = this.cloneCell(edge);
-
- // Removes waypoints before/after new cell
- var state = this.view.getState(edge);
- var geo = this.getCellGeometry(newEdge);
-
- if (geo != null && geo.points != null && state != null)
- {
- var t = this.view.translate;
- var s = this.view.scale;
- var idx = mxUtils.findNearestSegment(state, (dx + t.x) * s, (dy + t.y) * s);
- geo.points = geo.points.slice(0, idx);
-
- geo = this.getCellGeometry(edge);
-
- if (geo != null && geo.points != null)
- {
- geo = geo.clone();
- geo.points = geo.points.slice(idx);
- this.model.setGeometry(edge, geo);
- }
- }
- }
-
- this.cellsMoved(cells, dx, dy, false, false);
- this.cellsAdded(cells, parent, this.model.getChildCount(parent), null, null,
- true);
- this.cellsAdded([newEdge], parent, this.model.getChildCount(parent),
- source, cells[0], false);
- this.cellConnected(edge, cells[0], true);
- this.fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE, 'edge', edge,
- 'cells', cells, 'newEdge', newEdge, 'dx', dx, 'dy', dy));
- }
- finally
- {
- this.model.endUpdate();
- }
- return newEdge;
- };
- /**
- * Group: Cell visibility
- */
- /**
- * Function: toggleCells
- *
- * Sets the visible state of the specified cells and all connected edges
- * if includeEdges is true. The change is carried out using <cellsToggled>.
- * This method fires <mxEvent.TOGGLE_CELLS> while the transaction is in
- * progress. Returns the cells whose visible state was changed.
- *
- * Parameters:
- *
- * show - Boolean that specifies the visible state to be assigned.
- * cells - Array of <mxCells> whose visible state should be changed. If
- * null is specified then the selection cells are used.
- * includeEdges - Optional boolean indicating if the visible state of all
- * connected edges should be changed as well. Default is true.
- */
- mxGraph.prototype.toggleCells = function(show, cells, includeEdges)
- {
- if (cells == null)
- {
- cells = this.getSelectionCells();
- }
- // Adds all connected edges recursively
- if (includeEdges)
- {
- cells = this.addAllEdges(cells);
- }
- this.model.beginUpdate();
- try
- {
- this.cellsToggled(cells, show);
- this.fireEvent(new mxEventObject(mxEvent.TOGGLE_CELLS,
- 'show', show, 'cells', cells, 'includeEdges', includeEdges));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: cellsToggled
- *
- * Sets the visible state of the specified cells.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose visible state should be changed.
- * show - Boolean that specifies the visible state to be assigned.
- */
- mxGraph.prototype.cellsToggled = function(cells, show)
- {
- if (cells != null && cells.length > 0)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- this.model.setVisible(cells[i], show);
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Group: Folding
- */
- /**
- * Function: foldCells
- *
- * Sets the collapsed state of the specified cells and all descendants
- * if recurse is true. The change is carried out using <cellsFolded>.
- * This method fires <mxEvent.FOLD_CELLS> while the transaction is in
- * progress. Returns the cells whose collapsed state was changed.
- *
- * Parameters:
- *
- * collapsed - Boolean indicating the collapsed state to be assigned.
- * recurse - Optional boolean indicating if the collapsed state of all
- * descendants should be set. Default is false.
- * cells - Array of <mxCells> whose collapsed state should be set. If
- * null is specified then the foldable selection cells are used.
- * checkFoldable - Optional boolean indicating of isCellFoldable should be
- * checked. Default is false.
- * evt - Optional native event that triggered the invocation.
- */
- mxGraph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt)
- {
- recurse = (recurse != null) ? recurse : false;
-
- if (cells == null)
- {
- cells = this.getFoldableCells(this.getSelectionCells(), collapse);
- }
- this.stopEditing(false);
- this.model.beginUpdate();
- try
- {
- this.cellsFolded(cells, collapse, recurse, checkFoldable);
- this.fireEvent(new mxEventObject(mxEvent.FOLD_CELLS,
- 'collapse', collapse, 'recurse', recurse, 'cells', cells));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: cellsFolded
- *
- * Sets the collapsed state of the specified cells. This method fires
- * <mxEvent.CELLS_FOLDED> while the transaction is in progress. Returns the
- * cells whose collapsed state was changed.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose collapsed state should be set.
- * collapsed - Boolean indicating the collapsed state to be assigned.
- * recurse - Boolean indicating if the collapsed state of all descendants
- * should be set.
- * checkFoldable - Optional boolean indicating of isCellFoldable should be
- * checked. Default is false.
- */
- mxGraph.prototype.cellsFolded = function(cells, collapse, recurse, checkFoldable)
- {
- if (cells != null && cells.length > 0)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- if ((!checkFoldable || this.isCellFoldable(cells[i], collapse)) &&
- collapse != this.isCellCollapsed(cells[i]))
- {
- this.model.setCollapsed(cells[i], collapse);
- this.swapBounds(cells[i], collapse);
- if (this.isExtendParent(cells[i]))
- {
- this.extendParent(cells[i]);
- }
- if (recurse)
- {
- var children = this.model.getChildren(cells[i]);
- this.cellsFolded(children, collapse, recurse);
- }
-
- this.constrainChild(cells[i]);
- }
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED,
- 'cells', cells, 'collapse', collapse, 'recurse', recurse));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: swapBounds
- *
- * Swaps the alternate and the actual bounds in the geometry of the given
- * cell invoking <updateAlternateBounds> before carrying out the swap.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the bounds should be swapped.
- * willCollapse - Boolean indicating if the cell is going to be collapsed.
- */
- mxGraph.prototype.swapBounds = function(cell, willCollapse)
- {
- if (cell != null)
- {
- var geo = this.model.getGeometry(cell);
-
- if (geo != null)
- {
- geo = geo.clone();
-
- this.updateAlternateBounds(cell, geo, willCollapse);
- geo.swap();
-
- this.model.setGeometry(cell, geo);
- }
- }
- };
- /**
- * Function: updateAlternateBounds
- *
- * Updates or sets the alternate bounds in the given geometry for the given
- * cell depending on whether the cell is going to be collapsed. If no
- * alternate bounds are defined in the geometry and
- * <collapseToPreferredSize> is true, then the preferred size is used for
- * the alternate bounds. The top, left corner is always kept at the same
- * location.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the geometry is being udpated.
- * g - <mxGeometry> for which the alternate bounds should be updated.
- * willCollapse - Boolean indicating if the cell is going to be collapsed.
- */
- mxGraph.prototype.updateAlternateBounds = function(cell, geo, willCollapse)
- {
- if (cell != null && geo != null)
- {
- var style = this.getCurrentCellStyle(cell);
- if (geo.alternateBounds == null)
- {
- var bounds = geo;
-
- if (this.collapseToPreferredSize)
- {
- var tmp = this.getPreferredSizeForCell(cell);
-
- if (tmp != null)
- {
- bounds = tmp;
- var startSize = mxUtils.getValue(style, mxConstants.STYLE_STARTSIZE);
- if (startSize > 0)
- {
- bounds.height = Math.max(bounds.height, startSize);
- }
- }
- }
-
- geo.alternateBounds = new mxRectangle(0, 0, bounds.width, bounds.height);
- }
-
- if (geo.alternateBounds != null)
- {
- geo.alternateBounds.x = geo.x;
- geo.alternateBounds.y = geo.y;
-
- var alpha = mxUtils.toRadians(style[mxConstants.STYLE_ROTATION] || 0);
-
- if (alpha != 0)
- {
- var dx = geo.alternateBounds.getCenterX() - geo.getCenterX();
- var dy = geo.alternateBounds.getCenterY() - geo.getCenterY();
-
- var cos = Math.cos(alpha);
- var sin = Math.sin(alpha);
-
- var dx2 = cos * dx - sin * dy;
- var dy2 = sin * dx + cos * dy;
-
- geo.alternateBounds.x += dx2 - dx;
- geo.alternateBounds.y += dy2 - dy;
- }
- }
- }
- };
- /**
- * Function: addAllEdges
- *
- * Returns an array with the given cells and all edges that are connected
- * to a cell or one of its descendants.
- */
- mxGraph.prototype.addAllEdges = function(cells)
- {
- var allCells = cells.slice();
-
- return mxUtils.removeDuplicates(allCells.concat(this.getAllEdges(cells)));
- };
- /**
- * Function: getAllEdges
- *
- * Returns all edges connected to the given cells or its descendants.
- */
- mxGraph.prototype.getAllEdges = function(cells)
- {
- var edges = [];
-
- if (cells != null)
- {
- for (var i = 0; i < cells.length; i++)
- {
- var edgeCount = this.model.getEdgeCount(cells[i]);
-
- for (var j = 0; j < edgeCount; j++)
- {
- edges.push(this.model.getEdgeAt(cells[i], j));
- }
- // Recurses
- var children = this.model.getChildren(cells[i]);
- edges = edges.concat(this.getAllEdges(children));
- }
- }
-
- return edges;
- };
- /**
- * Group: Cell sizing
- */
- /**
- * Function: updateCellSize
- *
- * Updates the size of the given cell in the model using <cellSizeUpdated>.
- * This method fires <mxEvent.UPDATE_CELL_SIZE> while the transaction is in
- * progress. Returns the cell whose size was updated.
- *
- * Parameters:
- *
- * cell - <mxCell> whose size should be updated.
- */
- mxGraph.prototype.updateCellSize = function(cell, ignoreChildren)
- {
- ignoreChildren = (ignoreChildren != null) ? ignoreChildren : false;
-
- this.model.beginUpdate();
- try
- {
- this.cellSizeUpdated(cell, ignoreChildren);
- this.fireEvent(new mxEventObject(mxEvent.UPDATE_CELL_SIZE,
- 'cell', cell, 'ignoreChildren', ignoreChildren));
- }
- finally
- {
- this.model.endUpdate();
- }
-
- return cell;
- };
- /**
- * Function: cellSizeUpdated
- *
- * Updates the size of the given cell in the model using
- * <getPreferredSizeForCell> to get the new size.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the size should be changed.
- */
- mxGraph.prototype.cellSizeUpdated = function(cell, ignoreChildren)
- {
- if (cell != null)
- {
- this.model.beginUpdate();
- try
- {
- var size = this.getPreferredSizeForCell(cell);
- var geo = this.model.getGeometry(cell);
-
- if (size != null && geo != null)
- {
- var collapsed = this.isCellCollapsed(cell);
- geo = geo.clone();
- if (this.isSwimlane(cell))
- {
- var style = this.getCellStyle(cell);
- var cellStyle = this.model.getStyle(cell);
- if (cellStyle == null)
- {
- cellStyle = '';
- }
- if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
- {
- cellStyle = mxUtils.setStyle(cellStyle,
- mxConstants.STYLE_STARTSIZE, size.height + 8);
- if (collapsed)
- {
- geo.height = size.height + 8;
- }
- geo.width = size.width;
- }
- else
- {
- cellStyle = mxUtils.setStyle(cellStyle,
- mxConstants.STYLE_STARTSIZE, size.width + 8);
- if (collapsed)
- {
- geo.width = size.width + 8;
- }
- geo.height = size.height;
- }
- this.model.setStyle(cell, cellStyle);
- }
- else
- {
- var state = this.view.createState(cell);
- var align = (state.style[mxConstants.STYLE_ALIGN] || mxConstants.ALIGN_CENTER);
-
- if (align == mxConstants.ALIGN_RIGHT)
- {
- geo.x += geo.width - size.width;
- }
- else if (align == mxConstants.ALIGN_CENTER)
- {
- geo.x += Math.round((geo.width - size.width) / 2);
- }
- var valign = this.getVerticalAlign(state);
-
- if (valign == mxConstants.ALIGN_BOTTOM)
- {
- geo.y += geo.height - size.height;
- }
- else if (valign == mxConstants.ALIGN_MIDDLE)
- {
- geo.y += Math.round((geo.height - size.height) / 2);
- }
- geo.width = size.width;
- geo.height = size.height;
- }
- if (!ignoreChildren && !collapsed)
- {
- var bounds = this.view.getBounds(this.model.getChildren(cell));
- if (bounds != null)
- {
- var tr = this.view.translate;
- var scale = this.view.scale;
- var width = (bounds.x + bounds.width) / scale - geo.x - tr.x;
- var height = (bounds.y + bounds.height) / scale - geo.y - tr.y;
- geo.width = Math.max(geo.width, width);
- geo.height = Math.max(geo.height, height);
- }
- }
- this.cellsResized([cell], [geo], false);
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: getPreferredSizeForCell
- *
- * Returns the preferred width and height of the given <mxCell> as an
- * <mxRectangle>. To implement a minimum width, add a new style eg.
- * minWidth in the vertex and override this method as follows.
- *
- * (code)
- * var graphGetPreferredSizeForCell = graph.getPreferredSizeForCell;
- * graph.getPreferredSizeForCell = function(cell)
- * {
- * var result = graphGetPreferredSizeForCell.apply(this, arguments);
- * var style = this.getCellStyle(cell);
- *
- * if (style['minWidth'] > 0)
- * {
- * result.width = Math.max(style['minWidth'], result.width);
- * }
- *
- * return result;
- * };
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> for which the preferred size should be returned.
- * textWidth - Optional maximum text width for word wrapping.
- */
- mxGraph.prototype.getPreferredSizeForCell = function(cell, textWidth)
- {
- var result = null;
-
- if (cell != null)
- {
- var state = this.view.createState(cell);
- var style = state.style;
- if (!this.model.isEdge(cell))
- {
- var fontSize = style[mxConstants.STYLE_FONTSIZE] || mxConstants.DEFAULT_FONTSIZE;
- var dx = 0;
- var dy = 0;
-
- // Adds dimension of image if shape is a label
- if (this.getImage(state) != null || style[mxConstants.STYLE_IMAGE] != null)
- {
- if (style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_LABEL)
- {
- if (style[mxConstants.STYLE_VERTICAL_ALIGN] == mxConstants.ALIGN_MIDDLE)
- {
- dx += parseFloat(style[mxConstants.STYLE_IMAGE_WIDTH]) || mxLabel.prototype.imageSize;
- }
-
- if (style[mxConstants.STYLE_ALIGN] != mxConstants.ALIGN_CENTER)
- {
- dy += parseFloat(style[mxConstants.STYLE_IMAGE_HEIGHT]) || mxLabel.prototype.imageSize;
- }
- }
- }
- // Adds spacings
- dx += 2 * (style[mxConstants.STYLE_SPACING] || 0);
- dx += style[mxConstants.STYLE_SPACING_LEFT] || 0;
- dx += style[mxConstants.STYLE_SPACING_RIGHT] || 0;
- dy += 2 * (style[mxConstants.STYLE_SPACING] || 0);
- dy += style[mxConstants.STYLE_SPACING_TOP] || 0;
- dy += style[mxConstants.STYLE_SPACING_BOTTOM] || 0;
-
- // Add spacing for collapse/expand icon
- // LATER: Check alignment and use constants
- // for image spacing
- var image = this.getFoldingImage(state);
-
- if (image != null)
- {
- dx += image.width + 8;
- }
- // Adds space for label
- var value = this.cellRenderer.getLabelValue(state);
- if (value != null && value.length > 0)
- {
- if (!this.isHtmlLabel(state.cell))
- {
- value = mxUtils.htmlEntities(value, false);
- }
-
- value = value.replace(/\n/g, '<br>');
-
- var size = mxUtils.getSizeForString(value, fontSize,
- style[mxConstants.STYLE_FONTFAMILY], textWidth,
- style[mxConstants.STYLE_FONTSTYLE]);
- var width = size.width + dx;
- var height = size.height + dy;
-
- if (!mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
- {
- var tmp = height;
-
- height = width;
- width = tmp;
- }
-
- if (this.gridEnabled)
- {
- width = this.snap(width + this.gridSize / 2);
- height = this.snap(height + this.gridSize / 2);
- }
- result = new mxRectangle(0, 0, width, height);
- }
- else
- {
- var gs2 = 4 * this.gridSize;
- result = new mxRectangle(0, 0, gs2, gs2);
- }
- }
- }
-
- return result;
- };
- /**
- * Function: resizeCell
- *
- * Sets the bounds of the given cell using <resizeCells>. Returns the
- * cell which was passed to the function.
- *
- * Parameters:
- *
- * cell - <mxCell> whose bounds should be changed.
- * bounds - <mxRectangle> that represents the new bounds.
- */
- mxGraph.prototype.resizeCell = function(cell, bounds, recurse)
- {
- return this.resizeCells([cell], [bounds], recurse)[0];
- };
- /**
- * Function: resizeCells
- *
- * Sets the bounds of the given cells and fires a <mxEvent.RESIZE_CELLS>
- * event while the transaction is in progress. Returns the cells which
- * have been passed to the function.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose bounds should be changed.
- * bounds - Array of <mxRectangles> that represent the new bounds.
- */
- mxGraph.prototype.resizeCells = function(cells, bounds, recurse)
- {
- recurse = (recurse != null) ? recurse : this.isRecursiveResize();
-
- this.model.beginUpdate();
- try
- {
- var prev = this.cellsResized(cells, bounds, recurse);
- this.fireEvent(new mxEventObject(mxEvent.RESIZE_CELLS,
- 'cells', cells, 'bounds', bounds, 'previous', prev));
- }
- finally
- {
- this.model.endUpdate();
- }
- return cells;
- };
- /**
- * Function: cellsResized
- *
- * Sets the bounds of the given cells and fires a <mxEvent.CELLS_RESIZED>
- * event. If <extendParents> is true, then the parent is extended if a
- * child size is changed so that it overlaps with the parent.
- *
- * The following example shows how to control group resizes to make sure
- * that all child cells stay within the group.
- *
- * (code)
- * graph.addListener(mxEvent.CELLS_RESIZED, function(sender, evt)
- * {
- * var cells = evt.getProperty('cells');
- *
- * if (cells != null)
- * {
- * for (var i = 0; i < cells.length; i++)
- * {
- * if (graph.getModel().getChildCount(cells[i]) > 0)
- * {
- * var geo = graph.getCellGeometry(cells[i]);
- *
- * if (geo != null)
- * {
- * var children = graph.getChildCells(cells[i], true, true);
- * var bounds = graph.getBoundingBoxFromGeometry(children, true);
- *
- * geo = geo.clone();
- * geo.width = Math.max(geo.width, bounds.width);
- * geo.height = Math.max(geo.height, bounds.height);
- *
- * graph.getModel().setGeometry(cells[i], geo);
- * }
- * }
- * }
- * }
- * });
- * (end)
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose bounds should be changed.
- * bounds - Array of <mxRectangles> that represent the new bounds.
- * recurse - Optional boolean that specifies if the children should be resized.
- */
- mxGraph.prototype.cellsResized = function(cells, bounds, recurse)
- {
- recurse = (recurse != null) ? recurse : false;
- var prev = [];
- if (cells != null && bounds != null && cells.length == bounds.length)
- {
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- prev.push(this.cellResized(cells[i], bounds[i], false, recurse));
- if (this.isExtendParent(cells[i]))
- {
- this.extendParent(cells[i]);
- }
-
- this.constrainChild(cells[i]);
- }
- if (this.resetEdgesOnResize)
- {
- this.resetEdges(cells);
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_RESIZED,
- 'cells', cells, 'bounds', bounds, 'previous', prev));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
-
- return prev;
- };
- /**
- * Function: cellResized
- *
- * Resizes the parents recursively so that they contain the complete area
- * of the resized child cell.
- *
- * Parameters:
- *
- * cell - <mxCell> whose bounds should be changed.
- * bounds - <mxRectangles> that represent the new bounds.
- * ignoreRelative - Boolean that indicates if relative cells should be ignored.
- * recurse - Optional boolean that specifies if the children should be resized.
- */
- mxGraph.prototype.cellResized = function(cell, bounds, ignoreRelative, recurse)
- {
- var prev = this.model.getGeometry(cell);
- if (prev != null && (prev.x != bounds.x || prev.y != bounds.y ||
- prev.width != bounds.width || prev.height != bounds.height))
- {
- var geo = prev.clone();
- if (!ignoreRelative && geo.relative)
- {
- var offset = geo.offset;
- if (offset != null)
- {
- offset.x += bounds.x - geo.x;
- offset.y += bounds.y - geo.y;
- }
- }
- else
- {
- geo.x = bounds.x;
- geo.y = bounds.y;
- }
- geo.width = bounds.width;
- geo.height = bounds.height;
- if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
- {
- geo.x = Math.max(0, geo.x);
- geo.y = Math.max(0, geo.y);
- }
- this.model.beginUpdate();
- try
- {
- if (recurse)
- {
- this.resizeChildCells(cell, geo);
- }
-
- this.model.setGeometry(cell, geo);
- this.constrainChildCells(cell);
- }
- finally
- {
- this.model.endUpdate();
- }
- }
-
- return prev;
- };
- /**
- * Function: resizeChildCells
- *
- * Resizes the child cells of the given cell for the given new geometry with
- * respect to the current geometry of the cell.
- *
- * Parameters:
- *
- * cell - <mxCell> that has been resized.
- * newGeo - <mxGeometry> that represents the new bounds.
- */
- mxGraph.prototype.resizeChildCells = function(cell, newGeo)
- {
- var geo = this.model.getGeometry(cell);
- var dx = (geo.width != 0) ? newGeo.width / geo.width : 1;
- var dy = (geo.height != 0) ? newGeo.height / geo.height : 1;
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- this.scaleCell(this.model.getChildAt(cell, i), dx, dy, true);
- }
- };
- /**
- * Function: constrainChildCells
- *
- * Constrains the children of the given cell using <constrainChild>.
- *
- * Parameters:
- *
- * cell - <mxCell> that has been resized.
- */
- mxGraph.prototype.constrainChildCells = function(cell)
- {
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- this.constrainChild(this.model.getChildAt(cell, i));
- }
- };
- /**
- * Function: scaleCell
- *
- * Scales the points, position and size of the given cell according to the
- * given vertical and horizontal scaling factors.
- *
- * Parameters:
- *
- * cell - <mxCell> whose geometry should be scaled.
- * dx - Horizontal scaling factor.
- * dy - Vertical scaling factor.
- * recurse - Boolean indicating if the child cells should be scaled.
- */
- mxGraph.prototype.scaleCell = function(cell, dx, dy, recurse)
- {
- var geo = this.model.getGeometry(cell);
-
- if (geo != null)
- {
- var style = this.getCurrentCellStyle(cell);
- geo = geo.clone();
-
- // Stores values for restoring based on style
- var x = geo.x;
- var y = geo.y
- var w = geo.width;
- var h = geo.height;
-
- geo.scale(dx, dy, style[mxConstants.STYLE_ASPECT] == 'fixed');
-
- if (style[mxConstants.STYLE_RESIZE_WIDTH] == '1')
- {
- geo.width = w * dx;
- }
- else if (style[mxConstants.STYLE_RESIZE_WIDTH] == '0')
- {
- geo.width = w;
- }
-
- if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '1')
- {
- geo.height = h * dy;
- }
- else if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '0')
- {
- geo.height = h;
- }
-
- if (!this.isCellMovable(cell))
- {
- geo.x = x;
- geo.y = y;
- }
-
- if (!this.isCellResizable(cell))
- {
- geo.width = w;
- geo.height = h;
- }
- if (this.model.isVertex(cell))
- {
- this.cellResized(cell, geo, true, recurse);
- }
- else
- {
- this.model.setGeometry(cell, geo);
- }
- }
- };
- /**
- * Function: extendParent
- *
- * Resizes the parents recursively so that they contain the complete area
- * of the resized child cell.
- *
- * Parameters:
- *
- * cell - <mxCell> that has been resized.
- */
- mxGraph.prototype.extendParent = function(cell)
- {
- if (cell != null)
- {
- var parent = this.model.getParent(cell);
- var p = this.getCellGeometry(parent);
-
- if (parent != null && p != null && !this.isCellCollapsed(parent))
- {
- var geo = this.getCellGeometry(cell);
-
- if (geo != null && !geo.relative &&
- (p.width < geo.x + geo.width ||
- p.height < geo.y + geo.height))
- {
- p = p.clone();
-
- p.width = Math.max(p.width, geo.x + geo.width);
- p.height = Math.max(p.height, geo.y + geo.height);
-
- this.cellsResized([parent], [p], false);
- }
- }
- }
- };
- /**
- * Group: Cell moving
- */
- /**
- * Function: importCells
- *
- * Clones and inserts the given cells into the graph using the move
- * method and returns the inserted cells. This shortcut is used if
- * cells are inserted via datatransfer.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be imported.
- * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
- * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
- * target - <mxCell> that represents the new parent of the cells.
- * evt - Mouseevent that triggered the invocation.
- * mapping - Optional mapping for existing clones.
- */
- mxGraph.prototype.importCells = function(cells, dx, dy, target, evt, mapping)
- {
- return this.moveCells(cells, dx, dy, true, target, evt, mapping);
- };
- /**
- * Function: moveCells
- *
- * Moves or clones the specified cells and moves the cells or clones by the
- * given amount, adding them to the optional target cell. The evt is the
- * mouse event as the mouse was released. The change is carried out using
- * <cellsMoved>. This method fires <mxEvent.MOVE_CELLS> while the
- * transaction is in progress. Returns the cells that were moved.
- *
- * Use the following code to move all cells in the graph.
- *
- * (code)
- * graph.moveCells(graph.getChildCells(null, true, true), 10, 10);
- * (end)
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be moved, cloned or added to the target.
- * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
- * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
- * clone - Boolean indicating if the cells should be cloned. Default is false.
- * target - <mxCell> that represents the new parent of the cells.
- * evt - Mouseevent that triggered the invocation.
- * mapping - Optional mapping for existing clones.
- */
- mxGraph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
- {
- dx = (dx != null) ? dx : 0;
- dy = (dy != null) ? dy : 0;
- clone = (clone != null) ? clone : false;
-
- if (cells != null && (dx != 0 || dy != 0 || clone || target != null))
- {
- // Removes descendants with ancestors in cells to avoid multiple moving
- cells = this.model.getTopmostCells(cells);
- var origCells = cells;
-
- this.model.beginUpdate();
- try
- {
- // Faster cell lookups to remove relative edge labels with selected
- // terminals to avoid explicit and implicit move at same time
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- var isSelected = mxUtils.bind(this, function(cell)
- {
- while (cell != null)
- {
- if (dict.get(cell))
- {
- return true;
- }
-
- cell = this.model.getParent(cell);
- }
-
- return false;
- });
-
- // Removes relative edge labels with selected terminals
- var checked = [];
-
- for (var i = 0; i < cells.length; i++)
- {
- var geo = this.getCellGeometry(cells[i]);
- var parent = this.model.getParent(cells[i]);
-
- if ((geo == null || !geo.relative) || !this.model.isEdge(parent) ||
- (!isSelected(this.model.getTerminal(parent, true)) &&
- !isSelected(this.model.getTerminal(parent, false))))
- {
- checked.push(cells[i]);
- }
- }
- cells = checked;
-
- if (clone)
- {
- cells = this.cloneCells(cells, this.isCloneInvalidEdges(), mapping);
- if (target == null)
- {
- target = this.getDefaultParent();
- }
- }
- // FIXME: Cells should always be inserted first before any other edit
- // to avoid forward references in sessions.
- // Need to disable allowNegativeCoordinates if target not null to
- // allow for temporary negative numbers until cellsAdded is called.
- var previous = this.isAllowNegativeCoordinates();
-
- if (target != null)
- {
- this.setAllowNegativeCoordinates(true);
- }
-
- this.cellsMoved(cells, dx, dy, !clone && this.isDisconnectOnMove()
- && this.isAllowDanglingEdges(), target == null,
- this.isExtendParentsOnMove() && target == null);
-
- this.setAllowNegativeCoordinates(previous);
- if (target != null)
- {
- var index = this.model.getChildCount(target);
- this.cellsAdded(cells, target, index, null, null, true);
-
- // Restores parent edge on cloned edge labels
- if (clone)
- {
- for (var i = 0; i < cells.length; i++)
- {
- var geo = this.getCellGeometry(cells[i]);
- var parent = this.model.getParent(origCells[i]);
-
- if (geo != null && geo.relative &&
- this.model.isEdge(parent) &&
- this.model.contains(parent))
- {
- this.model.add(parent, cells[i]);
- }
- }
- }
- }
- // Dispatches a move event
- this.fireEvent(new mxEventObject(mxEvent.MOVE_CELLS, 'cells', cells,
- 'dx', dx, 'dy', dy, 'clone', clone, 'target', target, 'event', evt));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- return cells;
- };
- /**
- * Function: cellsMoved
- *
- * Moves the specified cells by the given vector, disconnecting the cells
- * using disconnectGraph is disconnect is true. This method fires
- * <mxEvent.CELLS_MOVED> while the transaction is in progress.
- */
- mxGraph.prototype.cellsMoved = function(cells, dx, dy, disconnect, constrain, extend)
- {
- if (cells != null && (dx != 0 || dy != 0))
- {
- extend = (extend != null) ? extend : false;
- this.model.beginUpdate();
- try
- {
- if (disconnect)
- {
- this.disconnectGraph(cells);
- }
- for (var i = 0; i < cells.length; i++)
- {
- this.translateCell(cells[i], dx, dy);
-
- if (extend && this.isExtendParent(cells[i]))
- {
- this.extendParent(cells[i]);
- }
- else if (constrain)
- {
- this.constrainChild(cells[i]);
- }
- }
- if (this.resetEdgesOnMove)
- {
- this.resetEdges(cells);
- }
-
- this.fireEvent(new mxEventObject(mxEvent.CELLS_MOVED,
- 'cells', cells, 'dx', dx, 'dy', dy, 'disconnect', disconnect));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: translateCell
- *
- * Translates the geometry of the given cell and stores the new,
- * translated geometry in the model as an atomic change.
- */
- mxGraph.prototype.translateCell = function(cell, dx, dy)
- {
- var geo = this.model.getGeometry(cell);
- if (geo != null)
- {
- dx = parseFloat(dx);
- dy = parseFloat(dy);
- geo = geo.clone();
- geo.translate(dx, dy);
- if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
- {
- geo.x = Math.max(0, parseFloat(geo.x));
- geo.y = Math.max(0, parseFloat(geo.y));
- }
-
- if (geo.relative && !this.model.isEdge(cell))
- {
- var parent = this.model.getParent(cell);
- var angle = 0;
-
- if (this.model.isVertex(parent))
- {
- var style = this.getCurrentCellStyle(parent);
- angle = mxUtils.getValue(style, mxConstants.STYLE_ROTATION, 0);
- }
-
- if (angle != 0)
- {
- var rad = mxUtils.toRadians(-angle);
- var cos = Math.cos(rad);
- var sin = Math.sin(rad);
- var pt = mxUtils.getRotatedPoint(new mxPoint(dx, dy), cos, sin, new mxPoint(0, 0));
- dx = pt.x;
- dy = pt.y;
- }
-
- if (geo.offset == null)
- {
- geo.offset = new mxPoint(dx, dy);
- }
- else
- {
- geo.offset.x = parseFloat(geo.offset.x) + dx;
- geo.offset.y = parseFloat(geo.offset.y) + dy;
- }
- }
- this.model.setGeometry(cell, geo);
- }
- };
- /**
- * Function: getCellContainmentArea
- *
- * Returns the <mxRectangle> inside which a cell is to be kept.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the area should be returned.
- */
- mxGraph.prototype.getCellContainmentArea = function(cell)
- {
- if (cell != null && !this.model.isEdge(cell))
- {
- var parent = this.model.getParent(cell);
-
- if (parent != null && parent != this.getDefaultParent())
- {
- var g = this.model.getGeometry(parent);
-
- if (g != null)
- {
- var x = 0;
- var y = 0;
- var w = g.width;
- var h = g.height;
-
- if (this.isSwimlane(parent))
- {
- var size = this.getStartSize(parent);
- var style = this.getCurrentCellStyle(parent);
- var dir = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
- var flipH = mxUtils.getValue(style, mxConstants.STYLE_FLIPH, 0) == 1;
- var flipV = mxUtils.getValue(style, mxConstants.STYLE_FLIPV, 0) == 1;
-
- if (dir == mxConstants.DIRECTION_SOUTH || dir == mxConstants.DIRECTION_NORTH)
- {
- var tmp = size.width;
- size.width = size.height;
- size.height = tmp;
- }
-
- if ((dir == mxConstants.DIRECTION_EAST && !flipV) || (dir == mxConstants.DIRECTION_NORTH && !flipH) ||
- (dir == mxConstants.DIRECTION_WEST && flipV) || (dir == mxConstants.DIRECTION_SOUTH && flipH))
- {
- x = size.width;
- y = size.height;
- }
- w -= size.width;
- h -= size.height;
- }
-
- return new mxRectangle(x, y, w, h);
- }
- }
- }
-
- return null;
- };
- /**
- * Function: getMaximumGraphBounds
- *
- * Returns the bounds inside which the diagram should be kept as an
- * <mxRectangle>.
- */
- mxGraph.prototype.getMaximumGraphBounds = function()
- {
- return this.maximumGraphBounds;
- };
- /**
- * Function: constrainChild
- *
- * Keeps the given cell inside the bounds returned by
- * <getCellContainmentArea> for its parent, according to the rules defined by
- * <getOverlap> and <isConstrainChild>. This modifies the cell's geometry
- * in-place and does not clone it.
- *
- * Parameters:
- *
- * cells - <mxCell> which should be constrained.
- * sizeFirst - Specifies if the size should be changed first. Default is true.
- */
- mxGraph.prototype.constrainChild = function(cell, sizeFirst)
- {
- sizeFirst = (sizeFirst != null) ? sizeFirst : true;
-
- if (cell != null)
- {
- var geo = this.getCellGeometry(cell);
-
- if (geo != null && (this.isConstrainRelativeChildren() || !geo.relative))
- {
- var parent = this.model.getParent(cell);
- var pgeo = this.getCellGeometry(parent);
- var max = this.getMaximumGraphBounds();
-
- // Finds parent offset
- if (max != null)
- {
- var off = this.getBoundingBoxFromGeometry([parent], false);
-
- if (off != null)
- {
- max = mxRectangle.fromRectangle(max);
-
- max.x -= off.x;
- max.y -= off.y;
- }
- }
-
- if (this.isConstrainChild(cell))
- {
- var tmp = this.getCellContainmentArea(cell);
-
- if (tmp != null)
- {
- var overlap = this.getOverlap(cell);
-
- if (overlap > 0)
- {
- tmp = mxRectangle.fromRectangle(tmp);
-
- tmp.x -= tmp.width * overlap;
- tmp.y -= tmp.height * overlap;
- tmp.width += 2 * tmp.width * overlap;
- tmp.height += 2 * tmp.height * overlap;
- }
-
- // Find the intersection between max and tmp
- if (max == null)
- {
- max = tmp;
- }
- else
- {
- max = mxRectangle.fromRectangle(max);
- max.intersect(tmp);
- }
- }
- }
-
- if (max != null)
- {
- var cells = [cell];
-
- if (!this.isCellCollapsed(cell))
- {
- var desc = this.model.getDescendants(cell);
-
- for (var i = 0; i < desc.length; i++)
- {
- if (this.isCellVisible(desc[i]))
- {
- cells.push(desc[i]);
- }
- }
- }
-
- var bbox = this.getBoundingBoxFromGeometry(cells, false);
-
- if (bbox != null)
- {
- geo = geo.clone();
-
- // Cumulative horizontal movement
- var dx = 0;
-
- if (geo.width > max.width)
- {
- dx = geo.width - max.width;
- geo.width -= dx;
- }
-
- if (bbox.x + bbox.width > max.x + max.width)
- {
- dx -= bbox.x + bbox.width - max.x - max.width - dx;
- }
-
- // Cumulative vertical movement
- var dy = 0;
-
- if (geo.height > max.height)
- {
- dy = geo.height - max.height;
- geo.height -= dy;
- }
-
- if (bbox.y + bbox.height > max.y + max.height)
- {
- dy -= bbox.y + bbox.height - max.y - max.height - dy;
- }
-
- if (bbox.x < max.x)
- {
- dx -= bbox.x - max.x;
- }
-
- if (bbox.y < max.y)
- {
- dy -= bbox.y - max.y;
- }
-
- if (dx != 0 || dy != 0)
- {
- if (geo.relative)
- {
- // Relative geometries are moved via absolute offset
- if (geo.offset == null)
- {
- geo.offset = new mxPoint();
- }
-
- geo.offset.x += dx;
- geo.offset.y += dy;
- }
- else
- {
- geo.x += dx;
- geo.y += dy;
- }
- }
-
- this.model.setGeometry(cell, geo);
- }
- }
- }
- }
- };
- /**
- * Function: resetEdges
- *
- * Resets the control points of the edges that are connected to the given
- * cells if not both ends of the edge are in the given cells array.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> for which the connected edges should be
- * reset.
- */
- mxGraph.prototype.resetEdges = function(cells)
- {
- if (cells != null)
- {
- // Prepares faster cells lookup
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- this.model.beginUpdate();
- try
- {
- for (var i = 0; i < cells.length; i++)
- {
- var edges = this.model.getEdges(cells[i]);
-
- if (edges != null)
- {
- for (var j = 0; j < edges.length; j++)
- {
- var state = this.view.getState(edges[j]);
-
- var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[j], true);
- var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[j], false);
-
- // Checks if one of the terminals is not in the given array
- if (!dict.get(source) || !dict.get(target))
- {
- this.resetEdge(edges[j]);
- }
- }
- }
-
- this.resetEdges(this.model.getChildren(cells[i]));
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: resetEdge
- *
- * Resets the control points of the given edge.
- *
- * Parameters:
- *
- * edge - <mxCell> whose points should be reset.
- */
- mxGraph.prototype.resetEdge = function(edge)
- {
- var geo = this.model.getGeometry(edge);
-
- // Resets the control points
- if (geo != null && geo.points != null && geo.points.length > 0)
- {
- geo = geo.clone();
- geo.points = [];
- this.model.setGeometry(edge, geo);
- }
-
- return edge;
- };
- /**
- * Group: Cell connecting and connection constraints
- */
- /**
- * Function: getOutlineConstraint
- *
- * Returns the constraint used to connect to the outline of the given state.
- */
- mxGraph.prototype.getOutlineConstraint = function(point, terminalState, me)
- {
- if (terminalState.shape != null)
- {
- var bounds = this.view.getPerimeterBounds(terminalState);
- var direction = terminalState.style[mxConstants.STYLE_DIRECTION];
-
- if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
- {
- bounds.x += bounds.width / 2 - bounds.height / 2;
- bounds.y += bounds.height / 2 - bounds.width / 2;
- var tmp = bounds.width;
- bounds.width = bounds.height;
- bounds.height = tmp;
- }
-
- var alpha = mxUtils.toRadians(terminalState.shape.getShapeRotation());
-
- if (alpha != 0)
- {
- var cos = Math.cos(-alpha);
- var sin = Math.sin(-alpha);
-
- var ct = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
- point = mxUtils.getRotatedPoint(point, cos, sin, ct);
- }
- var sx = 1;
- var sy = 1;
- var dx = 0;
- var dy = 0;
-
- // LATER: Add flipping support for image shapes
- if (this.getModel().isVertex(terminalState.cell))
- {
- var flipH = terminalState.style[mxConstants.STYLE_FLIPH];
- var flipV = terminalState.style[mxConstants.STYLE_FLIPV];
-
- // Legacy support for stencilFlipH/V
- if (terminalState.shape != null && terminalState.shape.stencil != null)
- {
- flipH = mxUtils.getValue(terminalState.style, 'stencilFlipH', 0) == 1 || flipH;
- flipV = mxUtils.getValue(terminalState.style, 'stencilFlipV', 0) == 1 || flipV;
- }
-
- if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
- {
- var tmp = flipH;
- flipH = flipV;
- flipV = tmp;
- }
-
- if (flipH)
- {
- sx = -1;
- dx = -bounds.width;
- }
-
- if (flipV)
- {
- sy = -1;
- dy = -bounds.height ;
- }
- }
-
- point = new mxPoint((point.x - bounds.x) * sx - dx + bounds.x, (point.y - bounds.y) * sy - dy + bounds.y);
-
- var x = (bounds.width == 0) ? 0 : Math.round((point.x - bounds.x) * 1000 / bounds.width) / 1000;
- var y = (bounds.height == 0) ? 0 : Math.round((point.y - bounds.y) * 1000 / bounds.height) / 1000;
-
- return new mxConnectionConstraint(new mxPoint(x, y), false);
- }
-
- return null;
- };
- /**
- * Function: getAllConnectionConstraints
- *
- * Returns an array of all <mxConnectionConstraints> for the given terminal. If
- * the shape of the given terminal is a <mxStencilShape> then the constraints
- * of the corresponding <mxStencil> are returned.
- *
- * Parameters:
- *
- * terminal - <mxCellState> that represents the terminal.
- * source - Boolean that specifies if the terminal is the source or target.
- */
- mxGraph.prototype.getAllConnectionConstraints = function(terminal, source)
- {
- if (terminal != null && terminal.shape != null && terminal.shape.stencil != null)
- {
- return terminal.shape.stencil.constraints;
- }
- return null;
- };
- /**
- * Function: getConnectionConstraint
- *
- * Returns an <mxConnectionConstraint> that describes the given connection
- * point. This result can then be passed to <getConnectionPoint>.
- *
- * Parameters:
- *
- * edge - <mxCellState> that represents the edge.
- * terminal - <mxCellState> that represents the terminal.
- * source - Boolean indicating if the terminal is the source or target.
- */
- mxGraph.prototype.getConnectionConstraint = function(edge, terminal, source)
- {
- var point = null;
- var x = edge.style[(source) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
- if (x != null)
- {
- var y = edge.style[(source) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
-
- if (y != null)
- {
- point = new mxPoint(parseFloat(x), parseFloat(y));
- }
- }
-
- var perimeter = false;
- var dx = 0, dy = 0;
-
- if (point != null)
- {
- perimeter = mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_EXIT_PERIMETER :
- mxConstants.STYLE_ENTRY_PERIMETER, true);
- //Add entry/exit offset
- dx = parseFloat(edge.style[(source) ? mxConstants.STYLE_EXIT_DX : mxConstants.STYLE_ENTRY_DX]);
- dy = parseFloat(edge.style[(source) ? mxConstants.STYLE_EXIT_DY : mxConstants.STYLE_ENTRY_DY]);
-
- dx = isFinite(dx)? dx : 0;
- dy = isFinite(dy)? dy : 0;
- }
- return new mxConnectionConstraint(point, perimeter, null, dx, dy);
- };
- /**
- * Function: setConnectionConstraint
- *
- * Sets the <mxConnectionConstraint> that describes the given connection point.
- * If no constraint is given then nothing is changed. To remove an existing
- * constraint from the given edge, use an empty constraint instead.
- *
- * Parameters:
- *
- * edge - <mxCell> that represents the edge.
- * terminal - <mxCell> that represents the terminal.
- * source - Boolean indicating if the terminal is the source or target.
- * constraint - Optional <mxConnectionConstraint> to be used for this
- * connection.
- */
- mxGraph.prototype.setConnectionConstraint = function(edge, terminal, source, constraint)
- {
- if (constraint != null)
- {
- this.model.beginUpdate();
-
- try
- {
- if (constraint == null || constraint.point == null)
- {
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
- mxConstants.STYLE_ENTRY_X, null, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
- mxConstants.STYLE_ENTRY_Y, null, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_DX :
- mxConstants.STYLE_ENTRY_DX, null, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_DY :
- mxConstants.STYLE_ENTRY_DY, null, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
- mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
- }
- else if (constraint.point != null)
- {
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
- mxConstants.STYLE_ENTRY_X, constraint.point.x, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
- mxConstants.STYLE_ENTRY_Y, constraint.point.y, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_DX :
- mxConstants.STYLE_ENTRY_DX, constraint.dx, [edge]);
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_DY :
- mxConstants.STYLE_ENTRY_DY, constraint.dy, [edge]);
-
- // Only writes 0 since 1 is default
- if (!constraint.perimeter)
- {
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
- mxConstants.STYLE_ENTRY_PERIMETER, '0', [edge]);
- }
- else
- {
- this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
- mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
- }
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: getConnectionPoint
- *
- * Returns the nearest point in the list of absolute points or the center
- * of the opposite terminal.
- *
- * Parameters:
- *
- * vertex - <mxCellState> that represents the vertex.
- * constraint - <mxConnectionConstraint> that represents the connection point
- * constraint as returned by <getConnectionConstraint>.
- */
- mxGraph.prototype.getConnectionPoint = function(vertex, constraint, round)
- {
- round = (round != null) ? round : true;
- var point = null;
-
- if (vertex != null && constraint.point != null)
- {
- var bounds = this.view.getPerimeterBounds(vertex);
- var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
- var direction = vertex.style[mxConstants.STYLE_DIRECTION];
- var r1 = 0;
-
- // Bounds need to be rotated by 90 degrees for further computation
- if (direction != null && mxUtils.getValue(vertex.style,
- mxConstants.STYLE_ANCHOR_POINT_DIRECTION, 1) == 1)
- {
- if (direction == mxConstants.DIRECTION_NORTH)
- {
- r1 += 270;
- }
- else if (direction == mxConstants.DIRECTION_WEST)
- {
- r1 += 180;
- }
- else if (direction == mxConstants.DIRECTION_SOUTH)
- {
- r1 += 90;
- }
- // Bounds need to be rotated by 90 degrees for further computation
- if (direction == mxConstants.DIRECTION_NORTH ||
- direction == mxConstants.DIRECTION_SOUTH)
- {
- bounds.rotate90();
- }
- }
- var scale = this.view.scale;
- point = new mxPoint(bounds.x + constraint.point.x * bounds.width + constraint.dx * scale,
- bounds.y + constraint.point.y * bounds.height + constraint.dy * scale);
-
- // Rotation for direction before projection on perimeter
- var r2 = vertex.style[mxConstants.STYLE_ROTATION] || 0;
-
- if (constraint.perimeter)
- {
- if (r1 != 0)
- {
- // Only 90 degrees steps possible here so no trig needed
- var cos = 0;
- var sin = 0;
-
- if (r1 == 90)
- {
- sin = 1;
- }
- else if (r1 == 180)
- {
- cos = -1;
- }
- else if (r1 == 270)
- {
- sin = -1;
- }
-
- point = mxUtils.getRotatedPoint(point, cos, sin, cx);
- }
-
- point = this.view.getPerimeterPoint(vertex, point, false);
- }
- else
- {
- r2 += r1;
-
- if (this.getModel().isVertex(vertex.cell))
- {
- var flipH = vertex.style[mxConstants.STYLE_FLIPH] == 1;
- var flipV = vertex.style[mxConstants.STYLE_FLIPV] == 1;
-
- // Legacy support for stencilFlipH/V
- if (vertex.shape != null && vertex.shape.stencil != null)
- {
- flipH = (mxUtils.getValue(vertex.style, 'stencilFlipH', 0) == 1) || flipH;
- flipV = (mxUtils.getValue(vertex.style, 'stencilFlipV', 0) == 1) || flipV;
- }
-
- if (direction == mxConstants.DIRECTION_NORTH ||
- direction == mxConstants.DIRECTION_SOUTH)
- {
- var temp = flipH;
- flipH = flipV
- flipV = temp;
- }
-
- if (flipH)
- {
- point.x = 2 * bounds.getCenterX() - point.x;
- }
-
- if (flipV)
- {
- point.y = 2 * bounds.getCenterY() - point.y;
- }
- }
- }
- // Generic rotation after projection on perimeter
- if (r2 != 0 && point != null)
- {
- var rad = mxUtils.toRadians(r2);
- var cos = Math.cos(rad);
- var sin = Math.sin(rad);
-
- point = mxUtils.getRotatedPoint(point, cos, sin, cx);
- }
- }
-
- if (round && point != null)
- {
- point.x = Math.round(point.x);
- point.y = Math.round(point.y);
- }
- return point;
- };
- /**
- * Function: connectCell
- *
- * Connects the specified end of the given edge to the given terminal
- * using <cellConnected> and fires <mxEvent.CONNECT_CELL> while the
- * transaction is in progress. Returns the updated edge.
- *
- * Parameters:
- *
- * edge - <mxCell> whose terminal should be updated.
- * terminal - <mxCell> that represents the new terminal to be used.
- * source - Boolean indicating if the new terminal is the source or target.
- * constraint - Optional <mxConnectionConstraint> to be used for this
- * connection.
- */
- mxGraph.prototype.connectCell = function(edge, terminal, source, constraint)
- {
- this.model.beginUpdate();
- try
- {
- var previous = this.model.getTerminal(edge, source);
- this.cellConnected(edge, terminal, source, constraint);
- this.fireEvent(new mxEventObject(mxEvent.CONNECT_CELL,
- 'edge', edge, 'terminal', terminal, 'source', source,
- 'previous', previous));
- }
- finally
- {
- this.model.endUpdate();
- }
- return edge;
- };
- /**
- * Function: cellConnected
- *
- * Sets the new terminal for the given edge and resets the edge points if
- * <resetEdgesOnConnect> is true. This method fires
- * <mxEvent.CELL_CONNECTED> while the transaction is in progress.
- *
- * Parameters:
- *
- * edge - <mxCell> whose terminal should be updated.
- * terminal - <mxCell> that represents the new terminal to be used.
- * source - Boolean indicating if the new terminal is the source or target.
- * constraint - <mxConnectionConstraint> to be used for this connection.
- */
- mxGraph.prototype.cellConnected = function(edge, terminal, source, constraint)
- {
- if (edge != null)
- {
- this.model.beginUpdate();
- try
- {
- var previous = this.model.getTerminal(edge, source);
- // Updates the constraint
- this.setConnectionConstraint(edge, terminal, source, constraint);
-
- // Checks if the new terminal is a port, uses the ID of the port in the
- // style and the parent of the port as the actual terminal of the edge.
- if (this.isPortsEnabled())
- {
- var id = null;
-
- if (this.isPort(terminal))
- {
- id = terminal.getId();
- terminal = this.getTerminalForPort(terminal, source);
- }
-
- // Sets or resets all previous information for connecting to a child port
- var key = (source) ? mxConstants.STYLE_SOURCE_PORT :
- mxConstants.STYLE_TARGET_PORT;
- this.setCellStyles(key, id, [edge]);
- }
-
- this.model.setTerminal(edge, terminal, source);
-
- if (this.resetEdgesOnConnect)
- {
- this.resetEdge(edge);
- }
- this.fireEvent(new mxEventObject(mxEvent.CELL_CONNECTED,
- 'edge', edge, 'terminal', terminal, 'source', source,
- 'previous', previous));
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Function: disconnectGraph
- *
- * Disconnects the given edges from the terminals which are not in the
- * given array.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be disconnected.
- */
- mxGraph.prototype.disconnectGraph = function(cells)
- {
- if (cells != null)
- {
- this.model.beginUpdate();
- try
- {
- var scale = this.view.scale;
- var tr = this.view.translate;
-
- // Fast lookup for finding cells in array
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- for (var i = 0; i < cells.length; i++)
- {
- if (this.model.isEdge(cells[i]))
- {
- var geo = this.model.getGeometry(cells[i]);
-
- if (geo != null)
- {
- var state = this.view.getState(cells[i]);
- var pstate = this.view.getState(
- this.model.getParent(cells[i]));
-
- if (state != null &&
- pstate != null)
- {
- geo = geo.clone();
-
- var dx = -pstate.origin.x;
- var dy = -pstate.origin.y;
- var pts = state.absolutePoints;
- var src = this.model.getTerminal(cells[i], true);
-
- if (src != null && this.isCellDisconnectable(cells[i], src, true))
- {
- while (src != null && !dict.get(src))
- {
- src = this.model.getParent(src);
- }
-
- if (src == null)
- {
- geo.setTerminalPoint(
- new mxPoint(pts[0].x / scale - tr.x + dx,
- pts[0].y / scale - tr.y + dy), true);
- this.model.setTerminal(cells[i], null, true);
- }
- }
-
- var trg = this.model.getTerminal(cells[i], false);
-
- if (trg != null && this.isCellDisconnectable(cells[i], trg, false))
- {
- while (trg != null && !dict.get(trg))
- {
- trg = this.model.getParent(trg);
- }
-
- if (trg == null)
- {
- var n = pts.length - 1;
- geo.setTerminalPoint(
- new mxPoint(pts[n].x / scale - tr.x + dx,
- pts[n].y / scale - tr.y + dy), false);
- this.model.setTerminal(cells[i], null, false);
- }
- }
- this.model.setGeometry(cells[i], geo);
- }
- }
- }
- }
- }
- finally
- {
- this.model.endUpdate();
- }
- }
- };
- /**
- * Group: Drilldown
- */
- /**
- * Function: getCurrentRoot
- *
- * Returns the current root of the displayed cell hierarchy. This is a
- * shortcut to <mxGraphView.currentRoot> in <view>.
- */
- mxGraph.prototype.getCurrentRoot = function()
- {
- return this.view.currentRoot;
- };
-
- /**
- * Function: getTranslateForRoot
- *
- * Returns the translation to be used if the given cell is the root cell as
- * an <mxPoint>. This implementation returns null.
- *
- * Example:
- *
- * To keep the children at their absolute position while stepping into groups,
- * this function can be overridden as follows.
- *
- * (code)
- * var offset = new mxPoint(0, 0);
- *
- * while (cell != null)
- * {
- * var geo = this.model.getGeometry(cell);
- *
- * if (geo != null)
- * {
- * offset.x -= geo.x;
- * offset.y -= geo.y;
- * }
- *
- * cell = this.model.getParent(cell);
- * }
- *
- * return offset;
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the root.
- */
- mxGraph.prototype.getTranslateForRoot = function(cell)
- {
- return null;
- };
- /**
- * Function: isPort
- *
- * Returns true if the given cell is a "port", that is, when connecting to
- * it, the cell returned by getTerminalForPort should be used as the
- * terminal and the port should be referenced by the ID in either the
- * mxConstants.STYLE_SOURCE_PORT or the or the
- * mxConstants.STYLE_TARGET_PORT. Note that a port should not be movable.
- * This implementation always returns false.
- *
- * A typical implementation is the following:
- *
- * (code)
- * graph.isPort = function(cell)
- * {
- * var geo = this.getCellGeometry(cell);
- *
- * return (geo != null) ? geo.relative : false;
- * };
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the port.
- */
- mxGraph.prototype.isPort = function(cell)
- {
- return false;
- };
- /**
- * Function: getTerminalForPort
- *
- * Returns the terminal to be used for a given port. This implementation
- * always returns the parent cell.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the port.
- * source - If the cell is the source or target port.
- */
- mxGraph.prototype.getTerminalForPort = function(cell, source)
- {
- return this.model.getParent(cell);
- };
- /**
- * Function: getChildOffsetForCell
- *
- * Returns the offset to be used for the cells inside the given cell. The
- * root and layer cells may be identified using <mxGraphModel.isRoot> and
- * <mxGraphModel.isLayer>. For all other current roots, the
- * <mxGraphView.currentRoot> field points to the respective cell, so that
- * the following holds: cell == this.view.currentRoot. This implementation
- * returns null.
- *
- * Parameters:
- *
- * cell - <mxCell> whose offset should be returned.
- */
- mxGraph.prototype.getChildOffsetForCell = function(cell)
- {
- return null;
- };
- /**
- * Function: enterGroup
- *
- * Uses the given cell as the root of the displayed cell hierarchy. If no
- * cell is specified then the selection cell is used. The cell is only used
- * if <isValidRoot> returns true.
- *
- * Parameters:
- *
- * cell - Optional <mxCell> to be used as the new root. Default is the
- * selection cell.
- */
- mxGraph.prototype.enterGroup = function(cell)
- {
- cell = cell || this.getSelectionCell();
-
- if (cell != null && this.isValidRoot(cell))
- {
- this.view.setCurrentRoot(cell);
- this.clearSelection();
- }
- };
- /**
- * Function: exitGroup
- *
- * Changes the current root to the next valid root in the displayed cell
- * hierarchy.
- */
- mxGraph.prototype.exitGroup = function()
- {
- var root = this.model.getRoot();
- var current = this.getCurrentRoot();
-
- if (current != null)
- {
- var next = this.model.getParent(current);
-
- // Finds the next valid root in the hierarchy
- while (next != root && !this.isValidRoot(next) &&
- this.model.getParent(next) != root)
- {
- next = this.model.getParent(next);
- }
-
- // Clears the current root if the new root is
- // the model's root or one of the layers.
- if (next == root || this.model.getParent(next) == root)
- {
- this.view.setCurrentRoot(null);
- }
- else
- {
- this.view.setCurrentRoot(next);
- }
-
- var state = this.view.getState(current);
-
- // Selects the previous root in the graph
- if (state != null)
- {
- this.setSelectionCell(current);
- }
- }
- };
- /**
- * Function: home
- *
- * Uses the root of the model as the root of the displayed cell hierarchy
- * and selects the previous root.
- */
- mxGraph.prototype.home = function()
- {
- var current = this.getCurrentRoot();
-
- if (current != null)
- {
- this.view.setCurrentRoot(null);
- var state = this.view.getState(current);
-
- if (state != null)
- {
- this.setSelectionCell(current);
- }
- }
- };
- /**
- * Function: isValidRoot
- *
- * Returns true if the given cell is a valid root for the cell display
- * hierarchy. This implementation returns true for all non-null values.
- *
- * Parameters:
- *
- * cell - <mxCell> which should be checked as a possible root.
- */
- mxGraph.prototype.isValidRoot = function(cell)
- {
- return (cell != null);
- };
- /**
- * Group: Graph display
- */
-
- /**
- * Function: getGraphBounds
- *
- * Returns the bounds of the visible graph. Shortcut to
- * <mxGraphView.getGraphBounds>. See also: <getBoundingBoxFromGeometry>.
- */
- mxGraph.prototype.getGraphBounds = function()
- {
- return this.view.getGraphBounds();
- };
- /**
- * Function: getCellBounds
- *
- * Returns the scaled, translated bounds for the given cell. See
- * <mxGraphView.getBounds> for arrays.
- *
- * Parameters:
- *
- * cell - <mxCell> whose bounds should be returned.
- * includeEdge - Optional boolean that specifies if the bounds of
- * the connected edges should be included. Default is false.
- * includeDescendants - Optional boolean that specifies if the bounds
- * of all descendants should be included. Default is false.
- */
- mxGraph.prototype.getCellBounds = function(cell, includeEdges, includeDescendants)
- {
- var cells = [cell];
-
- // Includes all connected edges
- if (includeEdges)
- {
- cells = cells.concat(this.model.getEdges(cell));
- }
-
- var result = this.view.getBounds(cells);
-
- // Recursively includes the bounds of the children
- if (includeDescendants)
- {
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- var tmp = this.getCellBounds(this.model.getChildAt(cell, i),
- includeEdges, true);
- if (result != null)
- {
- result.add(tmp);
- }
- else
- {
- result = tmp;
- }
- }
- }
-
- return result;
- };
- /**
- * Function: getBoundingBoxFromGeometry
- *
- * Returns the bounding box for the geometries of the vertices in the
- * given array of cells. This can be used to find the graph bounds during
- * a layout operation (ie. before the last endUpdate) as follows:
- *
- * (code)
- * var cells = graph.getChildCells(graph.getDefaultParent(), true, true);
- * var bounds = graph.getBoundingBoxFromGeometry(cells, true);
- * (end)
- *
- * This can then be used to move cells to the origin:
- *
- * (code)
- * if (bounds.x < 0 || bounds.y < 0)
- * {
- * graph.moveCells(cells, -Math.min(bounds.x, 0), -Math.min(bounds.y, 0))
- * }
- * (end)
- *
- * Or to translate the graph view:
- *
- * (code)
- * if (bounds.x < 0 || bounds.y < 0)
- * {
- * graph.view.setTranslate(-Math.min(bounds.x, 0), -Math.min(bounds.y, 0));
- * }
- * (end)
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose bounds should be returned.
- * includeEdges - Specifies if edge bounds should be included by computing
- * the bounding box for all points in geometry. Default is false.
- */
- mxGraph.prototype.getBoundingBoxFromGeometry = function(cells, includeEdges)
- {
- includeEdges = (includeEdges != null) ? includeEdges : false;
- var result = null;
-
- if (cells != null)
- {
- for (var i = 0; i < cells.length; i++)
- {
- if (includeEdges || this.model.isVertex(cells[i]))
- {
- // Computes the bounding box for the points in the geometry
- var geo = this.getCellGeometry(cells[i]);
-
- if (geo != null)
- {
- var bbox = null;
-
- if (this.model.isEdge(cells[i]))
- {
- var addPoint = function(pt)
- {
- if (pt != null)
- {
- if (tmp == null)
- {
- tmp = new mxRectangle(pt.x, pt.y, 0, 0);
- }
- else
- {
- tmp.add(new mxRectangle(pt.x, pt.y, 0, 0));
- }
- }
- };
-
- if (this.model.getTerminal(cells[i], true) == null)
- {
- addPoint(geo.getTerminalPoint(true));
- }
-
- if (this.model.getTerminal(cells[i], false) == null)
- {
- addPoint(geo.getTerminalPoint(false));
- }
-
- var pts = geo.points;
-
- if (pts != null && pts.length > 0)
- {
- var tmp = new mxRectangle(pts[0].x, pts[0].y, 0, 0);
- for (var j = 1; j < pts.length; j++)
- {
- addPoint(pts[j]);
- }
- }
-
- bbox = tmp;
- }
- else
- {
- var parent = this.model.getParent(cells[i]);
-
- if (geo.relative)
- {
- if (this.model.isVertex(parent) && parent != this.view.currentRoot)
- {
- var tmp = this.getBoundingBoxFromGeometry([parent], false);
-
- if (tmp != null)
- {
- bbox = new mxRectangle(geo.x * tmp.width, geo.y * tmp.height, geo.width, geo.height);
-
- if (mxUtils.indexOf(cells, parent) >= 0)
- {
- bbox.x += tmp.x;
- bbox.y += tmp.y;
- }
- }
- }
- }
- else
- {
- bbox = mxRectangle.fromRectangle(geo);
-
- if (this.model.isVertex(parent) && mxUtils.indexOf(cells, parent) >= 0)
- {
- var tmp = this.getBoundingBoxFromGeometry([parent], false);
- if (tmp != null)
- {
- bbox.x += tmp.x;
- bbox.y += tmp.y;
- }
- }
- }
-
- if (bbox != null && geo.offset != null)
- {
- bbox.x += geo.offset.x;
- bbox.y += geo.offset.y;
- }
- var style = this.getCurrentCellStyle(cells[i]);
-
- if (bbox != null)
- {
- var angle = mxUtils.getValue(style, mxConstants.STYLE_ROTATION, 0);
-
- if (angle != 0)
- {
- bbox = mxUtils.getBoundingBox(bbox, angle);
- }
- }
- }
-
- if (bbox != null)
- {
- if (result == null)
- {
- result = mxRectangle.fromRectangle(bbox);
- }
- else
- {
- result.add(bbox);
- }
- }
- }
- }
- }
- }
-
- return result;
- };
- /**
- * Function: refresh
- *
- * Clears all cell states or the states for the hierarchy starting at the
- * given cell and validates the graph. This fires a refresh event as the
- * last step.
- *
- * Parameters:
- *
- * cell - Optional <mxCell> for which the cell states should be cleared.
- */
- mxGraph.prototype.refresh = function(cell)
- {
- this.view.clear(cell, cell == null);
- this.view.validate();
- this.sizeDidChange();
- this.fireEvent(new mxEventObject(mxEvent.REFRESH));
- };
- /**
- * Function: snap
- *
- * Snaps the given numeric value to the grid if <gridEnabled> is true.
- *
- * Parameters:
- *
- * value - Numeric value to be snapped to the grid.
- */
- mxGraph.prototype.snap = function(value)
- {
- if (this.gridEnabled)
- {
- value = Math.round(value / this.gridSize ) * this.gridSize;
- }
-
- return value;
- };
- /**
- * Function: snapDelta
- *
- * Snaps the given delta with the given scaled bounds.
- */
- mxGraph.prototype.snapDelta = function(delta, bounds, ignoreGrid, ignoreHorizontal, ignoreVertical)
- {
- var t = this.view.translate;
- var s = this.view.scale;
-
- if (!ignoreGrid && this.gridEnabled)
- {
- var tol = this.gridSize * s * 0.5;
-
- if (!ignoreHorizontal)
- {
- var tx = bounds.x - (this.snap(bounds.x / s - t.x) + t.x) * s;
-
- if (Math.abs(delta.x- tx) < tol)
- {
- delta.x = 0;
- }
- else
- {
- delta.x = this.snap(delta.x / s) * s - tx;
- }
- }
-
- if (!ignoreVertical)
- {
- var ty = bounds.y - (this.snap(bounds.y / s - t.y) + t.y) * s;
-
- if (Math.abs(delta.y - ty) < tol)
- {
- delta.y = 0;
- }
- else
- {
- delta.y = this.snap(delta.y / s) * s - ty;
- }
- }
- }
- else
- {
- var tol = 0.5 * s;
-
- if (!ignoreHorizontal)
- {
- var tx = bounds.x - (Math.round(bounds.x / s - t.x) + t.x) * s;
-
- if (Math.abs(delta.x - tx) < tol)
- {
- delta.x = 0;
- }
- else
- {
- delta.x = Math.round(delta.x / s) * s - tx;
- }
- }
-
- if (!ignoreVertical)
- {
- var ty = bounds.y - (Math.round(bounds.y / s - t.y) + t.y) * s;
-
- if (Math.abs(delta.y - ty) < tol)
- {
- delta.y = 0;
- }
- else
- {
- delta.y = Math.round(delta.y / s) * s - ty;
- }
- }
- }
-
- return delta;
- };
- /**
- * Function: panGraph
- *
- * Shifts the graph display by the given amount. This is used to preview
- * panning operations, use <mxGraphView.setTranslate> to set a persistent
- * translation of the view. Fires <mxEvent.PAN>.
- *
- * Parameters:
- *
- * dx - Amount to shift the graph along the x-axis.
- * dy - Amount to shift the graph along the y-axis.
- */
- mxGraph.prototype.panGraph = function(dx, dy)
- {
- if (this.useScrollbarsForPanning && mxUtils.hasScrollbars(this.container))
- {
- this.container.scrollLeft = -dx;
- this.container.scrollTop = -dy;
- }
- else
- {
- var canvas = this.view.getCanvas();
-
- if (this.dialect == mxConstants.DIALECT_SVG)
- {
- // Puts everything inside the container in a DIV so that it
- // can be moved without changing the state of the container
- if (dx == 0 && dy == 0)
- {
- // Workaround for ignored removeAttribute on SVG element in IE9 standards
- if (mxClient.IS_IE)
- {
- canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
- }
- else
- {
- canvas.removeAttribute('transform');
- }
-
- if (this.shiftPreview1 != null)
- {
- var child = this.shiftPreview1.firstChild;
-
- while (child != null)
- {
- var next = child.nextSibling;
- this.container.appendChild(child);
- child = next;
- }
- if (this.shiftPreview1.parentNode != null)
- {
- this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
- }
-
- this.shiftPreview1 = null;
-
- this.container.appendChild(canvas.parentNode);
-
- child = this.shiftPreview2.firstChild;
-
- while (child != null)
- {
- var next = child.nextSibling;
- this.container.appendChild(child);
- child = next;
- }
- if (this.shiftPreview2.parentNode != null)
- {
- this.shiftPreview2.parentNode.removeChild(this.shiftPreview2);
- }
-
- this.shiftPreview2 = null;
- }
- }
- else
- {
- canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
-
- if (this.shiftPreview1 == null)
- {
- // Needs two divs for stuff before and after the SVG element
- this.shiftPreview1 = document.createElement('div');
- this.shiftPreview1.style.position = 'absolute';
- this.shiftPreview1.style.overflow = 'visible';
-
- this.shiftPreview2 = document.createElement('div');
- this.shiftPreview2.style.position = 'absolute';
- this.shiftPreview2.style.overflow = 'visible';
- var current = this.shiftPreview1;
- var child = this.container.firstChild;
-
- while (child != null)
- {
- var next = child.nextSibling;
-
- // SVG element is moved via transform attribute
- if (child != canvas.parentNode)
- {
- current.appendChild(child);
- }
- else
- {
- current = this.shiftPreview2;
- }
-
- child = next;
- }
-
- // Inserts elements only if not empty
- if (this.shiftPreview1.firstChild != null)
- {
- this.container.insertBefore(this.shiftPreview1, canvas.parentNode);
- }
-
- if (this.shiftPreview2.firstChild != null)
- {
- this.container.appendChild(this.shiftPreview2);
- }
- }
-
- this.shiftPreview1.style.left = dx + 'px';
- this.shiftPreview1.style.top = dy + 'px';
- this.shiftPreview2.style.left = dx + 'px';
- this.shiftPreview2.style.top = dy + 'px';
- }
- }
- else
- {
- canvas.style.left = dx + 'px';
- canvas.style.top = dy + 'px';
- }
-
- this.panDx = dx;
- this.panDy = dy;
- this.fireEvent(new mxEventObject(mxEvent.PAN));
- }
- };
- /**
- * Function: zoomIn
- *
- * Zooms into the graph by <zoomFactor>.
- */
- mxGraph.prototype.zoomIn = function()
- {
- this.zoom(this.zoomFactor);
- };
- /**
- * Function: zoomOut
- *
- * Zooms out of the graph by <zoomFactor>.
- */
- mxGraph.prototype.zoomOut = function()
- {
- this.zoom(1 / this.zoomFactor);
- };
- /**
- * Function: zoomActual
- *
- * Resets the zoom and panning in the view.
- */
- mxGraph.prototype.zoomActual = function()
- {
- if (this.view.scale == 1)
- {
- this.view.setTranslate(0, 0);
- }
- else
- {
- this.view.translate.x = 0;
- this.view.translate.y = 0;
- this.view.setScale(1);
- }
- };
- /**
- * Function: zoomTo
- *
- * Zooms the graph to the given scale with an optional boolean center
- * argument, which is passd to <zoom>.
- */
- mxGraph.prototype.zoomTo = function(scale, center)
- {
- this.zoom(scale / this.view.scale, center);
- };
- /**
- * Function: center
- *
- * Centers the graph in the container.
- *
- * Parameters:
- *
- * horizontal - Optional boolean that specifies if the graph should be centered
- * horizontally. Default is true.
- * vertical - Optional boolean that specifies if the graph should be centered
- * vertically. Default is true.
- * cx - Optional float that specifies the horizontal center. Default is 0.5.
- * cy - Optional float that specifies the vertical center. Default is 0.5.
- */
- mxGraph.prototype.center = function(horizontal, vertical, cx, cy)
- {
- horizontal = (horizontal != null) ? horizontal : true;
- vertical = (vertical != null) ? vertical : true;
- cx = (cx != null) ? cx : 0.5;
- cy = (cy != null) ? cy : 0.5;
-
- var hasScrollbars = mxUtils.hasScrollbars(this.container);
- var padding = 2 * this.getBorder();
- var cw = this.container.clientWidth - padding;
- var ch = this.container.clientHeight - padding;
- var bounds = this.getGraphBounds();
- var t = this.view.translate;
- var s = this.view.scale;
- var dx = (horizontal) ? cw - bounds.width : 0;
- var dy = (vertical) ? ch - bounds.height : 0;
-
- if (!hasScrollbars)
- {
- this.view.setTranslate((horizontal) ? Math.floor(t.x - bounds.x * s + dx * cx / s) : t.x,
- (vertical) ? Math.floor(t.y - bounds.y * s + dy * cy / s) : t.y);
- }
- else
- {
- bounds.x -= t.x;
- bounds.y -= t.y;
-
- var sw = this.container.scrollWidth;
- var sh = this.container.scrollHeight;
-
- if (sw > cw)
- {
- dx = 0;
- }
-
- if (sh > ch)
- {
- dy = 0;
- }
- this.view.setTranslate(Math.floor(dx / 2 - bounds.x), Math.floor(dy / 2 - bounds.y));
- this.container.scrollLeft = (sw - cw) / 2;
- this.container.scrollTop = (sh - ch) / 2;
- }
- };
- /**
- * Function: zoom
- *
- * Zooms the graph using the given factor. Center is an optional boolean
- * argument that keeps the graph scrolled to the center. If the center argument
- * is omitted, then <centerZoom> will be used as its value.
- */
- mxGraph.prototype.zoom = function(factor, center)
- {
- center = (center != null) ? center : this.centerZoom;
- var scale = Math.round(this.view.scale * factor * 100) / 100;
- var state = this.view.getState(this.getSelectionCell());
- factor = scale / this.view.scale;
-
- if (this.keepSelectionVisibleOnZoom && state != null)
- {
- var rect = new mxRectangle(state.x * factor, state.y * factor,
- state.width * factor, state.height * factor);
-
- // Refreshes the display only once if a scroll is carried out
- this.view.scale = scale;
-
- if (!this.scrollRectToVisible(rect))
- {
- this.view.revalidate();
-
- // Forces an event to be fired but does not revalidate again
- this.view.setScale(scale);
- }
- }
- else
- {
- var hasScrollbars = mxUtils.hasScrollbars(this.container);
-
- if (center && !hasScrollbars)
- {
- var dx = this.container.offsetWidth;
- var dy = this.container.offsetHeight;
-
- if (factor > 1)
- {
- var f = (factor - 1) / (scale * 2);
- dx *= -f;
- dy *= -f;
- }
- else
- {
- var f = (1 / factor - 1) / (this.view.scale * 2);
- dx *= f;
- dy *= f;
- }
- this.view.scaleAndTranslate(scale,
- this.view.translate.x + dx,
- this.view.translate.y + dy);
- }
- else
- {
- // Allows for changes of translate and scrollbars during setscale
- var tx = this.view.translate.x;
- var ty = this.view.translate.y;
- var sl = this.container.scrollLeft;
- var st = this.container.scrollTop;
-
- this.view.setScale(scale);
-
- if (hasScrollbars)
- {
- var dx = 0;
- var dy = 0;
-
- if (center)
- {
- dx = this.container.offsetWidth * (factor - 1) / 2;
- dy = this.container.offsetHeight * (factor - 1) / 2;
- }
-
- this.container.scrollLeft = (this.view.translate.x - tx) * this.view.scale + Math.round(sl * factor + dx);
- this.container.scrollTop = (this.view.translate.y - ty) * this.view.scale + Math.round(st * factor + dy);
- }
- }
- }
- };
- /**
- * Function: zoomToRect
- *
- * Zooms the graph to the specified rectangle. If the rectangle does not have same aspect
- * ratio as the display container, it is increased in the smaller relative dimension only
- * until the aspect match. The original rectangle is centralised within this expanded one.
- *
- * Note that the input rectangular must be un-scaled and un-translated.
- *
- * Parameters:
- *
- * rect - The un-scaled and un-translated rectangluar region that should be just visible
- * after the operation
- */
- mxGraph.prototype.zoomToRect = function(rect)
- {
- var scaleX = this.container.clientWidth / rect.width;
- var scaleY = this.container.clientHeight / rect.height;
- var aspectFactor = scaleX / scaleY;
- // Remove any overlap of the rect outside the client area
- rect.x = Math.max(0, rect.x);
- rect.y = Math.max(0, rect.y);
- var rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
- var rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
- rect.width = rectRight - rect.x;
- rect.height = rectBottom - rect.y;
- // The selection area has to be increased to the same aspect
- // ratio as the container, centred around the centre point of the
- // original rect passed in.
- if (aspectFactor < 1.0)
- {
- // Height needs increasing
- var newHeight = rect.height / aspectFactor;
- var deltaHeightBuffer = (newHeight - rect.height) / 2.0;
- rect.height = newHeight;
-
- // Assign up to half the buffer to the upper part of the rect, not crossing 0
- // put the rest on the bottom
- var upperBuffer = Math.min(rect.y , deltaHeightBuffer);
- rect.y = rect.y - upperBuffer;
-
- // Check if the bottom has extended too far
- rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
- rect.height = rectBottom - rect.y;
- }
- else
- {
- // Width needs increasing
- var newWidth = rect.width * aspectFactor;
- var deltaWidthBuffer = (newWidth - rect.width) / 2.0;
- rect.width = newWidth;
-
- // Assign up to half the buffer to the upper part of the rect, not crossing 0
- // put the rest on the bottom
- var leftBuffer = Math.min(rect.x , deltaWidthBuffer);
- rect.x = rect.x - leftBuffer;
-
- // Check if the right hand side has extended too far
- rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
- rect.width = rectRight - rect.x;
- }
- var scale = this.container.clientWidth / rect.width;
- var newScale = this.view.scale * scale;
- if (!mxUtils.hasScrollbars(this.container))
- {
- this.view.scaleAndTranslate(newScale, (this.view.translate.x - rect.x / this.view.scale), (this.view.translate.y - rect.y / this.view.scale));
- }
- else
- {
- this.view.setScale(newScale);
- this.container.scrollLeft = Math.round(rect.x * scale);
- this.container.scrollTop = Math.round(rect.y * scale);
- }
- };
- /**
- * Function: scrollCellToVisible
- *
- * Pans the graph so that it shows the given cell. Optionally the cell may
- * be centered in the container.
- *
- * To center a given graph if the <container> has no scrollbars, use the following code.
- *
- * [code]
- * var bounds = graph.getGraphBounds();
- * graph.view.setTranslate(-bounds.x - (bounds.width - container.clientWidth) / 2,
- * -bounds.y - (bounds.height - container.clientHeight) / 2);
- * [/code]
- *
- * Parameters:
- *
- * cell - <mxCell> to be made visible.
- * center - Optional boolean flag. Default is false.
- */
- mxGraph.prototype.scrollCellToVisible = function(cell, center)
- {
- var x = -this.view.translate.x;
- var y = -this.view.translate.y;
- var state = this.view.getState(cell);
- if (state != null)
- {
- var bounds = new mxRectangle(x + state.x, y + state.y, state.width,
- state.height);
- if (center && this.container != null)
- {
- var w = this.container.clientWidth;
- var h = this.container.clientHeight;
- bounds.x = bounds.getCenterX() - w / 2;
- bounds.width = w;
- bounds.y = bounds.getCenterY() - h / 2;
- bounds.height = h;
- }
-
- var tr = new mxPoint(this.view.translate.x, this.view.translate.y);
- if (this.scrollRectToVisible(bounds))
- {
- // Triggers an update via the view's event source
- var tr2 = new mxPoint(this.view.translate.x, this.view.translate.y);
- this.view.translate.x = tr.x;
- this.view.translate.y = tr.y;
- this.view.setTranslate(tr2.x, tr2.y);
- }
- }
- };
- /**
- * Function: scrollRectToVisible
- *
- * Pans the graph so that it shows the given rectangle.
- *
- * Parameters:
- *
- * rect - <mxRectangle> to be made visible.
- */
- mxGraph.prototype.scrollRectToVisible = function(rect)
- {
- var isChanged = false;
-
- if (rect != null)
- {
- var w = this.container.offsetWidth;
- var h = this.container.offsetHeight;
- var widthLimit = Math.min(w, rect.width);
- var heightLimit = Math.min(h, rect.height);
- if (mxUtils.hasScrollbars(this.container))
- {
- var c = this.container;
- rect.x += this.view.translate.x;
- rect.y += this.view.translate.y;
- var dx = c.scrollLeft - rect.x;
- var ddx = Math.max(dx - c.scrollLeft, 0);
- if (dx > 0)
- {
- c.scrollLeft -= dx + 2;
- }
- else
- {
- dx = rect.x + widthLimit - c.scrollLeft - c.clientWidth;
- if (dx > 0)
- {
- c.scrollLeft += dx + 2;
- }
- }
- var dy = c.scrollTop - rect.y;
- var ddy = Math.max(0, dy - c.scrollTop);
- if (dy > 0)
- {
- c.scrollTop -= dy + 2;
- }
- else
- {
- dy = rect.y + heightLimit - c.scrollTop - c.clientHeight;
- if (dy > 0)
- {
- c.scrollTop += dy + 2;
- }
- }
- if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0))
- {
- this.view.setTranslate(ddx, ddy);
- }
- }
- else
- {
- var x = -this.view.translate.x;
- var y = -this.view.translate.y;
- var s = this.view.scale;
- if (rect.x + widthLimit > x + w)
- {
- this.view.translate.x -= (rect.x + widthLimit - w - x) / s;
- isChanged = true;
- }
- if (rect.y + heightLimit > y + h)
- {
- this.view.translate.y -= (rect.y + heightLimit - h - y) / s;
- isChanged = true;
- }
- if (rect.x < x)
- {
- this.view.translate.x += (x - rect.x) / s;
- isChanged = true;
- }
- if (rect.y < y)
- {
- this.view.translate.y += (y - rect.y) / s;
- isChanged = true;
- }
- if (isChanged)
- {
- this.view.refresh();
-
- // Repaints selection marker (ticket 18)
- if (this.selectionCellsHandler != null)
- {
- this.selectionCellsHandler.refresh();
- }
- }
- }
- }
- return isChanged;
- };
- /**
- * Function: getCellGeometry
- *
- * Returns the <mxGeometry> for the given cell. This implementation uses
- * <mxGraphModel.getGeometry>. Subclasses can override this to implement
- * specific geometries for cells in only one graph, that is, it can return
- * geometries that depend on the current state of the view.
- *
- * Parameters:
- *
- * cell - <mxCell> whose geometry should be returned.
- */
- mxGraph.prototype.getCellGeometry = function(cell)
- {
- return this.model.getGeometry(cell);
- };
- /**
- * Function: isCellVisible
- *
- * Returns true if the given cell is visible in this graph. This
- * implementation uses <mxGraphModel.isVisible>. Subclassers can override
- * this to implement specific visibility for cells in only one graph, that
- * is, without affecting the visible state of the cell.
- *
- * When using dynamic filter expressions for cell visibility, then the
- * graph should be revalidated after the filter expression has changed.
- *
- * Parameters:
- *
- * cell - <mxCell> whose visible state should be returned.
- */
- mxGraph.prototype.isCellVisible = function(cell)
- {
- return this.model.isVisible(cell);
- };
- /**
- * Function: isCellCollapsed
- *
- * Returns true if the given cell is collapsed in this graph. This
- * implementation uses <mxGraphModel.isCollapsed>. Subclassers can override
- * this to implement specific collapsed states for cells in only one graph,
- * that is, without affecting the collapsed state of the cell.
- *
- * When using dynamic filter expressions for the collapsed state, then the
- * graph should be revalidated after the filter expression has changed.
- *
- * Parameters:
- *
- * cell - <mxCell> whose collapsed state should be returned.
- */
- mxGraph.prototype.isCellCollapsed = function(cell)
- {
- return this.model.isCollapsed(cell);
- };
- /**
- * Function: isCellConnectable
- *
- * Returns true if the given cell is connectable in this graph. This
- * implementation uses <mxGraphModel.isConnectable>. Subclassers can override
- * this to implement specific connectable states for cells in only one graph,
- * that is, without affecting the connectable state of the cell in the model.
- *
- * Parameters:
- *
- * cell - <mxCell> whose connectable state should be returned.
- */
- mxGraph.prototype.isCellConnectable = function(cell)
- {
- return this.model.isConnectable(cell);
- };
- /**
- * Function: isOrthogonal
- *
- * Returns true if perimeter points should be computed such that the
- * resulting edge has only horizontal or vertical segments.
- *
- * Parameters:
- *
- * edge - <mxCellState> that represents the edge.
- */
- mxGraph.prototype.isOrthogonal = function(edge)
- {
- var orthogonal = edge.style[mxConstants.STYLE_ORTHOGONAL];
-
- if (orthogonal != null)
- {
- return orthogonal;
- }
-
- var tmp = this.view.getEdgeStyle(edge);
-
- return tmp == mxEdgeStyle.SegmentConnector ||
- tmp == mxEdgeStyle.ElbowConnector ||
- tmp == mxEdgeStyle.SideToSide ||
- tmp == mxEdgeStyle.TopToBottom ||
- tmp == mxEdgeStyle.EntityRelation ||
- tmp == mxEdgeStyle.OrthConnector;
- };
- /**
- * Function: isLoop
- *
- * Returns true if the given cell state is a loop.
- *
- * Parameters:
- *
- * state - <mxCellState> that represents a potential loop.
- */
- mxGraph.prototype.isLoop = function(state)
- {
- var src = state.getVisibleTerminalState(true);
- var trg = state.getVisibleTerminalState(false);
-
- return (src != null && src == trg);
- };
- /**
- * Function: isCloneEvent
- *
- * Returns true if the given event is a clone event. This implementation
- * returns true if control is pressed.
- */
- mxGraph.prototype.isCloneEvent = function(evt)
- {
- return mxEvent.isControlDown(evt);
- };
- /**
- * Function: isTransparentClickEvent
- *
- * Hook for implementing click-through behaviour on selected cells. If this
- * returns true the cell behind the selected cell will be selected. This
- * implementation returns false;
- */
- mxGraph.prototype.isTransparentClickEvent = function(evt)
- {
- return false;
- };
- /**
- * Function: isToggleEvent
- *
- * Returns true if the given event is a toggle event. This implementation
- * returns true if the meta key (Cmd) is pressed on Macs or if control is
- * pressed on any other platform.
- */
- mxGraph.prototype.isToggleEvent = function(evt)
- {
- return (mxClient.IS_MAC) ? mxEvent.isMetaDown(evt) : mxEvent.isControlDown(evt);
- };
- /**
- * Function: isGridEnabledEvent
- *
- * Returns true if the given mouse event should be aligned to the grid.
- */
- mxGraph.prototype.isGridEnabledEvent = function(evt)
- {
- return evt != null && !mxEvent.isAltDown(evt);
- };
- /**
- * Function: isConstrainedEvent
- *
- * Returns true if the given mouse event should be aligned to the grid.
- */
- mxGraph.prototype.isConstrainedEvent = function(evt)
- {
- return mxEvent.isShiftDown(evt);
- };
- /**
- * Function: isIgnoreTerminalEvent
- *
- * Returns true if the given mouse event should not allow any connections to be
- * made. This implementation returns false.
- */
- mxGraph.prototype.isIgnoreTerminalEvent = function(evt)
- {
- return false;
- };
- /**
- * Group: Validation
- */
- /**
- * Function: validationAlert
- *
- * Displays the given validation error in a dialog. This implementation uses
- * mxUtils.alert.
- */
- mxGraph.prototype.validationAlert = function(message)
- {
- mxUtils.alert(message);
- };
- /**
- * Function: isEdgeValid
- *
- * Checks if the return value of <getEdgeValidationError> for the given
- * arguments is null.
- *
- * Parameters:
- *
- * edge - <mxCell> that represents the edge to validate.
- * source - <mxCell> that represents the source terminal.
- * target - <mxCell> that represents the target terminal.
- */
- mxGraph.prototype.isEdgeValid = function(edge, source, target)
- {
- return this.getEdgeValidationError(edge, source, target) == null;
- };
- /**
- * Function: getEdgeValidationError
- *
- * Returns the validation error message to be displayed when inserting or
- * changing an edges' connectivity. A return value of null means the edge
- * is valid, a return value of '' means it's not valid, but do not display
- * an error message. Any other (non-empty) string returned from this method
- * is displayed as an error message when trying to connect an edge to a
- * source and target. This implementation uses the <multiplicities>, and
- * checks <multigraph>, <allowDanglingEdges> and <allowLoops> to generate
- * validation errors.
- *
- * For extending this method with specific checks for source/target cells,
- * the method can be extended as follows. Returning an empty string means
- * the edge is invalid with no error message, a non-null string specifies
- * the error message, and null means the edge is valid.
- *
- * (code)
- * graph.getEdgeValidationError = function(edge, source, target)
- * {
- * if (source != null && target != null &&
- * this.model.getValue(source) != null &&
- * this.model.getValue(target) != null)
- * {
- * if (target is not valid for source)
- * {
- * return 'Invalid Target';
- * }
- * }
- *
- * // "Supercall"
- * return mxGraph.prototype.getEdgeValidationError.apply(this, arguments);
- * }
- * (end)
- *
- * Parameters:
- *
- * edge - <mxCell> that represents the edge to validate.
- * source - <mxCell> that represents the source terminal.
- * target - <mxCell> that represents the target terminal.
- */
- mxGraph.prototype.getEdgeValidationError = function(edge, source, target)
- {
- if (edge != null && !this.isAllowDanglingEdges() && (source == null || target == null))
- {
- return '';
- }
-
- if (edge != null && this.model.getTerminal(edge, true) == null &&
- this.model.getTerminal(edge, false) == null)
- {
- return null;
- }
-
- // Checks if we're dealing with a loop
- if (!this.allowLoops && source == target && source != null)
- {
- return '';
- }
-
- // Checks if the connection is generally allowed
- if (!this.isValidConnection(source, target))
- {
- return '';
- }
- if (source != null && target != null)
- {
- var error = '';
- // Checks if the cells are already connected
- // and adds an error message if required
- if (!this.multigraph)
- {
- var tmp = this.model.getEdgesBetween(source, target, true);
-
- // Checks if the source and target are not connected by another edge
- if (tmp.length > 1 || (tmp.length == 1 && tmp[0] != edge))
- {
- error += (mxResources.get(this.alreadyConnectedResource) ||
- this.alreadyConnectedResource)+'\n';
- }
- }
- // Gets the number of outgoing edges from the source
- // and the number of incoming edges from the target
- // without counting the edge being currently changed.
- var sourceOut = this.model.getDirectedEdgeCount(source, true, edge);
- var targetIn = this.model.getDirectedEdgeCount(target, false, edge);
- // Checks the change against each multiplicity rule
- if (this.multiplicities != null)
- {
- for (var i = 0; i < this.multiplicities.length; i++)
- {
- var err = this.multiplicities[i].check(this, edge, source,
- target, sourceOut, targetIn);
-
- if (err != null)
- {
- error += err;
- }
- }
- }
- // Validates the source and target terminals independently
- var err = this.validateEdge(edge, source, target);
-
- if (err != null)
- {
- error += err;
- }
-
- return (error.length > 0) ? error : null;
- }
-
- return (this.allowDanglingEdges) ? null : '';
- };
- /**
- * Function: validateEdge
- *
- * Hook method for subclassers to return an error message for the given
- * edge and terminals. This implementation returns null.
- *
- * Parameters:
- *
- * edge - <mxCell> that represents the edge to validate.
- * source - <mxCell> that represents the source terminal.
- * target - <mxCell> that represents the target terminal.
- */
- mxGraph.prototype.validateEdge = function(edge, source, target)
- {
- return null;
- };
- /**
- * Function: validateGraph
- *
- * Validates the graph by validating each descendant of the given cell or
- * the root of the model. Context is an object that contains the validation
- * state for the complete validation run. The validation errors are
- * attached to their cells using <setCellWarning>. Returns null in the case of
- * successful validation or an array of strings (warnings) in the case of
- * failed validations.
- *
- * Paramters:
- *
- * cell - Optional <mxCell> to start the validation recursion. Default is
- * the graph root.
- * context - Object that represents the global validation state.
- */
- mxGraph.prototype.validateGraph = function(cell, context)
- {
- cell = (cell != null) ? cell : this.model.getRoot();
- context = (context != null) ? context : new Object();
-
- var isValid = true;
- var childCount = this.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- var tmp = this.model.getChildAt(cell, i);
- var ctx = context;
-
- if (this.isValidRoot(tmp))
- {
- ctx = new Object();
- }
-
- var warn = this.validateGraph(tmp, ctx);
-
- if (warn != null)
- {
- this.setCellWarning(tmp, warn.replace(/\n/g, '<br>'));
- }
- else
- {
- this.setCellWarning(tmp, null);
- }
-
- isValid = isValid && warn == null;
- }
-
- var warning = '';
-
- // Adds error for invalid children if collapsed (children invisible)
- if (this.isCellCollapsed(cell) && !isValid)
- {
- warning += (mxResources.get(this.containsValidationErrorsResource) ||
- this.containsValidationErrorsResource) + '\n';
- }
-
- // Checks edges and cells using the defined multiplicities
- if (this.model.isEdge(cell))
- {
- warning += this.getEdgeValidationError(cell,
- this.model.getTerminal(cell, true),
- this.model.getTerminal(cell, false)) || '';
- }
- else
- {
- warning += this.getCellValidationError(cell) || '';
- }
-
- // Checks custom validation rules
- var err = this.validateCell(cell, context);
-
- if (err != null)
- {
- warning += err;
- }
-
- // Updates the display with the warning icons
- // before any potential alerts are displayed.
- // LATER: Move this into addCellOverlay. Redraw
- // should check if overlay was added or removed.
- if (this.model.getParent(cell) == null)
- {
- this.view.validate();
- }
- return (warning.length > 0 || !isValid) ? warning : null;
- };
- /**
- * Function: getCellValidationError
- *
- * Checks all <multiplicities> that cannot be enforced while the graph is
- * being modified, namely, all multiplicities that require a minimum of
- * 1 edge.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the multiplicities should be checked.
- */
- mxGraph.prototype.getCellValidationError = function(cell)
- {
- var outCount = this.model.getDirectedEdgeCount(cell, true);
- var inCount = this.model.getDirectedEdgeCount(cell, false);
- var value = this.model.getValue(cell);
- var error = '';
- if (this.multiplicities != null)
- {
- for (var i = 0; i < this.multiplicities.length; i++)
- {
- var rule = this.multiplicities[i];
-
- if (rule.source && mxUtils.isNode(value, rule.type,
- rule.attr, rule.value) && (outCount > rule.max ||
- outCount < rule.min))
- {
- error += rule.countError + '\n';
- }
- else if (!rule.source && mxUtils.isNode(value, rule.type,
- rule.attr, rule.value) && (inCount > rule.max ||
- inCount < rule.min))
- {
- error += rule.countError + '\n';
- }
- }
- }
- return (error.length > 0) ? error : null;
- };
- /**
- * Function: validateCell
- *
- * Hook method for subclassers to return an error message for the given
- * cell and validation context. This implementation returns null. Any HTML
- * breaks will be converted to linefeeds in the calling method.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the cell to validate.
- * context - Object that represents the global validation state.
- */
- mxGraph.prototype.validateCell = function(cell, context)
- {
- return null;
- };
- /**
- * Group: Graph appearance
- */
- /**
- * Function: getBackgroundImage
- *
- * Returns the <backgroundImage> as an <mxImage>.
- */
- mxGraph.prototype.getBackgroundImage = function()
- {
- return this.backgroundImage;
- };
- /**
- * Function: setBackgroundImage
- *
- * Sets the new <backgroundImage>.
- *
- * Parameters:
- *
- * image - New <mxImage> to be used for the background.
- */
- mxGraph.prototype.setBackgroundImage = function(image)
- {
- this.backgroundImage = image;
- };
- /**
- * Function: getFoldingImage
- *
- * Returns the <mxImage> used to display the collapsed state of
- * the specified cell state. This returns null for all edges.
- */
- mxGraph.prototype.getFoldingImage = function(state)
- {
- if (state != null && this.foldingEnabled && !this.getModel().isEdge(state.cell))
- {
- var tmp = this.isCellCollapsed(state.cell);
-
- if (this.isCellFoldable(state.cell, !tmp))
- {
- return (tmp) ? this.collapsedImage : this.expandedImage;
- }
- }
-
- return null;
- };
- /**
- * Function: convertValueToString
- *
- * Returns the textual representation for the given cell. This
- * implementation returns the nodename or string-representation of the user
- * object.
- *
- * Example:
- *
- * The following returns the label attribute from the cells user
- * object if it is an XML node.
- *
- * (code)
- * graph.convertValueToString = function(cell)
- * {
- * return cell.getAttribute('label');
- * }
- * (end)
- *
- * See also: <cellLabelChanged>.
- *
- * Parameters:
- *
- * cell - <mxCell> whose textual representation should be returned.
- */
- mxGraph.prototype.convertValueToString = function(cell)
- {
- var value = this.model.getValue(cell);
-
- if (value != null)
- {
- if (mxUtils.isNode(value))
- {
- return value.nodeName;
- }
- else if (typeof(value.toString) == 'function')
- {
- return value.toString();
- }
- }
-
- return '';
- };
- /**
- * Function: getLabel
- *
- * Returns a string or DOM node that represents the label for the given
- * cell. This implementation uses <convertValueToString> if <labelsVisible>
- * is true. Otherwise it returns an empty string.
- *
- * To truncate a label to match the size of the cell, the following code
- * can be used.
- *
- * (code)
- * graph.getLabel = function(cell)
- * {
- * var label = mxGraph.prototype.getLabel.apply(this, arguments);
- *
- * if (label != null && this.model.isVertex(cell))
- * {
- * var geo = this.getCellGeometry(cell);
- *
- * if (geo != null)
- * {
- * var max = parseInt(geo.width / 8);
- *
- * if (label.length > max)
- * {
- * label = label.substring(0, max)+'...';
- * }
- * }
- * }
- * return mxUtils.htmlEntities(label);
- * }
- * (end)
- *
- * A resize listener is needed in the graph to force a repaint of the label
- * after a resize.
- *
- * (code)
- * graph.addListener(mxEvent.RESIZE_CELLS, function(sender, evt)
- * {
- * var cells = evt.getProperty('cells');
- *
- * for (var i = 0; i < cells.length; i++)
- * {
- * this.view.removeState(cells[i]);
- * }
- * });
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> whose label should be returned.
- */
- mxGraph.prototype.getLabel = function(cell)
- {
- var result = '';
-
- if (this.labelsVisible && cell != null)
- {
- var style = this.getCurrentCellStyle(cell);
-
- if (!mxUtils.getValue(style, mxConstants.STYLE_NOLABEL, false))
- {
- result = this.convertValueToString(cell);
- }
- }
-
- return result;
- };
- /**
- * Function: isHtmlLabel
- *
- * Returns true if the label must be rendered as HTML markup. The default
- * implementation returns <htmlLabels>.
- *
- * Parameters:
- *
- * cell - <mxCell> whose label should be displayed as HTML markup.
- */
- mxGraph.prototype.isHtmlLabel = function(cell)
- {
- return this.isHtmlLabels();
- };
-
- /**
- * Function: isHtmlLabels
- *
- * Returns <htmlLabels>.
- */
- mxGraph.prototype.isHtmlLabels = function()
- {
- return this.htmlLabels;
- };
-
- /**
- * Function: setHtmlLabels
- *
- * Sets <htmlLabels>.
- */
- mxGraph.prototype.setHtmlLabels = function(value)
- {
- this.htmlLabels = value;
- };
- /**
- * Function: isWrapping
- *
- * This enables wrapping for HTML labels.
- *
- * Returns true if no white-space CSS style directive should be used for
- * displaying the given cells label. This implementation returns true if
- * <mxConstants.STYLE_WHITE_SPACE> in the style of the given cell is 'wrap'.
- *
- * This is used as a workaround for IE ignoring the white-space directive
- * of child elements if the directive appears in a parent element. It
- * should be overridden to return true if a white-space directive is used
- * in the HTML markup that represents the given cells label. In order for
- * HTML markup to work in labels, <isHtmlLabel> must also return true
- * for the given cell.
- *
- * Example:
- *
- * (code)
- * graph.getLabel = function(cell)
- * {
- * var tmp = mxGraph.prototype.getLabel.apply(this, arguments); // "supercall"
- *
- * if (this.model.isEdge(cell))
- * {
- * tmp = '<div style="width: 150px; white-space:normal;">'+tmp+'</div>';
- * }
- *
- * return tmp;
- * }
- *
- * graph.isWrapping = function(state)
- * {
- * return this.model.isEdge(state.cell);
- * }
- * (end)
- *
- * Makes sure no edge label is wider than 150 pixels, otherwise the content
- * is wrapped. Note: No width must be specified for wrapped vertex labels as
- * the vertex defines the width in its geometry.
- *
- * Parameters:
- *
- * state - <mxCell> whose label should be wrapped.
- */
- mxGraph.prototype.isWrapping = function(cell)
- {
- return this.getCurrentCellStyle(cell)[mxConstants.STYLE_WHITE_SPACE] == 'wrap';
- };
- /**
- * Function: isLabelClipped
- *
- * Returns true if the overflow portion of labels should be hidden. If this
- * returns true then vertex labels will be clipped to the size of the vertices.
- * This implementation returns true if <mxConstants.STYLE_OVERFLOW> in the
- * style of the given cell is 'hidden'.
- *
- * Parameters:
- *
- * state - <mxCell> whose label should be clipped.
- */
- mxGraph.prototype.isLabelClipped = function(cell)
- {
- return this.getCurrentCellStyle(cell)[mxConstants.STYLE_OVERFLOW] == 'hidden';
- };
- /**
- * Function: getTooltip
- *
- * Returns the string or DOM node that represents the tooltip for the given
- * state, node and coordinate pair. This implementation checks if the given
- * node is a folding icon or overlay and returns the respective tooltip. If
- * this does not result in a tooltip, the handler for the cell is retrieved
- * from <selectionCellsHandler> and the optional getTooltipForNode method is
- * called. If no special tooltip exists here then <getTooltipForCell> is used
- * with the cell in the given state as the argument to return a tooltip for the
- * given state.
- *
- * Parameters:
- *
- * state - <mxCellState> whose tooltip should be returned.
- * node - DOM node that is currently under the mouse.
- * x - X-coordinate of the mouse.
- * y - Y-coordinate of the mouse.
- */
- mxGraph.prototype.getTooltip = function(state, node, x, y)
- {
- var tip = null;
-
- if (state != null)
- {
- // Checks if the mouse is over the folding icon
- if (state.control != null && (node == state.control.node ||
- node.parentNode == state.control.node))
- {
- tip = this.collapseExpandResource;
- tip = mxUtils.htmlEntities(mxResources.get(tip) || tip).replace(/\\n/g, '<br>');
- }
- if (tip == null && state.overlays != null)
- {
- state.overlays.visit(function(id, shape)
- {
- // LATER: Exit loop if tip is not null
- if (tip == null && (node == shape.node || node.parentNode == shape.node))
- {
- tip = shape.overlay.toString();
- }
- });
- }
-
- if (tip == null)
- {
- var handler = this.selectionCellsHandler.getHandler(state.cell);
-
- if (handler != null && typeof(handler.getTooltipForNode) == 'function')
- {
- tip = handler.getTooltipForNode(node);
- }
- }
-
- if (tip == null)
- {
- tip = this.getTooltipForCell(state.cell);
- }
- }
-
- return tip;
- };
- /**
- * Function: getTooltipForCell
- *
- * Returns the string or DOM node to be used as the tooltip for the given
- * cell. This implementation uses the cells getTooltip function if it
- * exists, or else it returns <convertValueToString> for the cell.
- *
- * Example:
- *
- * (code)
- * graph.getTooltipForCell = function(cell)
- * {
- * return 'Hello, World!';
- * }
- * (end)
- *
- * Replaces all tooltips with the string Hello, World!
- *
- * Parameters:
- *
- * cell - <mxCell> whose tooltip should be returned.
- */
- mxGraph.prototype.getTooltipForCell = function(cell)
- {
- var tip = null;
-
- if (cell != null && cell.getTooltip != null)
- {
- tip = cell.getTooltip();
- }
- else
- {
- tip = this.convertValueToString(cell);
- }
-
- return tip;
- };
- /**
- * Function: getLinkForCell
- *
- * Returns the string to be used as the link for the given cell. This
- * implementation returns null.
- *
- * Parameters:
- *
- * cell - <mxCell> whose tooltip should be returned.
- */
- mxGraph.prototype.getLinkForCell = function(cell)
- {
- return null;
- };
- /**
- * Function: getCursorForMouseEvent
- *
- * Returns the cursor value to be used for the CSS of the shape for the
- * given event. This implementation calls <getCursorForCell>.
- *
- * Parameters:
- *
- * me - <mxMouseEvent> whose cursor should be returned.
- */
- mxGraph.prototype.getCursorForMouseEvent = function(me)
- {
- return this.getCursorForCell(me.getCell());
- };
- /**
- * Function: getCursorForCell
- *
- * Returns the cursor value to be used for the CSS of the shape for the
- * given cell. This implementation returns null.
- *
- * Parameters:
- *
- * cell - <mxCell> whose cursor should be returned.
- */
- mxGraph.prototype.getCursorForCell = function(cell)
- {
- return null;
- };
- /**
- * Function: getStartSize
- *
- * Returns the start size of the given swimlane, that is, the width or
- * height of the part that contains the title, depending on the
- * horizontal style. The return value is an <mxRectangle> with either
- * width or height set as appropriate.
- *
- * Parameters:
- *
- * swimlane - <mxCell> whose start size should be returned.
- * ignoreState - Optional boolean that specifies if cell state should be ignored.
- */
- mxGraph.prototype.getStartSize = function(swimlane, ignoreState)
- {
- var result = new mxRectangle();
- var style = this.getCurrentCellStyle(swimlane, ignoreState);
- var size = parseInt(mxUtils.getValue(style,
- mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE));
-
- if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
- {
- result.height = size;
- }
- else
- {
- result.width = size;
- }
-
- return result;
- };
- /**
- * Function: getSwimlaneDirection
- *
- * Returns the direction for the given swimlane style.
- */
- mxGraph.prototype.getSwimlaneDirection = function(style)
- {
- var dir = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
- var flipH = mxUtils.getValue(style, mxConstants.STYLE_FLIPH, 0) == 1;
- var flipV = mxUtils.getValue(style, mxConstants.STYLE_FLIPV, 0) == 1;
- var h = mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true);
- var n = (h) ? 0 : 3;
-
- if (dir == mxConstants.DIRECTION_NORTH)
- {
- n--;
- }
- else if (dir == mxConstants.DIRECTION_WEST)
- {
- n += 2;
- }
- else if (dir == mxConstants.DIRECTION_SOUTH)
- {
- n += 1;
- }
-
- var mod = mxUtils.mod(n, 2);
-
- if (flipH && mod == 1)
- {
- n += 2;
- }
-
- if (flipV && mod == 0)
- {
- n += 2;
- }
-
- return [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST,
- mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST]
- [mxUtils.mod(n, 4)];
- };
- /**
- * Function: getActualStartSize
- *
- * Returns the actual start size of the given swimlane taking into account
- * direction and horizontal and vertial flip styles. The start size is
- * returned as an <mxRectangle> where top, left, bottom, right start sizes
- * are returned as x, y, height and width, respectively.
- *
- * Parameters:
- *
- * swimlane - <mxCell> whose start size should be returned.
- * ignoreState - Optional boolean that specifies if cell state should be ignored.
- */
- mxGraph.prototype.getActualStartSize = function(swimlane, ignoreState)
- {
- var result = new mxRectangle();
-
- if (this.isSwimlane(swimlane, ignoreState))
- {
- var style = this.getCurrentCellStyle(swimlane, ignoreState);
- var size = parseInt(mxUtils.getValue(style, mxConstants.STYLE_STARTSIZE,
- mxConstants.DEFAULT_STARTSIZE));
- var dir = this.getSwimlaneDirection(style);
-
- if (dir == mxConstants.DIRECTION_NORTH)
- {
- result.y = size;
- }
- else if (dir == mxConstants.DIRECTION_WEST)
- {
- result.x = size;
- }
- else if (dir == mxConstants.DIRECTION_SOUTH)
- {
- result.height = size;
- }
- else
- {
- result.width = size;
- }
- }
-
- return result;
- };
- /**
- * Function: getImage
- *
- * Returns the image URL for the given cell state. This implementation
- * returns the value stored under <mxConstants.STYLE_IMAGE> in the cell
- * style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose image URL should be returned.
- */
- mxGraph.prototype.getImage = function(state)
- {
- return (state != null && state.style != null) ? state.style[mxConstants.STYLE_IMAGE] : null;
- };
- /**
- * Function: isTransparentState
- *
- * Returns true if the given state has no stroke- or fillcolor and no image.
- *
- * Parameters:
- *
- * state - <mxCellState> to check.
- */
- mxGraph.prototype.isTransparentState = function(state)
- {
- var result = false;
-
- if (state != null)
- {
- var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
- var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE);
-
- result = stroke == mxConstants.NONE && fill == mxConstants.NONE && this.getImage(state) == null;
-
- }
-
- return result;
- };
- /**
- * Function: getVerticalAlign
- *
- * Returns the vertical alignment for the given cell state. This
- * implementation returns the value stored under
- * <mxConstants.STYLE_VERTICAL_ALIGN> in the cell style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose vertical alignment should be
- * returned.
- */
- mxGraph.prototype.getVerticalAlign = function(state)
- {
- return (state != null && state.style != null) ?
- (state.style[mxConstants.STYLE_VERTICAL_ALIGN] ||
- mxConstants.ALIGN_MIDDLE) : null;
- };
- /**
- * Function: getIndicatorColor
- *
- * Returns the indicator color for the given cell state. This
- * implementation returns the value stored under
- * <mxConstants.STYLE_INDICATOR_COLOR> in the cell style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose indicator color should be
- * returned.
- */
- mxGraph.prototype.getIndicatorColor = function(state)
- {
- return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_COLOR] : null;
- };
- /**
- * Function: getIndicatorGradientColor
- *
- * Returns the indicator gradient color for the given cell state. This
- * implementation returns the value stored under
- * <mxConstants.STYLE_INDICATOR_GRADIENTCOLOR> in the cell style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose indicator gradient color should be
- * returned.
- */
- mxGraph.prototype.getIndicatorGradientColor = function(state)
- {
- return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR] : null;
- };
- /**
- * Function: getIndicatorShape
- *
- * Returns the indicator shape for the given cell state. This
- * implementation returns the value stored under
- * <mxConstants.STYLE_INDICATOR_SHAPE> in the cell style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose indicator shape should be returned.
- */
- mxGraph.prototype.getIndicatorShape = function(state)
- {
- return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_SHAPE] : null;
- };
- /**
- * Function: getIndicatorImage
- *
- * Returns the indicator image for the given cell state. This
- * implementation returns the value stored under
- * <mxConstants.STYLE_INDICATOR_IMAGE> in the cell style.
- *
- * Parameters:
- *
- * state - <mxCellState> whose indicator image should be returned.
- */
- mxGraph.prototype.getIndicatorImage = function(state)
- {
- return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_IMAGE] : null;
- };
- /**
- * Function: getBorder
- *
- * Returns the value of <border>.
- */
- mxGraph.prototype.getBorder = function()
- {
- return this.border;
- };
- /**
- * Function: setBorder
- *
- * Sets the value of <border>.
- *
- * Parameters:
- *
- * value - Positive integer that represents the border to be used.
- */
- mxGraph.prototype.setBorder = function(value)
- {
- this.border = value;
- };
- /**
- * Function: isSwimlane
- *
- * Returns true if the given cell is a swimlane in the graph. A swimlane is
- * a container cell with some specific behaviour. This implementation
- * checks if the shape associated with the given cell is a <mxSwimlane>.
- *
- * Parameters:
- *
- * cell - <mxCell> to be checked.
- * ignoreState - Optional boolean that specifies if the cell state should be ignored.
- */
- mxGraph.prototype.isSwimlane = function(cell, ignoreState)
- {
- if (cell != null && this.model.getParent(cell) != this.model.getRoot() &&
- !this.model.isEdge(cell))
- {
- return this.getCurrentCellStyle(cell, ignoreState)
- [mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_SWIMLANE;
- }
-
- return false;
- };
- /**
- * Group: Graph behaviour
- */
- /**
- * Function: isResizeContainer
- *
- * Returns <resizeContainer>.
- */
- mxGraph.prototype.isResizeContainer = function()
- {
- return this.resizeContainer;
- };
- /**
- * Function: setResizeContainer
- *
- * Sets <resizeContainer>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the container should be resized.
- */
- mxGraph.prototype.setResizeContainer = function(value)
- {
- this.resizeContainer = value;
- };
- /**
- * Function: isEnabled
- *
- * Returns true if the graph is <enabled>.
- */
- mxGraph.prototype.isEnabled = function()
- {
- return this.enabled;
- };
- /**
- * Function: setEnabled
- *
- * Specifies if the graph should allow any interactions. This
- * implementation updates <enabled>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should be enabled.
- */
- mxGraph.prototype.setEnabled = function(value)
- {
- this.enabled = value;
- };
- /**
- * Function: isEscapeEnabled
- *
- * Returns <escapeEnabled>.
- */
- mxGraph.prototype.isEscapeEnabled = function()
- {
- return this.escapeEnabled;
- };
- /**
- * Function: setEscapeEnabled
- *
- * Sets <escapeEnabled>.
- *
- * Parameters:
- *
- * enabled - Boolean indicating if escape should be enabled.
- */
- mxGraph.prototype.setEscapeEnabled = function(value)
- {
- this.escapeEnabled = value;
- };
- /**
- * Function: isInvokesStopCellEditing
- *
- * Returns <invokesStopCellEditing>.
- */
- mxGraph.prototype.isInvokesStopCellEditing = function()
- {
- return this.invokesStopCellEditing;
- };
- /**
- * Function: setInvokesStopCellEditing
- *
- * Sets <invokesStopCellEditing>.
- */
- mxGraph.prototype.setInvokesStopCellEditing = function(value)
- {
- this.invokesStopCellEditing = value;
- };
- /**
- * Function: isEnterStopsCellEditing
- *
- * Returns <enterStopsCellEditing>.
- */
- mxGraph.prototype.isEnterStopsCellEditing = function()
- {
- return this.enterStopsCellEditing;
- };
- /**
- * Function: setEnterStopsCellEditing
- *
- * Sets <enterStopsCellEditing>.
- */
- mxGraph.prototype.setEnterStopsCellEditing = function(value)
- {
- this.enterStopsCellEditing = value;
- };
- /**
- * Function: isCellLocked
- *
- * Returns true if the given cell may not be moved, sized, bended,
- * disconnected, edited or selected. This implementation returns true for
- * all vertices with a relative geometry if <locked> is false.
- *
- * Parameters:
- *
- * cell - <mxCell> whose locked state should be returned.
- */
- mxGraph.prototype.isCellLocked = function(cell)
- {
- var geometry = this.model.getGeometry(cell);
-
- return this.isCellsLocked() || (geometry != null && this.model.isVertex(cell) && geometry.relative);
- };
- /**
- * Function: isCellsLocked
- *
- * Returns true if the given cell may not be moved, sized, bended,
- * disconnected, edited or selected. This implementation returns true for
- * all vertices with a relative geometry if <locked> is false.
- *
- * Parameters:
- *
- * cell - <mxCell> whose locked state should be returned.
- */
- mxGraph.prototype.isCellsLocked = function()
- {
- return this.cellsLocked;
- };
- /**
- * Function: setCellsLocked
- *
- * Sets if any cell may be moved, sized, bended, disconnected, edited or
- * selected.
- *
- * Parameters:
- *
- * value - Boolean that defines the new value for <cellsLocked>.
- */
- mxGraph.prototype.setCellsLocked = function(value)
- {
- this.cellsLocked = value;
- };
- /**
- * Function: getCloneableCells
- *
- * Returns the cells which may be exported in the given array of cells.
- */
- mxGraph.prototype.getCloneableCells = function(cells)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.isCellCloneable(cell);
- }));
- };
- /**
- * Function: isCellCloneable
- *
- * Returns true if the given cell is cloneable. This implementation returns
- * <isCellsCloneable> for all cells unless a cell style specifies
- * <mxConstants.STYLE_CLONEABLE> to be 0.
- *
- * Parameters:
- *
- * cell - Optional <mxCell> whose cloneable state should be returned.
- */
- mxGraph.prototype.isCellCloneable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
- return this.isCellsCloneable() && style[mxConstants.STYLE_CLONEABLE] != 0;
- };
- /**
- * Function: isCellsCloneable
- *
- * Returns <cellsCloneable>, that is, if the graph allows cloning of cells
- * by using control-drag.
- */
- mxGraph.prototype.isCellsCloneable = function()
- {
- return this.cellsCloneable;
- };
- /**
- * Function: setCellsCloneable
- *
- * Specifies if the graph should allow cloning of cells by holding down the
- * control key while cells are being moved. This implementation updates
- * <cellsCloneable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should be cloneable.
- */
- mxGraph.prototype.setCellsCloneable = function(value)
- {
- this.cellsCloneable = value;
- };
- /**
- * Function: getExportableCells
- *
- * Returns the cells which may be exported in the given array of cells.
- */
- mxGraph.prototype.getExportableCells = function(cells)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.canExportCell(cell);
- }));
- };
- /**
- * Function: canExportCell
- *
- * Returns true if the given cell may be exported to the clipboard. This
- * implementation returns <exportEnabled> for all cells.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the cell to be exported.
- */
- mxGraph.prototype.canExportCell = function(cell)
- {
- return this.exportEnabled;
- };
- /**
- * Function: getImportableCells
- *
- * Returns the cells which may be imported in the given array of cells.
- */
- mxGraph.prototype.getImportableCells = function(cells)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.canImportCell(cell);
- }));
- };
- /**
- * Function: canImportCell
- *
- * Returns true if the given cell may be imported from the clipboard.
- * This implementation returns <importEnabled> for all cells.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the cell to be imported.
- */
- mxGraph.prototype.canImportCell = function(cell)
- {
- return this.importEnabled;
- };
- /**
- * Function: isCellSelectable
- *
- * Returns true if the given cell is selectable. This implementation
- * returns <cellsSelectable>.
- *
- * To add a new style for making cells (un)selectable, use the following code.
- *
- * (code)
- * mxGraph.prototype.isCellSelectable = function(cell)
- * {
- * var style = this.getCurrentCellStyle(cell);
- *
- * return this.isCellsSelectable() && !this.isCellLocked(cell) && style['selectable'] != 0;
- * };
- * (end)
- *
- * You can then use the new style as shown in this example.
- *
- * (code)
- * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'selectable=0');
- * (end)
- *
- * Parameters:
- *
- * cell - <mxCell> whose selectable state should be returned.
- */
- mxGraph.prototype.isCellSelectable = function(cell)
- {
- return this.isCellsSelectable();
- };
- /**
- * Function: isCellsSelectable
- *
- * Returns <cellsSelectable>.
- */
- mxGraph.prototype.isCellsSelectable = function()
- {
- return this.cellsSelectable;
- };
- /**
- * Function: setCellsSelectable
- *
- * Sets <cellsSelectable>.
- */
- mxGraph.prototype.setCellsSelectable = function(value)
- {
- this.cellsSelectable = value;
- };
- /**
- * Function: getDeletableCells
- *
- * Returns the cells which may be exported in the given array of cells.
- */
- mxGraph.prototype.getDeletableCells = function(cells)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.isCellDeletable(cell);
- }));
- };
- /**
- * Function: isCellDeletable
- *
- * Returns true if the given cell is moveable. This returns
- * <cellsDeletable> for all given cells if a cells style does not specify
- * <mxConstants.STYLE_DELETABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose deletable state should be returned.
- */
- mxGraph.prototype.isCellDeletable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.isCellsDeletable() && style[mxConstants.STYLE_DELETABLE] != 0;
- };
- /**
- * Function: isCellsDeletable
- *
- * Returns <cellsDeletable>.
- */
- mxGraph.prototype.isCellsDeletable = function()
- {
- return this.cellsDeletable;
- };
- /**
- * Function: setCellsDeletable
- *
- * Sets <cellsDeletable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should allow deletion of cells.
- */
- mxGraph.prototype.setCellsDeletable = function(value)
- {
- this.cellsDeletable = value;
- };
- /**
- * Function: isLabelMovable
- *
- * Returns true if the given edges's label is moveable. This returns
- * <movable> for all given cells if <isLocked> does not return true
- * for the given cell.
- *
- * Parameters:
- *
- * cell - <mxCell> whose label should be moved.
- */
- mxGraph.prototype.isLabelMovable = function(cell)
- {
- return !this.isCellLocked(cell) &&
- ((this.model.isEdge(cell) && this.edgeLabelsMovable) ||
- (this.model.isVertex(cell) && this.vertexLabelsMovable));
- };
- /**
- * Function: isCellRotatable
- *
- * Returns true if the given cell is rotatable. This returns true for the given
- * cell if its style does not specify <mxConstants.STYLE_ROTATABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose rotatable state should be returned.
- */
- mxGraph.prototype.isCellRotatable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return style[mxConstants.STYLE_ROTATABLE] != 0;
- };
- /**
- * Function: getMovableCells
- *
- * Returns the cells which are movable in the given array of cells.
- */
- mxGraph.prototype.getMovableCells = function(cells)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.isCellMovable(cell);
- }));
- };
- /**
- * Function: isCellMovable
- *
- * Returns true if the given cell is moveable. This returns <cellsMovable>
- * for all given cells if <isCellLocked> does not return true for the given
- * cell and its style does not specify <mxConstants.STYLE_MOVABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose movable state should be returned.
- */
- mxGraph.prototype.isCellMovable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.isCellsMovable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_MOVABLE] != 0;
- };
- /**
- * Function: isCellsMovable
- *
- * Returns <cellsMovable>.
- */
- mxGraph.prototype.isCellsMovable = function()
- {
- return this.cellsMovable;
- };
- /**
- * Function: setCellsMovable
- *
- * Specifies if the graph should allow moving of cells. This implementation
- * updates <cellsMsovable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should allow moving of cells.
- */
- mxGraph.prototype.setCellsMovable = function(value)
- {
- this.cellsMovable = value;
- };
- /**
- * Function: isGridEnabled
- *
- * Returns <gridEnabled> as a boolean.
- */
- mxGraph.prototype.isGridEnabled = function()
- {
- return this.gridEnabled;
- };
- /**
- * Function: setGridEnabled
- *
- * Specifies if the grid should be enabled.
- *
- * Parameters:
- *
- * value - Boolean indicating if the grid should be enabled.
- */
- mxGraph.prototype.setGridEnabled = function(value)
- {
- this.gridEnabled = value;
- };
- /**
- * Function: isPortsEnabled
- *
- * Returns <portsEnabled> as a boolean.
- */
- mxGraph.prototype.isPortsEnabled = function()
- {
- return this.portsEnabled;
- };
- /**
- * Function: setPortsEnabled
- *
- * Specifies if the ports should be enabled.
- *
- * Parameters:
- *
- * value - Boolean indicating if the ports should be enabled.
- */
- mxGraph.prototype.setPortsEnabled = function(value)
- {
- this.portsEnabled = value;
- };
- /**
- * Function: getGridSize
- *
- * Returns <gridSize>.
- */
- mxGraph.prototype.getGridSize = function()
- {
- return this.gridSize;
- };
- /**
- * Function: setGridSize
- *
- * Sets <gridSize>.
- */
- mxGraph.prototype.setGridSize = function(value)
- {
- this.gridSize = value;
- };
- /**
- * Function: getTolerance
- *
- * Returns <tolerance>.
- */
- mxGraph.prototype.getTolerance = function()
- {
- return this.tolerance;
- };
- /**
- * Function: setTolerance
- *
- * Sets <tolerance>.
- */
- mxGraph.prototype.setTolerance = function(value)
- {
- this.tolerance = value;
- };
- /**
- * Function: isVertexLabelsMovable
- *
- * Returns <vertexLabelsMovable>.
- */
- mxGraph.prototype.isVertexLabelsMovable = function()
- {
- return this.vertexLabelsMovable;
- };
- /**
- * Function: setVertexLabelsMovable
- *
- * Sets <vertexLabelsMovable>.
- */
- mxGraph.prototype.setVertexLabelsMovable = function(value)
- {
- this.vertexLabelsMovable = value;
- };
- /**
- * Function: isEdgeLabelsMovable
- *
- * Returns <edgeLabelsMovable>.
- */
- mxGraph.prototype.isEdgeLabelsMovable = function()
- {
- return this.edgeLabelsMovable;
- };
- /**
- * Function: isEdgeLabelsMovable
- *
- * Sets <edgeLabelsMovable>.
- */
- mxGraph.prototype.setEdgeLabelsMovable = function(value)
- {
- this.edgeLabelsMovable = value;
- };
- /**
- * Function: isSwimlaneNesting
- *
- * Returns <swimlaneNesting> as a boolean.
- */
- mxGraph.prototype.isSwimlaneNesting = function()
- {
- return this.swimlaneNesting;
- };
- /**
- * Function: setSwimlaneNesting
- *
- * Specifies if swimlanes can be nested by drag and drop. This is only
- * taken into account if dropEnabled is true.
- *
- * Parameters:
- *
- * value - Boolean indicating if swimlanes can be nested.
- */
- mxGraph.prototype.setSwimlaneNesting = function(value)
- {
- this.swimlaneNesting = value;
- };
- /**
- * Function: isSwimlaneSelectionEnabled
- *
- * Returns <swimlaneSelectionEnabled> as a boolean.
- */
- mxGraph.prototype.isSwimlaneSelectionEnabled = function()
- {
- return this.swimlaneSelectionEnabled;
- };
- /**
- * Function: setSwimlaneSelectionEnabled
- *
- * Specifies if swimlanes should be selected if the mouse is released
- * over their content area.
- *
- * Parameters:
- *
- * value - Boolean indicating if swimlanes content areas
- * should be selected when the mouse is released over them.
- */
- mxGraph.prototype.setSwimlaneSelectionEnabled = function(value)
- {
- this.swimlaneSelectionEnabled = value;
- };
- /**
- * Function: isMultigraph
- *
- * Returns <multigraph> as a boolean.
- */
- mxGraph.prototype.isMultigraph = function()
- {
- return this.multigraph;
- };
- /**
- * Function: setMultigraph
- *
- * Specifies if the graph should allow multiple connections between the
- * same pair of vertices.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph allows multiple connections
- * between the same pair of vertices.
- */
- mxGraph.prototype.setMultigraph = function(value)
- {
- this.multigraph = value;
- };
- /**
- * Function: isAllowLoops
- *
- * Returns <allowLoops> as a boolean.
- */
- mxGraph.prototype.isAllowLoops = function()
- {
- return this.allowLoops;
- };
- /**
- * Function: setAllowDanglingEdges
- *
- * Specifies if dangling edges are allowed, that is, if edges are allowed
- * that do not have a source and/or target terminal defined.
- *
- * Parameters:
- *
- * value - Boolean indicating if dangling edges are allowed.
- */
- mxGraph.prototype.setAllowDanglingEdges = function(value)
- {
- this.allowDanglingEdges = value;
- };
- /**
- * Function: isAllowDanglingEdges
- *
- * Returns <allowDanglingEdges> as a boolean.
- */
- mxGraph.prototype.isAllowDanglingEdges = function()
- {
- return this.allowDanglingEdges;
- };
- /**
- * Function: setConnectableEdges
- *
- * Specifies if edges should be connectable.
- *
- * Parameters:
- *
- * value - Boolean indicating if edges should be connectable.
- */
- mxGraph.prototype.setConnectableEdges = function(value)
- {
- this.connectableEdges = value;
- };
- /**
- * Function: isConnectableEdges
- *
- * Returns <connectableEdges> as a boolean.
- */
- mxGraph.prototype.isConnectableEdges = function()
- {
- return this.connectableEdges;
- };
- /**
- * Function: setCloneInvalidEdges
- *
- * Specifies if edges should be inserted when cloned but not valid wrt.
- * <getEdgeValidationError>. If false such edges will be silently ignored.
- *
- * Parameters:
- *
- * value - Boolean indicating if cloned invalid edges should be
- * inserted into the graph or ignored.
- */
- mxGraph.prototype.setCloneInvalidEdges = function(value)
- {
- this.cloneInvalidEdges = value;
- };
- /**
- * Function: isCloneInvalidEdges
- *
- * Returns <cloneInvalidEdges> as a boolean.
- */
- mxGraph.prototype.isCloneInvalidEdges = function()
- {
- return this.cloneInvalidEdges;
- };
- /**
- * Function: setAllowLoops
- *
- * Specifies if loops are allowed.
- *
- * Parameters:
- *
- * value - Boolean indicating if loops are allowed.
- */
- mxGraph.prototype.setAllowLoops = function(value)
- {
- this.allowLoops = value;
- };
- /**
- * Function: isDisconnectOnMove
- *
- * Returns <disconnectOnMove> as a boolean.
- */
- mxGraph.prototype.isDisconnectOnMove = function()
- {
- return this.disconnectOnMove;
- };
- /**
- * Function: setDisconnectOnMove
- *
- * Specifies if edges should be disconnected when moved. (Note: Cloned
- * edges are always disconnected.)
- *
- * Parameters:
- *
- * value - Boolean indicating if edges should be disconnected
- * when moved.
- */
- mxGraph.prototype.setDisconnectOnMove = function(value)
- {
- this.disconnectOnMove = value;
- };
- /**
- * Function: isDropEnabled
- *
- * Returns <dropEnabled> as a boolean.
- */
- mxGraph.prototype.isDropEnabled = function()
- {
- return this.dropEnabled;
- };
- /**
- * Function: setDropEnabled
- *
- * Specifies if the graph should allow dropping of cells onto or into other
- * cells.
- *
- * Parameters:
- *
- * dropEnabled - Boolean indicating if the graph should allow dropping
- * of cells into other cells.
- */
- mxGraph.prototype.setDropEnabled = function(value)
- {
- this.dropEnabled = value;
- };
- /**
- * Function: isSplitEnabled
- *
- * Returns <splitEnabled> as a boolean.
- */
- mxGraph.prototype.isSplitEnabled = function()
- {
- return this.splitEnabled;
- };
- /**
- * Function: setSplitEnabled
- *
- * Specifies if the graph should allow dropping of cells onto or into other
- * cells.
- *
- * Parameters:
- *
- * dropEnabled - Boolean indicating if the graph should allow dropping
- * of cells into other cells.
- */
- mxGraph.prototype.setSplitEnabled = function(value)
- {
- this.splitEnabled = value;
- };
- /**
- * Function: isCellResizable
- *
- * Returns true if the given cell is resizable. This returns
- * <cellsResizable> for all given cells if <isCellLocked> does not return
- * true for the given cell and its style does not specify
- * <mxConstants.STYLE_RESIZABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose resizable state should be returned.
- */
- mxGraph.prototype.isCellResizable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
- return this.isCellsResizable() && !this.isCellLocked(cell) &&
- mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0';
- };
- /**
- * Function: isCellsResizable
- *
- * Returns <cellsResizable>.
- */
- mxGraph.prototype.isCellsResizable = function()
- {
- return this.cellsResizable;
- };
- /**
- * Function: setCellsResizable
- *
- * Specifies if the graph should allow resizing of cells. This
- * implementation updates <cellsResizable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should allow resizing of
- * cells.
- */
- mxGraph.prototype.setCellsResizable = function(value)
- {
- this.cellsResizable = value;
- };
- /**
- * Function: isTerminalPointMovable
- *
- * Returns true if the given terminal point is movable. This is independent
- * from <isCellConnectable> and <isCellDisconnectable> and controls if terminal
- * points can be moved in the graph if the edge is not connected. Note that it
- * is required for this to return true to connect unconnected edges. This
- * implementation returns true.
- *
- * Parameters:
- *
- * cell - <mxCell> whose terminal point should be moved.
- * source - Boolean indicating if the source or target terminal should be moved.
- */
- mxGraph.prototype.isTerminalPointMovable = function(cell, source)
- {
- return true;
- };
- /**
- * Function: isCellBendable
- *
- * Returns true if the given cell is bendable. This returns <cellsBendable>
- * for all given cells if <isLocked> does not return true for the given
- * cell and its style does not specify <mxConstants.STYLE_BENDABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose bendable state should be returned.
- */
- mxGraph.prototype.isCellBendable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.isCellsBendable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_BENDABLE] != 0;
- };
- /**
- * Function: isCellsBendable
- *
- * Returns <cellsBenadable>.
- */
- mxGraph.prototype.isCellsBendable = function()
- {
- return this.cellsBendable;
- };
- /**
- * Function: setCellsBendable
- *
- * Specifies if the graph should allow bending of edges. This
- * implementation updates <bendable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should allow bending of
- * edges.
- */
- mxGraph.prototype.setCellsBendable = function(value)
- {
- this.cellsBendable = value;
- };
- /**
- * Function: isCellEditable
- *
- * Returns true if the given cell is editable. This returns <cellsEditable> for
- * all given cells if <isCellLocked> does not return true for the given cell
- * and its style does not specify <mxConstants.STYLE_EDITABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose editable state should be returned.
- */
- mxGraph.prototype.isCellEditable = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.isCellsEditable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_EDITABLE] != 0;
- };
- /**
- * Function: isCellsEditable
- *
- * Returns <cellsEditable>.
- */
- mxGraph.prototype.isCellsEditable = function()
- {
- return this.cellsEditable;
- };
- /**
- * Function: setCellsEditable
- *
- * Specifies if the graph should allow in-place editing for cell labels.
- * This implementation updates <cellsEditable>.
- *
- * Parameters:
- *
- * value - Boolean indicating if the graph should allow in-place
- * editing.
- */
- mxGraph.prototype.setCellsEditable = function(value)
- {
- this.cellsEditable = value;
- };
- /**
- * Function: isCellDisconnectable
- *
- * Returns true if the given cell is disconnectable from the source or
- * target terminal. This returns <isCellsDisconnectable> for all given
- * cells if <isCellLocked> does not return true for the given cell.
- *
- * Parameters:
- *
- * cell - <mxCell> whose disconnectable state should be returned.
- * terminal - <mxCell> that represents the source or target terminal.
- * source - Boolean indicating if the source or target terminal is to be
- * disconnected.
- */
- mxGraph.prototype.isCellDisconnectable = function(cell, terminal, source)
- {
- return this.isCellsDisconnectable() && !this.isCellLocked(cell);
- };
- /**
- * Function: isCellsDisconnectable
- *
- * Returns <cellsDisconnectable>.
- */
- mxGraph.prototype.isCellsDisconnectable = function()
- {
- return this.cellsDisconnectable;
- };
- /**
- * Function: setCellsDisconnectable
- *
- * Sets <cellsDisconnectable>.
- */
- mxGraph.prototype.setCellsDisconnectable = function(value)
- {
- this.cellsDisconnectable = value;
- };
- /**
- * Function: isValidSource
- *
- * Returns true if the given cell is a valid source for new connections.
- * This implementation returns true for all non-null values and is
- * called by is called by <isValidConnection>.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents a possible source or null.
- */
- mxGraph.prototype.isValidSource = function(cell)
- {
- return (cell == null && this.allowDanglingEdges) ||
- (cell != null && (!this.model.isEdge(cell) ||
- this.connectableEdges) && this.isCellConnectable(cell));
- };
-
- /**
- * Function: isValidTarget
- *
- * Returns <isValidSource> for the given cell. This is called by
- * <isValidConnection>.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents a possible target or null.
- */
- mxGraph.prototype.isValidTarget = function(cell)
- {
- return this.isValidSource(cell);
- };
- /**
- * Function: isValidConnection
- *
- * Returns true if the given target cell is a valid target for source.
- * This is a boolean implementation for not allowing connections between
- * certain pairs of vertices and is called by <getEdgeValidationError>.
- * This implementation returns true if <isValidSource> returns true for
- * the source and <isValidTarget> returns true for the target.
- *
- * Parameters:
- *
- * source - <mxCell> that represents the source cell.
- * target - <mxCell> that represents the target cell.
- */
- mxGraph.prototype.isValidConnection = function(source, target)
- {
- return this.isValidSource(source) && this.isValidTarget(target);
- };
- /**
- * Function: setConnectable
- *
- * Specifies if the graph should allow new connections. This implementation
- * updates <mxConnectionHandler.enabled> in <connectionHandler>.
- *
- * Parameters:
- *
- * connectable - Boolean indicating if new connections should be allowed.
- */
- mxGraph.prototype.setConnectable = function(connectable)
- {
- this.connectionHandler.setEnabled(connectable);
- };
-
- /**
- * Function: isConnectable
- *
- * Returns true if the <connectionHandler> is enabled.
- */
- mxGraph.prototype.isConnectable = function()
- {
- return this.connectionHandler.isEnabled();
- };
- /**
- * Function: setTooltips
- *
- * Specifies if tooltips should be enabled. This implementation updates
- * <mxTooltipHandler.enabled> in <tooltipHandler>.
- *
- * Parameters:
- *
- * enabled - Boolean indicating if tooltips should be enabled.
- */
- mxGraph.prototype.setTooltips = function (enabled)
- {
- this.tooltipHandler.setEnabled(enabled);
- };
- /**
- * Function: setPanning
- *
- * Specifies if panning should be enabled. This implementation updates
- * <mxPanningHandler.panningEnabled> in <panningHandler>.
- *
- * Parameters:
- *
- * enabled - Boolean indicating if panning should be enabled.
- */
- mxGraph.prototype.setPanning = function(enabled)
- {
- this.panningHandler.panningEnabled = enabled;
- };
- /**
- * Function: isEditing
- *
- * Returns true if the given cell is currently being edited.
- * If no cell is specified then this returns true if any
- * cell is currently being edited.
- *
- * Parameters:
- *
- * cell - <mxCell> that should be checked.
- */
- mxGraph.prototype.isEditing = function(cell)
- {
- if (this.cellEditor != null)
- {
- var editingCell = this.cellEditor.getEditingCell();
-
- return (cell == null) ? editingCell != null : cell == editingCell;
- }
-
- return false;
- };
- /**
- * Function: isAutoSizeCell
- *
- * Returns true if the size of the given cell should automatically be
- * updated after a change of the label. This implementation returns
- * <autoSizeCells> or checks if the cell style does specify
- * <mxConstants.STYLE_AUTOSIZE> to be 1.
- *
- * Parameters:
- *
- * cell - <mxCell> that should be resized.
- */
- mxGraph.prototype.isAutoSizeCell = function(cell)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.isAutoSizeCells() || style[mxConstants.STYLE_AUTOSIZE] == 1;
- };
- /**
- * Function: isAutoSizeCells
- *
- * Returns <autoSizeCells>.
- */
- mxGraph.prototype.isAutoSizeCells = function()
- {
- return this.autoSizeCells;
- };
- /**
- * Function: setAutoSizeCells
- *
- * Specifies if cell sizes should be automatically updated after a label
- * change. This implementation sets <autoSizeCells> to the given parameter.
- * To update the size of cells when the cells are added, set
- * <autoSizeCellsOnAdd> to true.
- *
- * Parameters:
- *
- * value - Boolean indicating if cells should be resized
- * automatically.
- */
- mxGraph.prototype.setAutoSizeCells = function(value)
- {
- this.autoSizeCells = value;
- };
- /**
- * Function: isExtendParent
- *
- * Returns true if the parent of the given cell should be extended if the
- * child has been resized so that it overlaps the parent. This
- * implementation returns <isExtendParents> if the cell is not an edge.
- *
- * Parameters:
- *
- * cell - <mxCell> that has been resized.
- */
- mxGraph.prototype.isExtendParent = function(cell)
- {
- return !this.getModel().isEdge(cell) && this.isExtendParents();
- };
- /**
- * Function: isExtendParents
- *
- * Returns <extendParents>.
- */
- mxGraph.prototype.isExtendParents = function()
- {
- return this.extendParents;
- };
- /**
- * Function: setExtendParents
- *
- * Sets <extendParents>.
- *
- * Parameters:
- *
- * value - New boolean value for <extendParents>.
- */
- mxGraph.prototype.setExtendParents = function(value)
- {
- this.extendParents = value;
- };
- /**
- * Function: isExtendParentsOnAdd
- *
- * Returns <extendParentsOnAdd>.
- */
- mxGraph.prototype.isExtendParentsOnAdd = function(cell)
- {
- return this.extendParentsOnAdd;
- };
- /**
- * Function: setExtendParentsOnAdd
- *
- * Sets <extendParentsOnAdd>.
- *
- * Parameters:
- *
- * value - New boolean value for <extendParentsOnAdd>.
- */
- mxGraph.prototype.setExtendParentsOnAdd = function(value)
- {
- this.extendParentsOnAdd = value;
- };
- /**
- * Function: isExtendParentsOnMove
- *
- * Returns <extendParentsOnMove>.
- */
- mxGraph.prototype.isExtendParentsOnMove = function()
- {
- return this.extendParentsOnMove;
- };
- /**
- * Function: setExtendParentsOnMove
- *
- * Sets <extendParentsOnMove>.
- *
- * Parameters:
- *
- * value - New boolean value for <extendParentsOnAdd>.
- */
- mxGraph.prototype.setExtendParentsOnMove = function(value)
- {
- this.extendParentsOnMove = value;
- };
- /**
- * Function: isRecursiveResize
- *
- * Returns <recursiveResize>.
- *
- * Parameters:
- *
- * state - <mxCellState> that is being resized.
- */
- mxGraph.prototype.isRecursiveResize = function(state)
- {
- return this.recursiveResize;
- };
- /**
- * Function: setRecursiveResize
- *
- * Sets <recursiveResize>.
- *
- * Parameters:
- *
- * value - New boolean value for <recursiveResize>.
- */
- mxGraph.prototype.setRecursiveResize = function(value)
- {
- this.recursiveResize = value;
- };
- /**
- * Function: isConstrainChild
- *
- * Returns true if the given cell should be kept inside the bounds of its
- * parent according to the rules defined by <getOverlap> and
- * <isAllowOverlapParent>. This implementation returns false for all children
- * of edges and <isConstrainChildren> otherwise.
- *
- * Parameters:
- *
- * cell - <mxCell> that should be constrained.
- */
- mxGraph.prototype.isConstrainChild = function(cell)
- {
- return this.isConstrainChildren() && !this.getModel().isEdge(this.getModel().getParent(cell));
- };
- /**
- * Function: isConstrainChildren
- *
- * Returns <constrainChildren>.
- */
- mxGraph.prototype.isConstrainChildren = function()
- {
- return this.constrainChildren;
- };
- /**
- * Function: setConstrainChildren
- *
- * Sets <constrainChildren>.
- */
- mxGraph.prototype.setConstrainChildren = function(value)
- {
- this.constrainChildren = value;
- };
- /**
- * Function: isConstrainRelativeChildren
- *
- * Returns <constrainRelativeChildren>.
- */
- mxGraph.prototype.isConstrainRelativeChildren = function()
- {
- return this.constrainRelativeChildren;
- };
- /**
- * Function: setConstrainRelativeChildren
- *
- * Sets <constrainRelativeChildren>.
- */
- mxGraph.prototype.setConstrainRelativeChildren = function(value)
- {
- this.constrainRelativeChildren = value;
- };
- /**
- * Function: isConstrainChildren
- *
- * Returns <allowNegativeCoordinates>.
- */
- mxGraph.prototype.isAllowNegativeCoordinates = function()
- {
- return this.allowNegativeCoordinates;
- };
- /**
- * Function: setConstrainChildren
- *
- * Sets <allowNegativeCoordinates>.
- */
- mxGraph.prototype.setAllowNegativeCoordinates = function(value)
- {
- this.allowNegativeCoordinates = value;
- };
- /**
- * Function: getOverlap
- *
- * Returns a decimal number representing the amount of the width and height
- * of the given cell that is allowed to overlap its parent. A value of 0
- * means all children must stay inside the parent, 1 means the child is
- * allowed to be placed outside of the parent such that it touches one of
- * the parents sides. If <isAllowOverlapParent> returns false for the given
- * cell, then this method returns 0.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the overlap ratio should be returned.
- */
- mxGraph.prototype.getOverlap = function(cell)
- {
- return (this.isAllowOverlapParent(cell)) ? this.defaultOverlap : 0;
- };
-
- /**
- * Function: isAllowOverlapParent
- *
- * Returns true if the given cell is allowed to be placed outside of the
- * parents area.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the child to be checked.
- */
- mxGraph.prototype.isAllowOverlapParent = function(cell)
- {
- return false;
- };
- /**
- * Function: getFoldableCells
- *
- * Returns the cells which are movable in the given array of cells.
- */
- mxGraph.prototype.getFoldableCells = function(cells, collapse)
- {
- return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
- {
- return this.isCellFoldable(cell, collapse);
- }));
- };
- /**
- * Function: isCellFoldable
- *
- * Returns true if the given cell is foldable. This implementation
- * returns true if the cell has at least one child and its style
- * does not specify <mxConstants.STYLE_FOLDABLE> to be 0.
- *
- * Parameters:
- *
- * cell - <mxCell> whose foldable state should be returned.
- */
- mxGraph.prototype.isCellFoldable = function(cell, collapse)
- {
- var style = this.getCurrentCellStyle(cell);
-
- return this.model.getChildCount(cell) > 0 && style[mxConstants.STYLE_FOLDABLE] != 0;
- };
- /**
- * Function: isValidDropTarget
- *
- * Returns true if the given cell is a valid drop target for the specified
- * cells. If <splitEnabled> is true then this returns <isSplitTarget> for
- * the given arguments else it returns true if the cell is not collapsed
- * and its child count is greater than 0.
- *
- * Parameters:
- *
- * cell - <mxCell> that represents the possible drop target.
- * cells - <mxCells> that should be dropped into the target.
- * evt - Mouseevent that triggered the invocation.
- */
- mxGraph.prototype.isValidDropTarget = function(cell, cells, evt)
- {
- return cell != null && ((this.isSplitEnabled() &&
- this.isSplitTarget(cell, cells, evt)) || (!this.model.isEdge(cell) &&
- (this.isSwimlane(cell) || (this.model.getChildCount(cell) > 0 &&
- !this.isCellCollapsed(cell)))));
- };
- /**
- * Function: isSplitTarget
- *
- * Returns true if the given edge may be splitted into two edges with the
- * given cell as a new terminal between the two.
- *
- * Parameters:
- *
- * target - <mxCell> that represents the edge to be splitted.
- * cells - <mxCells> that should split the edge.
- * evt - Mouseevent that triggered the invocation.
- */
- mxGraph.prototype.isSplitTarget = function(target, cells, evt)
- {
- if (this.model.isEdge(target) && cells != null && cells.length == 1 &&
- this.isCellConnectable(cells[0]) && this.getEdgeValidationError(target,
- this.model.getTerminal(target, true), cells[0]) == null)
- {
- var src = this.model.getTerminal(target, true);
- var trg = this.model.getTerminal(target, false);
- return (!this.model.isAncestor(cells[0], src) &&
- !this.model.isAncestor(cells[0], trg));
- }
- return false;
- };
- /**
- * Function: getDropTarget
- *
- * Returns the given cell if it is a drop target for the given cells or the
- * nearest ancestor that may be used as a drop target for the given cells.
- * If the given array contains a swimlane and <swimlaneNesting> is false
- * then this always returns null. If no cell is given, then the bottommost
- * swimlane at the location of the given event is returned.
- *
- * This function should only be used if <isDropEnabled> returns true.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> which are to be dropped onto the target.
- * evt - Mouseevent for the drag and drop.
- * cell - <mxCell> that is under the mousepointer.
- * clone - Optional boolean to indicate of cells will be cloned.
- */
- mxGraph.prototype.getDropTarget = function(cells, evt, cell, clone)
- {
- if (!this.isSwimlaneNesting())
- {
- for (var i = 0; i < cells.length; i++)
- {
- if (this.isSwimlane(cells[i]))
- {
- return null;
- }
- }
- }
- var pt = mxUtils.convertPoint(this.container,
- mxEvent.getClientX(evt), mxEvent.getClientY(evt));
- pt.x -= this.panDx;
- pt.y -= this.panDy;
- var swimlane = this.getSwimlaneAt(pt.x, pt.y);
-
- if (cell == null)
- {
- cell = swimlane;
- }
- else if (swimlane != null)
- {
- // Checks if the cell is an ancestor of the swimlane
- // under the mouse and uses the swimlane in that case
- var tmp = this.model.getParent(swimlane);
-
- while (tmp != null && this.isSwimlane(tmp) && tmp != cell)
- {
- tmp = this.model.getParent(tmp);
- }
-
- if (tmp == cell)
- {
- cell = swimlane;
- }
- }
-
- while (cell != null && !this.isValidDropTarget(cell, cells, evt) &&
- !this.model.isLayer(cell))
- {
- cell = this.model.getParent(cell);
- }
-
- // Checks if parent is dropped into child if not cloning
- if (clone == null || !clone)
- {
- var parent = cell;
-
- while (parent != null && mxUtils.indexOf(cells, parent) < 0)
- {
- parent = this.model.getParent(parent);
- }
- }
- return (!this.model.isLayer(cell) && parent == null) ? cell : null;
- };
- /**
- * Group: Cell retrieval
- */
- /**
- * Function: getDefaultParent
- *
- * Returns <defaultParent> or <mxGraphView.currentRoot> or the first child
- * child of <mxGraphModel.root> if both are null. The value returned by
- * this function should be used as the parent for new cells (aka default
- * layer).
- */
- mxGraph.prototype.getDefaultParent = function()
- {
- var parent = this.getCurrentRoot();
-
- if (parent == null)
- {
- parent = this.defaultParent;
-
- if (parent == null)
- {
- var root = this.model.getRoot();
- parent = this.model.getChildAt(root, 0);
- }
- }
-
- return parent;
- };
- /**
- * Function: setDefaultParent
- *
- * Sets the <defaultParent> to the given cell. Set this to null to return
- * the first child of the root in getDefaultParent.
- */
- mxGraph.prototype.setDefaultParent = function(cell)
- {
- this.defaultParent = cell;
- };
- /**
- * Function: getSwimlane
- *
- * Returns the nearest ancestor of the given cell which is a swimlane, or
- * the given cell, if it is itself a swimlane.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the ancestor swimlane should be returned.
- */
- mxGraph.prototype.getSwimlane = function(cell)
- {
- while (cell != null && !this.isSwimlane(cell))
- {
- cell = this.model.getParent(cell);
- }
-
- return cell;
- };
- /**
- * Function: getSwimlaneAt
- *
- * Returns the bottom-most swimlane that intersects the given point (x, y)
- * in the cell hierarchy that starts at the given parent.
- *
- * Parameters:
- *
- * x - X-coordinate of the location to be checked.
- * y - Y-coordinate of the location to be checked.
- * parent - <mxCell> that should be used as the root of the recursion.
- * Default is <defaultParent>.
- */
- mxGraph.prototype.getSwimlaneAt = function (x, y, parent)
- {
- if (parent == null)
- {
- parent = this.getCurrentRoot();
-
- if (parent == null)
- {
- parent = this.model.getRoot();
- }
- }
-
- if (parent != null)
- {
- var childCount = this.model.getChildCount(parent);
-
- for (var i = 0; i < childCount; i++)
- {
- var child = this.model.getChildAt(parent, i);
-
- if (child != null)
- {
- var result = this.getSwimlaneAt(x, y, child);
-
- if (result != null)
- {
- return result;
- }
- else if (this.isCellVisible(child) && this.isSwimlane(child))
- {
- var state = this.view.getState(child);
-
- if (this.intersects(state, x, y))
- {
- return child;
- }
- }
- }
- }
- }
-
- return null;
- };
- /**
- * Function: getCellAt
- *
- * Returns the bottom-most cell that intersects the given point (x, y) in
- * the cell hierarchy starting at the given parent. This will also return
- * swimlanes if the given location intersects the content area of the
- * swimlane. If this is not desired, then the <hitsSwimlaneContent> may be
- * used if the returned cell is a swimlane to determine if the location
- * is inside the content area or on the actual title of the swimlane.
- *
- * Parameters:
- *
- * x - X-coordinate of the location to be checked.
- * y - Y-coordinate of the location to be checked.
- * parent - <mxCell> that should be used as the root of the recursion.
- * Default is current root of the view or the root of the model.
- * vertices - Optional boolean indicating if vertices should be returned.
- * Default is true.
- * edges - Optional boolean indicating if edges should be returned. Default
- * is true.
- * ignoreFn - Optional function that returns true if cell should be ignored.
- * The function is passed the cell state and the x and y parameter.
- */
- mxGraph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn)
- {
- vertices = (vertices != null) ? vertices : true;
- edges = (edges != null) ? edges : true;
- if (parent == null)
- {
- parent = this.getCurrentRoot();
-
- if (parent == null)
- {
- parent = this.getModel().getRoot();
- }
- }
- if (parent != null)
- {
- var childCount = this.model.getChildCount(parent);
-
- for (var i = childCount - 1; i >= 0; i--)
- {
- var cell = this.model.getChildAt(parent, i);
- var result = this.getCellAt(x, y, cell, vertices, edges, ignoreFn);
-
- if (result != null)
- {
- return result;
- }
- else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) ||
- vertices && this.model.isVertex(cell)))
- {
- var state = this.view.getState(cell);
- if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) &&
- this.intersects(state, x, y))
- {
- return cell;
- }
- }
- }
- }
-
- return null;
- };
- /**
- * Function: intersects
- *
- * Returns the bottom-most cell that intersects the given point (x, y) in
- * the cell hierarchy that starts at the given parent.
- *
- * Parameters:
- *
- * state - <mxCellState> that represents the cell state.
- * x - X-coordinate of the location to be checked.
- * y - Y-coordinate of the location to be checked.
- */
- mxGraph.prototype.intersects = function(state, x, y)
- {
- if (state != null)
- {
- var pts = state.absolutePoints;
- if (pts != null)
- {
- var t2 = this.tolerance * this.tolerance;
- var pt = pts[0];
-
- for (var i = 1; i < pts.length; i++)
- {
- var next = pts[i];
- var dist = mxUtils.ptSegDistSq(pt.x, pt.y, next.x, next.y, x, y);
-
- if (dist <= t2)
- {
- return true;
- }
-
- pt = next;
- }
- }
- else
- {
- var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0);
-
- if (alpha != 0)
- {
- var cos = Math.cos(-alpha);
- var sin = Math.sin(-alpha);
- var cx = new mxPoint(state.getCenterX(), state.getCenterY());
- var pt = mxUtils.getRotatedPoint(new mxPoint(x, y), cos, sin, cx);
- x = pt.x;
- y = pt.y;
- }
-
- if (mxUtils.contains(state, x, y))
- {
- return true;
- }
- }
- }
-
- return false;
- };
- /**
- * Function: hitsSwimlaneContent
- *
- * Returns true if the given coordinate pair is inside the content
- * are of the given swimlane.
- *
- * Parameters:
- *
- * swimlane - <mxCell> that specifies the swimlane.
- * x - X-coordinate of the mouse event.
- * y - Y-coordinate of the mouse event.
- */
- mxGraph.prototype.hitsSwimlaneContent = function(swimlane, x, y)
- {
- var state = this.getView().getState(swimlane);
- var size = this.getStartSize(swimlane);
-
- if (state != null)
- {
- var scale = this.getView().getScale();
- x -= state.x;
- y -= state.y;
-
- if (size.width > 0 && x > 0 && x > size.width * scale)
- {
- return true;
- }
- else if (size.height > 0 && y > 0 && y > size.height * scale)
- {
- return true;
- }
- }
-
- return false;
- };
- /**
- * Function: getChildVertices
- *
- * Returns the visible child vertices of the given parent.
- *
- * Parameters:
- *
- * parent - <mxCell> whose children should be returned.
- */
- mxGraph.prototype.getChildVertices = function(parent)
- {
- return this.getChildCells(parent, true, false);
- };
-
- /**
- * Function: getChildEdges
- *
- * Returns the visible child edges of the given parent.
- *
- * Parameters:
- *
- * parent - <mxCell> whose child vertices should be returned.
- */
- mxGraph.prototype.getChildEdges = function(parent)
- {
- return this.getChildCells(parent, false, true);
- };
- /**
- * Function: getChildCells
- *
- * Returns the visible child vertices or edges in the given parent. If
- * vertices and edges is false, then all children are returned.
- *
- * Parameters:
- *
- * parent - <mxCell> whose children should be returned.
- * vertices - Optional boolean that specifies if child vertices should
- * be returned. Default is false.
- * edges - Optional boolean that specifies if child edges should
- * be returned. Default is false.
- */
- mxGraph.prototype.getChildCells = function(parent, vertices, edges)
- {
- parent = (parent != null) ? parent : this.getDefaultParent();
- vertices = (vertices != null) ? vertices : false;
- edges = (edges != null) ? edges : false;
- var cells = this.model.getChildCells(parent, vertices, edges);
- var result = [];
- // Filters out the non-visible child cells
- for (var i = 0; i < cells.length; i++)
- {
- if (this.isCellVisible(cells[i]))
- {
- result.push(cells[i]);
- }
- }
- return result;
- };
-
- /**
- * Function: getConnections
- *
- * Returns all visible edges connected to the given cell without loops.
- *
- * Parameters:
- *
- * cell - <mxCell> whose connections should be returned.
- * parent - Optional parent of the opposite end for a connection to be
- * returned.
- */
- mxGraph.prototype.getConnections = function(cell, parent)
- {
- return this.getEdges(cell, parent, true, true, false);
- };
-
- /**
- * Function: getIncomingEdges
- *
- * Returns the visible incoming edges for the given cell. If the optional
- * parent argument is specified, then only child edges of the given parent
- * are returned.
- *
- * Parameters:
- *
- * cell - <mxCell> whose incoming edges should be returned.
- * parent - Optional parent of the opposite end for an edge to be
- * returned.
- */
- mxGraph.prototype.getIncomingEdges = function(cell, parent)
- {
- return this.getEdges(cell, parent, true, false, false);
- };
-
- /**
- * Function: getOutgoingEdges
- *
- * Returns the visible outgoing edges for the given cell. If the optional
- * parent argument is specified, then only child edges of the given parent
- * are returned.
- *
- * Parameters:
- *
- * cell - <mxCell> whose outgoing edges should be returned.
- * parent - Optional parent of the opposite end for an edge to be
- * returned.
- */
- mxGraph.prototype.getOutgoingEdges = function(cell, parent)
- {
- return this.getEdges(cell, parent, false, true, false);
- };
-
- /**
- * Function: getEdges
- *
- * Returns the incoming and/or outgoing edges for the given cell.
- * If the optional parent argument is specified, then only edges are returned
- * where the opposite is in the given parent cell. If at least one of incoming
- * or outgoing is true, then loops are ignored, if both are false, then all
- * edges connected to the given cell are returned including loops.
- *
- * Parameters:
- *
- * cell - <mxCell> whose edges should be returned.
- * parent - Optional parent of the opposite end for an edge to be
- * returned.
- * incoming - Optional boolean that specifies if incoming edges should
- * be included in the result. Default is true.
- * outgoing - Optional boolean that specifies if outgoing edges should
- * be included in the result. Default is true.
- * includeLoops - Optional boolean that specifies if loops should be
- * included in the result. Default is true.
- * recurse - Optional boolean the specifies if the parent specified only
- * need be an ancestral parent, true, or the direct parent, false.
- * Default is false
- */
- mxGraph.prototype.getEdges = function(cell, parent, incoming, outgoing, includeLoops, recurse)
- {
- incoming = (incoming != null) ? incoming : true;
- outgoing = (outgoing != null) ? outgoing : true;
- includeLoops = (includeLoops != null) ? includeLoops : true;
- recurse = (recurse != null) ? recurse : false;
-
- var edges = [];
- var isCollapsed = this.isCellCollapsed(cell);
- var childCount = this.model.getChildCount(cell);
- for (var i = 0; i < childCount; i++)
- {
- var child = this.model.getChildAt(cell, i);
- if (isCollapsed || !this.isCellVisible(child))
- {
- edges = edges.concat(this.model.getEdges(child, incoming, outgoing));
- }
- }
- edges = edges.concat(this.model.getEdges(cell, incoming, outgoing));
- var result = [];
-
- for (var i = 0; i < edges.length; i++)
- {
- var state = this.view.getState(edges[i]);
-
- var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
- var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
- if ((includeLoops && source == target) || ((source != target) && ((incoming &&
- target == cell && (parent == null || this.isValidAncestor(source, parent, recurse))) ||
- (outgoing && source == cell && (parent == null ||
- this.isValidAncestor(target, parent, recurse))))))
- {
- result.push(edges[i]);
- }
- }
- return result;
- };
- /**
- * Function: isValidAncestor
- *
- * Returns whether or not the specified parent is a valid
- * ancestor of the specified cell, either direct or indirectly
- * based on whether ancestor recursion is enabled.
- *
- * Parameters:
- *
- * cell - <mxCell> the possible child cell
- * parent - <mxCell> the possible parent cell
- * recurse - boolean whether or not to recurse the child ancestors
- */
- mxGraph.prototype.isValidAncestor = function(cell, parent, recurse)
- {
- return (recurse ? this.model.isAncestor(parent, cell) : this.model
- .getParent(cell) == parent);
- };
- /**
- * Function: getOpposites
- *
- * Returns all distinct visible opposite cells for the specified terminal
- * on the given edges.
- *
- * Parameters:
- *
- * edges - Array of <mxCells> that contains the edges whose opposite
- * terminals should be returned.
- * terminal - Terminal that specifies the end whose opposite should be
- * returned.
- * sources - Optional boolean that specifies if source terminals should be
- * included in the result. Default is true.
- * targets - Optional boolean that specifies if targer terminals should be
- * included in the result. Default is true.
- */
- mxGraph.prototype.getOpposites = function(edges, terminal, sources, targets)
- {
- sources = (sources != null) ? sources : true;
- targets = (targets != null) ? targets : true;
-
- var terminals = [];
-
- // Fast lookup to avoid duplicates in terminals array
- var dict = new mxDictionary();
-
- if (edges != null)
- {
- for (var i = 0; i < edges.length; i++)
- {
- var state = this.view.getState(edges[i]);
-
- var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
- var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
-
- // Checks if the terminal is the source of the edge and if the
- // target should be stored in the result
- if (source == terminal && target != null && target != terminal && targets)
- {
- if (!dict.get(target))
- {
- dict.put(target, true);
- terminals.push(target);
- }
- }
-
- // Checks if the terminal is the taget of the edge and if the
- // source should be stored in the result
- else if (target == terminal && source != null && source != terminal && sources)
- {
- if (!dict.get(source))
- {
- dict.put(source, true);
- terminals.push(source);
- }
- }
- }
- }
-
- return terminals;
- };
- /**
- * Function: getEdgesBetween
- *
- * Returns the edges between the given source and target. This takes into
- * account collapsed and invisible cells and returns the connected edges
- * as displayed on the screen.
- *
- * Parameters:
- *
- * source -
- * target -
- * directed -
- */
- mxGraph.prototype.getEdgesBetween = function(source, target, directed)
- {
- directed = (directed != null) ? directed : false;
- var edges = this.getEdges(source);
- var result = [];
- // Checks if the edge is connected to the correct
- // cell and returns the first match
- for (var i = 0; i < edges.length; i++)
- {
- var state = this.view.getState(edges[i]);
-
- var src = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
- var trg = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
- if ((src == source && trg == target) || (!directed && src == target && trg == source))
- {
- result.push(edges[i]);
- }
- }
- return result;
- };
- /**
- * Function: getPointForEvent
- *
- * Returns an <mxPoint> representing the given event in the unscaled,
- * non-translated coordinate space of <container> and applies the grid.
- *
- * Parameters:
- *
- * evt - Mousevent that contains the mouse pointer location.
- * addOffset - Optional boolean that specifies if the position should be
- * offset by half of the <gridSize>. Default is true.
- */
- mxGraph.prototype.getPointForEvent = function(evt, addOffset)
- {
- var p = mxUtils.convertPoint(this.container,
- mxEvent.getClientX(evt), mxEvent.getClientY(evt));
-
- var s = this.view.scale;
- var tr = this.view.translate;
- var off = (addOffset != false) ? this.gridSize / 2 : 0;
-
- p.x = this.snap(p.x / s - tr.x - off);
- p.y = this.snap(p.y / s - tr.y - off);
-
- return p;
- };
- /**
- * Function: getCells
- *
- * Returns the child vertices and edges of the given parent that are contained
- * in the given rectangle. The result is added to the optional result array,
- * which is returned. If no result array is specified then a new array is
- * created and returned.
- *
- * Parameters:
- *
- * x - X-coordinate of the rectangle.
- * y - Y-coordinate of the rectangle.
- * width - Width of the rectangle.
- * height - Height of the rectangle.
- * parent - <mxCell> that should be used as the root of the recursion.
- * Default is current root of the view or the root of the model.
- * result - Optional array to store the result in.
- * intersection - Optional <mxRectangle> to check vertices for intersection.
- * ignoreFn - Optional function to check if a cell state is ignored.
- * includeDescendants - Optional boolean flag to add descendants to the result.
- * Default is false.
- */
- mxGraph.prototype.getCells = function(x, y, width, height, parent, result, intersection, ignoreFn, includeDescendants)
- {
- result = (result != null) ? result : [];
-
- if (width > 0 || height > 0 || intersection != null)
- {
- var model = this.getModel();
- var right = x + width;
- var bottom = y + height;
- if (parent == null)
- {
- parent = this.getCurrentRoot();
-
- if (parent == null)
- {
- parent = model.getRoot();
- }
- }
-
- if (parent != null)
- {
- var childCount = model.getChildCount(parent);
-
- for (var i = 0; i < childCount; i++)
- {
- var cell = model.getChildAt(parent, i);
- var state = this.view.getState(cell);
-
- if (state != null && this.isCellVisible(cell) &&
- (ignoreFn == null || !ignoreFn(state)))
- {
- var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0;
- var box = state;
-
- if (deg != 0)
- {
- box = mxUtils.getBoundingBox(box, deg);
- }
-
- var hit = (intersection != null && model.isVertex(cell) && mxUtils.intersects(intersection, box)) ||
- (intersection == null && (model.isEdge(cell) || model.isVertex(cell)) &&
- box.x >= x && box.y + box.height <= bottom &&
- box.y >= y && box.x + box.width <= right);
-
- if (hit)
- {
- result.push(cell);
- }
-
- if (!hit || includeDescendants)
- {
- this.getCells(x, y, width, height, cell, result, intersection, ignoreFn, includeDescendants);
- }
- }
- }
- }
- }
-
- return result;
- };
- /**
- * Function: getCellsBeyond
- *
- * Returns the children of the given parent that are contained in the
- * halfpane from the given point (x0, y0) rightwards or downwards
- * depending on rightHalfpane and bottomHalfpane.
- *
- * Parameters:
- *
- * x0 - X-coordinate of the origin.
- * y0 - Y-coordinate of the origin.
- * parent - Optional <mxCell> whose children should be checked. Default is
- * <defaultParent>.
- * rightHalfpane - Boolean indicating if the cells in the right halfpane
- * from the origin should be returned.
- * bottomHalfpane - Boolean indicating if the cells in the bottom halfpane
- * from the origin should be returned.
- */
- mxGraph.prototype.getCellsBeyond = function(x0, y0, parent, rightHalfpane, bottomHalfpane)
- {
- var result = [];
-
- if (rightHalfpane || bottomHalfpane)
- {
- if (parent == null)
- {
- parent = this.getDefaultParent();
- }
-
- if (parent != null)
- {
- var childCount = this.model.getChildCount(parent);
-
- for (var i = 0; i < childCount; i++)
- {
- var child = this.model.getChildAt(parent, i);
- var state = this.view.getState(child);
-
- if (this.isCellVisible(child) && state != null)
- {
- if ((!rightHalfpane || state.x >= x0) &&
- (!bottomHalfpane || state.y >= y0))
- {
- result.push(child);
- }
- }
- }
- }
- }
-
- return result;
- };
- /**
- * Function: findTreeRoots
- *
- * Returns all children in the given parent which do not have incoming
- * edges. If the result is empty then the with the greatest difference
- * between incoming and outgoing edges is returned.
- *
- * Parameters:
- *
- * parent - <mxCell> whose children should be checked.
- * isolate - Optional boolean that specifies if edges should be ignored if
- * the opposite end is not a child of the given parent cell. Default is
- * false.
- * invert - Optional boolean that specifies if outgoing or incoming edges
- * should be counted for a tree root. If false then outgoing edges will be
- * counted. Default is false.
- */
- mxGraph.prototype.findTreeRoots = function(parent, isolate, invert)
- {
- isolate = (isolate != null) ? isolate : false;
- invert = (invert != null) ? invert : false;
- var roots = [];
-
- if (parent != null)
- {
- var model = this.getModel();
- var childCount = model.getChildCount(parent);
- var best = null;
- var maxDiff = 0;
-
- for (var i=0; i<childCount; i++)
- {
- var cell = model.getChildAt(parent, i);
-
- if (this.model.isVertex(cell) && this.isCellVisible(cell))
- {
- var conns = this.getConnections(cell, (isolate) ? parent : null);
- var fanOut = 0;
- var fanIn = 0;
-
- for (var j = 0; j < conns.length; j++)
- {
- var src = this.view.getVisibleTerminal(conns[j], true);
- if (src == cell)
- {
- fanOut++;
- }
- else
- {
- fanIn++;
- }
- }
-
- if ((invert && fanOut == 0 && fanIn > 0) ||
- (!invert && fanIn == 0 && fanOut > 0))
- {
- roots.push(cell);
- }
-
- var diff = (invert) ? fanIn - fanOut : fanOut - fanIn;
-
- if (diff > maxDiff)
- {
- maxDiff = diff;
- best = cell;
- }
- }
- }
-
- if (roots.length == 0 && best != null)
- {
- roots.push(best);
- }
- }
-
- return roots;
- };
- /**
- * Function: traverse
- *
- * Traverses the (directed) graph invoking the given function for each
- * visited vertex and edge. The function is invoked with the current vertex
- * and the incoming edge as a parameter. This implementation makes sure
- * each vertex is only visited once. The function may return false if the
- * traversal should stop at the given vertex.
- *
- * Example:
- *
- * (code)
- * mxLog.show();
- * var cell = graph.getSelectionCell();
- * graph.traverse(cell, false, function(vertex, edge)
- * {
- * mxLog.debug(graph.getLabel(vertex));
- * });
- * (end)
- *
- * Parameters:
- *
- * vertex - <mxCell> that represents the vertex where the traversal starts.
- * directed - Optional boolean indicating if edges should only be traversed
- * from source to target. Default is true.
- * func - Visitor function that takes the current vertex and the incoming
- * edge as arguments. The traversal stops if the function returns false.
- * edge - Optional <mxCell> that represents the incoming edge. This is
- * null for the first step of the traversal.
- * visited - Optional <mxDictionary> from cells to true for the visited cells.
- * inverse - Optional boolean to traverse in inverse direction. Default is false.
- * This is ignored if directed is false.
- */
- mxGraph.prototype.traverse = function(vertex, directed, func, edge, visited, inverse)
- {
- if (func != null && vertex != null)
- {
- directed = (directed != null) ? directed : true;
- inverse = (inverse != null) ? inverse : false;
- visited = visited || new mxDictionary();
-
- if (!visited.get(vertex))
- {
- visited.put(vertex, true);
- var result = func(vertex, edge);
-
- if (result == null || result)
- {
- var edgeCount = this.model.getEdgeCount(vertex);
-
- if (edgeCount > 0)
- {
- for (var i = 0; i < edgeCount; i++)
- {
- var e = this.model.getEdgeAt(vertex, i);
- var isSource = this.model.getTerminal(e, true) == vertex;
-
- if (!directed || (!inverse == isSource))
- {
- var next = this.model.getTerminal(e, !isSource);
- this.traverse(next, directed, func, e, visited, inverse);
- }
- }
- }
- }
- }
- }
- };
- /**
- * Group: Selection
- */
- /**
- * Function: isCellSelected
- *
- * Returns true if the given cell is selected.
- *
- * Parameters:
- *
- * cell - <mxCell> for which the selection state should be returned.
- */
- mxGraph.prototype.isCellSelected = function(cell)
- {
- return this.getSelectionModel().isSelected(cell);
- };
- /**
- * Function: isSelectionEmpty
- *
- * Returns true if the selection is empty.
- */
- mxGraph.prototype.isSelectionEmpty = function()
- {
- return this.getSelectionModel().isEmpty();
- };
- /**
- * Function: clearSelection
- *
- * Clears the selection using <mxGraphSelectionModel.clear>.
- */
- mxGraph.prototype.clearSelection = function()
- {
- return this.getSelectionModel().clear();
- };
- /**
- * Function: getSelectionCount
- *
- * Returns the number of selected cells.
- */
- mxGraph.prototype.getSelectionCount = function()
- {
- return this.getSelectionModel().cells.length;
- };
-
- /**
- * Function: getSelectionCell
- *
- * Returns the first cell from the array of selected <mxCells>.
- */
- mxGraph.prototype.getSelectionCell = function()
- {
- return this.getSelectionModel().cells[0];
- };
- /**
- * Function: getSelectionCells
- *
- * Returns the array of selected <mxCells>.
- */
- mxGraph.prototype.getSelectionCells = function()
- {
- return this.getSelectionModel().cells.slice();
- };
- /**
- * Function: setSelectionCell
- *
- * Sets the selection cell.
- *
- * Parameters:
- *
- * cell - <mxCell> to be selected.
- */
- mxGraph.prototype.setSelectionCell = function(cell)
- {
- this.getSelectionModel().setCell(cell);
- };
- /**
- * Function: setSelectionCells
- *
- * Sets the selection cell.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be selected.
- */
- mxGraph.prototype.setSelectionCells = function(cells)
- {
- this.getSelectionModel().setCells(cells);
- };
- /**
- * Function: addSelectionCell
- *
- * Adds the given cell to the selection.
- *
- * Parameters:
- *
- * cell - <mxCell> to be add to the selection.
- */
- mxGraph.prototype.addSelectionCell = function(cell)
- {
- this.getSelectionModel().addCell(cell);
- };
- /**
- * Function: addSelectionCells
- *
- * Adds the given cells to the selection.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be added to the selection.
- */
- mxGraph.prototype.addSelectionCells = function(cells)
- {
- this.getSelectionModel().addCells(cells);
- };
- /**
- * Function: removeSelectionCell
- *
- * Removes the given cell from the selection.
- *
- * Parameters:
- *
- * cell - <mxCell> to be removed from the selection.
- */
- mxGraph.prototype.removeSelectionCell = function(cell)
- {
- this.getSelectionModel().removeCell(cell);
- };
- /**
- * Function: removeSelectionCells
- *
- * Removes the given cells from the selection.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be removed from the selection.
- */
- mxGraph.prototype.removeSelectionCells = function(cells)
- {
- this.getSelectionModel().removeCells(cells);
- };
- /**
- * Function: selectRegion
- *
- * Selects and returns the cells inside the given rectangle for the
- * specified event.
- *
- * Parameters:
- *
- * rect - <mxRectangle> that represents the region to be selected.
- * evt - Mouseevent that triggered the selection.
- */
- mxGraph.prototype.selectRegion = function(rect, evt)
- {
- var cells = this.getCells(rect.x, rect.y, rect.width, rect.height);
- this.selectCellsForEvent(cells, evt);
-
- return cells;
- };
- /**
- * Function: selectNextCell
- *
- * Selects the next cell.
- */
- mxGraph.prototype.selectNextCell = function()
- {
- this.selectCell(true);
- };
- /**
- * Function: selectPreviousCell
- *
- * Selects the previous cell.
- */
- mxGraph.prototype.selectPreviousCell = function()
- {
- this.selectCell();
- };
- /**
- * Function: selectParentCell
- *
- * Selects the parent cell.
- */
- mxGraph.prototype.selectParentCell = function()
- {
- this.selectCell(false, true);
- };
- /**
- * Function: selectChildCell
- *
- * Selects the first child cell.
- */
- mxGraph.prototype.selectChildCell = function()
- {
- this.selectCell(false, false, true);
- };
- /**
- * Function: selectCell
- *
- * Selects the next, parent, first child or previous cell, if all arguments
- * are false.
- *
- * Parameters:
- *
- * isNext - Boolean indicating if the next cell should be selected.
- * isParent - Boolean indicating if the parent cell should be selected.
- * isChild - Boolean indicating if the first child cell should be selected.
- */
- mxGraph.prototype.selectCell = function(isNext, isParent, isChild)
- {
- var sel = this.selectionModel;
- var cell = (sel.cells.length > 0) ? sel.cells[0] : null;
-
- if (sel.cells.length > 1)
- {
- sel.clear();
- }
-
- var parent = (cell != null) ?
- this.model.getParent(cell) :
- this.getDefaultParent();
-
- var childCount = this.model.getChildCount(parent);
-
- if (cell == null && childCount > 0)
- {
- var child = this.model.getChildAt(parent, 0);
- this.setSelectionCell(child);
- }
- else if ((cell == null || isParent) &&
- this.view.getState(parent) != null &&
- this.model.getGeometry(parent) != null)
- {
- if (this.getCurrentRoot() != parent)
- {
- this.setSelectionCell(parent);
- }
- }
- else if (cell != null && isChild)
- {
- var tmp = this.model.getChildCount(cell);
-
- if (tmp > 0)
- {
- var child = this.model.getChildAt(cell, 0);
- this.setSelectionCell(child);
- }
- }
- else if (childCount > 0)
- {
- var i = parent.getIndex(cell);
-
- if (isNext)
- {
- i++;
- var child = this.model.getChildAt(parent, i % childCount);
- this.setSelectionCell(child);
- }
- else
- {
- i--;
- var index = (i < 0) ? childCount - 1 : i;
- var child = this.model.getChildAt(parent, index);
- this.setSelectionCell(child);
- }
- }
- };
- /**
- * Function: selectAll
- *
- * Selects all children of the given parent cell or the children of the
- * default parent if no parent is specified. To select leaf vertices and/or
- * edges use <selectCells>.
- *
- * Parameters:
- *
- * parent - Optional <mxCell> whose children should be selected.
- * Default is <defaultParent>.
- * descendants - Optional boolean specifying whether all descendants should be
- * selected. Default is false.
- */
- mxGraph.prototype.selectAll = function(parent, descendants)
- {
- parent = parent || this.getDefaultParent();
-
- var cells = (descendants) ? this.model.filterDescendants(mxUtils.bind(this, function(cell)
- {
- return cell != parent && this.view.getState(cell) != null;
- }), parent) : this.model.getChildren(parent);
-
- if (cells != null)
- {
- this.setSelectionCells(cells);
- }
- };
- /**
- * Function: selectVertices
- *
- * Select all vertices inside the given parent or the default parent.
- */
- mxGraph.prototype.selectVertices = function(parent, selectGroups)
- {
- this.selectCells(true, false, parent, selectGroups);
- };
- /**
- * Function: selectVertices
- *
- * Select all vertices inside the given parent or the default parent.
- */
- mxGraph.prototype.selectEdges = function(parent)
- {
- this.selectCells(false, true, parent);
- };
- /**
- * Function: selectCells
- *
- * Selects all vertices and/or edges depending on the given boolean
- * arguments recursively, starting at the given parent or the default
- * parent if no parent is specified. Use <selectAll> to select all cells.
- * For vertices, only cells with no children are selected.
- *
- * Parameters:
- *
- * vertices - Boolean indicating if vertices should be selected.
- * edges - Boolean indicating if edges should be selected.
- * parent - Optional <mxCell> that acts as the root of the recursion.
- * Default is <defaultParent>.
- * selectGroups - Optional boolean that specifies if groups should be
- * selected. Default is false.
- */
- mxGraph.prototype.selectCells = function(vertices, edges, parent, selectGroups)
- {
- parent = parent || this.getDefaultParent();
-
- var filter = mxUtils.bind(this, function(cell)
- {
- return this.view.getState(cell) != null &&
- (((selectGroups || this.model.getChildCount(cell) == 0) &&
- this.model.isVertex(cell) && vertices
- && !this.model.isEdge(this.model.getParent(cell))) ||
- (this.model.isEdge(cell) && edges));
- });
-
- var cells = this.model.filterDescendants(filter, parent);
-
- if (cells != null)
- {
- this.setSelectionCells(cells);
- }
- };
- /**
- * Function: selectCellForEvent
- *
- * Selects the given cell by either adding it to the selection or
- * replacing the selection depending on whether the given mouse event is a
- * toggle event.
- *
- * Parameters:
- *
- * cell - <mxCell> to be selected.
- * evt - Optional mouseevent that triggered the selection.
- */
- mxGraph.prototype.selectCellForEvent = function(cell, evt)
- {
- var isSelected = this.isCellSelected(cell);
-
- if (this.isToggleEvent(evt))
- {
- if (isSelected)
- {
- this.removeSelectionCell(cell);
- }
- else
- {
- this.addSelectionCell(cell);
- }
- }
- else if (!isSelected || this.getSelectionCount() != 1)
- {
- this.setSelectionCell(cell);
- }
- };
- /**
- * Function: selectCellsForEvent
- *
- * Selects the given cells by either adding them to the selection or
- * replacing the selection depending on whether the given mouse event is a
- * toggle event.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> to be selected.
- * evt - Optional mouseevent that triggered the selection.
- */
- mxGraph.prototype.selectCellsForEvent = function(cells, evt)
- {
- if (this.isToggleEvent(evt))
- {
- this.addSelectionCells(cells);
- }
- else
- {
- this.setSelectionCells(cells);
- }
- };
- /**
- * Group: Selection state
- */
- /**
- * Function: createHandler
- *
- * Creates a new handler for the given cell state. This implementation
- * returns a new <mxEdgeHandler> of the corresponding cell is an edge,
- * otherwise it returns an <mxVertexHandler>.
- *
- * Parameters:
- *
- * state - <mxCellState> whose handler should be created.
- */
- mxGraph.prototype.createHandler = function(state)
- {
- var result = null;
-
- if (state != null)
- {
- if (this.model.isEdge(state.cell))
- {
- var source = state.getVisibleTerminalState(true);
- var target = state.getVisibleTerminalState(false);
- var geo = this.getCellGeometry(state.cell);
-
- var edgeStyle = this.view.getEdgeStyle(state, (geo != null) ? geo.points : null, source, target);
- result = this.createEdgeHandler(state, edgeStyle);
- }
- else
- {
- result = this.createVertexHandler(state);
- }
- }
-
- return result;
- };
- /**
- * Function: createVertexHandler
- *
- * Hooks to create a new <mxVertexHandler> for the given <mxCellState>.
- *
- * Parameters:
- *
- * state - <mxCellState> to create the handler for.
- */
- mxGraph.prototype.createVertexHandler = function(state)
- {
- return new mxVertexHandler(state);
- };
- /**
- * Function: createEdgeHandler
- *
- * Hooks to create a new <mxEdgeHandler> for the given <mxCellState>.
- *
- * Parameters:
- *
- * state - <mxCellState> to create the handler for.
- */
- mxGraph.prototype.createEdgeHandler = function(state, edgeStyle)
- {
- var result = null;
-
- if (edgeStyle == mxEdgeStyle.Loop ||
- edgeStyle == mxEdgeStyle.ElbowConnector ||
- edgeStyle == mxEdgeStyle.SideToSide ||
- edgeStyle == mxEdgeStyle.TopToBottom)
- {
- result = this.createElbowEdgeHandler(state);
- }
- else if (edgeStyle == mxEdgeStyle.SegmentConnector ||
- edgeStyle == mxEdgeStyle.OrthConnector)
- {
- result = this.createEdgeSegmentHandler(state);
- }
- else
- {
- result = new mxEdgeHandler(state);
- }
-
- return result;
- };
- /**
- * Function: createEdgeSegmentHandler
- *
- * Hooks to create a new <mxEdgeSegmentHandler> for the given <mxCellState>.
- *
- * Parameters:
- *
- * state - <mxCellState> to create the handler for.
- */
- mxGraph.prototype.createEdgeSegmentHandler = function(state)
- {
- return new mxEdgeSegmentHandler(state);
- };
- /**
- * Function: createElbowEdgeHandler
- *
- * Hooks to create a new <mxElbowEdgeHandler> for the given <mxCellState>.
- *
- * Parameters:
- *
- * state - <mxCellState> to create the handler for.
- */
- mxGraph.prototype.createElbowEdgeHandler = function(state)
- {
- return new mxElbowEdgeHandler(state);
- };
- /**
- * Group: Graph events
- */
- /**
- * Function: addMouseListener
- *
- * Adds a listener to the graph event dispatch loop. The listener
- * must implement the mouseDown, mouseMove and mouseUp methods
- * as shown in the <mxMouseEvent> class.
- *
- * Parameters:
- *
- * listener - Listener to be added to the graph event listeners.
- */
- mxGraph.prototype.addMouseListener = function(listener)
- {
- if (this.mouseListeners == null)
- {
- this.mouseListeners = [];
- }
-
- this.mouseListeners.push(listener);
- };
- /**
- * Function: removeMouseListener
- *
- * Removes the specified graph listener.
- *
- * Parameters:
- *
- * listener - Listener to be removed from the graph event listeners.
- */
- mxGraph.prototype.removeMouseListener = function(listener)
- {
- if (this.mouseListeners != null)
- {
- for (var i = 0; i < this.mouseListeners.length; i++)
- {
- if (this.mouseListeners[i] == listener)
- {
- this.mouseListeners.splice(i, 1);
- break;
- }
- }
- }
- };
- /**
- * Function: updateMouseEvent
- *
- * Sets the graphX and graphY properties if the given <mxMouseEvent> if
- * required and returned the event.
- *
- * Parameters:
- *
- * me - <mxMouseEvent> to be updated.
- * evtName - Name of the mouse event.
- */
- mxGraph.prototype.updateMouseEvent = function(me, evtName)
- {
- if (me.graphX == null || me.graphY == null)
- {
- var pt = mxUtils.convertPoint(this.container, me.getX(), me.getY());
-
- me.graphX = pt.x - this.panDx;
- me.graphY = pt.y - this.panDy;
-
- // Searches for rectangles using method if native hit detection is disabled on shape
- if (me.getCell() == null && this.isMouseDown && evtName == mxEvent.MOUSE_MOVE)
- {
- me.state = this.view.getState(this.getCellAt(pt.x, pt.y, null, null, null, function(state)
- {
- return state.shape == null || state.shape.paintBackground != mxRectangleShape.prototype.paintBackground ||
- mxUtils.getValue(state.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1' ||
- (state.shape.fill != null && state.shape.fill != mxConstants.NONE);
- }));
- }
- }
-
- return me;
- };
- /**
- * Function: getStateForEvent
- *
- * Returns the state for the given touch event.
- */
- mxGraph.prototype.getStateForTouchEvent = function(evt)
- {
- var x = mxEvent.getClientX(evt);
- var y = mxEvent.getClientY(evt);
-
- // Dispatches the drop event to the graph which
- // consumes and executes the source function
- var pt = mxUtils.convertPoint(this.container, x, y);
- return this.view.getState(this.getCellAt(pt.x, pt.y));
- };
- /**
- * Function: isEventIgnored
- *
- * Returns true if the event should be ignored in <fireMouseEvent>.
- */
- mxGraph.prototype.isEventIgnored = function(evtName, me, sender)
- {
- var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
- var result = false;
- // Drops events that are fired more than once
- if (me.getEvent() == this.lastEvent)
- {
- result = true;
- }
- else
- {
- this.lastEvent = me.getEvent();
- }
- // Installs event listeners to capture the complete gesture from the event source
- // for non-MS touch events as a workaround for all events for the same geture being
- // fired from the event source even if that was removed from the DOM.
- if (this.eventSource != null && evtName != mxEvent.MOUSE_MOVE)
- {
- mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
- this.mouseMoveRedirect = null;
- this.mouseUpRedirect = null;
- this.eventSource = null;
- }
- else if (!mxClient.IS_GC && this.eventSource != null && me.getSource() != this.eventSource)
- {
- result = true;
- }
- else if (mxClient.IS_TOUCH && evtName == mxEvent.MOUSE_DOWN &&
- !mouseEvent && !mxEvent.isPenEvent(me.getEvent()))
- {
- this.eventSource = me.getSource();
- this.mouseMoveRedirect = mxUtils.bind(this, function(evt)
- {
- this.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
- });
- this.mouseUpRedirect = mxUtils.bind(this, function(evt)
- {
- this.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
- });
-
- mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
- }
- // Factored out the workarounds for FF to make it easier to override/remove
- // Note this method has side-effects!
- if (this.isSyntheticEventIgnored(evtName, me, sender))
- {
- result = true;
- }
- // Never fires mouseUp/-Down for double clicks
- if (!mxEvent.isPopupTrigger(this.lastEvent) && evtName != mxEvent.MOUSE_MOVE && this.lastEvent.detail == 2)
- {
- return true;
- }
-
- // Filters out of sequence events or mixed event types during a gesture
- if (evtName == mxEvent.MOUSE_UP && this.isMouseDown)
- {
- this.isMouseDown = false;
- }
- else if (evtName == mxEvent.MOUSE_DOWN && !this.isMouseDown)
- {
- this.isMouseDown = true;
- this.isMouseTrigger = mouseEvent;
- }
- // Drops mouse events that are fired during touch gestures as a workaround for Webkit
- // and mouse events that are not in sync with the current internal button state
- else if (!result && (((!mxClient.IS_FF || evtName != mxEvent.MOUSE_MOVE) &&
- this.isMouseDown && this.isMouseTrigger != mouseEvent) ||
- (evtName == mxEvent.MOUSE_DOWN && this.isMouseDown) ||
- (evtName == mxEvent.MOUSE_UP && !this.isMouseDown)))
- {
- result = true;
- }
-
- if (!result && evtName == mxEvent.MOUSE_DOWN)
- {
- this.lastMouseX = me.getX();
- this.lastMouseY = me.getY();
- }
- return result;
- };
- /**
- * Function: isSyntheticEventIgnored
- *
- * Hook for ignoring synthetic mouse events after touchend in Firefox.
- */
- mxGraph.prototype.isSyntheticEventIgnored = function(evtName, me, sender)
- {
- var result = false;
- var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
-
- // LATER: This does not cover all possible cases that can go wrong in FF
- if (this.ignoreMouseEvents && mouseEvent && evtName != mxEvent.MOUSE_MOVE)
- {
- this.ignoreMouseEvents = evtName != mxEvent.MOUSE_UP;
- result = true;
- }
- else if (mxClient.IS_FF && !mouseEvent && evtName == mxEvent.MOUSE_UP)
- {
- this.ignoreMouseEvents = true;
- }
-
- return result;
- };
- /**
- * Function: isEventSourceIgnored
- *
- * Returns true if the event should be ignored in <fireMouseEvent>. This
- * implementation returns true for select, option and input (if not of type
- * checkbox, radio, button, submit or file) event sources if the event is not
- * a mouse event or a left mouse button press event.
- *
- * Parameters:
- *
- * evtName - The name of the event.
- * me - <mxMouseEvent> that should be ignored.
- */
- mxGraph.prototype.isEventSourceIgnored = function(evtName, me)
- {
- var source = me.getSource();
- var name = (source.nodeName != null) ? source.nodeName.toLowerCase() : '';
- var candidate = !mxEvent.isMouseEvent(me.getEvent()) || mxEvent.isLeftMouseButton(me.getEvent());
-
- return evtName == mxEvent.MOUSE_DOWN && candidate && (name == 'select' || name == 'option' ||
- (name == 'input' && source.type != 'checkbox' && source.type != 'radio' &&
- source.type != 'button' && source.type != 'submit' && source.type != 'file'));
- };
- /**
- * Function: getEventState
- *
- * Returns the <mxCellState> to be used when firing the mouse event for the
- * given state. This implementation returns the given state.
- *
- * Parameters:
- *
- * <mxCellState> - State whose event source should be returned.
- */
- mxGraph.prototype.getEventState = function(state)
- {
- return state;
- };
- /**
- * Function: fireMouseEvent
- *
- * Dispatches the given event in the graph event dispatch loop. Possible
- * event names are <mxEvent.MOUSE_DOWN>, <mxEvent.MOUSE_MOVE> and
- * <mxEvent.MOUSE_UP>. All listeners are invoked for all events regardless
- * of the consumed state of the event.
- *
- * Parameters:
- *
- * evtName - String that specifies the type of event to be dispatched.
- * me - <mxMouseEvent> to be fired.
- * sender - Optional sender argument. Default is this.
- */
- mxGraph.prototype.fireMouseEvent = function(evtName, me, sender)
- {
- if (this.isEventSourceIgnored(evtName, me))
- {
- if (this.tooltipHandler != null)
- {
- this.tooltipHandler.hide();
- }
-
- return;
- }
-
- if (sender == null)
- {
- sender = this;
- }
- // Updates the graph coordinates in the event
- me = this.updateMouseEvent(me, evtName);
- // Detects and processes double taps for touch-based devices which do not have native double click events
- // or where detection of double click is not always possible (quirks, IE10+). Note that this can only handle
- // double clicks on cells because the sequence of events in IE prevents detection on the background, it fires
- // two mouse ups, one of which without a cell but no mousedown for the second click which means we cannot
- // detect which mouseup(s) are part of the first click, ie we do not know when the first click ends.
- if ((!this.nativeDblClickEnabled && !mxEvent.isPopupTrigger(me.getEvent())) || (this.doubleTapEnabled &&
- mxClient.IS_TOUCH && (mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent()))))
- {
- var currentTime = new Date().getTime();
-
- // NOTE: Second mouseDown for double click missing in quirks mode
- if ((!mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_DOWN) || (mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_UP && !this.fireDoubleClick))
- {
- if (this.lastTouchEvent != null && this.lastTouchEvent != me.getEvent() &&
- currentTime - this.lastTouchTime < this.doubleTapTimeout &&
- Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
- Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance &&
- this.doubleClickCounter < 2)
- {
- this.doubleClickCounter++;
- var doubleClickFired = false;
-
- if (evtName == mxEvent.MOUSE_UP)
- {
- if (me.getCell() == this.lastTouchCell && this.lastTouchCell != null)
- {
- this.lastTouchTime = 0;
- var cell = this.lastTouchCell;
- this.lastTouchCell = null;
- // Fires native dblclick event via event source
- // NOTE: This fires two double click events on edges in quirks mode. While
- // trying to fix this, we realized that nativeDoubleClick can be disabled for
- // quirks and IE10+ (or we didn't find the case mentioned above where it
- // would not work), ie. all double clicks seem to be working without this.
- if (mxClient.IS_QUIRKS)
- {
- me.getSource().fireEvent('ondblclick');
- }
-
- this.dblClick(me.getEvent(), cell);
- doubleClickFired = true;
- }
- }
- else
- {
- this.fireDoubleClick = true;
- this.lastTouchTime = 0;
- }
- // Do not ignore mouse up in quirks in this case
- if (!mxClient.IS_QUIRKS || doubleClickFired)
- {
- mxEvent.consume(me.getEvent());
- return;
- }
- }
- else if (this.lastTouchEvent == null || this.lastTouchEvent != me.getEvent())
- {
- this.lastTouchCell = me.getCell();
- this.lastTouchX = me.getX();
- this.lastTouchY = me.getY();
- this.lastTouchTime = currentTime;
- this.lastTouchEvent = me.getEvent();
- this.doubleClickCounter = 0;
- }
- }
- else if ((this.isMouseDown || evtName == mxEvent.MOUSE_UP) && this.fireDoubleClick)
- {
- this.fireDoubleClick = false;
- var cell = this.lastTouchCell;
- this.lastTouchCell = null;
- this.isMouseDown = false;
-
- // Workaround for Chrome/Safari not firing native double click events for double touch on background
- var valid = (cell != null) || ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
- (mxClient.IS_GC || mxClient.IS_SF));
-
- if (valid && Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
- Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance)
- {
- this.dblClick(me.getEvent(), cell);
- }
- else
- {
- mxEvent.consume(me.getEvent());
- }
-
- return;
- }
- }
- if (!this.isEventIgnored(evtName, me, sender))
- {
- // Updates the event state via getEventState
- me.state = this.getEventState(me.getState());
- this.fireEvent(new mxEventObject(mxEvent.FIRE_MOUSE_EVENT, 'eventName', evtName, 'event', me));
-
- if ((mxClient.IS_OP || mxClient.IS_SF || mxClient.IS_GC || mxClient.IS_IE11 ||
- (mxClient.IS_IE && mxClient.IS_SVG) || me.getEvent().target != this.container))
- {
- if (evtName == mxEvent.MOUSE_MOVE && this.isMouseDown && this.autoScroll && !mxEvent.isMultiTouchEvent(me.getEvent))
- {
- this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.autoExtend);
- }
- else if (evtName == mxEvent.MOUSE_UP && this.ignoreScrollbars && this.translateToScrollPosition &&
- (this.container.scrollLeft != 0 || this.container.scrollTop != 0))
- {
- var s = this.view.scale;
- var tr = this.view.translate;
- this.view.setTranslate(tr.x - this.container.scrollLeft / s, tr.y - this.container.scrollTop / s);
- this.container.scrollLeft = 0;
- this.container.scrollTop = 0;
- }
-
- if (this.mouseListeners != null)
- {
- var args = [sender, me];
-
- // Does not change returnValue in Opera
- if (!me.getEvent().preventDefault)
- {
- me.getEvent().returnValue = true;
- }
-
- for (var i = 0; i < this.mouseListeners.length; i++)
- {
- var l = this.mouseListeners[i];
-
- if (evtName == mxEvent.MOUSE_DOWN)
- {
- l.mouseDown.apply(l, args);
- }
- else if (evtName == mxEvent.MOUSE_MOVE)
- {
- l.mouseMove.apply(l, args);
- }
- else if (evtName == mxEvent.MOUSE_UP)
- {
- l.mouseUp.apply(l, args);
- }
- }
- }
-
- // Invokes the click handler
- if (evtName == mxEvent.MOUSE_UP)
- {
- this.click(me);
- }
- }
-
- // Detects tapAndHold events using a timer
- if ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
- evtName == mxEvent.MOUSE_DOWN && this.tapAndHoldEnabled && !this.tapAndHoldInProgress)
- {
- this.tapAndHoldInProgress = true;
- this.initialTouchX = me.getGraphX();
- this.initialTouchY = me.getGraphY();
-
- var handler = function()
- {
- if (this.tapAndHoldValid)
- {
- this.tapAndHold(me);
- }
-
- this.tapAndHoldInProgress = false;
- this.tapAndHoldValid = false;
- };
-
- if (this.tapAndHoldThread)
- {
- window.clearTimeout(this.tapAndHoldThread);
- }
-
- this.tapAndHoldThread = window.setTimeout(mxUtils.bind(this, handler), this.tapAndHoldDelay);
- this.tapAndHoldValid = true;
- }
- else if (evtName == mxEvent.MOUSE_UP)
- {
- this.tapAndHoldInProgress = false;
- this.tapAndHoldValid = false;
- }
- else if (this.tapAndHoldValid)
- {
- this.tapAndHoldValid =
- Math.abs(this.initialTouchX - me.getGraphX()) < this.tolerance &&
- Math.abs(this.initialTouchY - me.getGraphY()) < this.tolerance;
- }
- // Stops editing for all events other than from cellEditor
- if (evtName == mxEvent.MOUSE_DOWN && this.isEditing() && !this.cellEditor.isEventSource(me.getEvent()))
- {
- this.stopEditing(!this.isInvokesStopCellEditing());
- }
- this.consumeMouseEvent(evtName, me, sender);
- }
- };
- /**
- * Function: consumeMouseEvent
- *
- * Consumes the given <mxMouseEvent> if it's a touchStart event.
- */
- mxGraph.prototype.consumeMouseEvent = function(evtName, me, sender)
- {
- // Workaround for duplicate click in Windows 8 with Chrome/FF/Opera with touch
- if (evtName == mxEvent.MOUSE_DOWN && mxEvent.isTouchEvent(me.getEvent()))
- {
- me.consume(false);
- }
- };
- /**
- * Function: fireGestureEvent
- *
- * Dispatches a <mxEvent.GESTURE> event. The following example will resize the
- * cell under the mouse based on the scale property of the native touch event.
- *
- * (code)
- * graph.addListener(mxEvent.GESTURE, function(sender, eo)
- * {
- * var evt = eo.getProperty('event');
- * var state = graph.view.getState(eo.getProperty('cell'));
- *
- * if (graph.isEnabled() && graph.isCellResizable(state.cell) && Math.abs(1 - evt.scale) > 0.2)
- * {
- * var scale = graph.view.scale;
- * var tr = graph.view.translate;
- *
- * var w = state.width * evt.scale;
- * var h = state.height * evt.scale;
- * var x = state.x - (w - state.width) / 2;
- * var y = state.y - (h - state.height) / 2;
- *
- * var bounds = new mxRectangle(graph.snap(x / scale) - tr.x,
- * graph.snap(y / scale) - tr.y, graph.snap(w / scale), graph.snap(h / scale));
- * graph.resizeCell(state.cell, bounds);
- * eo.consume();
- * }
- * });
- * (end)
- *
- * Parameters:
- *
- * evt - Gestureend event that represents the gesture.
- * cell - Optional <mxCell> associated with the gesture.
- */
- mxGraph.prototype.fireGestureEvent = function(evt, cell)
- {
- // Resets double tap event handling when gestures take place
- this.lastTouchTime = 0;
- this.fireEvent(new mxEventObject(mxEvent.GESTURE, 'event', evt, 'cell', cell));
- };
- /**
- * Function: destroy
- *
- * Destroys the graph and all its resources.
- */
- mxGraph.prototype.destroy = function()
- {
- if (!this.destroyed)
- {
- this.destroyed = true;
-
- if (this.tooltipHandler != null)
- {
- this.tooltipHandler.destroy();
- }
-
- if (this.selectionCellsHandler != null)
- {
- this.selectionCellsHandler.destroy();
- }
- if (this.panningHandler != null)
- {
- this.panningHandler.destroy();
- }
- if (this.popupMenuHandler != null)
- {
- this.popupMenuHandler.destroy();
- }
-
- if (this.connectionHandler != null)
- {
- this.connectionHandler.destroy();
- }
-
- if (this.graphHandler != null)
- {
- this.graphHandler.destroy();
- }
-
- if (this.cellEditor != null)
- {
- this.cellEditor.destroy();
- }
-
- if (this.view != null)
- {
- this.view.destroy();
- }
- if (this.model != null && this.graphModelChangeListener != null)
- {
- this.model.removeListener(this.graphModelChangeListener);
- this.graphModelChangeListener = null;
- }
- this.container = null;
- }
- };
- __mxOutput.mxGraph = typeof mxGraph !== 'undefined' ? mxGraph : undefined;
|