Graph.js 238 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697
  1. /**
  2. * Copyright (c) 2006-2012, JGraph Ltd
  3. */
  4. // Workaround for allowing target="_blank" in HTML sanitizer
  5. // see https://code.google.com/p/google-caja/issues/detail?can=2&q=&colspec=ID%20Type%20Status%20Priority%20Owner%20Summary&groupby=&sort=&id=1296
  6. if (typeof html4 !== 'undefined')
  7. {
  8. html4.ATTRIBS["a::target"] = 0;
  9. html4.ATTRIBS["source::src"] = 0;
  10. html4.ATTRIBS["video::src"] = 0;
  11. // Would be nice for tooltips but probably a security risk...
  12. //html4.ATTRIBS["video::autoplay"] = 0;
  13. //html4.ATTRIBS["video::autobuffer"] = 0;
  14. }
  15. // Shim for missing toISOString in older versions of IE
  16. // See https://stackoverflow.com/questions/12907862
  17. if (!Date.prototype.toISOString)
  18. {
  19. (function()
  20. {
  21. function pad(number)
  22. {
  23. var r = String(number);
  24. if (r.length === 1)
  25. {
  26. r = '0' + r;
  27. }
  28. return r;
  29. };
  30. Date.prototype.toISOString = function()
  31. {
  32. return this.getUTCFullYear()
  33. + '-' + pad( this.getUTCMonth() + 1 )
  34. + '-' + pad( this.getUTCDate() )
  35. + 'T' + pad( this.getUTCHours() )
  36. + ':' + pad( this.getUTCMinutes() )
  37. + ':' + pad( this.getUTCSeconds() )
  38. + '.' + String( (this.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 )
  39. + 'Z';
  40. };
  41. }());
  42. }
  43. // Shim for Date.now()
  44. if (!Date.now)
  45. {
  46. Date.now = function()
  47. {
  48. return new Date().getTime();
  49. };
  50. }
  51. // Changes default colors
  52. mxConstants.SHADOW_OPACITY = 0.25;
  53. mxConstants.SHADOWCOLOR = '#000000';
  54. mxConstants.VML_SHADOWCOLOR = '#d0d0d0';
  55. mxGraph.prototype.pageBreakColor = '#c0c0c0';
  56. mxGraph.prototype.pageScale = 1;
  57. // Letter page format is default in US, Canada and Mexico
  58. (function()
  59. {
  60. try
  61. {
  62. if (navigator != null && navigator.language != null)
  63. {
  64. var lang = navigator.language.toLowerCase();
  65. mxGraph.prototype.pageFormat = (lang === 'en-us' || lang === 'en-ca' || lang === 'es-mx') ?
  66. mxConstants.PAGE_FORMAT_LETTER_PORTRAIT : mxConstants.PAGE_FORMAT_A4_PORTRAIT;
  67. }
  68. }
  69. catch (e)
  70. {
  71. // ignore
  72. }
  73. })();
  74. // Matches label positions of mxGraph 1.x
  75. mxText.prototype.baseSpacingTop = 5;
  76. mxText.prototype.baseSpacingBottom = 1;
  77. // Keeps edges between relative child cells inside parent
  78. mxGraphModel.prototype.ignoreRelativeEdgeParent = false;
  79. // Defines grid properties
  80. mxGraphView.prototype.gridImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCgAKAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAAKAAoAAAIJ1I6py+0Po2wFADs=' :
  81. IMAGE_PATH + '/grid.gif';
  82. mxGraphView.prototype.gridSteps = 4;
  83. mxGraphView.prototype.minGridSize = 4;
  84. // UrlParams is null in embed mode
  85. mxGraphView.prototype.gridColor = '#e0e0e0';
  86. // Alternative text for unsupported foreignObjects
  87. mxSvgCanvas2D.prototype.foAltText = '[Not supported by viewer]';
  88. // Hook for custom constraints
  89. mxShape.prototype.getConstraints = function(style, w, h)
  90. {
  91. return null;
  92. };
  93. /**
  94. * Constructs a new graph instance. Note that the constructor does not take a
  95. * container because the graph instance is needed for creating the UI, which
  96. * in turn will create the container for the graph. Hence, the container is
  97. * assigned later in EditorUi.
  98. */
  99. /**
  100. * Defines graph class.
  101. */
  102. Graph = function(container, model, renderHint, stylesheet, themes)
  103. {
  104. mxGraph.call(this, container, model, renderHint, stylesheet);
  105. this.themes = themes || this.defaultThemes;
  106. this.currentEdgeStyle = mxUtils.clone(this.defaultEdgeStyle);
  107. this.currentVertexStyle = mxUtils.clone(this.defaultVertexStyle);
  108. // Sets the base domain URL and domain path URL for relative links.
  109. var b = this.baseUrl;
  110. var p = b.indexOf('//');
  111. this.domainUrl = '';
  112. this.domainPathUrl = '';
  113. if (p > 0)
  114. {
  115. var d = b.indexOf('/', p + 2);
  116. if (d > 0)
  117. {
  118. this.domainUrl = b.substring(0, d);
  119. }
  120. d = b.lastIndexOf('/');
  121. if (d > 0)
  122. {
  123. this.domainPathUrl = b.substring(0, d + 1);
  124. }
  125. }
  126. // Adds support for HTML labels via style. Note: Currently, only the Java
  127. // backend supports HTML labels but CSS support is limited to the following:
  128. // http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html
  129. // TODO: Wrap should not affect isHtmlLabel output (should be handled later)
  130. this.isHtmlLabel = function(cell)
  131. {
  132. var state = this.view.getState(cell);
  133. var style = (state != null) ? state.style : this.getCellStyle(cell);
  134. return (style != null) ? (style['html'] == '1' || style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') : false;
  135. };
  136. // Implements a listener for hover and click handling on edges
  137. if (this.edgeMode)
  138. {
  139. var start = {
  140. point: null,
  141. event: null,
  142. state: null,
  143. handle: null,
  144. selected: false
  145. };
  146. // Uses this event to process mouseDown to check the selection state before it is changed
  147. this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt)
  148. {
  149. if (evt.getProperty('eventName') == 'mouseDown' && this.isEnabled())
  150. {
  151. var me = evt.getProperty('event');
  152. if (!mxEvent.isControlDown(me.getEvent()) && !mxEvent.isShiftDown(me.getEvent()))
  153. {
  154. var state = me.getState();
  155. if (state != null)
  156. {
  157. // Checks if state was removed in call to stopEditing above
  158. if (this.model.isEdge(state.cell))
  159. {
  160. start.point = new mxPoint(me.getGraphX(), me.getGraphY());
  161. start.selected = this.isCellSelected(state.cell);
  162. start.state = state;
  163. start.event = me;
  164. if (state.text != null && state.text.boundingBox != null &&
  165. mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY()))
  166. {
  167. start.handle = mxEvent.LABEL_HANDLE;
  168. }
  169. else
  170. {
  171. var handler = this.selectionCellsHandler.getHandler(state.cell);
  172. if (handler != null && handler.bends != null && handler.bends.length > 0)
  173. {
  174. start.handle = handler.getHandleForEvent(me);
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }));
  182. var mouseDown = null;
  183. this.addMouseListener(
  184. {
  185. mouseDown: function(sender, me) {},
  186. mouseMove: mxUtils.bind(this, function(sender, me)
  187. {
  188. // Checks if any other handler is active
  189. var handlerMap = this.selectionCellsHandler.handlers.map;
  190. for (var key in handlerMap)
  191. {
  192. if (handlerMap[key].index != null)
  193. {
  194. return;
  195. }
  196. }
  197. if (this.isEnabled() && !this.panningHandler.isActive() && !mxEvent.isControlDown(me.getEvent()) &&
  198. !mxEvent.isShiftDown(me.getEvent()) && !mxEvent.isAltDown(me.getEvent()))
  199. {
  200. var tol = this.tolerance;
  201. if (start.point != null && start.state != null && start.event != null)
  202. {
  203. var state = start.state;
  204. if (Math.abs(start.point.x - me.getGraphX()) > tol ||
  205. Math.abs(start.point.y - me.getGraphY()) > tol)
  206. {
  207. // Lazy selection for edges inside groups
  208. if (!this.isCellSelected(state.cell))
  209. {
  210. this.setSelectionCell(state.cell);
  211. }
  212. var handler = this.selectionCellsHandler.getHandler(state.cell);
  213. if (handler != null && handler.bends != null && handler.bends.length > 0)
  214. {
  215. var handle = handler.getHandleForEvent(start.event);
  216. var edgeStyle = this.view.getEdgeStyle(state);
  217. var entity = edgeStyle == mxEdgeStyle.EntityRelation;
  218. // Handles special case where label was clicked on unselected edge in which
  219. // case the label will be moved regardless of the handle that is returned
  220. if (!start.selected && start.handle == mxEvent.LABEL_HANDLE)
  221. {
  222. handle = start.handle;
  223. }
  224. if (!entity || handle == 0 || handle == handler.bends.length - 1 || handle == mxEvent.LABEL_HANDLE)
  225. {
  226. // Source or target handle or connected for direct handle access or orthogonal line
  227. // with just two points where the central handle is moved regardless of mouse position
  228. if (handle == mxEvent.LABEL_HANDLE || handle == 0 || state.visibleSourceState != null ||
  229. handle == handler.bends.length - 1 || state.visibleTargetState != null)
  230. {
  231. if (!entity && handle != mxEvent.LABEL_HANDLE)
  232. {
  233. var pts = state.absolutePoints;
  234. // Default case where handles are at corner points handles
  235. // drag of corner as drag of existing point
  236. if (pts != null && ((edgeStyle == null && handle == null) ||
  237. edgeStyle == mxEdgeStyle.OrthConnector))
  238. {
  239. // Does not use handles if they were not initially visible
  240. handle = start.handle;
  241. if (handle == null)
  242. {
  243. var box = new mxRectangle(start.point.x, start.point.y);
  244. box.grow(mxEdgeHandler.prototype.handleImage.width / 2);
  245. if (mxUtils.contains(box, pts[0].x, pts[0].y))
  246. {
  247. // Moves source terminal handle
  248. handle = 0;
  249. }
  250. else if (mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y))
  251. {
  252. // Moves target terminal handle
  253. handle = handler.bends.length - 1;
  254. }
  255. else
  256. {
  257. // Checks if edge has no bends
  258. var nobends = edgeStyle != null && (pts.length == 2 || (pts.length == 3 &&
  259. ((Math.round(pts[0].x - pts[1].x) == 0 && Math.round(pts[1].x - pts[2].x) == 0) ||
  260. (Math.round(pts[0].y - pts[1].y) == 0 && Math.round(pts[1].y - pts[2].y) == 0))));
  261. if (nobends)
  262. {
  263. // Moves central handle for straight orthogonal edges
  264. handle = 2;
  265. }
  266. else
  267. {
  268. // Finds and moves vertical or horizontal segment
  269. handle = mxUtils.findNearestSegment(state, start.point.x, start.point.y);
  270. // Converts segment to virtual handle index
  271. if (edgeStyle == null)
  272. {
  273. handle = mxEvent.VIRTUAL_HANDLE - handle;
  274. }
  275. // Maps segment to handle
  276. else
  277. {
  278. handle += 1;
  279. }
  280. }
  281. }
  282. }
  283. }
  284. // Creates a new waypoint and starts moving it
  285. if (handle == null)
  286. {
  287. handle = mxEvent.VIRTUAL_HANDLE;
  288. }
  289. }
  290. handler.start(me.getGraphX(), me.getGraphX(), handle);
  291. start.state = null;
  292. start.event = null;
  293. start.point = null;
  294. start.handle = null;
  295. start.selected = false;
  296. me.consume();
  297. // Removes preview rectangle in graph handler
  298. this.graphHandler.reset();
  299. }
  300. }
  301. else if (entity && (state.visibleSourceState != null || state.visibleTargetState != null))
  302. {
  303. // Disables moves on entity to make it consistent
  304. this.graphHandler.reset();
  305. me.consume();
  306. }
  307. }
  308. }
  309. }
  310. else
  311. {
  312. // Updates cursor for unselected edges under the mouse
  313. var state = me.getState();
  314. if (state != null)
  315. {
  316. // Checks if state was removed in call to stopEditing above
  317. if (this.model.isEdge(state.cell))
  318. {
  319. var cursor = null;
  320. var pts = state.absolutePoints;
  321. if (pts != null)
  322. {
  323. var box = new mxRectangle(me.getGraphX(), me.getGraphY());
  324. box.grow(mxEdgeHandler.prototype.handleImage.width / 2);
  325. if (state.text != null && state.text.boundingBox != null &&
  326. mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY()))
  327. {
  328. cursor = 'move';
  329. }
  330. else if (mxUtils.contains(box, pts[0].x, pts[0].y) ||
  331. mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y))
  332. {
  333. cursor = 'pointer';
  334. }
  335. else if (state.visibleSourceState != null || state.visibleTargetState != null)
  336. {
  337. // Moving is not allowed for entity relation but still indicate hover state
  338. var tmp = this.view.getEdgeStyle(state);
  339. cursor = 'crosshair';
  340. if (tmp != mxEdgeStyle.EntityRelation && this.isOrthogonal(state))
  341. {
  342. var idx = mxUtils.findNearestSegment(state, me.getGraphX(), me.getGraphY());
  343. if (idx < pts.length - 1 && idx >= 0)
  344. {
  345. cursor = (Math.round(pts[idx].x - pts[idx + 1].x) == 0) ?
  346. 'col-resize' : 'row-resize';
  347. }
  348. }
  349. }
  350. }
  351. if (cursor != null)
  352. {
  353. state.setCursor(cursor);
  354. }
  355. }
  356. }
  357. }
  358. }
  359. }),
  360. mouseUp: mxUtils.bind(this, function(sender, me)
  361. {
  362. start.state = null;
  363. start.event = null;
  364. start.point = null;
  365. start.handle = null;
  366. })
  367. });
  368. }
  369. // HTML entities are displayed as plain text in wrapped plain text labels
  370. this.cellRenderer.getLabelValue = function(state)
  371. {
  372. var result = mxCellRenderer.prototype.getLabelValue.apply(this, arguments);
  373. if (state.view.graph.isHtmlLabel(state.cell))
  374. {
  375. if (state.style['html'] != 1)
  376. {
  377. result = mxUtils.htmlEntities(result, false);
  378. }
  379. else
  380. {
  381. result = state.view.graph.sanitizeHtml(result);
  382. }
  383. }
  384. return result;
  385. };
  386. // All code below not available and not needed in embed mode
  387. if (typeof mxVertexHandler !== 'undefined')
  388. {
  389. this.setConnectable(true);
  390. this.setDropEnabled(true);
  391. this.setPanning(true);
  392. this.setTooltips(true);
  393. this.setAllowLoops(true);
  394. this.allowAutoPanning = true;
  395. this.resetEdgesOnConnect = false;
  396. this.constrainChildren = false;
  397. this.constrainRelativeChildren = true;
  398. // Do not scroll after moving cells
  399. this.graphHandler.scrollOnMove = false;
  400. this.graphHandler.scaleGrid = true;
  401. // Disables cloning of connection sources by default
  402. this.connectionHandler.setCreateTarget(false);
  403. this.connectionHandler.insertBeforeSource = true;
  404. // Disables built-in connection starts
  405. this.connectionHandler.isValidSource = function(cell, me)
  406. {
  407. return false;
  408. };
  409. // Sets the style to be used when an elbow edge is double clicked
  410. this.alternateEdgeStyle = 'vertical';
  411. if (stylesheet == null)
  412. {
  413. this.loadStylesheet();
  414. }
  415. // Adds page centers to the guides for moving cells
  416. var graphHandlerGetGuideStates = this.graphHandler.getGuideStates;
  417. this.graphHandler.getGuideStates = function()
  418. {
  419. var result = graphHandlerGetGuideStates.apply(this, arguments);
  420. // Create virtual cell state for page centers
  421. if (this.graph.pageVisible)
  422. {
  423. var guides = [];
  424. var pf = this.graph.pageFormat;
  425. var ps = this.graph.pageScale;
  426. var pw = pf.width * ps;
  427. var ph = pf.height * ps;
  428. var t = this.graph.view.translate;
  429. var s = this.graph.view.scale;
  430. var layout = this.graph.getPageLayout();
  431. for (var i = 0; i < layout.width; i++)
  432. {
  433. guides.push(new mxRectangle(((layout.x + i) * pw + t.x) * s,
  434. (layout.y * ph + t.y) * s, pw * s, ph * s));
  435. }
  436. for (var j = 0; j < layout.height; j++)
  437. {
  438. guides.push(new mxRectangle((layout.x * pw + t.x) * s,
  439. ((layout.y + j) * ph + t.y) * s, pw * s, ph * s));
  440. }
  441. // Page center guides have predence over normal guides
  442. result = guides.concat(result);
  443. }
  444. return result;
  445. };
  446. // Overrides zIndex for dragElement
  447. mxDragSource.prototype.dragElementZIndex = mxPopupMenu.prototype.zIndex;
  448. // Overrides color for virtual guides for page centers
  449. mxGuide.prototype.getGuideColor = function(state, horizontal)
  450. {
  451. return (state.cell == null) ? '#ffa500' /* orange */ : mxConstants.GUIDE_COLOR;
  452. };
  453. // Changes color of move preview for black backgrounds
  454. this.graphHandler.createPreviewShape = function(bounds)
  455. {
  456. this.previewColor = (this.graph.background == '#000000') ? '#ffffff' : mxGraphHandler.prototype.previewColor;
  457. return mxGraphHandler.prototype.createPreviewShape.apply(this, arguments);
  458. };
  459. // Handles parts of cells by checking if part=1 is in the style and returning the parent
  460. // if the parent is not already in the list of cells. container style is used to disable
  461. // step into swimlanes and dropTarget style is used to disable acting as a drop target.
  462. // LATER: Handle recursive parts
  463. this.graphHandler.getCells = function(initialCell)
  464. {
  465. var cells = mxGraphHandler.prototype.getCells.apply(this, arguments);
  466. var newCells = [];
  467. for (var i = 0; i < cells.length; i++)
  468. {
  469. var state = this.graph.view.getState(cells[i]);
  470. var style = (state != null) ? state.style : this.graph.getCellStyle(cells[i]);
  471. if (mxUtils.getValue(style, 'part', '0') == '1')
  472. {
  473. var parent = this.graph.model.getParent(cells[i]);
  474. if (this.graph.model.isVertex(parent) && mxUtils.indexOf(cells, parent) < 0)
  475. {
  476. newCells.push(parent);
  477. }
  478. }
  479. else
  480. {
  481. newCells.push(cells[i]);
  482. }
  483. }
  484. return newCells;
  485. };
  486. // Handles parts of cells when cloning the source for new connections
  487. this.connectionHandler.createTargetVertex = function(evt, source)
  488. {
  489. var state = this.graph.view.getState(source);
  490. var style = (state != null) ? state.style : this.graph.getCellStyle(source);
  491. if (mxUtils.getValue(style, 'part', false))
  492. {
  493. var parent = this.graph.model.getParent(source);
  494. if (this.graph.model.isVertex(parent))
  495. {
  496. source = parent;
  497. }
  498. }
  499. return mxConnectionHandler.prototype.createTargetVertex.apply(this, arguments);
  500. };
  501. var rubberband = new mxRubberband(this);
  502. this.getRubberband = function()
  503. {
  504. return rubberband;
  505. };
  506. // Timer-based activation of outline connect in connection handler
  507. var startTime = new Date().getTime();
  508. var timeOnTarget = 0;
  509. var connectionHandlerMouseMove = this.connectionHandler.mouseMove;
  510. this.connectionHandler.mouseMove = function()
  511. {
  512. var prev = this.currentState;
  513. connectionHandlerMouseMove.apply(this, arguments);
  514. if (prev != this.currentState)
  515. {
  516. startTime = new Date().getTime();
  517. timeOnTarget = 0;
  518. }
  519. else
  520. {
  521. timeOnTarget = new Date().getTime() - startTime;
  522. }
  523. };
  524. // Activates outline connect after 1500ms with touch event or if alt is pressed inside the shape
  525. // outlineConnect=0 is a custom style that means do not connect to strokes inside the shape,
  526. // or in other words, connect to the shape's perimeter if the highlight is under the mouse
  527. // (the name is because the highlight, including all strokes, is called outline in the code)
  528. var connectionHandleIsOutlineConnectEvent = this.connectionHandler.isOutlineConnectEvent;
  529. this.connectionHandler.isOutlineConnectEvent = function(me)
  530. {
  531. return (this.currentState != null && me.getState() == this.currentState && timeOnTarget > 2000) ||
  532. ((this.currentState == null || mxUtils.getValue(this.currentState.style, 'outlineConnect', '1') != '0') &&
  533. connectionHandleIsOutlineConnectEvent.apply(this, arguments));
  534. };
  535. // Adds shift+click to toggle selection state
  536. var isToggleEvent = this.isToggleEvent;
  537. this.isToggleEvent = function(evt)
  538. {
  539. return isToggleEvent.apply(this, arguments) || mxEvent.isShiftDown(evt);
  540. };
  541. // Workaround for Firefox where first mouse down is received
  542. // after tap and hold if scrollbars are visible, which means
  543. // start rubberband immediately if no cell is under mouse.
  544. var isForceRubberBandEvent = rubberband.isForceRubberbandEvent;
  545. rubberband.isForceRubberbandEvent = function(me)
  546. {
  547. return isForceRubberBandEvent.apply(this, arguments) ||
  548. (mxUtils.hasScrollbars(this.graph.container) && mxClient.IS_FF &&
  549. mxClient.IS_WIN && me.getState() == null && mxEvent.isTouchEvent(me.getEvent()));
  550. };
  551. // Shows hand cursor while panning
  552. var prevCursor = null;
  553. this.panningHandler.addListener(mxEvent.PAN_START, mxUtils.bind(this, function()
  554. {
  555. if (this.isEnabled())
  556. {
  557. prevCursor = this.container.style.cursor;
  558. this.container.style.cursor = 'move';
  559. }
  560. }));
  561. this.panningHandler.addListener(mxEvent.PAN_END, mxUtils.bind(this, function()
  562. {
  563. if (this.isEnabled())
  564. {
  565. this.container.style.cursor = prevCursor;
  566. }
  567. }));
  568. this.popupMenuHandler.autoExpand = true;
  569. this.popupMenuHandler.isSelectOnPopup = function(me)
  570. {
  571. return mxEvent.isMouseEvent(me.getEvent());
  572. };
  573. // Handles links if graph is read-only or cell is locked
  574. var click = this.click;
  575. this.click = function(me)
  576. {
  577. var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell);
  578. if ((!this.isEnabled() || locked) && !me.isConsumed())
  579. {
  580. var cell = (locked) ? me.sourceState.cell : me.getCell();
  581. if (cell != null)
  582. {
  583. var link = this.getLinkForCell(cell);
  584. if (link != null)
  585. {
  586. if (this.isCustomLink(link))
  587. {
  588. this.customLinkClicked(link);
  589. }
  590. else
  591. {
  592. this.openLink(link);
  593. }
  594. }
  595. }
  596. if (this.isEnabled() && locked)
  597. {
  598. this.clearSelection();
  599. }
  600. }
  601. else
  602. {
  603. return click.apply(this, arguments);
  604. }
  605. };
  606. // Redirects tooltips for locked cells
  607. this.tooltipHandler.getStateForEvent = function(me)
  608. {
  609. return me.sourceState;
  610. };
  611. // Redirects cursor for locked cells
  612. var getCursorForMouseEvent = this.getCursorForMouseEvent;
  613. this.getCursorForMouseEvent = function(me)
  614. {
  615. var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell);
  616. return this.getCursorForCell((locked) ? me.sourceState.cell : me.getCell());
  617. };
  618. // Shows pointer cursor for clickable cells with links
  619. // ie. if the graph is disabled and cells cannot be selected
  620. var getCursorForCell = this.getCursorForCell;
  621. this.getCursorForCell = function(cell)
  622. {
  623. if (!this.isEnabled() || this.isCellLocked(cell))
  624. {
  625. var link = this.getLinkForCell(cell);
  626. if (link != null)
  627. {
  628. return 'pointer';
  629. }
  630. else if (this.isCellLocked(cell))
  631. {
  632. return 'default';
  633. }
  634. }
  635. return getCursorForCell.apply(this, arguments);
  636. };
  637. // Changes rubberband selection to be recursive
  638. this.selectRegion = function(rect, evt)
  639. {
  640. var cells = this.getAllCells(rect.x, rect.y, rect.width, rect.height);
  641. this.selectCellsForEvent(cells, evt);
  642. return cells;
  643. };
  644. // Recursive implementation for rubberband selection
  645. this.getAllCells = function(x, y, width, height, parent, result)
  646. {
  647. result = (result != null) ? result : [];
  648. if (width > 0 || height > 0)
  649. {
  650. var model = this.getModel();
  651. var right = x + width;
  652. var bottom = y + height;
  653. if (parent == null)
  654. {
  655. parent = this.getCurrentRoot();
  656. if (parent == null)
  657. {
  658. parent = model.getRoot();
  659. }
  660. }
  661. if (parent != null)
  662. {
  663. var childCount = model.getChildCount(parent);
  664. for (var i = 0; i < childCount; i++)
  665. {
  666. var cell = model.getChildAt(parent, i);
  667. var state = this.view.getState(cell);
  668. if (state != null && this.isCellVisible(cell) && mxUtils.getValue(state.style, 'locked', '0') != '1')
  669. {
  670. var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0;
  671. var box = state;
  672. if (deg != 0)
  673. {
  674. box = mxUtils.getBoundingBox(box, deg);
  675. }
  676. if ((model.isEdge(cell) || model.isVertex(cell)) &&
  677. box.x >= x && box.y + box.height <= bottom &&
  678. box.y >= y && box.x + box.width <= right)
  679. {
  680. result.push(cell);
  681. }
  682. this.getAllCells(x, y, width, height, cell, result);
  683. }
  684. }
  685. }
  686. }
  687. return result;
  688. };
  689. // Never removes cells from parents that are being moved
  690. var graphHandlerShouldRemoveCellsFromParent = this.graphHandler.shouldRemoveCellsFromParent;
  691. this.graphHandler.shouldRemoveCellsFromParent = function(parent, cells, evt)
  692. {
  693. if (this.graph.isCellSelected(parent))
  694. {
  695. return false;
  696. }
  697. return graphHandlerShouldRemoveCellsFromParent.apply(this, arguments);
  698. };
  699. // Unlocks all cells
  700. this.isCellLocked = function(cell)
  701. {
  702. var pState = this.view.getState(cell);
  703. while (pState != null)
  704. {
  705. if (mxUtils.getValue(pState.style, 'locked', '0') == '1')
  706. {
  707. return true;
  708. }
  709. pState = this.view.getState(this.model.getParent(pState.cell));
  710. }
  711. return false;
  712. };
  713. var tapAndHoldSelection = null;
  714. // Uses this event to process mouseDown to check the selection state before it is changed
  715. this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt)
  716. {
  717. if (evt.getProperty('eventName') == 'mouseDown')
  718. {
  719. var me = evt.getProperty('event');
  720. var state = me.getState();
  721. if (state != null && !this.isSelectionEmpty() && !this.isCellSelected(state.cell))
  722. {
  723. tapAndHoldSelection = this.getSelectionCells();
  724. }
  725. else
  726. {
  727. tapAndHoldSelection = null;
  728. }
  729. }
  730. }));
  731. // Tap and hold on background starts rubberband for multiple selected
  732. // cells the cell associated with the event is deselected
  733. this.addListener(mxEvent.TAP_AND_HOLD, mxUtils.bind(this, function(sender, evt)
  734. {
  735. if (!mxEvent.isMultiTouchEvent(evt))
  736. {
  737. var me = evt.getProperty('event');
  738. var cell = evt.getProperty('cell');
  739. if (cell == null)
  740. {
  741. var pt = mxUtils.convertPoint(this.container,
  742. mxEvent.getClientX(me), mxEvent.getClientY(me));
  743. rubberband.start(pt.x, pt.y);
  744. }
  745. else if (tapAndHoldSelection != null)
  746. {
  747. this.addSelectionCells(tapAndHoldSelection);
  748. }
  749. else if (this.getSelectionCount() > 1 && this.isCellSelected(cell))
  750. {
  751. this.removeSelectionCell(cell);
  752. }
  753. // Blocks further processing of the event
  754. tapAndHoldSelection = null;
  755. evt.consume();
  756. }
  757. }));
  758. // On connect the target is selected and we clone the cell of the preview edge for insert
  759. this.connectionHandler.selectCells = function(edge, target)
  760. {
  761. this.graph.setSelectionCell(target || edge);
  762. };
  763. // Shows connection points only if cell not selected
  764. this.connectionHandler.constraintHandler.isStateIgnored = function(state, source)
  765. {
  766. return source && state.view.graph.isCellSelected(state.cell);
  767. };
  768. // Updates constraint handler if the selection changes
  769. this.selectionModel.addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
  770. {
  771. var ch = this.connectionHandler.constraintHandler;
  772. if (ch.currentFocus != null && ch.isStateIgnored(ch.currentFocus, true))
  773. {
  774. ch.currentFocus = null;
  775. ch.constraints = null;
  776. ch.destroyIcons();
  777. }
  778. ch.destroyFocusHighlight();
  779. }));
  780. // Initializes touch interface
  781. if (Graph.touchStyle)
  782. {
  783. this.initTouch();
  784. }
  785. /**
  786. * Adds locking
  787. */
  788. var graphUpdateMouseEvent = this.updateMouseEvent;
  789. this.updateMouseEvent = function(me)
  790. {
  791. me = graphUpdateMouseEvent.apply(this, arguments);
  792. if (me.state != null && this.isCellLocked(me.getCell()))
  793. {
  794. me.state = null;
  795. }
  796. return me;
  797. };
  798. }
  799. //Create a unique offset object for each graph instance.
  800. this.currentTranslate = new mxPoint(0, 0);
  801. };
  802. /**
  803. * Specifies if the touch UI should be used (cannot detect touch in FF so always on for Windows/Linux)
  804. */
  805. Graph.touchStyle = mxClient.IS_TOUCH || (mxClient.IS_FF && mxClient.IS_WIN) || navigator.maxTouchPoints > 0 ||
  806. navigator.msMaxTouchPoints > 0 || window.urlParams == null || urlParams['touch'] == '1';
  807. /**
  808. * Shortcut for capability check.
  809. */
  810. Graph.fileSupport = window.File != null && window.FileReader != null && window.FileList != null &&
  811. (window.urlParams == null || urlParams['filesupport'] != '0');
  812. /**
  813. * Default size for line jumps.
  814. */
  815. Graph.lineJumpsEnabled = true;
  816. /**
  817. * Default size for line jumps.
  818. */
  819. Graph.defaultJumpSize = 6;
  820. /**
  821. * Helper function for creating SVG data URI.
  822. */
  823. Graph.createSvgImage = function(w, h, data)
  824. {
  825. var tmp = unescape(encodeURIComponent(
  826. '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
  827. '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + w + 'px" height="' + h + 'px" ' +
  828. 'version="1.1">' + data + '</svg>'));
  829. return new mxImage('data:image/svg+xml;base64,' + ((window.btoa) ? btoa(tmp) : Base64.encode(tmp, true)), w, h)
  830. };
  831. /**
  832. * Removes all illegal control characters with ASCII code <32 except TAB, LF
  833. * and CR.
  834. */
  835. Graph.zapGremlins = function(text)
  836. {
  837. var checked = [];
  838. for (var i = 0; i < text.length; i++)
  839. {
  840. var code = text.charCodeAt(i);
  841. // Removes all control chars except TAB, LF and CR
  842. if ((code >= 32 || code == 9 || code == 10 || code == 13) && code != 0xFFFF)
  843. {
  844. checked.push(text.charAt(i));
  845. }
  846. }
  847. return checked.join('');
  848. };
  849. /**
  850. * Turns the given string into an array.
  851. */
  852. Graph.stringToBytes = function(str)
  853. {
  854. var arr = new Array(str.length);
  855. for (var i = 0; i < str.length; i++)
  856. {
  857. arr[i] = str.charCodeAt(i);
  858. }
  859. return arr;
  860. };
  861. /**
  862. * Turns the given array into a string.
  863. */
  864. Graph.bytesToString = function(arr)
  865. {
  866. var result = new Array(arr.length);
  867. for (var i = 0; i < arr.length; i++)
  868. {
  869. result[i] = String.fromCharCode(arr[i]);
  870. }
  871. return result.join('');
  872. };
  873. /**
  874. * Returns a base64 encoded version of the compressed outer XML of the given node.
  875. */
  876. Graph.compressNode = function(node)
  877. {
  878. return Graph.compress(Graph.zapGremlins(mxUtils.getXml(node)));
  879. };
  880. /**
  881. * Returns a base64 encoded version of the compressed string.
  882. */
  883. Graph.compress = function(data, deflate)
  884. {
  885. if (data == null || data.length == 0 || typeof(pako) === 'undefined')
  886. {
  887. return data;
  888. }
  889. else
  890. {
  891. var tmp = Graph.bytesToString((deflate) ? pako.deflate(encodeURIComponent(data)) :
  892. pako.deflateRaw(encodeURIComponent(data)));
  893. return (window.btoa) ? btoa(tmp) : Base64.encode(tmp, true);
  894. }
  895. };
  896. /**
  897. * Returns a decompressed version of the base64 encoded string.
  898. */
  899. Graph.decompress = function(data, inflate)
  900. {
  901. if (data == null || data.length == 0 || typeof(pako) === 'undefined')
  902. {
  903. return data;
  904. }
  905. else
  906. {
  907. var tmp = (window.atob) ? atob(data) : Base64.decode(data, true);
  908. return Graph.zapGremlins(decodeURIComponent(
  909. Graph.bytesToString((inflate) ? pako.inflate(tmp) :
  910. pako.inflateRaw(tmp))));
  911. }
  912. };
  913. /**
  914. * Graph inherits from mxGraph.
  915. */
  916. mxUtils.extend(Graph, mxGraph);
  917. /**
  918. * Allows all values in fit.
  919. */
  920. Graph.prototype.minFitScale = null;
  921. /**
  922. * Allows all values in fit.
  923. */
  924. Graph.prototype.maxFitScale = null;
  925. /**
  926. * Sets the policy for links. Possible values are "self" to replace any framesets,
  927. * "blank" to load the URL in <linkTarget> and "auto" (default).
  928. */
  929. Graph.prototype.linkPolicy = (urlParams['target'] == 'frame') ? 'blank' : (urlParams['target'] || 'auto');
  930. /**
  931. * Target for links that open in a new window. Default is _blank.
  932. */
  933. Graph.prototype.linkTarget = (urlParams['target'] == 'frame') ? '_self' : '_blank';
  934. /**
  935. * Value to the rel attribute of links. Default is 'nofollow noopener noreferrer'.
  936. * NOTE: There are security implications when this is changed and if noopener is removed,
  937. * then <openLink> must be overridden to allow for the opener to be set by default.
  938. */
  939. Graph.prototype.linkRelation = 'nofollow noopener noreferrer';
  940. /**
  941. * Scrollbars are enabled on non-touch devices (not including Firefox because touch events
  942. * cannot be detected in Firefox, see above).
  943. */
  944. Graph.prototype.defaultScrollbars = !mxClient.IS_IOS;
  945. /**
  946. * Specifies if the page should be visible for new files. Default is true.
  947. */
  948. Graph.prototype.defaultPageVisible = true;
  949. /**
  950. * Specifies if the app should run in chromeless mode. Default is false.
  951. * This default is only used if the contructor argument is null.
  952. */
  953. Graph.prototype.lightbox = false;
  954. /**
  955. *
  956. */
  957. Graph.prototype.defaultPageBackgroundColor = '#ffffff';
  958. /**
  959. *
  960. */
  961. Graph.prototype.defaultPageBorderColor = '#ffffff';
  962. /**
  963. * Specifies the size of the size for "tiles" to be used for a graph with
  964. * scrollbars but no visible background page. A good value is large
  965. * enough to reduce the number of repaints that is caused for auto-
  966. * translation, which depends on this value, and small enough to give
  967. * a small empty buffer around the graph. Default is 400x400.
  968. */
  969. Graph.prototype.scrollTileSize = new mxRectangle(0, 0, 400, 400);
  970. /**
  971. * Overrides the background color and paints a transparent background.
  972. */
  973. Graph.prototype.transparentBackground = true;
  974. /**
  975. * Sets global constants.
  976. */
  977. Graph.prototype.selectParentAfterDelete = false;
  978. /**
  979. * Sets the default target for all links in cells.
  980. */
  981. Graph.prototype.defaultEdgeLength = 80;
  982. /**
  983. * Disables move of bends/segments without selecting.
  984. */
  985. Graph.prototype.edgeMode = false;
  986. /**
  987. * Allows all values in fit.
  988. */
  989. Graph.prototype.connectionArrowsEnabled = true;
  990. /**
  991. * Specifies the regular expression for matching placeholders.
  992. */
  993. Graph.prototype.placeholderPattern = new RegExp('%(date\{.*\}|[^%^\{^\}]+)%', 'g');
  994. /**
  995. * Specifies the regular expression for matching placeholders.
  996. */
  997. Graph.prototype.absoluteUrlPattern = new RegExp('^(?:[a-z]+:)?//', 'i');
  998. /**
  999. * Specifies the default name for the theme. Default is 'default'.
  1000. */
  1001. Graph.prototype.defaultThemeName = 'default';
  1002. /**
  1003. * Specifies the default name for the theme. Default is 'default'.
  1004. */
  1005. Graph.prototype.defaultThemes = {};
  1006. /**
  1007. * Base URL for relative links.
  1008. */
  1009. Graph.prototype.baseUrl = (urlParams['base'] != null) ?
  1010. decodeURIComponent(urlParams['base']) :
  1011. (((window != window.top) ? document.referrer :
  1012. document.location.toString()).split('#')[0]);
  1013. /**
  1014. * Specifies if the label should be edited after an insert.
  1015. */
  1016. Graph.prototype.editAfterInsert = false;
  1017. /**
  1018. * Defines the built-in properties to be ignored in tooltips.
  1019. */
  1020. Graph.prototype.builtInProperties = ['label', 'tooltip', 'placeholders', 'placeholder'];
  1021. /**
  1022. * Installs child layout styles.
  1023. */
  1024. Graph.prototype.init = function(container)
  1025. {
  1026. mxGraph.prototype.init.apply(this, arguments);
  1027. // Intercepts links with no target attribute and opens in new window
  1028. this.cellRenderer.initializeLabel = function(state, shape)
  1029. {
  1030. mxCellRenderer.prototype.initializeLabel.apply(this, arguments);
  1031. // Checks tolerance for clicks on links
  1032. var tol = state.view.graph.tolerance;
  1033. var handleClick = true;
  1034. var first = null;
  1035. var down = mxUtils.bind(this, function(evt)
  1036. {
  1037. handleClick = true;
  1038. first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  1039. });
  1040. var move = mxUtils.bind(this, function(evt)
  1041. {
  1042. handleClick = handleClick && first != null &&
  1043. Math.abs(first.x - mxEvent.getClientX(evt)) < tol &&
  1044. Math.abs(first.y - mxEvent.getClientY(evt)) < tol;
  1045. });
  1046. var up = mxUtils.bind(this, function(evt)
  1047. {
  1048. if (handleClick)
  1049. {
  1050. var elt = mxEvent.getSource(evt)
  1051. while (elt != null && elt != shape.node)
  1052. {
  1053. if (elt.nodeName.toLowerCase() == 'a')
  1054. {
  1055. state.view.graph.labelLinkClicked(state, elt, evt);
  1056. break;
  1057. }
  1058. elt = elt.parentNode;
  1059. }
  1060. }
  1061. });
  1062. mxEvent.addGestureListeners(shape.node, down, move, up);
  1063. mxEvent.addListener(shape.node, 'click', function(evt)
  1064. {
  1065. mxEvent.consume(evt);
  1066. });
  1067. };
  1068. this.initLayoutManager();
  1069. };
  1070. /**
  1071. * Implements zoom and offset via CSS transforms. This is currently only used
  1072. * in read-only as there are fewer issues with the mxCellState not being scaled
  1073. * and translated.
  1074. *
  1075. * KNOWN ISSUES TO FIX:
  1076. * - Apply CSS transforms to HTML labels in IE11
  1077. */
  1078. (function()
  1079. {
  1080. /**
  1081. * Uses CSS transforms for scale and translate.
  1082. */
  1083. Graph.prototype.useCssTransforms = false;
  1084. /**
  1085. * Contains the scale.
  1086. */
  1087. Graph.prototype.currentScale = 1;
  1088. /**
  1089. * Contains the offset.
  1090. */
  1091. Graph.prototype.currentTranslate = new mxPoint(0, 0);
  1092. /**
  1093. * Only foreignObject supported for now (no IE11).
  1094. */
  1095. Graph.prototype.isCssTransformsSupported = function()
  1096. {
  1097. return this.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO;
  1098. };
  1099. /**
  1100. * Function: getCellAt
  1101. *
  1102. * Needs to modify original method for recursive call.
  1103. */
  1104. Graph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn)
  1105. {
  1106. if (this.useCssTransforms)
  1107. {
  1108. x = x / this.currentScale - this.currentTranslate.x;
  1109. y = y / this.currentScale - this.currentTranslate.y;
  1110. }
  1111. return this.getScaledCellAt.apply(this, arguments);
  1112. };
  1113. /**
  1114. * Function: getScaledCellAt
  1115. *
  1116. * Overridden for recursion.
  1117. */
  1118. Graph.prototype.getScaledCellAt = function(x, y, parent, vertices, edges, ignoreFn)
  1119. {
  1120. vertices = (vertices != null) ? vertices : true;
  1121. edges = (edges != null) ? edges : true;
  1122. if (parent == null)
  1123. {
  1124. parent = this.getCurrentRoot();
  1125. if (parent == null)
  1126. {
  1127. parent = this.getModel().getRoot();
  1128. }
  1129. }
  1130. if (parent != null)
  1131. {
  1132. var childCount = this.model.getChildCount(parent);
  1133. for (var i = childCount - 1; i >= 0; i--)
  1134. {
  1135. var cell = this.model.getChildAt(parent, i);
  1136. var result = this.getScaledCellAt(x, y, cell, vertices, edges, ignoreFn);
  1137. if (result != null)
  1138. {
  1139. return result;
  1140. }
  1141. else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) ||
  1142. vertices && this.model.isVertex(cell)))
  1143. {
  1144. var state = this.view.getState(cell);
  1145. if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) &&
  1146. this.intersects(state, x, y))
  1147. {
  1148. return cell;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. return null;
  1154. };
  1155. /**
  1156. * Function: repaint
  1157. *
  1158. * Updates the highlight after a change of the model or view.
  1159. */
  1160. mxCellHighlight.prototype.getStrokeWidth = function(state)
  1161. {
  1162. var s = this.strokeWidth;
  1163. if (this.graph.useCssTransforms)
  1164. {
  1165. s /= this.graph.currentScale;
  1166. }
  1167. return s;
  1168. };
  1169. /**
  1170. * Function: getGraphBounds
  1171. *
  1172. * Overrides getGraphBounds to use bounding box from SVG.
  1173. */
  1174. mxGraphView.prototype.getGraphBounds = function()
  1175. {
  1176. var b = this.graphBounds;
  1177. if (this.graph.useCssTransforms)
  1178. {
  1179. var t = this.graph.currentTranslate;
  1180. var s = this.graph.currentScale;
  1181. b = new mxRectangle(
  1182. (b.x + t.x) * s, (b.y + t.y) * s,
  1183. b.width * s, b.height * s);
  1184. }
  1185. return b;
  1186. };
  1187. /**
  1188. * Function: viewStateChanged
  1189. *
  1190. * Overrides to bypass full cell tree validation.
  1191. * TODO: Check if this improves performance
  1192. */
  1193. mxGraphView.prototype.viewStateChanged = function()
  1194. {
  1195. if (this.graph.useCssTransforms)
  1196. {
  1197. this.validate();
  1198. this.graph.sizeDidChange();
  1199. }
  1200. else
  1201. {
  1202. this.revalidate();
  1203. this.graph.sizeDidChange();
  1204. }
  1205. };
  1206. /**
  1207. * Function: validate
  1208. *
  1209. * Overrides validate to normalize validation view state and pass
  1210. * current state to CSS transform.
  1211. */
  1212. var graphViewValidate = mxGraphView.prototype.validate;
  1213. mxGraphView.prototype.validate = function(cell)
  1214. {
  1215. if (this.graph.useCssTransforms)
  1216. {
  1217. this.graph.currentScale = this.scale;
  1218. this.graph.currentTranslate.x = this.translate.x;
  1219. this.graph.currentTranslate.y = this.translate.y;
  1220. this.scale = 1;
  1221. this.translate.x = 0;
  1222. this.translate.y = 0;
  1223. }
  1224. graphViewValidate.apply(this, arguments);
  1225. if (this.graph.useCssTransforms)
  1226. {
  1227. this.graph.updateCssTransform();
  1228. this.scale = this.graph.currentScale;
  1229. this.translate.x = this.graph.currentTranslate.x;
  1230. this.translate.y = this.graph.currentTranslate.y;
  1231. }
  1232. };
  1233. /**
  1234. * Function: updateCssTransform
  1235. *
  1236. * Zooms out of the graph by <zoomFactor>.
  1237. */
  1238. Graph.prototype.updateCssTransform = function()
  1239. {
  1240. var temp = this.view.getDrawPane();
  1241. if (temp != null)
  1242. {
  1243. var g = temp.parentNode;
  1244. if (!this.useCssTransforms)
  1245. {
  1246. g.removeAttribute('transformOrigin');
  1247. g.removeAttribute('transform');
  1248. }
  1249. else
  1250. {
  1251. var prev = g.getAttribute('transform');
  1252. g.setAttribute('transformOrigin', '0 0');
  1253. g.setAttribute('transform', 'scale(' + this.currentScale + ',' + this.currentScale + ')' +
  1254. 'translate(' + this.currentTranslate.x + ',' + this.currentTranslate.y + ')');
  1255. // Applies workarounds only if translate has changed
  1256. if (prev != g.getAttribute('transform'))
  1257. {
  1258. try
  1259. {
  1260. // Applies transform to labels outside of the SVG DOM
  1261. // Excluded via isCssTransformsSupported
  1262. // if (mxClient.NO_FO)
  1263. // {
  1264. // var transform = 'scale(' + this.currentScale + ')' + 'translate(' +
  1265. // this.currentTranslate.x + 'px,' + this.currentTranslate.y + 'px)';
  1266. //
  1267. // this.view.states.visit(mxUtils.bind(this, function(cell, state)
  1268. // {
  1269. // if (state.text != null && state.text.node != null)
  1270. // {
  1271. // // Stores initial CSS transform that is used for the label alignment
  1272. // if (state.text.originalTransform == null)
  1273. // {
  1274. // state.text.originalTransform = state.text.node.style.transform;
  1275. // }
  1276. //
  1277. // state.text.node.style.transform = transform + state.text.originalTransform;
  1278. // }
  1279. // }));
  1280. // }
  1281. // Workaround for https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4320441/
  1282. if (mxClient.IS_EDGE)
  1283. {
  1284. // Recommended workaround is to do this on all
  1285. // foreignObjects, but this seems to be faster
  1286. var val = g.style.display;
  1287. g.style.display = 'none';
  1288. g.getBBox();
  1289. g.style.display = val;
  1290. }
  1291. }
  1292. catch (e)
  1293. {
  1294. // ignore
  1295. }
  1296. }
  1297. }
  1298. }
  1299. };
  1300. var graphViewValidateBackgroundPage = mxGraphView.prototype.validateBackgroundPage;
  1301. mxGraphView.prototype.validateBackgroundPage = function()
  1302. {
  1303. var useCssTranforms = this.graph.useCssTransforms, scale = this.scale,
  1304. translate = this.translate;
  1305. if (useCssTranforms)
  1306. {
  1307. this.scale = this.graph.currentScale;
  1308. this.translate = this.graph.currentTranslate;
  1309. }
  1310. graphViewValidateBackgroundPage.apply(this, arguments);
  1311. if (useCssTranforms)
  1312. {
  1313. this.scale = scale;
  1314. this.translate = translate;
  1315. }
  1316. };
  1317. var graphUpdatePageBreaks = mxGraph.prototype.updatePageBreaks;
  1318. mxGraph.prototype.updatePageBreaks = function(visible, width, height)
  1319. {
  1320. var useCssTranforms = this.useCssTransforms, scale = this.view.scale,
  1321. translate = this.view.translate;
  1322. if (useCssTranforms)
  1323. {
  1324. this.view.scale = 1;
  1325. this.view.translate = new mxPoint(0, 0);
  1326. this.useCssTransforms = false;
  1327. }
  1328. graphUpdatePageBreaks.apply(this, arguments);
  1329. if (useCssTranforms)
  1330. {
  1331. this.view.scale = scale;
  1332. this.view.translate = translate;
  1333. this.useCssTransforms = true;
  1334. }
  1335. };
  1336. })();
  1337. /**
  1338. * Sets the XML node for the current diagram.
  1339. */
  1340. Graph.prototype.isLightboxView = function()
  1341. {
  1342. return this.lightbox;
  1343. };
  1344. /**
  1345. * Installs automatic layout via styles
  1346. */
  1347. Graph.prototype.labelLinkClicked = function(state, elt, evt)
  1348. {
  1349. var href = elt.getAttribute('href');
  1350. if (href != null && !this.isCustomLink(href) && (mxEvent.isLeftMouseButton(evt) &&
  1351. !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt))
  1352. {
  1353. if (!this.isEnabled() || this.isCellLocked(state.cell))
  1354. {
  1355. var target = this.isBlankLink(href) ? this.linkTarget : '_top';
  1356. this.openLink(this.getAbsoluteUrl(href), target);
  1357. }
  1358. mxEvent.consume(evt);
  1359. }
  1360. };
  1361. /**
  1362. * Returns the size of the page format scaled with the page size.
  1363. */
  1364. Graph.prototype.openLink = function(href, target, allowOpener)
  1365. {
  1366. var result = window;
  1367. try
  1368. {
  1369. // Workaround for blocking in same iframe
  1370. if (target == '_self' && window != window.top)
  1371. {
  1372. window.location.href = href;
  1373. }
  1374. else
  1375. {
  1376. // Avoids page reload for anchors (workaround for IE but used everywhere)
  1377. if (href.substring(0, this.baseUrl.length) == this.baseUrl &&
  1378. href.charAt(this.baseUrl.length) == '#' &&
  1379. target == '_top' && window == window.top)
  1380. {
  1381. var hash = href.split('#')[1];
  1382. // Forces navigation if on same hash
  1383. if (window.location.hash == '#' + hash)
  1384. {
  1385. window.location.hash = '';
  1386. }
  1387. window.location.hash = hash;
  1388. }
  1389. else
  1390. {
  1391. result = window.open(href, target);
  1392. if (result != null && !allowOpener)
  1393. {
  1394. result.opener = null;
  1395. }
  1396. }
  1397. }
  1398. }
  1399. catch (e)
  1400. {
  1401. // ignores permission denied
  1402. }
  1403. return result;
  1404. };
  1405. /**
  1406. * Adds support for page links.
  1407. */
  1408. Graph.prototype.getLinkTitle = function(href)
  1409. {
  1410. return href.substring(href.lastIndexOf('/') + 1);
  1411. };
  1412. /**
  1413. * Adds support for page links.
  1414. */
  1415. Graph.prototype.isCustomLink = function(href)
  1416. {
  1417. return href.substring(0, 5) == 'data:';
  1418. };
  1419. /**
  1420. * Adds support for page links.
  1421. */
  1422. Graph.prototype.customLinkClicked = function(link)
  1423. {
  1424. return false;
  1425. };
  1426. /**
  1427. * Returns true if the fiven href references an external protocol that
  1428. * should never open in a new window. Default returns true for mailto.
  1429. */
  1430. Graph.prototype.isExternalProtocol = function(href)
  1431. {
  1432. return href.substring(0, 7) === 'mailto:';
  1433. };
  1434. /**
  1435. * Hook for links to open in same window. Default returns true for anchors,
  1436. * links to same domain or if target == 'self' in the config.
  1437. */
  1438. Graph.prototype.isBlankLink = function(href)
  1439. {
  1440. return !this.isExternalProtocol(href) &&
  1441. (this.linkPolicy === 'blank' ||
  1442. (this.linkPolicy !== 'self' &&
  1443. !this.isRelativeUrl(href) &&
  1444. href.substring(0, this.domainUrl.length) !== this.domainUrl));
  1445. };
  1446. /**
  1447. *
  1448. */
  1449. Graph.prototype.isRelativeUrl = function(url)
  1450. {
  1451. return url != null && !this.absoluteUrlPattern.test(url) &&
  1452. url.substring(0, 5) !== 'data:' &&
  1453. !this.isExternalProtocol(url);
  1454. };
  1455. /**
  1456. *
  1457. */
  1458. Graph.prototype.getAbsoluteUrl = function(url)
  1459. {
  1460. if (url != null && this.isRelativeUrl(url))
  1461. {
  1462. if (url.charAt(0) == '#')
  1463. {
  1464. url = this.baseUrl + url;
  1465. }
  1466. else if (url.charAt(0) == '/')
  1467. {
  1468. url = this.domainUrl + url;
  1469. }
  1470. else
  1471. {
  1472. url = this.domainPathUrl + url;
  1473. }
  1474. }
  1475. return url;
  1476. };
  1477. /**
  1478. * Installs automatic layout via styles
  1479. */
  1480. Graph.prototype.initLayoutManager = function()
  1481. {
  1482. this.layoutManager = new mxLayoutManager(this);
  1483. this.layoutManager.getLayout = function(cell)
  1484. {
  1485. // Workaround for possible invalid style after change and before view validation
  1486. var style = this.graph.getCellStyle(cell);
  1487. if (style != null)
  1488. {
  1489. if (style['childLayout'] == 'stackLayout')
  1490. {
  1491. var stackLayout = new mxStackLayout(this.graph, true);
  1492. stackLayout.resizeParentMax = mxUtils.getValue(style, 'resizeParentMax', '1') == '1';
  1493. stackLayout.horizontal = mxUtils.getValue(style, 'horizontalStack', '1') == '1';
  1494. stackLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1';
  1495. stackLayout.resizeLast = mxUtils.getValue(style, 'resizeLast', '0') == '1';
  1496. stackLayout.spacing = style['stackSpacing'] || stackLayout.spacing;
  1497. stackLayout.border = style['stackBorder'] || stackLayout.border;
  1498. stackLayout.marginLeft = style['marginLeft'] || 0;
  1499. stackLayout.marginRight = style['marginRight'] || 0;
  1500. stackLayout.marginTop = style['marginTop'] || 0;
  1501. stackLayout.marginBottom = style['marginBottom'] || 0;
  1502. stackLayout.fill = true;
  1503. return stackLayout;
  1504. }
  1505. else if (style['childLayout'] == 'treeLayout')
  1506. {
  1507. var treeLayout = new mxCompactTreeLayout(this.graph);
  1508. treeLayout.horizontal = mxUtils.getValue(style, 'horizontalTree', '1') == '1';
  1509. treeLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1';
  1510. treeLayout.groupPadding = mxUtils.getValue(style, 'parentPadding', 20);
  1511. treeLayout.levelDistance = mxUtils.getValue(style, 'treeLevelDistance', 30);
  1512. treeLayout.maintainParentLocation = true;
  1513. treeLayout.edgeRouting = false;
  1514. treeLayout.resetEdges = false;
  1515. return treeLayout;
  1516. }
  1517. else if (style['childLayout'] == 'flowLayout')
  1518. {
  1519. var flowLayout = new mxHierarchicalLayout(this.graph, mxUtils.getValue(style,
  1520. 'flowOrientation', mxConstants.DIRECTION_EAST));
  1521. flowLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1';
  1522. flowLayout.parentBorder = mxUtils.getValue(style, 'parentPadding', 20);
  1523. flowLayout.maintainParentLocation = true;
  1524. // Special undocumented styles for changing the hierarchical
  1525. flowLayout.intraCellSpacing = mxUtils.getValue(style, 'intraCellSpacing', mxHierarchicalLayout.prototype.intraCellSpacing);
  1526. flowLayout.interRankCellSpacing = mxUtils.getValue(style, 'interRankCellSpacing', mxHierarchicalLayout.prototype.interRankCellSpacing);
  1527. flowLayout.interHierarchySpacing = mxUtils.getValue(style, 'interHierarchySpacing', mxHierarchicalLayout.prototype.interHierarchySpacing);
  1528. flowLayout.parallelEdgeSpacing = mxUtils.getValue(style, 'parallelEdgeSpacing', mxHierarchicalLayout.prototype.parallelEdgeSpacing);
  1529. return flowLayout;
  1530. }
  1531. }
  1532. return null;
  1533. };
  1534. };
  1535. /**
  1536. * Returns the size of the page format scaled with the page size.
  1537. */
  1538. Graph.prototype.getPageSize = function()
  1539. {
  1540. return (this.pageVisible) ? new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,
  1541. this.pageFormat.height * this.pageScale) : this.scrollTileSize;
  1542. };
  1543. /**
  1544. * Returns a rectangle describing the position and count of the
  1545. * background pages, where x and y are the position of the top,
  1546. * left page and width and height are the vertical and horizontal
  1547. * page count.
  1548. */
  1549. Graph.prototype.getPageLayout = function()
  1550. {
  1551. var size = this.getPageSize();
  1552. var bounds = this.getGraphBounds();
  1553. if (bounds.width == 0 || bounds.height == 0)
  1554. {
  1555. return new mxRectangle(0, 0, 1, 1);
  1556. }
  1557. else
  1558. {
  1559. // Computes untransformed graph bounds
  1560. var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);
  1561. var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);
  1562. var w = Math.floor(bounds.width / this.view.scale);
  1563. var h = Math.floor(bounds.height / this.view.scale);
  1564. var x0 = Math.floor(x / size.width);
  1565. var y0 = Math.floor(y / size.height);
  1566. var w0 = Math.ceil((x + w) / size.width) - x0;
  1567. var h0 = Math.ceil((y + h) / size.height) - y0;
  1568. return new mxRectangle(x0, y0, w0, h0);
  1569. }
  1570. };
  1571. /**
  1572. * Sanitizes the given HTML markup.
  1573. */
  1574. Graph.prototype.sanitizeHtml = function(value, editing)
  1575. {
  1576. // Uses https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer
  1577. // NOTE: Original minimized sanitizer was modified to support
  1578. // data URIs for images, mailto and special data:-links.
  1579. // LATER: Add MathML to whitelisted tags
  1580. function urlX(link)
  1581. {
  1582. if (link != null && link.toString().toLowerCase().substring(0, 11) !== 'javascript:')
  1583. {
  1584. return link;
  1585. }
  1586. return null;
  1587. };
  1588. function idX(id) { return id };
  1589. return html_sanitize(value, urlX, idX);
  1590. };
  1591. /**
  1592. * Revalidates all cells with placeholders in the current graph model.
  1593. */
  1594. Graph.prototype.updatePlaceholders = function()
  1595. {
  1596. var model = this.model;
  1597. var validate = false;
  1598. for (var key in this.model.cells)
  1599. {
  1600. var cell = this.model.cells[key];
  1601. if (this.isReplacePlaceholders(cell))
  1602. {
  1603. this.view.invalidate(cell, false, false);
  1604. validate = true;
  1605. }
  1606. }
  1607. if (validate)
  1608. {
  1609. this.view.validate();
  1610. }
  1611. };
  1612. /**
  1613. * Adds support for placeholders in labels.
  1614. */
  1615. Graph.prototype.isReplacePlaceholders = function(cell)
  1616. {
  1617. return cell.value != null && typeof(cell.value) == 'object' &&
  1618. cell.value.getAttribute('placeholders') == '1';
  1619. };
  1620. /**
  1621. * Returns true if the given mouse wheel event should be used for zooming. This
  1622. * is invoked if no dialogs are showing and returns true with Alt or Control
  1623. * (except macOS) is pressed.
  1624. */
  1625. Graph.prototype.isZoomWheelEvent = function(evt)
  1626. {
  1627. return mxEvent.isAltDown(evt) || (mxEvent.isMetaDown(evt) && mxClient.IS_MAC) ||
  1628. (mxEvent.isControlDown(evt) && !mxClient.IS_MAC);
  1629. };
  1630. /**
  1631. * Adds Alt+click to select cells behind cells.
  1632. */
  1633. Graph.prototype.isTransparentClickEvent = function(evt)
  1634. {
  1635. return mxEvent.isAltDown(evt);
  1636. };
  1637. /**
  1638. * Adds ctrl+shift+connect to disable connections.
  1639. */
  1640. Graph.prototype.isIgnoreTerminalEvent = function(evt)
  1641. {
  1642. return mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt);
  1643. };
  1644. /**
  1645. * Adds support for placeholders in labels.
  1646. */
  1647. Graph.prototype.isSplitTarget = function(target, cells, evt)
  1648. {
  1649. return !this.model.isEdge(cells[0]) &&
  1650. !mxEvent.isAltDown(evt) && !mxEvent.isShiftDown(evt) &&
  1651. mxGraph.prototype.isSplitTarget.apply(this, arguments);
  1652. };
  1653. /**
  1654. * Adds support for placeholders in labels.
  1655. */
  1656. Graph.prototype.getLabel = function(cell)
  1657. {
  1658. var result = mxGraph.prototype.getLabel.apply(this, arguments);
  1659. if (result != null && this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') == null)
  1660. {
  1661. result = this.replacePlaceholders(cell, result);
  1662. }
  1663. return result;
  1664. };
  1665. /**
  1666. * Adds labelMovable style.
  1667. */
  1668. Graph.prototype.isLabelMovable = function(cell)
  1669. {
  1670. var state = this.view.getState(cell);
  1671. var style = (state != null) ? state.style : this.getCellStyle(cell);
  1672. return !this.isCellLocked(cell) &&
  1673. ((this.model.isEdge(cell) && this.edgeLabelsMovable) ||
  1674. (this.model.isVertex(cell) && (this.vertexLabelsMovable ||
  1675. mxUtils.getValue(style, 'labelMovable', '0') == '1')));
  1676. };
  1677. /**
  1678. * Adds event if grid size is changed.
  1679. */
  1680. Graph.prototype.setGridSize = function(value)
  1681. {
  1682. this.gridSize = value;
  1683. this.fireEvent(new mxEventObject('gridSizeChanged'));
  1684. };
  1685. /**
  1686. * Private helper method.
  1687. */
  1688. Graph.prototype.getGlobalVariable = function(name)
  1689. {
  1690. var val = null;
  1691. if (name == 'date')
  1692. {
  1693. val = new Date().toLocaleDateString();
  1694. }
  1695. else if (name == 'time')
  1696. {
  1697. val = new Date().toLocaleTimeString();
  1698. }
  1699. else if (name == 'timestamp')
  1700. {
  1701. val = new Date().toLocaleString();
  1702. }
  1703. else if (name.substring(0, 5) == 'date{')
  1704. {
  1705. var fmt = name.substring(5, name.length - 1);
  1706. val = this.formatDate(new Date(), fmt);
  1707. }
  1708. return val;
  1709. };
  1710. /**
  1711. * Formats a date, see http://blog.stevenlevithan.com/archives/date-time-format
  1712. */
  1713. Graph.prototype.formatDate = function(date, mask, utc)
  1714. {
  1715. // LATER: Cache regexs
  1716. if (this.dateFormatCache == null)
  1717. {
  1718. this.dateFormatCache = {
  1719. i18n: {
  1720. dayNames: [
  1721. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
  1722. "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
  1723. ],
  1724. monthNames: [
  1725. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  1726. "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
  1727. ]
  1728. },
  1729. masks: {
  1730. "default": "ddd mmm dd yyyy HH:MM:ss",
  1731. shortDate: "m/d/yy",
  1732. mediumDate: "mmm d, yyyy",
  1733. longDate: "mmmm d, yyyy",
  1734. fullDate: "dddd, mmmm d, yyyy",
  1735. shortTime: "h:MM TT",
  1736. mediumTime: "h:MM:ss TT",
  1737. longTime: "h:MM:ss TT Z",
  1738. isoDate: "yyyy-mm-dd",
  1739. isoTime: "HH:MM:ss",
  1740. isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
  1741. isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
  1742. }
  1743. };
  1744. }
  1745. var dF = this.dateFormatCache;
  1746. var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
  1747. timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
  1748. timezoneClip = /[^-+\dA-Z]/g,
  1749. pad = function (val, len) {
  1750. val = String(val);
  1751. len = len || 2;
  1752. while (val.length < len) val = "0" + val;
  1753. return val;
  1754. };
  1755. // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
  1756. if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
  1757. mask = date;
  1758. date = undefined;
  1759. }
  1760. // Passing date through Date applies Date.parse, if necessary
  1761. date = date ? new Date(date) : new Date;
  1762. if (isNaN(date)) throw SyntaxError("invalid date");
  1763. mask = String(dF.masks[mask] || mask || dF.masks["default"]);
  1764. // Allow setting the utc argument via the mask
  1765. if (mask.slice(0, 4) == "UTC:") {
  1766. mask = mask.slice(4);
  1767. utc = true;
  1768. }
  1769. var _ = utc ? "getUTC" : "get",
  1770. d = date[_ + "Date"](),
  1771. D = date[_ + "Day"](),
  1772. m = date[_ + "Month"](),
  1773. y = date[_ + "FullYear"](),
  1774. H = date[_ + "Hours"](),
  1775. M = date[_ + "Minutes"](),
  1776. s = date[_ + "Seconds"](),
  1777. L = date[_ + "Milliseconds"](),
  1778. o = utc ? 0 : date.getTimezoneOffset(),
  1779. flags = {
  1780. d: d,
  1781. dd: pad(d),
  1782. ddd: dF.i18n.dayNames[D],
  1783. dddd: dF.i18n.dayNames[D + 7],
  1784. m: m + 1,
  1785. mm: pad(m + 1),
  1786. mmm: dF.i18n.monthNames[m],
  1787. mmmm: dF.i18n.monthNames[m + 12],
  1788. yy: String(y).slice(2),
  1789. yyyy: y,
  1790. h: H % 12 || 12,
  1791. hh: pad(H % 12 || 12),
  1792. H: H,
  1793. HH: pad(H),
  1794. M: M,
  1795. MM: pad(M),
  1796. s: s,
  1797. ss: pad(s),
  1798. l: pad(L, 3),
  1799. L: pad(L > 99 ? Math.round(L / 10) : L),
  1800. t: H < 12 ? "a" : "p",
  1801. tt: H < 12 ? "am" : "pm",
  1802. T: H < 12 ? "A" : "P",
  1803. TT: H < 12 ? "AM" : "PM",
  1804. Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
  1805. o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
  1806. S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
  1807. };
  1808. return mask.replace(token, function ($0)
  1809. {
  1810. return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
  1811. });
  1812. };
  1813. /**
  1814. *
  1815. */
  1816. Graph.prototype.createLayersDialog = function()
  1817. {
  1818. var div = document.createElement('div');
  1819. div.style.position = 'absolute';
  1820. var model = this.getModel();
  1821. var childCount = model.getChildCount(model.root);
  1822. for (var i = 0; i < childCount; i++)
  1823. {
  1824. (mxUtils.bind(this, function(layer)
  1825. {
  1826. var span = document.createElement('div');
  1827. span.style.overflow = 'hidden';
  1828. span.style.textOverflow = 'ellipsis';
  1829. span.style.padding = '2px';
  1830. span.style.whiteSpace = 'nowrap';
  1831. var cb = document.createElement('input');
  1832. cb.style.display = 'inline-block';
  1833. cb.setAttribute('type', 'checkbox');
  1834. if (model.isVisible(layer))
  1835. {
  1836. cb.setAttribute('checked', 'checked');
  1837. cb.defaultChecked = true;
  1838. }
  1839. span.appendChild(cb);
  1840. var title = this.convertValueToString(layer) || (mxResources.get('background') || 'Background');
  1841. span.setAttribute('title', title);
  1842. mxUtils.write(span, title);
  1843. div.appendChild(span);
  1844. mxEvent.addListener(cb, 'click', function()
  1845. {
  1846. if (cb.getAttribute('checked') != null)
  1847. {
  1848. cb.removeAttribute('checked');
  1849. }
  1850. else
  1851. {
  1852. cb.setAttribute('checked', 'checked');
  1853. }
  1854. model.setVisible(layer, cb.checked);
  1855. });
  1856. })(model.getChildAt(model.root, i)));
  1857. }
  1858. return div;
  1859. };
  1860. /**
  1861. * Private helper method.
  1862. */
  1863. Graph.prototype.replacePlaceholders = function(cell, str)
  1864. {
  1865. var result = [];
  1866. if (str != null)
  1867. {
  1868. var last = 0;
  1869. while (match = this.placeholderPattern.exec(str))
  1870. {
  1871. var val = match[0];
  1872. if (val.length > 2 && val != '%label%' && val != '%tooltip%')
  1873. {
  1874. var tmp = null;
  1875. if (match.index > last && str.charAt(match.index - 1) == '%')
  1876. {
  1877. tmp = val.substring(1);
  1878. }
  1879. else
  1880. {
  1881. var name = val.substring(1, val.length - 1);
  1882. // Workaround for invalid char for getting attribute in older versions of IE
  1883. if (name.indexOf('{') < 0)
  1884. {
  1885. var current = cell;
  1886. while (tmp == null && current != null)
  1887. {
  1888. if (current.value != null && typeof(current.value) == 'object')
  1889. {
  1890. tmp = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ?
  1891. current.getAttribute(name) : '') : null;
  1892. }
  1893. current = this.model.getParent(current);
  1894. }
  1895. }
  1896. if (tmp == null)
  1897. {
  1898. tmp = this.getGlobalVariable(name);
  1899. }
  1900. }
  1901. result.push(str.substring(last, match.index) + ((tmp != null) ? tmp : val));
  1902. last = match.index + val.length;
  1903. }
  1904. }
  1905. result.push(str.substring(last));
  1906. }
  1907. return result.join('');
  1908. };
  1909. /**
  1910. * Resolves the given cells in the model and selects them.
  1911. */
  1912. Graph.prototype.restoreSelection = function(cells)
  1913. {
  1914. if (cells != null && cells.length > 0)
  1915. {
  1916. var temp = [];
  1917. for (var i = 0; i < cells.length; i++)
  1918. {
  1919. var newCell = this.model.getCell(cells[i].id);
  1920. if (newCell != null)
  1921. {
  1922. temp.push(newCell);
  1923. }
  1924. }
  1925. this.setSelectionCells(temp);
  1926. }
  1927. else
  1928. {
  1929. this.clearSelection();
  1930. }
  1931. };
  1932. /**
  1933. * Selects cells for connect vertex return value.
  1934. */
  1935. Graph.prototype.selectCellsForConnectVertex = function(cells, evt, hoverIcons)
  1936. {
  1937. // Selects only target vertex if one exists
  1938. if (cells.length == 2 && this.model.isVertex(cells[1]))
  1939. {
  1940. this.setSelectionCell(cells[1]);
  1941. if (hoverIcons != null)
  1942. {
  1943. // Adds hover icons to new target vertex for touch devices
  1944. if (mxEvent.isTouchEvent(evt))
  1945. {
  1946. hoverIcons.update(hoverIcons.getState(this.view.getState(cells[1])));
  1947. }
  1948. else
  1949. {
  1950. // Hides hover icons after click with mouse
  1951. hoverIcons.reset();
  1952. }
  1953. }
  1954. this.scrollCellToVisible(cells[1]);
  1955. }
  1956. else
  1957. {
  1958. this.setSelectionCells(cells);
  1959. }
  1960. };
  1961. /**
  1962. * Adds a connection to the given vertex.
  1963. */
  1964. Graph.prototype.connectVertex = function(source, direction, length, evt, forceClone, ignoreCellAt)
  1965. {
  1966. // Ignores relative edge labels
  1967. if (source.geometry.relative && this.model.isEdge(source.parent))
  1968. {
  1969. return [];
  1970. }
  1971. ignoreCellAt = (ignoreCellAt) ? ignoreCellAt : false;
  1972. var pt = (source.geometry.relative && source.parent.geometry != null) ?
  1973. new mxPoint(source.parent.geometry.width * source.geometry.x, source.parent.geometry.height * source.geometry.y) :
  1974. new mxPoint(source.geometry.x, source.geometry.y);
  1975. if (direction == mxConstants.DIRECTION_NORTH)
  1976. {
  1977. pt.x += source.geometry.width / 2;
  1978. pt.y -= length ;
  1979. }
  1980. else if (direction == mxConstants.DIRECTION_SOUTH)
  1981. {
  1982. pt.x += source.geometry.width / 2;
  1983. pt.y += source.geometry.height + length;
  1984. }
  1985. else if (direction == mxConstants.DIRECTION_WEST)
  1986. {
  1987. pt.x -= length;
  1988. pt.y += source.geometry.height / 2;
  1989. }
  1990. else
  1991. {
  1992. pt.x += source.geometry.width + length;
  1993. pt.y += source.geometry.height / 2;
  1994. }
  1995. var parentState = this.view.getState(this.model.getParent(source));
  1996. var s = this.view.scale;
  1997. var t = this.view.translate;
  1998. var dx = t.x * s;
  1999. var dy = t.y * s;
  2000. if (parentState != null && this.model.isVertex(parentState.cell))
  2001. {
  2002. dx = parentState.x;
  2003. dy = parentState.y;
  2004. }
  2005. // Workaround for relative child cells
  2006. if (this.model.isVertex(source.parent) && source.geometry.relative)
  2007. {
  2008. pt.x += source.parent.geometry.x;
  2009. pt.y += source.parent.geometry.y;
  2010. }
  2011. // Checks actual end point of edge for target cell
  2012. var target = (ignoreCellAt || (mxEvent.isControlDown(evt) && !forceClone)) ?
  2013. null : this.getCellAt(dx + pt.x * s, dy + pt.y * s);
  2014. if (this.model.isAncestor(target, source))
  2015. {
  2016. target = null;
  2017. }
  2018. // Checks if target or ancestor is locked
  2019. var temp = target;
  2020. while (temp != null)
  2021. {
  2022. if (this.isCellLocked(temp))
  2023. {
  2024. target = null;
  2025. break;
  2026. }
  2027. temp = this.model.getParent(temp);
  2028. }
  2029. // Checks if source and target intersect
  2030. if (target != null)
  2031. {
  2032. var sourceState = this.view.getState(source);
  2033. var targetState = this.view.getState(target);
  2034. if (sourceState != null && targetState != null && mxUtils.intersects(sourceState, targetState))
  2035. {
  2036. target = null;
  2037. }
  2038. }
  2039. var duplicate = !mxEvent.isShiftDown(evt) || forceClone;
  2040. if (duplicate)
  2041. {
  2042. if (direction == mxConstants.DIRECTION_NORTH)
  2043. {
  2044. pt.y -= source.geometry.height / 2;
  2045. }
  2046. else if (direction == mxConstants.DIRECTION_SOUTH)
  2047. {
  2048. pt.y += source.geometry.height / 2;
  2049. }
  2050. else if (direction == mxConstants.DIRECTION_WEST)
  2051. {
  2052. pt.x -= source.geometry.width / 2;
  2053. }
  2054. else
  2055. {
  2056. pt.x += source.geometry.width / 2;
  2057. }
  2058. }
  2059. // Uses connectable parent vertex if one exists
  2060. if (target != null && !this.isCellConnectable(target))
  2061. {
  2062. var parent = this.getModel().getParent(target);
  2063. if (this.getModel().isVertex(parent) && this.isCellConnectable(parent))
  2064. {
  2065. target = parent;
  2066. }
  2067. }
  2068. if (target == source || this.model.isEdge(target) || !this.isCellConnectable(target))
  2069. {
  2070. target = null;
  2071. }
  2072. var result = [];
  2073. this.model.beginUpdate();
  2074. try
  2075. {
  2076. var realTarget = target;
  2077. if (realTarget == null && duplicate)
  2078. {
  2079. // Handles relative children
  2080. var cellToClone = source;
  2081. var geo = this.getCellGeometry(source);
  2082. while (geo != null && geo.relative)
  2083. {
  2084. cellToClone = this.getModel().getParent(cellToClone);
  2085. geo = this.getCellGeometry(cellToClone);
  2086. }
  2087. // Handle consistuents for cloning
  2088. var state = this.view.getState(cellToClone);
  2089. var style = (state != null) ? state.style : this.getCellStyle(cellToClone);
  2090. if (mxUtils.getValue(style, 'part', false))
  2091. {
  2092. var tmpParent = this.model.getParent(cellToClone);
  2093. if (this.model.isVertex(tmpParent))
  2094. {
  2095. cellToClone = tmpParent;
  2096. }
  2097. }
  2098. realTarget = this.duplicateCells([cellToClone], false)[0];
  2099. var geo = this.getCellGeometry(realTarget);
  2100. if (geo != null)
  2101. {
  2102. geo.x = pt.x - geo.width / 2;
  2103. geo.y = pt.y - geo.height / 2;
  2104. }
  2105. }
  2106. // Never connects children in stack layouts
  2107. var layout = null;
  2108. if (this.layoutManager != null)
  2109. {
  2110. layout = this.layoutManager.getLayout(this.model.getParent(source));
  2111. }
  2112. var edge = ((mxEvent.isControlDown(evt) && duplicate) || (target == null && layout != null && layout.constructor == mxStackLayout)) ? null :
  2113. this.insertEdge(this.model.getParent(source), null, '', source, realTarget, this.createCurrentEdgeStyle());
  2114. // Inserts edge before source
  2115. if (edge != null && this.connectionHandler.insertBeforeSource)
  2116. {
  2117. var index = null;
  2118. var tmp = source;
  2119. while (tmp.parent != null && tmp.geometry != null &&
  2120. tmp.geometry.relative && tmp.parent != edge.parent)
  2121. {
  2122. tmp = this.model.getParent(tmp);
  2123. }
  2124. if (tmp != null && tmp.parent != null && tmp.parent == edge.parent)
  2125. {
  2126. var index = tmp.parent.getIndex(tmp);
  2127. this.model.add(tmp.parent, edge, index);
  2128. }
  2129. }
  2130. // Special case: Click on west icon puts clone before cell
  2131. if (target == null && realTarget != null && layout != null && source.parent != null &&
  2132. layout.constructor == mxStackLayout && direction == mxConstants.DIRECTION_WEST)
  2133. {
  2134. var index = source.parent.getIndex(source);
  2135. this.model.add(source.parent, realTarget, index);
  2136. }
  2137. if (edge != null)
  2138. {
  2139. result.push(edge);
  2140. }
  2141. if (target == null && realTarget != null)
  2142. {
  2143. result.push(realTarget);
  2144. }
  2145. if (realTarget == null && edge != null)
  2146. {
  2147. edge.geometry.setTerminalPoint(pt, false);
  2148. }
  2149. if (edge != null)
  2150. {
  2151. this.fireEvent(new mxEventObject('cellsInserted', 'cells', [edge]));
  2152. }
  2153. }
  2154. finally
  2155. {
  2156. this.model.endUpdate();
  2157. }
  2158. return result;
  2159. };
  2160. /**
  2161. * Returns all labels in the diagram as a string.
  2162. */
  2163. Graph.prototype.getIndexableText = function()
  2164. {
  2165. var tmp = document.createElement('div');
  2166. var labels = [];
  2167. var label = '';
  2168. for (var key in this.model.cells)
  2169. {
  2170. var cell = this.model.cells[key];
  2171. if (this.model.isVertex(cell) || this.model.isEdge(cell))
  2172. {
  2173. if (this.isHtmlLabel(cell))
  2174. {
  2175. tmp.innerHTML = this.getLabel(cell);
  2176. label = mxUtils.extractTextWithWhitespace([tmp]);
  2177. }
  2178. else
  2179. {
  2180. label = this.getLabel(cell);
  2181. }
  2182. label = mxUtils.trim(label.replace(/[\x00-\x1F\x7F-\x9F]|\s+/g, ' '));
  2183. if (label.length > 0)
  2184. {
  2185. labels.push(label);
  2186. }
  2187. }
  2188. }
  2189. return labels.join(' ');
  2190. };
  2191. /**
  2192. * Returns the label for the given cell.
  2193. */
  2194. Graph.prototype.convertValueToString = function(cell)
  2195. {
  2196. if (cell.value != null && typeof(cell.value) == 'object')
  2197. {
  2198. if (this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') != null)
  2199. {
  2200. var name = cell.getAttribute('placeholder');
  2201. var current = cell;
  2202. var result = null;
  2203. while (result == null && current != null)
  2204. {
  2205. if (current.value != null && typeof(current.value) == 'object')
  2206. {
  2207. result = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ?
  2208. current.getAttribute(name) : '') : null;
  2209. }
  2210. current = this.model.getParent(current);
  2211. }
  2212. return result || '';
  2213. }
  2214. else
  2215. {
  2216. return cell.value.getAttribute('label') || '';
  2217. }
  2218. }
  2219. return mxGraph.prototype.convertValueToString.apply(this, arguments);
  2220. };
  2221. /**
  2222. * Returns the link for the given cell.
  2223. */
  2224. Graph.prototype.getLinksForState = function(state)
  2225. {
  2226. if (state != null && state.text != null && state.text.node != null)
  2227. {
  2228. return state.text.node.getElementsByTagName('a');
  2229. }
  2230. return null;
  2231. };
  2232. /**
  2233. * Returns the link for the given cell.
  2234. */
  2235. Graph.prototype.getLinkForCell = function(cell)
  2236. {
  2237. if (cell.value != null && typeof(cell.value) == 'object')
  2238. {
  2239. var link = cell.value.getAttribute('link');
  2240. // Removes links with leading javascript: protocol
  2241. // TODO: Check more possible attack vectors
  2242. if (link != null && link.toLowerCase().substring(0, 11) === 'javascript:')
  2243. {
  2244. link = link.substring(11);
  2245. }
  2246. return link;
  2247. }
  2248. return null;
  2249. };
  2250. /**
  2251. * Overrides label orientation for collapsed swimlanes inside stack.
  2252. */
  2253. Graph.prototype.getCellStyle = function(cell)
  2254. {
  2255. var style = mxGraph.prototype.getCellStyle.apply(this, arguments);
  2256. if (cell != null && this.layoutManager != null)
  2257. {
  2258. var parent = this.model.getParent(cell);
  2259. if (this.model.isVertex(parent) && this.isCellCollapsed(cell))
  2260. {
  2261. var layout = this.layoutManager.getLayout(parent);
  2262. if (layout != null && layout.constructor == mxStackLayout)
  2263. {
  2264. style[mxConstants.STYLE_HORIZONTAL] = !layout.horizontal;
  2265. }
  2266. }
  2267. }
  2268. return style;
  2269. };
  2270. /**
  2271. * Disables alternate width persistence for stack layout parents
  2272. */
  2273. Graph.prototype.updateAlternateBounds = function(cell, geo, willCollapse)
  2274. {
  2275. if (cell != null && geo != null && this.layoutManager != null && geo.alternateBounds != null)
  2276. {
  2277. var layout = this.layoutManager.getLayout(this.model.getParent(cell));
  2278. if (layout != null && layout.constructor == mxStackLayout)
  2279. {
  2280. if (layout.horizontal)
  2281. {
  2282. geo.alternateBounds.height = 0;
  2283. }
  2284. else
  2285. {
  2286. geo.alternateBounds.width = 0;
  2287. }
  2288. }
  2289. }
  2290. mxGraph.prototype.updateAlternateBounds.apply(this, arguments);
  2291. };
  2292. /**
  2293. * Adds Shift+collapse/expand and size management for folding inside stack
  2294. */
  2295. Graph.prototype.isMoveCellsEvent = function(evt)
  2296. {
  2297. return mxEvent.isShiftDown(evt);
  2298. };
  2299. /**
  2300. * Adds Shift+collapse/expand and size management for folding inside stack
  2301. */
  2302. Graph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt)
  2303. {
  2304. recurse = (recurse != null) ? recurse : false;
  2305. if (cells == null)
  2306. {
  2307. cells = this.getFoldableCells(this.getSelectionCells(), collapse);
  2308. }
  2309. if (cells != null)
  2310. {
  2311. this.model.beginUpdate();
  2312. try
  2313. {
  2314. mxGraph.prototype.foldCells.apply(this, arguments);
  2315. // Resizes all parent stacks if alt is not pressed
  2316. if (this.layoutManager != null)
  2317. {
  2318. for (var i = 0; i < cells.length; i++)
  2319. {
  2320. var state = this.view.getState(cells[i]);
  2321. var geo = this.getCellGeometry(cells[i]);
  2322. if (state != null && geo != null)
  2323. {
  2324. var dx = Math.round(geo.width - state.width / this.view.scale);
  2325. var dy = Math.round(geo.height - state.height / this.view.scale);
  2326. if (dy != 0 || dx != 0)
  2327. {
  2328. var parent = this.model.getParent(cells[i]);
  2329. var layout = this.layoutManager.getLayout(parent);
  2330. if (layout == null)
  2331. {
  2332. // Moves cells to the right and down after collapse/expand
  2333. if (evt != null && this.isMoveCellsEvent(evt))
  2334. {
  2335. this.moveSiblings(state, parent, dx, dy);
  2336. }
  2337. }
  2338. else if ((evt == null || !mxEvent.isAltDown(evt)) && layout.constructor == mxStackLayout && !layout.resizeLast)
  2339. {
  2340. this.resizeParentStacks(parent, layout, dx, dy);
  2341. }
  2342. }
  2343. }
  2344. }
  2345. }
  2346. }
  2347. finally
  2348. {
  2349. this.model.endUpdate();
  2350. }
  2351. // Selects cells after folding
  2352. if (this.isEnabled())
  2353. {
  2354. this.setSelectionCells(cells);
  2355. }
  2356. }
  2357. };
  2358. /**
  2359. * Overrides label orientation for collapsed swimlanes inside stack.
  2360. */
  2361. Graph.prototype.moveSiblings = function(state, parent, dx, dy)
  2362. {
  2363. this.model.beginUpdate();
  2364. try
  2365. {
  2366. var cells = this.getCellsBeyond(state.x, state.y, parent, true, true);
  2367. for (var i = 0; i < cells.length; i++)
  2368. {
  2369. if (cells[i] != state.cell)
  2370. {
  2371. var tmp = this.view.getState(cells[i]);
  2372. var geo = this.getCellGeometry(cells[i]);
  2373. if (tmp != null && geo != null)
  2374. {
  2375. geo = geo.clone();
  2376. geo.translate(Math.round(dx * Math.max(0, Math.min(1, (tmp.x - state.x) / state.width))),
  2377. Math.round(dy * Math.max(0, Math.min(1, (tmp.y - state.y) / state.height))));
  2378. this.model.setGeometry(cells[i], geo);
  2379. }
  2380. }
  2381. }
  2382. }
  2383. finally
  2384. {
  2385. this.model.endUpdate();
  2386. }
  2387. };
  2388. /**
  2389. * Overrides label orientation for collapsed swimlanes inside stack.
  2390. */
  2391. Graph.prototype.resizeParentStacks = function(parent, layout, dx, dy)
  2392. {
  2393. if (this.layoutManager != null && layout != null && layout.constructor == mxStackLayout && !layout.resizeLast)
  2394. {
  2395. this.model.beginUpdate();
  2396. try
  2397. {
  2398. var dir = layout.horizontal;
  2399. // Bubble resize up for all parent stack layouts with same orientation
  2400. while (parent != null && layout != null && layout.constructor == mxStackLayout &&
  2401. layout.horizontal == dir && !layout.resizeLast)
  2402. {
  2403. var pgeo = this.getCellGeometry(parent);
  2404. var pstate = this.view.getState(parent);
  2405. if (pstate != null && pgeo != null)
  2406. {
  2407. pgeo = pgeo.clone();
  2408. if (layout.horizontal)
  2409. {
  2410. pgeo.width += dx + Math.min(0, pstate.width / this.view.scale - pgeo.width);
  2411. }
  2412. else
  2413. {
  2414. pgeo.height += dy + Math.min(0, pstate.height / this.view.scale - pgeo.height);
  2415. }
  2416. this.model.setGeometry(parent, pgeo);
  2417. }
  2418. parent = this.model.getParent(parent);
  2419. layout = this.layoutManager.getLayout(parent);
  2420. }
  2421. }
  2422. finally
  2423. {
  2424. this.model.endUpdate();
  2425. }
  2426. }
  2427. };
  2428. /**
  2429. * Disables drill-down for non-swimlanes.
  2430. */
  2431. Graph.prototype.isContainer = function(cell)
  2432. {
  2433. var state = this.view.getState(cell);
  2434. var style = (state != null) ? state.style : this.getCellStyle(cell);
  2435. if (this.isSwimlane(cell))
  2436. {
  2437. return style['container'] != '0';
  2438. }
  2439. else
  2440. {
  2441. return style['container'] == '1';
  2442. }
  2443. };
  2444. /**
  2445. * Adds a connectable style.
  2446. */
  2447. Graph.prototype.isCellConnectable = function(cell)
  2448. {
  2449. var state = this.view.getState(cell);
  2450. var style = (state != null) ? state.style : this.getCellStyle(cell);
  2451. return (style != null && style['connectable'] != null) ? style['connectable'] != '0' :
  2452. mxGraph.prototype.isCellConnectable.apply(this, arguments);
  2453. };
  2454. /**
  2455. * Function: selectAll
  2456. *
  2457. * Selects all children of the given parent cell or the children of the
  2458. * default parent if no parent is specified. To select leaf vertices and/or
  2459. * edges use <selectCells>.
  2460. *
  2461. * Parameters:
  2462. *
  2463. * parent - Optional <mxCell> whose children should be selected.
  2464. * Default is <defaultParent>.
  2465. */
  2466. Graph.prototype.selectAll = function(parent)
  2467. {
  2468. parent = parent || this.getDefaultParent();
  2469. if (!this.isCellLocked(parent))
  2470. {
  2471. mxGraph.prototype.selectAll.apply(this, arguments);
  2472. }
  2473. };
  2474. /**
  2475. * Function: selectCells
  2476. *
  2477. * Selects all vertices and/or edges depending on the given boolean
  2478. * arguments recursively, starting at the given parent or the default
  2479. * parent if no parent is specified. Use <selectAll> to select all cells.
  2480. * For vertices, only cells with no children are selected.
  2481. *
  2482. * Parameters:
  2483. *
  2484. * vertices - Boolean indicating if vertices should be selected.
  2485. * edges - Boolean indicating if edges should be selected.
  2486. * parent - Optional <mxCell> that acts as the root of the recursion.
  2487. * Default is <defaultParent>.
  2488. */
  2489. Graph.prototype.selectCells = function(vertices, edges, parent)
  2490. {
  2491. parent = parent || this.getDefaultParent();
  2492. if (!this.isCellLocked(parent))
  2493. {
  2494. mxGraph.prototype.selectCells.apply(this, arguments);
  2495. }
  2496. };
  2497. /**
  2498. * Function: getSwimlaneAt
  2499. *
  2500. * Returns the bottom-most swimlane that intersects the given point (x, y)
  2501. * in the cell hierarchy that starts at the given parent.
  2502. *
  2503. * Parameters:
  2504. *
  2505. * x - X-coordinate of the location to be checked.
  2506. * y - Y-coordinate of the location to be checked.
  2507. * parent - <mxCell> that should be used as the root of the recursion.
  2508. * Default is <defaultParent>.
  2509. */
  2510. Graph.prototype.getSwimlaneAt = function (x, y, parent)
  2511. {
  2512. parent = parent || this.getDefaultParent();
  2513. if (!this.isCellLocked(parent))
  2514. {
  2515. return mxGraph.prototype.getSwimlaneAt.apply(this, arguments);
  2516. }
  2517. return null;
  2518. };
  2519. /**
  2520. * Disables folding for non-swimlanes.
  2521. */
  2522. Graph.prototype.isCellFoldable = function(cell)
  2523. {
  2524. var state = this.view.getState(cell);
  2525. var style = (state != null) ? state.style : this.getCellStyle(cell);
  2526. return this.foldingEnabled && !this.isCellLocked(cell) &&
  2527. ((this.isContainer(cell) && style['collapsible'] != '0') ||
  2528. (!this.isContainer(cell) && style['collapsible'] == '1'));
  2529. };
  2530. /**
  2531. * Stops all interactions and clears the selection.
  2532. */
  2533. Graph.prototype.reset = function()
  2534. {
  2535. if (this.isEditing())
  2536. {
  2537. this.stopEditing(true);
  2538. }
  2539. this.escape();
  2540. if (!this.isSelectionEmpty())
  2541. {
  2542. this.clearSelection();
  2543. }
  2544. };
  2545. /**
  2546. * Overridden to limit zoom to 1% - 16.000%.
  2547. */
  2548. Graph.prototype.zoom = function(factor, center)
  2549. {
  2550. factor = Math.max(0.01, Math.min(this.view.scale * factor, 160)) / this.view.scale;
  2551. mxGraph.prototype.zoom.apply(this, arguments);
  2552. };
  2553. /**
  2554. * Function: zoomIn
  2555. *
  2556. * Zooms into the graph by <zoomFactor>.
  2557. */
  2558. Graph.prototype.zoomIn = function()
  2559. {
  2560. // Switches to 1% zoom steps below 15%
  2561. if (this.view.scale < 0.15)
  2562. {
  2563. this.zoom((this.view.scale + 0.01) / this.view.scale);
  2564. }
  2565. else
  2566. {
  2567. // Uses to 5% zoom steps for better grid rendering in webkit
  2568. // and to avoid rounding errors for zoom steps
  2569. this.zoom((Math.round(this.view.scale * this.zoomFactor * 20) / 20) / this.view.scale);
  2570. }
  2571. };
  2572. /**
  2573. * Function: zoomOut
  2574. *
  2575. * Zooms out of the graph by <zoomFactor>.
  2576. */
  2577. Graph.prototype.zoomOut = function()
  2578. {
  2579. // Switches to 1% zoom steps below 15%
  2580. if (this.view.scale <= 0.15)
  2581. {
  2582. this.zoom((this.view.scale - 0.01) / this.view.scale);
  2583. }
  2584. else
  2585. {
  2586. // Uses to 5% zoom steps for better grid rendering in webkit
  2587. // and to avoid rounding errors for zoom steps
  2588. this.zoom((Math.round(this.view.scale * (1 / this.zoomFactor) * 20) / 20) / this.view.scale);
  2589. }
  2590. };
  2591. /**
  2592. * Overrides tooltips to show custom tooltip or metadata.
  2593. */
  2594. Graph.prototype.getTooltipForCell = function(cell)
  2595. {
  2596. var tip = '';
  2597. if (mxUtils.isNode(cell.value))
  2598. {
  2599. var tmp = cell.value.getAttribute('tooltip');
  2600. if (tmp != null)
  2601. {
  2602. if (tmp != null && this.isReplacePlaceholders(cell))
  2603. {
  2604. tmp = this.replacePlaceholders(cell, tmp);
  2605. }
  2606. tip = this.sanitizeHtml(tmp);
  2607. }
  2608. else
  2609. {
  2610. var ignored = this.builtInProperties;
  2611. var attrs = cell.value.attributes;
  2612. var temp = [];
  2613. // Hides links in edit mode
  2614. if (this.isEnabled())
  2615. {
  2616. ignored.push('link');
  2617. }
  2618. for (var i = 0; i < attrs.length; i++)
  2619. {
  2620. if (mxUtils.indexOf(ignored, attrs[i].nodeName) < 0 && attrs[i].nodeValue.length > 0)
  2621. {
  2622. temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue});
  2623. }
  2624. }
  2625. // Sorts by name
  2626. temp.sort(function(a, b)
  2627. {
  2628. if (a.name < b.name)
  2629. {
  2630. return -1;
  2631. }
  2632. else if (a.name > b.name)
  2633. {
  2634. return 1;
  2635. }
  2636. else
  2637. {
  2638. return 0;
  2639. }
  2640. });
  2641. for (var i = 0; i < temp.length; i++)
  2642. {
  2643. if (temp[i].name != 'link' || !this.isCustomLink(temp[i].value))
  2644. {
  2645. tip += ((temp[i].name != 'link') ? '<b>' + temp[i].name + ':</b> ' : '') +
  2646. mxUtils.htmlEntities(temp[i].value) + '\n';
  2647. }
  2648. }
  2649. if (tip.length > 0)
  2650. {
  2651. tip = tip.substring(0, tip.length - 1);
  2652. if (mxClient.IS_SVG)
  2653. {
  2654. tip = '<div style="max-width:360px;">' + tip + '</div>';
  2655. }
  2656. }
  2657. }
  2658. }
  2659. return tip;
  2660. };
  2661. /**
  2662. * Turns the given string into an array.
  2663. */
  2664. Graph.prototype.stringToBytes = function(str)
  2665. {
  2666. return Graph.stringToBytes(str);
  2667. };
  2668. /**
  2669. * Turns the given array into a string.
  2670. */
  2671. Graph.prototype.bytesToString = function(arr)
  2672. {
  2673. return Graph.bytesToString(arr);
  2674. };
  2675. /**
  2676. * Returns a base64 encoded version of the compressed outer XML of the given node.
  2677. */
  2678. Graph.prototype.compressNode = function(node)
  2679. {
  2680. return Graph.compressNode(node);
  2681. };
  2682. /**
  2683. * Returns a base64 encoded version of the compressed string.
  2684. */
  2685. Graph.prototype.compress = function(data, deflate)
  2686. {
  2687. return Graph.compress(data, deflate);
  2688. };
  2689. /**
  2690. * Returns a decompressed version of the base64 encoded string.
  2691. */
  2692. Graph.prototype.decompress = function(data, inflate)
  2693. {
  2694. return Graph.decompress(data, inflate);
  2695. };
  2696. /**
  2697. * Redirects to Graph.zapGremlins.
  2698. */
  2699. Graph.prototype.zapGremlins = function(text)
  2700. {
  2701. return Graph.zapGremlins(text);
  2702. };
  2703. /**
  2704. * Hover icons are used for hover, vertex handler and drag from sidebar.
  2705. */
  2706. HoverIcons = function(graph)
  2707. {
  2708. this.graph = graph;
  2709. this.init();
  2710. };
  2711. /**
  2712. * Up arrow.
  2713. */
  2714. HoverIcons.prototype.arrowSpacing = 2;
  2715. /**
  2716. * Delay to switch to another state for overlapping bbox. Default is 500ms.
  2717. */
  2718. HoverIcons.prototype.updateDelay = 500;
  2719. /**
  2720. * Delay to switch between states. Default is 140ms.
  2721. */
  2722. HoverIcons.prototype.activationDelay = 140;
  2723. /**
  2724. * Up arrow.
  2725. */
  2726. HoverIcons.prototype.currentState = null;
  2727. /**
  2728. * Up arrow.
  2729. */
  2730. HoverIcons.prototype.activeArrow = null;
  2731. /**
  2732. * Up arrow.
  2733. */
  2734. HoverIcons.prototype.inactiveOpacity = 15;
  2735. /**
  2736. * Up arrow.
  2737. */
  2738. HoverIcons.prototype.cssCursor = 'copy';
  2739. /**
  2740. * Whether to hide arrows that collide with vertices.
  2741. * LATER: Add keyboard override, touch support.
  2742. */
  2743. HoverIcons.prototype.checkCollisions = true;
  2744. /**
  2745. * Up arrow.
  2746. */
  2747. HoverIcons.prototype.arrowFill = '#29b6f2';
  2748. /**
  2749. * Up arrow.
  2750. */
  2751. HoverIcons.prototype.triangleUp = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-up.png', 26, 14) :
  2752. Graph.createSvgImage(18, 28, '<path d="m 6 26 L 12 26 L 12 12 L 18 12 L 9 1 L 1 12 L 6 12 z" ' +
  2753. 'stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '"/>');
  2754. /**
  2755. * Right arrow.
  2756. */
  2757. HoverIcons.prototype.triangleRight = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-right.png', 14, 26) :
  2758. Graph.createSvgImage(26, 18, '<path d="m 1 6 L 14 6 L 14 1 L 26 9 L 14 18 L 14 12 L 1 12 z" ' +
  2759. 'stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '"/>');
  2760. /**
  2761. * Down arrow.
  2762. */
  2763. HoverIcons.prototype.triangleDown = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-down.png', 26, 14) :
  2764. Graph.createSvgImage(18, 26, '<path d="m 6 1 L 6 14 L 1 14 L 9 26 L 18 14 L 12 14 L 12 1 z" ' +
  2765. 'stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '"/>');
  2766. /**
  2767. * Left arrow.
  2768. */
  2769. HoverIcons.prototype.triangleLeft = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-left.png', 14, 26) :
  2770. Graph.createSvgImage(28, 18, '<path d="m 1 9 L 12 1 L 12 6 L 26 6 L 26 12 L 12 12 L 12 18 z" ' +
  2771. 'stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '"/>');
  2772. /**
  2773. * Round target.
  2774. */
  2775. HoverIcons.prototype.roundDrop = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/round-drop.png', 26, 26) :
  2776. Graph.createSvgImage(26, 26, '<circle cx="13" cy="13" r="12" ' +
  2777. 'stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '"/>');
  2778. /**
  2779. * Refresh target.
  2780. */
  2781. HoverIcons.prototype.refreshTarget = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQxNERDRTU1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQxNERDRTY1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NDE0RENFMzVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NDE0RENFNDVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsvuX50AAANaSURBVHja7FjRZ1tRGD9ZJ1NCyIQSwrivI4Q8hCpjlFDyFEoYfSp9Ko1QWnmo0If+BSXkIfo0QirTMUpeGo2EPfWllFYjZMLKLDJn53d3biU337m5J223bPbxk5t7v+/c3/2+73znO8fDOWezKM/YjMpz68Lj8ejY+QTeCCwLxOS9qPxtyN+6wAeBTwJ31CCO0cJDjXBGBN4LfIepSwykTUT1bgpuib0SONIgo8KRHOtRiCFcvUcgZeGrHPNBxLIyFPyRgTGz0xLbegJCdmzpElue5KlAIMDX19d5uVzm5+fnfDAYmMA17uEZdOx2Yvb/sHlu2S0xwymn5ufneTab5b1ej08S6EAXNrDd2dnhiUTim21MvMtwQ6yiIrWwsMDPzs64rsBmf3/fvM7n89TYlUnEllSkQqEQv7q64g+Vk5MTVXosORErU0Zer5f0FEIlw2N6MxwO82QyaXql2+2SxDqdjopYWUUsqEp45IldqtWq6UWVh/1+P7+8vCTJ4QMUJSRIEXuneoH96w8PDyeWAnhSJfCqwm6NIlaklFdXV0cGhRcQ2mlJQXK5nMq2YPEZbnteU1U2lUqN/D84OGD9fl+5fgnSrFarsUwmw0qlEru4uBjTicViTk3Cr27HSnxR+Doyz0ZE1CAWiUTusbu7y9rttlZv5fP5WDQavYfIMba4uEipfhF8XtqJoZXx/uH+sC/4vPg7OljZZQbsCmLtYzc3N6zRaJhotVrmfx0xDINtbm6athYUeXpHdbBNaqZUKpWxWXV7e2vex+xaWVnhc3NzjrPUXgexyCt0m67LBV7uJMITjqRE4o8tZeg8FPpFitgapYxiOC0poFgsji1jKNo6BZZckrAGUtJsNk1vqAihCBcKhTE7hNWhqw2qFnGy5UFOUYJVIJ1OjzSE+BCEilon0URavRmBqnbbQ00AXbm+vnZc9O1tj72OnQoc2+cwygRkb2+P1et17ZoEm3g87lRmjgWZ00kbXkNuse6/Bu2wlegIxfb2tuvWGroO4bO2c4bbzUh60mxDXm1sbJhhxkQYnhS4h2fUZoRAWnf7lv8N27f8P7Xhnekjgpk+VKGOoQbsiY+hhhtF3YO7twIJ+ULvUGv+GQ2fQEvWxI/THNx5/p/BaspPAQYAqStgiSQwCDoAAAAASUVORK5CYII=' :
  2782. IMAGE_PATH + '/refresh.png', 38, 38);
  2783. /**
  2784. * Tolerance for hover icon clicks.
  2785. */
  2786. HoverIcons.prototype.tolerance = (mxClient.IS_TOUCH) ? 6 : 0;
  2787. /**
  2788. *
  2789. */
  2790. HoverIcons.prototype.init = function()
  2791. {
  2792. this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip'));
  2793. this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip'));
  2794. this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip'));
  2795. this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip'));
  2796. this.elts = [this.arrowUp, this.arrowRight, this.arrowDown, this.arrowLeft];
  2797. this.repaintHandler = mxUtils.bind(this, function()
  2798. {
  2799. this.repaint();
  2800. });
  2801. this.graph.selectionModel.addListener(mxEvent.CHANGE, this.repaintHandler);
  2802. this.graph.model.addListener(mxEvent.CHANGE, this.repaintHandler);
  2803. this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler);
  2804. this.graph.view.addListener(mxEvent.TRANSLATE, this.repaintHandler);
  2805. this.graph.view.addListener(mxEvent.SCALE, this.repaintHandler);
  2806. this.graph.view.addListener(mxEvent.DOWN, this.repaintHandler);
  2807. this.graph.view.addListener(mxEvent.UP, this.repaintHandler);
  2808. this.graph.addListener(mxEvent.ROOT, this.repaintHandler);
  2809. // Resets the mouse point on escape
  2810. this.graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function()
  2811. {
  2812. this.mouseDownPoint = null;
  2813. }));
  2814. // Removes hover icons if mouse leaves the container
  2815. mxEvent.addListener(this.graph.container, 'mouseleave', mxUtils.bind(this, function(evt)
  2816. {
  2817. // Workaround for IE11 firing mouseleave for touch in diagram
  2818. if (evt.relatedTarget != null && mxEvent.getSource(evt) == this.graph.container)
  2819. {
  2820. this.setDisplay('none');
  2821. }
  2822. }));
  2823. // Resets current state when in-place editor starts
  2824. this.graph.addListener(mxEvent.START_EDITING, mxUtils.bind(this, function(evt)
  2825. {
  2826. this.reset();
  2827. }));
  2828. // Resets current state after update of selection state for touch events
  2829. var graphClick = this.graph.click;
  2830. this.graph.click = mxUtils.bind(this, function(me)
  2831. {
  2832. graphClick.apply(this.graph, arguments);
  2833. if (this.currentState != null && !this.graph.isCellSelected(this.currentState.cell) &&
  2834. mxEvent.isTouchEvent(me.getEvent()) && !this.graph.model.isVertex(me.getCell()))
  2835. {
  2836. this.reset();
  2837. }
  2838. });
  2839. // Checks if connection handler was active in mouse move
  2840. // as workaround for possible double connection inserted
  2841. var connectionHandlerActive = false;
  2842. // Implements a listener for hover and click handling
  2843. this.graph.addMouseListener(
  2844. {
  2845. mouseDown: mxUtils.bind(this, function(sender, me)
  2846. {
  2847. connectionHandlerActive = false;
  2848. var evt = me.getEvent();
  2849. if (this.isResetEvent(evt))
  2850. {
  2851. this.reset();
  2852. }
  2853. else if (!this.isActive())
  2854. {
  2855. var state = this.getState(me.getState());
  2856. if (state != null || !mxEvent.isTouchEvent(evt))
  2857. {
  2858. this.update(state);
  2859. }
  2860. }
  2861. this.setDisplay('none');
  2862. }),
  2863. mouseMove: mxUtils.bind(this, function(sender, me)
  2864. {
  2865. var evt = me.getEvent();
  2866. if (this.isResetEvent(evt))
  2867. {
  2868. this.reset();
  2869. }
  2870. else if (!this.graph.isMouseDown && !mxEvent.isTouchEvent(evt))
  2871. {
  2872. this.update(this.getState(me.getState()),
  2873. me.getGraphX(), me.getGraphY());
  2874. }
  2875. if (this.graph.connectionHandler != null &&
  2876. this.graph.connectionHandler.shape != null)
  2877. {
  2878. connectionHandlerActive = true;
  2879. }
  2880. }),
  2881. mouseUp: mxUtils.bind(this, function(sender, me)
  2882. {
  2883. var evt = me.getEvent();
  2884. var pt = mxUtils.convertPoint(this.graph.container,
  2885. mxEvent.getClientX(evt), mxEvent.getClientY(evt))
  2886. if (this.isResetEvent(evt))
  2887. {
  2888. this.reset();
  2889. }
  2890. else if (this.isActive() && !connectionHandlerActive &&
  2891. this.mouseDownPoint != null)
  2892. {
  2893. this.click(this.currentState, this.getDirection(), me);
  2894. }
  2895. else if (this.isActive())
  2896. {
  2897. // Selects target vertex after drag and clone if not only new edge was inserted
  2898. if (this.graph.getSelectionCount() != 1 || !this.graph.model.isEdge(
  2899. this.graph.getSelectionCell()))
  2900. {
  2901. this.update(this.getState(this.graph.view.getState(
  2902. this.graph.getCellAt(me.getGraphX(), me.getGraphY()))));
  2903. }
  2904. else
  2905. {
  2906. this.reset();
  2907. }
  2908. }
  2909. else if (mxEvent.isTouchEvent(evt) || (this.bbox != null &&
  2910. mxUtils.contains(this.bbox, me.getGraphX(), me.getGraphY())))
  2911. {
  2912. // Shows existing hover icons if inside bounding box
  2913. this.setDisplay('');
  2914. this.repaint();
  2915. }
  2916. else if (!mxEvent.isTouchEvent(evt))
  2917. {
  2918. this.reset();
  2919. }
  2920. connectionHandlerActive = false;
  2921. this.resetActiveArrow();
  2922. })
  2923. });
  2924. };
  2925. /**
  2926. *
  2927. */
  2928. HoverIcons.prototype.isResetEvent = function(evt, allowShift)
  2929. {
  2930. return mxEvent.isAltDown(evt) || (this.activeArrow == null && mxEvent.isShiftDown(evt)) ||
  2931. mxEvent.isMetaDown(evt) || (mxEvent.isPopupTrigger(evt) && !mxEvent.isControlDown(evt));
  2932. };
  2933. /**
  2934. *
  2935. */
  2936. HoverIcons.prototype.createArrow = function(img, tooltip)
  2937. {
  2938. var arrow = null;
  2939. if (mxClient.IS_IE && !mxClient.IS_SVG)
  2940. {
  2941. // Workaround for PNG images in IE6
  2942. if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat')
  2943. {
  2944. arrow = document.createElement(mxClient.VML_PREFIX + ':image');
  2945. arrow.setAttribute('src', img.src);
  2946. arrow.style.borderStyle = 'none';
  2947. }
  2948. else
  2949. {
  2950. arrow = document.createElement('div');
  2951. arrow.style.backgroundImage = 'url(' + img.src + ')';
  2952. arrow.style.backgroundPosition = 'center';
  2953. arrow.style.backgroundRepeat = 'no-repeat';
  2954. }
  2955. arrow.style.width = (img.width + 4) + 'px';
  2956. arrow.style.height = (img.height + 4) + 'px';
  2957. arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
  2958. }
  2959. else
  2960. {
  2961. arrow = mxUtils.createImage(img.src);
  2962. arrow.style.width = img.width + 'px';
  2963. arrow.style.height = img.height + 'px';
  2964. arrow.style.padding = this.tolerance + 'px';
  2965. }
  2966. if (tooltip != null)
  2967. {
  2968. arrow.setAttribute('title', tooltip);
  2969. }
  2970. arrow.style.position = 'absolute';
  2971. arrow.style.cursor = this.cssCursor;
  2972. mxEvent.addGestureListeners(arrow, mxUtils.bind(this, function(evt)
  2973. {
  2974. if (this.currentState != null && !this.isResetEvent(evt))
  2975. {
  2976. this.mouseDownPoint = mxUtils.convertPoint(this.graph.container,
  2977. mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  2978. this.drag(evt, this.mouseDownPoint.x, this.mouseDownPoint.y);
  2979. this.activeArrow = arrow;
  2980. this.setDisplay('none');
  2981. mxEvent.consume(evt);
  2982. }
  2983. }));
  2984. // Captures mouse events as events on graph
  2985. mxEvent.redirectMouseEvents(arrow, this.graph, this.currentState);
  2986. mxEvent.addListener(arrow, 'mouseenter', mxUtils.bind(this, function(evt)
  2987. {
  2988. // Workaround for Firefox firing mouseenter on touchend
  2989. if (mxEvent.isMouseEvent(evt))
  2990. {
  2991. if (this.activeArrow != null && this.activeArrow != arrow)
  2992. {
  2993. mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity);
  2994. }
  2995. this.graph.connectionHandler.constraintHandler.reset();
  2996. mxUtils.setOpacity(arrow, 100);
  2997. this.activeArrow = arrow;
  2998. }
  2999. }));
  3000. mxEvent.addListener(arrow, 'mouseleave', mxUtils.bind(this, function(evt)
  3001. {
  3002. // Workaround for IE11 firing this event on touch
  3003. if (!this.graph.isMouseDown)
  3004. {
  3005. this.resetActiveArrow();
  3006. }
  3007. }));
  3008. return arrow;
  3009. };
  3010. /**
  3011. *
  3012. */
  3013. HoverIcons.prototype.resetActiveArrow = function()
  3014. {
  3015. if (this.activeArrow != null)
  3016. {
  3017. mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity);
  3018. this.activeArrow = null;
  3019. }
  3020. };
  3021. /**
  3022. *
  3023. */
  3024. HoverIcons.prototype.getDirection = function()
  3025. {
  3026. var dir = mxConstants.DIRECTION_EAST;
  3027. if (this.activeArrow == this.arrowUp)
  3028. {
  3029. dir = mxConstants.DIRECTION_NORTH;
  3030. }
  3031. else if (this.activeArrow == this.arrowDown)
  3032. {
  3033. dir = mxConstants.DIRECTION_SOUTH;
  3034. }
  3035. else if (this.activeArrow == this.arrowLeft)
  3036. {
  3037. dir = mxConstants.DIRECTION_WEST;
  3038. }
  3039. return dir;
  3040. };
  3041. /**
  3042. *
  3043. */
  3044. HoverIcons.prototype.visitNodes = function(visitor)
  3045. {
  3046. for (var i = 0; i < this.elts.length; i++)
  3047. {
  3048. if (this.elts[i] != null)
  3049. {
  3050. visitor(this.elts[i]);
  3051. }
  3052. }
  3053. };
  3054. /**
  3055. *
  3056. */
  3057. HoverIcons.prototype.removeNodes = function()
  3058. {
  3059. this.visitNodes(function(elt)
  3060. {
  3061. if (elt.parentNode != null)
  3062. {
  3063. elt.parentNode.removeChild(elt);
  3064. }
  3065. });
  3066. };
  3067. /**
  3068. *
  3069. */
  3070. HoverIcons.prototype.setDisplay = function(display)
  3071. {
  3072. this.visitNodes(function(elt)
  3073. {
  3074. elt.style.display = display;
  3075. });
  3076. };
  3077. /**
  3078. *
  3079. */
  3080. HoverIcons.prototype.isActive = function()
  3081. {
  3082. return this.activeArrow != null && this.currentState != null;
  3083. };
  3084. /**
  3085. *
  3086. */
  3087. HoverIcons.prototype.drag = function(evt, x, y)
  3088. {
  3089. this.graph.popupMenuHandler.hideMenu();
  3090. this.graph.stopEditing(false);
  3091. // Checks if state was removed in call to stopEditing above
  3092. if (this.currentState != null)
  3093. {
  3094. this.graph.connectionHandler.start(this.currentState, x, y);
  3095. this.graph.isMouseTrigger = mxEvent.isMouseEvent(evt);
  3096. this.graph.isMouseDown = true;
  3097. // Hides handles for selection cell
  3098. var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell);
  3099. if (handler != null)
  3100. {
  3101. handler.setHandlesVisible(false);
  3102. }
  3103. // Ctrl+shift drag sets source constraint
  3104. var es = this.graph.connectionHandler.edgeState;
  3105. if (evt != null && mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt) && es != null &&
  3106. mxUtils.getValue(es.style, mxConstants.STYLE_EDGE, null) === 'orthogonalEdgeStyle')
  3107. {
  3108. var direction = this.getDirection();
  3109. es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction);
  3110. es.style['sourcePortConstraint'] = direction;
  3111. }
  3112. }
  3113. };
  3114. /**
  3115. *
  3116. */
  3117. HoverIcons.prototype.getStateAt = function(state, x, y)
  3118. {
  3119. return this.graph.view.getState(this.graph.getCellAt(x, y));
  3120. };
  3121. /**
  3122. *
  3123. */
  3124. HoverIcons.prototype.click = function(state, dir, me)
  3125. {
  3126. var evt = me.getEvent();
  3127. var x = me.getGraphX();
  3128. var y = me.getGraphY();
  3129. var tmp = this.getStateAt(state, x, y);
  3130. if (tmp != null && this.graph.model.isEdge(tmp.cell) && !mxEvent.isControlDown(evt) &&
  3131. (tmp.getVisibleTerminalState(true) == state || tmp.getVisibleTerminalState(false) == state))
  3132. {
  3133. this.graph.setSelectionCell(tmp.cell);
  3134. this.reset();
  3135. }
  3136. else if (state != null)
  3137. {
  3138. var cells = this.graph.connectVertex(state.cell, dir, this.graph.defaultEdgeLength, evt);
  3139. this.graph.selectCellsForConnectVertex(cells, evt, this);
  3140. // Selects only target vertex if one exists
  3141. if (cells.length == 2 && this.graph.model.isVertex(cells[1]))
  3142. {
  3143. this.graph.setSelectionCell(cells[1]);
  3144. // Adds hover icons to new target vertex for touch devices
  3145. if (mxEvent.isTouchEvent(evt))
  3146. {
  3147. this.update(this.getState(this.graph.view.getState(cells[1])));
  3148. }
  3149. else
  3150. {
  3151. // Hides hover icons after click with mouse
  3152. this.reset();
  3153. }
  3154. this.graph.scrollCellToVisible(cells[1]);
  3155. }
  3156. else
  3157. {
  3158. this.graph.setSelectionCells(cells);
  3159. }
  3160. }
  3161. me.consume();
  3162. };
  3163. /**
  3164. *
  3165. */
  3166. HoverIcons.prototype.reset = function(clearTimeout)
  3167. {
  3168. clearTimeout = (clearTimeout == null) ? true : clearTimeout;
  3169. if (clearTimeout && this.updateThread != null)
  3170. {
  3171. window.clearTimeout(this.updateThread);
  3172. }
  3173. this.mouseDownPoint = null;
  3174. this.currentState = null;
  3175. this.activeArrow = null;
  3176. this.removeNodes();
  3177. this.bbox = null;
  3178. };
  3179. /**
  3180. *
  3181. */
  3182. HoverIcons.prototype.repaint = function()
  3183. {
  3184. this.bbox = null;
  3185. if (this.currentState != null)
  3186. {
  3187. // Checks if cell was deleted
  3188. this.currentState = this.getState(this.currentState);
  3189. // Cell was deleted
  3190. if (this.currentState != null &&
  3191. this.graph.model.isVertex(this.currentState.cell) &&
  3192. this.graph.isCellConnectable(this.currentState.cell))
  3193. {
  3194. var bds = mxRectangle.fromRectangle(this.currentState);
  3195. // Uses outer bounding box to take rotation into account
  3196. if (this.currentState.shape != null && this.currentState.shape.boundingBox != null)
  3197. {
  3198. bds = mxRectangle.fromRectangle(this.currentState.shape.boundingBox);
  3199. }
  3200. bds.grow(this.graph.tolerance);
  3201. bds.grow(this.arrowSpacing);
  3202. var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell);
  3203. if (handler != null)
  3204. {
  3205. bds.x -= handler.horizontalOffset / 2;
  3206. bds.y -= handler.verticalOffset / 2;
  3207. bds.width += handler.horizontalOffset;
  3208. bds.height += handler.verticalOffset;
  3209. // Adds bounding box of rotation handle to avoid overlap
  3210. if (handler.rotationShape != null && handler.rotationShape.node != null &&
  3211. handler.rotationShape.node.style.visibility != 'hidden' &&
  3212. handler.rotationShape.node.style.display != 'none' &&
  3213. handler.rotationShape.boundingBox != null)
  3214. {
  3215. bds.add(handler.rotationShape.boundingBox);
  3216. }
  3217. }
  3218. this.arrowUp.style.left = Math.round(this.currentState.getCenterX() - this.triangleUp.width / 2 - this.tolerance) + 'px';
  3219. this.arrowUp.style.top = Math.round(bds.y - this.triangleUp.height - this.tolerance) + 'px';
  3220. mxUtils.setOpacity(this.arrowUp, this.inactiveOpacity);
  3221. this.arrowRight.style.left = Math.round(bds.x + bds.width - this.tolerance) + 'px';
  3222. this.arrowRight.style.top = Math.round(this.currentState.getCenterY() - this.triangleRight.height / 2 - this.tolerance) + 'px';
  3223. mxUtils.setOpacity(this.arrowRight, this.inactiveOpacity);
  3224. this.arrowDown.style.left = this.arrowUp.style.left;
  3225. this.arrowDown.style.top = Math.round(bds.y + bds.height - this.tolerance) + 'px';
  3226. mxUtils.setOpacity(this.arrowDown, this.inactiveOpacity);
  3227. this.arrowLeft.style.left = Math.round(bds.x - this.triangleLeft.width - this.tolerance) + 'px';
  3228. this.arrowLeft.style.top = this.arrowRight.style.top;
  3229. mxUtils.setOpacity(this.arrowLeft, this.inactiveOpacity);
  3230. if (this.checkCollisions)
  3231. {
  3232. var right = this.graph.getCellAt(bds.x + bds.width +
  3233. this.triangleRight.width / 2, this.currentState.getCenterY());
  3234. var left = this.graph.getCellAt(bds.x - this.triangleLeft.width / 2, this.currentState.getCenterY());
  3235. var top = this.graph.getCellAt(this.currentState.getCenterX(), bds.y - this.triangleUp.height / 2);
  3236. var bottom = this.graph.getCellAt(this.currentState.getCenterX(), bds.y + bds.height + this.triangleDown.height / 2);
  3237. // Shows hover icons large cell is behind all directions of current cell
  3238. if (right != null && right == left && left == top && top == bottom)
  3239. {
  3240. right = null;
  3241. left = null;
  3242. top = null;
  3243. bottom = null;
  3244. }
  3245. var currentGeo = this.graph.getCellGeometry(this.currentState.cell);
  3246. var checkCollision = mxUtils.bind(this, function(cell, arrow)
  3247. {
  3248. var geo = this.graph.model.isVertex(cell) && this.graph.getCellGeometry(cell);
  3249. // Ignores collision if vertex is more than 3 times the size of this vertex
  3250. if (cell != null && !this.graph.model.isAncestor(cell, this.currentState.cell) &&
  3251. (geo == null || currentGeo == null || (geo.height < 6 * currentGeo.height &&
  3252. geo.width < 6 * currentGeo.width)))
  3253. {
  3254. arrow.style.visibility = 'hidden';
  3255. }
  3256. else
  3257. {
  3258. arrow.style.visibility = 'visible';
  3259. }
  3260. });
  3261. checkCollision(right, this.arrowRight);
  3262. checkCollision(left, this.arrowLeft);
  3263. checkCollision(top, this.arrowUp);
  3264. checkCollision(bottom, this.arrowDown);
  3265. }
  3266. else
  3267. {
  3268. this.arrowLeft.style.visibility = 'visible';
  3269. this.arrowRight.style.visibility = 'visible';
  3270. this.arrowUp.style.visibility = 'visible';
  3271. this.arrowDown.style.visibility = 'visible';
  3272. }
  3273. if (this.graph.tooltipHandler.isEnabled())
  3274. {
  3275. this.arrowLeft.setAttribute('title', mxResources.get('plusTooltip'));
  3276. this.arrowRight.setAttribute('title', mxResources.get('plusTooltip'));
  3277. this.arrowUp.setAttribute('title', mxResources.get('plusTooltip'));
  3278. this.arrowDown.setAttribute('title', mxResources.get('plusTooltip'));
  3279. }
  3280. else
  3281. {
  3282. this.arrowLeft.removeAttribute('title');
  3283. this.arrowRight.removeAttribute('title');
  3284. this.arrowUp.removeAttribute('title');
  3285. this.arrowDown.removeAttribute('title');
  3286. }
  3287. }
  3288. else
  3289. {
  3290. this.reset();
  3291. }
  3292. // Updates bounding box
  3293. if (this.currentState != null)
  3294. {
  3295. this.bbox = this.computeBoundingBox();
  3296. // Adds tolerance for hover
  3297. if (this.bbox != null)
  3298. {
  3299. this.bbox.grow(10);
  3300. }
  3301. }
  3302. }
  3303. };
  3304. /**
  3305. *
  3306. */
  3307. HoverIcons.prototype.computeBoundingBox = function()
  3308. {
  3309. var bbox = (!this.graph.model.isEdge(this.currentState.cell)) ? mxRectangle.fromRectangle(this.currentState) : null;
  3310. this.visitNodes(function(elt)
  3311. {
  3312. if (elt.parentNode != null)
  3313. {
  3314. var tmp = new mxRectangle(elt.offsetLeft, elt.offsetTop, elt.offsetWidth, elt.offsetHeight);
  3315. if (bbox == null)
  3316. {
  3317. bbox = tmp;
  3318. }
  3319. else
  3320. {
  3321. bbox.add(tmp);
  3322. }
  3323. }
  3324. });
  3325. return bbox;
  3326. };
  3327. /**
  3328. *
  3329. */
  3330. HoverIcons.prototype.getState = function(state)
  3331. {
  3332. if (state != null)
  3333. {
  3334. var cell = state.cell;
  3335. if (!this.graph.getModel().contains(cell))
  3336. {
  3337. state = null;
  3338. }
  3339. else
  3340. {
  3341. // Uses connectable parent vertex if child is not connectable
  3342. if (this.graph.getModel().isVertex(cell) && !this.graph.isCellConnectable(cell))
  3343. {
  3344. var parent = this.graph.getModel().getParent(cell);
  3345. if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
  3346. {
  3347. cell = parent;
  3348. }
  3349. }
  3350. // Ignores locked cells and edges
  3351. if (this.graph.isCellLocked(cell) || this.graph.model.isEdge(cell))
  3352. {
  3353. cell = null;
  3354. }
  3355. state = this.graph.view.getState(cell);
  3356. if (state != null && state.style == null)
  3357. {
  3358. state = null;
  3359. }
  3360. }
  3361. }
  3362. return state;
  3363. };
  3364. /**
  3365. *
  3366. */
  3367. HoverIcons.prototype.update = function(state, x, y)
  3368. {
  3369. if (!this.graph.connectionArrowsEnabled)
  3370. {
  3371. this.reset();
  3372. }
  3373. else
  3374. {
  3375. if (state != null && state.cell.geometry != null && state.cell.geometry.relative &&
  3376. this.graph.model.isEdge(state.cell.parent))
  3377. {
  3378. state = null;
  3379. }
  3380. var timeOnTarget = null;
  3381. // Time on target
  3382. if (this.prev != state || this.isActive())
  3383. {
  3384. this.startTime = new Date().getTime();
  3385. this.prev = state;
  3386. timeOnTarget = 0;
  3387. if (this.updateThread != null)
  3388. {
  3389. window.clearTimeout(this.updateThread);
  3390. }
  3391. if (state != null)
  3392. {
  3393. // Starts timer to update current state with no mouse events
  3394. this.updateThread = window.setTimeout(mxUtils.bind(this, function()
  3395. {
  3396. if (!this.isActive() && !this.graph.isMouseDown &&
  3397. !this.graph.panningHandler.isActive())
  3398. {
  3399. this.prev = state;
  3400. this.update(state, x, y);
  3401. }
  3402. }), this.updateDelay + 10);
  3403. }
  3404. }
  3405. else if (this.startTime != null)
  3406. {
  3407. timeOnTarget = new Date().getTime() - this.startTime;
  3408. }
  3409. this.setDisplay('');
  3410. if (this.currentState != null && this.currentState != state && timeOnTarget < this.activationDelay &&
  3411. this.bbox != null && !mxUtils.contains(this.bbox, x, y))
  3412. {
  3413. this.reset(false);
  3414. }
  3415. else if (this.currentState != null || timeOnTarget > this.activationDelay)
  3416. {
  3417. if (this.currentState != state && ((timeOnTarget > this.updateDelay && state != null) ||
  3418. this.bbox == null || x == null || y == null || !mxUtils.contains(this.bbox, x, y)))
  3419. {
  3420. if (state != null && this.graph.isEnabled())
  3421. {
  3422. this.removeNodes();
  3423. this.setCurrentState(state);
  3424. this.repaint();
  3425. // Resets connection points on other focused cells
  3426. if (this.graph.connectionHandler.constraintHandler.currentFocus != state)
  3427. {
  3428. this.graph.connectionHandler.constraintHandler.reset();
  3429. }
  3430. }
  3431. else
  3432. {
  3433. this.reset();
  3434. }
  3435. }
  3436. }
  3437. }
  3438. };
  3439. /**
  3440. *
  3441. */
  3442. HoverIcons.prototype.setCurrentState = function(state)
  3443. {
  3444. if (state.style['portConstraint'] != 'eastwest')
  3445. {
  3446. this.graph.container.appendChild(this.arrowUp);
  3447. this.graph.container.appendChild(this.arrowDown);
  3448. }
  3449. this.graph.container.appendChild(this.arrowRight);
  3450. this.graph.container.appendChild(this.arrowLeft);
  3451. this.currentState = state;
  3452. };
  3453. (function()
  3454. {
  3455. /**
  3456. * Reset the list of processed edges.
  3457. */
  3458. var mxGraphViewResetValidationState = mxGraphView.prototype.resetValidationState;
  3459. mxGraphView.prototype.resetValidationState = function()
  3460. {
  3461. mxGraphViewResetValidationState.apply(this, arguments);
  3462. this.validEdges = [];
  3463. };
  3464. /**
  3465. * Updates jumps for valid edges and repaints if needed.
  3466. */
  3467. var mxGraphViewValidateCellState = mxGraphView.prototype.validateCellState;
  3468. mxGraphView.prototype.validateCellState = function(cell, recurse)
  3469. {
  3470. recurse = (recurse != null) ? recurse : true;
  3471. var state = this.getState(cell);
  3472. // Forces repaint if jumps change on a valid edge
  3473. if (state != null && recurse && this.graph.model.isEdge(state.cell) &&
  3474. state.style != null && state.style[mxConstants.STYLE_CURVED] != 1 &&
  3475. !state.invalid && this.updateLineJumps(state))
  3476. {
  3477. this.graph.cellRenderer.redraw(state, false, this.isRendering());
  3478. }
  3479. state = mxGraphViewValidateCellState.apply(this, arguments);
  3480. // Adds to the list of edges that may intersect with later edges
  3481. if (state != null && recurse && this.graph.model.isEdge(state.cell) &&
  3482. state.style != null && state.style[mxConstants.STYLE_CURVED] != 1)
  3483. {
  3484. // LATER: Reuse jumps for valid edges
  3485. this.validEdges.push(state);
  3486. }
  3487. return state;
  3488. };
  3489. /**
  3490. * Forces repaint if routed points have changed.
  3491. */
  3492. var mxCellRendererIsShapeInvalid = mxCellRenderer.prototype.isShapeInvalid;
  3493. mxCellRenderer.prototype.isShapeInvalid = function(state, shape)
  3494. {
  3495. return mxCellRendererIsShapeInvalid.apply(this, arguments) ||
  3496. (state.routedPoints != null && shape.routedPoints != null &&
  3497. !mxUtils.equalPoints(shape.routedPoints, state.routedPoints))
  3498. };
  3499. /**
  3500. * Updates jumps for invalid edges.
  3501. */
  3502. var mxGraphViewUpdateCellState = mxGraphView.prototype.updateCellState;
  3503. mxGraphView.prototype.updateCellState = function(state)
  3504. {
  3505. mxGraphViewUpdateCellState.apply(this, arguments);
  3506. // Updates jumps on invalid edge before repaint
  3507. if (this.graph.model.isEdge(state.cell) &&
  3508. state.style[mxConstants.STYLE_CURVED] != 1)
  3509. {
  3510. this.updateLineJumps(state);
  3511. }
  3512. };
  3513. /**
  3514. * Updates the jumps between given state and processed edges.
  3515. */
  3516. mxGraphView.prototype.updateLineJumps = function(state)
  3517. {
  3518. var pts = state.absolutePoints;
  3519. if (Graph.lineJumpsEnabled)
  3520. {
  3521. var changed = state.routedPoints != null;
  3522. var actual = null;
  3523. if (pts != null && this.validEdges != null &&
  3524. mxUtils.getValue(state.style, 'jumpStyle', 'none') !== 'none')
  3525. {
  3526. var thresh = 0.5 * this.scale;
  3527. changed = false;
  3528. actual = [];
  3529. // Type 0 means normal waypoint, 1 means jump
  3530. function addPoint(type, x, y)
  3531. {
  3532. var rpt = new mxPoint(x, y);
  3533. rpt.type = type;
  3534. actual.push(rpt);
  3535. var curr = (state.routedPoints != null) ? state.routedPoints[actual.length - 1] : null;
  3536. return curr == null || curr.type != type || curr.x != x || curr.y != y;
  3537. };
  3538. for (var i = 0; i < pts.length - 1; i++)
  3539. {
  3540. var p1 = pts[i + 1];
  3541. var p0 = pts[i];
  3542. var list = [];
  3543. // Ignores waypoints on straight segments
  3544. var pn = pts[i + 2];
  3545. while (i < pts.length - 2 &&
  3546. mxUtils.ptSegDistSq(p0.x, p0.y, pn.x, pn.y,
  3547. p1.x, p1.y) < 1 * this.scale * this.scale)
  3548. {
  3549. p1 = pn;
  3550. i++;
  3551. pn = pts[i + 2];
  3552. }
  3553. changed = addPoint(0, p0.x, p0.y) || changed;
  3554. // Processes all previous edges
  3555. for (var e = 0; e < this.validEdges.length; e++)
  3556. {
  3557. var state2 = this.validEdges[e];
  3558. var pts2 = state2.absolutePoints;
  3559. if (pts2 != null && mxUtils.intersects(state, state2) && state2.style['noJump'] != '1')
  3560. {
  3561. // Compares each segment of the edge with the current segment
  3562. for (var j = 0; j < pts2.length - 1; j++)
  3563. {
  3564. var p3 = pts2[j + 1];
  3565. var p2 = pts2[j];
  3566. // Ignores waypoints on straight segments
  3567. pn = pts2[j + 2];
  3568. while (j < pts2.length - 2 &&
  3569. mxUtils.ptSegDistSq(p2.x, p2.y, pn.x, pn.y,
  3570. p3.x, p3.y) < 1 * this.scale * this.scale)
  3571. {
  3572. p3 = pn;
  3573. j++;
  3574. pn = pts2[j + 2];
  3575. }
  3576. var pt = mxUtils.intersection(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
  3577. // Handles intersection between two segments
  3578. if (pt != null && (Math.abs(pt.x - p0.x) > thresh ||
  3579. Math.abs(pt.y - p0.y) > thresh) &&
  3580. (Math.abs(pt.x - p1.x) > thresh ||
  3581. Math.abs(pt.y - p1.y) > thresh) &&
  3582. (Math.abs(pt.x - p2.x) > thresh ||
  3583. Math.abs(pt.y - p2.y) > thresh) &&
  3584. (Math.abs(pt.x - p3.x) > thresh ||
  3585. Math.abs(pt.y - p3.y) > thresh))
  3586. {
  3587. var dx = pt.x - p0.x;
  3588. var dy = pt.y - p0.y;
  3589. var temp = {distSq: dx * dx + dy * dy, x: pt.x, y: pt.y};
  3590. // Intersections must be ordered by distance from start of segment
  3591. for (var t = 0; t < list.length; t++)
  3592. {
  3593. if (list[t].distSq > temp.distSq)
  3594. {
  3595. list.splice(t, 0, temp);
  3596. temp = null;
  3597. break;
  3598. }
  3599. }
  3600. // Ignores multiple intersections at segment joint
  3601. if (temp != null && (list.length == 0 ||
  3602. list[list.length - 1].x !== temp.x ||
  3603. list[list.length - 1].y !== temp.y))
  3604. {
  3605. list.push(temp);
  3606. }
  3607. }
  3608. }
  3609. }
  3610. }
  3611. // Adds ordered intersections to routed points
  3612. for (var j = 0; j < list.length; j++)
  3613. {
  3614. changed = addPoint(1, list[j].x, list[j].y) || changed;
  3615. }
  3616. }
  3617. var pt = pts[pts.length - 1];
  3618. changed = addPoint(0, pt.x, pt.y) || changed;
  3619. }
  3620. state.routedPoints = actual;
  3621. return changed;
  3622. }
  3623. else
  3624. {
  3625. return false;
  3626. }
  3627. };
  3628. /**
  3629. * Overrides painting the actual shape for taking into account jump style.
  3630. */
  3631. var mxConnectorPaintLine = mxConnector.prototype.paintLine;
  3632. mxConnector.prototype.paintLine = function (c, absPts, rounded)
  3633. {
  3634. // Required for checking dirty state
  3635. this.routedPoints = (this.state != null) ? this.state.routedPoints : null;
  3636. if (this.outline || this.state == null || this.style == null ||
  3637. this.state.routedPoints == null || this.state.routedPoints.length == 0)
  3638. {
  3639. mxConnectorPaintLine.apply(this, arguments);
  3640. }
  3641. else
  3642. {
  3643. var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE,
  3644. mxConstants.LINE_ARCSIZE) / 2;
  3645. var size = (parseInt(mxUtils.getValue(this.style, 'jumpSize',
  3646. Graph.defaultJumpSize)) - 2) / 2 + this.strokewidth;
  3647. var style = mxUtils.getValue(this.style, 'jumpStyle', 'none');
  3648. var f = Editor.jumpSizeRatio;
  3649. var moveTo = true;
  3650. var last = null;
  3651. var len = null;
  3652. var pts = [];
  3653. var n = null;
  3654. c.begin();
  3655. for (var i = 0; i < this.state.routedPoints.length; i++)
  3656. {
  3657. var rpt = this.state.routedPoints[i];
  3658. var pt = new mxPoint(rpt.x / this.scale, rpt.y / this.scale);
  3659. // Takes first and last point from passed-in array
  3660. if (i == 0)
  3661. {
  3662. pt = absPts[0];
  3663. }
  3664. else if (i == this.state.routedPoints.length - 1)
  3665. {
  3666. pt = absPts[absPts.length - 1];
  3667. }
  3668. var done = false;
  3669. // Type 1 is an intersection
  3670. if (last != null && rpt.type == 1)
  3671. {
  3672. // Checks if next/previous points are too close
  3673. var next = this.state.routedPoints[i + 1];
  3674. var dx = next.x / this.scale - pt.x;
  3675. var dy = next.y / this.scale - pt.y;
  3676. var dist = dx * dx + dy * dy;
  3677. if (n == null)
  3678. {
  3679. n = new mxPoint(pt.x - last.x, pt.y - last.y);
  3680. len = Math.sqrt(n.x * n.x + n.y * n.y);
  3681. if (len > 0)
  3682. {
  3683. n.x = n.x * size / len;
  3684. n.y = n.y * size / len;
  3685. }
  3686. else
  3687. {
  3688. n = null;
  3689. }
  3690. }
  3691. if (dist > size * size && len > 0)
  3692. {
  3693. var dx = last.x - pt.x;
  3694. var dy = last.y - pt.y;
  3695. var dist = dx * dx + dy * dy;
  3696. if (dist > size * size)
  3697. {
  3698. var p0 = new mxPoint(pt.x - n.x, pt.y - n.y);
  3699. var p1 = new mxPoint(pt.x + n.x, pt.y + n.y);
  3700. pts.push(p0);
  3701. this.addPoints(c, pts, rounded, arcSize, false, null, moveTo);
  3702. var f = (Math.round(n.x) < 0 || (Math.round(n.x) == 0
  3703. && Math.round(n.y) <= 0)) ? 1 : -1;
  3704. moveTo = false;
  3705. if (style == 'sharp')
  3706. {
  3707. c.lineTo(p0.x - n.y * f, p0.y + n.x * f);
  3708. c.lineTo(p1.x - n.y * f, p1.y + n.x * f);
  3709. c.lineTo(p1.x, p1.y);
  3710. }
  3711. else if (style == 'arc')
  3712. {
  3713. f *= 1.3;
  3714. c.curveTo(p0.x - n.y * f, p0.y + n.x * f,
  3715. p1.x - n.y * f, p1.y + n.x * f,
  3716. p1.x, p1.y);
  3717. }
  3718. else
  3719. {
  3720. c.moveTo(p1.x, p1.y);
  3721. moveTo = true;
  3722. }
  3723. pts = [p1];
  3724. done = true;
  3725. }
  3726. }
  3727. }
  3728. else
  3729. {
  3730. n = null;
  3731. }
  3732. if (!done)
  3733. {
  3734. pts.push(pt);
  3735. last = pt;
  3736. }
  3737. }
  3738. this.addPoints(c, pts, rounded, arcSize, false, null, moveTo);
  3739. c.stroke();
  3740. }
  3741. };
  3742. /**
  3743. * Adds support for snapToPoint style.
  3744. */
  3745. var mxGraphViewUpdateFloatingTerminalPoint = mxGraphView.prototype.updateFloatingTerminalPoint;
  3746. mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source)
  3747. {
  3748. if (start != null && edge != null &&
  3749. (start.style['snapToPoint'] == '1' ||
  3750. edge.style['snapToPoint'] == '1'))
  3751. {
  3752. start = this.getTerminalPort(edge, start, source);
  3753. var next = this.getNextPoint(edge, end, source);
  3754. var orth = this.graph.isOrthogonal(edge);
  3755. var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0'));
  3756. var center = new mxPoint(start.getCenterX(), start.getCenterY());
  3757. if (alpha != 0)
  3758. {
  3759. var cos = Math.cos(-alpha);
  3760. var sin = Math.sin(-alpha);
  3761. next = mxUtils.getRotatedPoint(next, cos, sin, center);
  3762. }
  3763. var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0);
  3764. border += parseFloat(edge.style[(source) ?
  3765. mxConstants.STYLE_SOURCE_PERIMETER_SPACING :
  3766. mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0);
  3767. var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border);
  3768. if (alpha != 0)
  3769. {
  3770. var cos = Math.cos(alpha);
  3771. var sin = Math.sin(alpha);
  3772. pt = mxUtils.getRotatedPoint(pt, cos, sin, center);
  3773. }
  3774. edge.setAbsoluteTerminalPoint(this.snapToAnchorPoint(edge, start, end, source, pt), source);
  3775. }
  3776. else
  3777. {
  3778. mxGraphViewUpdateFloatingTerminalPoint.apply(this, arguments);
  3779. }
  3780. };
  3781. mxGraphView.prototype.snapToAnchorPoint = function(edge, start, end, source, pt)
  3782. {
  3783. if (start != null && edge != null)
  3784. {
  3785. var constraints = this.graph.getAllConnectionConstraints(start)
  3786. var nearest = null;
  3787. var dist = null;
  3788. if (constraints != null)
  3789. {
  3790. for (var i = 0; i < constraints.length; i++)
  3791. {
  3792. var cp = this.graph.getConnectionPoint(start, constraints[i]);
  3793. if (cp != null)
  3794. {
  3795. var tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y);
  3796. if (dist == null || tmp < dist)
  3797. {
  3798. nearest = cp;
  3799. dist = tmp;
  3800. }
  3801. }
  3802. }
  3803. }
  3804. if (nearest != null)
  3805. {
  3806. pt = nearest;
  3807. }
  3808. }
  3809. return pt;
  3810. };
  3811. /**
  3812. * Adds support for placeholders in text elements of shapes.
  3813. */
  3814. var mxStencilEvaluateTextAttribute = mxStencil.prototype.evaluateTextAttribute;
  3815. mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape)
  3816. {
  3817. var result = mxStencilEvaluateTextAttribute.apply(this, arguments);
  3818. var placeholders = node.getAttribute('placeholders');
  3819. if (placeholders == '1' && shape.state != null)
  3820. {
  3821. result = shape.state.view.graph.replacePlaceholders(shape.state.cell, result);
  3822. }
  3823. return result;
  3824. };
  3825. /**
  3826. * Adds custom stencils defined via shape=stencil(value) style. The value is a base64 encoded, compressed and
  3827. * URL encoded XML definition of the shape according to the stencil definition language of mxGraph.
  3828. *
  3829. * Needs to be in this file to make sure its part of the embed client code. Also the check for ZLib is
  3830. * different than for the Editor code.
  3831. */
  3832. var mxCellRendererCreateShape = mxCellRenderer.prototype.createShape;
  3833. mxCellRenderer.prototype.createShape = function(state)
  3834. {
  3835. if (state.style != null && typeof(pako) !== 'undefined')
  3836. {
  3837. var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
  3838. // Extracts and decodes stencil XML if shape has the form shape=stencil(value)
  3839. if (shape != null && shape.substring(0, 8) == 'stencil(')
  3840. {
  3841. try
  3842. {
  3843. var stencil = shape.substring(8, shape.length - 1);
  3844. var doc = mxUtils.parseXml(Graph.decompress(stencil));
  3845. return new mxShape(new mxStencil(doc.documentElement));
  3846. }
  3847. catch (e)
  3848. {
  3849. if (window.console != null)
  3850. {
  3851. console.log('Error in shape: ' + e);
  3852. }
  3853. }
  3854. }
  3855. }
  3856. return mxCellRendererCreateShape.apply(this, arguments);
  3857. };
  3858. })();
  3859. /**
  3860. * Overrides stencil registry for dynamic loading of stencils.
  3861. */
  3862. /**
  3863. * Maps from library names to an array of Javascript filenames,
  3864. * which are synchronously loaded. Currently only stencil files
  3865. * (.xml) and JS files (.js) are supported.
  3866. * IMPORTANT: For embedded diagrams to work entries must also
  3867. * be added in EmbedServlet.java.
  3868. */
  3869. mxStencilRegistry.libraries = {};
  3870. /**
  3871. * Global switch to disable dynamic loading.
  3872. */
  3873. mxStencilRegistry.dynamicLoading = true;
  3874. /**
  3875. * Global switch to disable eval for JS (preload all JS instead).
  3876. */
  3877. mxStencilRegistry.allowEval = true;
  3878. /**
  3879. * Stores all package names that have been dynamically loaded.
  3880. * Each package is only loaded once.
  3881. */
  3882. mxStencilRegistry.packages = [];
  3883. // Extends the default stencil registry to add dynamic loading
  3884. mxStencilRegistry.getStencil = function(name)
  3885. {
  3886. var result = mxStencilRegistry.stencils[name];
  3887. if (result == null && mxCellRenderer.defaultShapes[name] == null && mxStencilRegistry.dynamicLoading)
  3888. {
  3889. var basename = mxStencilRegistry.getBasenameForStencil(name);
  3890. // Loads stencil files and tries again
  3891. if (basename != null)
  3892. {
  3893. var libs = mxStencilRegistry.libraries[basename];
  3894. if (libs != null)
  3895. {
  3896. if (mxStencilRegistry.packages[basename] == null)
  3897. {
  3898. for (var i = 0; i < libs.length; i++)
  3899. {
  3900. var fname = libs[i];
  3901. if (fname.toLowerCase().substring(fname.length - 4, fname.length) == '.xml')
  3902. {
  3903. mxStencilRegistry.loadStencilSet(fname, null);
  3904. }
  3905. else if (fname.toLowerCase().substring(fname.length - 3, fname.length) == '.js')
  3906. {
  3907. try
  3908. {
  3909. if (mxStencilRegistry.allowEval)
  3910. {
  3911. var req = mxUtils.load(fname);
  3912. if (req != null && req.getStatus() >= 200 && req.getStatus() <= 299)
  3913. {
  3914. eval.call(window, req.getText());
  3915. }
  3916. }
  3917. }
  3918. catch (e)
  3919. {
  3920. if (window.console != null)
  3921. {
  3922. console.log('error in getStencil:', fname, e);
  3923. }
  3924. }
  3925. }
  3926. else
  3927. {
  3928. // FIXME: This does not yet work as the loading is triggered after
  3929. // the shape was used in the graph, at which point the keys have
  3930. // typically been translated in the calling method.
  3931. //mxResources.add(fname);
  3932. }
  3933. }
  3934. mxStencilRegistry.packages[basename] = 1;
  3935. }
  3936. }
  3937. else
  3938. {
  3939. // Replaces '_-_' with '_'
  3940. basename = basename.replace('_-_', '_');
  3941. mxStencilRegistry.loadStencilSet(STENCIL_PATH + '/' + basename + '.xml', null);
  3942. }
  3943. result = mxStencilRegistry.stencils[name];
  3944. }
  3945. }
  3946. return result;
  3947. };
  3948. // Returns the basename for the given stencil or null if no file must be
  3949. // loaded to render the given stencil.
  3950. mxStencilRegistry.getBasenameForStencil = function(name)
  3951. {
  3952. var tmp = null;
  3953. if (name != null)
  3954. {
  3955. var parts = name.split('.');
  3956. if (parts.length > 0 && parts[0] == 'mxgraph')
  3957. {
  3958. tmp = parts[1];
  3959. for (var i = 2; i < parts.length - 1; i++)
  3960. {
  3961. tmp += '/' + parts[i];
  3962. }
  3963. }
  3964. }
  3965. return tmp;
  3966. };
  3967. // Loads the given stencil set
  3968. mxStencilRegistry.loadStencilSet = function(stencilFile, postStencilLoad, force, async)
  3969. {
  3970. force = (force != null) ? force : false;
  3971. // Uses additional cache for detecting previous load attempts
  3972. var xmlDoc = mxStencilRegistry.packages[stencilFile];
  3973. if (force || xmlDoc == null)
  3974. {
  3975. var install = false;
  3976. if (xmlDoc == null)
  3977. {
  3978. try
  3979. {
  3980. if (async)
  3981. {
  3982. mxStencilRegistry.loadStencil(stencilFile, mxUtils.bind(this, function(xmlDoc2)
  3983. {
  3984. if (xmlDoc2 != null && xmlDoc2.documentElement != null)
  3985. {
  3986. mxStencilRegistry.packages[stencilFile] = xmlDoc2;
  3987. install = true;
  3988. mxStencilRegistry.parseStencilSet(xmlDoc2.documentElement, postStencilLoad, install);
  3989. }
  3990. }));
  3991. return;
  3992. }
  3993. else
  3994. {
  3995. xmlDoc = mxStencilRegistry.loadStencil(stencilFile);
  3996. mxStencilRegistry.packages[stencilFile] = xmlDoc;
  3997. install = true;
  3998. }
  3999. }
  4000. catch (e)
  4001. {
  4002. if (window.console != null)
  4003. {
  4004. console.log('error in loadStencilSet:', stencilFile, e);
  4005. }
  4006. }
  4007. }
  4008. if (xmlDoc != null && xmlDoc.documentElement != null)
  4009. {
  4010. mxStencilRegistry.parseStencilSet(xmlDoc.documentElement, postStencilLoad, install);
  4011. }
  4012. }
  4013. };
  4014. // Loads the given stencil XML file.
  4015. mxStencilRegistry.loadStencil = function(filename, fn)
  4016. {
  4017. if (fn != null)
  4018. {
  4019. var req = mxUtils.get(filename, mxUtils.bind(this, function(req)
  4020. {
  4021. fn((req.getStatus() >= 200 && req.getStatus() <= 299) ? req.getXml() : null);
  4022. }));
  4023. }
  4024. else
  4025. {
  4026. return mxUtils.load(filename).getXml();
  4027. }
  4028. };
  4029. // Takes array of strings
  4030. mxStencilRegistry.parseStencilSets = function(stencils)
  4031. {
  4032. for (var i = 0; i < stencils.length; i++)
  4033. {
  4034. mxStencilRegistry.parseStencilSet(mxUtils.parseXml(stencils[i]).documentElement);
  4035. }
  4036. };
  4037. // Parses the given stencil set
  4038. mxStencilRegistry.parseStencilSet = function(root, postStencilLoad, install)
  4039. {
  4040. if (root.nodeName == 'stencils')
  4041. {
  4042. var shapes = root.firstChild;
  4043. while (shapes != null)
  4044. {
  4045. if (shapes.nodeName == 'shapes')
  4046. {
  4047. mxStencilRegistry.parseStencilSet(shapes, postStencilLoad, install);
  4048. }
  4049. shapes = shapes.nextSibling;
  4050. }
  4051. }
  4052. else
  4053. {
  4054. install = (install != null) ? install : true;
  4055. var shape = root.firstChild;
  4056. var packageName = '';
  4057. var name = root.getAttribute('name');
  4058. if (name != null)
  4059. {
  4060. packageName = name + '.';
  4061. }
  4062. while (shape != null)
  4063. {
  4064. if (shape.nodeType == mxConstants.NODETYPE_ELEMENT)
  4065. {
  4066. name = shape.getAttribute('name');
  4067. if (name != null)
  4068. {
  4069. packageName = packageName.toLowerCase();
  4070. var stencilName = name.replace(/ /g,"_");
  4071. if (install)
  4072. {
  4073. mxStencilRegistry.addStencil(packageName + stencilName.toLowerCase(), new mxStencil(shape));
  4074. }
  4075. if (postStencilLoad != null)
  4076. {
  4077. var w = shape.getAttribute('w');
  4078. var h = shape.getAttribute('h');
  4079. w = (w == null) ? 80 : parseInt(w, 10);
  4080. h = (h == null) ? 80 : parseInt(h, 10);
  4081. postStencilLoad(packageName, stencilName, name, w, h);
  4082. }
  4083. }
  4084. }
  4085. shape = shape.nextSibling;
  4086. }
  4087. }
  4088. };
  4089. /**
  4090. * These overrides are only added if mxVertexHandler is defined (ie. not in embedded graph)
  4091. */
  4092. if (typeof mxVertexHandler != 'undefined')
  4093. {
  4094. (function()
  4095. {
  4096. // Sets colors for handles
  4097. mxConstants.HANDLE_FILLCOLOR = '#29b6f2';
  4098. mxConstants.HANDLE_STROKECOLOR = '#0088cf';
  4099. mxConstants.VERTEX_SELECTION_COLOR = '#00a8ff';
  4100. mxConstants.OUTLINE_COLOR = '#00a8ff';
  4101. mxConstants.OUTLINE_HANDLE_FILLCOLOR = '#99ccff';
  4102. mxConstants.OUTLINE_HANDLE_STROKECOLOR = '#00a8ff';
  4103. mxConstants.CONNECT_HANDLE_FILLCOLOR = '#cee7ff';
  4104. mxConstants.EDGE_SELECTION_COLOR = '#00a8ff';
  4105. mxConstants.DEFAULT_VALID_COLOR = '#00a8ff';
  4106. mxConstants.LABEL_HANDLE_FILLCOLOR = '#cee7ff';
  4107. mxConstants.GUIDE_COLOR = '#0088cf';
  4108. mxConstants.HIGHLIGHT_OPACITY = 30;
  4109. mxConstants.HIGHLIGHT_SIZE = 5;
  4110. // Enables snapping to off-grid terminals for edge waypoints
  4111. mxEdgeHandler.prototype.snapToTerminals = true;
  4112. // Enables guides
  4113. mxGraphHandler.prototype.guidesEnabled = true;
  4114. // Enables fading of rubberband
  4115. mxRubberband.prototype.fadeOut = true;
  4116. // Alt-move disables guides
  4117. mxGuide.prototype.isEnabledForEvent = function(evt)
  4118. {
  4119. return !mxEvent.isAltDown(evt);
  4120. };
  4121. // Extends connection handler to enable ctrl+drag for cloning source cell
  4122. // since copyOnConnect is now disabled by default
  4123. var mxConnectionHandlerCreateTarget = mxConnectionHandler.prototype.isCreateTarget;
  4124. mxConnectionHandler.prototype.isCreateTarget = function(evt)
  4125. {
  4126. return mxEvent.isControlDown(evt) || mxConnectionHandlerCreateTarget.apply(this, arguments);
  4127. };
  4128. // Overrides highlight shape for connection points
  4129. mxConstraintHandler.prototype.createHighlightShape = function()
  4130. {
  4131. var hl = new mxEllipse(null, this.highlightColor, this.highlightColor, 0);
  4132. hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
  4133. return hl;
  4134. };
  4135. // Overrides edge preview to use current edge shape and default style
  4136. mxConnectionHandler.prototype.livePreview = true;
  4137. mxConnectionHandler.prototype.cursor = 'crosshair';
  4138. // Uses current edge style for connect preview
  4139. mxConnectionHandler.prototype.createEdgeState = function(me)
  4140. {
  4141. var style = this.graph.createCurrentEdgeStyle();
  4142. var edge = this.graph.createEdge(null, null, null, null, null, style);
  4143. var state = new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
  4144. for (var key in this.graph.currentEdgeStyle)
  4145. {
  4146. state.style[key] = this.graph.currentEdgeStyle[key];
  4147. }
  4148. return state;
  4149. };
  4150. // Overrides dashed state with current edge style
  4151. var connectionHandlerCreateShape = mxConnectionHandler.prototype.createShape;
  4152. mxConnectionHandler.prototype.createShape = function()
  4153. {
  4154. var shape = connectionHandlerCreateShape.apply(this, arguments);
  4155. shape.isDashed = this.graph.currentEdgeStyle[mxConstants.STYLE_DASHED] == '1';
  4156. return shape;
  4157. }
  4158. // Overrides live preview to keep current style
  4159. mxConnectionHandler.prototype.updatePreview = function(valid)
  4160. {
  4161. // do not change color of preview
  4162. };
  4163. // Overrides connection handler to ignore edges instead of not allowing connections
  4164. var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
  4165. mxConnectionHandler.prototype.createMarker = function()
  4166. {
  4167. var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);
  4168. var markerGetCell = marker.getCell;
  4169. marker.getCell = mxUtils.bind(this, function(me)
  4170. {
  4171. var result = markerGetCell.apply(this, arguments);
  4172. this.error = null;
  4173. return result;
  4174. });
  4175. return marker;
  4176. };
  4177. /**
  4178. * Function: isCellLocked
  4179. *
  4180. * Returns true if the given cell does not allow new connections to be created.
  4181. * This implementation returns false.
  4182. */
  4183. mxConnectionHandler.prototype.isCellEnabled = function(cell)
  4184. {
  4185. return !this.graph.isCellLocked(cell);
  4186. };
  4187. /**
  4188. *
  4189. */
  4190. Graph.prototype.defaultVertexStyle = {};
  4191. /**
  4192. * Contains the default style for edges.
  4193. */
  4194. Graph.prototype.defaultEdgeStyle = {'edgeStyle': 'orthogonalEdgeStyle', 'rounded': '0',
  4195. 'jettySize': 'auto', 'orthogonalLoop': '1'};
  4196. /**
  4197. * Returns the current edge style as a string.
  4198. */
  4199. Graph.prototype.createCurrentEdgeStyle = function()
  4200. {
  4201. var style = 'edgeStyle=' + (this.currentEdgeStyle['edgeStyle'] || 'none') + ';';
  4202. if (this.currentEdgeStyle['shape'] != null)
  4203. {
  4204. style += 'shape=' + this.currentEdgeStyle['shape'] + ';';
  4205. }
  4206. if (this.currentEdgeStyle['curved'] != null)
  4207. {
  4208. style += 'curved=' + this.currentEdgeStyle['curved'] + ';';
  4209. }
  4210. if (this.currentEdgeStyle['rounded'] != null)
  4211. {
  4212. style += 'rounded=' + this.currentEdgeStyle['rounded'] + ';';
  4213. }
  4214. if (this.currentEdgeStyle['comic'] != null)
  4215. {
  4216. style += 'comic=' + this.currentEdgeStyle['comic'] + ';';
  4217. }
  4218. if (this.currentEdgeStyle['jumpStyle'] != null)
  4219. {
  4220. style += 'jumpStyle=' + this.currentEdgeStyle['jumpStyle'] + ';';
  4221. }
  4222. if (this.currentEdgeStyle['jumpSize'] != null)
  4223. {
  4224. style += 'jumpSize=' + this.currentEdgeStyle['jumpSize'] + ';';
  4225. }
  4226. // Overrides the global default to match the default edge style
  4227. if (this.currentEdgeStyle['orthogonalLoop'] != null)
  4228. {
  4229. style += 'orthogonalLoop=' + this.currentEdgeStyle['orthogonalLoop'] + ';';
  4230. }
  4231. else if (Graph.prototype.defaultEdgeStyle['orthogonalLoop'] != null)
  4232. {
  4233. style += 'orthogonalLoop=' + Graph.prototype.defaultEdgeStyle['orthogonalLoop'] + ';';
  4234. }
  4235. // Overrides the global default to match the default edge style
  4236. if (this.currentEdgeStyle['jettySize'] != null)
  4237. {
  4238. style += 'jettySize=' + this.currentEdgeStyle['jettySize'] + ';';
  4239. }
  4240. else if (Graph.prototype.defaultEdgeStyle['jettySize'] != null)
  4241. {
  4242. style += 'jettySize=' + Graph.prototype.defaultEdgeStyle['jettySize'] + ';';
  4243. }
  4244. // Special logic for custom property of elbowEdgeStyle
  4245. if (this.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle' && this.currentEdgeStyle['elbow'] != null)
  4246. {
  4247. style += 'elbow=' + this.currentEdgeStyle['elbow'] + ';';
  4248. }
  4249. if (this.currentEdgeStyle['html'] != null)
  4250. {
  4251. style += 'html=' + this.currentEdgeStyle['html'] + ';';
  4252. }
  4253. else
  4254. {
  4255. style += 'html=1;';
  4256. }
  4257. return style;
  4258. };
  4259. /**
  4260. * Hook for subclassers.
  4261. */
  4262. Graph.prototype.getPagePadding = function()
  4263. {
  4264. return new mxPoint(0, 0);
  4265. };
  4266. /**
  4267. * Loads the stylesheet for this graph.
  4268. */
  4269. Graph.prototype.loadStylesheet = function()
  4270. {
  4271. var node = (this.themes != null) ? this.themes[this.defaultThemeName] :
  4272. (!mxStyleRegistry.dynamicLoading) ? null :
  4273. mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement();
  4274. if (node != null)
  4275. {
  4276. var dec = new mxCodec(node.ownerDocument);
  4277. dec.decode(node, this.getStylesheet());
  4278. }
  4279. };
  4280. /**
  4281. *
  4282. */
  4283. Graph.prototype.importGraphModel = function(node, dx, dy, crop)
  4284. {
  4285. dx = (dx != null) ? dx : 0;
  4286. dy = (dy != null) ? dy : 0;
  4287. var codec = new mxCodec(node.ownerDocument);
  4288. var tempModel = new mxGraphModel();
  4289. codec.decode(node, tempModel);
  4290. var cells = []
  4291. // Clones cells to remove invalid edges
  4292. var layers = tempModel.getChildren(this.cloneCell(
  4293. tempModel.root, this.isCloneInvalidEdges()));
  4294. if (layers != null)
  4295. {
  4296. // Uses copy as layers are removed from array inside loop
  4297. layers = layers.slice();
  4298. this.model.beginUpdate();
  4299. try
  4300. {
  4301. // Merges into unlocked current layer if one layer is pasted
  4302. if (layers.length == 1 && !this.isCellLocked(this.getDefaultParent()))
  4303. {
  4304. cells = this.moveCells(tempModel.getChildren(layers[0]),
  4305. dx, dy, false, this.getDefaultParent());
  4306. }
  4307. else
  4308. {
  4309. for (var i = 0; i < layers.length; i++)
  4310. {
  4311. cells = cells.concat(this.model.getChildren(this.moveCells(
  4312. [layers[i]], dx, dy, false, this.model.getRoot())[0]));
  4313. }
  4314. }
  4315. if (crop)
  4316. {
  4317. if (this.isGridEnabled())
  4318. {
  4319. dx = this.snap(dx);
  4320. dy = this.snap(dy);
  4321. }
  4322. var bounds = this.getBoundingBoxFromGeometry(cells, true);
  4323. if (bounds != null)
  4324. {
  4325. this.moveCells(cells, dx - bounds.x, dy - bounds.y);
  4326. }
  4327. }
  4328. }
  4329. finally
  4330. {
  4331. this.model.endUpdate();
  4332. }
  4333. }
  4334. return cells;
  4335. };
  4336. /**
  4337. * Overrides method to provide connection constraints for shapes.
  4338. */
  4339. Graph.prototype.getAllConnectionConstraints = function(terminal, source)
  4340. {
  4341. if (terminal != null)
  4342. {
  4343. var constraints = mxUtils.getValue(terminal.style, 'points', null);
  4344. if (constraints != null)
  4345. {
  4346. // Requires an array of arrays with x, y (0..1), an optional
  4347. // [perimeter (0 or 1), dx, and dy] eg. points=[[0,0,1,-10,10],[0,1,0],[1,1]]
  4348. var result = [];
  4349. try
  4350. {
  4351. var c = JSON.parse(constraints);
  4352. for (var i = 0; i < c.length; i++)
  4353. {
  4354. var tmp = c[i];
  4355. result.push(new mxConnectionConstraint(new mxPoint(tmp[0], tmp[1]), (tmp.length > 2) ? tmp[2] != '0' : true,
  4356. null, (tmp.length > 3) ? tmp[3] : 0, (tmp.length > 4) ? tmp[4] : 0));
  4357. }
  4358. }
  4359. catch (e)
  4360. {
  4361. // ignore
  4362. }
  4363. return result;
  4364. }
  4365. else if (terminal.shape != null && terminal.shape.bounds != null)
  4366. {
  4367. var dir = terminal.shape.direction;
  4368. var bounds = terminal.shape.bounds;
  4369. var scale = terminal.shape.scale;
  4370. var w = bounds.width / scale;
  4371. var h = bounds.height / scale;
  4372. if (dir == mxConstants.DIRECTION_NORTH || dir == mxConstants.DIRECTION_SOUTH)
  4373. {
  4374. var tmp = w;
  4375. w = h;
  4376. h = tmp;
  4377. }
  4378. constraints = terminal.shape.getConstraints(terminal.style, w, h);
  4379. if (constraints != null)
  4380. {
  4381. return constraints;
  4382. }
  4383. else if (terminal.shape.stencil != null && terminal.shape.stencil.constraints != null)
  4384. {
  4385. return terminal.shape.stencil.constraints;
  4386. }
  4387. else if (terminal.shape.constraints != null)
  4388. {
  4389. return terminal.shape.constraints;
  4390. }
  4391. }
  4392. }
  4393. return null;
  4394. };
  4395. /**
  4396. * Inverts the elbow edge style without removing existing styles.
  4397. */
  4398. Graph.prototype.flipEdge = function(edge)
  4399. {
  4400. if (edge != null)
  4401. {
  4402. var state = this.view.getState(edge);
  4403. var style = (state != null) ? state.style : this.getCellStyle(edge);
  4404. if (style != null)
  4405. {
  4406. var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW,
  4407. mxConstants.ELBOW_HORIZONTAL);
  4408. var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ?
  4409. mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL;
  4410. this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]);
  4411. }
  4412. }
  4413. };
  4414. /**
  4415. * Disables drill-down for non-swimlanes.
  4416. */
  4417. Graph.prototype.isValidRoot = function(cell)
  4418. {
  4419. // Counts non-relative children
  4420. var childCount = this.model.getChildCount(cell);
  4421. var realChildCount = 0;
  4422. for (var i = 0; i < childCount; i++)
  4423. {
  4424. var child = this.model.getChildAt(cell, i);
  4425. if (this.model.isVertex(child))
  4426. {
  4427. var geometry = this.getCellGeometry(child);
  4428. if (geometry != null && !geometry.relative)
  4429. {
  4430. realChildCount++;
  4431. }
  4432. }
  4433. }
  4434. return realChildCount > 0 || this.isContainer(cell);
  4435. };
  4436. /**
  4437. * Disables drill-down for non-swimlanes.
  4438. */
  4439. Graph.prototype.isValidDropTarget = function(cell)
  4440. {
  4441. var state = this.view.getState(cell);
  4442. var style = (state != null) ? state.style : this.getCellStyle(cell);
  4443. return mxUtils.getValue(style, 'part', '0') != '1' && (this.isContainer(cell) ||
  4444. (mxGraph.prototype.isValidDropTarget.apply(this, arguments) &&
  4445. mxUtils.getValue(style, 'dropTarget', '1') != '0'));
  4446. };
  4447. /**
  4448. * Overrides createGroupCell to set the group style for new groups to 'group'.
  4449. */
  4450. Graph.prototype.createGroupCell = function()
  4451. {
  4452. var group = mxGraph.prototype.createGroupCell.apply(this, arguments);
  4453. group.setStyle('group');
  4454. return group;
  4455. };
  4456. /**
  4457. * Disables extending parents with stack layouts on add
  4458. */
  4459. Graph.prototype.isExtendParentsOnAdd = function(cell)
  4460. {
  4461. var result = mxGraph.prototype.isExtendParentsOnAdd.apply(this, arguments);
  4462. if (result && cell != null && this.layoutManager != null)
  4463. {
  4464. var parent = this.model.getParent(cell);
  4465. if (parent != null)
  4466. {
  4467. var layout = this.layoutManager.getLayout(parent);
  4468. if (layout != null && layout.constructor == mxStackLayout)
  4469. {
  4470. result = false;
  4471. }
  4472. }
  4473. }
  4474. return result;
  4475. };
  4476. /**
  4477. * Overrides autosize to add a border.
  4478. */
  4479. Graph.prototype.getPreferredSizeForCell = function(cell)
  4480. {
  4481. var result = mxGraph.prototype.getPreferredSizeForCell.apply(this, arguments);
  4482. // Adds buffer
  4483. if (result != null)
  4484. {
  4485. result.width += 10;
  4486. result.height += 4;
  4487. if (this.gridEnabled)
  4488. {
  4489. result.width = this.snap(result.width);
  4490. result.height = this.snap(result.height);
  4491. }
  4492. }
  4493. return result;
  4494. }
  4495. /**
  4496. * Turns the given cells and returns the changed cells.
  4497. */
  4498. Graph.prototype.turnShapes = function(cells)
  4499. {
  4500. var model = this.getModel();
  4501. var select = [];
  4502. model.beginUpdate();
  4503. try
  4504. {
  4505. for (var i = 0; i < cells.length; i++)
  4506. {
  4507. var cell = cells[i];
  4508. if (model.isEdge(cell))
  4509. {
  4510. var src = model.getTerminal(cell, true);
  4511. var trg = model.getTerminal(cell, false);
  4512. model.setTerminal(cell, trg, true);
  4513. model.setTerminal(cell, src, false);
  4514. var geo = model.getGeometry(cell);
  4515. if (geo != null)
  4516. {
  4517. geo = geo.clone();
  4518. if (geo.points != null)
  4519. {
  4520. geo.points.reverse();
  4521. }
  4522. var sp = geo.getTerminalPoint(true);
  4523. var tp = geo.getTerminalPoint(false)
  4524. geo.setTerminalPoint(sp, false);
  4525. geo.setTerminalPoint(tp, true);
  4526. model.setGeometry(cell, geo);
  4527. // Inverts constraints
  4528. var edgeState = this.view.getState(cell);
  4529. var sourceState = this.view.getState(src);
  4530. var targetState = this.view.getState(trg);
  4531. if (edgeState != null)
  4532. {
  4533. var sc = (sourceState != null) ? this.getConnectionConstraint(edgeState, sourceState, true) : null;
  4534. var tc = (targetState != null) ? this.getConnectionConstraint(edgeState, targetState, false) : null;
  4535. this.setConnectionConstraint(cell, src, true, tc);
  4536. this.setConnectionConstraint(cell, trg, false, sc);
  4537. }
  4538. select.push(cell);
  4539. }
  4540. }
  4541. else if (model.isVertex(cell))
  4542. {
  4543. var geo = this.getCellGeometry(cell);
  4544. if (geo != null)
  4545. {
  4546. // Rotates the size and position in the geometry
  4547. geo = geo.clone();
  4548. geo.x += geo.width / 2 - geo.height / 2;
  4549. geo.y += geo.height / 2 - geo.width / 2;
  4550. var tmp = geo.width;
  4551. geo.width = geo.height;
  4552. geo.height = tmp;
  4553. model.setGeometry(cell, geo);
  4554. // Reads the current direction and advances by 90 degrees
  4555. var state = this.view.getState(cell);
  4556. if (state != null)
  4557. {
  4558. var dir = state.style[mxConstants.STYLE_DIRECTION] || 'east'/*default*/;
  4559. if (dir == 'east')
  4560. {
  4561. dir = 'south';
  4562. }
  4563. else if (dir == 'south')
  4564. {
  4565. dir = 'west';
  4566. }
  4567. else if (dir == 'west')
  4568. {
  4569. dir = 'north';
  4570. }
  4571. else if (dir == 'north')
  4572. {
  4573. dir = 'east';
  4574. }
  4575. this.setCellStyles(mxConstants.STYLE_DIRECTION, dir, [cell]);
  4576. }
  4577. select.push(cell);
  4578. }
  4579. }
  4580. }
  4581. }
  4582. finally
  4583. {
  4584. model.endUpdate();
  4585. }
  4586. return select;
  4587. };
  4588. /**
  4589. * Returns true if the given stencil contains any placeholder text.
  4590. */
  4591. Graph.prototype.stencilHasPlaceholders = function(stencil)
  4592. {
  4593. if (stencil != null && stencil.fgNode != null)
  4594. {
  4595. var node = stencil.fgNode.firstChild;
  4596. while (node != null)
  4597. {
  4598. if (node.nodeName == 'text' && node.getAttribute('placeholders') == '1')
  4599. {
  4600. return true;
  4601. }
  4602. node = node.nextSibling;
  4603. }
  4604. }
  4605. return false;
  4606. };
  4607. /**
  4608. * Updates the child cells with placeholders if metadata of a cell has changed.
  4609. */
  4610. Graph.prototype.processChange = function(change)
  4611. {
  4612. mxGraph.prototype.processChange.apply(this, arguments);
  4613. if (change instanceof mxValueChange && change.cell != null &&
  4614. change.cell.value != null && typeof(change.cell.value) == 'object')
  4615. {
  4616. // Invalidates all descendants with placeholders
  4617. var desc = this.model.getDescendants(change.cell);
  4618. // LATER: Check if only label or tooltip have changed
  4619. if (desc.length > 0)
  4620. {
  4621. for (var i = 0; i < desc.length; i++)
  4622. {
  4623. var state = this.view.getState(desc[i]);
  4624. if (state != null && state.shape != null && state.shape.stencil != null &&
  4625. this.stencilHasPlaceholders(state.shape.stencil))
  4626. {
  4627. this.removeStateForCell(desc[i]);
  4628. }
  4629. else if (this.isReplacePlaceholders(desc[i]))
  4630. {
  4631. this.view.invalidate(desc[i], false, false);
  4632. }
  4633. }
  4634. }
  4635. }
  4636. };
  4637. /**
  4638. * Replaces the given element with a span.
  4639. */
  4640. Graph.prototype.replaceElement = function(elt, tagName)
  4641. {
  4642. var span = elt.ownerDocument.createElement((tagName != null) ? tagName : 'span');
  4643. var attributes = Array.prototype.slice.call(elt.attributes);
  4644. while (attr = attributes.pop())
  4645. {
  4646. span.setAttribute(attr.nodeName, attr.nodeValue);
  4647. }
  4648. span.innerHTML = elt.innerHTML;
  4649. elt.parentNode.replaceChild(span, elt);
  4650. };
  4651. /**
  4652. * Handles label changes for XML user objects.
  4653. */
  4654. Graph.prototype.updateLabelElements = function(cells, fn, tagName)
  4655. {
  4656. cells = (cells != null) ? cells : this.getSelectionCells();
  4657. var div = document.createElement('div');
  4658. for (var i = 0; i < cells.length; i++)
  4659. {
  4660. // Changes font tags inside HTML labels
  4661. if (this.isHtmlLabel(cells[i]))
  4662. {
  4663. var label = this.convertValueToString(cells[i]);
  4664. if (label != null && label.length > 0)
  4665. {
  4666. div.innerHTML = label;
  4667. var elts = div.getElementsByTagName((tagName != null) ? tagName : '*');
  4668. for (var j = 0; j < elts.length; j++)
  4669. {
  4670. fn(elts[j]);
  4671. }
  4672. if (div.innerHTML != label)
  4673. {
  4674. this.cellLabelChanged(cells[i], div.innerHTML);
  4675. }
  4676. }
  4677. }
  4678. }
  4679. };
  4680. /**
  4681. * Handles label changes for XML user objects.
  4682. */
  4683. Graph.prototype.cellLabelChanged = function(cell, value, autoSize)
  4684. {
  4685. // Removes all illegal control characters in user input
  4686. value = Graph.zapGremlins(value);
  4687. this.model.beginUpdate();
  4688. try
  4689. {
  4690. if (cell.value != null && typeof cell.value == 'object')
  4691. {
  4692. if (this.isReplacePlaceholders(cell) &&
  4693. cell.getAttribute('placeholder') != null)
  4694. {
  4695. // LATER: Handle delete, name change
  4696. var name = cell.getAttribute('placeholder');
  4697. var current = cell;
  4698. while (current != null)
  4699. {
  4700. if (current == this.model.getRoot() || (current.value != null &&
  4701. typeof(current.value) == 'object' && current.hasAttribute(name)))
  4702. {
  4703. this.setAttributeForCell(current, name, value);
  4704. break;
  4705. }
  4706. current = this.model.getParent(current);
  4707. }
  4708. }
  4709. var tmp = cell.value.cloneNode(true);
  4710. tmp.setAttribute('label', value);
  4711. value = tmp;
  4712. }
  4713. mxGraph.prototype.cellLabelChanged.apply(this, arguments);
  4714. }
  4715. finally
  4716. {
  4717. this.model.endUpdate();
  4718. }
  4719. };
  4720. /**
  4721. * Removes transparent empty groups if all children are removed.
  4722. */
  4723. Graph.prototype.cellsRemoved = function(cells)
  4724. {
  4725. if (cells != null)
  4726. {
  4727. var dict = new mxDictionary();
  4728. for (var i = 0; i < cells.length; i++)
  4729. {
  4730. dict.put(cells[i], true);
  4731. }
  4732. // LATER: Recurse up the cell hierarchy
  4733. var parents = [];
  4734. for (var i = 0; i < cells.length; i++)
  4735. {
  4736. var parent = this.model.getParent(cells[i]);
  4737. if (parent != null && !dict.get(parent))
  4738. {
  4739. dict.put(parent, true);
  4740. parents.push(parent);
  4741. }
  4742. }
  4743. for (var i = 0; i < parents.length; i++)
  4744. {
  4745. var state = this.view.getState(parents[i]);
  4746. if (state != null && (this.model.isEdge(state.cell) || this.model.isVertex(state.cell)) && this.isCellDeletable(state.cell))
  4747. {
  4748. var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
  4749. var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE);
  4750. if (stroke == mxConstants.NONE && fill == mxConstants.NONE)
  4751. {
  4752. var allChildren = true;
  4753. for (var j = 0; j < this.model.getChildCount(state.cell) && allChildren; j++)
  4754. {
  4755. if (!dict.get(this.model.getChildAt(state.cell, j)))
  4756. {
  4757. allChildren = false;
  4758. }
  4759. }
  4760. if (allChildren)
  4761. {
  4762. cells.push(state.cell);
  4763. }
  4764. }
  4765. }
  4766. }
  4767. }
  4768. mxGraph.prototype.cellsRemoved.apply(this, arguments);
  4769. };
  4770. /**
  4771. * Overrides ungroup to check if group should be removed.
  4772. */
  4773. Graph.prototype.removeCellsAfterUngroup = function(cells)
  4774. {
  4775. var cellsToRemove = [];
  4776. for (var i = 0; i < cells.length; i++)
  4777. {
  4778. if (this.isCellDeletable(cells[i]))
  4779. {
  4780. var state = this.view.getState(cells[i]);
  4781. if (state != null)
  4782. {
  4783. var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
  4784. var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE);
  4785. if (stroke == mxConstants.NONE && fill == mxConstants.NONE)
  4786. {
  4787. cellsToRemove.push(cells[i]);
  4788. }
  4789. }
  4790. }
  4791. }
  4792. cells = cellsToRemove;
  4793. mxGraph.prototype.removeCellsAfterUngroup.apply(this, arguments);
  4794. };
  4795. /**
  4796. * Sets the link for the given cell.
  4797. */
  4798. Graph.prototype.setLinkForCell = function(cell, link)
  4799. {
  4800. this.setAttributeForCell(cell, 'link', link);
  4801. };
  4802. /**
  4803. * Sets the link for the given cell.
  4804. */
  4805. Graph.prototype.setTooltipForCell = function(cell, link)
  4806. {
  4807. this.setAttributeForCell(cell, 'tooltip', link);
  4808. };
  4809. /**
  4810. * Returns the cells in the model (or given array) that have all of the
  4811. * given tags in their tags property.
  4812. */
  4813. Graph.prototype.getAttributeForCell = function(cell, attributeName, defaultValue)
  4814. {
  4815. return (cell.value != null && typeof cell.value === 'object') ?
  4816. (cell.value.getAttribute(attributeName) || defaultValue) :
  4817. defaultValue;
  4818. };
  4819. /**
  4820. * Sets the link for the given cell.
  4821. */
  4822. Graph.prototype.setAttributeForCell = function(cell, attributeName, attributeValue)
  4823. {
  4824. var value = null;
  4825. if (cell.value != null && typeof(cell.value) == 'object')
  4826. {
  4827. value = cell.value.cloneNode(true);
  4828. }
  4829. else
  4830. {
  4831. var doc = mxUtils.createXmlDocument();
  4832. value = doc.createElement('UserObject');
  4833. value.setAttribute('label', cell.value || '');
  4834. }
  4835. if (attributeValue != null)
  4836. {
  4837. value.setAttribute(attributeName, attributeValue);
  4838. }
  4839. else
  4840. {
  4841. value.removeAttribute(attributeName);
  4842. }
  4843. this.model.setValue(cell, value);
  4844. };
  4845. /**
  4846. * Overridden to stop moving edge labels between cells.
  4847. */
  4848. Graph.prototype.getDropTarget = function(cells, evt, cell, clone)
  4849. {
  4850. var model = this.getModel();
  4851. // Disables drop into group if alt is pressed
  4852. if (mxEvent.isAltDown(evt))
  4853. {
  4854. return null;
  4855. }
  4856. // Disables dragging edge labels out of edges
  4857. for (var i = 0; i < cells.length; i++)
  4858. {
  4859. if (this.model.isEdge(this.model.getParent(cells[i])))
  4860. {
  4861. return null;
  4862. }
  4863. }
  4864. return mxGraph.prototype.getDropTarget.apply(this, arguments);
  4865. };
  4866. /**
  4867. * Overrides double click handling to avoid accidental inserts of new labels in dblClick below.
  4868. */
  4869. Graph.prototype.click = function(me)
  4870. {
  4871. mxGraph.prototype.click.call(this, me);
  4872. // Stores state and source for checking in dblClick
  4873. this.firstClickState = me.getState();
  4874. this.firstClickSource = me.getSource();
  4875. };
  4876. /**
  4877. * Overrides double click handling to add the tolerance and inserting text.
  4878. */
  4879. Graph.prototype.dblClick = function(evt, cell)
  4880. {
  4881. if (this.isEnabled())
  4882. {
  4883. var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  4884. // Automatically adds new child cells to edges on double click
  4885. if (evt != null && !this.model.isVertex(cell))
  4886. {
  4887. var state = (this.model.isEdge(cell)) ? this.view.getState(cell) : null;
  4888. var src = mxEvent.getSource(evt);
  4889. if ((this.firstClickState == state && this.firstClickSource == src) &&
  4890. (state == null || (state.text == null || state.text.node == null ||
  4891. state.text.boundingBox == null || (!mxUtils.contains(state.text.boundingBox,
  4892. pt.x, pt.y) && !mxUtils.isAncestorNode(state.text.node, mxEvent.getSource(evt))))) &&
  4893. ((state == null && !this.isCellLocked(this.getDefaultParent())) ||
  4894. (state != null && !this.isCellLocked(state.cell))) &&
  4895. (state != null || (mxClient.IS_VML && src == this.view.getCanvas()) ||
  4896. (mxClient.IS_SVG && src == this.view.getCanvas().ownerSVGElement)))
  4897. {
  4898. cell = this.addText(pt.x, pt.y, state);
  4899. }
  4900. }
  4901. mxGraph.prototype.dblClick.call(this, evt, cell);
  4902. }
  4903. };
  4904. /**
  4905. * Returns a point that specifies the location for inserting cells.
  4906. */
  4907. Graph.prototype.getInsertPoint = function()
  4908. {
  4909. var gs = this.getGridSize();
  4910. var dx = this.container.scrollLeft / this.view.scale - this.view.translate.x;
  4911. var dy = this.container.scrollTop / this.view.scale - this.view.translate.y;
  4912. if (this.pageVisible)
  4913. {
  4914. var layout = this.getPageLayout();
  4915. var page = this.getPageSize();
  4916. dx = Math.max(dx, layout.x * page.width);
  4917. dy = Math.max(dy, layout.y * page.height);
  4918. }
  4919. return new mxPoint(this.snap(dx + gs), this.snap(dy + gs));
  4920. };
  4921. /**
  4922. *
  4923. */
  4924. Graph.prototype.getFreeInsertPoint = function()
  4925. {
  4926. var view = this.view;
  4927. var bds = this.getGraphBounds();
  4928. var pt = this.getInsertPoint();
  4929. // Places at same x-coord and 2 grid sizes below existing graph
  4930. var x = this.snap(Math.round(Math.max(pt.x, bds.x / view.scale - view.translate.x +
  4931. ((bds.width == 0) ? 2 * this.gridSize : 0))));
  4932. var y = this.snap(Math.round(Math.max(pt.y, (bds.y + bds.height) / view.scale - view.translate.y +
  4933. 2 * this.gridSize)));
  4934. return new mxPoint(x, y);
  4935. };
  4936. /**
  4937. * Hook for subclassers to return true if the current insert point was defined
  4938. * using a mouse hover event.
  4939. */
  4940. Graph.prototype.isMouseInsertPoint = function()
  4941. {
  4942. return false;
  4943. };
  4944. /**
  4945. * Adds a new label at the given position and returns the new cell. State is
  4946. * an optional edge state to be used as the parent for the label. Vertices
  4947. * are not allowed currently as states.
  4948. */
  4949. Graph.prototype.addText = function(x, y, state)
  4950. {
  4951. // Creates a new edge label with a predefined text
  4952. var label = new mxCell();
  4953. label.value = 'Text';
  4954. label.style = 'text;html=1;resizable=0;points=[];'
  4955. label.geometry = new mxGeometry(0, 0, 0, 0);
  4956. label.vertex = true;
  4957. if (state != null)
  4958. {
  4959. label.style += 'align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;'
  4960. label.geometry.relative = true;
  4961. label.connectable = false;
  4962. // Resets the relative location stored inside the geometry
  4963. var pt2 = this.view.getRelativePoint(state, x, y);
  4964. label.geometry.x = Math.round(pt2.x * 10000) / 10000;
  4965. label.geometry.y = Math.round(pt2.y);
  4966. // Resets the offset inside the geometry to find the offset from the resulting point
  4967. label.geometry.offset = new mxPoint(0, 0);
  4968. pt2 = this.view.getPoint(state, label.geometry);
  4969. var scale = this.view.scale;
  4970. label.geometry.offset = new mxPoint(Math.round((x - pt2.x) / scale), Math.round((y - pt2.y) / scale));
  4971. }
  4972. else
  4973. {
  4974. label.style += 'autosize=1;align=left;verticalAlign=top;spacingTop=-4;'
  4975. var tr = this.view.translate;
  4976. label.geometry.width = 40;
  4977. label.geometry.height = 20;
  4978. label.geometry.x = Math.round(x / this.view.scale) - tr.x;
  4979. label.geometry.y = Math.round(y / this.view.scale) - tr.y;
  4980. }
  4981. this.getModel().beginUpdate();
  4982. try
  4983. {
  4984. this.addCells([label], (state != null) ? state.cell : null);
  4985. this.fireEvent(new mxEventObject('textInserted', 'cells', [label]));
  4986. // Updates size of text after possible change of style via event
  4987. this.autoSizeCell(label);
  4988. }
  4989. finally
  4990. {
  4991. this.getModel().endUpdate();
  4992. }
  4993. return label;
  4994. };
  4995. /**
  4996. * Adds a handler for clicking on shapes with links. This replaces all links in labels.
  4997. */
  4998. Graph.prototype.addClickHandler = function(highlight, beforeClick, onClick)
  4999. {
  5000. // Replaces links in labels for consistent right-clicks
  5001. var checkLinks = mxUtils.bind(this, function()
  5002. {
  5003. var links = this.container.getElementsByTagName('a');
  5004. if (links != null)
  5005. {
  5006. for (var i = 0; i < links.length; i++)
  5007. {
  5008. var href = this.getAbsoluteUrl(links[i].getAttribute('href'));
  5009. if (href != null)
  5010. {
  5011. links[i].setAttribute('rel', this.linkRelation);
  5012. links[i].setAttribute('href', href);
  5013. if (beforeClick != null)
  5014. {
  5015. mxEvent.addGestureListeners(links[i], null, null, beforeClick);
  5016. }
  5017. }
  5018. }
  5019. }
  5020. });
  5021. this.model.addListener(mxEvent.CHANGE, checkLinks);
  5022. checkLinks();
  5023. var cursor = this.container.style.cursor;
  5024. var tol = this.getTolerance();
  5025. var graph = this;
  5026. var mouseListener =
  5027. {
  5028. currentState: null,
  5029. currentLink: null,
  5030. highlight: (highlight != null && highlight != '' && highlight != mxConstants.NONE) ?
  5031. new mxCellHighlight(graph, highlight, 4) : null,
  5032. startX: 0,
  5033. startY: 0,
  5034. scrollLeft: 0,
  5035. scrollTop: 0,
  5036. updateCurrentState: function(me)
  5037. {
  5038. var tmp = me.sourceState;
  5039. // Gets topmost intersecting cell with link
  5040. if (tmp == null || graph.getLinkForCell(tmp.cell) == null)
  5041. {
  5042. var cell = graph.getCellAt(me.getGraphX(), me.getGraphY(), null, null, null, function(state, x, y)
  5043. {
  5044. return graph.getLinkForCell(state.cell) == null;
  5045. });
  5046. tmp = graph.view.getState(cell);
  5047. }
  5048. if (tmp != this.currentState)
  5049. {
  5050. if (this.currentState != null)
  5051. {
  5052. this.clear();
  5053. }
  5054. this.currentState = tmp;
  5055. if (this.currentState != null)
  5056. {
  5057. this.activate(this.currentState);
  5058. }
  5059. }
  5060. },
  5061. mouseDown: function(sender, me)
  5062. {
  5063. this.startX = me.getGraphX();
  5064. this.startY = me.getGraphY();
  5065. this.scrollLeft = graph.container.scrollLeft;
  5066. this.scrollTop = graph.container.scrollTop;
  5067. if (this.currentLink == null && graph.container.style.overflow == 'auto')
  5068. {
  5069. graph.container.style.cursor = 'move';
  5070. }
  5071. this.updateCurrentState(me);
  5072. },
  5073. mouseMove: function(sender, me)
  5074. {
  5075. if (graph.isMouseDown)
  5076. {
  5077. if (this.currentLink != null)
  5078. {
  5079. var dx = Math.abs(this.startX - me.getGraphX());
  5080. var dy = Math.abs(this.startY - me.getGraphY());
  5081. if (dx > tol || dy > tol)
  5082. {
  5083. this.clear();
  5084. }
  5085. }
  5086. }
  5087. else
  5088. {
  5089. // Checks for parent link
  5090. var linkNode = me.getSource();
  5091. while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a')
  5092. {
  5093. linkNode = linkNode.parentNode;
  5094. }
  5095. if (linkNode != null)
  5096. {
  5097. this.clear();
  5098. }
  5099. else
  5100. {
  5101. if (graph.tooltipHandler != null && this.currentLink != null && this.currentState != null)
  5102. {
  5103. graph.tooltipHandler.reset(me, true, this.currentState);
  5104. }
  5105. if (this.currentState != null && (me.getState() == this.currentState || me.sourceState == null) &&
  5106. graph.intersects(this.currentState, me.getGraphX(), me.getGraphY()))
  5107. {
  5108. return;
  5109. }
  5110. this.updateCurrentState(me);
  5111. }
  5112. }
  5113. },
  5114. mouseUp: function(sender, me)
  5115. {
  5116. var source = me.getSource();
  5117. var evt = me.getEvent();
  5118. // Checks for parent link
  5119. var linkNode = source;
  5120. while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a')
  5121. {
  5122. linkNode = linkNode.parentNode;
  5123. }
  5124. // Ignores clicks on links and collapse/expand icon
  5125. if (linkNode == null &&
  5126. (((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol &&
  5127. Math.abs(this.scrollTop - graph.container.scrollTop) < tol) &&
  5128. (me.sourceState == null || !me.isSource(me.sourceState.control))) &&
  5129. (((mxEvent.isLeftMouseButton(evt) || mxEvent.isMiddleMouseButton(evt)) &&
  5130. !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt))))
  5131. {
  5132. if (this.currentLink != null)
  5133. {
  5134. var blank = graph.isBlankLink(this.currentLink);
  5135. if ((this.currentLink.substring(0, 5) === 'data:' ||
  5136. !blank) && beforeClick != null)
  5137. {
  5138. beforeClick(evt, this.currentLink);
  5139. }
  5140. if (!mxEvent.isConsumed(evt))
  5141. {
  5142. var target = (mxEvent.isMiddleMouseButton(evt)) ? '_blank' :
  5143. ((blank) ? graph.linkTarget : '_top');
  5144. graph.openLink(this.currentLink, target);
  5145. me.consume();
  5146. }
  5147. }
  5148. else if (onClick != null && !me.isConsumed() &&
  5149. (Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol &&
  5150. Math.abs(this.scrollTop - graph.container.scrollTop) < tol) &&
  5151. (Math.abs(this.startX - me.getGraphX()) < tol &&
  5152. Math.abs(this.startY - me.getGraphY()) < tol))
  5153. {
  5154. onClick(me.getEvent());
  5155. }
  5156. }
  5157. this.clear();
  5158. },
  5159. activate: function(state)
  5160. {
  5161. this.currentLink = graph.getAbsoluteUrl(graph.getLinkForCell(state.cell));
  5162. if (this.currentLink != null)
  5163. {
  5164. graph.container.style.cursor = 'pointer';
  5165. if (this.highlight != null)
  5166. {
  5167. this.highlight.highlight(state);
  5168. }
  5169. }
  5170. },
  5171. clear: function()
  5172. {
  5173. if (graph.container != null)
  5174. {
  5175. graph.container.style.cursor = cursor;
  5176. }
  5177. this.currentState = null;
  5178. this.currentLink = null;
  5179. if (this.highlight != null)
  5180. {
  5181. this.highlight.hide();
  5182. }
  5183. if (graph.tooltipHandler != null)
  5184. {
  5185. graph.tooltipHandler.hide();
  5186. }
  5187. }
  5188. };
  5189. // Ignores built-in click handling
  5190. graph.click = function(me) {};
  5191. graph.addMouseListener(mouseListener);
  5192. mxEvent.addListener(document, 'mouseleave', function(evt)
  5193. {
  5194. mouseListener.clear();
  5195. });
  5196. };
  5197. /**
  5198. * Duplicates the given cells and returns the duplicates.
  5199. */
  5200. Graph.prototype.duplicateCells = function(cells, append)
  5201. {
  5202. cells = (cells != null) ? cells : this.getSelectionCells();
  5203. append = (append != null) ? append : true;
  5204. cells = this.model.getTopmostCells(cells);
  5205. var model = this.getModel();
  5206. var s = this.gridSize;
  5207. var select = [];
  5208. model.beginUpdate();
  5209. try
  5210. {
  5211. var clones = this.cloneCells(cells, false, null, true);
  5212. for (var i = 0; i < cells.length; i++)
  5213. {
  5214. var parent = model.getParent(cells[i]);
  5215. var child = this.moveCells([clones[i]], s, s, false)[0];
  5216. select.push(child);
  5217. if (append)
  5218. {
  5219. model.add(parent, clones[i]);
  5220. }
  5221. else
  5222. {
  5223. // Maintains child index by inserting after clone in parent
  5224. var index = parent.getIndex(cells[i]);
  5225. model.add(parent, clones[i], index + 1);
  5226. }
  5227. }
  5228. }
  5229. finally
  5230. {
  5231. model.endUpdate();
  5232. }
  5233. return select;
  5234. };
  5235. /**
  5236. * Inserts the given image at the cursor in a content editable text box using
  5237. * the insertimage command on the document instance.
  5238. */
  5239. Graph.prototype.insertImage = function(newValue, w, h)
  5240. {
  5241. // To find the new image, we create a list of all existing links first
  5242. if (newValue != null && this.cellEditor.textarea != null)
  5243. {
  5244. var tmp = this.cellEditor.textarea.getElementsByTagName('img');
  5245. var oldImages = [];
  5246. for (var i = 0; i < tmp.length; i++)
  5247. {
  5248. oldImages.push(tmp[i]);
  5249. }
  5250. // LATER: Fix inserting link/image in IE8/quirks after focus lost
  5251. document.execCommand('insertimage', false, newValue);
  5252. // Sets size of new image
  5253. var newImages = this.cellEditor.textarea.getElementsByTagName('img');
  5254. if (newImages.length == oldImages.length + 1)
  5255. {
  5256. // Inverse order in favor of appended images
  5257. for (var i = newImages.length - 1; i >= 0; i--)
  5258. {
  5259. if (i == 0 || newImages[i] != oldImages[i - 1])
  5260. {
  5261. // Workaround for lost styles during undo and redo is using attributes
  5262. newImages[i].setAttribute('width', w);
  5263. newImages[i].setAttribute('height', h);
  5264. break;
  5265. }
  5266. }
  5267. }
  5268. }
  5269. };
  5270. /**
  5271. * Inserts the given image at the cursor in a content editable text box using
  5272. * the insertimage command on the document instance.
  5273. */
  5274. Graph.prototype.insertLink = function(value)
  5275. {
  5276. if (this.cellEditor.textarea != null)
  5277. {
  5278. if (value.length == 0)
  5279. {
  5280. document.execCommand('unlink', false);
  5281. }
  5282. else if (mxClient.IS_FF)
  5283. {
  5284. // Workaround for Firefox that adds a new link and removes
  5285. // the href from the inner link if its parent is a span is
  5286. // to remove all inner links inside the new outer link
  5287. var tmp = this.cellEditor.textarea.getElementsByTagName('a');
  5288. var oldLinks = [];
  5289. for (var i = 0; i < tmp.length; i++)
  5290. {
  5291. oldLinks.push(tmp[i]);
  5292. }
  5293. document.execCommand('createlink', false, mxUtils.trim(value));
  5294. // Finds the new link element
  5295. var newLinks = this.cellEditor.textarea.getElementsByTagName('a');
  5296. if (newLinks.length == oldLinks.length + 1)
  5297. {
  5298. // Inverse order in favor of appended links
  5299. for (var i = newLinks.length - 1; i >= 0; i--)
  5300. {
  5301. if (newLinks[i] != oldLinks[i - 1])
  5302. {
  5303. // Removes all inner links from the new link and
  5304. // moves the children to the inner link parent
  5305. var tmp = newLinks[i].getElementsByTagName('a');
  5306. while (tmp.length > 0)
  5307. {
  5308. var parent = tmp[0].parentNode;
  5309. while (tmp[0].firstChild != null)
  5310. {
  5311. parent.insertBefore(tmp[0].firstChild, tmp[0]);
  5312. }
  5313. parent.removeChild(tmp[0]);
  5314. }
  5315. break;
  5316. }
  5317. }
  5318. }
  5319. }
  5320. else
  5321. {
  5322. // LATER: Fix inserting link/image in IE8/quirks after focus lost
  5323. document.execCommand('createlink', false, mxUtils.trim(value));
  5324. }
  5325. }
  5326. };
  5327. /**
  5328. *
  5329. * @param cell
  5330. * @returns {Boolean}
  5331. */
  5332. Graph.prototype.isCellResizable = function(cell)
  5333. {
  5334. var result = mxGraph.prototype.isCellResizable.apply(this, arguments);
  5335. var state = this.view.getState(cell);
  5336. var style = (state != null) ? state.style : this.getCellStyle(cell);
  5337. return result || (mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0' &&
  5338. style[mxConstants.STYLE_WHITE_SPACE] == 'wrap');
  5339. };
  5340. /**
  5341. * Function: distributeCells
  5342. *
  5343. * Distribuets the centers of the given cells equally along the available
  5344. * horizontal or vertical space.
  5345. *
  5346. * Parameters:
  5347. *
  5348. * horizontal - Boolean that specifies the direction of the distribution.
  5349. * cells - Optional array of <mxCells> to be distributed. Edges are ignored.
  5350. */
  5351. Graph.prototype.distributeCells = function(horizontal, cells)
  5352. {
  5353. if (cells == null)
  5354. {
  5355. cells = this.getSelectionCells();
  5356. }
  5357. if (cells != null && cells.length > 1)
  5358. {
  5359. var vertices = [];
  5360. var max = null;
  5361. var min = null;
  5362. for (var i = 0; i < cells.length; i++)
  5363. {
  5364. if (this.getModel().isVertex(cells[i]))
  5365. {
  5366. var state = this.view.getState(cells[i]);
  5367. if (state != null)
  5368. {
  5369. var tmp = (horizontal) ? state.getCenterX() : state.getCenterY();
  5370. max = (max != null) ? Math.max(max, tmp) : tmp;
  5371. min = (min != null) ? Math.min(min, tmp) : tmp;
  5372. vertices.push(state);
  5373. }
  5374. }
  5375. }
  5376. if (vertices.length > 2)
  5377. {
  5378. vertices.sort(function(a, b)
  5379. {
  5380. return (horizontal) ? a.x - b.x : a.y - b.y;
  5381. });
  5382. var t = this.view.translate;
  5383. var s = this.view.scale;
  5384. min = min / s - ((horizontal) ? t.x : t.y);
  5385. max = max / s - ((horizontal) ? t.x : t.y);
  5386. this.getModel().beginUpdate();
  5387. try
  5388. {
  5389. var dt = (max - min) / (vertices.length - 1);
  5390. var t0 = min;
  5391. for (var i = 1; i < vertices.length - 1; i++)
  5392. {
  5393. var pstate = this.view.getState(this.model.getParent(vertices[i].cell));
  5394. var geo = this.getCellGeometry(vertices[i].cell);
  5395. t0 += dt;
  5396. if (geo != null && pstate != null)
  5397. {
  5398. geo = geo.clone();
  5399. if (horizontal)
  5400. {
  5401. geo.x = Math.round(t0 - geo.width / 2) - pstate.origin.x;
  5402. }
  5403. else
  5404. {
  5405. geo.y = Math.round(t0 - geo.height / 2) - pstate.origin.y;
  5406. }
  5407. this.getModel().setGeometry(vertices[i].cell, geo);
  5408. }
  5409. }
  5410. }
  5411. finally
  5412. {
  5413. this.getModel().endUpdate();
  5414. }
  5415. }
  5416. }
  5417. return cells;
  5418. };
  5419. /**
  5420. * Adds meta-drag an Mac.
  5421. * @param evt
  5422. * @returns
  5423. */
  5424. Graph.prototype.isCloneEvent = function(evt)
  5425. {
  5426. return (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || mxEvent.isControlDown(evt);
  5427. };
  5428. /**
  5429. * Translates this point by the given vector.
  5430. *
  5431. * @param {number} dx X-coordinate of the translation.
  5432. * @param {number} dy Y-coordinate of the translation.
  5433. */
  5434. Graph.prototype.encodeCells = function(cells)
  5435. {
  5436. var clones = this.cloneCells(cells);
  5437. // Creates a dictionary for fast lookups
  5438. var dict = new mxDictionary();
  5439. for (var i = 0; i < cells.length; i++)
  5440. {
  5441. dict.put(cells[i], true);
  5442. }
  5443. // Checks for orphaned relative children and makes absolute
  5444. for (var i = 0; i < clones.length; i++)
  5445. {
  5446. var state = this.view.getState(cells[i]);
  5447. if (state != null)
  5448. {
  5449. var geo = this.getCellGeometry(clones[i]);
  5450. if (geo != null && geo.relative && !this.model.isEdge(cells[i]) &&
  5451. !dict.get(this.model.getParent(cells[i])))
  5452. {
  5453. geo.relative = false;
  5454. geo.x = state.x / state.view.scale - state.view.translate.x;
  5455. geo.y = state.y / state.view.scale - state.view.translate.y;
  5456. }
  5457. }
  5458. }
  5459. var codec = new mxCodec();
  5460. var model = new mxGraphModel();
  5461. var parent = model.getChildAt(model.getRoot(), 0);
  5462. for (var i = 0; i < cells.length; i++)
  5463. {
  5464. model.add(parent, clones[i]);
  5465. }
  5466. return codec.encode(model);
  5467. };
  5468. /**
  5469. * Translates this point by the given vector.
  5470. *
  5471. * @param {number} dx X-coordinate of the translation.
  5472. * @param {number} dy Y-coordinate of the translation.
  5473. */
  5474. Graph.prototype.createSvgImageExport = function()
  5475. {
  5476. var exp = new mxImageExport();
  5477. // Adds hyperlinks (experimental)
  5478. exp.getLinkForCellState = mxUtils.bind(this, function(state, canvas)
  5479. {
  5480. return this.getLinkForCell(state.cell);
  5481. });
  5482. return exp;
  5483. };
  5484. /**
  5485. * Translates this point by the given vector.
  5486. *
  5487. * @param {number} dx X-coordinate of the translation.
  5488. * @param {number} dy Y-coordinate of the translation.
  5489. */
  5490. Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp,
  5491. ignoreSelection, showText, imgExport, linkTarget, hasShadow)
  5492. {
  5493. //Disable Css Transforms if it is used
  5494. var origUseCssTrans = this.useCssTransforms;
  5495. if (origUseCssTrans)
  5496. {
  5497. this.useCssTransforms = false;
  5498. this.view.revalidate();
  5499. this.sizeDidChange();
  5500. }
  5501. try
  5502. {
  5503. scale = (scale != null) ? scale : 1;
  5504. border = (border != null) ? border : 0;
  5505. crisp = (crisp != null) ? crisp : true;
  5506. ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
  5507. showText = (showText != null) ? showText : true;
  5508. var bounds = (ignoreSelection || nocrop) ?
  5509. this.getGraphBounds() : this.getBoundingBox(this.getSelectionCells());
  5510. if (bounds == null)
  5511. {
  5512. throw Error(mxResources.get('drawingEmpty'));
  5513. }
  5514. var vs = this.view.scale;
  5515. // Prepares SVG document that holds the output
  5516. var svgDoc = mxUtils.createXmlDocument();
  5517. var root = (svgDoc.createElementNS != null) ?
  5518. svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
  5519. if (background != null)
  5520. {
  5521. if (root.style != null)
  5522. {
  5523. root.style.backgroundColor = background;
  5524. }
  5525. else
  5526. {
  5527. root.setAttribute('style', 'background-color:' + background);
  5528. }
  5529. }
  5530. if (svgDoc.createElementNS == null)
  5531. {
  5532. root.setAttribute('xmlns', mxConstants.NS_SVG);
  5533. root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
  5534. }
  5535. else
  5536. {
  5537. // KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround.
  5538. root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
  5539. }
  5540. var s = scale / vs;
  5541. var w = Math.max(1, Math.ceil(bounds.width * s) + 2 * border) + ((hasShadow) ? 5 : 0);
  5542. var h = Math.max(1, Math.ceil(bounds.height * s) + 2 * border) + ((hasShadow) ? 5 : 0);
  5543. root.setAttribute('version', '1.1');
  5544. root.setAttribute('width', w + 'px');
  5545. root.setAttribute('height', h + 'px');
  5546. root.setAttribute('viewBox', ((crisp) ? '-0.5 -0.5' : '0 0') + ' ' + w + ' ' + h);
  5547. svgDoc.appendChild(root);
  5548. // Renders graph. Offset will be multiplied with state's scale when painting state.
  5549. // TextOffset only seems to affect FF output but used everywhere for consistency.
  5550. var group = (svgDoc.createElementNS != null) ?
  5551. svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g');
  5552. root.appendChild(group);
  5553. var svgCanvas = this.createSvgCanvas(group);
  5554. svgCanvas.foOffset = (crisp) ? -0.5 : 0;
  5555. svgCanvas.textOffset = (crisp) ? -0.5 : 0;
  5556. svgCanvas.imageOffset = (crisp) ? -0.5 : 0;
  5557. svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs),
  5558. Math.floor((border / scale - bounds.y) / vs));
  5559. // Convert HTML entities
  5560. var htmlConverter = document.createElement('textarea');
  5561. // Adds simple text fallback for viewers with no support for foreignObjects
  5562. var createAlternateContent = svgCanvas.createAlternateContent;
  5563. svgCanvas.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation)
  5564. {
  5565. var s = this.state;
  5566. // Assumes a max character width of 0.2em
  5567. if (this.foAltText != null && (w == 0 || (s.fontSize != 0 && str.length < (w * 5) / s.fontSize)))
  5568. {
  5569. var alt = this.createElement('text');
  5570. alt.setAttribute('x', Math.round(w / 2));
  5571. alt.setAttribute('y', Math.round((h + s.fontSize) / 2));
  5572. alt.setAttribute('fill', s.fontColor || 'black');
  5573. alt.setAttribute('text-anchor', 'middle');
  5574. alt.setAttribute('font-size', Math.round(s.fontSize) + 'px');
  5575. alt.setAttribute('font-family', s.fontFamily);
  5576. if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
  5577. {
  5578. alt.setAttribute('font-weight', 'bold');
  5579. }
  5580. if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
  5581. {
  5582. alt.setAttribute('font-style', 'italic');
  5583. }
  5584. if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
  5585. {
  5586. alt.setAttribute('text-decoration', 'underline');
  5587. }
  5588. try
  5589. {
  5590. htmlConverter.innerHTML = str;
  5591. alt.textContent = htmlConverter.value;
  5592. return alt;
  5593. }
  5594. catch (e)
  5595. {
  5596. return createAlternateContent.apply(this, arguments);
  5597. }
  5598. }
  5599. else
  5600. {
  5601. return createAlternateContent.apply(this, arguments);
  5602. }
  5603. };
  5604. // Paints background image
  5605. var bgImg = this.backgroundImage;
  5606. if (bgImg != null)
  5607. {
  5608. var s2 = vs / scale;
  5609. var tr = this.view.translate;
  5610. var tmp = new mxRectangle(tr.x * s2, tr.y * s2, bgImg.width * s2, bgImg.height * s2);
  5611. // Checks if visible
  5612. if (mxUtils.intersects(bounds, tmp))
  5613. {
  5614. svgCanvas.image(tr.x, tr.y, bgImg.width, bgImg.height, bgImg.src, true);
  5615. }
  5616. }
  5617. svgCanvas.scale(s);
  5618. svgCanvas.textEnabled = showText;
  5619. imgExport = (imgExport != null) ? imgExport : this.createSvgImageExport();
  5620. var imgExportDrawCellState = imgExport.drawCellState;
  5621. // Implements ignoreSelection flag
  5622. imgExport.drawCellState = function(state, canvas)
  5623. {
  5624. var graph = state.view.graph;
  5625. var selected = graph.isCellSelected(state.cell);
  5626. var parent = graph.model.getParent(state.cell);
  5627. // Checks if parent cell is selected
  5628. while (!ignoreSelection && !selected && parent != null)
  5629. {
  5630. selected = graph.isCellSelected(parent);
  5631. parent = graph.model.getParent(parent);
  5632. }
  5633. if (ignoreSelection || selected)
  5634. {
  5635. imgExportDrawCellState.apply(this, arguments);
  5636. }
  5637. };
  5638. imgExport.drawState(this.getView().getState(this.model.root), svgCanvas);
  5639. this.updateSvgLinks(root, linkTarget, true);
  5640. return root;
  5641. }
  5642. finally
  5643. {
  5644. if (origUseCssTrans)
  5645. {
  5646. this.useCssTransforms = true;
  5647. this.view.revalidate();
  5648. this.sizeDidChange();
  5649. }
  5650. }
  5651. };
  5652. /**
  5653. * Hook for creating the canvas used in getSvg.
  5654. */
  5655. Graph.prototype.updateSvgLinks = function(node, target, removeCustom)
  5656. {
  5657. var links = node.getElementsByTagName('a');
  5658. for (var i = 0; i < links.length; i++)
  5659. {
  5660. var href = links[i].getAttribute('href');
  5661. if (href == null)
  5662. {
  5663. href = links[i].getAttribute('xlink:href');
  5664. }
  5665. if (href != null)
  5666. {
  5667. if (target != null && /^https?:\/\//.test(href))
  5668. {
  5669. links[i].setAttribute('target', target);
  5670. }
  5671. else if (removeCustom && this.isCustomLink(href))
  5672. {
  5673. links[i].setAttribute('href', 'javascript:void(0);');
  5674. }
  5675. }
  5676. }
  5677. };
  5678. /**
  5679. * Hook for creating the canvas used in getSvg.
  5680. */
  5681. Graph.prototype.createSvgCanvas = function(node)
  5682. {
  5683. return new mxSvgCanvas2D(node);
  5684. };
  5685. /**
  5686. * Returns the first ancestor of the current selection with the given name.
  5687. */
  5688. Graph.prototype.getSelectedElement = function()
  5689. {
  5690. var node = null;
  5691. if (window.getSelection)
  5692. {
  5693. var sel = window.getSelection();
  5694. if (sel.getRangeAt && sel.rangeCount)
  5695. {
  5696. var range = sel.getRangeAt(0);
  5697. node = range.commonAncestorContainer;
  5698. }
  5699. }
  5700. else if (document.selection)
  5701. {
  5702. node = document.selection.createRange().parentElement();
  5703. }
  5704. return node;
  5705. };
  5706. /**
  5707. * Returns the first ancestor of the current selection with the given name.
  5708. */
  5709. Graph.prototype.getParentByName = function(node, name, stopAt)
  5710. {
  5711. while (node != null)
  5712. {
  5713. if (node.nodeName == name)
  5714. {
  5715. return node;
  5716. }
  5717. if (node == stopAt)
  5718. {
  5719. return null;
  5720. }
  5721. node = node.parentNode;
  5722. }
  5723. return node;
  5724. };
  5725. /**
  5726. * Selects the given node.
  5727. */
  5728. Graph.prototype.selectNode = function(node)
  5729. {
  5730. var sel = null;
  5731. // IE9 and non-IE
  5732. if (window.getSelection)
  5733. {
  5734. sel = window.getSelection();
  5735. if (sel.getRangeAt && sel.rangeCount)
  5736. {
  5737. var range = document.createRange();
  5738. range.selectNode(node);
  5739. sel.removeAllRanges();
  5740. sel.addRange(range);
  5741. }
  5742. }
  5743. // IE < 9
  5744. else if ((sel = document.selection) && sel.type != 'Control')
  5745. {
  5746. var originalRange = sel.createRange();
  5747. originalRange.collapse(true);
  5748. var range = sel.createRange();
  5749. range.setEndPoint('StartToStart', originalRange);
  5750. range.select();
  5751. }
  5752. };
  5753. /**
  5754. * Inserts a new row into the given table.
  5755. */
  5756. Graph.prototype.insertRow = function(table, index)
  5757. {
  5758. var bd = table.tBodies[0];
  5759. var cells = bd.rows[0].cells;
  5760. var cols = 0;
  5761. // Counts columns including colspans
  5762. for (var i = 0; i < cells.length; i++)
  5763. {
  5764. var colspan = cells[i].getAttribute('colspan');
  5765. cols += (colspan != null) ? parseInt(colspan) : 1;
  5766. }
  5767. var row = bd.insertRow(index);
  5768. for (var i = 0; i < cols; i++)
  5769. {
  5770. mxUtils.br(row.insertCell(-1));
  5771. }
  5772. return row.cells[0];
  5773. };
  5774. /**
  5775. * Deletes the given column.
  5776. */
  5777. Graph.prototype.deleteRow = function(table, index)
  5778. {
  5779. table.tBodies[0].deleteRow(index);
  5780. };
  5781. /**
  5782. * Deletes the given column.
  5783. */
  5784. Graph.prototype.insertColumn = function(table, index)
  5785. {
  5786. var hd = table.tHead;
  5787. if (hd != null)
  5788. {
  5789. // TODO: use colIndex
  5790. for (var h = 0; h < hd.rows.length; h++)
  5791. {
  5792. var th = document.createElement('th');
  5793. hd.rows[h].appendChild(th);
  5794. mxUtils.br(th);
  5795. }
  5796. }
  5797. var bd = table.tBodies[0];
  5798. for (var i = 0; i < bd.rows.length; i++)
  5799. {
  5800. var cell = bd.rows[i].insertCell(index);
  5801. mxUtils.br(cell);
  5802. }
  5803. return bd.rows[0].cells[(index >= 0) ? index : bd.rows[0].cells.length - 1];
  5804. };
  5805. /**
  5806. * Deletes the given column.
  5807. */
  5808. Graph.prototype.deleteColumn = function(table, index)
  5809. {
  5810. if (index >= 0)
  5811. {
  5812. var bd = table.tBodies[0];
  5813. var rows = bd.rows;
  5814. for (var i = 0; i < rows.length; i++)
  5815. {
  5816. if (rows[i].cells.length > index)
  5817. {
  5818. rows[i].deleteCell(index);
  5819. }
  5820. }
  5821. }
  5822. };
  5823. /**
  5824. * Inserts the given HTML at the caret position (no undo).
  5825. */
  5826. Graph.prototype.pasteHtmlAtCaret = function(html)
  5827. {
  5828. var sel, range;
  5829. // IE9 and non-IE
  5830. if (window.getSelection)
  5831. {
  5832. sel = window.getSelection();
  5833. if (sel.getRangeAt && sel.rangeCount)
  5834. {
  5835. range = sel.getRangeAt(0);
  5836. range.deleteContents();
  5837. // Range.createContextualFragment() would be useful here but is
  5838. // only relatively recently standardized and is not supported in
  5839. // some browsers (IE9, for one)
  5840. var el = document.createElement("div");
  5841. el.innerHTML = html;
  5842. var frag = document.createDocumentFragment(), node;
  5843. while ((node = el.firstChild))
  5844. {
  5845. lastNode = frag.appendChild(node);
  5846. }
  5847. range.insertNode(frag);
  5848. }
  5849. }
  5850. // IE < 9
  5851. else if ((sel = document.selection) && sel.type != "Control")
  5852. {
  5853. // FIXME: Does not work if selection is empty
  5854. sel.createRange().pasteHTML(html);
  5855. }
  5856. };
  5857. /**
  5858. * Creates an anchor elements for handling the given link in the
  5859. * hint that is shown when the cell is selected.
  5860. */
  5861. Graph.prototype.createLinkForHint = function(link, label)
  5862. {
  5863. link = (link != null) ? link : 'javascript:void(0);';
  5864. if (label == null || label.length == 0)
  5865. {
  5866. if (this.isCustomLink(link))
  5867. {
  5868. label = this.getLinkTitle(link);
  5869. }
  5870. else
  5871. {
  5872. label = link;
  5873. }
  5874. }
  5875. // Helper function to shorten strings
  5876. function short(str, max)
  5877. {
  5878. if (str.length > max)
  5879. {
  5880. str = str.substring(0, Math.round(max / 2)) + '...' +
  5881. str.substring(str.length - Math.round(max / 4));
  5882. }
  5883. return str;
  5884. };
  5885. var a = document.createElement('a');
  5886. a.setAttribute('rel', this.linkRelation);
  5887. a.setAttribute('href', this.getAbsoluteUrl(link));
  5888. a.setAttribute('title', short((this.isCustomLink(link)) ?
  5889. this.getLinkTitle(link) : link, 80));
  5890. if (this.linkTarget != null)
  5891. {
  5892. a.setAttribute('target', this.linkTarget);
  5893. }
  5894. // Adds shortened label to link
  5895. mxUtils.write(a, short(label, 40));
  5896. // Handles custom links
  5897. if (this.isCustomLink(link))
  5898. {
  5899. mxEvent.addListener(a, 'click', mxUtils.bind(this, function(evt)
  5900. {
  5901. this.customLinkClicked(link);
  5902. mxEvent.consume(evt);
  5903. }));
  5904. }
  5905. return a;
  5906. };
  5907. /**
  5908. * Customized graph for touch devices.
  5909. */
  5910. Graph.prototype.initTouch = function()
  5911. {
  5912. // Disables new connections via "hotspot"
  5913. this.connectionHandler.marker.isEnabled = function()
  5914. {
  5915. return this.graph.connectionHandler.first != null;
  5916. };
  5917. // Hides menu when editing starts
  5918. this.addListener(mxEvent.START_EDITING, function(sender, evt)
  5919. {
  5920. this.popupMenuHandler.hideMenu();
  5921. });
  5922. // Adds custom hit detection if native hit detection found no cell
  5923. var graphUpdateMouseEvent = this.updateMouseEvent;
  5924. this.updateMouseEvent = function(me)
  5925. {
  5926. me = graphUpdateMouseEvent.apply(this, arguments);
  5927. if (mxEvent.isTouchEvent(me.getEvent()) && me.getState() == null)
  5928. {
  5929. var cell = this.getCellAt(me.graphX, me.graphY);
  5930. if (cell != null && this.isSwimlane(cell) && this.hitsSwimlaneContent(cell, me.graphX, me.graphY))
  5931. {
  5932. cell = null;
  5933. }
  5934. else
  5935. {
  5936. me.state = this.view.getState(cell);
  5937. if (me.state != null && me.state.shape != null)
  5938. {
  5939. this.container.style.cursor = me.state.shape.node.style.cursor;
  5940. }
  5941. }
  5942. }
  5943. if (me.getState() == null && this.isEnabled())
  5944. {
  5945. this.container.style.cursor = 'default';
  5946. }
  5947. return me;
  5948. };
  5949. // Context menu trigger implementation depending on current selection state
  5950. // combined with support for normal popup trigger.
  5951. var cellSelected = false;
  5952. var selectionEmpty = false;
  5953. var menuShowing = false;
  5954. var oldFireMouseEvent = this.fireMouseEvent;
  5955. this.fireMouseEvent = function(evtName, me, sender)
  5956. {
  5957. if (evtName == mxEvent.MOUSE_DOWN)
  5958. {
  5959. // For hit detection on edges
  5960. me = this.updateMouseEvent(me);
  5961. cellSelected = this.isCellSelected(me.getCell());
  5962. selectionEmpty = this.isSelectionEmpty();
  5963. menuShowing = this.popupMenuHandler.isMenuShowing();
  5964. }
  5965. oldFireMouseEvent.apply(this, arguments);
  5966. };
  5967. // Shows popup menu if cell was selected or selection was empty and background was clicked
  5968. // FIXME: Conflicts with mxPopupMenuHandler.prototype.getCellForPopupEvent in Editor.js by
  5969. // selecting parent for selected children in groups before this check can be made.
  5970. this.popupMenuHandler.mouseUp = mxUtils.bind(this, function(sender, me)
  5971. {
  5972. this.popupMenuHandler.popupTrigger = !this.isEditing() && this.isEnabled() &&
  5973. (me.getState() == null || !me.isSource(me.getState().control)) &&
  5974. (this.popupMenuHandler.popupTrigger || (!menuShowing && !mxEvent.isMouseEvent(me.getEvent()) &&
  5975. ((selectionEmpty && me.getCell() == null && this.isSelectionEmpty()) ||
  5976. (cellSelected && this.isCellSelected(me.getCell())))));
  5977. mxPopupMenuHandler.prototype.mouseUp.apply(this.popupMenuHandler, arguments);
  5978. });
  5979. };
  5980. /**
  5981. * HTML in-place editor
  5982. */
  5983. mxCellEditor.prototype.isContentEditing = function()
  5984. {
  5985. var state = this.graph.view.getState(this.editingCell);
  5986. return state != null && state.style['html'] == 1;
  5987. };
  5988. /**
  5989. * Creates the keyboard event handler for the current graph and history.
  5990. */
  5991. mxCellEditor.prototype.saveSelection = function()
  5992. {
  5993. if (window.getSelection)
  5994. {
  5995. var sel = window.getSelection();
  5996. if (sel.getRangeAt && sel.rangeCount)
  5997. {
  5998. var ranges = [];
  5999. for (var i = 0, len = sel.rangeCount; i < len; ++i)
  6000. {
  6001. ranges.push(sel.getRangeAt(i));
  6002. }
  6003. return ranges;
  6004. }
  6005. }
  6006. else if (document.selection && document.selection.createRange)
  6007. {
  6008. return document.selection.createRange();
  6009. }
  6010. return null;
  6011. };
  6012. /**
  6013. * Creates the keyboard event handler for the current graph and history.
  6014. */
  6015. mxCellEditor.prototype.restoreSelection = function(savedSel)
  6016. {
  6017. try
  6018. {
  6019. if (savedSel)
  6020. {
  6021. if (window.getSelection)
  6022. {
  6023. sel = window.getSelection();
  6024. sel.removeAllRanges();
  6025. for (var i = 0, len = savedSel.length; i < len; ++i)
  6026. {
  6027. sel.addRange(savedSel[i]);
  6028. }
  6029. }
  6030. else if (document.selection && savedSel.select)
  6031. {
  6032. savedSel.select();
  6033. }
  6034. }
  6035. }
  6036. catch (e)
  6037. {
  6038. // ignore
  6039. }
  6040. };
  6041. /**
  6042. * Handling of special nl2Br style for not converting newlines to breaks in HTML labels.
  6043. * NOTE: Since it's easier to set this when the label is created we assume that it does
  6044. * not change during the lifetime of the mxText instance.
  6045. */
  6046. var mxCellRendererInitializeLabel = mxCellRenderer.prototype.initializeLabel;
  6047. mxCellRenderer.prototype.initializeLabel = function(state)
  6048. {
  6049. if (state.text != null)
  6050. {
  6051. state.text.replaceLinefeeds = mxUtils.getValue(state.style, 'nl2Br', '1') != '0';
  6052. }
  6053. mxCellRendererInitializeLabel.apply(this, arguments);
  6054. };
  6055. var mxConstraintHandlerUpdate = mxConstraintHandler.prototype.update;
  6056. mxConstraintHandler.prototype.update = function(me, source)
  6057. {
  6058. if (this.isKeepFocusEvent(me) || !mxEvent.isAltDown(me.getEvent()))
  6059. {
  6060. mxConstraintHandlerUpdate.apply(this, arguments);
  6061. }
  6062. else
  6063. {
  6064. this.reset();
  6065. }
  6066. };
  6067. /**
  6068. * No dashed shapes.
  6069. */
  6070. mxGuide.prototype.createGuideShape = function(horizontal)
  6071. {
  6072. var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH);
  6073. return guide;
  6074. };
  6075. /**
  6076. * HTML in-place editor
  6077. */
  6078. mxCellEditor.prototype.escapeCancelsEditing = false;
  6079. var mxCellEditorStartEditing = mxCellEditor.prototype.startEditing;
  6080. mxCellEditor.prototype.startEditing = function(cell, trigger)
  6081. {
  6082. mxCellEditorStartEditing.apply(this, arguments);
  6083. // Overrides class in case of HTML content to add
  6084. // dashed borders for divs and table cells
  6085. var state = this.graph.view.getState(cell);
  6086. if (state != null && state.style['html'] == 1)
  6087. {
  6088. this.textarea.className = 'mxCellEditor geContentEditable';
  6089. }
  6090. else
  6091. {
  6092. this.textarea.className = 'mxCellEditor mxPlainTextEditor';
  6093. }
  6094. // Toggles markup vs wysiwyg mode
  6095. this.codeViewMode = false;
  6096. // Stores current selection range when switching between markup and code
  6097. this.switchSelectionState = null;
  6098. // Selects editing cell
  6099. this.graph.setSelectionCell(cell);
  6100. // Enables focus outline for edges and edge labels
  6101. var parent = this.graph.getModel().getParent(cell);
  6102. var geo = this.graph.getCellGeometry(cell);
  6103. if ((this.graph.getModel().isEdge(parent) && geo != null && geo.relative) ||
  6104. this.graph.getModel().isEdge(cell))
  6105. {
  6106. // Quirks does not support outline at all so use border instead
  6107. if (mxClient.IS_QUIRKS)
  6108. {
  6109. this.textarea.style.border = 'gray dotted 1px';
  6110. }
  6111. // IE>8 and FF on Windows uses outline default of none
  6112. else if (mxClient.IS_IE || mxClient.IS_IE11 || (mxClient.IS_FF && mxClient.IS_WIN))
  6113. {
  6114. this.textarea.style.outline = 'gray dotted 1px';
  6115. }
  6116. else
  6117. {
  6118. this.textarea.style.outline = '';
  6119. }
  6120. }
  6121. else if (mxClient.IS_QUIRKS)
  6122. {
  6123. this.textarea.style.outline = 'none';
  6124. this.textarea.style.border = '';
  6125. }
  6126. }
  6127. /**
  6128. * HTML in-place editor
  6129. */
  6130. var cellEditorInstallListeners = mxCellEditor.prototype.installListeners;
  6131. mxCellEditor.prototype.installListeners = function(elt)
  6132. {
  6133. cellEditorInstallListeners.apply(this, arguments);
  6134. // Adds a reference from the clone to the original node, recursively
  6135. function reference(node, clone)
  6136. {
  6137. clone.originalNode = node;
  6138. node = node.firstChild;
  6139. var child = clone.firstChild;
  6140. while (node != null && child != null)
  6141. {
  6142. reference(node, child);
  6143. node = node.nextSibling;
  6144. child = child.nextSibling;
  6145. }
  6146. return clone;
  6147. };
  6148. // Checks the given node for new nodes, recursively
  6149. function checkNode(node, clone)
  6150. {
  6151. if (node != null)
  6152. {
  6153. if (clone.originalNode != node)
  6154. {
  6155. cleanNode(node);
  6156. }
  6157. else
  6158. {
  6159. node = node.firstChild;
  6160. clone = clone.firstChild;
  6161. while (node != null)
  6162. {
  6163. var nextNode = node.nextSibling;
  6164. if (clone == null)
  6165. {
  6166. cleanNode(node);
  6167. }
  6168. else
  6169. {
  6170. checkNode(node, clone);
  6171. clone = clone.nextSibling;
  6172. }
  6173. node = nextNode;
  6174. }
  6175. }
  6176. }
  6177. };
  6178. // Removes unused DOM nodes and attributes, recursively
  6179. function cleanNode(node)
  6180. {
  6181. var child = node.firstChild;
  6182. while (child != null)
  6183. {
  6184. var next = child.nextSibling;
  6185. cleanNode(child);
  6186. child = next;
  6187. }
  6188. if ((node.nodeType != 1 || (node.nodeName !== 'BR' && node.firstChild == null)) &&
  6189. (node.nodeType != 3 || mxUtils.trim(mxUtils.getTextContent(node)).length == 0))
  6190. {
  6191. node.parentNode.removeChild(node);
  6192. }
  6193. else
  6194. {
  6195. // Removes linefeeds
  6196. if (node.nodeType == 3)
  6197. {
  6198. mxUtils.setTextContent(node, mxUtils.getTextContent(node).replace(/\n|\r/g, ''));
  6199. }
  6200. // Removes CSS classes and styles (for Word and Excel)
  6201. if (node.nodeType == 1)
  6202. {
  6203. node.removeAttribute('style');
  6204. node.removeAttribute('class');
  6205. node.removeAttribute('width');
  6206. node.removeAttribute('cellpadding');
  6207. node.removeAttribute('cellspacing');
  6208. node.removeAttribute('border');
  6209. }
  6210. }
  6211. };
  6212. // Handles paste from Word, Excel etc by removing styles, classnames and unused nodes
  6213. // LATER: Fix undo/redo for paste
  6214. if (!mxClient.IS_QUIRKS && document.documentMode !== 7 && document.documentMode !== 8)
  6215. {
  6216. mxEvent.addListener(this.textarea, 'paste', mxUtils.bind(this, function(evt)
  6217. {
  6218. var clone = reference(this.textarea, this.textarea.cloneNode(true));
  6219. window.setTimeout(mxUtils.bind(this, function()
  6220. {
  6221. checkNode(this.textarea, clone);
  6222. }), 0);
  6223. }));
  6224. }
  6225. };
  6226. mxCellEditor.prototype.toggleViewMode = function()
  6227. {
  6228. var state = this.graph.view.getState(this.editingCell);
  6229. if (state != null)
  6230. {
  6231. var nl2Br = state != null && mxUtils.getValue(state.style, 'nl2Br', '1') != '0';
  6232. var tmp = this.saveSelection();
  6233. if (!this.codeViewMode)
  6234. {
  6235. // Clears the initial empty label on the first keystroke
  6236. if (this.clearOnChange && this.textarea.innerHTML == this.getEmptyLabelText())
  6237. {
  6238. this.clearOnChange = false;
  6239. this.textarea.innerHTML = '';
  6240. }
  6241. // Removes newlines from HTML and converts breaks to newlines
  6242. // to match the HTML output in plain text
  6243. var content = mxUtils.htmlEntities(this.textarea.innerHTML);
  6244. // Workaround for trailing line breaks being ignored in the editor
  6245. if (!mxClient.IS_QUIRKS && document.documentMode != 8)
  6246. {
  6247. content = mxUtils.replaceTrailingNewlines(content, '<div><br></div>');
  6248. }
  6249. content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '').replace(/&lt;br\s*.?&gt;/g, '<br>') : content, true);
  6250. this.textarea.className = 'mxCellEditor mxPlainTextEditor';
  6251. var size = mxConstants.DEFAULT_FONTSIZE;
  6252. this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
  6253. this.textarea.style.fontSize = Math.round(size) + 'px';
  6254. this.textarea.style.textDecoration = '';
  6255. this.textarea.style.fontWeight = 'normal';
  6256. this.textarea.style.fontStyle = '';
  6257. this.textarea.style.fontFamily = mxConstants.DEFAULT_FONTFAMILY;
  6258. this.textarea.style.textAlign = 'left';
  6259. // Adds padding to make cursor visible with borders
  6260. this.textarea.style.padding = '2px';
  6261. if (this.textarea.innerHTML != content)
  6262. {
  6263. this.textarea.innerHTML = content;
  6264. }
  6265. this.codeViewMode = true;
  6266. }
  6267. else
  6268. {
  6269. var content = mxUtils.extractTextWithWhitespace(this.textarea.childNodes);
  6270. // Strips trailing line break
  6271. if (content.length > 0 && content.charAt(content.length - 1) == '\n')
  6272. {
  6273. content = content.substring(0, content.length - 1);
  6274. }
  6275. content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '<br/>') : content, true)
  6276. this.textarea.className = 'mxCellEditor geContentEditable';
  6277. var size = mxUtils.getValue(state.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
  6278. var family = mxUtils.getValue(state.style, mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY);
  6279. var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT);
  6280. var bold = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
  6281. mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD;
  6282. var italic = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
  6283. mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC;
  6284. var uline = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
  6285. mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE;
  6286. this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
  6287. this.textarea.style.fontSize = Math.round(size) + 'px';
  6288. this.textarea.style.textDecoration = (uline) ? 'underline' : '';
  6289. this.textarea.style.fontWeight = (bold) ? 'bold' : 'normal';
  6290. this.textarea.style.fontStyle = (italic) ? 'italic' : '';
  6291. this.textarea.style.fontFamily = family;
  6292. this.textarea.style.textAlign = align;
  6293. this.textarea.style.padding = '0px';
  6294. if (this.textarea.innerHTML != content)
  6295. {
  6296. this.textarea.innerHTML = content;
  6297. if (this.textarea.innerHTML.length == 0)
  6298. {
  6299. this.textarea.innerHTML = this.getEmptyLabelText();
  6300. this.clearOnChange = this.textarea.innerHTML.length > 0;
  6301. }
  6302. }
  6303. this.codeViewMode = false;
  6304. }
  6305. this.textarea.focus();
  6306. if (this.switchSelectionState != null)
  6307. {
  6308. this.restoreSelection(this.switchSelectionState);
  6309. }
  6310. this.switchSelectionState = tmp;
  6311. this.resize();
  6312. }
  6313. };
  6314. var mxCellEditorResize = mxCellEditor.prototype.resize;
  6315. mxCellEditor.prototype.resize = function(state, trigger)
  6316. {
  6317. if (this.textarea != null)
  6318. {
  6319. var state = this.graph.getView().getState(this.editingCell);
  6320. if (this.codeViewMode && state != null)
  6321. {
  6322. var scale = state.view.scale;
  6323. this.bounds = mxRectangle.fromRectangle(state);
  6324. // General placement of code editor if cell has no size
  6325. // LATER: Fix HTML editor bounds for edge labels
  6326. if (this.bounds.width == 0 && this.bounds.height == 0)
  6327. {
  6328. this.bounds.width = 160 * scale;
  6329. this.bounds.height = 60 * scale;
  6330. var m = (state.text != null) ? state.text.margin : null;
  6331. if (m == null)
  6332. {
  6333. m = mxUtils.getAlignmentAsPoint(mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER),
  6334. mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE));
  6335. }
  6336. this.bounds.x += m.x * this.bounds.width;
  6337. this.bounds.y += m.y * this.bounds.height;
  6338. }
  6339. this.textarea.style.width = Math.round((this.bounds.width - 4) / scale) + 'px';
  6340. this.textarea.style.height = Math.round((this.bounds.height - 4) / scale) + 'px';
  6341. this.textarea.style.overflow = 'auto';
  6342. // Adds scrollbar offset if visible
  6343. if (this.textarea.clientHeight < this.textarea.offsetHeight)
  6344. {
  6345. this.textarea.style.height = Math.round((this.bounds.height / scale)) + (this.textarea.offsetHeight - this.textarea.clientHeight) + 'px';
  6346. this.bounds.height = parseInt(this.textarea.style.height) * scale;
  6347. }
  6348. if (this.textarea.clientWidth < this.textarea.offsetWidth)
  6349. {
  6350. this.textarea.style.width = Math.round((this.bounds.width / scale)) + (this.textarea.offsetWidth - this.textarea.clientWidth) + 'px';
  6351. this.bounds.width = parseInt(this.textarea.style.width) * scale;
  6352. }
  6353. this.textarea.style.left = Math.round(this.bounds.x) + 'px';
  6354. this.textarea.style.top = Math.round(this.bounds.y) + 'px';
  6355. if (mxClient.IS_VML)
  6356. {
  6357. this.textarea.style.zoom = scale;
  6358. }
  6359. else
  6360. {
  6361. mxUtils.setPrefixedStyle(this.textarea.style, 'transform', 'scale(' + scale + ',' + scale + ')');
  6362. }
  6363. }
  6364. else
  6365. {
  6366. this.textarea.style.height = '';
  6367. this.textarea.style.overflow = '';
  6368. mxCellEditorResize.apply(this, arguments);
  6369. }
  6370. }
  6371. };
  6372. mxCellEditorGetInitialValue = mxCellEditor.prototype.getInitialValue;
  6373. mxCellEditor.prototype.getInitialValue = function(state, trigger)
  6374. {
  6375. if (mxUtils.getValue(state.style, 'html', '0') == '0')
  6376. {
  6377. return mxCellEditorGetInitialValue.apply(this, arguments);
  6378. }
  6379. else
  6380. {
  6381. var result = this.graph.getEditingValue(state.cell, trigger)
  6382. if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1')
  6383. {
  6384. result = result.replace(/\n/g, '<br/>');
  6385. }
  6386. result = this.graph.sanitizeHtml(result, true);
  6387. return result;
  6388. }
  6389. };
  6390. mxCellEditorGetCurrentValue = mxCellEditor.prototype.getCurrentValue;
  6391. mxCellEditor.prototype.getCurrentValue = function(state)
  6392. {
  6393. if (mxUtils.getValue(state.style, 'html', '0') == '0')
  6394. {
  6395. return mxCellEditorGetCurrentValue.apply(this, arguments);
  6396. }
  6397. else
  6398. {
  6399. var result = this.graph.sanitizeHtml(this.textarea.innerHTML, true);
  6400. if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1')
  6401. {
  6402. result = result.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>');
  6403. }
  6404. else
  6405. {
  6406. result = result.replace(/\r\n/g, '').replace(/\n/g, '');
  6407. }
  6408. return result;
  6409. }
  6410. };
  6411. var mxCellEditorStopEditing = mxCellEditor.prototype.stopEditing;
  6412. mxCellEditor.prototype.stopEditing = function(cancel)
  6413. {
  6414. // Restores default view mode before applying value
  6415. if (this.codeViewMode)
  6416. {
  6417. this.toggleViewMode();
  6418. }
  6419. mxCellEditorStopEditing.apply(this, arguments);
  6420. // Tries to move focus back to container after editing if possible
  6421. this.focusContainer();
  6422. };
  6423. mxCellEditor.prototype.focusContainer = function()
  6424. {
  6425. try
  6426. {
  6427. this.graph.container.focus();
  6428. }
  6429. catch (e)
  6430. {
  6431. // ignore
  6432. }
  6433. };
  6434. var mxCellEditorApplyValue = mxCellEditor.prototype.applyValue;
  6435. mxCellEditor.prototype.applyValue = function(state, value)
  6436. {
  6437. // Removes empty relative child labels in edges
  6438. this.graph.getModel().beginUpdate();
  6439. try
  6440. {
  6441. mxCellEditorApplyValue.apply(this, arguments);
  6442. if (this.graph.isCellDeletable(state.cell) && this.graph.model.getChildCount(state.cell) == 0)
  6443. {
  6444. var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
  6445. var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE);
  6446. if (value == '' && stroke == mxConstants.NONE && fill == mxConstants.NONE)
  6447. {
  6448. this.graph.removeCells([state.cell], false);
  6449. }
  6450. }
  6451. }
  6452. finally
  6453. {
  6454. this.graph.getModel().endUpdate();
  6455. }
  6456. };
  6457. /**
  6458. * Returns the background color to be used for the editing box. This returns
  6459. * the label background for edge labels and null for all other cases.
  6460. */
  6461. mxCellEditor.prototype.getBackgroundColor = function(state)
  6462. {
  6463. var color = null;
  6464. if (this.graph.getModel().isEdge(state.cell) || this.graph.getModel().isEdge(this.graph.getModel().getParent(state.cell)))
  6465. {
  6466. var color = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, null);
  6467. if (color == mxConstants.NONE)
  6468. {
  6469. color = null;
  6470. }
  6471. }
  6472. return color;
  6473. };
  6474. mxCellEditor.prototype.getMinimumSize = function(state)
  6475. {
  6476. var scale = this.graph.getView().scale;
  6477. return new mxRectangle(0, 0, (state.text == null) ? 30 : state.text.size * scale + 20, 30);
  6478. };
  6479. // Hold alt to ignore drop target
  6480. var mxGraphHandlerMoveCells = mxGraphHandler.prototype.moveCells;
  6481. mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
  6482. {
  6483. if (mxEvent.isAltDown(evt))
  6484. {
  6485. target = null;
  6486. }
  6487. mxGraphHandlerMoveCells.apply(this, arguments);
  6488. };
  6489. /**
  6490. * Hints on handlers
  6491. */
  6492. function createHint()
  6493. {
  6494. var hint = document.createElement('div');
  6495. hint.className = 'geHint';
  6496. hint.style.whiteSpace = 'nowrap';
  6497. hint.style.position = 'absolute';
  6498. return hint;
  6499. };
  6500. /**
  6501. * Updates the hint for the current operation.
  6502. */
  6503. mxGraphHandler.prototype.updateHint = function(me)
  6504. {
  6505. if (this.shape != null)
  6506. {
  6507. if (this.hint == null)
  6508. {
  6509. this.hint = createHint();
  6510. this.graph.container.appendChild(this.hint);
  6511. }
  6512. var t = this.graph.view.translate;
  6513. var s = this.graph.view.scale;
  6514. var x = this.roundLength((this.bounds.x + this.currentDx) / s - t.x);
  6515. var y = this.roundLength((this.bounds.y + this.currentDy) / s - t.y);
  6516. this.hint.innerHTML = x + ', ' + y;
  6517. this.hint.style.left = (this.shape.bounds.x + Math.round((this.shape.bounds.width - this.hint.clientWidth) / 2)) + 'px';
  6518. this.hint.style.top = (this.shape.bounds.y + this.shape.bounds.height + 12) + 'px';
  6519. }
  6520. };
  6521. /**
  6522. * Updates the hint for the current operation.
  6523. */
  6524. mxGraphHandler.prototype.removeHint = function()
  6525. {
  6526. if (this.hint != null)
  6527. {
  6528. this.hint.parentNode.removeChild(this.hint);
  6529. this.hint = null;
  6530. }
  6531. };
  6532. /**
  6533. * Enables recursive resize for groups.
  6534. */
  6535. mxVertexHandler.prototype.isRecursiveResize = function(state, me)
  6536. {
  6537. return !this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 &&
  6538. !mxEvent.isControlDown(me.getEvent()) && !this.graph.isCellCollapsed(state.cell) &&
  6539. mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' &&
  6540. mxUtils.getValue(state.style, 'childLayout', null) == null;
  6541. };
  6542. /**
  6543. * Enables centered resize events.
  6544. */
  6545. mxVertexHandler.prototype.isCenteredEvent = function(state, me)
  6546. {
  6547. return (!(!this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 &&
  6548. !this.graph.isCellCollapsed(state.cell) &&
  6549. mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' &&
  6550. mxUtils.getValue(state.style, 'childLayout', null) == null) &&
  6551. mxEvent.isControlDown(me.getEvent())) ||
  6552. mxEvent.isMetaDown(me.getEvent());
  6553. };
  6554. var vertexHandlerGetHandlePadding = mxVertexHandler.prototype.getHandlePadding;
  6555. mxVertexHandler.prototype.getHandlePadding = function()
  6556. {
  6557. var result = new mxPoint(0, 0);
  6558. var tol = this.tolerance;
  6559. if (this.graph.cellEditor.getEditingCell() == this.state.cell &&
  6560. this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null)
  6561. {
  6562. tol /= 2;
  6563. result.x = this.sizers[0].bounds.width + tol;
  6564. result.y = this.sizers[0].bounds.height + tol;
  6565. }
  6566. else
  6567. {
  6568. result = vertexHandlerGetHandlePadding.apply(this, arguments);
  6569. }
  6570. return result;
  6571. };
  6572. /**
  6573. * Updates the hint for the current operation.
  6574. */
  6575. mxVertexHandler.prototype.updateHint = function(me)
  6576. {
  6577. if (this.index != mxEvent.LABEL_HANDLE)
  6578. {
  6579. if (this.hint == null)
  6580. {
  6581. this.hint = createHint();
  6582. this.state.view.graph.container.appendChild(this.hint);
  6583. }
  6584. if (this.index == mxEvent.ROTATION_HANDLE)
  6585. {
  6586. this.hint.innerHTML = this.currentAlpha + '&deg;';
  6587. }
  6588. else
  6589. {
  6590. var s = this.state.view.scale;
  6591. this.hint.innerHTML = this.roundLength(this.bounds.width / s) + ' x ' + this.roundLength(this.bounds.height / s);
  6592. }
  6593. var rot = (this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0';
  6594. var bb = mxUtils.getBoundingBox(this.bounds, rot);
  6595. if (bb == null)
  6596. {
  6597. bb = this.bounds;
  6598. }
  6599. this.hint.style.left = bb.x + Math.round((bb.width - this.hint.clientWidth) / 2) + 'px';
  6600. this.hint.style.top = (bb.y + bb.height + 12) + 'px';
  6601. if (this.linkHint != null)
  6602. {
  6603. this.linkHint.style.display = 'none';
  6604. }
  6605. }
  6606. };
  6607. /**
  6608. * Updates the hint for the current operation.
  6609. */
  6610. mxVertexHandler.prototype.removeHint = function()
  6611. {
  6612. mxGraphHandler.prototype.removeHint.apply(this, arguments);
  6613. if (this.linkHint != null)
  6614. {
  6615. this.linkHint.style.display = '';
  6616. }
  6617. };
  6618. /**
  6619. * Updates the hint for the current operation.
  6620. */
  6621. mxEdgeHandler.prototype.updateHint = function(me, point)
  6622. {
  6623. if (this.hint == null)
  6624. {
  6625. this.hint = createHint();
  6626. this.state.view.graph.container.appendChild(this.hint);
  6627. }
  6628. var t = this.graph.view.translate;
  6629. var s = this.graph.view.scale;
  6630. var x = this.roundLength(point.x / s - t.x);
  6631. var y = this.roundLength(point.y / s - t.y);
  6632. this.hint.innerHTML = x + ', ' + y;
  6633. this.hint.style.visibility = 'visible';
  6634. if (this.isSource || this.isTarget)
  6635. {
  6636. if (this.constraintHandler.currentConstraint != null &&
  6637. this.constraintHandler.currentFocus != null)
  6638. {
  6639. var pt = this.constraintHandler.currentConstraint.point;
  6640. this.hint.innerHTML = '[' + Math.round(pt.x * 100) + '%, '+ Math.round(pt.y * 100) + '%]';
  6641. }
  6642. else if (this.marker.hasValidState())
  6643. {
  6644. this.hint.style.visibility = 'hidden';
  6645. }
  6646. }
  6647. this.hint.style.left = Math.round(me.getGraphX() - this.hint.clientWidth / 2) + 'px';
  6648. this.hint.style.top = (Math.max(me.getGraphY(), point.y) + this.state.view.graph.gridSize) + 'px';
  6649. if (this.linkHint != null)
  6650. {
  6651. this.linkHint.style.display = 'none';
  6652. }
  6653. };
  6654. /**
  6655. * Updates the hint for the current operation.
  6656. */
  6657. mxEdgeHandler.prototype.removeHint = mxVertexHandler.prototype.removeHint;
  6658. /**
  6659. * Defines the handles for the UI. Uses data-URIs to speed-up loading time where supported.
  6660. */
  6661. // TODO: Increase handle padding
  6662. HoverIcons.prototype.mainHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-main.png', 17, 17) :
  6663. Graph.createSvgImage(18, 18, '<circle cx="9" cy="9" r="5" stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '" stroke-width="1"/>');
  6664. HoverIcons.prototype.secondaryHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-secondary.png', 17, 17) :
  6665. Graph.createSvgImage(16, 16, '<path d="m 8 3 L 13 8 L 8 13 L 3 8 z" stroke="#fff" fill="#fca000"/>');
  6666. HoverIcons.prototype.fixedHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-fixed.png', 17, 17) :
  6667. Graph.createSvgImage(18, 18, '<circle cx="9" cy="9" r="5" stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '" stroke-width="1"/><path d="m 7 7 L 11 11 M 7 11 L 11 7" stroke="#fff"/>');
  6668. HoverIcons.prototype.terminalHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-terminal.png', 17, 17) :
  6669. Graph.createSvgImage(18, 18, '<circle cx="9" cy="9" r="5" stroke="#fff" fill="' + HoverIcons.prototype.arrowFill + '" stroke-width="1"/><circle cx="9" cy="9" r="2" stroke="#fff" fill="transparent"/>');
  6670. HoverIcons.prototype.rotationHandle = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAVCAYAAACkCdXRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA6ZJREFUeNqM001IY1cUB/D/fYmm2sbR2lC1zYlgoRG6MpEyBlpxM9iFIGKFIm3s0lCKjOByhCLZCFqLBF1YFVJdSRbdFHRhBbULtRuFVBTzYRpJgo2mY5OX5N9Fo2TG+eiFA/dd3vvd8+65ByTxshARTdf1JySp6/oTEdFe9T5eg5lIcnBwkCSZyWS+exX40oyur68/KxaLf5Okw+H4X+A9JBaLfUySZ2dnnJqaosPhIAACeC34DJRKpb7IZrMcHx+nwWCgUopGo/EOKwf9fn/1CzERUevr6+9ls1mOjIwQAH0+H4PBIKPR6D2ofAQCgToRUeVYJUkuLy8TANfW1kiS8/PzCy84Mw4MDBAAZ2dnmc/nub+/X0MSEBF1cHDwMJVKsaGhgV6vl+l0mqOjo1+KyKfl1dze3l4NBoM/PZ+diFSLiIKIGBOJxA9bW1sEwNXVVSaTyQMRaRaRxrOzs+9J8ujoaE5EPhQRq67rcZ/PRwD0+/3Udf03EdEgIqZisZibnJykwWDg4eEhd3Z2xkXELCJvPpdBrYjUiEhL+Xo4HH4sIhUaAKNSqiIcDsNkMqG+vh6RSOQQQM7tdhsAQCkFAHC73UUATxcWFqypVApmsxnDw8OwWq2TADQNgAYAFosF+XweyWQSdru9BUBxcXFRB/4rEgDcPouIIx6P4+bmBi0tLSCpAzBqAIqnp6c/dnZ2IpfLYXNzE62traMADACKNputpr+/v8lms9UAKAAwiMjXe3t7KBQKqKurQy6Xi6K0i2l6evpROp1mbW0t29vbGY/Hb8/IVIqq2zlJXl1dsaOjg2azmefn5wwEAl+JSBVExCgi75PkzMwMlVJsbGxkIpFgPp8PX15ePopEIs3JZPITXdf/iEajbGpqolKKExMT1HWdHo/nIxGpgIgoEXnQ3d39kCTHxsYIgC6Xi3NzcwyHw8xkMozFYlxaWmJbWxuVUuzt7WUul6PX6/1cRN4WEe2uA0SkaWVl5XGpRVhdXU0A1DSNlZWVdz3qdDrZ09PDWCzG4+Pjn0XEWvp9KJKw2WwKwBsA3gHQHAqFfr24uMDGxgZ2d3cRiUQAAHa7HU6nE319fTg5Ofmlq6vrGwB/AngaCoWK6rbsNptNA1AJoA7Aux6Pp3NoaMhjsVg+QNmIRqO/u1yubwFEASRKUAEA7rASqABUAKgC8KAUb5XWCOAfAFcA/gJwDSB7C93DylCtdM8qABhLc5TumV6KQigUeubjfwcAHkQJ94ndWeYAAAAASUVORK5CYII=' :
  6671. IMAGE_PATH + '/handle-rotate.png', 19, 21);
  6672. if (mxClient.IS_SVG)
  6673. {
  6674. mxConstraintHandler.prototype.pointImage = Graph.createSvgImage(5, 5, '<path d="m 0 0 L 5 5 M 0 5 L 5 0" stroke="' + HoverIcons.prototype.arrowFill + '"/>');
  6675. }
  6676. mxVertexHandler.prototype.handleImage = HoverIcons.prototype.mainHandle;
  6677. mxVertexHandler.prototype.secondaryHandleImage = HoverIcons.prototype.secondaryHandle;
  6678. mxEdgeHandler.prototype.handleImage = HoverIcons.prototype.mainHandle;
  6679. mxEdgeHandler.prototype.terminalHandleImage = HoverIcons.prototype.terminalHandle;
  6680. mxEdgeHandler.prototype.fixedHandleImage = HoverIcons.prototype.fixedHandle;
  6681. mxEdgeHandler.prototype.labelHandleImage = HoverIcons.prototype.secondaryHandle;
  6682. mxOutline.prototype.sizerImage = HoverIcons.prototype.mainHandle;
  6683. if (window.Sidebar != null)
  6684. {
  6685. Sidebar.prototype.triangleUp = HoverIcons.prototype.triangleUp;
  6686. Sidebar.prototype.triangleRight = HoverIcons.prototype.triangleRight;
  6687. Sidebar.prototype.triangleDown = HoverIcons.prototype.triangleDown;
  6688. Sidebar.prototype.triangleLeft = HoverIcons.prototype.triangleLeft;
  6689. Sidebar.prototype.refreshTarget = HoverIcons.prototype.refreshTarget;
  6690. Sidebar.prototype.roundDrop = HoverIcons.prototype.roundDrop;
  6691. }
  6692. // Pre-fetches images (only needed for non data-uris)
  6693. if (!mxClient.IS_SVG)
  6694. {
  6695. new Image().src = HoverIcons.prototype.mainHandle.src;
  6696. new Image().src = HoverIcons.prototype.fixedHandle.src;
  6697. new Image().src = HoverIcons.prototype.terminalHandle.src;
  6698. new Image().src = HoverIcons.prototype.secondaryHandle.src;
  6699. new Image().src = HoverIcons.prototype.rotationHandle.src;
  6700. new Image().src = HoverIcons.prototype.triangleUp.src;
  6701. new Image().src = HoverIcons.prototype.triangleRight.src;
  6702. new Image().src = HoverIcons.prototype.triangleDown.src;
  6703. new Image().src = HoverIcons.prototype.triangleLeft.src;
  6704. new Image().src = HoverIcons.prototype.refreshTarget.src;
  6705. new Image().src = HoverIcons.prototype.roundDrop.src;
  6706. }
  6707. // Adds rotation handle and live preview
  6708. mxVertexHandler.prototype.rotationEnabled = true;
  6709. mxVertexHandler.prototype.manageSizers = true;
  6710. mxVertexHandler.prototype.livePreview = true;
  6711. // Increases default rubberband opacity (default is 20)
  6712. mxRubberband.prototype.defaultOpacity = 30;
  6713. // Enables connections along the outline, virtual waypoints, parent highlight etc
  6714. mxConnectionHandler.prototype.outlineConnect = true;
  6715. mxCellHighlight.prototype.keepOnTop = true;
  6716. mxVertexHandler.prototype.parentHighlightEnabled = true;
  6717. mxVertexHandler.prototype.rotationHandleVSpacing = -20;
  6718. mxEdgeHandler.prototype.parentHighlightEnabled = true;
  6719. mxEdgeHandler.prototype.dblClickRemoveEnabled = true;
  6720. mxEdgeHandler.prototype.straightRemoveEnabled = true;
  6721. mxEdgeHandler.prototype.virtualBendsEnabled = true;
  6722. mxEdgeHandler.prototype.mergeRemoveEnabled = true;
  6723. mxEdgeHandler.prototype.manageLabelHandle = true;
  6724. mxEdgeHandler.prototype.outlineConnect = true;
  6725. // Disables adding waypoints if shift is pressed
  6726. mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me)
  6727. {
  6728. return !mxEvent.isShiftDown(me.getEvent());
  6729. };
  6730. // Disables custom handles if shift is pressed
  6731. mxEdgeHandler.prototype.isCustomHandleEvent = function(me)
  6732. {
  6733. return !mxEvent.isShiftDown(me.getEvent());
  6734. };
  6735. /**
  6736. * Implements touch style
  6737. */
  6738. if (Graph.touchStyle)
  6739. {
  6740. // Larger tolerance for real touch devices
  6741. if (mxClient.IS_TOUCH || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0)
  6742. {
  6743. mxShape.prototype.svgStrokeTolerance = 18;
  6744. mxVertexHandler.prototype.tolerance = 12;
  6745. mxEdgeHandler.prototype.tolerance = 12;
  6746. Graph.prototype.tolerance = 12;
  6747. mxVertexHandler.prototype.rotationHandleVSpacing = -24;
  6748. // Implements a smaller tolerance for mouse events and a larger tolerance for touch
  6749. // events on touch devices. The default tolerance (4px) is used for mouse events.
  6750. mxConstraintHandler.prototype.getTolerance = function(me)
  6751. {
  6752. return (mxEvent.isMouseEvent(me.getEvent())) ? 4 : this.graph.getTolerance();
  6753. };
  6754. }
  6755. // One finger pans (no rubberband selection) must start regardless of mouse button
  6756. mxPanningHandler.prototype.isPanningTrigger = function(me)
  6757. {
  6758. var evt = me.getEvent();
  6759. return (me.getState() == null && !mxEvent.isMouseEvent(evt)) ||
  6760. (mxEvent.isPopupTrigger(evt) && (me.getState() == null ||
  6761. mxEvent.isControlDown(evt) || mxEvent.isShiftDown(evt)));
  6762. };
  6763. // Don't clear selection if multiple cells selected
  6764. var graphHandlerMouseDown = mxGraphHandler.prototype.mouseDown;
  6765. mxGraphHandler.prototype.mouseDown = function(sender, me)
  6766. {
  6767. graphHandlerMouseDown.apply(this, arguments);
  6768. if (mxEvent.isTouchEvent(me.getEvent()) && this.graph.isCellSelected(me.getCell()) &&
  6769. this.graph.getSelectionCount() > 1)
  6770. {
  6771. this.delayedSelection = false;
  6772. }
  6773. };
  6774. }
  6775. else
  6776. {
  6777. // Removes ctrl+shift as panning trigger for space splitting
  6778. mxPanningHandler.prototype.isPanningTrigger = function(me)
  6779. {
  6780. var evt = me.getEvent();
  6781. return (mxEvent.isLeftMouseButton(evt) && ((this.useLeftButtonForPanning &&
  6782. me.getState() == null) || (mxEvent.isControlDown(evt) &&
  6783. !mxEvent.isShiftDown(evt)))) || (this.usePopupTrigger &&
  6784. mxEvent.isPopupTrigger(evt));
  6785. };
  6786. }
  6787. // Overrides/extends rubberband for space handling with Ctrl+Shift(+Alt) drag ("scissors tool")
  6788. mxRubberband.prototype.isSpaceEvent = function(me)
  6789. {
  6790. return this.graph.isEnabled() && !this.graph.isCellLocked(this.graph.getDefaultParent()) &&
  6791. mxEvent.isControlDown(me.getEvent()) && mxEvent.isShiftDown(me.getEvent());
  6792. };
  6793. // Handles moving of cells in both half panes
  6794. mxRubberband.prototype.mouseUp = function(sender, me)
  6795. {
  6796. var execute = this.div != null && this.div.style.display != 'none';
  6797. var x0 = null;
  6798. var y0 = null;
  6799. var dx = null;
  6800. var dy = null;
  6801. if (this.first != null && this.currentX != null && this.currentY != null)
  6802. {
  6803. x0 = this.first.x;
  6804. y0 = this.first.y;
  6805. dx = (this.currentX - x0) / this.graph.view.scale;
  6806. dy = (this.currentY - y0) / this.graph.view.scale;
  6807. if (!mxEvent.isAltDown(me.getEvent()))
  6808. {
  6809. dx = this.graph.snap(dx);
  6810. dy = this.graph.snap(dy);
  6811. if (!this.graph.isGridEnabled())
  6812. {
  6813. if (Math.abs(dx) < this.graph.tolerance)
  6814. {
  6815. dx = 0;
  6816. }
  6817. if (Math.abs(dy) < this.graph.tolerance)
  6818. {
  6819. dy = 0;
  6820. }
  6821. }
  6822. }
  6823. }
  6824. this.reset();
  6825. if (execute)
  6826. {
  6827. if (mxEvent.isAltDown(me.getEvent()) && this.graph.isToggleEvent(me.getEvent()))
  6828. {
  6829. var rect = new mxRectangle(this.x, this.y, this.width, this.height);
  6830. var cells = this.graph.getCells(rect.x, rect.y, rect.width, rect.height);
  6831. this.graph.removeSelectionCells(cells);
  6832. }
  6833. else if (this.isSpaceEvent(me))
  6834. {
  6835. this.graph.model.beginUpdate();
  6836. try
  6837. {
  6838. var cells = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), true, true);
  6839. for (var i = 0; i < cells.length; i++)
  6840. {
  6841. if (this.graph.isCellMovable(cells[i]))
  6842. {
  6843. var tmp = this.graph.view.getState(cells[i]);
  6844. var geo = this.graph.getCellGeometry(cells[i]);
  6845. if (tmp != null && geo != null)
  6846. {
  6847. geo = geo.clone();
  6848. geo.translate(dx, dy);
  6849. this.graph.model.setGeometry(cells[i], geo);
  6850. }
  6851. }
  6852. }
  6853. }
  6854. finally
  6855. {
  6856. this.graph.model.endUpdate();
  6857. }
  6858. }
  6859. else
  6860. {
  6861. var rect = new mxRectangle(this.x, this.y, this.width, this.height);
  6862. this.graph.selectRegion(rect, me.getEvent());
  6863. }
  6864. me.consume();
  6865. }
  6866. };
  6867. // Handles preview for creating/removing space in diagram
  6868. mxRubberband.prototype.mouseMove = function(sender, me)
  6869. {
  6870. if (!me.isConsumed() && this.first != null)
  6871. {
  6872. var origin = mxUtils.getScrollOrigin(this.graph.container);
  6873. var offset = mxUtils.getOffset(this.graph.container);
  6874. origin.x -= offset.x;
  6875. origin.y -= offset.y;
  6876. var x = me.getX() + origin.x;
  6877. var y = me.getY() + origin.y;
  6878. var dx = this.first.x - x;
  6879. var dy = this.first.y - y;
  6880. var tol = this.graph.tolerance;
  6881. if (this.div != null || Math.abs(dx) > tol || Math.abs(dy) > tol)
  6882. {
  6883. if (this.div == null)
  6884. {
  6885. this.div = this.createShape();
  6886. }
  6887. // Clears selection while rubberbanding. This is required because
  6888. // the event is not consumed in mouseDown.
  6889. mxUtils.clearSelection();
  6890. this.update(x, y);
  6891. if (this.isSpaceEvent(me))
  6892. {
  6893. var right = this.x + this.width;
  6894. var bottom = this.y + this.height;
  6895. var scale = this.graph.view.scale;
  6896. if (!mxEvent.isAltDown(me.getEvent()))
  6897. {
  6898. this.width = this.graph.snap(this.width / scale) * scale;
  6899. this.height = this.graph.snap(this.height / scale) * scale;
  6900. if (!this.graph.isGridEnabled())
  6901. {
  6902. if (this.width < this.graph.tolerance)
  6903. {
  6904. this.width = 0;
  6905. }
  6906. if (this.height < this.graph.tolerance)
  6907. {
  6908. this.height = 0;
  6909. }
  6910. }
  6911. if (this.x < this.first.x)
  6912. {
  6913. this.x = right - this.width;
  6914. }
  6915. if (this.y < this.first.y)
  6916. {
  6917. this.y = bottom - this.height;
  6918. }
  6919. }
  6920. this.div.style.borderStyle = 'dashed';
  6921. this.div.style.backgroundColor = 'white';
  6922. this.div.style.left = this.x + 'px';
  6923. this.div.style.top = this.y + 'px';
  6924. this.div.style.width = Math.max(0, this.width) + 'px';
  6925. this.div.style.height = this.graph.container.clientHeight + 'px';
  6926. this.div.style.borderWidth = (this.width <= 0) ? '0px 1px 0px 0px' : '0px 1px 0px 1px';
  6927. if (this.secondDiv == null)
  6928. {
  6929. this.secondDiv = this.div.cloneNode(true);
  6930. this.div.parentNode.appendChild(this.secondDiv);
  6931. }
  6932. this.secondDiv.style.left = this.x + 'px';
  6933. this.secondDiv.style.top = this.y + 'px';
  6934. this.secondDiv.style.width = this.graph.container.clientWidth + 'px';
  6935. this.secondDiv.style.height = Math.max(0, this.height) + 'px';
  6936. this.secondDiv.style.borderWidth = (this.height <= 0) ? '1px 0px 0px 0px' : '1px 0px 1px 0px';
  6937. }
  6938. else
  6939. {
  6940. // Hides second div and restores style
  6941. this.div.style.backgroundColor = '';
  6942. this.div.style.borderWidth = '';
  6943. this.div.style.borderStyle = '';
  6944. if (this.secondDiv != null)
  6945. {
  6946. this.secondDiv.parentNode.removeChild(this.secondDiv);
  6947. this.secondDiv = null;
  6948. }
  6949. }
  6950. me.consume();
  6951. }
  6952. }
  6953. };
  6954. // Removes preview
  6955. var mxRubberbandReset = mxRubberband.prototype.reset;
  6956. mxRubberband.prototype.reset = function()
  6957. {
  6958. if (this.secondDiv != null)
  6959. {
  6960. this.secondDiv.parentNode.removeChild(this.secondDiv);
  6961. this.secondDiv = null;
  6962. }
  6963. mxRubberbandReset.apply(this, arguments);
  6964. };
  6965. // Timer-based activation of outline connect in connection handler
  6966. var startTime = new Date().getTime();
  6967. var timeOnTarget = 0;
  6968. var mxEdgeHandlerUpdatePreviewState = mxEdgeHandler.prototype.updatePreviewState;
  6969. mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
  6970. {
  6971. mxEdgeHandlerUpdatePreviewState.apply(this, arguments);
  6972. if (terminalState != this.currentTerminalState)
  6973. {
  6974. startTime = new Date().getTime();
  6975. timeOnTarget = 0;
  6976. }
  6977. else
  6978. {
  6979. timeOnTarget = new Date().getTime() - startTime;
  6980. }
  6981. this.currentTerminalState = terminalState;
  6982. };
  6983. // Timer-based outline connect
  6984. var mxEdgeHandlerIsOutlineConnectEvent = mxEdgeHandler.prototype.isOutlineConnectEvent;
  6985. mxEdgeHandler.prototype.isOutlineConnectEvent = function(me)
  6986. {
  6987. return (this.currentTerminalState != null && me.getState() == this.currentTerminalState && timeOnTarget > 2000) ||
  6988. ((this.currentTerminalState == null || mxUtils.getValue(this.currentTerminalState.style, 'outlineConnect', '1') != '0') &&
  6989. mxEdgeHandlerIsOutlineConnectEvent.apply(this, arguments));
  6990. };
  6991. // Disables custom handles if shift is pressed
  6992. mxVertexHandler.prototype.isCustomHandleEvent = function(me)
  6993. {
  6994. return !mxEvent.isShiftDown(me.getEvent());
  6995. };
  6996. // Shows secondary handle for fixed connection points
  6997. mxEdgeHandler.prototype.createHandleShape = function(index, virtual)
  6998. {
  6999. var source = index != null && index == 0;
  7000. var terminalState = this.state.getVisibleTerminalState(source);
  7001. var c = (index != null && (index == 0 || index >= this.state.absolutePoints.length - 1 ||
  7002. (this.constructor == mxElbowEdgeHandler && index == 2))) ?
  7003. this.graph.getConnectionConstraint(this.state, terminalState, source) : null;
  7004. var pt = (c != null) ? this.graph.getConnectionPoint(this.state.getVisibleTerminalState(source), c) : null;
  7005. var img = (pt != null) ? this.fixedHandleImage : ((c != null && terminalState != null) ?
  7006. this.terminalHandleImage : this.handleImage);
  7007. if (img != null)
  7008. {
  7009. var shape = new mxImageShape(new mxRectangle(0, 0, img.width, img.height), img.src);
  7010. // Allows HTML rendering of the images
  7011. shape.preserveImageAspect = false;
  7012. return shape;
  7013. }
  7014. else
  7015. {
  7016. var s = mxConstants.HANDLE_SIZE;
  7017. if (this.preferHtml)
  7018. {
  7019. s -= 1;
  7020. }
  7021. return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
  7022. }
  7023. };
  7024. var vertexHandlerCreateSizerShape = mxVertexHandler.prototype.createSizerShape;
  7025. mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor)
  7026. {
  7027. this.handleImage = (index == mxEvent.ROTATION_HANDLE) ? HoverIcons.prototype.rotationHandle : (index == mxEvent.LABEL_HANDLE) ? this.secondaryHandleImage : this.handleImage;
  7028. return vertexHandlerCreateSizerShape.apply(this, arguments);
  7029. };
  7030. // Special case for single edge label handle moving in which case the text bounding box is used
  7031. var mxGraphHandlerGetBoundingBox = mxGraphHandler.prototype.getBoundingBox;
  7032. mxGraphHandler.prototype.getBoundingBox = function(cells)
  7033. {
  7034. if (cells != null && cells.length == 1)
  7035. {
  7036. var model = this.graph.getModel();
  7037. var parent = model.getParent(cells[0]);
  7038. var geo = this.graph.getCellGeometry(cells[0]);
  7039. if (model.isEdge(parent) && geo != null && geo.relative)
  7040. {
  7041. var state = this.graph.view.getState(cells[0]);
  7042. if (state != null && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null)
  7043. {
  7044. return mxRectangle.fromRectangle(state.text.boundingBox);
  7045. }
  7046. }
  7047. }
  7048. return mxGraphHandlerGetBoundingBox.apply(this, arguments);
  7049. };
  7050. // Uses text bounding box for edge labels
  7051. var mxVertexHandlerGetSelectionBounds = mxVertexHandler.prototype.getSelectionBounds;
  7052. mxVertexHandler.prototype.getSelectionBounds = function(state)
  7053. {
  7054. var model = this.graph.getModel();
  7055. var parent = model.getParent(state.cell);
  7056. var geo = this.graph.getCellGeometry(state.cell);
  7057. if (model.isEdge(parent) && geo != null && geo.relative && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null)
  7058. {
  7059. var bbox = state.text.unrotatedBoundingBox || state.text.boundingBox;
  7060. return new mxRectangle(Math.round(bbox.x), Math.round(bbox.y), Math.round(bbox.width), Math.round(bbox.height));
  7061. }
  7062. else
  7063. {
  7064. return mxVertexHandlerGetSelectionBounds.apply(this, arguments);
  7065. }
  7066. };
  7067. // Redirects moving of edge labels to mxGraphHandler by not starting here.
  7068. // This will use the move preview of mxGraphHandler (see above).
  7069. var mxVertexHandlerMouseDown = mxVertexHandler.prototype.mouseDown;
  7070. mxVertexHandler.prototype.mouseDown = function(sender, me)
  7071. {
  7072. var model = this.graph.getModel();
  7073. var parent = model.getParent(this.state.cell);
  7074. var geo = this.graph.getCellGeometry(this.state.cell);
  7075. // Lets rotation events through
  7076. var handle = this.getHandleForEvent(me);
  7077. if (handle == mxEvent.ROTATION_HANDLE || !model.isEdge(parent) || geo == null || !geo.relative ||
  7078. this.state == null || this.state.width >= 2 || this.state.height >= 2)
  7079. {
  7080. mxVertexHandlerMouseDown.apply(this, arguments);
  7081. }
  7082. };
  7083. // Shows rotation handle for edge labels.
  7084. mxVertexHandler.prototype.isRotationHandleVisible = function()
  7085. {
  7086. return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) &&
  7087. (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells);
  7088. };
  7089. // Invokes turn on single click on rotation handle
  7090. mxVertexHandler.prototype.rotateClick = function()
  7091. {
  7092. this.state.view.graph.turnShapes([this.state.cell]);
  7093. };
  7094. var vertexHandlerMouseMove = mxVertexHandler.prototype.mouseMove;
  7095. // Workaround for "isConsumed not defined" in MS Edge is to use arguments
  7096. mxVertexHandler.prototype.mouseMove = function(sender, me)
  7097. {
  7098. vertexHandlerMouseMove.apply(this, arguments);
  7099. if (this.graph.graphHandler.first != null)
  7100. {
  7101. if (this.rotationShape != null && this.rotationShape.node != null)
  7102. {
  7103. this.rotationShape.node.style.display = 'none';
  7104. }
  7105. }
  7106. };
  7107. var vertexHandlerMouseUp = mxVertexHandler.prototype.mouseUp;
  7108. mxVertexHandler.prototype.mouseUp = function(sender, me)
  7109. {
  7110. vertexHandlerMouseUp.apply(this, arguments);
  7111. // Shows rotation handle only if one vertex is selected
  7112. if (this.rotationShape != null && this.rotationShape.node != null)
  7113. {
  7114. this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none';
  7115. }
  7116. };
  7117. var vertexHandlerInit = mxVertexHandler.prototype.init;
  7118. mxVertexHandler.prototype.init = function()
  7119. {
  7120. vertexHandlerInit.apply(this, arguments);
  7121. var redraw = false;
  7122. if (this.rotationShape != null)
  7123. {
  7124. this.rotationShape.node.setAttribute('title', mxResources.get('rotateTooltip'));
  7125. }
  7126. var update = mxUtils.bind(this, function()
  7127. {
  7128. // Shows rotation handle only if one vertex is selected
  7129. if (this.rotationShape != null && this.rotationShape.node != null)
  7130. {
  7131. this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none';
  7132. }
  7133. if (this.specialHandle != null)
  7134. {
  7135. this.specialHandle.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none';
  7136. }
  7137. this.redrawHandles();
  7138. });
  7139. this.selectionHandler = mxUtils.bind(this, function(sender, evt)
  7140. {
  7141. update();
  7142. });
  7143. this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler);
  7144. this.changeHandler = mxUtils.bind(this, function(sender, evt)
  7145. {
  7146. this.updateLinkHint(this.graph.getLinkForCell(this.state.cell),
  7147. this.graph.getLinksForState(this.state));
  7148. update();
  7149. });
  7150. this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
  7151. // Repaint needed when editing stops and no change event is fired
  7152. this.editingHandler = mxUtils.bind(this, function(sender, evt)
  7153. {
  7154. this.redrawHandles();
  7155. });
  7156. this.graph.addListener(mxEvent.EDITING_STOPPED, this.editingHandler);
  7157. var link = this.graph.getLinkForCell(this.state.cell);
  7158. var links = this.graph.getLinksForState(this.state);
  7159. this.updateLinkHint(link, links);
  7160. if (link != null || (links != null && links.length > 0))
  7161. {
  7162. redraw = true;
  7163. }
  7164. if (redraw)
  7165. {
  7166. this.redrawHandles();
  7167. }
  7168. };
  7169. mxVertexHandler.prototype.updateLinkHint = function(link, links)
  7170. {
  7171. if ((link == null && (links == null || links.length == 0)) ||
  7172. this.graph.getSelectionCount() > 1)
  7173. {
  7174. if (this.linkHint != null)
  7175. {
  7176. this.linkHint.parentNode.removeChild(this.linkHint);
  7177. this.linkHint = null;
  7178. }
  7179. }
  7180. else if (link != null || (links != null && links.length > 0))
  7181. {
  7182. if (this.linkHint == null)
  7183. {
  7184. this.linkHint = createHint();
  7185. this.linkHint.style.padding = '6px 8px 6px 8px';
  7186. this.linkHint.style.opacity = '1';
  7187. this.linkHint.style.filter = '';
  7188. this.graph.container.appendChild(this.linkHint);
  7189. }
  7190. this.linkHint.innerHTML = '';
  7191. if (link != null)
  7192. {
  7193. this.linkHint.appendChild(this.graph.createLinkForHint(link));
  7194. if (this.graph.isEnabled() && typeof this.graph.editLink === 'function')
  7195. {
  7196. var changeLink = document.createElement('img');
  7197. changeLink.setAttribute('src', Editor.editImage);
  7198. changeLink.setAttribute('title', mxResources.get('editLink'));
  7199. changeLink.setAttribute('width', '11');
  7200. changeLink.setAttribute('height', '11');
  7201. changeLink.style.marginLeft = '10px';
  7202. changeLink.style.marginBottom = '-1px';
  7203. changeLink.style.cursor = 'pointer';
  7204. this.linkHint.appendChild(changeLink);
  7205. mxEvent.addListener(changeLink, 'click', mxUtils.bind(this, function(evt)
  7206. {
  7207. this.graph.setSelectionCell(this.state.cell);
  7208. this.graph.editLink();
  7209. mxEvent.consume(evt);
  7210. }));
  7211. var removeLink = document.createElement('img');
  7212. removeLink.setAttribute('src', Dialog.prototype.clearImage);
  7213. removeLink.setAttribute('title', mxResources.get('removeIt', [mxResources.get('link')]));
  7214. removeLink.setAttribute('width', '13');
  7215. removeLink.setAttribute('height', '10');
  7216. removeLink.style.marginLeft = '4px';
  7217. removeLink.style.marginBottom = '-1px';
  7218. removeLink.style.cursor = 'pointer';
  7219. this.linkHint.appendChild(removeLink);
  7220. mxEvent.addListener(removeLink, 'click', mxUtils.bind(this, function(evt)
  7221. {
  7222. this.graph.setLinkForCell(this.state.cell, null);
  7223. mxEvent.consume(evt);
  7224. }));
  7225. }
  7226. }
  7227. if (links != null)
  7228. {
  7229. for (var i = 0; i < links.length; i++)
  7230. {
  7231. var div = document.createElement('div');
  7232. div.style.marginTop = (link != null || i > 0) ? '6px' : '0px';
  7233. div.appendChild(this.graph.createLinkForHint(
  7234. links[i].getAttribute('href'),
  7235. mxUtils.getTextContent(links[i])));
  7236. this.linkHint.appendChild(div);
  7237. }
  7238. }
  7239. }
  7240. };
  7241. mxEdgeHandler.prototype.updateLinkHint = mxVertexHandler.prototype.updateLinkHint;
  7242. var edgeHandlerInit = mxEdgeHandler.prototype.init;
  7243. mxEdgeHandler.prototype.init = function()
  7244. {
  7245. edgeHandlerInit.apply(this, arguments);
  7246. // Disables connection points
  7247. this.constraintHandler.isEnabled = mxUtils.bind(this, function()
  7248. {
  7249. return this.state.view.graph.connectionHandler.isEnabled();
  7250. });
  7251. var update = mxUtils.bind(this, function()
  7252. {
  7253. if (this.linkHint != null)
  7254. {
  7255. this.linkHint.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none';
  7256. }
  7257. if (this.labelShape != null)
  7258. {
  7259. this.labelShape.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none';
  7260. }
  7261. });
  7262. this.selectionHandler = mxUtils.bind(this, function(sender, evt)
  7263. {
  7264. update();
  7265. });
  7266. this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler);
  7267. this.changeHandler = mxUtils.bind(this, function(sender, evt)
  7268. {
  7269. this.updateLinkHint(this.graph.getLinkForCell(this.state.cell),
  7270. this.graph.getLinksForState(this.state));
  7271. update();
  7272. this.redrawHandles();
  7273. });
  7274. this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
  7275. var link = this.graph.getLinkForCell(this.state.cell);
  7276. var links = this.graph.getLinksForState(this.state);
  7277. if (link != null || (links != null && links.length > 0))
  7278. {
  7279. this.updateLinkHint(link, links);
  7280. this.redrawHandles();
  7281. }
  7282. };
  7283. // Disables connection points
  7284. var connectionHandlerInit = mxConnectionHandler.prototype.init;
  7285. mxConnectionHandler.prototype.init = function()
  7286. {
  7287. connectionHandlerInit.apply(this, arguments);
  7288. this.constraintHandler.isEnabled = mxUtils.bind(this, function()
  7289. {
  7290. return this.graph.connectionHandler.isEnabled();
  7291. });
  7292. };
  7293. var vertexHandlerRedrawHandles = mxVertexHandler.prototype.redrawHandles;
  7294. mxVertexHandler.prototype.redrawHandles = function()
  7295. {
  7296. vertexHandlerRedrawHandles.apply(this);
  7297. if (this.state != null && this.linkHint != null)
  7298. {
  7299. var c = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
  7300. var tmp = new mxRectangle(this.state.x, this.state.y - 22, this.state.width + 24, this.state.height + 22);
  7301. var bb = mxUtils.getBoundingBox(tmp, this.state.style[mxConstants.STYLE_ROTATION] || '0', c);
  7302. var rs = (bb != null) ? mxUtils.getBoundingBox(this.state,
  7303. this.state.style[mxConstants.STYLE_ROTATION] || '0') : this.state;
  7304. var tb = (this.state.text != null) ? this.state.text.boundingBox : null;
  7305. if (bb == null)
  7306. {
  7307. bb = this.state;
  7308. }
  7309. var b = bb.y + bb.height;
  7310. if (tb != null)
  7311. {
  7312. b = Math.max(b, tb.y + tb.height);
  7313. }
  7314. this.linkHint.style.left = Math.max(0, Math.round(rs.x + (rs.width - this.linkHint.clientWidth) / 2)) + 'px';
  7315. this.linkHint.style.top = Math.round(b + this.verticalOffset / 2 + 6 +
  7316. this.state.view.graph.tolerance) + 'px';
  7317. }
  7318. };
  7319. var vertexHandlerReset = mxVertexHandler.prototype.reset;
  7320. mxVertexHandler.prototype.reset = function()
  7321. {
  7322. vertexHandlerReset.apply(this, arguments);
  7323. // Shows rotation handle only if one vertex is selected
  7324. if (this.rotationShape != null && this.rotationShape.node != null)
  7325. {
  7326. this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none';
  7327. }
  7328. };
  7329. var vertexHandlerDestroy = mxVertexHandler.prototype.destroy;
  7330. mxVertexHandler.prototype.destroy = function()
  7331. {
  7332. vertexHandlerDestroy.apply(this, arguments);
  7333. if (this.linkHint != null)
  7334. {
  7335. this.linkHint.parentNode.removeChild(this.linkHint);
  7336. this.linkHint = null;
  7337. }
  7338. if (this.selectionHandler != null)
  7339. {
  7340. this.graph.getSelectionModel().removeListener(this.selectionHandler);
  7341. this.selectionHandler = null;
  7342. }
  7343. if (this.changeHandler != null)
  7344. {
  7345. this.graph.getModel().removeListener(this.changeHandler);
  7346. this.changeHandler = null;
  7347. }
  7348. if (this.editingHandler != null)
  7349. {
  7350. this.graph.removeListener(this.editingHandler);
  7351. this.editingHandler = null;
  7352. }
  7353. };
  7354. var edgeHandlerRedrawHandles = mxEdgeHandler.prototype.redrawHandles;
  7355. mxEdgeHandler.prototype.redrawHandles = function()
  7356. {
  7357. // Workaround for special case where handler
  7358. // is reset before this which leads to a NPE
  7359. if (this.marker != null)
  7360. {
  7361. edgeHandlerRedrawHandles.apply(this);
  7362. if (this.state != null && this.linkHint != null)
  7363. {
  7364. var b = this.state;
  7365. if (this.state.text != null && this.state.text.bounds != null)
  7366. {
  7367. b = new mxRectangle(b.x, b.y, b.width, b.height);
  7368. b.add(this.state.text.bounds);
  7369. }
  7370. this.linkHint.style.left = Math.max(0, Math.round(b.x + (b.width - this.linkHint.clientWidth) / 2)) + 'px';
  7371. this.linkHint.style.top = Math.round(b.y + b.height + 6 + this.state.view.graph.tolerance) + 'px';
  7372. }
  7373. }
  7374. };
  7375. var edgeHandlerReset = mxEdgeHandler.prototype.reset;
  7376. mxEdgeHandler.prototype.reset = function()
  7377. {
  7378. edgeHandlerReset.apply(this, arguments);
  7379. if (this.linkHint != null)
  7380. {
  7381. this.linkHint.style.visibility = '';
  7382. }
  7383. };
  7384. var edgeHandlerDestroy = mxEdgeHandler.prototype.destroy;
  7385. mxEdgeHandler.prototype.destroy = function()
  7386. {
  7387. edgeHandlerDestroy.apply(this, arguments);
  7388. if (this.linkHint != null)
  7389. {
  7390. this.linkHint.parentNode.removeChild(this.linkHint);
  7391. this.linkHint = null;
  7392. }
  7393. if (this.selectionHandler != null)
  7394. {
  7395. this.graph.getSelectionModel().removeListener(this.selectionHandler);
  7396. this.selectionHandler = null;
  7397. }
  7398. if (this.changeHandler != null)
  7399. {
  7400. this.graph.getModel().removeListener(this.changeHandler);
  7401. this.changeHandler = null;
  7402. }
  7403. };
  7404. })();
  7405. }