| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914 |
- /**
- * Copyright (c) 2006-2017, JGraph Ltd
- * Copyright (c) 2006-2017, Gaudenz Alder
- */
- (function()
- {
- /**
- * Version
- */
- EditorUi.VERSION = '@DRAWIO-VERSION@';
-
- /**
- * Overrides compact UI setting.
- */
- EditorUi.compactUi = Editor.currentTheme != 'atlas' || window.DRAWIO_PUBLIC_BUILD;
- /**
- * Overrides default grid color for dark mode
- */
- if (Editor.isDarkMode())
- {
- mxGraphView.prototype.gridColor = mxGraphView.prototype.defaultDarkGridColor;
- }
-
- /**
- * Switch to disable logging for mode and search terms.
- */
- EditorUi.enableLogging = urlParams['stealth'] != '1' && urlParams['lockdown'] != '1' &&
- (/.*\.draw\.io$/.test(window.location.hostname) ||
- /.*\.diagrams\.net$/.test(window.location.hostname)) &&
- window.location.hostname != 'https://preprod.diagrams.net/' &&
- window.location.hostname != 'support.draw.io';
-
- /**
- * Protocol and hostname to use for embedded files. Default is https://www.draw.io
- */
- EditorUi.drawHost = window.DRAWIO_BASE_URL;
-
- /**
- * Protocol and hostname to use for embedded files. Default is https://www.draw.io
- */
- EditorUi.lightboxHost = window.DRAWIO_LIGHTBOX_URL;
-
- /**
- * Switch to disable logging for mode and search terms.
- */
- EditorUi.lastErrorMessage = null;
- /**
- * Switch to disable logging for mode and search terms.
- */
- EditorUi.ignoredAnonymizedChars = '\n\t`~!@#$%^&*()_+{}|:"<>?-=[]\;\'.\/,\n\t';
- /**
- * Specifies the URL for the templates index file.
- */
- EditorUi.templateFile = TEMPLATE_PATH + '/index.xml';
- /**
- * Specifies the URL for the diffsync cache.
- */
- EditorUi.cacheUrl = window.REALTIME_URL;
- /**
- * Disables sync if no diffsync cache is defined.
- */
- if (EditorUi.cacheUrl == null && typeof DrawioFile !== 'undefined')
- {
- DrawioFile.SYNC = 'none'; // Disables real-time sync
- }
-
- /**
- * Cache timeout is 10 seconds.
- */
- Editor.cacheTimeout = 10000;
- /**
- * Switch to enable PlantUML in the insert from text dialog.
- * NOTE: This must also be enabled on the server-side.
- */
- EditorUi.enablePlantUml = EditorUi.enableLogging;
- /**
- * https://github.com/electron/electron/issues/2288
- */
- EditorUi.isElectronApp = window != null && window.process != null &&
- window.process.versions != null && window.process.versions['electron'] != null;
-
- /**
- * Shortcut for capability check.
- */
- EditorUi.nativeFileSupport = !mxClient.IS_OP && !EditorUi.isElectronApp &&
- urlParams['extAuth'] != '1' && 'showSaveFilePicker' in window &&
- 'showOpenFilePicker' in window;
- /**
- * Specifies if drafts should be saved in IndexedDB.
- */
- EditorUi.enableDrafts = !mxClient.IS_CHROMEAPP &&
- isLocalStorage && urlParams['drafts'] != '0';
-
- /**
- * Link for scratchpad help.
- */
- EditorUi.scratchpadHelpLink = 'https://www.drawio.com/doc/faq/scratchpad';
- /**
- * Specifies if the edit option should be shown in the HTML export dialog.
- */
- EditorUi.enableHtmlEditOption = true;
-
- /**
- * Default Mermaid config without using foreign objects in flowcharts.
- */
- EditorUi.mermaidDiagramTypes = ['flowchart', 'classDiagram', 'sequenceDiagram',
- 'stateDiagram', 'mindmap', 'graph', 'erDiagram', 'requirementDiagram',
- 'journey', 'gantt', 'pie', 'gitGraph'];
- /**
- * Default Mermaid config without using foreign objects in flowcharts.
- */
- EditorUi.defaultMermaidConfig = {
- theme:'neutral',
- arrowMarkerAbsolute:false,
- flowchart:
- {
- htmlLabels:false
- },
- sequence:
- {
- diagramMarginX:50,
- diagramMarginY:10,
- actorMargin:50,
- width:150,
- height:65,
- boxMargin:10,
- boxTextMargin:5,
- noteMargin:10,
- messageMargin:35,
- mirrorActors:true,
- bottomMarginAdj:1,
- useMaxWidth:true,
- rightAngles:false,
- showSequenceNumbers:false
- },
- gantt:{
- titleTopMargin:25,
- barHeight:20,
- barGap:4,
- topPadding:50,
- leftPadding:75,
- gridLineStartPadding:35,
- fontSize:11,
- fontFamily:'"Open-Sans", "sans-serif"',
- numberSectionStyles:4,
- axisFormat:'%Y-%m-%d'
- }
- };
- /**
- * Updates action states depending on the selection.
- */
- EditorUi.logError = function(message, url, linenumber, colno, err, severity, quiet)
- {
- if (message != null)
- {
- err = (err != null) ? err : new Error(message);
- err.stack = (err.stack != null) ? err.stack : '';
- severity = (severity != null) ? severity : ((message.indexOf('NetworkError') < 0 &&
- message.indexOf('SecurityError') < 0 && message.indexOf('NS_ERROR_FAILURE') < 0 &&
- message.indexOf('out of memory') < 0) ? 'SEVERE' : 'CONFIG');
-
- try
- {
- if (EditorUi.enableLogging && urlParams['dev'] != '1' &&
- message != EditorUi.lastErrorMessage && message.indexOf('extension:') < 0 &&
- message.indexOf('ResizeObserver loop completed with undelivered notifications') < 0 &&
- err.stack.indexOf('extension:') < 0 && err.stack.indexOf('<anonymous>:') < 0 &&
- err.stack.indexOf('/math/es5/') < 0)
- {
- EditorUi.lastErrorMessage = message;
- var img = new Image();
- var logDomain = window.DRAWIO_LOG_URL != null ?
- window.DRAWIO_LOG_URL : '';
- img.src = logDomain + '/log?severity=' + severity +
- '&v=' + encodeURIComponent(EditorUi.VERSION) +
- '&msg=clientError:' + encodeURIComponent(message) +
- ':url:' + encodeURIComponent(window.location.href) +
- ':lnum:' + encodeURIComponent(linenumber) +
- ((colno != null) ?
- ':colno:' + encodeURIComponent(colno) : '') +
- ((err.stack != '') ?
- '&stack=' + encodeURIComponent(err.stack) : '');
- }
- }
- catch (e)
- {
- // ignore
- }
-
- try
- {
- if (!quiet && window.console != null)
- {
- console.error(severity, message, url, linenumber, colno, err);
- }
- }
- catch (e)
- {
- // ignore
- }
- }
- };
-
- /**
- * Updates action states depending on the selection.
- */
- EditorUi.logEvent = function(data)
- {
- if (urlParams['dev'] == '1')
- {
- EditorUi.debug('logEvent', data);
- }
- else if (EditorUi.enableLogging)
- {
- try
- {
- var logDomain = window.DRAWIO_LOG_URL != null ? window.DRAWIO_LOG_URL : '';
- var img = new Image();
- img.src = logDomain + '/images/1x1.png?' +
- 'v=' + encodeURIComponent(EditorUi.VERSION) +
- ((data != null) ? '&data=' + encodeURIComponent(JSON.stringify(data)) : '');
- }
- catch (e)
- {
- // ignore
- }
- }
- };
- /**
- * Sending error reports.
- */
- EditorUi.sendReport = function(data, maxLength)
- {
- if (urlParams['dev'] == '1')
- {
- EditorUi.debug('sendReport', data);
- }
- else if (EditorUi.enableLogging)
- {
- try
- {
- maxLength = (maxLength != null) ? maxLength : 50000;
- if (data.length > maxLength)
- {
- data = data.substring(0, maxLength) + '\n...[SHORTENED]'
- }
-
- mxUtils.post('/email', 'version=' + encodeURIComponent(EditorUi.VERSION) +
- '&url=' + encodeURIComponent(window.location.href) +
- '&data=' + encodeURIComponent(data));
- }
- catch (e)
- {
- // ignore
- }
- }
- };
- /**
- * Adds the listener for automatically saving the diagram for local changes.
- */
- EditorUi.debug = function()
- {
- try
- {
- if (window.console != null && urlParams['test'] == '1')
- {
- var args = [new Date().toISOString()];
-
- for (var i = 0; i < arguments.length; i++)
- {
- args.push(arguments[i]);
- }
-
- console.log.apply(console, args);
- }
- }
- catch (e)
- {
- // ignore
- }
- };
- /**
- * Removes any values, styles and geometries from the given XML node.
- */
- EditorUi.removeChildNodes = function(node)
- {
- while (node.firstChild != null)
- {
- node.removeChild(node.firstChild);
- }
- };
-
- /**
- * Replaces SVG data URIs in images with the actual SVG for
- * the images to be supported in apps like Powerpoint.
- */
- EditorUi.embedSvgImages = function(root)
- {
- var temp = root.getElementsByTagName('image');
- // Clones array
- var imgs = [];
- for (var i = 0; i < temp.length; i++)
- {
- imgs.push(temp[i]);
- }
- // Replaces images
- for (var i = 0; i < imgs.length; i++)
- {
- EditorUi.replaceSvgImage(imgs[i]);
- }
- };
-
- /**
- * Replaces the given SVG image with an SVG subtree.
- */
- EditorUi.replaceSvgImage = function(node)
- {
- try
- {
- var href = null;
- // Workaround for missing namespace support
- if (node.getAttributeNS == null)
- {
- href = node.getAttribute('xlink:href');
- }
- else
- {
- href = node.getAttributeNS(mxConstants.NS_XLINK, 'href');
- }
- var svg = EditorUi.getSvgSubtree(href);
- // Checks nodeName as parsers can get foreignObjects
- // content to go before the SVG element
- if (svg != null && svg.nodeName == 'svg')
- {
- svg.setAttribute('x', node.getAttribute('x'));
- svg.setAttribute('y', node.getAttribute('y'));
- svg.setAttribute('width', node.getAttribute('width'));
- svg.setAttribute('height', node.getAttribute('height'));
- svg.style.fontFamily = 'initial';
- // Adds group with optional transform attribute
- var group = mxUtils.createElementNs(svg.ownerDocument,
- mxConstants.NS_SVG, 'g');
- group.appendChild(svg);
- if (node.hasAttribute('transform'))
- {
- group.setAttribute('transform', node.getAttribute('transform'));
- }
-
- node.parentNode.replaceChild(group, node);
- }
- }
- catch (e)
- {
- // ignore
- }
- };
-
- /**
- * Returns SVG with modified CSS rules that limit scope to subtree.
- */
- EditorUi.getSvgSubtree = function(href)
- {
- var data = Graph.getSvgFromDataUri(href);
- var svg = null;
- if (data != null)
- {
- svg = Graph.sanitizeNode(mxUtils.parseXml(data).documentElement);
- // Limits CSS rules to subtree
- var styles = svg.getElementsByTagName('style');
- if (styles.length > 0)
- {
- var id = 'svg-image-' + Editor.guid();
- svg.setAttribute('id', id);
-
- // Adds ID selector for all CSS rules to limit scope
- var doc = document.implementation.createHTMLDocument(''),
- styleElement = document.createElement('style');
- for (var j = 0; j < styles.length; j++)
- {
- styleElement.textContent = styles[j].textContent;
- doc.body.appendChild(styleElement);
- var modifiedCss = '';
- for (var k = 0; k < styleElement.sheet.cssRules.length; k++)
- {
- var rule = styleElement.sheet.cssRules[k];
- if (rule.selectorText != null)
- {
- var tokens = rule.selectorText.split(',');
- for (var l = 0; l < tokens.length; l++)
- {
- tokens[l] = '#' + id + ' ' + tokens[l];
- }
- rule.selectorText = tokens.join(',');
- }
- modifiedCss += rule.cssText + '\n';
- }
-
- styles[j].textContent = modifiedCss;
- }
- }
- // Removes data-cell-id attribute from all elements
- var elements = svg.getElementsByTagName('*');
- for (var i = 0; i < elements.length; i++)
- {
- elements[i].removeAttribute('data-cell-id');
- }
- }
- return svg;
- };
-
- /**
- * Contains the default XML for an empty diagram.
- */
- EditorUi.prototype.emptyDiagramXml = '<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel>';
- /**
- *
- */
- EditorUi.prototype.emptyLibraryXml = '<mxlibrary>[]</mxlibrary>';
- /**
- * Sets the delay for autosave in milliseconds. Default is 2000.
- */
- EditorUi.prototype.mode = null;
- /**
- * General timeout is 25 seconds.
- * LATER: Move to Editor
- */
- EditorUi.prototype.timeout = Editor.prototype.timeout;
- /**
- * Specifies the default custom shape style.
- */
- EditorUi.prototype.defaultCustomShapeStyle = 'shape=stencil(tZRtTsQgEEBPw1+DJR7AoN6DbWftpAgE0Ortd/jYRGq72R+YNE2YgTePloEJGWblgA18ZuKFDcMj5/Sm8boZq+BgjCX4pTyqk6ZlKROitwusOMXKQDODx5iy4pXxZ5qTHiFHawxB0JrQZH7lCabQ0Fr+XWC1/E8zcsT/gAi+Subo2/3Mh6d/oJb5nU1b5tW7r2knautaa3T+U32o7f7vZwpJkaNDLORJjcu7t59m2jXxqX9un+tt022acsfmoKaQZ+vhhswZtS6Ne/ThQGt0IV0N3Yyv6P3CeT9/tHO0XFI5cAE=);whiteSpace=wrap;html=1;';
- /**
- * Defines the maximum size for images.
- */
- EditorUi.prototype.maxBackgroundSize = 1600;
- /**
- * Defines the maximum size for images in px. Default is 1200.
- */
- EditorUi.prototype.maxImageSize = 1200;
-
- /**
- * Defines the maximum width for pasted text.
- * Use 0 to disable check.
- */
- EditorUi.prototype.maxTextWidth = 520;
- /**
- * Images above 100K should be resampled.
- */
- EditorUi.prototype.resampleThreshold = 100000;
- /**
- * Defines the maximum size for images in bytes. Default is 2 MB.
- */
- EditorUi.prototype.maxImageBytes = 2000000;
- /**
- * Maximum size for background images is 2.5 MB.
- */
- EditorUi.prototype.maxBackgroundBytes = 2500000;
- /**
- * Maximum size for text files in labels is 0.5 MB.
- */
- EditorUi.prototype.maxTextBytes = 500000;
- /**
- * Holds the current file.
- */
- EditorUi.prototype.currentFile = null;
- /**
- * Specifies if PDF export should be done via print dialog. Default is
- * false which uses the PhantomJS backend to create the PDF.
- */
- EditorUi.prototype.printPdfExport = false;
-
- /**
- * Specifies if PDF export with pages is enabled.
- */
- EditorUi.prototype.pdfPageExport = true;
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.formatEnabled = urlParams['format'] != '0';
- /**
- * Whether template action should be shown in insert menu.
- */
- EditorUi.prototype.insertTemplateEnabled = true;
-
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.closableScratchpad = true;
-
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.embedExportBorder = 8;
-
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.embedExportBackground = null;
-
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.shareCursorPosition = true;
- /**
- * Restores app defaults for UI
- */
- EditorUi.prototype.showRemoteCursors = true;
-
- /**
- * Capability check for canvas export
- */
- (function()
- {
- EditorUi.prototype.useCanvasForExport = false;
- EditorUi.prototype.jpgSupported = false;
- EditorUi.prototype.webpSupported = false;
-
- // Checks if canvas is supported
- try
- {
- var cnv = document.createElement('canvas');
- EditorUi.prototype.canvasSupported = !!(cnv.getContext && cnv.getContext('2d'));
- }
- catch (e)
- {
- // ignore
- }
-
- try
- {
- var canvas = document.createElement('canvas');
- var img = new Image();
-
- // LATER: Capability check should not be async
- img.onload = function()
- {
- try
- {
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- // Works in Chrome, Firefox, Edge, Safari and Opera
- var result = canvas.toDataURL('image/png');
- EditorUi.prototype.useCanvasForExport = result != null && result.length > 6;
- }
- catch (e)
- {
- // ignore
- }
- };
- // Checks if SVG with foreignObject can be exported
- var svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1px" height="1px" version="1.1"><foreignObject pointer-events="all" width="1" height="1"><div xmlns="http://www.w3.org/1999/xhtml"></div></foreignObject></svg>';
- img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
- }
- catch (e)
- {
- // ignore
- }
-
- // Checks for client-side JPG and WebP support
- try
- {
- var canvas = document.createElement('canvas');
- canvas.width = canvas.height = 1;
- var uri = canvas.toDataURL('image/jpeg');
- EditorUi.prototype.jpgSupported = (uri.match('image/jpeg') !== null);
- uri = canvas.toDataURL('image/webp');
- EditorUi.prototype.webpSupported = (uri.match('image/webp') !== null);
- }
- catch (e)
- {
- // ignore
- }
- })();
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.createButtonContainer = function()
- {
- var div = document.createElement('div');
- div.className = 'geButtonContainer';
- div.style.overflow = (urlParams['embed'] == '1') ? 'hidden' : '';
- return div;
- };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.openLink = function(url, target, allowOpener)
- {
- // LATER: Replace this with direct calls to graph
- return this.editor.graph.openLink(url, target, allowOpener);
- };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.showSplash = function(force) { };
- /**
- * Abstraction for local storage access.
- */
- EditorUi.prototype.getLocalData = function(key, fn)
- {
- fn(localStorage.getItem(key));
- };
-
- /**
- * Abstraction for local storage access.
- */
- EditorUi.prototype.setLocalData = function(key, data, fn)
- {
- localStorage.setItem(key, data);
-
- if (fn != null)
- {
- fn();
- }
- };
-
- /**
- * Abstraction for local storage access.
- */
- EditorUi.prototype.isLocked = function()
- {
- var file = this.getCurrentFile();
- return file != null && file.isLocked()
- };
-
- /**
- * Abstraction for local storage access.
- */
- EditorUi.prototype.removeLocalData = function(key, fn)
- {
- localStorage.removeItem(key)
- fn();
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.setShareCursorPosition = function(value)
- {
- this.shareCursorPosition = value;
- this.fireEvent(new mxEventObject('shareCursorPositionChanged'));
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.isShareCursorPosition = function()
- {
- return this.shareCursorPosition;
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.setShowRemoteCursors = function(value)
- {
- this.showRemoteCursors = value;
- this.fireEvent(new mxEventObject('showRemoteCursorsChanged'));
- };
-
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.isShowRemoteCursors = function()
- {
- return this.showRemoteCursors;
- };
-
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.setMathEnabled = function(value)
- {
- var graph = this.editor.graph;
- graph.mathEnabled = value;
- // Forces refresh of background image
- if (graph.view.backgroundImage != null)
- {
- graph.view.backgroundImage.destroy();
- graph.view.backgroundImage = null;
- }
- this.editor.updateGraphComponents();
- graph.refresh();
- graph.defaultMathEnabled = value;
-
- this.fireEvent(new mxEventObject('mathEnabledChanged'));
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.isMathEnabled = function(value)
- {
- return this.editor.graph.mathEnabled;
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.isStandaloneApp = function()
- {
- return mxClient.IS_CHROMEAPP || EditorUi.isElectronApp || this.isOfflineApp();
- };
- /**
- * Returns true if offline app, which isn't a defined thing
- */
- EditorUi.prototype.isOfflineApp = function()
- {
- return urlParams['offline'] == '1';
- };
- /**
- * Deprecated. Poorly defined, to be replaced with isExternalDataComms and other more granular flags.
- * Original idea was it returns true if no external comms allowed or possible
- */
- EditorUi.prototype.isOffline = function(ignoreStealth)
- {
- return this.isOfflineApp() || !navigator.onLine || (!ignoreStealth && (urlParams['stealth'] == '1' || urlParams['lockdown'] == '1'));
- };
- /**
- * Returns true if diagram data transmission other than save/load is allowed or possible..
- */
- EditorUi.prototype.isExternalDataComms = function()
- {
- return urlParams['offline'] != '1' && !this.isOffline() && !this.isOfflineApp();
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.createSpinner = function(x, y, size)
- {
- var autoPosition = (x == null || y == null);
- size = (size != null) ? size : 24;
- var spinner = new Spinner({
- lines: 12, // The number of lines to draw
- length: size, // The length of each line
- width: Math.round(size / 3), // The line thickness
- radius: Math.round(size / 2), // The radius of the inner circle
- rotate: 0, // The rotation offset
- color: (Editor.isDarkMode()) ? '#c0c0c0' : '#000', // #rgb or #rrggbb
- speed: 1.5, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: false, // Whether to use hardware acceleration
- zIndex: 2e9 // The z-index (defaults to 2000000000)
- });
- // Extends spin method to include an optional label
- var defaultTimeout = this.timeout;
- var oldSpin = spinner.spin;
- var thread = null;
- var retry = null;
- var resume = mxUtils.bind(this, function(fn)
- {
- if (fn != null)
- {
- fn();
- }
- });
-
- spinner.spin = function(container, label, error, timeout)
- {
- timeout = (timeout != null) ? timeout : defaultTimeout;
- var result = false;
-
- if (!this.active)
- {
- var start = Date.now();
- if (error != null)
- {
- thread = window.setTimeout(function()
- {
- spinner.stop();
- thread = null;
- error({code: App.ERROR_TIMEOUT,
- message: mxResources.get('timeout'),
- retry: retry});
- }, timeout);
- }
- oldSpin.call(this, container);
- this.active = true;
-
- if (label != null)
- {
- if (autoPosition)
- {
- y = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight || 0) / 2;
- x = document.body.clientWidth / 2 - 2;
- }
- var status = document.createElement('div');
- status.className = 'geSpinnerStatus';
- status.style.position = 'absolute';
- status.style.whiteSpace = 'nowrap';
- status.style.background = '#4B4243';
- status.style.color = 'white';
- status.style.fontFamily = Editor.defaultHtmlFont;
- status.style.fontSize = '9pt';
- status.style.padding = '6px';
- status.style.paddingLeft = '10px';
- status.style.paddingRight = '10px';
- status.style.zIndex = 2e9;
- status.style.left = Math.max(0, x) + 'px';
- status.style.top = Math.max(0, y + 70) + 'px';
-
- mxUtils.setPrefixedStyle(status.style, 'borderRadius', '6px');
- mxUtils.setPrefixedStyle(status.style, 'transform', 'translate(-50%,-50%)');
- if (!Editor.isDarkMode())
- {
- mxUtils.setPrefixedStyle(status.style, 'boxShadow', '2px 2px 3px 0px #ddd');
- }
-
- if (label.substring(label.length - 3, label.length) != '...' &&
- label.charAt(label.length - 1) != '!')
- {
- label = label + '...';
- }
-
- status.innerHTML = mxUtils.htmlEntities(label);
- container.appendChild(status);
- spinner.status = status;
- }
-
- // Pause returns a function to resume the spinner
- this.pause = mxUtils.bind(this, function()
- {
- var fn = resume;
-
- if (this.active)
- {
- // Reduces timeout by used time
- timeout = Math.max(0, timeout - (Date.now() - start));
- fn = mxUtils.bind(this, function(continueFn)
- {
- this.spin(container, label, error, timeout);
- // Continue is called after spinner was paused and is
- // used as the new retry function from here on
- if (continueFn != null)
- {
- try
- {
- continueFn();
- retry = mxUtils.bind(this, function()
- {
- this.spin(container, label, error, timeout);
- try
- {
- continueFn();
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- });
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }
- });
- }
-
- this.stop();
-
- return fn;
- });
-
- result = true;
- }
-
- return result;
- };
-
- // Extends stop method to remove the optional label
- var oldStop = spinner.stop;
-
- spinner.stop = function()
- {
- oldStop.call(this);
-
- if (this.active)
- {
- this.active = false;
- if (thread != null)
- {
- window.clearTimeout(thread);
- thread = null;
- }
-
- if (spinner.status != null && spinner.status.parentNode != null)
- {
- spinner.status.parentNode.removeChild(spinner.status);
- }
- spinner.status = null;
- }
- };
-
- spinner.pause = function()
- {
- return resume;
- };
-
- return spinner;
- };
- /**
- * Returns true if the given string contains a compatible graph model.
- */
- EditorUi.prototype.isCompatibleString = function(data)
- {
- try
- {
- var doc = mxUtils.parseXml(data);
- var node = this.editor.extractGraphModel(doc.documentElement, true);
-
- return node != null && node.getElementsByTagName('parsererror').length == 0;
- }
- catch (e)
- {
- // ignore
- }
-
- return false;
- };
- /**
- * Returns true if the given binary data is a Visio file.
- */
- EditorUi.isVisioFilename = function(filename)
- {
- return (/(\.v(dx|sdx?))($|\?)/i.test(filename) ||
- /(\.vs(x|sx?))($|\?)/i.test(filename));
- };
- /**
- * Returns true if the given binary data is a Visio file.
- */
- EditorUi.prototype.isVisioData = function(data)
- {
- return data.length > 8 && ((data.charCodeAt(0) == 0xD0 && data.charCodeAt(1) == 0xCF &&
- data.charCodeAt(2) == 0x11 && data.charCodeAt(3) == 0xE0 && data.charCodeAt(4) == 0xA1 && data.charCodeAt(5) == 0xB1 &&
- data.charCodeAt(6) == 0x1A && data.charCodeAt(7) == 0xE1) || (data.charCodeAt(0) == 0x50 && data.charCodeAt(1) == 0x4B &&
- data.charCodeAt(2) == 0x03 && data.charCodeAt(3) == 0x04) || (data.charCodeAt(0) == 0x50 && data.charCodeAt(1) == 0x4B &&
- data.charCodeAt(2) == 0x03 && data.charCodeAt(3) == 0x06));
- };
- /**
- * Returns true if the given binary data is a Visio file that requires remote conversion.
- * This code returns true for vss, vsd and vdx files.
- */
- EditorUi.prototype.isRemoteVisioData = function(data)
- {
- return data.length > 8 && ((data.charCodeAt(0) == 0xD0 && data.charCodeAt(1) == 0xCF &&
- data.charCodeAt(2) == 0x11 && data.charCodeAt(3) == 0xE0 && data.charCodeAt(4) == 0xA1 && data.charCodeAt(5) == 0xB1 &&
- data.charCodeAt(6) == 0x1A && data.charCodeAt(7) == 0xE1) || (data.charCodeAt(0) == 0x3C && data.charCodeAt(1) == 0x3F &&
- data.charCodeAt(2) == 0x78 && data.charCodeAt(3) == 0x6D && data.charCodeAt(3) == 0x6C));
- };
- /**
- * Adds keyboard shortcuts for page handling.
- */
- var editorUiCreateKeyHandler = EditorUi.prototype.createKeyHandler;
- EditorUi.prototype.createKeyHandler = function(editor)
- {
- var keyHandler = editorUiCreateKeyHandler.apply(this, arguments);
-
- if (!this.editor.chromeless || this.editor.editable)
- {
- var keyHandlerGetFunction = keyHandler.getFunction;
- var graph = this.editor.graph;
- var ui = this;
-
- keyHandler.getFunction = function(evt)
- {
- if (graph.isSelectionEmpty() && ui.pages != null && ui.pages.length > 0)
- {
- var idx = ui.getSelectedPageIndex();
- if (mxEvent.isShiftDown(evt))
- {
- if (evt.keyCode == 37)
- {
- return function()
- {
- if (idx > 0)
- {
- ui.movePage(idx, idx - 1);
- }
- };
- }
- else if (evt.keyCode == 38)
- {
- return function()
- {
- if (idx > 0)
- {
- ui.movePage(idx, 0);
- }
- };
- }
- else if (evt.keyCode == 39)
- {
- return function()
- {
- if (idx < ui.pages.length - 1)
- {
- ui.movePage(idx, idx + 1);
- }
- };
- }
- else if (evt.keyCode == 40)
- {
- return function()
- {
- if (idx < ui.pages.length - 1)
- {
- ui.movePage(idx, ui.pages.length - 1);
- }
- };
- }
- }
- else if (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && mxEvent.isMetaDown(evt)))
- {
- if (evt.keyCode == 37)
- {
- return function()
- {
- if (idx > 0)
- {
- ui.selectNextPage(false);
- }
- };
- }
- else if (evt.keyCode == 38)
- {
- return function()
- {
- if (idx > 0)
- {
- ui.selectPage(ui.pages[0]);
- }
- };
- }
- else if (evt.keyCode == 39)
- {
- return function()
- {
- if (idx < ui.pages.length - 1)
- {
- ui.selectNextPage(true);
- }
- };
- }
- else if (evt.keyCode == 40)
- {
- return function()
- {
- if (idx < ui.pages.length - 1)
- {
- ui.selectPage(ui.pages[ui.pages.length - 1]);
- }
- };
- }
- }
- }
- // Ignores normal keystrokes as shortcuts if cells are selected (eg. A/S/D/F)
- if (evt.keyCode >= 65 && evt.keyCode <= 90 && !graph.isSelectionEmpty() &&
- !mxEvent.isAltDown(evt) && !mxEvent.isShiftDown(evt) &&
- !mxEvent.isControlDown(evt) && !(mxClient.IS_MAC && mxEvent.isMetaDown(evt)))
- {
- return null;
- }
- else
- {
- return keyHandlerGetFunction.apply(this, arguments);
- }
- };
- }
-
- return keyHandler;
- };
- /**
- * Extracts the mxfile from the given HTML data from a data transfer event.
- */
- var editorUiExtractGraphModelFromHtml = EditorUi.prototype.extractGraphModelFromHtml;
- EditorUi.prototype.extractGraphModelFromHtml = function(data)
- {
- var result = editorUiExtractGraphModelFromHtml.apply(this, arguments);
-
- if (result == null)
- {
- try
- {
- var idx = data.indexOf('<mxfile ');
-
- if (idx >= 0)
- {
- var idx2 = data.lastIndexOf('</mxfile>');
-
- if (idx2 > idx)
- {
- result = data.substring(idx, idx2 + 15).replace(/>/g, '>').
- replace(/</g, '<').replace(/\\"/g, '"').replace(/\n/g, '');
- }
- }
- else
- {
- // Gets compressed data from mxgraph element in HTML document
- var doc = mxUtils.parseXml(data);
- var node = this.editor.extractGraphModel(doc.documentElement, this.pages != null ||
- this.diagramContainer.style.visibility == 'hidden');
- result = (node != null) ? mxUtils.getXml(node) : '';
- }
- }
- catch (e)
- {
- // ignore
- }
- }
-
- return result;
- };
-
- /**
- * Workaround for malformed xhtml meta element bug 07.08.16. The trailing slash was missing causing
- * reopen to fail trying to parse. Used in replaceFileData, setFileData and importFile.
- */
- EditorUi.prototype.validateFileData = function(data)
- {
- if (data != null && data.length > 0)
- {
- var index = data.indexOf('<meta charset="utf-8">');
-
- if (index >= 0)
- {
- var replaceString = '<meta charset="utf-8"/>';
- var replaceStrLen = replaceString.length;
- data = data.slice(0, index) + replaceString + data.slice(index + replaceStrLen - 1, data.length);
- }
-
- data = Graph.zapGremlins(data);
- }
-
- return data;
- };
-
- /**
- *
- */
- EditorUi.prototype.replaceFileData = function(data, patches)
- {
- EditorUi.debug('EditorUi.replaceFileData', [this],
- 'data', [data], 'patches', patches);
-
- data = this.validateFileData(data);
- var node = (data != null && data.length > 0) ? mxUtils.parseXml(data).documentElement : null;
- // Some nodes must be extracted here to find the mxfile node
- // LATER: Remove duplicate call to extractGraphModel in overridden setGraphXml
- var tmp = (node != null) ? this.editor.extractGraphModel(node, true) : null;
-
- if (tmp != null)
- {
- node = tmp;
- }
- if (node != null)
- {
- var graph = this.editor.graph;
-
- graph.model.beginUpdate();
- try
- {
- var oldPages = (this.pages != null) ? this.pages.slice() : null;
- var nodes = node.getElementsByTagName('diagram');
- if (nodes.length > 1 || (nodes.length == 1 && nodes[0].hasAttribute('name')))
- {
- this.fileNode = node;
- this.pages = (this.pages != null) ? this.pages : [];
-
- // Wraps page nodes
- for (var i = nodes.length - 1; i >= 0; i--)
- {
- var page = this.updatePageRoot(new DiagramPage(nodes[i]));
-
- // Checks for invalid page names
- if (page.getName() == null)
- {
- page.setName(mxResources.get('pageWithNumber', [i + 1]));
- }
- graph.model.execute(new ChangePage(this, page, (i == 0) ? page : null, 0));
- }
- }
- else
- {
- // Creates tabbed file structure if enforced by URL
- if (this.fileNode == null)
- {
- this.fileNode = node.ownerDocument.createElement('mxfile');
- this.currentPage = new DiagramPage(node.ownerDocument.createElement('diagram'));
- this.currentPage.setName(mxResources.get('pageWithNumber', [1]));
- graph.model.execute(new ChangePage(this, this.currentPage, this.currentPage, 0));
- }
-
- // Avoids scroll offset when switching page
- this.editor.setGraphXml(node);
-
- // Avoids duplicate parsing of the XML stored in the node
- if (this.currentPage != null)
- {
- this.currentPage.root = this.editor.graph.model.root;
- graph.model.execute(new ChangePage(this, this.currentPage, this.currentPage, 0));
- }
- }
-
- // Removes old pages
- if (oldPages != null)
- {
- for (var i = 0; i < oldPages.length; i++)
- {
- graph.model.execute(new ChangePage(this, oldPages[i], null));
- }
- }
-
- // Updates internal sync state for current file
- var file = this.getCurrentFile();
- if (file != null)
- {
- file.fileReplaced(patches);
- }
- }
- finally
- {
- graph.model.endUpdate();
- }
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.createFileData = function(node, graph, file, url, forceXml, forceSvg, forceHtml,
- embeddedCallback, ignoreSelection, compact, uncompressed, scale, border)
- {
- graph = (graph != null) ? graph : this.editor.graph;
- forceXml = (forceXml != null) ? forceXml : false;
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
- uncompressed = (uncompressed != null) ? uncompressed : !Editor.defaultCompressed;
-
- var editLink = null;
- var redirect = null;
-
- if (file == null || file.getMode() == App.MODE_DEVICE || file.getMode() == App.MODE_BROWSER)
- {
- editLink = '_blank';
- }
- else
- {
- editLink = url;
- redirect = editLink;
- }
- if (node == null)
- {
- return '';
- }
- else
- {
- var fileNode = node;
-
- // Ignores case for possible HTML or XML nodes
- if (fileNode.nodeName.toLowerCase() != 'mxfile')
- {
- if (uncompressed)
- {
- var diagramNode = node.ownerDocument.createElement('diagram');
- diagramNode.setAttribute('name', mxResources.get('pageWithNumber', [1]));
- diagramNode.setAttribute('id', Editor.guid());
- diagramNode.appendChild(node);
-
- fileNode = node.ownerDocument.createElement('mxfile');
- fileNode.appendChild(diagramNode);
- }
- else
- {
- // Removes control chars in input for correct roundtrip check
- var text = Graph.zapGremlins(mxUtils.getXml(node));
- var data = Graph.compress(text);
-
- // Fallback to plain XML for invalid compression
- // TODO: Remove this fallback with active pages
- if (Graph.decompress(data) != text)
- {
- return text;
- }
- else
- {
- var diagramNode = node.ownerDocument.createElement('diagram');
- diagramNode.setAttribute('name', mxResources.get('pageWithNumber', [1]));
- diagramNode.setAttribute('id', Editor.guid());
- mxUtils.setTextContent(diagramNode, data);
-
- fileNode = node.ownerDocument.createElement('mxfile');
- fileNode.appendChild(diagramNode);
- }
- }
- }
-
- if (!compact)
- {
- // Removes old metadata
- fileNode.removeAttribute('userAgent');
- fileNode.removeAttribute('modified');
- fileNode.removeAttribute('version');
- fileNode.removeAttribute('editor');
- fileNode.removeAttribute('pages');
- fileNode.removeAttribute('type');
- fileNode.removeAttribute('etag');
-
- if (mxClient.IS_CHROMEAPP)
- {
- fileNode.setAttribute('host', 'Chrome');
- }
- else if (EditorUi.isElectronApp)
- {
- fileNode.setAttribute('host', 'Electron');
- }
- else
- {
- fileNode.setAttribute('host', window.location.hostname);
- }
-
- // Adds new metadata
- fileNode.setAttribute('agent', (navigator.userAgent != null) ?
- navigator.userAgent : navigator.appVersion);
- fileNode.setAttribute('version', EditorUi.VERSION);
-
- if (fileNode.getElementsByTagName('diagram').length > 1 && this.pages != null)
- {
- fileNode.setAttribute('pages', this.pages.length);
- }
- }
- else
- {
- fileNode = fileNode.cloneNode(true);
- fileNode.removeAttribute('modified');
- fileNode.removeAttribute('host');
- fileNode.removeAttribute('agent');
- fileNode.removeAttribute('etag');
- fileNode.removeAttribute('userAgent');
- fileNode.removeAttribute('version');
- fileNode.removeAttribute('editor');
- fileNode.removeAttribute('type');
- }
- if (scale != null)
- {
- fileNode = fileNode.cloneNode(true);
- fileNode.setAttribute('scale', scale);
- }
- if (border != null)
- {
- fileNode = fileNode.cloneNode(true);
- fileNode.setAttribute('border', border);
- }
- var xml = (uncompressed) ? mxUtils.getPrettyXml(fileNode) : mxUtils.getXml(fileNode);
-
- // Writes the file as an embedded HTML file
- if (!forceSvg && !forceXml && (forceHtml || (file != null && /(\.html)$/i.test(file.getTitle()))))
- {
- xml = this.getHtml2(mxUtils.getXml(fileNode), graph, (file != null) ? file.getTitle() : null, editLink, redirect);
- }
- // Maps the XML data to the content attribute in the SVG node
- else if (forceSvg || (!forceXml && file != null && /(\.svg)$/i.test(file.getTitle())))
- {
- if (file != null && (file.getMode() == App.MODE_DEVICE || file.getMode() == App.MODE_BROWSER))
- {
- url = null;
- }
- var props = this.getSvgFileProperties(fileNode);
-
- xml = this.getEmbeddedSvg(xml, graph, url, null, embeddedCallback,
- ignoreSelection, redirect, null, null, props.scale, props.border);
- }
-
- return xml;
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getXmlFileData = function(ignoreSelection, currentPage, uncompressed, resolveReferences)
- {
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
- currentPage = (currentPage != null) ? currentPage : false;
- uncompressed = (uncompressed != null) ? uncompressed : !Editor.defaultCompressed;
-
- // Generats graph model XML node for single page export
- var node = this.editor.getGraphXml(ignoreSelection, resolveReferences);
-
- if (ignoreSelection && this.fileNode != null && this.currentPage != null)
- {
- // Updates current page XML if selection is ignored
- EditorUi.removeChildNodes(this.currentPage.node);
- this.currentPage.node.appendChild(node);
-
- // Creates a clone of the file node for processing
- node = this.fileNode.cloneNode(false);
- // Appends the node of the page and applies compression
- function appendPage(pageNode)
- {
- var models = pageNode.getElementsByTagName('mxGraphModel');
- var modelNode = (models.length > 0) ? models[0] : null;
- var clone = pageNode;
-
- if (modelNode == null && uncompressed)
- {
- var text = mxUtils.getNodeValue(pageNode);
-
- if (text.length > 0)
- {
- var tmp = Graph.decompress(text);
-
- if (tmp != null && tmp.length > 0)
- {
- clone = pageNode.cloneNode(false);
- clone.appendChild(mxUtils.parseXml(tmp).documentElement);
- }
- }
- }
- else if (modelNode != null && !uncompressed)
- {
- clone = pageNode.cloneNode(false);
- mxUtils.setTextContent(clone, Graph.compressNode(modelNode));
- }
- else
- {
- clone = pageNode.cloneNode(true);
- }
-
- node.appendChild(clone);
- };
- if (currentPage)
- {
- appendPage(this.currentPage.node);
- }
- else
- {
- // Restores order of pages
- for (var i = 0; i < this.pages.length; i++)
- {
- var page = this.pages[i];
- var currNode = page.node;
- if (page != this.currentPage)
- {
- if (page.needsUpdate)
- {
- var enc = new mxCodec(mxUtils.createXmlDocument());
- var temp = enc.encode(new mxGraphModel(page.root));
- this.editor.graph.saveViewState(page.viewState,
- temp, null, resolveReferences);
- EditorUi.removeChildNodes(currNode);
- currNode.appendChild(temp);
- // Marks the page as up-to-date
- delete page.needsUpdate;
- }
- else if (resolveReferences)
- {
- this.updatePageRoot(page);
- // Forces update of background page image in offscreen page
- if (page.viewState.backgroundImage != null)
- {
- if (page.viewState.backgroundImage.originalSrc != null)
- {
- page.viewState.backgroundImage = this.createImageForPageLink(
- page.viewState.backgroundImage.originalSrc, page);
- }
- else if (Graph.isPageLink(page.viewState.backgroundImage.src))
- {
- page.viewState.backgroundImage = this.createImageForPageLink(
- page.viewState.backgroundImage.src, page);
- }
- }
- // Updates the page node
- if (page.viewState.backgroundImage != null &&
- page.viewState.backgroundImage.originalSrc != null)
- {
- var enc = new mxCodec(mxUtils.createXmlDocument());
- var temp = enc.encode(new mxGraphModel(page.root));
- this.editor.graph.saveViewState(page.viewState,
- temp, null, resolveReferences);
- currNode = currNode.cloneNode(false);
- currNode.appendChild(temp);
- }
- }
- }
-
- appendPage(currNode);
- }
- }
- }
-
- return node;
- };
-
- /**
- * Removes any values, styles and geometries from the given XML node.
- */
- EditorUi.prototype.anonymizeString = function(text, zeros)
- {
- var result = [];
-
- for (var i = 0; i < text.length; i++)
- {
- var c = text.charAt(i);
-
- if (EditorUi.ignoredAnonymizedChars.indexOf(c) >= 0)
- {
- result.push(c);
- }
- else if (!isNaN(parseInt(c)))
- {
- result.push((zeros) ? '0' : Math.round(Math.random() * 9));
- }
- else if (c.toLowerCase() != c)
- {
- result.push(String.fromCharCode(65 + Math.round(Math.random() * 25)));
- }
- else if (c.toUpperCase() != c)
- {
- result.push(String.fromCharCode(97 + Math.round(Math.random() * 25)));
- }
- else if (/\s/.test(c))
- {
- /* any whitespace */
- result.push(' ');
- }
- else
- {
- result.push('?');
- }
- }
-
- return result.join('');
- };
-
- /**
- * Removes any values, styles and geometries from the given XML node.
- */
- EditorUi.prototype.anonymizePatch = function(patch)
- {
- if (patch[EditorUi.DIFF_INSERT] != null)
- {
- for (var i = 0; i < patch[EditorUi.DIFF_INSERT].length; i++)
- {
- try
- {
- var data = patch[EditorUi.DIFF_INSERT][i].data;
- var doc = mxUtils.parseXml(data);
- var clone = doc.documentElement.cloneNode(false);
-
- if (clone.getAttribute('name') != null)
- {
- clone.setAttribute('name', this.anonymizeString(clone.getAttribute('name')));
- }
-
- patch[EditorUi.DIFF_INSERT][i].data = mxUtils.getXml(clone);
- }
- catch (e)
- {
- patch[EditorUi.DIFF_INSERT][i].data = e.message;
- }
- }
- }
-
- if (patch[EditorUi.DIFF_UPDATE] != null)
- {
- for (var pageId in patch[EditorUi.DIFF_UPDATE])
- {
- var diff = patch[EditorUi.DIFF_UPDATE][pageId];
-
- if (diff.name != null)
- {
- diff.name = this.anonymizeString(diff.name);
- }
-
- if (diff.cells != null)
- {
- var anonymizeCellDiffs = mxUtils.bind(this, function(key)
- {
- var cellDiffs = diff.cells[key];
-
- if (cellDiffs != null)
- {
- for (var cellId in cellDiffs)
- {
- if (cellDiffs[cellId].value != null)
- {
- cellDiffs[cellId].value = '[' +
- cellDiffs[cellId].value.length + ']';
- }
- if (cellDiffs[cellId].xmlValue != null)
- {
- cellDiffs[cellId].xmlValue = '[' +
- cellDiffs[cellId].xmlValue.length + ']';
- }
-
- if (cellDiffs[cellId].style != null)
- {
- cellDiffs[cellId].style = '[' +
- cellDiffs[cellId].style.length + ']';
- }
-
- if (mxUtils.isEmptyObject(cellDiffs[cellId]))
- {
- delete cellDiffs[cellId];
- }
- }
-
- if (mxUtils.isEmptyObject(cellDiffs))
- {
- delete diff.cells[key];
- }
- }
- });
-
- anonymizeCellDiffs(EditorUi.DIFF_INSERT);
- anonymizeCellDiffs(EditorUi.DIFF_UPDATE);
-
- if (mxUtils.isEmptyObject(diff.cells))
- {
- delete diff.cells;
- }
- }
-
- if (mxUtils.isEmptyObject(diff))
- {
- delete patch[EditorUi.DIFF_UPDATE][pageId];
- }
- }
-
- if (mxUtils.isEmptyObject(patch[EditorUi.DIFF_UPDATE]))
- {
- delete patch[EditorUi.DIFF_UPDATE];
- }
- }
-
- return patch;
- };
- /**
- * Removes any values, styles and geometries from the given XML node.
- */
- EditorUi.prototype.anonymizeAttributes = function(node, zeros)
- {
- if (node.attributes != null)
- {
- for (var i = 0; i < node.attributes.length; i++)
- {
- if (node.attributes[i].name != 'as')
- {
- node.setAttribute(node.attributes[i].name,
- this.anonymizeString(node.attributes[i].value, zeros));
- }
- }
- }
-
- if (node.childNodes != null)
- {
- for (var i = 0; i < node.childNodes.length; i++)
- {
- this.anonymizeAttributes(node.childNodes[i], zeros);
- }
- }
- };
-
- /**
- * Removes any values, styles and geometries from the given XML node.
- */
- EditorUi.prototype.anonymizeNode = function(node, zeros)
- {
- var nodes = node.getElementsByTagName('mxCell');
-
- for (var i = 0; i < nodes.length; i++)
- {
- if (nodes[i].getAttribute('value') != null)
- {
- nodes[i].setAttribute('value', '[' + nodes[i].getAttribute('value').length + ']');
- }
- if (nodes[i].getAttribute('xmlValue') != null)
- {
- nodes[i].setAttribute('xmlValue', '[' + nodes[i].getAttribute('xmlValue').length + ']');
- }
-
- if (nodes[i].getAttribute('style') != null)
- {
- nodes[i].setAttribute('style', '[' + nodes[i].getAttribute('style').length + ']');
- }
-
- if (nodes[i].parentNode != null && nodes[i].parentNode.nodeName != 'root' &&
- nodes[i].parentNode.parentNode != null)
- {
- nodes[i].setAttribute('id', nodes[i].parentNode.getAttribute('id'));
- nodes[i].parentNode.parentNode.replaceChild(nodes[i], nodes[i].parentNode);
- }
- }
-
- return node;
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.synchronizeCurrentFile = function(forceReload)
- {
- var currentFile = this.getCurrentFile();
-
- if (currentFile != null)
- {
- if (currentFile.savingFile)
- {
- this.handleError({message: mxResources.get('busy')});
- }
- else if (!forceReload && currentFile.invalidChecksum)
- {
- currentFile.handleFileError(null, true);
- }
- else if (this.spinner.spin(document.body, mxResources.get('updatingDocument')))
- {
- currentFile.clearAutosave();
- this.editor.setStatus('');
-
- if (forceReload)
- {
- currentFile.reloadFile(mxUtils.bind(this, function()
- {
- this.spinner.stop();
- currentFile.handleFileSuccess(DrawioFile.SYNC == 'manual');
- }), mxUtils.bind(this, function(err)
- {
- this.spinner.stop();
- currentFile.handleFileError(err, true);
- }));
- }
- else
- {
- currentFile.synchronizeFile(mxUtils.bind(this, function()
- {
- currentFile.handleFileSuccess(DrawioFile.SYNC == 'manual');
- }), mxUtils.bind(this, function(err)
- {
- currentFile.handleFileError(err, true);
- }));
- }
- }
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getFileData = function(forceXml, forceSvg, forceHtml, embeddedCallback,
- ignoreSelection, currentPage, node, compact, file, uncompressed, resolveReferences,
- scale, border)
- {
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
- currentPage = (currentPage != null) ? currentPage : false;
- uncompressed = (uncompressed != null) ? uncompressed : !Editor.defaultCompressed;
- var graph = this.editor.graph;
-
- // Forces compression of embedded XML
- if (forceSvg || (!forceXml && file != null && /(\.svg)$/i.test(file.getTitle())))
- {
- var darkTheme = graph.themes != null && graph.defaultThemeName == 'darkTheme';
- // Exports SVG for first page while other page is visible by creating a graph
- // LATER: Add caching for the graph or SVG while not on first page
- // Dark mode requires a refresh that would destroy all handlers
- // LATER: Use dark theme here to bypass refresh
- if (darkTheme || (this.pages != null && this.currentPage != this.pages[0]))
- {
- var graphGetGlobalVariable = graph.getGlobalVariable;
- graph = this.createTemporaryGraph(darkTheme ?
- graph.getDefaultStylesheet() :
- graph.getStylesheet());
- graph.setBackgroundImage = this.editor.graph.setBackgroundImage;
- graph.background = this.editor.graph.background;
- var page = (this.pages != null) ? this.pages[0] : null;;
- if (page == null || this.currentPage == page)
- {
- graph.setBackgroundImage(this.editor.graph.backgroundImage);
- }
- else if (page.viewState != null && page.viewState != null)
- {
- graph.setBackgroundImage(page.viewState.backgroundImage);
- }
- graph.getGlobalVariable = function(name)
- {
- if (name == 'page' && page != null)
- {
- return page.getName();
- }
- else if (name == 'pagenumber')
- {
- return 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
-
- document.body.appendChild(graph.container);
- if (page != null)
- {
- graph.model.setRoot(page.root);
- }
- }
- }
- node = (node != null) ? node : this.getXmlFileData(ignoreSelection,
- currentPage, uncompressed, resolveReferences);
- file = (file != null) ? file : this.getCurrentFile();
- var result = this.createFileData(node, graph, file, window.location.href,
- forceXml, forceSvg, forceHtml, embeddedCallback, ignoreSelection, compact,
- uncompressed, scale, border);
-
- // Removes temporary graph from DOM
- if (graph != this.editor.graph)
- {
- graph.container.parentNode.removeChild(graph.container);
- }
-
- return result;
- };
-
- /**
- *
- */
- EditorUi.prototype.getHtml = function(node, graph, title, editLink, redirect, ignoreSelection)
- {
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
- var bg = null;
- var js = EditorUi.drawHost + '/js/embed-static.min.js';
-
- // LATER: Merge common code with EmbedDialog
- if (graph != null)
- {
- var bounds = (ignoreSelection) ? graph.getGraphBounds() :
- graph.getBoundingBox(graph.getSelectionCells());
- var scale = graph.view.scale;
- var x0 = Math.floor(bounds.x / scale - graph.view.translate.x);
- var y0 = Math.floor(bounds.y / scale - graph.view.translate.y);
- bg = graph.background;
-
- // Embed script only used if no redirect
- if (redirect == null)
- {
- var s = this.getBasenames().join(';');
-
- if (s.length > 0)
- {
- js = EditorUi.drawHost + '/embed.js?s=' + s;
- }
- }
-
- // Adds embed attributes
- node.setAttribute('x0', x0);
- node.setAttribute('y0', y0);
- }
-
- if (node != null)
- {
- node.setAttribute('pan', '1');
- node.setAttribute('zoom', '1');
- node.setAttribute('resize', '0');
- node.setAttribute('fit', '0');
- node.setAttribute('border', '20');
-
- // Hidden attributes
- node.setAttribute('links', '1');
-
- if (editLink != null)
- {
- node.setAttribute('edit', editLink);
- }
- }
-
- // Makes XHTML compatible
- if (redirect != null)
- {
- redirect = redirect.replace(/&/g, '&');
- }
-
- // Removes control chars in input for correct roundtrip check
- var text = (node != null) ? Graph.zapGremlins(mxUtils.getXml(node)) : '';
-
- // Double compression for mxfile not fixed since it may cause imcompatibilites with
- // embed clients that rely on this format. HTML files and export use getHtml2.
- var data = Graph.compress(text);
-
- // Fallback to URI encoded XML for invalid compression
- if (Graph.decompress(data) != text)
- {
- data = encodeURIComponent(text);
- }
-
- var style = 'position:relative;overflow:auto;width:100%;';
-
- return ((redirect == null) ? '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=9" ><![endif]-->\n' : '') +
- '<!DOCTYPE html>\n<html' + ((redirect != null) ? ' xmlns="http://www.w3.org/1999/xhtml">' : '>') +
- '\n<head>\n' + ((redirect == null) ? ((title != null) ? '<title>' + mxUtils.htmlEntities(title) +
- '</title>\n' : '') : '<title>draw.io</title>\n') +
- ((redirect != null) ? '<meta http-equiv="refresh" content="0;URL=\'' + redirect + '\'"/>\n' : '') +
- '</head>\n<body' +
- (((redirect == null && bg != null && bg != mxConstants.NONE) ? ' style="background-color:' + bg + ';">' : '>')) +
- '\n<div class="mxgraph" style="' + style + '">\n' +
- '<div style="width:1px;height:1px;overflow:hidden;">' + data + '</div>\n</div>\n' +
- ((redirect == null) ? '<script type="text/javascript" src="' + js + '"></script>' :
- '<a style="position:absolute;top:50%;left:50%;margin-top:-128px;margin-left:-64px;" ' +
- 'href="' + redirect + '" target="_blank"><img border="0" ' +
- 'src="' + EditorUi.drawHost + '/images/drawlogo128.png"/></a>') +
- '\n</body>\n</html>\n';
- };
-
- /**
- * Same as above but using the new embed code.
- */
- EditorUi.prototype.getHtml2 = function(xml, graph, title, editLink, redirect)
- {
- var js = window.DRAWIO_VIEWER_URL || EditorUi.drawHost + '/js/viewer-static.min.js';
-
- // Makes XHTML compatible
- if (redirect != null)
- {
- redirect = redirect.replace(/&/g, '&');
- }
-
- var data = {highlight: '#0000ff', nav: this.editor.graph.foldingEnabled, resize: true,
- xml: Graph.zapGremlins(xml), toolbar: 'pages zoom layers lightbox'};
-
- if (this.pages != null && this.currentPage != null)
- {
- data.page = mxUtils.indexOf(this.pages, this.currentPage);
- }
-
- var style = 'max-width:100%;border:1px solid transparent;';
-
- return ((redirect == null) ? '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=9" ><![endif]-->\n' : '') +
- '<!DOCTYPE html>\n<html' + ((redirect != null) ? ' xmlns="http://www.w3.org/1999/xhtml">' : '>') +
- '\n<head>\n' + ((redirect == null) ? ((title != null) ? '<title>' + mxUtils.htmlEntities(title) +
- '</title>\n' : '') : '<title>draw.io</title>\n') +
- ((redirect != null) ? '<meta http-equiv="refresh" content="0;URL=\'' + redirect + '\'"/>\n' : '') +
- '<meta charset="utf-8"/>\n</head>\n<body>' +
- '\n<div class="mxgraph" style="' + style + '" data-mxgraph="' + mxUtils.htmlEntities(JSON.stringify(data)) + '"></div>\n' +
- ((redirect == null) ? '<script type="text/javascript" src="' + js + '"></script>' :
- '<a style="position:absolute;top:50%;left:50%;margin-top:-128px;margin-left:-64px;" ' +
- 'href="' + redirect + '" target="_blank"><img border="0" ' +
- 'src="' + EditorUi.drawHost + '/images/drawlogo128.png"/></a>') +
- '\n</body>\n</html>\n';
- };
- /**
- *
- */
- EditorUi.prototype.setFileData = function(data)
- {
- data = this.validateFileData(data);
- this.currentPage = null;
- this.fileNode = null;
- this.pages = null;
- var node = (data != null && data.length > 0) ? mxUtils.parseXml(data).documentElement : null;
-
- // Checks for parser errors
- var cause = Editor.extractParserError(node, mxResources.get('invalidOrMissingFile'));
-
- if (cause)
- {
- EditorUi.debug('EditorUi.setFileData ParserError', [this],
- 'data', [data], 'node', [node], 'cause', [cause]);
- throw new Error(mxResources.get('notADiagramFile') + ' (' + cause + ')');
- }
- else
- {
- // Some nodes must be extracted here to find the mxfile node
- // LATER: Remove duplicate call to extractGraphModel in overridden setGraphXml
- var tmp = (node != null) ? this.editor.extractGraphModel(node, true) : null;
-
- if (tmp != null)
- {
- node = tmp;
- }
- if (node != null && node.nodeName == 'mxfile')
- {
- var nodes = node.getElementsByTagName('diagram');
- // Checks for duplicate page IDs
- var pages = {};
- if (nodes.length > 0)
- {
- var hashObj = this.getHashObject();
- var selectedPage = null;
- this.fileNode = node;
- this.pages = [];
-
- // Wraps page nodes
- for (var i = 0; i < nodes.length; i++)
- {
- // Adds page ID based on page order to match
- // remote IDs given if IDs are missing here
- if (nodes[i].getAttribute('id') == null)
- {
- nodes[i].setAttribute('id', i);
- }
-
- var page = new DiagramPage(nodes[i]);
-
- // Checks for invalid page names
- if (page.getName() == null)
- {
- page.setName(mxResources.get('pageWithNumber', [i + 1]));
- }
-
- this.pages.push(page);
-
- if ((hashObj.pageId == null && urlParams['page-id'] != null &&
- page.getId() == urlParams['page-id']) ||
- (hashObj.pageId != null && page.getId() == hashObj.pageId))
- {
- selectedPage = page;
- }
- if (pages[page.getId()] == null)
- {
- pages[page.getId()] = page;
- }
- else
- {
- throw new Error(page.getId() + ': Duplicate page ID');
- }
- }
-
- this.currentPage = (selectedPage != null) ? selectedPage :
- this.pages[Math.max(0, Math.min(this.pages.length - 1, urlParams['page'] || 0))];
- node = this.currentPage.node;
- }
- }
-
- // Creates tabbed file structure if enforced by URL
- if (this.fileNode == null && node != null)
- {
- this.fileNode = node.ownerDocument.createElement('mxfile');
- this.currentPage = new DiagramPage(node.ownerDocument.createElement('diagram'));
- this.currentPage.setName(mxResources.get('pageWithNumber', [1]));
- this.pages = [this.currentPage];
- }
-
- // Avoids scroll offset when switching page
- this.editor.setGraphXml(node);
-
- // Avoids duplicate parsing of the XML stored in the node
- if (this.currentPage != null)
- {
- this.currentPage.root = this.editor.graph.model.root;
- // Resets initial modified state
- this.currentPage.setDiagramModified(false);
-
- // Scrolls to current page
- this.scrollToPage();
- }
-
- if (urlParams['layer-ids'] != null)
- {
- try
- {
- var layerIds = decodeURIComponent(urlParams['layer-ids']).split(' ');
- var layerIdsMap = {};
-
- for (var i = 0; i < layerIds.length; i++)
- {
- layerIdsMap[layerIds[i]] = true;
- }
-
- var model = this.editor.graph.getModel();
- var children = model.getChildren(model.root);
-
- // handle layers visibility
- for (var i = 0; i < children.length; i++)
- {
- var child = children[i];
- model.setVisible(child, layerIdsMap[child.id] || false);
- }
- }
- catch(e){} //ignore
- }
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getBaseFilename = function(ignorePageName)
- {
- var file = this.getCurrentFile();
- var basename = (file != null && file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
-
- if (/(\.xml)$/i.test(basename) || /(\.html)$/i.test(basename) ||
- /(\.svg)$/i.test(basename) || /(\.png)$/i.test(basename))
- {
- basename = basename.substring(0, basename.lastIndexOf('.'));
- }
-
- if (/(\.drawio)$/i.test(basename))
- {
- basename = basename.substring(0, basename.lastIndexOf('.'));
- }
- if (!ignorePageName && this.pages != null && this.pages.length > 1 &&
- this.currentPage != null && this.currentPage.node.getAttribute('name') != null &&
- this.currentPage.getName().length > 0)
- {
- basename = basename + '-' + this.currentPage.getName();
- }
-
- return basename;
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.downloadFile = function(format, uncompressed, addShadow, ignoreSelection,
- currentPage, pageVisible, transparent, scale, border, grid, includeXml, pageRange, margin,
- fit, sheetsAcross, sheetsDown)
- {
- try
- {
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : this.editor.graph.isSelectionEmpty();
- var basename = this.getBaseFilename(!currentPage);
- var filename = basename + ((format == 'xml' || (format == 'pdf' &&
- includeXml)) ? '.drawio' : '') + '.' + format;
-
- if (format == 'xml')
- {
- var data = Graph.xmlDeclaration +'\n' +
- this.getFileData(true, null, null, null, ignoreSelection, currentPage,
- null, null, null, uncompressed);
-
- this.saveData(filename, format, data, 'text/xml');
- }
- else if (format == 'html')
- {
- var data = this.getHtml2(this.getFileData(true), this.editor.graph, basename);
- this.saveData(filename, format, data, 'text/html');
- }
- else if ((format == 'svg' || format == 'xmlsvg') && this.spinner.spin(document.body, mxResources.get('export')))
- {
- var svg = null;
-
- var saveSvg = mxUtils.bind(this, function(data)
- {
- if (data.length <= MAX_REQUEST_SIZE)
- {
- this.saveData(filename, 'svg', data, 'image/svg+xml');
- }
- else
- {
- this.handleError({message: mxResources.get('drawingTooLarge')}, mxResources.get('error'), mxUtils.bind(this, function()
- {
- mxUtils.popup(svg);
- }));
- }
- });
-
- if (format == 'svg')
- {
- var bg = this.editor.graph.background;
-
- if (transparent || bg == mxConstants.NONE)
- {
- bg = null;
- }
-
- // Sets or disables alternate text for foreignObjects. Disabling is needed
- // because PhantomJS seems to ignore switch statements and paint all text.
- var svgRoot = this.editor.graph.getSvg(bg, null, null, null, null, ignoreSelection);
-
- if (addShadow)
- {
- this.editor.graph.addSvgShadow(svgRoot);
- }
-
- // Embeds the images in the SVG output (async)
- this.editor.convertImages(svgRoot, mxUtils.bind(this, mxUtils.bind(this, function(svgRoot2)
- {
- this.spinner.stop();
-
- saveSvg(Graph.xmlDeclaration + '\n' + Graph.svgDoctype + '\n' + mxUtils.getXml(svgRoot2));
- })));
- }
- else
- {
- filename = basename + '.svg';
-
- svg = this.getFileData(false, true, null, mxUtils.bind(this, function(svg)
- {
- this.spinner.stop();
- saveSvg(svg);
- }), ignoreSelection);
- }
- }
- else
- {
- var w, h;
- if (format == 'xmlpng')
- {
- filename = basename + '.png';
- }
- else if (format == 'jpeg')
- {
- filename = basename + '.jpg';
- }
-
- this.saveRequest(filename, format, mxUtils.bind(this, function(newTitle, base64)
- {
- try
- {
- var req = this.createDownloadRequest(newTitle, format, ignoreSelection, base64,
- transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h,
- !pageVisible, margin, fit, sheetsAcross, sheetsDown);
-
- return req;
- }
- catch (e)
- {
- this.handleError(e);
- }
- }));
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- };
-
- // Note: Remember to adjust ElectronApp override when this function is modified
- EditorUi.prototype.createDownloadRequest = function(filename, format, ignoreSelection, base64,
- transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h, crop, margin,
- fit, sheetsAcross, sheetsDown)
- {
- var params = this.downloadRequestBuilder(filename, format, ignoreSelection, base64,
- transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h, crop,
- margin, fit, sheetsAcross, sheetsDown);
- var paramsStr = '';
- for (var p in params)
- {
- var val = params[p];
- if (val != null)
- {
- paramsStr += p + '=' + encodeURIComponent(val) + '&';
- }
- }
- return new mxXmlRequest(EXPORT_URL, paramsStr);
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.downloadRequestBuilder = function(filename, format, ignoreSelection, base64,
- transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h, crop, margin,
- fit, sheetsAcross, sheetsDown)
- {
- var graph = this.editor.graph;
- var bounds = graph.getGraphBounds();
- // Exports only current page for images that does not contain file data, but for
- // the other formats with XML included or pdf with all pages, we need to send the complete data and use
- // the from/to URL parameters to specify the page to be exported.
- var data = this.getFileData(true, null, null, null, ignoreSelection,
- currentPage == false ? false : format != 'xmlpng', null, null,
- null, !Editor.defaultCompressed, format == 'pdf');
- var from = null, to = null, allPages = null;
- if (!EditorUi.isElectronApp && (bounds.width * bounds.height > MAX_AREA || data.length > MAX_REQUEST_SIZE))
- {
- throw {message: mxResources.get('drawingTooLarge')};
- }
-
- var embed = (includeXml) ? '1' : '0';
-
- if (format == 'pdf')
- {
- if (pageRange != null)
- {
- from = pageRange.from;
- to = pageRange.to;
- }
- else if (currentPage == false)
- {
- allPages = '1';
- }
- }
-
- if (format == 'xmlpng')
- {
- embed = '1';
- format = 'png';
- }
-
- if (format == 'xmlpng' || format == 'svg')
- {
- // Finds the current page number
- if (this.pages != null && this.currentPage != null)
- {
- for (var i = 0; i < this.pages.length; i++)
- {
- if (this.pages[i] == this.currentPage)
- {
- from = i;
- break;
- }
- }
- }
- }
-
- var bg = graph.background;
-
- if ((format == 'png' || format == 'pdf' || format == 'svg') && transparent)
- {
- bg = mxConstants.NONE;
- }
- else if (!transparent && (bg == null || bg == mxConstants.NONE))
- {
- bg = '#ffffff';
- }
-
- var extras = {globalVars: graph.getExportVariables()};
-
- if (grid)
- {
- extras.grid = {
- size: graph.gridSize,
- steps: graph.view.gridSteps,
- color: (Editor.isDarkMode() && format == 'pdf') ?
- mxGraphView.prototype.defaultGridColor : graph.view.gridColor
- };
- }
-
- if (Graph.translateDiagram)
- {
- extras.diagramLanguage = Graph.diagramLanguage;
- }
-
- return {
- format: format,
- from: from,
- to: to,
- allPages: allPages,
- bg: ((bg != null) ? bg : mxConstants.NONE),
- base64: base64,
- embedXml: embed,
- xml: data,
- filename: ((filename != null) ? filename : ''),
- extras: JSON.stringify(extras),
- scale: scale,
- border: border,
- pageMargin: margin,
- w: (w && isFinite(w)? w : null),
- h: (h && isFinite(h)? h : null),
- crop: (crop != null && crop) ? '1' : '0',
- fit: (fit != null && fit) ? '1' : '0',
- sheetsAcross: sheetsAcross,
- sheetsDown: sheetsDown
- };
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.setMode = function(mode, remember)
- {
- this.mode = mode;
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getDiagramId = function()
- {
- var id = window.location.hash;
-
- // Strips the hash sign
- if (id != null && id.length > 0)
- {
- id = id.substring(1);
- }
- // Removes additional parameters after trailing hash
- if (id != null && id.length > 1)
- {
- var idx = id.indexOf('#');
-
- if (idx >= 0)
- {
- id = id.substring(0, idx);
- }
- }
-
- return id;
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getHashObject = function()
- {
- var id = window.location.hash;
- var result = {};
- if (id != null && id.length > 0)
- {
- var last = id.lastIndexOf('#');
- if (last > 0)
- {
- var temp = decodeURIComponent(id.substring(last + 1));
- try
- {
- result = JSON.parse(temp);
- }
- catch (e)
- {
- // ignore
- }
- }
- }
- return result;
- };
- /**
- * Updates the hash object with the current page id.
- */
- EditorUi.prototype.updateHashObject = function()
- {
- if (this.currentFile != null && this.currentPage != null &&
- this.currentFile.getHash() != '')
- {
- var obj = this.getHashObject();
- obj.pageId = this.currentPage.getId();
- this.setHashObject(obj);
- }
- else
- {
- this.setHashObject(null);
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.setHashObject = function(obj)
- {
- if (Editor.enableHashObjects)
- {
- var id = window.location.hash;
- if (id == null || id == '')
- {
- id = '#';
- }
- var last = id.lastIndexOf('#');
- if (last > 0)
- {
- id = id.substring(0, last);
- }
-
- try
- {
- if (obj != null && !mxUtils.isEmptyObject(obj))
- {
- id = id + '#' + encodeURIComponent(JSON.stringify(obj));
- }
- }
- catch (e)
- {
- // ignore
- }
-
- // Adds to browsing history if hash object changed
- if (last > 0 && id.lastIndexOf('#') > 0)
- {
- window.location.hash = id;
- }
- else
- {
- window.location.replace(id);
- }
- }
- };
- /**
- * Loads the given file descriptor. The descriptor may define the following properties:
- *
- * - url: The url to load the data from (proxy is used if CORS is not enabled)
- * - data: The data to be inserted. If both, data and url are defined, then the data
- * is preprendended to the data returned from the given URL.
- * - format: Currently, only 'csv' is supported as an optional value. Default is XML.
- * - update: Optional URL to fetch updates from (POST request with the page XML).
- * - interval: Optional interval for fetching updates. Default is 60000 (60 seconds).
- */
- EditorUi.prototype.loadDescriptor = function(desc, success, error)
- {
- var hash = window.location.hash;
-
- var loadData = mxUtils.bind(this, function(data)
- {
- var realData = (desc.data != null) ? desc.data : '';
-
- if (data != null && data.length > 0)
- {
- if (realData.length > 0)
- {
- realData += '\n';
- }
-
- realData += data;
- }
- var xml = (desc.format != 'csv' && realData.length > 0) ? realData : this.emptyDiagramXml;
- var tempFile = new LocalFile(this, xml, (urlParams['title'] != null) ?
- decodeURIComponent(urlParams['title']) : this.defaultFilename, true);
- tempFile.getHash = function()
- {
- return hash;
- };
- this.fileLoaded(tempFile);
-
- if (desc.format == 'csv')
- {
- this.importCsv(realData, mxUtils.bind(this, function(cells)
- {
- this.editor.undoManager.clear();
- this.editor.setModified(false);
- this.editor.setStatus('');
- }));
- }
-
- // Installs updates
- if (desc.update != null)
- {
- var interval = (desc.interval != null) ? parseInt(desc.interval) : 60000;
- var currentThread = null;
-
- var doUpdate = mxUtils.bind(this, function()
- {
- var page = this.currentPage;
-
- mxUtils.post(desc.update, 'xml=' + encodeURIComponent(
- mxUtils.getXml(this.editor.getGraphXml())),
- mxUtils.bind(this, function(req)
- {
- if (page === this.currentPage)
- {
- if (req.getStatus() >= 200 && req.getStatus() <= 300)
- {
- var doc = this.updateDiagram(req.getText());
- schedule();
- }
- else
- {
- this.handleError({message: mxResources.get('error') + ' ' + req.getStatus()});
- }
- }
- }), mxUtils.bind(this, function(err)
- {
- this.handleError(err);
- }));
- });
-
- var schedule = mxUtils.bind(this, function()
- {
- window.clearTimeout(currentThread);
- currentThread = window.setTimeout(doUpdate, interval);
- });
-
- this.editor.addListener('pageSelected', mxUtils.bind(this, function()
- {
- schedule();
- doUpdate();
- }));
-
- schedule();
- doUpdate();
- }
-
- if (success != null)
- {
- success();
- }
- });
-
- if (desc.url != null && desc.url.length > 0)
- {
- var url = this.editor.getProxiedUrl(desc.url);
-
- // LATER: Remove cache-control header
- this.editor.loadUrl(url, mxUtils.bind(this, function(data)
- {
- loadData(data);
- }), mxUtils.bind(this, function(err)
- {
- if (error != null)
- {
- error(err)
- }
- }));
- }
- else
- {
- loadData('');
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.updateDiagram = function(xml)
- {
- var doc = null;
- var ui = this;
-
- function createOverlay(desc)
- {
- var overlay = new mxCellOverlay(desc.image || graph.warningImage,
- desc.tooltip, desc.align, desc.valign, desc.offset);
- // Installs a handler for clicks on the overlay
- overlay.addListener(mxEvent.CLICK, function(sender, evt)
- {
- ui.alert(desc.tooltip);
- });
-
- return overlay;
- };
-
- if (xml != null && xml.length > 0)
- {
- doc = mxUtils.parseXml(xml);
- var node = (doc != null) ? doc.documentElement : null;
-
- if (node != null && node.nodeName == 'updates')
- {
- var graph = this.editor.graph;
- var model = graph.getModel();
- model.beginUpdate();
- var fit = null;
- try
- {
- node = node.firstChild;
-
- while (node != null)
- {
- if (node.nodeName == 'update')
- {
- // Resolves the cell ID
- var cell = model.getCell(node.getAttribute('id'));
-
- if (cell != null)
- {
- // Changes the value
- try
- {
- var value = node.getAttribute('value');
-
- if (value != null)
- {
- var valueNode = mxUtils.parseXml(value).documentElement;
- if (valueNode != null)
- {
- if (valueNode.getAttribute('replace-value') == '1')
- {
- model.setValue(cell, valueNode);
- }
- else
- {
- var attrs = valueNode.attributes;
-
- for (var j = 0; j < attrs.length; j++)
- {
- graph.setAttributeForCell(cell, attrs[j].nodeName,
- (attrs[j].nodeValue.length > 0) ? attrs[j].nodeValue : null);
- }
- }
- }
- }
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.log('Error in value for ' + cell.id + ': ' + e);
- }
- }
-
- // Changes the style
- try
- {
- var style = node.getAttribute('style');
-
- if (style != null)
- {
- graph.model.setStyle(cell, style);
- }
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.log('Error in style for ' + cell.id + ': ' + e);
- }
- }
-
- // Adds or removes an overlay icon
- try
- {
- var icon = node.getAttribute('icon');
-
- if (icon != null)
- {
- var desc = (icon.length > 0) ? JSON.parse(icon) : null;
-
- if (desc == null || !desc.append)
- {
- graph.removeCellOverlays(cell);
- }
-
- if (desc != null)
- {
- graph.addCellOverlay(cell, createOverlay(desc));
- }
- }
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.log('Error in icon for ' + cell.id + ': ' + e);
- }
- }
-
- // Replaces the geometry
- try
- {
- var geo = node.getAttribute('geometry');
-
- if (geo != null)
- {
- geo = JSON.parse(geo);
- var curr = graph.getCellGeometry(cell);
-
- if (curr != null)
- {
- curr = curr.clone();
-
- // Partially overwrites geometry
- for (key in geo)
- {
- var val = parseFloat(geo[key]);
-
- if (key == 'dx')
- {
- curr.x += val;
- }
- else if (key == 'dy')
- {
- curr.y += val;
- }
- else if (key == 'dw')
- {
- curr.width += val;
- }
- else if (key == 'dh')
- {
- curr.height += val;
- }
- else
- {
- curr[key] = parseFloat(geo[key]);
- }
- }
-
- graph.model.setGeometry(cell, curr);
- }
- }
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.log('Error in icon for ' + cell.id + ': ' + e);
- }
- }
- } // if cell != null
- } // if node.nodeName == 'update
- else if (node.nodeName == 'model')
- {
- // Finds first child element
- var dataNode = node.firstChild;
-
- while (dataNode != null && dataNode.nodeType != mxConstants.NODETYPE_ELEMENT)
- {
- dataNode = dataNode.nextSibling;
- }
-
- if (dataNode != null)
- {
- var dec = new mxCodec(node.firstChild);
- dec.decode(dataNode, model);
- }
- }
- else if (node.nodeName == 'view')
- {
- if (node.hasAttribute('scale'))
- {
- graph.view.scale = parseFloat(node.getAttribute('scale'));
- }
-
- if (node.hasAttribute('dx') || node.hasAttribute('dy'))
- {
- graph.view.translate = new mxPoint(parseFloat(node.getAttribute('dx') || 0),
- parseFloat(node.getAttribute('dy') || 0));
- }
- }
- else if (node.nodeName == 'fit')
- {
- if (node.hasAttribute('max-scale'))
- {
- fit = parseFloat(node.getAttribute('max-scale'));
- }
- else
- {
- fit = 1;
- }
- }
-
- node = node.nextSibling;
- } // end of while
- }
- finally
- {
- model.endUpdate();
- }
-
- if (fit != null && this.chromelessResize)
- {
- this.chromelessResize(true, fit);
- }
- }
- }
-
- return doc;
- };
-
- /**
- * Constructs a filename for a copy of the given file.
- */
- EditorUi.prototype.getCopyFilename = function(file, timestamp)
- {
- var title = (file != null && file.getTitle() != null) ?
- file.getTitle() : this.defaultFilename;
-
- // Handles extension
- var extension = '';
- var dot = title.lastIndexOf('.');
-
- if (dot >= 0)
- {
- extension = title.substring(dot);
- title = title.substring(0, dot);
- }
-
- if (timestamp)
- {
- function getFormattedTime()
- {
- var today = new Date();
- var y = today.getFullYear();
- // JavaScript months are 0-based.
- var m = today.getMonth() + 1;
- var d = today.getDate();
- var h = today.getHours();
- var mi = today.getMinutes();
- var s = today.getSeconds();
-
- return y + "-" + m + "-" + d + "-" + h + "-" + mi + "-" + s;
- }
-
- var ts = new Date();
- title += ' ' + getFormattedTime();
- }
-
- title = mxResources.get('copyOf', [title]) + extension;
-
- return title;
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.logIfModified = function(file, discarded)
- {
- try
- {
- if (file != null)
- {
- if (discarded)
- {
- EditorUi.debug('File.closed', [file]);
- }
- // if (file.isModified() && file.constructor == DriveFile &&
- // file.desc != null && this.drive != null)
- // {
- // var evt = {category: 'WARN-CLOSE-MODIFIED-FILE-' + file.getHash(),
- // action: (discarded ? 'DISCARDED' : 'WARNED') + ((file.savingFile) ? 'saving' : '') +
- // ((file.savingFile && file.savingFileTime != null) ? '_' +
- // Math.round((Date.now() - file.savingFileTime.getTime()) / 1000) : '') +
- // ((file.saveLevel != null) ? ('-sl_' + file.saveLevel) : '') +
- // '-age_' + ((file.ageStart != null) ? Math.round((Date.now() - file.ageStart.getTime()) / 1000) : 'x') +
- // ((this.editor.autosave) ? '' : '-nosave') +
- // ((file.isAutosave()) ? '' : '-noauto') +
- // '-open_' + ((file.opened != null) ? Math.round((Date.now() - file.opened.getTime()) / 1000) : 'x') +
- // '-save_' + ((file.lastSaved != null) ? Math.round((Date.now() - file.lastSaved.getTime()) / 1000) : 'x') +
- // '-change_' + ((file.lastChanged != null) ? Math.round((Date.now() - file.lastChanged.getTime()) / 1000) : 'x') +
- // '-alive_' + Math.round((Date.now() - App.startTime.getTime()) / 1000),
- // label: ((file.sync != null) ? ('client_' + file.sync.clientId) : 'nosync') +
- // ((this.drive.user != null) ? ('-user_' + this.drive.user.id) : '-nouser') + '-rev_' +
- // file.desc.headRevisionId + '-mod_' + file.desc.modifiedDate + '-size_' + file.getSize() +
- // '-mime_' + file.desc.mimeType};
- // EditorUi.logEvent(evt);
- // }
- }
- }
- catch (e)
- {
- // ignore
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.fileLoaded = function(file, noDialogs, success)
- {
- var oldFile = this.getCurrentFile();
- this.fileLoadedError = null;
- this.fileEditable = null;
- this.setCurrentFile(null);
- var result = false;
- this.hideDialog();
-
- if (oldFile != null)
- {
- oldFile.removeListener(this.descriptorChangedListener);
- this.logIfModified(oldFile, true);
- oldFile.close();
- }
-
- this.editor.graph.model.clear();
- this.editor.undoManager.clear();
-
- var noFile = mxUtils.bind(this, function()
- {
- this.setGraphEnabled(false);
- this.setCurrentFile(null);
-
- // Keeps initial title if no file existed before
- if (oldFile != null)
- {
- this.updateDocumentTitle();
- }
-
- // File might have been loaded halfway
- this.editor.graph.model.clear();
- this.editor.undoManager.clear();
- this.setBackgroundImage(null);
-
- // Avoids empty hash with no value
- if (!noDialogs && window.location.hash != null &&
- window.location.hash.length > 0)
- {
- window.location.hash = '';
- }
-
- if (this.fname != null)
- {
- this.fnameWrapper.style.display = 'none';
- this.fname.innerText = '';
- this.fname.setAttribute('title', mxResources.get('rename'));
- }
- this.editor.setStatus('');
- this.updateUi();
-
- if (!noDialogs)
- {
- this.showSplash();
- }
- });
-
- if (file != null)
- {
- try
- {
- // Workaround for delayed scroll repaint with min UI in Safari
- if (mxClient.IS_SF && uiTheme == 'min')
- {
- this.diagramContainer.style.visibility = '';
- }
-
- // Order is significant, current file needed for correct
- // file format for initial save after starting realtime
- this.openingFile = true;
- this.setCurrentFile(file);
- file.addListener('descriptorChanged', this.descriptorChangedListener);
- file.addListener('contentChanged', this.descriptorChangedListener);
- file.open();
- delete this.openingFile;
-
- // DescriptorChanged updates the enabled state of the graph
- this.setGraphEnabled(true);
- this.setMode(file.getMode());
- this.editor.graph.model.prefix = Editor.guid() + '-';
- this.editor.undoManager.clear();
- this.descriptorChanged();
- this.updateUi();
-
- // Realtime files have a valid status message
- if (!file.isEditable())
- {
- this.editor.setStatus('<div class="geStatusBox" title="' +
- mxUtils.htmlEntities(mxResources.get('readOnly')) + '">' +
- mxUtils.htmlEntities(mxResources.get('readOnly')) + '</div>');
- }
- // Handles modified state after error of loading new file
- else if (file.isModified())
- {
- file.addUnsavedStatus();
- }
- else
- {
- this.editor.setStatus('');
- }
-
- if (!this.editor.isChromelessView() || this.editor.editable)
- {
- this.editor.graph.selectUnlockedLayer();
- this.showLayersDialog();
- this.restoreLibraries();
-
- // Workaround for no initial focus in FF
- if (window.self !== window.top)
- {
- window.focus();
- }
- }
- else if (this.editor.graph.isLightboxView())
- {
- this.lightboxFit();
- }
-
- if (this.chromelessResize)
- {
- this.chromelessResize();
- }
- if (success != null)
- {
- success();
- }
-
- this.editor.fireEvent(new mxEventObject('fileLoaded'));
- result = true;
- if (!this.isOffline() && file.getMode() != null)
- {
- var theme = (urlParams['sketch'] == '1') ? 'sketch' : uiTheme;
- if (theme == null)
- {
- theme = 'default';
- }
- else if (theme == 'sketch' || theme == 'min')
- {
- theme += Editor.isDarkMode() ? '-dark' : '-light';
- }
- // EditorUi.logEvent({category: file.getMode().toUpperCase() + '-OPEN-FILE-' + file.getHash(),
- // action: 'size_' + file.getSize(),
- // label: 'autosave_' + ((this.editor.autosave) ? 'on' : 'off') + '_theme_' + theme});
- }
-
- EditorUi.debug('File.opened', [file]);
-
- //Notify users that editing is disabled within mobile apps (mainly for MS Teams)
- if (urlParams['viewerOnlyMsg'] == '1')
- {
- this.showAlert(mxResources.get('viewerOnlyMsg'));
- }
-
- if (this.editor.editable && this.mode == file.getMode() &&
- file.getMode() != App.MODE_DEVICE && file.getMode() != null)
- {
- try
- {
- this.addRecent({id: file.getHash(), title: file.getTitle(), mode: file.getMode()});
- }
- catch (e)
- {
- // ignore
- }
- }
-
- try
- {
- mxSettings.setOpenCounter(mxSettings.getOpenCounter() + 1);
- mxSettings.save();
- }
- catch (e)
- {
- // ignore
- }
- }
- catch (e)
- {
- this.fileLoadedError = e;
-
- // Disconnects file from UI
- if (file != null)
- {
- try
- {
- file.close();
- }
- catch (e2)
- {
- // ignore
- }
- }
-
- // if (EditorUi.enableLogging && !this.isOffline())
- // {
- // try
- // {
- // EditorUi.logEvent({category: 'ERROR-LOAD-FILE-' +
- // ((file != null) ? file.getHash() : 'none'),
- // action: 'message_' + e.message,
- // label: 'stack_' + e.stack});
- // }
- // catch (e)
- // {
- // // ignore
- // }
- // }
-
- // Asynchronous handling of errors
- var fn = mxUtils.bind(this, function()
- {
- // Removes URL parameter and reloads the page
- if (urlParams['url'] != null && this.spinner.spin(document.body, mxResources.get('reconnecting')))
- {
- window.location.search = this.getSearch(['url']);
- }
- else if (oldFile != null)
- {
- if (!this.fileLoaded(oldFile))
- {
- noFile();
- }
- }
- else
- {
- noFile();
- }
- });
-
- if (!noDialogs)
- {
- this.handleError(e, mxResources.get('errorLoadingFile'), fn, true, null, null, true);
- }
- else
- {
- fn();
- }
- }
- }
- else
- {
- noFile();
- }
-
- return result;
- };
- /**
- * Creates a hash value for the current file.
- */
- EditorUi.prototype.getHashValueForPages = function(pages, details)
- {
- // TODO: Avoid encoding to XML to make it faster
- var hash = 0;
- var model = new mxGraphModel();
- var codec = new mxCodec();
- if (details != null)
- {
- details.byteCount = 0;
- details.attrCount = 0;
- details.eltCount = 0;
- details.nodeCount = 0;
- }
-
- for (var i = 0; i < pages.length; i++)
- {
- this.updatePageRoot(pages[i]);
- var diagram = pages[i].node.cloneNode(false);
-
- // FIXME: Check why names can be null in newer files
- // ignore in hash and do not diff null names for now
- diagram.removeAttribute('name');
-
- // Model is only a holder for the root
- model.root = pages[i].root;
- var xmlNode = codec.encode(model);
- this.editor.graph.saveViewState(pages[i].viewState, xmlNode, true);
-
- // Local defaults may be different in files so ignore
- xmlNode.removeAttribute('pageWidth');
- xmlNode.removeAttribute('pageHeight');
-
- diagram.appendChild(xmlNode);
-
- if (details != null)
- {
- details.eltCount += diagram.getElementsByTagName('*').length;
- details.nodeCount += diagram.getElementsByTagName('mxCell').length;
- }
-
- hash = ((hash << 5) - hash + this.hashValue(diagram, function(obj, key, value, isXml)
- {
- // Ignores JS machine rounding errors in known numeric attributes
- // eg. 412.33333333333326 (Webkit/FF) == 412.33333333333325 (Edge/IE11)
- if (isXml && (obj.nodeName == 'mxGeometry' || obj.nodeName == 'mxPoint') &&
- (key == 'x' || key == 'y' || key == 'width' || key == 'height'))
- {
- return Math.round(value);
- }
- // Workaround for previous in patch written to mxCell in 10.0.23
- else if (isXml && obj.nodeName == 'mxCell' && key == 'previous')
- {
- return null;
- }
- else
- {
- return value;
- }
- }, details)) << 0;
- }
-
- return hash;
- };
-
- /**
- * Creates a hash value for the given object. Replacer returns the value of the
- * property or attribute for the given object or XML node.
- */
- EditorUi.prototype.hashValue = function(obj, replacer, details)
- {
- var hash = 0;
-
- // Checks for XML nodes
- if (obj != null && typeof obj === 'object' && typeof obj.nodeType === 'number' &&
- typeof obj.nodeName === 'string' && typeof obj.getAttribute === 'function')
- {
- if (obj.nodeName != null)
- {
- hash = hash ^ this.hashValue(obj.nodeName, replacer, details);
- }
-
- if (obj.attributes != null)
- {
- if (details != null)
- {
- details.attrCount += obj.attributes.length;
- }
-
- for (var i = 0; i < obj.attributes.length; i++)
- {
- var key = obj.attributes[i].name;
- var value = (replacer != null) ? replacer(obj, key, obj.attributes[i].value, true) : obj.attributes[i].value;
-
- if (value != null)
- {
- hash = hash ^ (this.hashValue(key, replacer, details) +
- this.hashValue(value, replacer, details));
- }
- }
- }
-
- if (obj.childNodes != null)
- {
- for (var i = 0; i < obj.childNodes.length; i++)
- {
- hash = ((hash << 5) - hash + this.hashValue(
- obj.childNodes[i], replacer, details)) << 0;
- }
- }
- }
- else if (obj != null && typeof obj !== 'function')
- {
- var str = String(obj);
- var temp = 0;
- if (details != null)
- {
- details.byteCount += str.length;
- }
-
- for (var i = 0; i < str.length; i++)
- {
- temp = ((temp << 5) - temp + str.charCodeAt(i)) << 0;
- }
-
- hash = hash ^ temp;
- }
-
- return hash;
- };
- /**
- * Adds empty implementation
- */
- EditorUi.prototype.descriptorChanged = function()
- {
- // empty
- };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.restoreLibraries = function() { };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload, fn) { };
-
- /**
- *
- */
- EditorUi.prototype.isScratchpadEnabled = function()
- {
- return isLocalStorage || mxClient.IS_CHROMEAPP;
- };
- /**
- * Shows or hides the scratchpad library.
- */
- EditorUi.prototype.toggleScratchpad = function()
- {
- if (this.isScratchpadEnabled())
- {
- if (this.scratchpad == null)
- {
- StorageFile.getFileContent(this, '.scratchpad', mxUtils.bind(this, function(xml)
- {
- if (xml == null)
- {
- xml = this.emptyLibraryXml;
- }
-
- this.loadLibrary(new StorageLibrary(this, xml, '.scratchpad'));
- }));
- }
- else
- {
- this.closeLibrary(this.scratchpad);
- }
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.createLibraryDataFromImages = function(images)
- {
- // Uncompresses existing entries for saving
- if (!Editor.defaultCompressed)
- {
- for (var i = 0; i < images.length; i++)
- {
- if (images[i].xml != null && images[i].xml.charAt(0) != '<')
- {
- images[i].xml = mxUtils.trim(Graph.decompress(images[i].xml));
- }
- }
- }
- var doc = mxUtils.createXmlDocument();
- var library = doc.createElement('mxlibrary');
- mxUtils.setTextContent(library, JSON.stringify(images, null, 2));
- doc.appendChild(library);
-
- return mxUtils.getXml(doc, '\n');
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.closeLibrary = function(file)
- {
- if (file != null)
- {
- this.removeLibrarySidebar(file.getHash());
-
- if (file.constructor != LocalLibrary)
- {
- mxSettings.removeCustomLibrary(file.getHash());
- }
-
- if (file.title == '.scratchpad')
- {
- this.scratchpad = null;
- }
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.removeLibrarySidebar = function(id)
- {
- var elts = this.sidebar.palettes[id];
-
- if (elts != null)
- {
- for (var i = 0; i < elts.length; i++)
- {
- elts[i].parentNode.removeChild(elts[i]);
- }
-
- delete this.sidebar.palettes[id];
- }
- };
-
- /**
- * Changes the position of the library in the sidebar.
- */
- EditorUi.prototype.repositionLibrary = function(nextChild, append)
- {
- var c = this.sidebar.getEntryContainer();
- if (!this.sidebar.appendCustomLibraries ||
- nextChild != null || !append)
- {
- if (nextChild == null)
- {
- var elts = this.sidebar.palettes['L.scratchpad'];
-
- if (elts == null)
- {
- elts = this.sidebar.palettes['search'];
- }
-
- if (elts != null)
- {
- nextChild = elts[elts.length - 1].nextSibling;
- }
- }
-
- nextChild = (nextChild != null) ? nextChild : c.firstChild.nextSibling.nextSibling;
-
- var content = c.lastChild;
- var title = content.previousSibling;
-
- c.insertBefore(content, nextChild);
- c.insertBefore(title, content);
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.loadLibrary = function(file, expand)
- {
- var doc = mxUtils.parseXml(file.getData());
-
- if (doc.documentElement.nodeName == 'mxlibrary')
- {
- var images = JSON.parse(mxUtils.getTextContent(doc.documentElement));
- this.libraryLoaded(file, images, doc.documentElement.getAttribute('title'), expand);
- }
- else
- {
- throw {message: mxResources.get('notALibraryFile')};
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getLibraryStorageHint = function(file)
- {
- return '';
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.showSidebar = function()
- {
- if (this.sidebarWindow != null)
- {
- this.sidebarWindow.window.setVisible(true);
- }
- else
- {
- this.toggleShapesPanel(true);
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.libraryLoaded = function(file, images, optionalTitle, expand)
- {
- if (this.sidebar == null)
- {
- return;
- }
- if (file.constructor != LocalLibrary)
- {
- mxSettings.addCustomLibrary(file.getHash());
- }
-
- var library = null;
- if (file.constructor != StorageLibrary || file.title != '.scratchpad')
- {
- if (this.openLibraries == null)
- {
- this.openLibraries = [];
- }
- // Removes existing entry for this file ID
- for (var i = 0; i < this.openLibraries.length; i++)
- {
- if (this.openLibraries[i].file.getHash() == file.getHash())
- {
- mxUtils.remove(this.openLibraries[i], this.openLibraries);
- break;
- }
- }
- // Adds new entry to the array of open libraries
- library = {file: file, images: images,
- title: optionalTitle,
- expand: expand};
- this.openLibraries.push(library);
- }
- else
- {
- this.scratchpad = file;
- }
-
- var elts = this.sidebar.palettes[file.getHash()];
- var nextSibling = (elts != null) ? elts[elts.length - 1].nextSibling : null;
-
- // Removes existing sidebar entry for this library
- this.removeLibrarySidebar(file.getHash());
- var dropTarget = null;
-
- var addImages = mxUtils.bind(this, function(imgs, content)
- {
- if (imgs.length == 0 && file.isEditable())
- {
- if (dropTarget == null)
- {
- dropTarget = document.createElement('div');
- dropTarget.className = 'geDropTarget';
- mxUtils.write(dropTarget, mxResources.get('dragElementsHere'));
- }
-
- content.appendChild(dropTarget);
- }
- else
- {
- this.addLibraryEntries(imgs, content);
- }
- });
- // Adds entries to search index
- // KNOWN: Existing entries are not replaced after edit of custom library
- if (this.sidebar != null && images != null)
- {
- this.sidebar.addEntries(images);
- }
-
- // Adds new sidebar entry for this library
- var tmp = optionalTitle;
-
- if (tmp == null)
- {
- tmp = file.getTitle();
- if (tmp != null && /(\.xml)$/i.test(tmp))
- {
- tmp = tmp.substring(0, tmp.lastIndexOf('.'));
- }
- }
- var contentDiv = this.sidebar.addPalette(file.getHash(), tmp,
- (expand != null) ? expand : true, mxUtils.bind(this, function(content)
- {
- addImages(images, content);
- }));
- if (library != null)
- {
- library.div = contentDiv;
- }
-
- this.repositionLibrary(nextSibling, file.getHash() != 'L.scratchpad');
-
- // Adds tooltip for backend
- var title = contentDiv.parentNode.previousSibling;
- var tip = title.getAttribute('title');
-
- if (tip != null && tip.length > 0 && file.title != '.scratchpad')
- {
- title.setAttribute('title', this.getLibraryStorageHint(file) + '\n' + tip);
- }
-
- var buttons = document.createElement('div');
- buttons.style.backgroundColor = 'inherit';
- title.style.position = 'relative';
-
- var btn = document.createElement('img');
- btn.className = 'geAdaptiveAsset';
- btn.setAttribute('src', Editor.crossImage);
- btn.setAttribute('title', mxResources.get('close'));
- btn.setAttribute('valign', 'absmiddle');
- btn.setAttribute('border', '0');
- btn.style.position = 'relative';
- btn.style.top = '2px';
- btn.style.width = '14px';
- btn.style.cursor = 'pointer';
- btn.style.margin = '0 3px';
- var saveBtn = null;
-
- if (file.title != '.scratchpad' || this.closableScratchpad)
- {
- buttons.appendChild(btn);
-
- mxEvent.addListener(btn, 'click', mxUtils.bind(this, function(evt)
- {
- // Workaround for close after any button click in IE8
- if (!mxEvent.isConsumed(evt))
- {
- var fn = mxUtils.bind(this, function()
- {
- if (library != null)
- {
- mxUtils.remove(library, this.openLibraries);
- }
- this.closeLibrary(file);
- });
-
- if (saveBtn != null)
- {
- this.confirm(mxResources.get('allChangesLost'), null, fn,
- mxResources.get('cancel'), mxResources.get('discardChanges'));
- }
- else
- {
- fn();
- }
-
- mxEvent.consume(evt);
- }
- }));
- }
-
- if (file.isEditable())
- {
- var graph = this.editor.graph;
- var spinBtn = null;
-
- var editLibrary = mxUtils.bind(this, function(evt)
- {
- this.showLibraryDialog(file.getTitle(), contentDiv, images, file, file.getMode());
- mxEvent.consume(evt);
- });
-
- var saveLibrary = mxUtils.bind(this, function(evt)
- {
- file.setModified(true);
-
- if (file.isAutosave())
- {
- if (spinBtn != null && spinBtn.parentNode != null)
- {
- spinBtn.parentNode.removeChild(spinBtn);
- }
-
- spinBtn = btn.cloneNode(false);
- spinBtn.setAttribute('src', Editor.spinImage);
- spinBtn.setAttribute('title', mxResources.get('saving'));
- spinBtn.style.cursor = 'default';
- spinBtn.style.marginRight = '2px';
- spinBtn.style.marginTop = '-2px';
- buttons.insertBefore(spinBtn, buttons.firstChild);
-
- this.saveLibrary(file.getTitle(), images, file, file.getMode(), true, true, function()
- {
- if (spinBtn != null && spinBtn.parentNode != null)
- {
- spinBtn.parentNode.removeChild(spinBtn);
- }
- });
- }
- else if (saveBtn == null)
- {
- saveBtn = btn.cloneNode(false);
- saveBtn.setAttribute('src', Editor.saveImage);
- saveBtn.setAttribute('title', mxResources.get('save'));
- buttons.insertBefore(saveBtn, buttons.firstChild);
-
- mxEvent.addListener(saveBtn, 'click', mxUtils.bind(this, function(evt)
- {
- this.saveLibrary(file.getTitle(), images, file, file.getMode(),
- file.constructor == LocalLibrary, true, function()
- {
- if (saveBtn != null && !file.isModified())
- {
- saveBtn.parentNode.removeChild(saveBtn);
- saveBtn = null;
- }
- });
-
- mxEvent.consume(evt);
- }));
- }
- });
-
- var addCells = mxUtils.bind(this, function(cells, bounds, evt, title)
- {
- cells = graph.cloneCells(mxUtils.sortCells(graph.model.getTopmostCells(cells)));
-
- // Translates cells to origin
- for (var i = 0; i < cells.length; i++)
- {
- var geo = graph.getCellGeometry(cells[i]);
-
- if (geo != null)
- {
- geo.translate(-bounds.x, -bounds.y);
- }
- }
- contentDiv.appendChild(this.sidebar.createVertexTemplateFromCells(
- cells, bounds.width, bounds.height, title || '', true, null, false));
- var xml = mxUtils.getXml(this.editor.graph.encodeCells(cells));
- if (Editor.defaultCompressed)
- {
- xml = Graph.compress(xml);
- }
- var entry = {xml: xml, w: bounds.width, h: bounds.height};
-
- if (title != null)
- {
- entry.title = title;
- }
-
- images.push(entry);
- saveLibrary(evt);
-
- if (dropTarget != null && dropTarget.parentNode != null && images.length > 0)
- {
- dropTarget.parentNode.removeChild(dropTarget);
- dropTarget = null;
- }
- });
-
- var addSelection = mxUtils.bind(this, function(evt)
- {
- if (!graph.isSelectionEmpty())
- {
- var cells = graph.getSelectionCells();
- var bounds = graph.view.getBounds(cells);
- if (bounds != null && bounds.width > 0 && bounds.height > 0)
- {
- var s = graph.view.scale;
-
- bounds.x /= s;
- bounds.y /= s;
- bounds.width /= s;
- bounds.height /= s;
-
- bounds.x -= graph.view.translate.x;
- bounds.y -= graph.view.translate.y;
-
- addCells(cells, bounds);
- }
- else
- {
- this.showError(mxResources.get('error'), mxResources.get('invalidSel'), mxResources.get('ok'));
- }
- }
- else if (graph.getRubberband().isActive())
- {
- graph.getRubberband().execute(evt);
- graph.getRubberband().reset();
- }
- else
- {
- this.showError(mxResources.get('error'), mxResources.get('nothingIsSelected'), mxResources.get('ok'));
- }
-
- mxEvent.consume(evt);
- });
-
- // Adds drop handler from graph
- mxEvent.addGestureListeners(contentDiv, function(){}, mxUtils.bind(this, function(evt)
- {
- if (graph.isMouseDown && graph.panningManager != null && graph.graphHandler.first != null)
- {
- graph.graphHandler.suspend();
-
- if (graph.graphHandler.hint != null)
- {
- graph.graphHandler.hint.style.visibility = 'hidden';
- }
-
- contentDiv.style.backgroundColor = (Editor.isDarkMode()) ? '#000000' : '#fefefe';
- contentDiv.style.cursor = 'copy';
- graph.panningManager.stop();
- graph.autoScroll = false;
-
- mxEvent.consume(evt);
- }
- }), mxUtils.bind(this, function(evt)
- {
- if (graph.isMouseDown && graph.panningManager != null && graph.graphHandler != null)
- {
- contentDiv.style.backgroundColor = '';
- contentDiv.style.cursor = 'default';
- this.sidebar.showTooltips = true;
- graph.panningManager.stop();
-
- graph.graphHandler.reset();
- graph.isMouseDown = false;
- graph.autoScroll = true;
-
- addSelection(evt);
- mxEvent.consume(evt);
- }
- }));
-
- // Handles mouse leaving the library and restoring move
- mxEvent.addListener(contentDiv, 'mouseleave', mxUtils.bind(this, function(evt)
- {
- if (graph.isMouseDown && graph.graphHandler.first != null)
- {
- graph.graphHandler.resume();
- if (graph.graphHandler.hint != null)
- {
- graph.graphHandler.hint.style.visibility = 'visible';
- }
-
- contentDiv.style.backgroundColor = '';
- contentDiv.style.cursor = '';
- graph.autoScroll = true;
- }
- }));
-
- // Adds drop handler from filesystem
- if (Graph.fileSupport)
- {
- mxEvent.addListener(contentDiv, 'dragover', mxUtils.bind(this, function(evt)
- {
- contentDiv.style.backgroundColor = (Editor.isDarkMode()) ? '#000000' : '#fefefe';
- evt.dataTransfer.dropEffect = 'copy';
- contentDiv.style.cursor = 'copy';
- this.sidebar.hideTooltip();
- evt.stopPropagation();
- evt.preventDefault();
- }));
-
- mxEvent.addListener(contentDiv, 'drop', mxUtils.bind(this, function(evt)
- {
- contentDiv.style.cursor = '';
- contentDiv.style.backgroundColor = '';
-
- if (evt.dataTransfer.files.length > 0)
- {
- this.importFiles(evt.dataTransfer.files, 0, 0, this.maxImageSize, mxUtils.bind(this, function(data, mimeType, x, y, w, h, img, doneFn, file)
- {
- if (data != null && mimeType.substring(0, 6) == 'image/')
- {
- var style = 'shape=image;verticalLabelPosition=bottom;verticalAlign=top;imageAspect=0;aspect=fixed;image=' +
- this.convertDataUri(data);
- var cells = [new mxCell('', new mxGeometry(0, 0, w, h), style)];
- cells[0].vertex = true;
-
- addCells(cells, new mxRectangle(0, 0, w, h), evt, (mxEvent.isAltDown(evt)) ? null : img.substring(0, img.lastIndexOf('.')).replace(/_/g, ' '));
- if (dropTarget != null && dropTarget.parentNode != null && images.length > 0)
- {
- dropTarget.parentNode.removeChild(dropTarget);
- dropTarget = null;
- }
- }
- else
- {
- var done = false;
-
- var doImport = mxUtils.bind(this, function(theData, theMimeType)
- {
- if (theData != null && theMimeType == 'application/pdf')
- {
- var xml = Editor.extractGraphModelFromPdf(theData);
-
- if (xml != null && xml.length > 0)
- {
- theMimeType = 'text/xml';
- theData = xml;
- }
- }
-
- if (theData != null) //Try to parse the file as xml (can be a library or mxfile). Otherwise, an error will be shown
- {
- var doc = mxUtils.parseXml(theData);
-
- if (doc.documentElement.nodeName == 'mxlibrary')
- {
- try
- {
- var temp = JSON.parse(mxUtils.getTextContent(doc.documentElement));
- addImages(temp, contentDiv);
- images = images.concat(temp);
- saveLibrary(evt);
- this.spinner.stop();
- done = true;
- }
- catch (e)
- {
- // ignore
- }
- }
- else if (doc.documentElement.nodeName == 'mxfile')
- {
- try
- {
- var pages = doc.documentElement.getElementsByTagName('diagram');
-
- for (var i = 0; i < pages.length; i++)
- {
- var cells = this.stringToCells(Editor.getDiagramNodeXml(pages[i]));
- var size = this.editor.graph.getBoundingBoxFromGeometry(cells);
- if (size != null)
- {
- addCells(cells, new mxRectangle(0, 0, size.width, size.height), evt);
- }
- }
-
- done = true;
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.log('error in drop handler:', e);
- }
- }
- }
- }
-
- if (!done)
- {
- this.spinner.stop();
- this.handleError({message: mxResources.get('errorLoadingFile')})
- }
- if (dropTarget != null && dropTarget.parentNode != null && images.length > 0)
- {
- dropTarget.parentNode.removeChild(dropTarget);
- dropTarget = null;
- }
- });
-
- if (file != null && img != null && EditorUi.isVisioFilename(img))
- {
- this.importVisio(file, function(xml)
- {
- doImport(xml, 'text/xml');
- }, null, img);
- }
- else if (new XMLHttpRequest().upload && this.isRemoteFileFormat(data, img) && file != null)
- {
- if (this.isExternalDataComms())
- {
- this.parseFile(file, mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4)
- {
- this.spinner.stop();
-
- if (xhr.status >= 200 && xhr.status <= 299)
- {
- doImport(xhr.responseText, 'text/xml');
- }
- else
- {
- this.handleError({message: mxResources.get((xhr.status == 413) ?
- 'drawingTooLarge' : 'invalidOrMissingFile')},
- mxResources.get('errorLoadingFile'));
- }
- }
- }));
- }
- else
- {
- this.spinner.stop();
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- }
- else
- {
- doImport(data, mimeType);
- }
- }
- }));
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- }));
-
- mxEvent.addListener(contentDiv, 'dragleave', function(evt)
- {
- contentDiv.style.cursor = '';
- contentDiv.style.backgroundColor = '';
- evt.stopPropagation();
- evt.preventDefault();
- });
- }
-
- btn = btn.cloneNode(false);
- btn.setAttribute('src', Editor.editImage);
- btn.setAttribute('title', mxResources.get('edit'));
- buttons.insertBefore(btn, buttons.firstChild);
-
- mxEvent.addListener(btn, 'click', editLibrary);
- var btn2 = btn.cloneNode(false);
- btn2.setAttribute('src', Editor.plusImage);
- btn2.setAttribute('title', mxResources.get('add'));
- buttons.insertBefore(btn2, buttons.firstChild);
- mxEvent.addListener(btn2, 'click', addSelection);
- // Hack to add selection via context menu
- if (file.title == '.scratchpad')
- {
- this.addSelectionToScratchpad = addSelection;
- }
-
- if (!this.isOffline() && file.title == '.scratchpad' &&
- EditorUi.scratchpadHelpLink != null)
- {
- var link = this.createHelpIcon(EditorUi.scratchpadHelpLink);
- var img = link.getElementsByTagName('img')[0];
- link.style.marginRight = '1px';
- img.style.marginTop = '-2px';
- img.style.opacity = '';
- buttons.insertBefore(link, buttons.firstChild);
- }
- }
-
- title.appendChild(buttons);
- this.editor.fireEvent(new mxEventObject('libraryLoaded'));
- };
- /**
- * Adds the library entries to the given DOM node.
- */
- EditorUi.prototype.addLibraryEntries = function(imgs, content)
- {
- for (var i = 0; i < imgs.length; i++)
- {
- try
- {
- var img = imgs[i];
- var data = img.data;
- if (data != null)
- {
- data = (data.substring(0, 5) == 'data:') ? this.convertDataUri(data) : data;
- var s = 'shape=image;verticalLabelPosition=bottom;verticalAlign=top;imageAspect=0;';
-
- if (img.aspect == 'fixed')
- {
- s += 'aspect=fixed;'
- }
- s = s + 'image=' + data + ';';
- if (img.style != null)
- {
- s += img.style;
- }
-
- content.appendChild(this.sidebar.createVertexTemplate(s,
- img.w, img.h, '', img.title || '', false, null, true));
- }
- else if (img.xml != null)
- {
- var cells = this.stringToCells((img.xml.charAt(0) == '<') ?
- img.xml : Graph.decompress(img.xml));
- var title = (cells.length == 0) ? mxResources.get('drawingEmpty') :
- ((img.title != null) ? img.title : '');
- content.appendChild(this.sidebar.createVertexTemplateFromCells(
- cells, img.w, img.h, title, true, null, true));
- }
- }
- catch (e)
- {
- var title = (e.message != null) ? mxResources.get('error') +
- ': ' + e.message : String(e);
- var elt = this.sidebar.createVertexTemplateFromCells(null,
- img.w, img.h, title, true, null, true);
- content.appendChild(elt);
- }
- }
- };
- /**
- * Extracts the resource for the current language from the given multi language
- * resource object of the form {es: "...", de: "...", main: "..."} where the keys
- * are country codes and main defines the fallback if no resource for the current
- * country code exists.
- */
- EditorUi.prototype.getResource = function(obj)
- {
- return (obj != null) ? (obj[mxLanguage] || obj.main) : null;
- };
-
- /**
- * EditorUi Overrides
- */
- EditorUi.prototype.footerHeight = 0;
-
- if (urlParams['savesidebar'] == '1')
- {
- Sidebar.prototype.thumbWidth = 64;
- Sidebar.prototype.thumbHeight = 64;
- }
- /**
- * Programmatic settings for theme.
- */
- EditorUi.initTheme = function()
- {
- if (Editor.currentTheme == 'atlas' && !window.DRAWIO_PUBLIC_BUILD)
- {
- mxClient.link('stylesheet', STYLE_PATH + '/atlas.css');
- if (typeof Toolbar !== 'undefined')
- {
- Toolbar.prototype.unselectedBackground = 'linear-gradient(rgb(255, 255, 255) 0px, rgb(242, 242, 242) 100%)';
- Toolbar.prototype.selectedBackground = 'rgb(242, 242, 242)';
- }
-
- Editor.prototype.initialTopSpacing = 3;
- EditorUi.prototype.menubarHeight = 41;
- EditorUi.prototype.toolbarHeight = 38;
- }
- // Implements the sketch-min UI
- if (Editor.currentTheme == 'sketch' && !window.DRAWIO_PUBLIC_BUILD)
- {
- Editor.configurationKey = '.sketch-configuration';
- Editor.settingsKey = '.sketch-config';
- }
- };
-
- EditorUi.initTheme();
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.showImageDialog = function(title, value, fn, ignoreExisting, convertDataUri, withCrop, initClipPath)
- {
- // KNOWN: IE+FF don't return keyboard focus after image dialog (calling focus doesn't help)
- var dlg = new ImageDialog(this, title, value, fn, ignoreExisting, convertDataUri, withCrop, initClipPath);
- this.showDialog(dlg.container, (Graph.fileSupport) ? 480 : 360, (Graph.fileSupport) ? 200 : 90, true, true);
- dlg.init();
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.showLocalStorageDialog = function(title, key, buttons, elt, helpLink, applyFn)
- {
- var value = localStorage.getItem(key);
- var dlg = new TextareaDialog(this, title, (value != null) ?
- JSON.stringify(JSON.parse(value), null, 2) : '',
- mxUtils.bind(this, function(newValue)
- {
- if (newValue != null)
- {
- try
- {
- if (applyFn != null)
- {
- applyFn(newValue);
- }
-
- if (newValue == value)
- {
- this.hideDialog();
- }
- else
- {
- if (newValue.length > 0)
- {
- var obj = JSON.parse(newValue);
-
- localStorage.setItem(key, JSON.stringify(obj));
- }
- else
- {
- localStorage.removeItem(key);
- }
- this.hideDialog();
- this.alert(mxResources.get('restartForChangeRequired'));
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }), null, mxResources.get('close'), null, null, null, true, null, null, helpLink, buttons, elt);
-
- this.showDialog(dlg.container, 660, 480, true, false);
- dlg.init();
- };
- /**
- * Hides the current menu.
- */
- EditorUi.prototype.showBackgroundImageDialog = function(apply, img, color, showColor)
- {
- apply = (apply != null) ? apply : mxUtils.bind(this, function(image, failed, color, shadowVisible)
- {
- if (!failed)
- {
- var change = new ChangePageSetup(this, (showColor) ? color : null, image);
- change.ignoreColor = !showColor;
- if (shadowVisible != null && showColor)
- {
- change.shadowVisible = shadowVisible;
- }
-
- this.editor.graph.model.execute(change);
- }
- });
- var dlg = new BackgroundImageDialog(this, apply, img, color, showColor);
- this.showDialog(dlg.container, 400, (showColor) ? 240 : 220, true, true);
- dlg.init();
- };
- /**
- * Hides the current menu.
- */
- EditorUi.prototype.showLibraryDialog = function(name, sidebar, images, file, mode)
- {
- var dlg = new LibraryDialog(this, name, sidebar, images, file, mode);
-
- this.showDialog(dlg.container, 640, 440, true, false, mxUtils.bind(this, function(cancel)
- {
- if (cancel && this.getCurrentFile() == null && urlParams['embed'] != '1')
- {
- this.showSplash();
- }
- }));
-
- dlg.init();
- };
- /**
- * Overridden to update after view state changes.
- */
- var editorUiCreateFormat = EditorUi.prototype.createFormat;
-
- EditorUi.prototype.createFormat = function(container)
- {
- var format = editorUiCreateFormat.apply(this, arguments);
-
- this.editor.graph.addListener('viewStateChanged', mxUtils.bind(this, function(evt)
- {
- if (this.editor.graph.isSelectionEmpty())
- {
- format.refresh();
- }
- }));
-
- return format;
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.handleError = function(resp, title, fn, invokeFnOnClose, notFoundMessage, fileHash, disableLogging)
- {
- var resume = (this.spinner != null && this.spinner.pause != null) ? this.spinner.pause() : function() {};
- var e = (resp != null && resp.error != null) ? resp.error : resp;
- // Logs errors and writes stack to console
- if (resp != null && (urlParams['test'] == '1' || resp.stack != null) && resp.message != null)
- {
- try
- {
- if (!disableLogging)
- {
- EditorUi.logError('Caught: ' +
- (resp.message == '' && resp.name != null) ? resp.name : resp.message,
- resp.filename, resp.lineNumber, resp.columnNumber, resp, 'INFO');
- }
- if ((urlParams['test'] == '1' || disableLogging) &&
- window.console != null)
- {
- console.error('EditorUi.handleError:', resp);
- }
- }
- catch (e)
- {
- // ignore
- }
- }
-
- if (e != null || title != null)
- {
- var msg = mxUtils.htmlEntities(mxResources.get('unknownError'), false);
- var btn = mxResources.get('ok');
- var retry = null;
- title = (title != null) ? title : mxResources.get('error');
-
- if (e != null)
- {
- if (e.retry != null)
- {
- btn = mxResources.get('cancel');
- retry = function()
- {
- resume();
- e.retry();
- };
- }
-
- if (e.code == 404 || e.status == 404 || e.code == 403)
- {
- if (e.code == 403)
- {
- if (e.message != null)
- {
- msg = mxUtils.htmlEntities(e.message, false);
- }
- else
- {
- msg = mxUtils.htmlEntities(mxResources.get('accessDenied'), false);
- }
- }
- else
- {
- msg = (notFoundMessage != null) ? notFoundMessage :
- mxUtils.htmlEntities(mxResources.get('fileNotFoundOrDenied') +
- ((this.drive != null && this.drive.user != null) ?
- ' (' + this.drive.user.email+ ')' : ''), false);
- }
-
- var id = (notFoundMessage != null) ? null : ((fileHash != null) ? fileHash : window.location.hash);
-
- // #U handles case where we tried to fallback to Google File and
- // hash property still shows the public URL we tried to load
- if (id != null && (id.substring(0, 2) == '#G' ||
- id.substring(0, 45) == '#Uhttps%3A%2F%2Fdrive.google.com%2Fuc%3Fid%3D') &&
- ((resp != null && resp.error != null && ((resp.error.errors != null &&
- resp.error.errors.length > 0 && resp.error.errors[0].reason == 'fileAccess') ||
- (resp.error.data != null && resp.error.data.length > 0 &&
- resp.error.data[0].reason == 'fileAccess'))) ||
- e.code == 404 || e.status == 404))
- {
- id = (id.substring(0, 2) == '#U') ? id.substring(45, id.lastIndexOf('%26ex')) : id.substring(2);
-
- // Special case where the button must have a different label and function
- this.showError(title, msg, mxResources.get('tryOpeningViaThisPage'), mxUtils.bind(this, function()
- {
- this.editor.graph.openLink('https://drive.google.com/open?id=' + id);
- if (invokeFnOnClose != null)
- {
- invokeFnOnClose();
- }
- }), retry, mxResources.get('changeUser'), mxUtils.bind(this, function()
- {
- var driveUsers = this.drive.getUsersList();
- var div = document.createElement('div');
- var title = document.createElement('div');
- title.style.marginBottom = '6px';
- mxUtils.write(title, mxResources.get('changeUser') + ': ');
- div.appendChild(title);
-
- var usersSelect = document.createElement('select');
- usersSelect.style.width = '100%';
-
- //TODO This code is similar to Dialogs.js change user part in SplashDialog
- function fillUsersSelect()
- {
- usersSelect.innerText = '';
-
- for (var i = 0; i < driveUsers.length; i++)
- {
- var option = document.createElement('option');
- mxUtils.write(option, driveUsers[i].displayName);
- option.value = i;
- usersSelect.appendChild(option);
- //More info (email) about the user in a disabled option
- option = document.createElement('option');
- option.innerHTML = ' ';
- mxUtils.write(option, '<' + driveUsers[i].email + '>');
- option.setAttribute('disabled', 'disabled');
- usersSelect.appendChild(option);
- }
-
- //Add account option
- var option = document.createElement('option');
- mxUtils.write(option, mxResources.get('addAccount'));
- option.value = driveUsers.length;
- usersSelect.appendChild(option);
- }
-
- fillUsersSelect();
-
- mxEvent.addListener(usersSelect, 'change', mxUtils.bind(this, function()
- {
- var userIndex = usersSelect.value;
- var existingAccount = driveUsers.length != userIndex;
-
- if (existingAccount)
- {
- this.drive.setUser(driveUsers[userIndex]);
- }
-
- this.drive.authorize(existingAccount, mxUtils.bind(this, function()
- {
- if (!existingAccount)
- {
- driveUsers = this.drive.getUsersList();
- fillUsersSelect();
- }
- }), mxUtils.bind(this, function(resp)
- {
- this.handleError(resp);
- }), true);
- }));
-
- div.appendChild(usersSelect);
-
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- this.loadFile(window.location.hash.substr(1), true);
- }));
- this.showDialog(dlg.container, 300, 100, true, true);
- }), mxResources.get('cancel'), mxUtils.bind(this, function()
- {
- this.hideDialog();
-
- if (fn != null)
- {
- fn();
- }
- }), 520, 150);
- return;
- }
- }
-
- if (e.message != null)
- {
- if (e.message == '' && e.name != null)
- {
- msg = mxUtils.htmlEntities(e.name, false);
- }
- else
- {
- msg = mxUtils.htmlEntities(e.message, false);
- }
- }
- else if (e.response != null && e.response.error != null)
- {
- msg = mxUtils.htmlEntities(e.response.error, false);
- }
- else if (typeof window.App !== 'undefined')
- {
- if (e.code == App.ERROR_TIMEOUT)
- {
- msg = mxUtils.htmlEntities(mxResources.get('timeout'), false);
- }
- else if (e.code == App.ERROR_BUSY)
- {
- msg = mxUtils.htmlEntities(mxResources.get('busy'), false);
- }
- else if (typeof e === 'string' && e.length > 0)
- {
- msg = mxUtils.htmlEntities(e, false);
- }
- }
- }
-
- var btn3 = null;
- var fn3 = null;
-
- if (e != null && e.helpLink != null)
- {
- btn3 = mxResources.get('help');
-
- fn3 = mxUtils.bind(this, function()
- {
- return this.editor.graph.openLink(e.helpLink);
- });
- }
- else if (e != null && e.ownerEmail != null)
- {
- btn3 = mxResources.get('contactOwner');
-
- msg += mxUtils.htmlEntities(' (' + btn3 + ': ' + e.ownerEmail + ')', false);
-
- fn3 = mxUtils.bind(this, function()
- {
- return this.openLink('mailto:' + mxUtils.htmlEntities(e.ownerEmail));
- });
- }
-
- this.showError(title, msg, btn, fn, retry, null, null, btn3, fn3,
- null, null, null, (invokeFnOnClose) ? fn : null);
- }
- else if (fn != null)
- {
- fn();
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.alert = function(msg, fn, optionalWidth)
- {
- var dlg = new ErrorDialog(this, null, msg, mxResources.get('ok'), fn);
- this.showDialog(dlg.container, optionalWidth || 340, 100, true, false);
- dlg.init();
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.confirm = function(msg, okFn, cancelFn, okLabel, cancelLabel, closable)
- {
- msg = (msg != null) ? msg : '';
- var resume = (this.spinner != null && this.spinner.pause != null) ? this.spinner.pause() : function() {};
- var height = Math.min(220, Math.ceil(Math.max(1, msg.length) / 50) * 28);
-
- var dlg = new ConfirmDialog(this, msg, function()
- {
- resume();
-
- if (okFn != null)
- {
- okFn();
- }
- }, function()
- {
- resume();
-
- if (cancelFn != null)
- {
- cancelFn();
- }
- }, okLabel, cancelLabel, null, null, null, null, height);
-
- this.showDialog(dlg.container, 340, 46 + height, true, closable);
- dlg.init();
- };
-
- /**
- * Creates a popup banner.
- */
- EditorUi.prototype.showBanner = function(id, text, onclick, doNotShowAgainOnClose, small, positionCss, t1, t2, to)
- {
- var result = false;
-
- if (!this.bannerShowing && !this['hideBanner' + id] &&
- (!isLocalStorage || mxSettings.settings == null ||
- mxSettings.settings['close' + id] == null))
- {
- positionCss = (positionCss != null) ? positionCss : 'bottom:10px;left:50%;';
- t1 = (t1 != null) ? t1 : 'translate(-50%,120%)';
- t2 = (t2 != null) ? t2 : 'translate(-50%,0%)';
- var delay = (small) ? 500 : 1000;
- var css = (!small) ? 'font-size:16px;padding:18px 34px 12px 20px;font-weight:bold;' :
- 'padding:4px;border-radius:6px;font-size:11px;height:12px;font-weight:normal;';
- var banner = document.createElement('div');
- banner.style.cssText = 'position:absolute;' + positionCss + ';max-width:90%;white-space:nowrap;' +
- 'cursor:pointer;z-index:' + mxPopupMenu.prototype.zIndex + ';' + css;
- mxUtils.setPrefixedStyle(banner.style, 'transform', t1);
- mxUtils.setPrefixedStyle(banner.style, 'transition', 'all ' + delay + 'ms ease');
- banner.className = 'geBtn gePrimaryBtn' + ((small) ? ' geSmallBanner' : '');
- if (!Editor.isDarkMode())
- {
- mxUtils.setPrefixedStyle(banner.style, 'box-shadow', '1px 1px 2px 0px #ddd');
- }
- if (to != null)
- {
- mxUtils.setPrefixedStyle(banner.style, 'transform-origin', to);
- }
-
- if (!small)
- {
- var logo = document.createElement('img');
- logo.setAttribute('src', IMAGE_PATH + '/logo.png');
- logo.setAttribute('border', '0');
- logo.setAttribute('align', 'absmiddle');
- logo.style.cssText = 'margin-top:-4px;margin-left:8px;'+
- 'margin-right:12px;width:26px;height:26px;';
- banner.appendChild(logo);
- var img = document.createElement('img');
- img.setAttribute('src', Dialog.prototype.closeImage);
- img.setAttribute('title', mxResources.get((doNotShowAgainOnClose) ? 'doNotShowAgain' : 'close'));
- img.setAttribute('border', '0');
- img.style.cssText = ((small) ? 'right:6px;top:9px;' :
- 'right:10px;top:12px;') + 'position:absolute;filter:invert(1);padding:6px;margin:-6px;cursor:default;';
- banner.appendChild(img);
- }
- mxUtils.write(banner, text);
- document.body.appendChild(banner);
- this.bannerShowing = true;
-
- var div = document.createElement('div');
- div.style.cssText = 'display:flex;align-items:center;justify-content:center;' +
- 'padding-top:6px;font-size:11px;text-align:center;font-weight:normal;';
- var chk = document.createElement('input');
- chk.setAttribute('type', 'checkbox');
- chk.setAttribute('id', 'geDoNotShowAgainCheckbox');
- chk.style.marginRight = '6px';
-
- if (!doNotShowAgainOnClose)
- {
- div.appendChild(chk);
-
- var label = document.createElement('label');
- label.setAttribute('for', 'geDoNotShowAgainCheckbox');
- mxUtils.write(label, mxResources.get('doNotShowAgain'));
- div.appendChild(label);
- banner.style.paddingBottom = (small) ? '16px' : '30px';
- banner.appendChild(div);
- }
-
- var onclose = mxUtils.bind(this, function()
- {
- if (banner.parentNode != null)
- {
- banner.parentNode.removeChild(banner);
- this.bannerShowing = false;
-
- if (chk.checked || doNotShowAgainOnClose)
- {
- this['hideBanner' + id] = true;
-
- if (isLocalStorage && mxSettings.settings != null)
- {
- mxSettings.settings['close' + id] = Date.now();
- mxSettings.save();
- }
- }
- }
- });
-
- if (img != null)
- {
- mxEvent.addListener(img, 'click', mxUtils.bind(this, function(e)
- {
- mxEvent.consume(e);
- onclose();
- }));
- }
-
- var hide = mxUtils.bind(this, function()
- {
- mxUtils.setPrefixedStyle(banner.style, 'transform', t1);
-
- window.setTimeout(mxUtils.bind(this, function()
- {
- onclose();
- }), delay);
- });
-
- mxEvent.addListener(banner, 'click', mxUtils.bind(this, function(e)
- {
- var source = mxEvent.getSource(e);
-
- if (source != chk && source != label)
- {
- if (onclick != null)
- {
- onclick();
- }
-
- onclose();
- mxEvent.consume(e);
- }
- else
- {
- hide();
- }
- }));
-
- window.setTimeout(mxUtils.bind(this, function()
- {
- mxUtils.setPrefixedStyle(banner.style, 'transform', t2);
- }), delay / 2);
-
- window.setTimeout(hide, (small) ? 4000 : 30000);
- result = true;
- }
-
- return result;
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.setCurrentFile = function(file)
- {
- if (file != null)
- {
- file.opened = new Date();
- }
-
- this.currentFile = file;
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getCurrentFile = function()
- {
- return this.currentFile;
- };
-
- /**
- * Handling for canvas export.
- */
- EditorUi.prototype.isExportToCanvas = function()
- {
- return this.editor.isExportToCanvas();
- };
- /**
- *
- */
- EditorUi.prototype.createImageDataUri = function(canvas, xml, format, dpi)
- {
- var data = canvas.toDataURL('image/' + format);
-
- // Checks for valid output
- if (data != null && data.length > 6)
- {
- if (xml != null)
- {
- data = Editor.writeGraphModelToPng(data, 'tEXt', 'mxfile', encodeURIComponent(xml));
- }
-
- if (dpi > 0)
- {
- data = Editor.writeGraphModelToPng(data, 'pHYs', 'dpi', dpi);
- }
- }
- else
- {
- throw {message: mxResources.get('unknownError')};
- }
-
- return data;
- };
-
- /**
- *
- */
- EditorUi.prototype.saveCanvas = function(canvas, xml, format, ignorePageName, dpi)
- {
- var ext = ((format == 'jpeg') ? 'jpg' : format);
- var filename = this.getBaseFilename(ignorePageName) +
- ((xml != null) ? '.drawio' : '') + '.' + ext;
- var data = this.createImageDataUri(canvas, xml, format, dpi);
- this.saveData(filename, ext, data.substring(data.lastIndexOf(',') + 1), 'image/' + format, true);
- };
-
- /**
- * Returns true if files should be saved using <saveLocalFile>.
- */
- EditorUi.prototype.isLocalFileSave = function()
- {
- return ((urlParams['save'] != 'remote' && (mxClient.IS_IE ||
- (typeof window.Blob !== 'undefined' && typeof window.URL !== 'undefined')) &&
- document.documentMode != 9 && document.documentMode != 8 &&
- document.documentMode != 7) ||
- this.isOfflineApp() || mxClient.IS_IOS);
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.showTextDialog = function(title, text)
- {
- var dlg = new TextareaDialog(this, title, text, null, null, mxResources.get('close'));
- this.showDialog(dlg.container, 620, 460, true, true, null, null, null, null, true);
- dlg.init();
- document.execCommand('selectall', false, null);
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.doSaveLocalFile = function(data, filename, mimeType, base64Encoded, format, defaultExtension)
- {
- // Appends .drawio extension for XML files with no extension
- // to avoid the browser to automatically append .xml instead
- if (mimeType == 'text/xml' &&
- !/(\.drawio)$/i.test(filename) &&
- !/(\.xml)$/i.test(filename) &&
- !/(\.svg)$/i.test(filename) &&
- !/(\.html)$/i.test(filename))
- {
- defaultExtension = (defaultExtension != null) ? defaultExtension : 'drawio';
- filename = filename + '.' + defaultExtension;
- }
-
- // Newer versions of IE
- if (window.Blob && navigator.msSaveOrOpenBlob)
- {
- var blob = (base64Encoded) ?
- this.base64ToBlob(data, mimeType) :
- new Blob([data], {type: mimeType})
- navigator.msSaveOrOpenBlob(blob, filename);
- }
- // Older versions of IE (binary not supported)
- else if (mxClient.IS_IE)
- {
- var win = window.open('about:blank', '_blank');
-
- if (win == null)
- {
- mxUtils.popup(data, true);
- }
- else
- {
- win.document.write(data);
- win.document.close();
- win.document.execCommand('SaveAs', true, filename);
- win.close();
- }
- }
- else if (mxClient.IS_IOS && this.isOffline())
- {
- // Workaround for "WebKitBlobResource error 1" in mobile Safari
- if (!navigator.standalone && mimeType != null && mimeType.substring(0, 6) == 'image/')
- {
- this.openInNewWindow(data, mimeType, base64Encoded);
- }
- else
- {
- this.showTextDialog(filename + ':', data);
- }
- }
- else
- {
- var a = document.createElement('a');
-
- // Workaround for mxXmlRequest.simulate no longer working in PaleMoon
- // if this is used (ie PNG export broken after XML export in PaleMoon)
- // and for "WebKitBlobResource error 1" for all browsers on iOS.
- var useDownload = (navigator.userAgent == null ||
- navigator.userAgent.indexOf("PaleMoon/") < 0) &&
- typeof a.download !== 'undefined';
-
- // Workaround for Chromium 65 cross-domain anchor download issue
- if (mxClient.IS_GC && navigator.userAgent != null)
- {
- var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)
- var vers = raw ? parseInt(raw[2], 10) : false;
- useDownload = vers == 65 ? false : useDownload;
- }
-
- if (useDownload || this.isOffline())
- {
- a.href = URL.createObjectURL((base64Encoded) ?
- this.base64ToBlob(data, mimeType) :
- new Blob([data], {type: mimeType}));
-
- if (useDownload)
- {
- a.download = filename;
- }
- else
- {
- // Workaround for same window in Safari
- a.setAttribute('target', '_blank');
- }
- document.body.appendChild(a);
-
- try
- {
- window.setTimeout(function()
- {
- URL.revokeObjectURL(a.href);
- }, 20000);
- a.click();
- a.parentNode.removeChild(a);
- }
- catch (e)
- {
- // ignore
- }
- }
- else
- {
- var req = this.createEchoRequest(data, filename, mimeType, base64Encoded, format);
-
- req.simulate(document, '_blank');
- }
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.createEchoRequest = function(data, filename, mimeType, base64Encoded, format, base64Response)
- {
- var param = (typeof(pako) === 'undefined' || true) ? 'xml=' + encodeURIComponent(data) :
- 'data=' + encodeURIComponent(Graph.compress(data));
-
- return new mxXmlRequest(SAVE_URL, param +
- ((mimeType != null) ? '&mime=' + mimeType : '') +
- ((format != null) ? '&format=' + format : '') +
- ((base64Response != null) ? '&base64=' + base64Response : '') +
- ((filename != null) ? '&filename=' + encodeURIComponent(filename) : '') +
- ((base64Encoded) ? '&binary=1' : ''));
- };
-
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.saveLocalFile = function(data, filename, mimeType, base64Encoded, format, allowBrowser, allowTab, defaultExtension)
- {
- allowBrowser = (allowBrowser != null) ? allowBrowser : false;
- allowTab = (allowTab != null) ? allowTab : (format != 'vsdx') && (!mxClient.IS_IOS || !navigator.standalone);
- var saveFunction = mxUtils.bind(this, function(newTitle, mode, input, folderId)
- {
- try
- {
- // Opens a new window
- if (mode == '_blank')
- {
- if (mimeType != null && mimeType.substring(0, 6) == 'image/')
- {
- this.openInNewWindow(data, mimeType, base64Encoded);
- }
- else if (mimeType != null && mimeType.substring(0, 9) == 'text/html')
- {
- var dlg = new EmbedDialog(this, data);
- this.showDialog(dlg.container, 450, 240, true, true);
- dlg.init();
- }
- else
- {
- var win = window.open('about:blank');
-
- if (win == null)
- {
- mxUtils.popup(data, true);
- }
- else
- {
- win.document.write('<pre>' + mxUtils.htmlEntities(data, false) + '</pre>');
- win.document.close();
- }
- }
- }
- else if (mode == App.MODE_DEVICE || mode == 'download')
- {
- this.doSaveLocalFile(data, newTitle, mimeType, base64Encoded, null, defaultExtension);
- }
- else if (newTitle != null && newTitle.length > 0)
- {
- var saveFile = mxUtils.bind(this, function(folderId)
- {
- try
- {
- this.exportFile(data, newTitle, mimeType, base64Encoded, mode, folderId);
- }
- catch (e)
- {
- this.handleError(e);
- }
- });
- if (folderId != null)
- {
- saveFile(folderId);
- }
- else
- {
- this.pickFolder(mode, saveFile);
- }
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- });
-
- var disabled = [];
- if (!allowBrowser)
- {
- disabled.push(App.MODE_BROWSER);
- }
- if (!allowTab)
- {
- disabled.push('_blank');
- }
- var dlg = new SaveDialog(this, filename, mxUtils.bind(this, function(input, mode, folderId)
- {
- saveFunction(input.value, mode, input, folderId);
- this.hideDialog(null, null, dlg.container);
- }), disabled, data, mimeType, base64Encoded);
- this.showDialog(dlg.container, 420, 110, true, false, mxUtils.bind(this, function()
- {
- this.hideDialog();
- }));
-
- dlg.init();
- };
-
- /**
- *
- */
- EditorUi.prototype.openInNewWindow = function(data, mimeType, base64Encoded)
- {
- var win = window.open('about:blank');
-
- if (win == null || win.document == null)
- {
- mxUtils.popup(data, true);
- }
- else
- {
- if (mimeType == 'image/svg+xml' && !mxClient.IS_SVG)
- {
- win.document.write('<html><pre>' + mxUtils.htmlEntities(data, false) + '</pre></html>');
- win.document.close();
- }
- else
- {
- if (mimeType == 'image/svg+xml' && !base64Encoded)
- {
- win.document.write('<html>'+ data + '</html>');
- }
- else
- {
- var temp = (base64Encoded) ? data : btoa(unescape(encodeURIComponent(data)));
-
- win.document.write('<html><img style="max-width:100%;" src="data:' +
- mimeType + ';base64,' + temp + '"/></html>');
- }
-
- win.document.close();
- }
- }
- };
-
- var editoUiAddChromelessToolbarItems = EditorUi.prototype.addChromelessToolbarItems;
- /**
- * Image export in viewer is only allowed for same domain or hosted environments
- * but disabled to avoid cross domain image export in canvas which isn't allowed.
- */
- EditorUi.prototype.isChromelessImageExportEnabled = function()
- {
- return this.getServiceName() != 'draw.io' ||
- /.*\.draw\.io$/.test(window.location.hostname) ||
- /.*\.diagrams\.net$/.test(window.location.hostname);
- };
- /**
- * Creates a temporary graph instance for rendering off-screen content.
- */
- EditorUi.prototype.addChromelessToolbarItems = function(addButton)
- {
- if (urlParams['tags'] != null)
- {
- this.tagsComponent = null;
- this.tagsDialog = null;
-
- var tagsButton = addButton(mxUtils.bind(this, function(evt)
- {
- if (this.tagsComponent == null)
- {
- this.tagsComponent = this.editor.graph.createTagsDialog(mxUtils.bind(this, function()
- {
- return this.tagsDialog != null;
- }), true);
- this.tagsComponent.div.getElementsByTagName('div')[0].style.position = '';
- mxUtils.setPrefixedStyle(this.tagsComponent.div.style, 'borderRadius', '5px');
- this.tagsComponent.div.className = 'geScrollable';
- this.tagsComponent.div.style.maxHeight = '160px';
- this.tagsComponent.div.style.maxWidth = '120px';
- this.tagsComponent.div.style.padding = '4px';
- this.tagsComponent.div.style.overflow = 'auto';
- this.tagsComponent.div.style.height = 'auto';
- this.tagsComponent.div.style.position = 'fixed';
- this.tagsComponent.div.style.fontFamily = Editor.defaultHtmlFont;
- if (!mxClient.IS_IE && !mxClient.IS_IE11)
- {
- this.tagsComponent.div.style.backgroundColor = '#000000';
- this.tagsComponent.div.style.color = '#ffffff';
- mxUtils.setOpacity(this.tagsComponent.div, 80);
- }
- else
- {
- this.tagsComponent.div.style.backgroundColor = '#ffffff';
- this.tagsComponent.div.style.border = '2px solid black';
- this.tagsComponent.div.style.color = '#000000';
- }
- }
- if (this.tagsDialog != null)
- {
- this.tagsDialog.parentNode.removeChild(this.tagsDialog);
- this.tagsDialog = null;
- }
- else
- {
- this.tagsDialog = this.tagsComponent.div;
- mxEvent.addListener(this.tagsDialog, 'mouseleave', mxUtils.bind(this, function()
- {
- if (this.tagsDialog != null)
- {
- this.tagsDialog.parentNode.removeChild(this.tagsDialog);
- this.tagsDialog = null;
- }
- }));
-
- var r = tagsButton.getBoundingClientRect();
- this.tagsDialog.style.left = r.left + 'px';
- this.tagsDialog.style.bottom = parseInt(this.chromelessToolbar.style.bottom) +
- this.chromelessToolbar.offsetHeight + 4 + 'px';
-
- // Puts the dialog on top of the container z-index
- var style = mxUtils.getCurrentStyle(this.editor.graph.container);
- this.tagsDialog.style.zIndex = style.zIndex;
- document.body.appendChild(this.tagsDialog);
- this.tagsComponent.refresh();
- this.editor.fireEvent(new mxEventObject('tagsDialogShown'));
- }
-
- mxEvent.consume(evt);
- }), Editor.tagsImage, mxResources.get('tags'));
- // Shows/hides tags button depending on content
- var model = this.editor.graph.getModel();
- model.addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
- {
- var tags = this.editor.graph.getAllTags();
- tagsButton.style.display = (tags.length > 0) ? '' : 'none';
- }));
- }
-
- editoUiAddChromelessToolbarItems.apply(this, arguments);
- this.editor.addListener('tagsDialogShown', mxUtils.bind(this, function()
- {
- if (this.layersDialog != null)
- {
- this.layersDialog.parentNode.removeChild(this.layersDialog);
- this.layersDialog = null;
- }
- }));
- this.editor.addListener('layersDialogShown', mxUtils.bind(this, function()
- {
- if (this.tagsDialog != null)
- {
- this.tagsDialog.parentNode.removeChild(this.tagsDialog);
- this.tagsDialog = null;
- }
- }));
- this.editor.addListener('pageSelected', mxUtils.bind(this, function()
- {
- if (this.tagsDialog != null)
- {
- this.tagsDialog.parentNode.removeChild(this.tagsDialog);
- this.tagsDialog = null;
- }
-
- if (this.layersDialog != null)
- {
- this.layersDialog.parentNode.removeChild(this.layersDialog);
- this.layersDialog = null;
- }
- }));
- mxEvent.addListener(this.editor.graph.container, 'click', mxUtils.bind(this, function()
- {
- if (this.tagsDialog != null)
- {
- this.tagsDialog.parentNode.removeChild(this.tagsDialog);
- this.tagsDialog = null;
- }
-
- if (this.layersDialog != null)
- {
- this.layersDialog.parentNode.removeChild(this.layersDialog);
- this.layersDialog = null;
- }
- }));
- if (this.isExportToCanvas() && this.isChromelessImageExportEnabled())
- {
- this.exportDialog = null;
-
- var exportButton = addButton(mxUtils.bind(this, function(evt)
- {
- var clickHandler = mxUtils.bind(this, function()
- {
- mxEvent.removeListener(this.editor.graph.container, 'click', clickHandler);
-
- if (this.exportDialog != null)
- {
- this.exportDialog.parentNode.removeChild(this.exportDialog);
- this.exportDialog = null;
- }
- });
-
- if (this.exportDialog != null)
- {
- clickHandler.apply(this);
- }
- else
- {
- this.exportDialog = document.createElement('div');
- var r = exportButton.getBoundingClientRect();
-
- mxUtils.setPrefixedStyle(this.exportDialog.style, 'borderRadius', '5px');
- this.exportDialog.style.position = 'fixed';
- this.exportDialog.style.textAlign = 'center';
- this.exportDialog.style.fontFamily = Editor.defaultHtmlFont;
- this.exportDialog.style.backgroundColor = '#000000';
- this.exportDialog.style.width = '50px';
- this.exportDialog.style.height = '50px';
- this.exportDialog.style.padding = '4px 2px 4px 2px';
- this.exportDialog.style.color = '#ffffff';
- mxUtils.setOpacity(this.exportDialog, 80);
- this.exportDialog.style.left = r.left + 'px';
- this.exportDialog.style.bottom = parseInt(this.chromelessToolbar.style.bottom) +
- this.chromelessToolbar.offsetHeight + 4 + 'px';
-
- // Puts the dialog on top of the container z-index
- var style = mxUtils.getCurrentStyle(this.editor.graph.container);
- this.exportDialog.style.zIndex = style.zIndex;
-
- var spinner = new Spinner({
- lines: 8, // The number of lines to draw
- length: 6, // The length of each line
- width: 5, // The line thickness
- radius: 6, // The radius of the inner circle
- rotate: 0, // The rotation offset
- color: '#fff', // #rgb or #rrggbb
- speed: 1.5, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: false, // Whether to use hardware acceleration
- top: '28px',
- zIndex: 2e9 // The z-index (defaults to 2000000000)
- });
- spinner.spin(this.exportDialog);
- document.body.appendChild(this.exportDialog);
- mxEvent.addListener(this.editor.graph.container, 'click', clickHandler);
-
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas)
- {
- spinner.stop();
-
- this.exportDialog.style.width = 'auto';
- this.exportDialog.style.height = 'auto';
- this.exportDialog.style.padding = '10px';
-
- var data = this.createImageDataUri(canvas, null, 'png');
- var img = document.createElement('img');
-
- img.style.maxWidth = '140px';
- img.style.maxHeight = '140px';
- img.style.cursor = 'pointer';
- img.style.backgroundColor = 'white';
-
- img.setAttribute('title', mxResources.get('openInNewWindow'));
- img.setAttribute('border', '0');
- img.setAttribute('src', data);
-
- this.exportDialog.appendChild(img);
- mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
- {
- this.openInNewWindow(data.substring(data.indexOf(',') + 1), 'image/png', true);
- clickHandler.apply(this, arguments);
- }));
- }), null, this.thumbImageCache, null, mxUtils.bind(this, function(e)
- {
- spinner.stop();
-
- if (this.exportDialog != null && this.exportDialog.parentNode != null)
- {
- this.exportDialog.parentNode.removeChild(this.exportDialog);
- this.exportDialog = null;
- }
- this.handleError(e);
- }), null, null, null, null, null, null, null, Editor.defaultBorder);
- }
-
- mxEvent.consume(evt);
- }), Editor.cameraImage, mxResources.get('export'));
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.saveData = function(filename, format, data, mime, base64Encoded)
- {
- if (this.isLocalFileSave())
- {
- this.saveLocalFile(data, filename, mime, base64Encoded, format);
- }
- else
- {
- this.saveRequest(filename, format, mxUtils.bind(this, function(newTitle, base64)
- {
- return this.createEchoRequest(data, newTitle, mime, base64Encoded, format, base64);
- }), data, base64Encoded, mime);
- }
- };
-
- /**
- * Translates this point by the given vector.
- *
- * Last 3 argument are optional and must only be used if the data can be stored as is on the client
- * side without requiring a server roundtrip.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.saveRequest = function(filename, format, fn, data, base64Encoded, mimeType, allowTab)
- {
- allowTab = (allowTab != null) ? allowTab : !mxClient.IS_IOS || !navigator.standalone;
- var saveFunction = mxUtils.bind(this, function(newTitle, mode, input, folderId)
- {
- if (mode == '_blank' || newTitle != null && newTitle.length > 0)
- {
- var base64 = (mode == App.MODE_DEVICE || mode == 'download' || mode == null || mode == '_blank') ? '0' : '1';
- var xhr = fn((mode == '_blank') ? null : newTitle, base64);
-
- if (xhr != null)
- {
- if (mode == App.MODE_DEVICE || mode == 'download' || mode == '_blank')
- {
- xhr.simulate(document, '_blank');
- }
- else
- {
- var doSave = mxUtils.bind(this, function(folderId)
- {
- mimeType = (mimeType != null) ? mimeType : ((format == 'pdf') ?
- 'application/pdf' : 'image/' + format);
-
- // Workaround for no roundtrip required if data is available on client-side
- // TODO: Refactor the saveData/saveRequest call chain for local data
- if (data != null)
- {
- try
- {
- this.exportFile(data, newTitle, mimeType, true, mode, folderId);
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- else if (this.spinner.spin(document.body, mxResources.get('saving')))
- {
- // LATER: Catch possible mixed content error
- // see http://stackoverflow.com/questions/30646417/catching-mixed-content-error
- xhr.send(mxUtils.bind(this, function()
- {
- this.spinner.stop();
-
- if (xhr.getStatus() >= 200 && xhr.getStatus() <= 299)
- {
- try
- {
- this.exportFile(xhr.getText(), newTitle, mimeType, true, mode, folderId);
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- else
- {
- this.handleError({message: mxResources.get('errorSavingFile')});
- }
- }), mxUtils.bind(this, function(resp)
- {
- this.spinner.stop();
- this.handleError(resp);
- }));
- }
- });
- if (folderId != null)
- {
- doSave(folderId);
- }
- else
- {
- this.pickFolder(mode, doSave);
- }
- }
- }
- }
- });
-
- var disabled = [App.MODE_BROWSER];
- if (!allowTab)
- {
- disabled.push('_blank');
- }
- var dlg = new SaveDialog(this, filename, mxUtils.bind(this, function(input, mode, folderId)
- {
- saveFunction(input.value, mode, input, folderId);
- this.hideDialog(null, null, dlg.container);
- }), disabled, null, 'application/pdf');
- this.showDialog(dlg.container, 420, 110, true, false, mxUtils.bind(this, function()
- {
- this.hideDialog();
- }));
- dlg.init();
- };
- /**
- * Returns whether or not any services should be shown in dialogs
- */
- EditorUi.prototype.isServices = function(count)
- {
- var noServices = 1; //(mxClient.IS_IOS) ? 0 : 1;
- return count != noServices;
- };
- /**
- *
- */
- EditorUi.prototype.getEditBlankXml = function()
- {
- return this.getFileData(true);
- };
-
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mode, folderId)
- {
- // do nothing
- };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.getServiceForName = function(name)
- {
- return null;
- };
-
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.getTitleForService = function(name)
- {
- return mxResources.get(name);
- };
- /**
- * Hook for subclassers.
- */
- EditorUi.prototype.pickFolder = function(mode, fn, enabled)
- {
- fn(null);
- };
- /**
- *
- */
- EditorUi.prototype.exportSvg = function(scale, transparentBackground, ignoreSelection, addShadow,
- editable, embedImages, border, noCrop, currentPage, linkTarget, theme, exportType,
- embedFonts, saveFn)
- {
- if (this.spinner.spin(document.body, mxResources.get('exporting'), mxUtils.bind(this, function(err)
- {
- Editor.addRetryToError(err, mxUtils.bind(this, function()
- {
- this.exportSvg(scale, transparentBackground, ignoreSelection, addShadow,
- editable, embedImages, border, noCrop, currentPage, linkTarget,
- theme, exportType, embedFonts, saveFn);
- }));
-
- this.handleError(err);
- })))
- {
- try
- {
- var selectionEmpty = this.editor.graph.isSelectionEmpty();
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
- var bg = (transparentBackground) ? null : this.editor.graph.background;
-
- if (bg == mxConstants.NONE)
- {
- bg = null;
- }
-
- // Handles special case where background is null but transparent is false
- if (bg == null && transparentBackground == false)
- {
- bg = (theme == 'dark' && !Editor.enableCssDarkMode) ? Editor.darkColor : '#ffffff';
- }
-
- // Removes global foreignObject warning if image fallback is added for text
- var graphAddForeignObjectWarning = this.editor.graph.addForeignObjectWarning;
- if (Editor.foreignObjectImages)
- {
- this.editor.graph.addForeignObjectWarning = function() {};
- }
- var svgRoot = this.editor.graph.getSvg(bg, scale, border, noCrop, null,
- ignoreSelection, null, null, (linkTarget == 'blank') ? '_blank' :
- ((linkTarget == 'self') ? '_top' : null), null, !embedFonts,
- theme, exportType);
- this.editor.graph.addForeignObjectWarning = graphAddForeignObjectWarning;
-
- if (addShadow)
- {
- this.editor.graph.addSvgShadow(svgRoot);
- }
-
- var filename = this.getBaseFilename() + ((editable) ? '.drawio' : '') + '.svg';
- saveFn = (saveFn != null) ? saveFn : mxUtils.bind(this, function(svg)
- {
- if (this.isLocalFileSave() || svg.length <= MAX_REQUEST_SIZE)
- {
- this.saveData(filename, 'svg', svg, 'image/svg+xml');
- }
- else
- {
- this.handleError({message: mxResources.get('drawingTooLarge')},
- mxResources.get('error'), mxUtils.bind(this, function()
- {
- mxUtils.popup(svg);
- }));
- }
- });
-
- var doSave = mxUtils.bind(this, function(svgRoot)
- {
- this.spinner.stop();
-
- if (editable)
- {
- svgRoot.setAttribute('content', this.getFileData(true, null, null,
- null, ignoreSelection, currentPage, null, null, null, null,
- null, scale, border));
- }
- saveFn(Graph.xmlDeclaration + '\n' + ((editable) ?
- Graph.svgFileComment + '\n' : '') +
- Graph.svgDoctype + '\n' +
- mxUtils.getXml(svgRoot));
- });
- // Adds CSS
- if (this.editor.graph.mathEnabled)
- {
- this.editor.addMathCss(svgRoot);
- }
-
- // Replaces alternate content
- var processResult = mxUtils.bind(this, function(svgRoot)
- {
- // Fixes ignored SVG data URIs for Office
- if (Editor.replaceSvgDataUris && embedImages)
- {
- EditorUi.embedSvgImages(svgRoot);
- }
- // Improves foreignObject fallback for Office/Inkscape
- if (Editor.foreignObjectImages && embedFonts)
- {
- this.replaceAlternateContent(svgRoot, theme, doSave);
- }
- else
- {
- doSave(svgRoot);
- }
- });
-
- var done = mxUtils.bind(this, function(svgRoot)
- {
- if (embedImages && !this.isOffline() && this.canvasSupported)
- {
- // Caches images
- if (this.thumbImageCache == null)
- {
- this.thumbImageCache = new Object();
- }
-
- this.editor.convertImages(svgRoot, processResult, this.thumbImageCache);
- }
- else
- {
- processResult(svgRoot);
- }
- });
- if (embedFonts)
- {
- this.embedFonts(svgRoot, done);
- }
- else
- {
- this.editor.addFontCss(svgRoot);
- done(svgRoot);
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- };
-
- /**
- * Replaces SVG data URIs in images with the actual SVG for
- * the images to be supported in apps like Powerpoint.
- */
- EditorUi.prototype.replaceAlternateContent = function(root, theme, callback)
- {
- // Collects CSS
- var css = '';
- var styles = root.getElementsByTagName('style');
- for (var i = 0; i < styles.length; i++)
- {
- css += styles[i].innerHTML;
- }
-
- // Replaces alternate content with image of text
- var switches = root.getElementsByTagName('switch');
- counter = switches.length;
- var done = mxUtils.bind(this, function()
- {
- counter--;
- if (counter == 0)
- {
- callback(root);
- }
- });
- // Caching for HTML size and images
- var sizeCache = {};
- var imgCache = {};
- var convert = [];
- function replaceElt(switchElt, x, y, size, src)
- {
- var node = mxUtils.createElementNs(root.ownerDocument,
- mxConstants.NS_SVG, 'image');
-
- node.setAttribute('x', x);
- node.setAttribute('y', y + 0.5);
- node.setAttribute('width', size.width);
- node.setAttribute('height', size.height);
- // Workaround for missing namespace support
- if (node.setAttributeNS == null)
- {
- node.setAttribute('xlink:href', src);
- }
- else
- {
- node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', src);
- }
-
- switchElt.replaceChild(node, switchElt.lastChild);
- done();
- };
- // Create an image for the given foreignObject
- for (var i = 0; i < switches.length; i++)
- {
- (mxUtils.bind(this, function(switchElt)
- {
- var divs = switchElt.getElementsByTagName('div');
- if (divs.length > 0)
- {
- try
- {
- var temp = divs[0].cloneNode(true);
- var x = parseInt(temp.style.marginLeft);
- var y = parseInt(temp.style.paddingTop);
- var measure = temp.getElementsByTagName('div')[1];
- var rl = false;
- var vertical = false;
- var alignItems = temp.style.alignItems;
- var justifyContent = temp.style.justifyContent;
- if (temp.style.writingMode != null)
- {
- var writingMode = temp.style.writingMode;
- vertical = writingMode.substring(0, 9) == 'vertical-';
- rl = writingMode.substring(writingMode.length - 3) == '-rl';
- }
- var h1 = (!vertical && temp.style.height == '1px') ||
- (vertical && temp.style.width == '1px');
- var w1 = (!vertical && temp.style.width == '1px') ||
- (vertical && temp.style.height == '1px');
- if (h1)
- {
- if (vertical && rl)
- {
- temp.style.alignItems = 'unsafe flex-end';
- }
- else
- {
- temp.style.alignItems = 'unsafe flex-start';
- }
- }
- if (w1)
- {
- temp.style.justifyContent = 'unsafe flex-start';
- }
- temp.style.paddingTop = '';
- temp.style.marginLeft = '';
- var html = temp.outerHTML;
- var size = sizeCache[html];
- if (size == null)
- {
- temp.style.position = 'absolute';
- temp.style.visibility = 'hidden';
- document.body.appendChild(temp);
- var w = 1;
- if (temp.style.width == '1px')
- {
- w = measure.offsetWidth;
- }
- else
- {
- w = parseInt(temp.style.width);
- }
- var h = 1;
- if (temp.style.height == '1px')
- {
- h = measure.offsetHeight;
- }
- else
- {
- h = parseInt(temp.style.height);
- }
- document.body.removeChild(temp);
- size = new mxRectangle(x, y, w, h);
- sizeCache[html] = size;
- }
-
- if (temp.style.height == '1px')
- {
- if (alignItems == 'unsafe center')
- {
- y -= size.height / 2;
- }
- else if (alignItems == 'unsafe flex-end')
- {
- y -= size.height;
- }
- }
-
- if (temp.style.width == '1px')
- {
- if ((!vertical && justifyContent == 'unsafe center') ||
- (vertical && alignItems == 'unsafe center'))
- {
- x -= size.width / 2;
- }
- else if ((!vertical && justifyContent == 'unsafe flex-end') ||
- (vertical && ((!rl && alignItems == 'unsafe flex-end') ||
- (rl && alignItems == 'unsafe flex-start'))))
- {
- x -= size.width;
- }
- }
- // Adds padding for font metrics
- size = new mxRectangle(x, y, size.width, size.height +
- (parseInt(measure.style.fontSize) / 4));
- // Sequence of async conversion to images
- convert.push(mxUtils.bind(this, function(next)
- {
- var cachedSrc = imgCache[html];
- if (cachedSrc == null)
- {
- Graph.htmlToPng(html, size.width, size.height, mxUtils.bind(this, function(src)
- {
- replaceElt(switchElt, x, y, size, src);
- imgCache[html] = src;
- next();
- }), css);
- }
- else
- {
- replaceElt(switchElt, x, y, size, cachedSrc);
- next();
- }
- }));
- }
- catch (e)
- {
- done();
- }
- }
- else
- {
- done();
- }
- }))(switches[i]);
- }
- if (counter == 0)
- {
- callback(root);
- }
-
- var i = 0;
- function next()
- {
- if (i < convert.length)
- {
- convert[i++](next);
- }
- };
- next();
- };
- /**
- *
- */
- EditorUi.prototype.addRadiobox = function(div, radioGroupName, label, checked, disabled, disableNewline, visible)
- {
- return this.addCheckbox(div, label, checked, disabled, disableNewline, visible, true, radioGroupName);
- };
-
- /**
- *
- */
- EditorUi.prototype.addCheckbox = function(div, label, checked, disabled, disableNewline, visible, asRadio, radioGroupName)
- {
- visible = (visible != null) ? visible : true;
-
- var cb = document.createElement('input');
- cb.style.marginRight = '8px';
- cb.style.marginTop = '16px';
- cb.setAttribute('type', asRadio? 'radio' : 'checkbox');
- var id = 'geCheckbox-' + Editor.guid();
- cb.id = id;
-
- if (radioGroupName != null)
- {
- cb.setAttribute('name', radioGroupName);
- }
-
- if (checked)
- {
- cb.setAttribute('checked', 'checked');
- cb.defaultChecked = true;
- }
-
- if (disabled)
- {
- cb.setAttribute('disabled', 'disabled');
- }
-
- if (visible)
- {
- div.appendChild(cb);
-
- var lbl = document.createElement('label');
- mxUtils.write(lbl, label);
- lbl.setAttribute('for', id);
- div.appendChild(lbl);
- if (!disableNewline)
- {
- mxUtils.br(div);
- }
- }
-
- return cb;
- };
-
- /**
- *
- */
- EditorUi.prototype.addEditButton = function(div, lightbox)
- {
- var edit = this.addCheckbox(div, mxResources.get('edit') + ':', true, null, true);
- edit.style.marginLeft = '24px';
-
- var file = this.getCurrentFile();
- var editUrl = '';
-
- if (file != null && file.getMode() != App.MODE_DEVICE && file.getMode() != App.MODE_BROWSER)
- {
- editUrl = window.location.href;
- }
-
- var editSelect = document.createElement('select');
- editSelect.style.maxWidth = '200px';
- editSelect.style.width = 'auto';
- editSelect.style.marginLeft = '8px';
- editSelect.style.marginRight = '10px';
- editSelect.className = 'geBtn';
- var blankOption = document.createElement('option');
- blankOption.setAttribute('value', 'blank');
- mxUtils.write(blankOption, mxResources.get('makeCopy'));
- editSelect.appendChild(blankOption);
- var customOption = document.createElement('option');
- customOption.setAttribute('value', 'custom');
- mxUtils.write(customOption, mxResources.get('custom') + '...');
- editSelect.appendChild(customOption);
-
- div.appendChild(editSelect);
-
- mxEvent.addListener(editSelect, 'change', mxUtils.bind(this, function()
- {
- if (editSelect.value == 'custom')
- {
- var dlg2 = new FilenameDialog(this, editUrl, mxResources.get('ok'), function(value)
- {
- if (value != null)
- {
- editUrl = value;
- customOption.setAttribute('title', value);
- }
- else
- {
- editSelect.value = 'blank';
- customOption.removeAttribute('title');
- }
- }, mxResources.get('url'), null, null, null, null, function()
- {
- editSelect.value = 'blank';
- });
- this.showDialog(dlg2.container, 300, 80, true, false);
- dlg2.init();
- }
- }));
-
- mxEvent.addListener(edit, 'change', mxUtils.bind(this, function()
- {
- if (edit.checked && (lightbox == null || lightbox.checked))
- {
- editSelect.removeAttribute('disabled');
- }
- else
- {
- editSelect.setAttribute('disabled', 'disabled');
- }
- }));
- mxUtils.br(div);
-
- return {
- getLink: function()
- {
- return (edit.checked) ? ((editSelect.value === 'blank') ? '_blank' : editUrl) : null;
- },
- getEditInput: function()
- {
- return edit;
- },
- getEditSelect: function()
- {
- return editSelect;
- }
- };
- }
-
- /**
- *
- */
- EditorUi.prototype.addLinkSection = function(div, showFrameOption)
- {
- mxUtils.write(div, mxResources.get('links') + ':');
- var linkSelect = document.createElement('select');
- linkSelect.style.width = '100px';
- linkSelect.style.padding = '0px';
- linkSelect.style.marginLeft = '8px';
- linkSelect.style.marginRight = '10px';
- linkSelect.className = 'geBtn';
- var autoOption = document.createElement('option');
- autoOption.setAttribute('value', 'auto');
- mxUtils.write(autoOption, mxResources.get('automatic'));
- linkSelect.appendChild(autoOption);
- var blankOption = document.createElement('option');
- blankOption.setAttribute('value', 'blank');
- mxUtils.write(blankOption, mxResources.get('openInNewWindow'));
- linkSelect.appendChild(blankOption);
- var selfOption = document.createElement('option');
- selfOption.setAttribute('value', 'self');
- mxUtils.write(selfOption, mxResources.get('openInThisWindow'));
- linkSelect.appendChild(selfOption);
- if (showFrameOption)
- {
- var frameOption = document.createElement('option');
- frameOption.setAttribute('value', 'frame');
- mxUtils.write(frameOption, mxResources.get('openInThisWindow') +
- ' (' + mxResources.get('iframe') + ')');
- linkSelect.appendChild(frameOption);
- }
-
- div.appendChild(linkSelect);
-
- mxUtils.write(div, mxResources.get('borderColor') + ':');
- var linkColor = '#0000ff';
- var linkButton = null;
-
- function updateLinkColor()
- {
- var div = document.createElement('div');
- div.style.width = '100%';
- div.style.height = '100%';
- div.style.boxSizing = 'border-box';
- if (linkColor != null && linkColor != mxConstants.NONE)
- {
- div.style.border = '1px solid black';
- div.style.backgroundColor = linkColor;
- }
- else
- {
- div.style.backgroundPosition = 'center center';
- div.style.backgroundRepeat = 'no-repeat';
- div.style.backgroundImage = 'url(\'' + Dialog.prototype.closeImage + '\')';
- }
- linkButton.innerText = '';
- linkButton.appendChild(div);
- };
-
- linkButton = mxUtils.button('', mxUtils.bind(this, function(evt)
- {
- this.pickColor(linkColor || 'none', function(color)
- {
- linkColor = color;
- updateLinkColor();
- });
-
- mxEvent.consume(evt);
- }));
- updateLinkColor();
- linkButton.style.padding = (mxClient.IS_FF) ? '4px 2px 4px 2px' : '4px';
- linkButton.style.marginLeft = '4px';
- linkButton.style.height = '22px';
- linkButton.style.width = '22px';
- linkButton.style.position = 'relative';
- linkButton.style.top = (mxClient.IS_IE || mxClient.IS_IE11 || mxClient.IS_EDGE) ? '6px' : '1px';
- linkButton.className = 'geColorBtn';
- div.appendChild(linkButton);
- mxUtils.br(div);
-
- return {
- getColor: function()
- {
- return linkColor;
- },
- getTarget: function()
- {
- return linkSelect.value;
- },
- focus: function()
- {
- linkSelect.focus();
- }
- };
- }
- /**
- *
- */
- EditorUi.prototype.createUrlParameters = function(linkTarget, linkColor, lightbox, editLink, layers, params)
- {
- params = (params != null) ? params : [];
-
- if (lightbox)
- {
- params.push('lightbox=1');
- if (linkTarget != 'auto')
- {
- params.push('target=' + linkTarget);
- }
-
- if (linkColor != null && linkColor != mxConstants.NONE)
- {
- params.push('highlight=' + ((linkColor.charAt(0) == '#') ?
- linkColor.substring(1) : linkColor));
- }
-
- if (editLink != null && editLink.length > 0)
- {
- params.push('edit=' + encodeURIComponent(editLink));
- }
-
- if (layers)
- {
- params.push('layers=1');
- }
-
- if (this.editor.graph.foldingEnabled)
- {
- params.push('nav=1');
- }
- }
-
- return params;
- };
-
- /**
- *
- */
- EditorUi.prototype.createLink = function(linkTarget, linkColor, allPages, lightbox, editLink, layers,
- url, ignoreFile, params, useOpenParameter, currentPage)
- {
- var file = this.getCurrentFile();
- params = this.createUrlParameters(linkTarget, linkColor,
- lightbox, editLink, layers, params);
- var addTitle = true;
- var data = '';
- if (url != null)
- {
- data = '#U' + encodeURIComponent(url);
- }
- else
- {
- // Fallback to non-public URL for Drive files
- if (!ignoreFile && file != null && file.getHash() != '')
- {
- data = '#' + file.getHash();
- addTitle = false;
- }
- else
- {
- data = '#R' + encodeURIComponent((allPages) ?
- this.getFileData(true, null, null, null, null, null, null, true, null, false) :
- Graph.compress(mxUtils.getXml(this.editor.getGraphXml())))
- }
- }
- if (addTitle && file != null && file.getTitle() != null && file.getTitle() != this.defaultFilename)
- {
- params.push('title=' + encodeURIComponent(file.getTitle()));
- }
- if (useOpenParameter && data.length > 1)
- {
- params.push('open=' + data.substring(1));
- data = '';
- }
- if (currentPage && this.currentPage != null)
- {
- params.push('page-id=' + this.currentPage.getId());
- }
- // Uses current host for non-public GitLab and GitHub files
- return ((lightbox && (url != null || (file != null &&
- file.getMode() != App.MODE_GITHUB && file.getMode() != App.MODE_GITLAB))) ?
- EditorUi.lightboxHost : (((mxClient.IS_CHROMEAPP || EditorUi.isElectronApp ||
- !(/.*\.draw\.io$/.test(window.location.hostname))) ?
- EditorUi.drawHost : 'https://' + window.location.host))) + '/' +
- ((params.length > 0) ? '?' + params.join('&') : '') + data;
- };
-
- /**
- *
- */
- EditorUi.prototype.createHtml = function(publicUrl, zoomEnabled, initialZoom, linkTarget,
- linkColor, fit, allPages, layers, tags, lightbox, editLink, fn)
- {
- var s = this.getBasenames();
- var data = {};
-
- if (linkColor != '' && linkColor != mxConstants.NONE)
- {
- data.highlight = linkColor;
- }
-
- if (linkTarget !== 'auto')
- {
- data.target = linkTarget;
- }
-
- if (!lightbox)
- {
- data.lightbox = false;
- }
-
- data.nav = this.editor.graph.foldingEnabled;
- var zoom = parseInt(initialZoom);
-
- if (!isNaN(zoom) && zoom != 100)
- {
- data.zoom = zoom / 100;
- }
-
- var tb = [];
-
- if (allPages)
- {
- tb.push('pages');
- data.resize = true;
-
- if (this.pages != null && this.currentPage != null)
- {
- data.page = mxUtils.indexOf(this.pages, this.currentPage);
- }
- }
-
- if (zoomEnabled)
- {
- tb.push('zoom');
- data.resize = true;
- }
-
- if (layers)
- {
- tb.push('layers');
- }
- if (tags)
- {
- tb.push('tags');
- }
-
- if (tb.length > 0)
- {
- if (lightbox)
- {
- tb.push('lightbox');
- }
-
- data.toolbar = tb.join(' ');
- }
- if (editLink != null && editLink.length > 0)
- {
- data.edit = editLink;
- }
-
- if (publicUrl != null)
- {
- data.url = publicUrl;
- }
- else
- {
- data.xml = this.getFileData(true, null, null, null, null, !allPages);
- }
-
- var value = '<div class="mxgraph" style="' +
- ((fit) ? 'max-width:100%;' : '') +
- ((tb != '') ? 'border:1px solid transparent;' : '') +
- '" data-mxgraph="' + mxUtils.htmlEntities(JSON.stringify(data)) + '"></div>';
-
- var fetchParam = (publicUrl != null) ? '&fetch=' + encodeURIComponent(publicUrl) : '';
- var s2 = (fetchParam.length > 0) ? (((urlParams['dev'] == '1') ?
- 'https://test.draw.io/embed2.js?dev=1' : EditorUi.lightboxHost + '/embed2.js?')) + fetchParam :
- (((urlParams['dev'] == '1') ? 'https://test.draw.io/js/viewer-static.min.js' :
- window.DRAWIO_VIEWER_URL ? window.DRAWIO_VIEWER_URL : EditorUi.lightboxHost + '/js/viewer-static.min.js'));
- var src = '<script type="text/javascript" src="' + s2 + '"></script>';
-
- fn(value, src);
- };
- /**
- *
- */
- EditorUi.prototype.showHtmlDialog = function(btnLabel, helpLink, publicUrl, fn)
- {
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
-
- var hd = document.createElement('h3');
- mxUtils.write(hd, mxResources.get('html'));
- hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:12px';
- div.appendChild(hd);
- var radioSection = document.createElement('div');
- radioSection.style.cssText = 'border-bottom:1px solid lightGray;padding-bottom:8px;margin-bottom:12px;';
- var publicUrlRadio = document.createElement('input');
- publicUrlRadio.style.cssText = 'margin-right:8px;margin-top:8px;margin-bottom:8px;';
- publicUrlRadio.setAttribute('value', 'url');
- publicUrlRadio.setAttribute('type', 'radio');
- publicUrlRadio.setAttribute('name', 'type-embedhtmldialog');
- var copyRadio = publicUrlRadio.cloneNode(true);
- copyRadio.setAttribute('value', 'copy');
- radioSection.appendChild(copyRadio);
-
- var span = document.createElement('span');
- mxUtils.write(span, mxResources.get('includeCopyOfMyDiagram'));
- radioSection.appendChild(span);
-
- mxUtils.br(radioSection);
- radioSection.appendChild(publicUrlRadio);
- var span = document.createElement('span');
- mxUtils.write(span, mxResources.get('publicDiagramUrl'));
- radioSection.appendChild(span);
- var file = this.getCurrentFile();
-
- if (publicUrl == null && file != null && file.constructor == window.DriveFile)
- {
- var testLink = document.createElement('a');
- testLink.style.paddingLeft = '12px';
- testLink.style.color = 'gray';
- testLink.style.cursor = 'pointer';
- mxUtils.write(testLink, mxResources.get('share'));
- radioSection.appendChild(testLink);
-
- mxEvent.addListener(testLink, 'click', mxUtils.bind(this, function()
- {
- this.hideDialog();
- this.drive.showPermissions(file.getId(), file);
- }));
- }
- copyRadio.setAttribute('checked', 'checked');
-
- if (publicUrl == null)
- {
- publicUrlRadio.setAttribute('disabled', 'disabled');
- }
- div.appendChild(radioSection);
- var linkSection = this.addLinkSection(div);
- var zoom = this.addCheckbox(div, mxResources.get('zoom'), true, null, true);
- mxUtils.write(div, ':');
-
- var zoomInput = document.createElement('input');
- zoomInput.setAttribute('type', 'text');
- zoomInput.style.marginRight = '16px';
- zoomInput.style.width = '60px';
- zoomInput.style.marginLeft = '4px';
- zoomInput.style.marginRight = '12px';
- zoomInput.value = '100%';
-
- div.appendChild(zoomInput);
- var fit = this.addCheckbox(div, mxResources.get('fit'), true);
- var hasPages = this.pages != null && this.pages.length > 1;
- var allPages = allPages = this.addCheckbox(div, mxResources.get('allPages'), hasPages, !hasPages);
- var layers = this.addCheckbox(div, mxResources.get('layers'), true);
- var tags = this.addCheckbox(div, mxResources.get('tags'), true);
- var lightbox = this.addCheckbox(div, mxResources.get('lightbox'), true);
- var editSection = null;
- var h = 380;
- if (EditorUi.enableHtmlEditOption)
- {
- editSection = this.addEditButton(div, lightbox);
- var edit = editSection.getEditInput();
- edit.style.marginBottom = '16px';
- h += 50;
- mxEvent.addListener(lightbox, 'change', function()
- {
- if (lightbox.checked)
- {
- edit.removeAttribute('disabled');
- }
- else
- {
- edit.setAttribute('disabled', 'disabled');
- }
-
- if (edit.checked && lightbox.checked)
- {
- editSection.getEditSelect().removeAttribute('disabled');
- }
- else
- {
- editSection.getEditSelect().setAttribute('disabled', 'disabled');
- }
- });
- }
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- fn((publicUrlRadio.checked) ? publicUrl : null, zoom.checked, zoomInput.value, linkSection.getTarget(),
- linkSection.getColor(), fit.checked, allPages.checked, layers.checked, tags.checked,
- lightbox.checked, (editSection != null) ? editSection.getLink() : null);
- }), null, btnLabel, helpLink);
- this.showDialog(dlg.container, 340, h, true, true);
- copyRadio.focus();
- };
-
- /**
- *
- */
- EditorUi.prototype.showPublishLinkDialog = function(title, width, height, showFrameOption,
- helpLink, footer, publicUrl, file, fn)
- {
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
-
- var hd = document.createElement('h3');
- mxUtils.write(hd, title || mxResources.get('publish'));
- hd.style.width = '100%';
- hd.style.textAlign = 'center';
- hd.style.marginTop = '2px';
- hd.style.marginBottom = '20px';
- div.appendChild(hd);
-
- var linkSelect = document.createElement('select');
- var dy = 30;
- linkSelect.className = 'geBtn';
- linkSelect.style.marginBottom = '8px';
- linkSelect.style.marginLeft = '0px';
- linkSelect.style.width = '100%';
- linkSelect.style.boxSizing = 'border-box';
- if (file == null || file.getHash() == '')
- {
- helpLink = (helpLink != null) ? helpLink : 'https://www.drawio.com/doc/faq/publish-diagram-as-link';
- var makeCopy = document.createElement('option');
- mxUtils.write(makeCopy, mxResources.get('makeCopy'));
- makeCopy.setAttribute('value', 'copy');
- linkSelect.appendChild(makeCopy);
- }
- var authRequired = document.createElement('option');
- mxUtils.write(authRequired, mxResources.get('authorizationRequired'));
- authRequired.setAttribute('value', 'auth');
- linkSelect.appendChild(authRequired);
- if (file == null || file.getHash() == '')
- {
- authRequired.setAttribute('disabled', 'disabled');
- }
- var publicLink = document.createElement('option');
- publicLink.setAttribute('value', 'public');
- linkSelect.appendChild(publicLink);
- if (publicUrl != null)
- {
- mxUtils.write(publicLink, mxResources.get('publicDiagramUrl'));
- publicLink.setAttribute('title', publicUrl);
- linkSelect.value = 'public';
- }
- else
- {
- mxUtils.write(publicLink, mxResources.get('publicDiagramUrl') +
- ' (' + mxResources.get('diagramIsNotPublic') + ')');
- publicLink.setAttribute('disabled', 'disabled');
- }
-
- div.appendChild(linkSelect);
- mxUtils.br(div);
- linkSelect.focus();
-
- var widthInput = null;
- var heightInput = null;
-
- if (width != null || height != null)
- {
- dy += 30;
- mxUtils.write(div, mxResources.get('width') + ':');
- widthInput = document.createElement('input');
- widthInput.setAttribute('type', 'text');
- widthInput.style.marginRight = '16px';
- widthInput.style.width = '50px';
- widthInput.style.marginLeft = '6px';
- widthInput.style.marginRight = '16px';
- widthInput.style.marginBottom = '10px';
- widthInput.value = '100%';
-
- div.appendChild(widthInput);
- mxUtils.write(div, mxResources.get('height') + ':');
-
- heightInput = document.createElement('input');
- heightInput.setAttribute('type', 'text');
- heightInput.style.width = '50px';
- heightInput.style.marginLeft = '6px';
- heightInput.style.marginBottom = '10px';
- heightInput.value = height + 'px';
-
- div.appendChild(heightInput);
- mxUtils.br(div);
- }
-
- var linkSection = this.addLinkSection(div, showFrameOption);
- var currentPage = null;
-
- if (this.pages != null && this.currentPage != null &&
- this.getPageIndex(this.currentPage) > 0)
- {
- dy += 30;
- var name = (this.currentPage != null) ? this.currentPage.getName() : '';
- if (name.length > 16)
- {
- name = name.substring(0, 16) + '...';
- }
- currentPage = this.addCheckbox(div, mxResources.get('selectedPage') + ': ' + name);
- }
- var lightbox = this.addCheckbox(div, mxResources.get('lightbox'),
- true, null, null, !showFrameOption);
- var editSection = this.addEditButton(div, lightbox);
- var edit = editSection.getEditInput();
- // Cannot disable lightbox in iframes
- if (showFrameOption)
- {
- edit.style.marginLeft = lightbox.style.marginLeft;
- lightbox.style.display = 'none';
- dy -= 20;
- }
-
- var layers = this.addCheckbox(div, mxResources.get('layers'), true);
- layers.style.marginLeft = edit.style.marginLeft;
- layers.style.marginTop = '8px';
-
- var tags = this.addCheckbox(div, mxResources.get('tags'), true);
- tags.style.marginLeft = edit.style.marginLeft;
- tags.style.marginBottom = '16px';
- tags.style.marginTop = '16px';
- mxEvent.addListener(lightbox, 'change', function()
- {
- if (lightbox.checked)
- {
- layers.removeAttribute('disabled');
- edit.removeAttribute('disabled');
- tags.removeAttribute('disabled');
- }
- else
- {
- layers.setAttribute('disabled', 'disabled');
- edit.setAttribute('disabled', 'disabled');
- tags.setAttribute('disabled', 'disabled');
- }
-
- if (edit.checked && lightbox.checked)
- {
- editSection.getEditSelect().removeAttribute('disabled');
- }
- else
- {
- editSection.getEditSelect().setAttribute('disabled', 'disabled');
- }
- });
-
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- fn(linkSection.getTarget(), linkSection.getColor(),
- (currentPage == null) ? false : currentPage.checked,
- lightbox.checked, editSection.getLink(), layers.checked,
- (widthInput != null) ? widthInput.value : null,
- (heightInput != null) ? heightInput.value : null,
- tags.checked, linkSelect.value);
- }), null, mxResources.get('create'), helpLink, footer);
- this.showDialog(dlg.container, 340, 296 + dy, true, true);
-
- if (widthInput != null)
- {
- widthInput.focus();
-
- if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5)
- {
- widthInput.select();
- }
- else
- {
- document.execCommand('selectAll', false, null);
- }
- }
- else (linkSelect.parentNode == null)
- {
- linkSection.focus();
- }
- };
- /**
- *
- */
- EditorUi.prototype.showRemoteExportDialog = function(btnLabel, helpLink, callback, hideInclude, showZoomBorder)
- {
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
-
- var hd = document.createElement('h3');
- mxUtils.write(hd, mxResources.get('image'));
- hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:' + (showZoomBorder? '10' : '4') +'px';
- div.appendChild(hd);
- if (showZoomBorder)
- {
- mxUtils.write(div, mxResources.get('zoom') + ':');
- var zoomInput = document.createElement('input');
- zoomInput.setAttribute('type', 'text');
- zoomInput.style.marginRight = '16px';
- zoomInput.style.width = '60px';
- zoomInput.style.marginLeft = '4px';
- zoomInput.style.marginRight = '12px';
- zoomInput.value = this.lastExportZoom || '100%';
- div.appendChild(zoomInput);
-
- mxUtils.write(div, mxResources.get('borderWidth') + ':');
- var borderInput = document.createElement('input');
- borderInput.setAttribute('type', 'text');
- borderInput.style.marginRight = '16px';
- borderInput.style.width = '60px';
- borderInput.style.marginLeft = '4px';
- borderInput.value = this.lastExportBorder || '0';
- div.appendChild(borderInput);
- mxUtils.br(div);
- }
-
- var selection = this.addCheckbox(div, mxResources.get('selectionOnly'), false,
- this.editor.graph.isSelectionEmpty());
- var include = (hideInclude) ? null : this.addCheckbox(div, mxResources.get('includeCopyOfMyDiagram'),
- Editor.defaultIncludeDiagram);
-
- var graph = this.editor.graph;
- var transparent = (hideInclude) ? null : this.addCheckbox(div, mxResources.get('transparentBackground'),
- graph.background == mxConstants.NONE || graph.background == null);
- if (transparent != null)
- {
- transparent.style.marginBottom = '16px';
- }
-
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- var scale = parseInt(zoomInput.value) / 100 || 1;
- var border = parseInt(borderInput.value) || 0;
-
- callback(!selection.checked, (include != null) ? include.checked : false,
- (transparent != null) ? transparent.checked : false, scale, border);
- }), null, btnLabel, helpLink);
- this.showDialog(dlg.container, 300, (showZoomBorder? 25 : 0) + (hideInclude ? 125 : 210), true, true);
- };
-
- /**
- *
- */
- EditorUi.prototype.showExportDialog = function(title, embedOption, btnLabel, helpLink, callback,
- cropOption, defaultInclude, format, exportOption)
- {
- defaultInclude = (defaultInclude != null) ? defaultInclude : Editor.defaultIncludeDiagram;
-
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
- var graph = this.editor.graph;
- var height = (format == 'jpeg' || format == 'webp') ? 220 : 320;
-
- var hd = document.createElement('h3');
- mxUtils.write(hd, title);
- hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:10px';
- div.appendChild(hd);
- mxUtils.write(div, mxResources.get('zoom') + ':');
- var zoomInput = document.createElement('input');
- zoomInput.setAttribute('type', 'text');
- zoomInput.style.marginRight = '16px';
- zoomInput.style.width = '60px';
- zoomInput.style.marginLeft = '4px';
- zoomInput.style.marginRight = '12px';
- zoomInput.value = this.lastExportZoom || '100%';
- div.appendChild(zoomInput);
-
- mxUtils.write(div, mxResources.get('borderWidth') + ':');
- var borderInput = document.createElement('input');
- borderInput.setAttribute('type', 'text');
- borderInput.style.marginRight = '16px';
- borderInput.style.width = '60px';
- borderInput.style.marginLeft = '4px';
- borderInput.value = this.lastExportBorder || '0';
- div.appendChild(borderInput);
- mxUtils.br(div);
- var selection = this.addCheckbox(div, mxResources.get('selectionOnly'),
- false, graph.isSelectionEmpty());
- var cb6 = document.createElement('input');
- cb6.style.marginTop = '16px';
- cb6.style.marginRight = '8px';
- cb6.style.marginLeft = '24px';
- cb6.setAttribute('disabled', 'disabled');
- cb6.setAttribute('type', 'checkbox');
- var exportSelect = document.createElement('select');
- exportSelect.style.marginTop = '16px';
- exportSelect.style.marginLeft = '8px';
- var sizes = ['selectionOnly', 'diagram', 'page'];
- var sizesOpt = {};
- for (var i = 0; i < sizes.length; i++)
- {
- if (!graph.isSelectionEmpty() || sizes[i] != 'selectionOnly')
- {
- var opt = document.createElement('option');
- mxUtils.write(opt, mxResources.get(sizes[i]));
- opt.setAttribute('value', sizes[i]);
- exportSelect.appendChild(opt);
- sizesOpt[sizes[i]] = opt;
- }
- }
- if (exportOption)
- {
- mxUtils.write(div, mxResources.get('size') + ':');
- div.appendChild(exportSelect);
- mxUtils.br(div);
- height += 26;
- mxEvent.addListener(exportSelect, 'change', function()
- {
- if (exportSelect.value == 'selectionOnly')
- {
- selection.checked = true;
- }
- });
- }
- else if (cropOption)
- {
- div.appendChild(cb6);
- mxUtils.write(div, mxResources.get('crop'));
- mxUtils.br(div);
-
- height += 30;
-
- mxEvent.addListener(selection, 'change', function()
- {
- if (selection.checked)
- {
- cb6.removeAttribute('disabled');
- }
- else
- {
- cb6.setAttribute('disabled', 'disabled');
- }
- });
- }
- if (graph.isSelectionEmpty())
- {
- if (exportOption)
- {
- selection.style.display = 'none';
- selection.nextSibling.style.display = 'none';
- selection.nextSibling.nextSibling.style.display = 'none';
- height -= 30;
- }
- }
- else
- {
- exportSelect.value = 'diagram';
- cb6.setAttribute('checked', 'checked');
- cb6.defaultChecked = true;
- mxEvent.addListener(selection, 'change', function()
- {
- if (selection.checked)
- {
- exportSelect.value = 'selectionOnly';
- }
- else
- {
- exportSelect.value = 'diagram';
- }
- });
- }
-
- var defaultTransparent = false; /*graph.background == mxConstants.NONE || graph.background == null*/;
- var transparent = this.addCheckbox(div, mxResources.get('transparentBackground'),
- defaultTransparent, null, null, format != 'jpeg' && format != 'webp');
- var themeSelect = null;
- if (Editor.isDarkMode() || Editor.enableCssDarkMode)
- {
- var themeSelect = document.createElement('select');
- themeSelect.style.maxWidth = '260px';
- themeSelect.style.marginLeft = '8px';
- themeSelect.style.marginTop = '16px';
-
- var lightOption = document.createElement('option');
- lightOption.setAttribute('value', 'light');
- mxUtils.write(lightOption, mxResources.get('light'));
- themeSelect.appendChild(lightOption);
-
- var darkOption = document.createElement('option');
- darkOption.setAttribute('value', 'dark');
- mxUtils.write(darkOption, mxResources.get('dark'));
- themeSelect.appendChild(darkOption);
-
- if (Editor.enableCssDarkMode && format == 'svg')
- {
- var autoOption = document.createElement('option');
- autoOption.setAttribute('value', 'auto');
- mxUtils.write(autoOption, mxResources.get('automatic'));
- themeSelect.appendChild(autoOption);
- }
- mxUtils.write(div, mxResources.get('appearance') + ':');
- div.appendChild(themeSelect);
- mxUtils.br(div);
- if (Editor.isDarkMode() || Editor.cssDarkMode)
- {
- darkOption.setAttribute('selected', 'selected');
- }
- else
- {
- lightOption.setAttribute('selected', 'selected');
- }
-
- height += 26;
- }
-
- var shadow = this.addCheckbox(div, mxResources.get('shadow'), graph.shadowVisible);
-
- var grid = null;
-
- if (format == 'png' || format == 'jpeg' || format == 'webp')
- {
- grid = this.addCheckbox(div, mxResources.get('grid'), false,
- this.isOffline() || !this.canvasSupported, false, true);
- height += 30;
- }
-
- var include = this.addCheckbox(div, mxResources.get('includeCopyOfMyDiagram') + ':',
- defaultInclude, null, null, format != 'jpeg' && format != 'webp');
-
- var includeSelect = document.createElement('select');
- includeSelect.style.maxWidth = '260px';
- includeSelect.style.marginLeft = '28px';
- if (format == 'png' || format == 'svg')
- {
- var includeAllPagesOption = document.createElement('option');
- includeAllPagesOption.setAttribute('value', 'allPages');
- mxUtils.write(includeAllPagesOption, mxResources.get('allPages'));
- includeSelect.appendChild(includeAllPagesOption);
- var includeCurrentPageOption = document.createElement('option');
- includeCurrentPageOption.setAttribute('value', 'currentPage');
- mxUtils.write(includeCurrentPageOption, mxResources.get('currentPage'));
- includeSelect.appendChild(includeCurrentPageOption);
- include.style.marginBottom = '12px';
- includeSelect.style.marginBottom = '16px';
- div.appendChild(includeSelect);
- mxUtils.br(div);
- height += 20;
- if (this.lastEmbedInclude != null)
- {
- includeSelect.value = this.lastEmbedInclude;
- }
- function updateIncludeSelect()
- {
- if (include.checked)
- {
- includeSelect.removeAttribute('disabled');
- }
- else
- {
- includeSelect.setAttribute('disabled', 'disabled');
- }
- };
- mxEvent.addListener(include, 'change', updateIncludeSelect);
- updateIncludeSelect();
- }
- else
- {
- include.style.marginBottom = '16px';
- }
-
- var cb5 = document.createElement('input');
- cb5.style.marginBottom = '16px';
- cb5.style.marginRight = '8px';
- cb5.setAttribute('type', 'checkbox');
- var cb7 = document.createElement('input');
- cb7.style.marginBottom = '16px';
- cb7.style.marginRight = '8px';
- cb7.setAttribute('type', 'checkbox');
-
- if (embedOption)
- {
- cb5.checked = (this.lastEmbedImages != null) ?
- this.lastEmbedImages : true;
- div.appendChild(cb5);
- mxUtils.write(div, mxResources.get('embedImages'));
- mxUtils.br(div);
- cb7.checked = (this.lastEmbedFonts != null) ?
- this.lastEmbedImages : true;
- div.appendChild(cb7);
- mxUtils.write(div, mxResources.get('embedFonts'));
- mxUtils.br(div);
-
- height += 50;
- }
- var linkSelect = document.createElement('select');
- linkSelect.style.maxWidth = '260px';
- linkSelect.style.marginLeft = '8px';
- var autoOption = document.createElement('option');
- autoOption.setAttribute('value', 'auto');
- mxUtils.write(autoOption, mxResources.get('automatic'));
- linkSelect.appendChild(autoOption);
- var blankOption = document.createElement('option');
- blankOption.setAttribute('value', 'blank');
- mxUtils.write(blankOption, mxResources.get('openInNewWindow'));
- linkSelect.appendChild(blankOption);
- var selfOption = document.createElement('option');
- selfOption.setAttribute('value', 'self');
- mxUtils.write(selfOption, mxResources.get('openInThisWindow'));
- linkSelect.appendChild(selfOption);
- //Inkscape doesn't support links from pdf to svg. Related to https://gitlab.com/inkscape/inbox/-/issues/583
- var linkLost = document.createElement('div');
- mxUtils.write(linkLost, mxResources.get('LinksLost'));
- linkLost.style.margin = '7px';
- linkLost.style.display = 'none';
-
- if (format == 'svg')
- {
- mxUtils.write(div, mxResources.get('links') + ':');
- div.appendChild(linkSelect);
- div.appendChild(linkLost);
- mxUtils.br(div);
- mxUtils.br(div);
- height += 50;
- }
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- this.lastExportBorder = borderInput.value;
- this.lastExportZoom = zoomInput.value;
- this.lastEmbedImages = cb5.checked;
- this.lastEmbedFonts = cb7.checked;
- this.lastEmbedInclude = includeSelect.value;
- callback(zoomInput.value, transparent.checked, !selection.checked, shadow.checked,
- include.checked, cb5.checked && embedOption, borderInput.value, cb6.checked,
- (format == 'png' || format == 'svg') && includeSelect.value == 'currentPage',
- linkSelect.value, (grid != null) ? grid.checked : null,
- (themeSelect != null) ? themeSelect.value : null,
- exportSelect.value, cb7.checked);
- }), null, btnLabel, helpLink);
- this.showDialog(dlg.container, 340, height, true, true, null, null, null, null, true);
- zoomInput.focus();
-
- if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5)
- {
- zoomInput.select();
- }
- else
- {
- document.execCommand('selectAll', false, null);
- }
- };
-
- /**
- *
- */
- EditorUi.prototype.showEmbedImageDialog = function(fn, title, imageLabel, shadowEnabled, helpLink)
- {
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
- var graph = this.editor.graph;
-
- if (title != null)
- {
- var hd = document.createElement('h3');
- mxUtils.write(hd, title);
- hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:4px';
- div.appendChild(hd);
- }
-
- var fit = this.addCheckbox(div, mxResources.get('fit'), true);
- var shadow = this.addCheckbox(div, mxResources.get('shadow'),
- graph.shadowVisible && shadowEnabled, !shadowEnabled);
- var image = this.addCheckbox(div, imageLabel);
- var lightbox = this.addCheckbox(div, mxResources.get('lightbox'), true);
- var editSection = this.addEditButton(div, lightbox);
- var edit = editSection.getEditInput();
-
- var hasLayers = graph.model.getChildCount(graph.model.getRoot()) > 1;
- var layers = this.addCheckbox(div, mxResources.get('layers'), hasLayers, !hasLayers);
- layers.style.marginLeft = edit.style.marginLeft;
- layers.style.marginBottom = '12px';
- layers.style.marginTop = '8px';
-
- mxEvent.addListener(lightbox, 'change', function()
- {
- if (lightbox.checked)
- {
- if (hasLayers)
- {
- layers.removeAttribute('disabled');
- }
-
- edit.removeAttribute('disabled');
- }
- else
- {
- layers.setAttribute('disabled', 'disabled');
- edit.setAttribute('disabled', 'disabled');
- }
-
- if (edit.checked && lightbox.checked)
- {
- editSection.getEditSelect().removeAttribute('disabled');
- }
- else
- {
- editSection.getEditSelect().setAttribute('disabled', 'disabled');
- }
- });
-
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- fn(fit.checked, shadow.checked, image.checked, lightbox.checked,
- editSection.getLink(), layers.checked);
- }), null, mxResources.get('embed'), helpLink);
- this.showDialog(dlg.container, 280, 300, true, true);
- };
- /**
- *
- */
- EditorUi.prototype.createEmbedImage = function(fit, shadow, retina, lightbox, edit, layers, fn, err)
- {
- var bounds = this.editor.graph.getGraphBounds();
- var page = this.getSelectedPageIndex();
-
- function doUpdate(dataUri)
- {
- var onclick = ' ';
- var css = '';
-
- // Adds double click handling
- if (lightbox)
- {
- // KNOWN: Message passing does not seem to work in IE11
- onclick = " onclick=\"(function(img){if(img.wnd!=null&&!img.wnd.closed){img.wnd.focus();}else{var r=function(evt){if(evt.data=='ready'&&evt.source==img.wnd){img.wnd.postMessage(decodeURIComponent(" +
- "img.getAttribute('src')),'*');window.removeEventListener('message',r);}};window.addEventListener('message',r);img.wnd=window.open('" + EditorUi.lightboxHost + "/?client=1" +
- ((page != null) ? ("&page=" + page) : "") +
- ((edit) ? "&edit=_blank" : "") +
- ((layers) ? '&layers=1' : '') + "');}})(this);\"";
- css += 'cursor:pointer;';
- }
-
- if (fit)
- {
- css += 'max-width:100%;';
- }
-
- var atts = '';
-
- if (retina)
- {
- atts = ' width="' + Math.round(bounds.width) + '" height="' + Math.round(bounds.height) + '"';
- }
-
- fn('<img src="' + dataUri + '"' + atts + ((css != '') ? ' style="' + css + '"' : '') + onclick + '/>');
- };
-
- if (this.isExportToCanvas())
- {
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas)
- {
- var xml = (lightbox) ? this.getFileData(true) : null;
- var data = this.createImageDataUri(canvas, xml, 'png');
- doUpdate(data);
- }), null, null, null, mxUtils.bind(this, function(e)
- {
- err({message: mxResources.get('unknownError')});
- }), null, true, (retina) ? 2 : 1, null, shadow, null, null, Editor.defaultBorder);
- }
- else
- {
- var data = this.getFileData(true);
-
- if (bounds.width * bounds.height <= MAX_AREA && data.length <= MAX_REQUEST_SIZE)
- {
- var size = '';
-
- if (retina)
- {
- size = '&w=' + Math.round(2 * bounds.width) +
- '&h=' + Math.round(2 * bounds.height);
- }
-
- var embed = (lightbox) ? '1' : '0';
- var req = new mxXmlRequest(EXPORT_URL, 'format=png' +
- '&base64=1&embedXml=' + embed + size + '&xml=' +
- encodeURIComponent(data));
-
- // LATER: Updates on each change, add a delay
- req.send(mxUtils.bind(this, function()
- {
- if (req.getStatus() >= 200 && req.getStatus() <= 299)
- {
- // Fixes possible "incorrect function" for select() on
- // DOM node which is no longer in document with IE11
- doUpdate('data:image/png;base64,' + req.getText());
- }
- else
- {
- err({message: mxResources.get('unknownError')});
- }
- }));
- }
- else
- {
- err({message: mxResources.get('drawingTooLarge')});
- }
- }
- };
-
- /**
- *
- */
- EditorUi.prototype.createEmbedSvg = function(fit, shadow, image, lightbox, edit, layers, fn)
- {
- var svgRoot = this.editor.graph.getSvg(null, null, null,
- null, null, null, null, null, null, null, !image);
-
- // Keeps hashtag links on same page
- var links = svgRoot.getElementsByTagName('a');
-
- if (links != null)
- {
- for (var i = 0; i < links.length; i++)
- {
- var href = links[i].getAttribute('href');
-
- if (href != null && href.charAt(0) == '#' &&
- links[i].getAttribute('target') == '_blank')
- {
- links[i].removeAttribute('target');
- }
- }
- }
-
- if (lightbox)
- {
- svgRoot.setAttribute('content', this.getFileData(true));
- }
-
- // Adds shadow filter
- if (shadow)
- {
- this.editor.graph.addSvgShadow(svgRoot);
- }
-
- // SVG inside image tag
- if (image)
- {
- var onclick = ' ';
- var css = '';
- // Adds double click handling
- if (lightbox)
- {
- // KNOWN: Message passing does not seem to work in IE11
- onclick = "onclick=\"(function(img){if(img.wnd!=null&&!img.wnd.closed){img.wnd.focus();}else{var r=function(evt){if(evt.data=='ready'&&evt.source==img.wnd){img.wnd.postMessage(decodeURIComponent(" +
- "img.getAttribute('src')),'*');window.removeEventListener('message',r);}};window.addEventListener('message',r);img.wnd=window.open('" + EditorUi.lightboxHost + "/?client=1" +
- ((edit) ? "&edit=_blank" : "") + ((layers) ? '&layers=1' : '') + "');}})(this);\"";
- css += 'cursor:pointer;';
- }
-
- if (fit)
- {
- css += 'max-width:100%;';
- }
-
- // Images inside IMG don't seem to work so embed them all
- this.editor.convertImages(svgRoot, mxUtils.bind(this, function(svgRoot)
- {
- fn('<img src="' + Editor.createSvgDataUri(mxUtils.getXml(svgRoot)) + '"' +
- ((css != '') ? ' style="' + css + '"' : '') + onclick + '/>');
- }));
- }
- else
- {
- var css = '';
-
- // Adds double click handling
- if (lightbox)
- {
- var page = this.getSelectedPageIndex();
-
- // KNOWN: Message passing does not seem to work in IE11
- var js = "(function(svg){var src=window.event.target||window.event.srcElement;" +
- // Ignores link events
- "while (src!=null&&src.nodeName.toLowerCase()!='a'){src=src.parentNode;}if(src==null)" +
- // Focus existing lightbox
- "{if(svg.wnd!=null&&!svg.wnd.closed){svg.wnd.focus();}else{var r=function(evt){" +
- // Message handling
- "if(evt.data=='ready'&&evt.source==svg.wnd){svg.wnd.postMessage(decodeURIComponent(" +
- "svg.getAttribute('content')),'*');window.removeEventListener('message',r);}};" +
- "window.addEventListener('message',r);" +
- // Opens lightbox window
- "svg.wnd=window.open('" + EditorUi.lightboxHost + "/?client=1" +
- ((page != null) ? ("&page=" + page) : "") +
- ((edit) ? "&edit=_blank" : "") + ((layers) ? '&layers=1' : '') + "');}}})(this);";
- svgRoot.setAttribute('onclick', js);
- css += 'cursor:pointer;';
- }
-
- // Adds responsive size
- if (fit)
- {
- var w = parseInt(svgRoot.getAttribute('width'));
- var h = parseInt(svgRoot.getAttribute('height'));
- svgRoot.setAttribute('viewBox', '-0.5 -0.5 ' + w + ' ' + h);
- css += 'max-width:100%;max-height:' + h + 'px;';
- svgRoot.removeAttribute('height');
- }
-
- if (css != '')
- {
- svgRoot.setAttribute('style', css);
- }
-
- // Adds CSS
- this.editor.addFontCss(svgRoot);
-
- if (this.editor.graph.mathEnabled)
- {
- this.editor.addMathCss(svgRoot);
- }
-
- fn(mxUtils.getXml(svgRoot));
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.timeSince = function(date)
- {
- var seconds = Math.floor((new Date() - date) / 1000);
- var interval = Math.floor(seconds / 31536000);
- if (interval > 1)
- {
- return interval + ' ' + mxResources.get('years');
- }
-
- interval = Math.floor(seconds / 2592000);
-
- if (interval > 1)
- {
- return interval + ' ' + mxResources.get('months');
- }
-
- interval = Math.floor(seconds / 86400);
-
- if (interval > 1)
- {
- return interval + ' ' + mxResources.get('days');
- }
-
- interval = Math.floor(seconds / 3600);
-
- if (interval > 1)
- {
- return interval + ' ' + mxResources.get('hours');
- }
-
- interval = Math.floor(seconds / 60);
-
- if (interval > 1)
- {
- return interval + ' ' + mxResources.get('minutes');
- }
-
- if (interval == 1)
- {
- return interval + ' ' + mxResources.get('minute');
- }
-
- return null;
- };
- /**
- *
- */
- EditorUi.prototype.decodeNodeIntoGraph = function(node, graph)
- {
- if (node != null)
- {
- var diagramNode = null;
-
- if (node.nodeName == 'diagram')
- {
- diagramNode = node;
- }
- else if (node.nodeName == 'mxfile')
- {
- var diagrams = node.getElementsByTagName('diagram');
- if (diagrams.length > 0)
- {
- diagramNode = diagrams[0];
- var graphGetGlobalVariable = graph.getGlobalVariable;
-
- graph.getGlobalVariable = function(name)
- {
- if (name == 'page')
- {
- return diagramNode.getAttribute('name') || mxResources.get('pageWithNumber', [1])
- }
- else if (name == 'pagenumber')
- {
- return 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
- }
- }
-
- if (diagramNode != null)
- {
- node = Editor.parseDiagramNode(diagramNode);
- }
- }
-
- // Hack to decode XML into temp graph via editor
- var prev = this.editor.graph;
-
- try
- {
- this.editor.graph = graph;
- this.editor.setGraphXml(node);
- }
- catch (e)
- {
- // ignore
- }
- finally
- {
- this.editor.graph = prev;
- }
-
- return node;
- };
- /**
- *
- */
- EditorUi.prototype.getSvgFileProperties = function(node)
- {
- return this.getPngFileProperties(node);
- };
-
- /**
- *
- */
- EditorUi.prototype.getPngFileProperties = function(node)
- {
- var scale = 1;
- var border = 0;
-
- if (node != null)
- {
- if (node.hasAttribute('scale'))
- {
- var temp = parseFloat(node.getAttribute('scale'));
-
- if (!isNaN(temp) && temp > 0)
- {
- scale = temp;
- }
- }
-
- if (node.hasAttribute('border'))
- {
- var temp = parseInt(node.getAttribute('border'));
-
- if (!isNaN(temp) && temp > 0)
- {
- border = temp;
- }
- }
- }
-
- return {scale: scale, border: border};
- };
-
- /**
- *
- */
- EditorUi.prototype.getEmbeddedPng = function(success, error, optionalData, scale, border)
- {
- try
- {
- var graph = this.editor.graph;
- var darkTheme = graph.themes != null && graph.defaultThemeName == 'darkTheme';
- var diagramData = null;
-
- // Exports PNG for given optional data
- if (optionalData != null && optionalData.length > 0)
- {
- graph = this.createTemporaryGraph((darkTheme) ?
- graph.getDefaultStylesheet() : graph.getStylesheet());
- document.body.appendChild(graph.container);
- this.decodeNodeIntoGraph(this.editor.extractGraphModel(
- mxUtils.parseXml(optionalData).documentElement, true), graph);
- diagramData = optionalData;
- }
- // Exports PNG for first page while other page is showing
- else if (darkTheme || (this.pages != null && this.currentPage != this.pages[0]))
- {
- graph = this.createTemporaryGraph((darkTheme) ?
- graph.getDefaultStylesheet() : graph.getStylesheet());
- var graphGetGlobalVariable = graph.getGlobalVariable;
- graph.setBackgroundImage = this.editor.graph.setBackgroundImage;
- var page = this.pages[0];
- if (this.currentPage == page)
- {
- graph.setBackgroundImage(this.editor.graph.backgroundImage);
- }
- else if (page.viewState != null && page.viewState != null)
- {
- graph.setBackgroundImage(page.viewState.backgroundImage);
- }
-
- graph.getGlobalVariable = function(name)
- {
- if (name == 'page')
- {
- return page.getName();
- }
- else if (name == 'pagenumber')
- {
- return 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
-
- document.body.appendChild(graph.container);
- graph.model.setRoot(page.root);
- }
-
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas)
- {
- try
- {
- if (diagramData == null)
- {
- diagramData = this.getFileData(true, null, null, null, null,
- null, null, null, null, false);
- }
-
- var data = canvas.toDataURL('image/png');
- data = Editor.writeGraphModelToPng(data,
- 'tEXt', 'mxfile', encodeURIComponent(diagramData));
- success(data.substring(data.lastIndexOf(',') + 1));
-
- // Removes temporary graph from DOM
- if (graph != this.editor.graph)
- {
- graph.container.parentNode.removeChild(graph.container);
- }
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), null, null, null, mxUtils.bind(this, function(e)
- {
- if (error != null)
- {
- error(e);
- }
- }), null, null, scale, null, graph.shadowVisible, null,
- graph, border, null, null, null, 'diagram', null);
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }
- /**
- * Returns the SVG of the diagram with embedded XML. If a callback function is
- * used, the images are converted to data URIs.
- */
- EditorUi.prototype.getEmbeddedSvg = function(xml, graph, url, noHeader, callback, ignoreSelection,
- redirect, embedImages, background, scale, border, shadow, theme)
- {
- embedImages = (embedImages != null) ? embedImages : true;
- border = (border != null) ? border : 0;
- var bg = (background != null) ? background : graph.background;
-
- if (bg == mxConstants.NONE)
- {
- bg = null;
- }
- // Sets or disables alternate text for foreignObjects. Disabling is needed
- // because PhantomJS seems to ignore switch statements and paint all text.
- var svgRoot = graph.getSvg(bg, scale, border, null, null, ignoreSelection, null,
- null, null, graph.shadowVisible || shadow, null, theme, 'diagram');
-
- if (graph.shadowVisible || shadow)
- {
- graph.addSvgShadow(svgRoot, null, null, border == 0);
- }
- if (xml != null)
- {
- svgRoot.setAttribute('content', xml);
- }
-
- if (url != null)
- {
- svgRoot.setAttribute('resource', url);
- }
-
- // LATER: Click on SVG content to start editing
- // if (redirect != null)
- // {
- // // TODO: Ignore anchor tag source for click event
- // svgRoot.setAttribute('style', 'cursor:pointer;');
- // svgRoot.setAttribute('onclick', 'window.location.href=\'' + redirect + '\';');
- // }
- var done = mxUtils.bind(this, function(svgRoot)
- {
- var result = ((!noHeader) ? Graph.xmlDeclaration + '\n' + Graph.svgFileComment +
- '\n' + Graph.svgDoctype + '\n' : '') + mxUtils.getXml(svgRoot);
- if (callback != null)
- {
- callback(result);
- }
- return result;
- });
- // Adds CSS
- if (graph.mathEnabled)
- {
- this.editor.addMathCss(svgRoot);
- }
- if (callback != null)
- {
- this.embedFonts(svgRoot, mxUtils.bind(this, function(svgRoot)
- {
- if (embedImages)
- {
- this.editor.convertImages(svgRoot, mxUtils.bind(this, function(svgRoot)
- {
- done(svgRoot);
- }));
- }
- else
- {
- done(svgRoot);
- }
- }));
- }
- else
- {
- return done(svgRoot);
- }
- };
-
- /**
- * Embeds font CSS as data URIs into the given svgRoot.
- */
- EditorUi.prototype.embedFonts = function(svgRoot, callback)
- {
- this.editor.loadFonts(mxUtils.bind(this, function()
- {
- try
- {
- if (this.editor.resolvedFontCss != null)
- {
- this.editor.addFontCss(svgRoot, this.editor.resolvedFontCss);
- }
-
- this.editor.embedExtFonts(mxUtils.bind(this, function(extFontsEmbeddedCss)
- {
- try
- {
- if (extFontsEmbeddedCss != null)
- {
- this.editor.addFontCss(svgRoot, extFontsEmbeddedCss);
- }
-
- callback(svgRoot);
- }
- catch (e)
- {
- callback(svgRoot);
- }
- }));
- }
- catch (e)
- {
- callback(svgRoot);
- }
- }));
- };
-
- /**
- *
- */
- EditorUi.prototype.exportImage = function(scale, transparentBackground, ignoreSelection, addShadow,
- editable, border, noCrop, currentPage, format, grid, dpi, theme, exportType)
- {
- format = (format != null) ? format : 'png';
- if (this.spinner.spin(document.body, mxResources.get('exporting'), mxUtils.bind(this, function(err)
- {
- Editor.addRetryToError(err, mxUtils.bind(this, function()
- {
- this.exportImage(scale, transparentBackground, ignoreSelection, addShadow,
- editable, border, noCrop, currentPage, format, grid, dpi, theme, exportType);
- }));
-
- this.handleError(err);
- })))
- {
- var selectionEmpty = this.editor.graph.isSelectionEmpty();
- ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
-
- // Caches images
- if (this.thumbImageCache == null)
- {
- this.thumbImageCache = new Object();
- }
-
- try
- {
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas)
- {
- this.spinner.stop();
-
- try
- {
- this.saveCanvas(canvas, (editable) ? this.getFileData(true, null,
- null, null, ignoreSelection, currentPage, null, null, null,
- null, null, scale, border) : null, format,
- (this.pages == null || this.pages.length == 0), dpi);
- }
- catch (e)
- {
- this.handleError(e);
- }
- }), null, this.thumbImageCache, null, mxUtils.bind(this, function(e)
- {
- this.spinner.stop();
- this.handleError(e);
- }), null, ignoreSelection, scale || 1, transparentBackground, addShadow,
- null, null, border, noCrop, grid, theme, exportType);
- }
- catch (e)
- {
- this.spinner.stop();
- this.handleError(e);
- }
- }
- };
- /**
- /**
- * Returns true if the given URL is known to have CORS headers.
- */
- EditorUi.prototype.isCorsEnabledForUrl = function(url)
- {
- return this.editor.isCorsEnabledForUrl(url);
- };
- /**
- * Handling drag and drop and import.
- */
- /**
- * Adds the local page IDs to the given mapping.
- */
- EditorUi.prototype.addLocalPagesToMapping = function(mapping)
- {
- mapping = (mapping != null) ? mapping : {};
- if (this.pages != null)
- {
- for (var i = 0; i < this.pages.length; i++)
- {
- mapping[this.pages[i].getId()] = this.pages[i].getId();
- }
- }
- return mapping;
- };
- /**
- * Imports the given XML into the existing diagram.
- */
- EditorUi.prototype.importXml = function(xml, dx, dy, crop, noErrorHandling, addNewPage, applyDefaultStyles)
- {
- dx = (dx != null) ? dx : 0;
- dy = (dy != null) ? dy : 0;
- var cells = []
-
- try
- {
- var graph = this.editor.graph;
-
- if (xml != null && xml.length > 0)
- {
- // Adds pages
- graph.model.beginUpdate();
- try
- {
- var doc = mxUtils.parseXml(xml);
- var mapping = {};
-
- // Checks for mxfile with multiple pages
- var node = this.editor.extractGraphModel(doc.documentElement, this.pages != null);
- if (node != null && node.nodeName == 'mxfile' && this.pages != null)
- {
- var diagrams = node.getElementsByTagName('diagram');
- if (diagrams.length == 1 && !addNewPage)
- {
- node = Editor.parseDiagramNode(diagrams[0]);
-
- if (this.currentPage != null)
- {
- mapping[diagrams[0].getAttribute('id')] = this.currentPage.getId();
-
- // Renames page if diagram has one blank page with default name
- if (this.isBlankFile())
- {
- var name = diagrams[0].getAttribute('name');
-
- if (name != null && name != '')
- {
- this.editor.graph.model.execute(new RenamePage(
- this, this.currentPage, name));
- }
- }
- }
- }
- else if (diagrams.length > 0)
- {
- var pages = [];
- var i0 = 0;
-
- // Adds first page to current page if current page is only page and empty
- if (this.pages != null && this.pages.length == 1 && this.isDiagramEmpty())
- {
- mapping[diagrams[0].getAttribute('id')] = this.pages[0].getId();
- var name = diagrams[0].getAttribute('name');
- if (name != null && name != '')
- {
- this.editor.graph.model.execute(new RenamePage(
- this, this.pages[0], name));
- }
- node = Editor.parseDiagramNode(diagrams[0]);
- crop = false;
- i0 = 1;
- }
- for (var i = i0; i < diagrams.length; i++)
- {
- // Imported pages must obtain a new ID and
- // all links to pages must be updated below
- var oldId = diagrams[i].getAttribute('id')
- diagrams[i].removeAttribute('id');
-
- var page = this.updatePageRoot(new DiagramPage(diagrams[i]));
- mapping[oldId] = diagrams[i].getAttribute('id');
- var index = this.pages.length;
-
- // Checks for invalid page names
- if (page.getName() == null)
- {
- page.setName(mxResources.get('pageWithNumber', [index + 1]));
- }
-
- graph.model.execute(new ChangePage(this, page, page, index, true));
- pages.push(page);
- }
-
- this.updatePageLinks(mapping, pages);
- }
- }
-
- if (node != null && node.nodeName === 'mxGraphModel')
- {
- cells = graph.importGraphModel(node, dx, dy, crop);
-
- if (cells != null)
- {
- this.addLocalPagesToMapping(mapping);
- for (var i = 0; i < cells.length; i++)
- {
- this.updatePageLinksForCell(mapping, cells[i]);
- }
- }
- var bgImg = graph.parseBackgroundImage(node.getAttribute('backgroundImage'));
- if (bgImg != null && bgImg.originalSrc != null)
- {
- this.updateBackgroundPageLink(mapping, bgImg);
- var change = new ChangePageSetup(this, null, bgImg);
- change.ignoreColor = true;
- graph.model.execute(change);
- }
- }
-
- if (applyDefaultStyles)
- {
- graph.pasteCellStyles(graph.includeDescendants(cells),
- graph.defaultVertexStyle, graph.defaultEdgeStyle);
- }
- }
- finally
- {
- graph.model.endUpdate();
- }
- }
- }
- catch (e)
- {
- if (!noErrorHandling)
- {
- this.handleError(e);
- }
- else
- {
- throw e;
- }
- }
-
- return cells;
- };
-
- /**
- * Updates links to pages in shapes and labels.
- */
- EditorUi.prototype.updatePageLinks = function(mapping, pages)
- {
- for (var i = 0; i < pages.length; i++)
- {
- this.updatePageLinksForCell(mapping, pages[i].root);
- if (pages[i].viewState != null)
- {
- this.updateBackgroundPageLink(mapping, pages[i].viewState.backgroundImage);
- }
- }
- };
-
- /**
- * Updates links to pages in shapes and labels.
- */
- EditorUi.prototype.updateBackgroundPageLink = function(mapping, obj)
- {
- try
- {
- if (obj != null && Graph.isPageLink(obj.originalSrc))
- {
- var newId = mapping[obj.originalSrc.substring(obj.originalSrc.indexOf(',') + 1)];
- if (newId != null)
- {
- obj.originalSrc = 'data:page/id,' + newId;
- }
- }
- }
- catch (e)
- {
- // ignore background image
- }
- };
- /**
- * Updates links to pages in shapes and labels.
- */
- EditorUi.prototype.updatePageLinksForCell = function(mapping, cell)
- {
- var temp = document.createElement('div');
- var graph = this.editor.graph;
- var href = graph.getLinkForCell(cell);
- if (href != null)
- {
- graph.setLinkForCell(cell, this.updatePageLink(mapping, href));
- }
-
- if (graph.isHtmlLabel(cell))
- {
- temp.innerHTML = Graph.sanitizeHtml(graph.getLabel(cell));
- var links = temp.getElementsByTagName('a');
- var changed = false;
-
- for (var i = 0; i < links.length; i++)
- {
- href = links[i].getAttribute('href');
-
- if (href != null)
- {
- links[i].setAttribute('href', this.updatePageLink(mapping, href));
- changed = true;
- }
- }
-
- if (changed)
- {
- graph.labelChanged(cell, temp.innerHTML);
- }
- }
-
- for (var i = 0; i < graph.model.getChildCount(cell); i++)
- {
- this.updatePageLinksForCell(mapping, graph.model.getChildAt(cell, i));
- }
- };
- /**
- * Updates links to pages in shapes and labels.
- */
- EditorUi.prototype.updatePageLink = function(mapping, href)
- {
- if (Graph.isPageLink(href))
- {
- var newId = mapping[href.substring(href.indexOf(',') + 1)];
- href = (newId != null) ? 'data:page/id,' + newId : null;
- }
- else if (href.substring(0, 17) == 'data:action/json,')
- {
- try
- {
- var link = JSON.parse(href.substring(17));
- if (link.actions != null)
- {
- for (var i = 0; i < link.actions.length; i++)
- {
- var action = link.actions[i];
-
- if (action.open != null && Graph.isPageLink(action.open))
- {
- var oldId = action.open.substring(action.open.indexOf(',') + 1);
- var newId = mapping[oldId];
-
- if (newId != null)
- {
- action.open = 'data:page/id,' + newId;
- }
- else if (this.getPageById(oldId) == null)
- {
- delete action.open;
- }
- }
- }
-
- href = 'data:action/json,' + JSON.stringify(link);
- }
- }
- catch (e)
- {
- // Ignore
- }
- }
-
- return href;
- };
-
- /**
- * Returns true for VSD, VDX and VSS, VSX files.
- */
- EditorUi.prototype.isRemoteVisioFormat = function(filename)
- {
- return (/(\.v(sd|dx))($|\?)/i.test(filename) ||
- /(\.vs(s|x))($|\?)/i.test(filename));
- };
-
- /**
- * Imports the given Visio file
- */
- EditorUi.prototype.importVisio = function(file, done, error, filename, customParam)
- {
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingExtensions = false;
- if (error != null)
- {
- error(e);
- }
- else
- {
- this.handleError(e);
- }
- });
- //A reduced version of this code is used in conf/jira plugins, review that code whenever this function is changed
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- filename = (filename != null) ? filename : file.name;
- var handleError = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- onerror(e);
- }
- });
- var delayed = mxUtils.bind(this, function()
- {
- this.loadingExtensions = false;
- if (this.doImportVisio)
- {
- var remote = this.isRemoteVisioFormat(filename);
-
- try
- {
- var ext = 'UNKNOWN-VISIO';
- var dot = filename.lastIndexOf('.');
-
- if (dot >= 0 && dot < filename.length)
- {
- ext = filename.substring(dot + 1).toUpperCase();
- }
- else
- {
- var slash = filename.lastIndexOf('/');
-
- if (slash >= 0 && slash < filename.length)
- {
- filename = filename.substring(slash + 1);
- }
- }
-
- // EditorUi.logEvent({category: ext + '-MS-IMPORT-FILE',
- // action: 'filename_' + filename,
- // label: (remote) ? 'remote' : 'local'});
- }
- catch (e)
- {
- // ignore
- }
-
- if (remote)
- {
- if (VSD_CONVERT_URL != null && !this.isOffline())
- {
- var formData = new FormData();
- formData.append('file1', file, filename);
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', VSD_CONVERT_URL + (/(\.vss|\.vsx)$/.test(filename)? '?stencil=1' : ''));
- xhr.responseType = 'blob';
- this.addRemoteServiceSecurityCheck(xhr);
-
- if (customParam != null)
- {
- xhr.setRequestHeader('x-convert-custom', customParam);
- }
-
- xhr.onreadystatechange = mxUtils.bind(this, function()
- {
- if (xhr.readyState == 4 && timeout.clear())
- {
- if (xhr.status >= 200 && xhr.status <= 299)
- {
- try
- {
- var resp = xhr.response;
- if (resp.type == 'text/xml')
- {
- var reader = new FileReader();
-
- reader.onload = mxUtils.bind(this, function(e)
- {
- try
- {
- done(e.target.result);
- }
- catch (e)
- {
- handleError({message: mxResources.get('errorLoadingFile')});
- }
- });
-
- reader.readAsText(resp);
- }
- else
- {
- this.doImportVisio(resp, done, handleError, filename);
- }
- }
- catch (e)
- {
- handleError(e);
- }
- }
- else
- {
- try
- {
- if (xhr.responseType == '' || xhr.responseType == 'text')
- {
- handleError({message: xhr.responseText});
- }
- else
- {
- var reader = new FileReader();
- reader.onload = function()
- {
- try
- {
- handleError({message: JSON.parse(reader.result).Message});
- }
- catch (e)
- {
- handleError(e);
- }
- }
- reader.readAsText(xhr.response);
- }
- }
- catch(e)
- {
- handleError({});
- }
- }
- }
- });
-
- xhr.send(formData);
- }
- else
- {
- handleError({message: this.getServiceName() != 'draw.io'? mxResources.get('vsdNoConfig') :
- mxResources.get('serviceUnavailableOrBlocked')});
- }
- }
- else if (timeout.clear())
- {
- try
- {
- this.doImportVisio(file, done, handleError, filename);
- }
- catch (e)
- {
- handleError(e);
- }
- }
- }
- else
- {
- handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
- }
- });
-
- if (!this.doImportVisio && !this.loadingExtensions && !this.isOffline(true))
- {
- this.loadingExtensions = true;
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed, null, null, null, handleError);
- }
- else
- {
- delayed();
- }
- }), onerror);
- };
- /**
- * Imports the given GraphML (yEd) file
- */
- EditorUi.prototype.importGraphML = function(xmlData, done, error)
- {
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingExtensions = false;
- if (error != null)
- {
- error(e);
- }
- else
- {
- this.handleError(e);
- }
- });
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- var handleError = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- onerror(e);
- }
- });
- var delayed = mxUtils.bind(this, function()
- {
- this.loadingExtensions = false;
- if (timeout.clear())
- {
- if (this.doImportGraphML)
- {
- try
- {
- this.doImportGraphML(xmlData, done, onerror);
- }
- catch (e)
- {
- handleError(e);
- }
- }
- else
- {
- handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
- }
- }
- });
-
- if (!this.doImportGraphML && !this.loadingExtensions && !this.isOffline(true))
- {
- this.loadingExtensions = true;
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed, null, null, null, handleError);
- }
- else
- {
- delayed();
- }
- }), onerror);
- };
-
- /**
- * Export the diagram to VSDX
- */
- EditorUi.prototype.exportVisio = function(currentPage)
- {
- if (this.spinner.spin(document.body, mxResources.get('loading')))
- {
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingExtensions = false;
- this.handleError(e);
- });
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- var handleError = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- onerror(e);
- }
- });
- var delayed = mxUtils.bind(this, function()
- {
- this.loadingExtensions = false;
- if (timeout.clear())
- {
- if (typeof VsdxExport !== 'undefined')
- {
- try
- {
- this.spinner.stop();
- var expSuccess = new VsdxExport(this).exportCurrentDiagrams(currentPage);
-
- if (!expSuccess)
- {
- handleError({message: mxResources.get('unknownError')});
- }
- }
- catch (e)
- {
- handleError(e);
- }
- }
- else
- {
- handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
- }
- }
- });
-
- if (typeof VsdxExport === 'undefined' && !this.loadingExtensions && !this.isOffline(true))
- {
- this.loadingExtensions = true;
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed, null, null, null, handleError);
- }
- else
- {
- // Async needed for showing spinner for longer exports
- window.setTimeout(delayed, 0);
- }
- }), onerror);
- }
- };
-
- /**
- * Imports the given Lucidchart data.
- */
- EditorUi.prototype.convertLucidChart = function(data, success, error)
- {
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingExtensions = false;
- if (error != null)
- {
- error(e);
- }
- else
- {
- this.handleError(e);
- }
- });
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- var handleError = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- onerror(e);
- }
- });
- var delayed = mxUtils.bind(this, function()
- {
- this.loadingExtensions = false;
-
- if (timeout.clear())
- {
- // Checks for signature method
- if (typeof window.LucidImporter !== 'undefined')
- {
- try
- {
- var obj = JSON.parse(data);
- success(LucidImporter.importState(obj));
- try
- {
- // EditorUi.logEvent({category: 'LUCIDCHART-IMPORT-FILE',
- // action: 'size_' + data.length});
- if (window.console != null && urlParams['test'] == '1')
- {
- var args = [new Date().toISOString(), 'convertLucidChart', obj];
- if (obj.state != null)
- {
- args.push(JSON.parse(obj.state));
- }
-
- if (obj.svgThumbs != null)
- {
- for (var i = 0; i < obj.svgThumbs.length; i++)
- {
- args.push(Editor.createSvgDataUri(obj.svgThumbs[i]));
- }
- }
- if (obj.thumb != null)
- {
- args.push(obj.thumb);
- }
- console.log.apply(console, args);
- }
- }
- catch (e)
- {
- // ignore
- }
- }
- catch (e)
- {
- if (window.console != null)
- {
- console.error(e);
- }
-
- handleError(e);
- }
- }
- else
- {
- handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
- }
- }
- });
-
- if (typeof window.LucidImporter === 'undefined' &&
- !this.loadingExtensions && !this.isOffline(true))
- {
- this.loadingExtensions = true;
-
- if (urlParams['dev'] == '1')
- {
- //Lucid org chart requires orgChart layout, in production, it is part of the extemsions.min.js
- mxscript('js/diagramly/Extensions.js', function()
- {
- mxscript('js/orgchart/bridge.min.js', function()
- {
- mxscript('js/orgchart/bridge.collections.min.js', function()
- {
- mxscript('js/orgchart/OrgChart.Layout.min.js', function()
- {
- mxscript('js/orgchart/mxOrgChartLayout.js',
- delayed, null, null, null, handleError);
- }, null, null, null, handleError);
- }, null, null, null, handleError);
- }, null, null, null, handleError);
- }, null, null, null, handleError);
- }
- else
- {
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed,
- null, null, null, handleError);
- }
- }
- else
- {
- // Async needed for selection
- window.setTimeout(delayed, 0);
- }
- }), onerror);
- };
- /**
- * Generates a Mermaid image.
- */
- EditorUi.prototype.createMermaidXml = function(input, config, data, w, h, prompt)
- {
- var graph = new Graph(document.createElement('div'));
- var cell = graph.insertVertex(null, null, null, 0, 0, w, h,
- 'shape=image;noLabel=1;verticalAlign=top;' +
- 'imageAspect=1;image=' + data + ';')
- graph.setAttributeForCell(cell, 'mermaidData', JSON.stringify(
- {data: input, config: config}, null, 2));
- if (prompt != null)
- {
- graph.setAttributeForCell(cell, 'templatePrompt', prompt);
- }
- var codec = new mxCodec();
- var node = codec.encode(graph.getModel());
- return mxUtils.getXml(node);
- };
- /**
- * Generates a Mermaid image.
- */
- EditorUi.prototype.generateOpenAiMermaidDiagram = function(prompt, success, error)
- {
- var maxRetries = 3;
- var retryCount = 0;
- var fn = mxUtils.bind(this, function()
- {
- this.createTimeout(10000, mxUtils.bind(this, function(timeout)
- {
- // EditorUi.logEvent({category: 'OPENAI-DIAGRAM',
- // action: 'generateOpenAiMermaidDiagram',
- // label: prompt});
- var url = 'https://www.draw.io/generate/v2';
- var req = new mxXmlRequest(url, prompt, 'POST');
-
- var handleError = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- error(e);
- }
- });
-
- req.send(mxUtils.bind(this, function(req)
- {
- if (timeout.isAlive())
- {
- if (req.getStatus() >= 200 && req.getStatus() <= 299)
- {
- this.tryAndHandle(mxUtils.bind(this, function()
- {
- var result = mxUtils.trim(req.getText());
-
- this.generateMermaidImage(result, null, mxUtils.bind(this, function(data, w, h)
- {
- this.tryAndHandle(mxUtils.bind(this, function()
- {
- if (timeout.clear())
- {
- EditorUi.debug('EditorUi.generateOpenAiMermaidDiagram',
- '\nprompt:', prompt, '\nresult:', result);
- success(result, data, w, h);
- }
- }), handleError);
- }), handleError, mxUtils.bind(this, function(e)
- {
- if (retryCount++ < maxRetries)
- {
- if (timeout.clear())
- {
- fn();
- }
- }
- else
- {
- handleError(e);
- }
- }));
- }), handleError);
- }
- else
- {
- var e = {message: mxResources.get('error') + ' ' + req.getStatus()};
- try
- {
- e = JSON.parse(req.getText());
- e = e.error;
- }
- catch (e)
- {
- // ignore
- }
- handleError(e);
- }
- }
- }), handleError);
- }), error);
- });
- fn();
- };
- /**
- * Generates a Mermaid image.
- */
- EditorUi.prototype.extractMermaidDeclaration = function(value)
- {
- value = mxUtils.trim(value);
- // Removes occasional "o" on first line in response
- if (value.substring(0, 3) == 'o\n\n')
- {
- value = value.substring(3);
- }
-
- // Various formats supported
- var tokens = value.split('```');
- tokens = (tokens.length > 1) ? tokens : value.split('<pre>');
- tokens = (tokens.length > 1) ? tokens : value.split('~~~');
- tokens = (tokens.length > 1) ? tokens : value.split('%%');
- tokens = (tokens.length > 1) ? tokens : value.split('(Begins)');
-
- var text = mxUtils.trim((tokens.length <= 1) ? value : tokens[1]);
- var lines = text.split('\n');
-
- // Removes occasional mermaid tag or other text on first line
- var type = (lines.length > 1) ? lines[1] : null;
- if (type != null)
- {
- var dash = type.indexOf('-');
- if (dash > 0)
- {
- type = type.substring(0, dash);
- }
- }
- if ((lines.length > 0 && mxUtils.trim(lines[0]) == 'mermaid') ||
- (type != null && mxUtils.indexOf(
- EditorUi.mermaidDiagramTypes, type) >= 0))
- {
- lines.shift();
- text = mxUtils.trim(lines.join('\n'));
- lines = text.split('\n');
- }
-
- // Validates diagram type on first line
- type = lines[0].split(' ')[0].replace(/:$/, '');
- var dash = type.indexOf('-');
- if (dash > 0)
- {
- type = type.substring(0, dash);
- }
- try
- {
- if (type == 'mindmap' && lines.length > 2)
- {
- text = extractMermaidMindmap(lines);
- }
- }
- catch (e)
- {
- // ignore
- }
-
- // TODO Is this too restrictive?
- if (mxUtils.indexOf(EditorUi.mermaidDiagramTypes, type) < 0)
- {
- text = null;
- }
-
- return text;
- };
- /**
- * Generates a Mermaid image.
- */
- EditorUi.prototype.generateMermaidImage = function(data, config, success, error, parseErrorHandler)
- {
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingMermaid = false;
- if (error != null)
- {
- error(e);
- }
- else
- {
- this.handleError(e);
- }
- });
-
- var delayed = mxUtils.bind(this, function()
- {
- try
- {
- this.loadingMermaid = false;
-
- config = (config != null) ? config : mxUtils.clone(EditorUi.defaultMermaidConfig);
- config.securityLevel = 'strict';
- config.startOnLoad = false;
- // Math labels
- if (typeof mxMermaidToDrawio !== 'undefined' && config.flowchart && data.indexOf('$$') >= 0)
- {
- config.flowchart.htmlLabels = true;
- mxMermaidToDrawio.htmlLabels = true;
- }
- if (Editor.isDarkMode())
- {
- config.theme = 'dark';
- }
-
- var renderCallback = mxUtils.bind(this, function(svg)
- {
- try
- {
- // Workaround for namespace errors in SVG output for IE
- if (mxClient.IS_IE || mxClient.IS_IE11)
- {
- svg = svg.replace(/ xmlns:\S*="http:\/\/www.w3.org\/XML\/1998\/namespace"/g, '').
- replace(/ (NS xml|\S*):space="preserve"/g, ' xml:space="preserve"');
- }
-
- var doc = mxUtils.parseXml(svg);
- var svgs = doc.getElementsByTagName('svg');
- if (svgs.length > 0 && svgs[0].getAttribute('aria-roledescription') != 'error')
- {
- var w = parseFloat(svgs[0].getAttribute('width'));
- var h = parseFloat(svgs[0].getAttribute('height'));
-
- if (isNaN(w) || isNaN(h))
- {
- try
- {
- var viewBox = svgs[0].getAttribute('viewBox').split(/\s+/);
- w = parseFloat(viewBox[2]);
- h = parseFloat(viewBox[3]);
- }
- catch(e)
- {
- //Any size such that it shows up
- w = w || 100;
- h = h || 100;
- }
- }
-
- success(this.convertDataUri(Editor.createSvgDataUri(svg)), w, h);
- }
- else
- {
- if (parseErrorHandler != null)
- {
- parseErrorHandler();
- }
- else
- {
- error({message: mxResources.get('invalidInput')});
- }
- }
- }
- catch (e)
- {
- error(e);
- }
- });
- mermaid.mermaidAPI.initialize(config);
- mermaid.mermaidAPI.render('geMermaidOutput-' + new Date().getTime(), data).then(function(result)
- {
- renderCallback(result.svg);
- }).catch(function(e)
- {
- if (parseErrorHandler != null)
- {
- parseErrorHandler(e);
- }
- else
- {
- error(e);
- }
- });
- }
- catch (e)
- {
- error(e);
- }
- });
- if (typeof mermaid === 'undefined' && !this.loadingMermaid && !this.isOffline(true))
- {
- this.loadingMermaid = true;
-
- if (urlParams['dev'] == '1')
- {
- if (urlParams['mermaidToDrawioTest'] == '1')
- {
- mxMermaidToDrawio.addListener(mxUtils.bind(this, function(modelXml)
- {
- this.importXml(modelXml, null, null, null, null, null, true);
- }));
- }
-
- mxscript('js/mermaid/mermaid.min.js', delayed,
- null, null, null, onerror);
- }
- else
- {
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed,
- null, null, null, onerror);
- }
- }
- else
- {
- window.setTimeout(delayed, 0);
- }
- };
-
- /**
- * Generates a plant UML image. Possible types are svg, png and txt.
- */
- EditorUi.prototype.generatePlantUmlImage = function(data, type, success, error)
- {
- function encode64(data)
- {
- r = "";
-
- for (i = 0; i < data.length; i += 3)
- {
- if (i + 2 == data.length)
- {
- r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), 0);
- }
- else if (i + 1 == data.length)
- {
- r += append3bytes(data.charCodeAt(i), 0, 0);
- }
- else
- {
- r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1),
- data.charCodeAt(i + 2));
- }
- }
-
- return r;
- }
- function append3bytes(b1, b2, b3)
- {
- c1 = b1 >> 2;
- c2 = ((b1 & 0x3) << 4) | (b2 >> 4);
- c3 = ((b2 & 0xF) << 2) | (b3 >> 6);
- c4 = b3 & 0x3F;
- r = "";
- r += encode6bit(c1 & 0x3F);
- r += encode6bit(c2 & 0x3F);
- r += encode6bit(c3 & 0x3F);
- r += encode6bit(c4 & 0x3F);
-
- return r;
- }
- function encode6bit(b)
- {
- if (b < 10)
- {
- return String.fromCharCode(48 + b);
- }
-
- b -= 10;
-
- if (b < 26)
- {
- return String.fromCharCode(65 + b);
- }
-
- b -= 26;
-
- if (b < 26)
- {
- return String.fromCharCode(97 + b);
- }
-
- b -= 26;
-
- if (b == 0)
- {
- return '-';
- }
-
- if (b == 1)
- {
- return '_';
- }
-
- return '?';
- }
- // TODO: Remove unescape, use btoa for compatibility with graph.compress
- function compress(s)
- {
- return encode64(Graph.arrayBufferToString(pako.deflateRaw(s)));
- };
- var plantUmlServerUrl = (type == 'txt') ? PLANT_URL + '/txt/' :
- ((type == 'png') ? PLANT_URL + '/png/' : PLANT_URL + '/svg/');
-
- var xhr = new XMLHttpRequest();
- xhr.open('GET', plantUmlServerUrl + compress(data), true);
- if (type != 'txt')
- {
- xhr.responseType = 'blob';
- }
- xhr.onload = function(e)
- {
- if (this.status >= 200 && this.status < 300)
- {
- if (type == 'txt')
- {
- success(this.response);
- }
- else
- {
- var reader = new FileReader();
- reader.readAsDataURL(this.response);
- reader.onloadend = function(e)
- {
- var img = new Image();
- img.onload = function()
- {
- try
- {
- var w = img.width;
- var h = img.height;
-
- // Workaround for 0 image size in IE11
- if (w == 0 && h == 0)
- {
- var data = reader.result;
- var comma = data.indexOf(',');
- var svgText = decodeURIComponent(escape(atob(data.substring(comma + 1))));
- var root = mxUtils.parseXml(svgText);
- var svgs = root.getElementsByTagName('svg');
-
- if (svgs.length > 0)
- {
- w = parseFloat(svgs[0].getAttribute('width'));
- h = parseFloat(svgs[0].getAttribute('height'));
- }
- }
-
- success(reader.result, w, h);
- }
- catch (e)
- {
- error(e);
- }
- };
- img.src = reader.result;
- };
- reader.onerror = function(e)
- {
- error(e);
- };
- }
- }
- else
- {
- error(e);
- }
- };
- xhr.onerror = function(e)
- {
- error(e);
- };
- xhr.send();
- };
- /**
- * Inserts the given text as a preformatted HTML text.
- */
- EditorUi.prototype.insertAsPreText = function(text, x, y)
- {
- var graph = this.editor.graph;
- var cell = null;
-
- graph.getModel().beginUpdate();
- try
- {
- cell = graph.insertVertex(null, null, '<pre>' + text + '</pre>',
- x, y, 1, 1, 'text;html=1;align=left;verticalAlign=top;');
- graph.updateCellSize(cell, true);
- }
- finally
- {
- graph.getModel().endUpdate();
- }
- return cell;
- };
- /**
- * Imports the given XML into the existing diagram.
- * TODO: Make this function asynchronous
- */
- EditorUi.prototype.insertTextAt = function(text, dx, dy, html, asImage, crop, resizeImages, addNewPage)
- {
- crop = (crop != null) ? crop : true;
- resizeImages = (resizeImages != null) ? resizeImages : true;
-
- // Handles special case for Gliffy data which requires async server-side for parsing
- if (text != null)
- {
- if (Graph.fileSupport && new XMLHttpRequest().upload && this.isRemoteFileFormat(text))
- {
- if (this.isOffline())
- {
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- else
- {
- // Fixes possible parsing problems with ASCII 160 (non-breaking space)
- this.parseFileData(text.replace(/\s+/g,' '), mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status <= 299)
- {
- this.editor.graph.setSelectionCells(this.insertTextAt(
- xhr.responseText, dx, dy, true));
- }
- }));
- }
- // Returns empty cells array as it is aysynchronous
- return [];
- }
- // Handles special case of data URI which requires async loading for finding size
- else if (text.substring(0, 5) == 'data:' || (!this.isOffline() &&
- (asImage || (/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(text))))
- {
- var graph = this.editor.graph;
-
- // Checks for embedded XML in PDF
- if (text.substring(0, 28) == 'data:application/pdf;base64,')
- {
- var xml = Editor.extractGraphModelFromPdf(text);
-
- if (xml != null && xml.length > 0)
- {
- return this.importXml(xml, dx, dy, crop, true, addNewPage);
- }
- }
-
- // Checks for embedded XML in PNG
- if (Editor.isPngDataUrl(text))
- {
- var xml = Editor.extractGraphModelFromPng(text);
-
- if (xml != null && xml.length > 0)
- {
- return this.importXml(xml, dx, dy, crop, true, addNewPage);
- }
- }
-
- // Tries to extract embedded XML from SVG data URI
- if (text.substring(0, 19) == 'data:image/svg+xml;')
- {
- try
- {
- var xml = null;
-
- if (text.substring(0, 26) == 'data:image/svg+xml;base64,')
- {
- xml = text.substring(text.indexOf(',') + 1);
- xml = (window.atob && !mxClient.IS_SF) ? atob(xml) : Base64.decode(xml, true);
- }
- else
- {
- xml = decodeURIComponent(text.substring(text.indexOf(',') + 1));
- }
-
- var result = this.importXml(xml, dx, dy, crop, true, addNewPage);
-
- if (result.length > 0)
- {
- return result;
- }
- }
- catch (e)
- {
- // Ignore
- }
- }
-
- this.loadImage(text, mxUtils.bind(this, function(img)
- {
- if (text.substring(0, 5) == 'data:')
- {
- this.resizeImage(img, text, mxUtils.bind(this, function(data2, w2, h2)
- {
- graph.setSelectionCell(graph.insertVertex(null, null, '', graph.snap(dx), graph.snap(dy),
- w2, h2, 'shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;' +
- 'verticalAlign=top;aspect=fixed;imageAspect=0;image=' + this.convertDataUri(data2) + ';'));
- }), resizeImages, this.maxImageSize);
- }
- else
- {
- var s = Math.min(1, Math.min(this.maxImageSize / img.width, this.maxImageSize / img.height));
- var w = Math.round(img.width * s);
- var h = Math.round(img.height * s);
-
- graph.setSelectionCell(graph.insertVertex(null, null, '', graph.snap(dx), graph.snap(dy),
- w, h, 'shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;' +
- 'verticalAlign=top;aspect=fixed;imageAspect=0;image=' + text + ';'));
- }
- }), mxUtils.bind(this, function()
- {
- var cell = null;
-
- // Inserts invalid data URIs as text
- graph.getModel().beginUpdate();
- try
- {
- cell = graph.insertVertex(graph.getDefaultParent(), null, text,
- graph.snap(dx), graph.snap(dy), 1, 1, 'text;' +
- ((html) ? 'html=1;' : ''));
- graph.updateCellSize(cell);
- graph.fireEvent(new mxEventObject('textInserted', 'cells', [cell]));
- }
- finally
- {
- graph.getModel().endUpdate();
- }
-
- graph.setSelectionCell(cell);
- }));
-
- return [];
- }
- else
- {
- text = Graph.zapGremlins(mxUtils.trim(text));
-
- if (this.isCompatibleString(text))
- {
- return this.importXml(text, dx, dy, crop, null, addNewPage);
- }
- else if (text.length > 0)
- {
- if (this.isLucidChartData(text))
- {
- this.convertLucidChart(text, mxUtils.bind(this, function(xml)
- {
- this.editor.graph.setSelectionCells(
- this.importXml(xml, dx, dy, crop,
- null, addNewPage));
- }), mxUtils.bind(this, function(e)
- {
- this.handleError(e);
- }));
- }
- else
- {
- var graph = this.editor.graph;
- var cell = null;
-
- graph.getModel().beginUpdate();
- try
- {
- // Fires cellsInserted to apply the current style to the inserted text.
- // This requires the value to be empty when the event is fired.
- cell = graph.insertVertex(graph.getDefaultParent(), null, '',
- graph.snap(dx), graph.snap(dy), 1, 1, 'text;whiteSpace=wrap;' +
- ((html) ? 'html=1;' : ''));
- graph.fireEvent(new mxEventObject('textInserted', 'cells', [cell]));
-
- if (html)
- {
- text = graph.sanitizeHtml(text);
- }
- //TODO Refuse unsupported file types early as at this stage a lot of processing has beed done and time is wasted.
- // For example, 5 MB PDF files is processed and then only 0.5 MB of meaningless text is added!
- //Limit labels to maxTextBytes
- if (text.length > this.maxTextBytes)
- {
- text = text.substring(0, this.maxTextBytes) + '...';
- }
-
- // Apply value and updates the cell size to fit the text block
- cell.value = text;
- graph.updateCellSize(cell);
-
- // Adds wrapping for large text blocks
- if (this.maxTextWidth > 0 && cell.geometry.width > this.maxTextWidth)
- {
- var size = graph.getPreferredSizeForCell(cell, this.maxTextWidth);
- cell.geometry.width = size.width;
- cell.geometry.height = size.height;
- }
-
- // See https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
- if (Graph.isLink(cell.value))
- {
- graph.setLinkForCell(cell, cell.value);
- }
-
- // Adds spacing
- cell.geometry.width += graph.gridSize;
- cell.geometry.height += graph.gridSize;
- }
- finally
- {
- graph.getModel().endUpdate();
- }
-
- return [cell];
- }
- }
- }
- }
-
- return [];
- };
- /**
- * Formats the given file size.
- */
- EditorUi.prototype.formatFileSize = function(size)
- {
- var units = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
- var i = -1;
-
- do
- {
- size = size / 1024;
- i++;
- } while (size > 1024);
- return Math.max(size, 0.1).toFixed(1) + units[i];
- };
- /**
- * Imports the given XML into the existing diagram.
- */
- EditorUi.prototype.convertDataUri = function(uri)
- {
- // Handles special case of data URI which needs to be rewritten
- // to be used in a cell style to remove the semicolon
- if (uri.substring(0, 5) == 'data:')
- {
- var semi = uri.indexOf(';');
-
- if (semi > 0)
- {
- uri = uri.substring(0, semi) + uri.substring(uri.indexOf(',', semi + 1));
- }
- }
-
- return uri;
- };
-
- /**
- * Returns true for Gliffy data.
- */
- EditorUi.prototype.isRemoteFileFormat = function(data, filename)
- {
- return /(\"contentType\":\s*\"application\/gliffy\+json\")/.test(data);
- };
-
- /**
- * Returns true for Gliffy
- */
- EditorUi.prototype.isLucidChartData = function(data)
- {
- return data != null && (data.substring(0, 26) ==
- '{"state":"{\\"Properties\\":' ||
- data.substring(0, 14) == '{"Properties":');
- };
- /**
- * Imports a local file from the device or local storage.
- */
- EditorUi.prototype.importLocalFile = function(device, noSplash)
- {
- if (device && Graph.fileSupport)
- {
- if (this.importFileInputElt == null)
- {
- var input = document.createElement('input');
- input.setAttribute('type', 'file');
-
- mxEvent.addListener(input, 'change', mxUtils.bind(this, function()
- {
- if (input.files != null)
- {
- // Using null for position will disable crop of input file
- this.importFiles(input.files, null, null, this.maxImageSize);
-
- // Resets input to force change event for same file (type reset required for IE)
- input.type = '';
- input.type = 'file';
- input.value = '';
- }
- }));
-
- input.style.display = 'none';
- document.body.appendChild(input);
- this.importFileInputElt = input;
- }
-
- this.importFileInputElt.click();
- }
- else
- {
- window.openNew = false;
- window.openKey = 'import';
-
- window.listBrowserFiles = mxUtils.bind(this, function(success, error)
- {
- StorageFile.listFiles(this, 'F', success, error);
- });
-
- window.openBrowserFile = mxUtils.bind(this, function(title, success, error)
- {
- StorageFile.getFileContent(this, title, success, error);
- });
-
- window.deleteBrowserFile = mxUtils.bind(this, function(title, success, error)
- {
- StorageFile.deleteFile(this, title, success, error);
- });
- if (!noSplash)
- {
- var prevValue = Editor.useLocalStorage;
- Editor.useLocalStorage = !device;
- }
- // Closes dialog after open
- window.openFile = new OpenFile(mxUtils.bind(this, function(cancel)
- {
- this.hideDialog(cancel);
- }));
-
- window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename)
- {
- if (filename != null && Graph.fileSupport && /(\.v(dx|sdx?))($|\?)/i.test(filename))
- {
- // "Not a UTF 8 file" when opening VSDX in IE so this is never called
- var file = new Blob([xml], {type: 'application/octet-stream'})
-
- this.importVisio(file, mxUtils.bind(this, function(xml)
- {
- this.importXml(xml, 0, 0, true);
- }), null, filename);
- }
- else
- {
- this.editor.graph.setSelectionCells(this.importXml(xml, 0, 0, true));
- }
- }));
- // Removes openFile if dialog is closed
- this.showDialog(new OpenDialog(this).container, (Editor.useLocalStorage) ? 640 : 360,
- (Editor.useLocalStorage) ? 480 : 220, true, true, function()
- {
- window.openFile = null;
- });
-
- // Extends dialog close to show splash screen
- if (!noSplash)
- {
- var dlg = this.dialog;
- var dlgClose = dlg.close;
-
- this.dialog.close = mxUtils.bind(this, function(cancel)
- {
- Editor.useLocalStorage = prevValue;
- dlgClose.apply(dlg, arguments);
-
- if (cancel && this.getCurrentFile() == null && urlParams['embed'] != '1')
- {
- this.showSplash();
- }
- });
- }
- }
- };
- /**
- * Imports the given zip file.
- */
- EditorUi.prototype.importZipFile = function(file, success, onerror)
- {
- var ui = this;
-
- var delayed = mxUtils.bind(this, function()
- {
- this.loadingExtensions = false;
-
- if (typeof JSZip !== 'undefined')
- {
- JSZip.loadAsync(file).then(function(zip)
- {
- if (mxUtils.isEmptyObject(zip.files))
- {
- onerror();
- }
- else
- {
- var gliffyLatestVer = {version: 0};
- var drawioFound = false;
-
- zip.forEach(function (relativePath, zipEntry)
- {
- var name = zipEntry.name.toLowerCase();
-
- if (name == 'diagram/diagram.xml') //draw.io zip format has the latest diagram version at diagram/diagram.xml
- {
- drawioFound = true;
-
- zipEntry.async("string").then(function(str){
- if (str.indexOf('<mxfile ') == 0)
- {
- success(str);
- }
- else
- {
- onerror();
- }
- });
- }
- else if (name.indexOf('versions/') == 0) //Gliffy zip format has the versions inside versions folder
- {
- var version = parseInt(name.substr(9)); //9 is the length of versions/
-
- if (version > gliffyLatestVer.version)
- {
- gliffyLatestVer = {version: version, zipEntry: zipEntry}
- }
- }
- });
-
- if (gliffyLatestVer.version > 0)
- {
- gliffyLatestVer.zipEntry.async("string").then(function(data)
- {
- if (new XMLHttpRequest().upload && ui.isRemoteFileFormat(data, file.name))
- {
- if (ui.isOffline())
- {
- ui.showError(mxResources.get('error'), mxResources.get('notInOffline'), null, onerror);
- }
- else
- {
- ui.parseFileData(data, mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4)
- {
- if (xhr.status >= 200 && xhr.status <= 299)
- {
- success(xhr.responseText);
- }
- else
- {
- onerror();
- }
- }
- }), file.name);
- }
- }
- else
- {
- onerror();
- }
- });
- }
- else if (!drawioFound)
- {
- onerror();
- }
- }
- }, function (e) {
- onerror(e);
- });
- }
- else
- {
- onerror();
- }
- });
-
- if (typeof JSZip === 'undefined' && !this.loadingExtensions && !this.isOffline(true))
- {
- this.loadingExtensions = true;
- mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', delayed,
- null, null, null, onerror);
- }
- else
- {
- delayed();
- }
- };
-
- /**
- * Imports the given XML into the existing diagram.
- */
- EditorUi.prototype.importFile = function(data, mimeType, dx, dy, w, h, filename,
- done, file, crop, ignoreEmbeddedXml, evt)
- {
- crop = (crop != null) ? crop : true;
- var async = false;
- var cells = null;
- var handleResult = mxUtils.bind(this, function(xml)
- {
- var importedCells = null;
-
- if (xml != null && xml.substring(0, 10) == '<mxlibrary')
- {
- this.loadLibrary(new LocalLibrary(this, xml, filename));
- this.showSidebar();
- }
- else
- {
- importedCells = this.importXml(xml, dx, dy, crop, null,
- (evt != null) ? mxEvent.isControlDown(evt) : null);
- }
-
- if (done != null)
- {
- done(importedCells);
- }
- });
- if (mimeType.substring(0, 5) == 'image')
- {
- var containsModel = false;
- if (mimeType.substring(0, 9) == 'image/png')
- {
- var xml = (ignoreEmbeddedXml) ? null : this.extractGraphModelFromPng(data);
-
- if (xml != null && xml.length > 0)
- {
- cells = this.importXml(xml, dx, dy, crop, null, (evt != null) ?
- mxEvent.isControlDown(evt) : null);
- containsModel = true;
- }
- }
-
- if (!containsModel)
- {
- var graph = this.editor.graph;
-
- // Strips encoding bit (eg. ;base64,) for cell style
- var semi = data.indexOf(';');
-
- if (semi > 0)
- {
- data = data.substring(0, semi) + data.substring(data.indexOf(',', semi + 1));
- }
-
- if (crop && graph.isGridEnabled())
- {
- dx = graph.snap(dx);
- dy = graph.snap(dy);
- }
- cells = [graph.insertVertex(null, null, '', dx, dy, w, h,
- 'shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;' +
- 'verticalAlign=top;aspect=fixed;imageAspect=0;image=' + data + ';')];
- }
- }
- else if (/(\.*<graphml )/.test(data))
- {
- async = true;
- this.importGraphML(data, handleResult);
- }
- else if (file != null && filename != null && EditorUi.isVisioFilename(filename))
- {
- // LATER: done and async are a hack before making this asynchronous
- async = true;
- this.importVisio(file, handleResult);
- }
- else if (new XMLHttpRequest().upload && this.isRemoteFileFormat(data, filename))
- {
- if (this.isOffline())
- {
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- else
- {
- // LATER: done and async are a hack before making this asynchronous
- async = true;
- // Returns empty cells array as it is aysynchronous
- var parseCallback = mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4)
- {
- if (xhr.status >= 200 && xhr.status <= 299)
- {
- handleResult(xhr.responseText);
- }
- else if (done != null)
- {
- done(null);
- this.showError(mxResources.get('error'), xhr.status == 413? mxResources.get('diagramTooLarge') :
- mxResources.get('unknownError'));
- }
- }
- });
- if (data != null)
- {
- this.parseFileData(data, parseCallback, filename);
- }
- else
- {
- this.parseFile(file, parseCallback, filename);
- }
- }
- }
- else if (data.indexOf('PK') == 0 && file != null)
- {
- async = true;
-
- this.importZipFile(file, handleResult, mxUtils.bind(this, function()
- {
- //If importing as a zip file failed, just insert as text
- cells = this.insertTextAt(this.validateFileData(data), dx, dy, true, null, crop);
- done(cells);
- }));
- }
- else if (!/(\.v(sd|dx))($|\?)/i.test(filename) && !/(\.vs(s|x))($|\?)/i.test(filename))
- {
- cells = this.insertTextAt(this.validateFileData(data), dx, dy, true,
- null, crop, null, (evt != null) ? mxEvent.isControlDown(evt) : null);
- }
- if (!async && done != null)
- {
- done(cells);
- }
-
- return cells;
- };
- /**
- *
- */
- EditorUi.prototype.importFiles = function(files, x, y, maxSize, fn, resultFn, filterFn, barrierFn,
- resizeDialog, maxBytes, resampleThreshold, ignoreEmbeddedXml, evt)
- {
- maxSize = (maxSize != null) ? maxSize : this.maxImageSize;
- maxBytes = (maxBytes != null) ? maxBytes : this.maxImageBytes;
-
- var crop = x != null && y != null;
- var resizeImages = true;
- x = (x != null) ? x : 0;
- y = (y != null) ? y : 0;
-
- // Checks if large images are imported
- var largeImages = false;
-
- if (!mxClient.IS_CHROMEAPP && files != null)
- {
- var thresh = resampleThreshold || this.resampleThreshold;
-
- for (var i = 0; i < files.length; i++)
- {
- if (files[i].type.substring(0, 9) !== 'image/svg' &&
- files[i].type.substring(0, 6) === 'image/' &&
- files[i].size > thresh)
- {
- largeImages = true;
-
- break;
- }
- }
- }
- var doImportFiles = mxUtils.bind(this, function()
- {
- var graph = this.editor.graph;
- var gs = graph.gridSize;
-
- fn = (fn != null) ? fn : mxUtils.bind(this, function(data, mimeType, x, y, w, h, filename, done, file)
- {
- try
- {
- if (data != null && data.substring(0, 10) == '<mxlibrary')
- {
- this.spinner.stop();
- this.loadLibrary(new LocalLibrary(this, data, filename));
- this.showSidebar();
-
- return null;
- }
- else
- {
- // Drop on empty file ignores drop location
- if (this.isCompatibleString(data) && files.length == 1 && evt != null &&
- evt.type == 'drop' && this.isBlankFile() && !this.canUndo())
- {
- crop = false;
- x = 0;
- y = 0;
- }
- return this.importFile(data, mimeType, x, y, w, h, filename,
- done, file, crop, ignoreEmbeddedXml, evt);
- }
- }
- catch (e)
- {
- this.handleError(e);
-
- return null;
- }
- });
-
- resultFn = (resultFn != null) ? resultFn : mxUtils.bind(this, function(cells)
- {
- graph.setSelectionCells(cells);
- });
-
- if (this.spinner.spin(document.body, mxResources.get('loading')))
- {
- var count = files.length;
- var remain = count;
- var queue = [];
-
- // Barrier waits for all files to be loaded asynchronously
- var barrier = mxUtils.bind(this, function(index, fnc)
- {
- queue[index] = fnc;
-
- if (--remain == 0)
- {
- this.spinner.stop();
-
- if (barrierFn != null)
- {
- barrierFn(queue);
- }
- else
- {
- var cells = [];
-
- graph.getModel().beginUpdate();
- try
- {
- for (var j = 0; j < queue.length; j++)
- {
- var tmp = queue[j]();
-
- if (tmp != null)
- {
- cells = cells.concat(tmp);
- }
- }
- }
- finally
- {
- graph.getModel().endUpdate();
- }
- }
-
- resultFn(cells);
- }
- });
-
- for (var i = 0; i < count; i++)
- {
- (mxUtils.bind(this, function(index)
- {
- var file = files[index];
-
- if (file != null)
- {
- var reader = new FileReader();
-
- reader.onload = mxUtils.bind(this, function(e)
- {
- if (filterFn == null || filterFn(file))
- {
- try
- {
- if (file.type.substring(0, 6) == 'image/')
- {
- if (file.type.substring(0, 9) == 'image/svg')
- {
- // Checks if SVG contains content attribute
- var data = Graph.clipSvgDataUri(e.target.result);
- var comma = data.indexOf(',');
- var svgText = decodeURIComponent(escape(atob(data.substring(comma + 1))));
- var root = mxUtils.parseXml(svgText);
- var svgs = root.getElementsByTagName('svg');
-
- if (svgs.length > 0)
- {
- var svgRoot = svgs[0];
- var cont = (ignoreEmbeddedXml) ? null : svgRoot.getAttribute('content');
-
- if (cont != null && cont.charAt(0) != '<' && cont.charAt(0) != '%')
- {
- cont = unescape((window.atob) ? atob(cont) : Base64.decode(cont, true));
- }
-
- if (cont != null && cont.charAt(0) == '%')
- {
- cont = decodeURIComponent(cont);
- }
-
- if (cont != null && (cont.substring(0, 8) === '<mxfile ' ||
- cont.substring(0, 14) === '<mxGraphModel '))
- {
- barrier(index, mxUtils.bind(this, function()
- {
- return fn(cont, 'text/xml', x + index * gs, y + index * gs, 0, 0, file.name);
- }));
- }
- else
- {
- // SVG needs special handling to add viewbox if missing and
- // find initial size from SVG attributes (only for IE11)
- barrier(index, mxUtils.bind(this, function()
- {
- try
- {
- // Parses SVG and find width and height
- if (root != null)
- {
- var svgs = root.getElementsByTagName('svg');
-
- if (svgs.length > 0)
- {
- var svgRoot = svgs[0];
- var w = svgRoot.getAttribute('width');
- var h = svgRoot.getAttribute('height');
-
- if (w != null && w.charAt(w.length - 1) != '%')
- {
- w = parseFloat(w);
- }
- else
- {
- w = NaN;
- }
-
- if (h != null && h.charAt(h.length - 1) != '%')
- {
- h = parseFloat(h);
- }
- else
- {
- h = NaN;
- }
-
- // Check if viewBox attribute already exists
- var vb = svgRoot.getAttribute('viewBox');
-
- if (vb == null || vb.length == 0)
- {
- svgRoot.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
- }
- // Uses width and height from viewbox for
- // missing width and height attributes
- else if (isNaN(w) || isNaN(h))
- {
- var tokens = vb.split(' ');
-
- if (tokens.length > 3)
- {
- w = parseFloat(tokens[2]);
- h = parseFloat(tokens[3]);
- }
- }
-
- data = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
- var s = Math.min(1, Math.min(maxSize / Math.max(1, w)), maxSize / Math.max(1, h));
- var cells = fn(data, file.type, x + index * gs, y + index * gs, Math.max(
- 1, Math.round(w * s)), Math.max(1, Math.round(h * s)), file.name);
-
- // Hack to fix width and height asynchronously
- if (cells != null && (isNaN(w) || isNaN(h)))
- {
- var img = new Image();
-
- img.onload = mxUtils.bind(this, function()
- {
- w = Math.max(1, img.width);
- h = Math.max(1, img.height);
-
- cells[0].geometry.width = w;
- cells[0].geometry.height = h;
-
- svgRoot.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
- data = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
-
- var semi = data.indexOf(';');
-
- if (semi > 0)
- {
- data = data.substring(0, semi) + data.substring(data.indexOf(',', semi + 1));
- }
-
- graph.setCellStyles('image', data, [cells[0]]);
- });
-
- img.src = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
- }
-
- return cells;
- }
- }
- }
- catch (e)
- {
- // ignores any SVG parsing errors
- }
-
- return null;
- }));
- }
- }
- else
- {
- barrier(index, mxUtils.bind(this, function()
- {
- return null;
- }));
- }
- }
- else
- {
- // Checks if PNG+XML is available to bypass code below
- var containsModel = false;
-
- if (file.type == 'image/png')
- {
- var xml = (ignoreEmbeddedXml) ? null : this.extractGraphModelFromPng(e.target.result);
-
- if (xml != null && xml.length > 0)
- {
- var img = new Image();
- img.src = e.target.result;
-
- barrier(index, mxUtils.bind(this, function()
- {
- return fn(xml, 'text/xml', x + index * gs, y + index * gs,
- img.width, img.height, file.name);
- }));
-
- containsModel = true;
- }
- }
-
- // Additional asynchronous step for finding image size
- if (!containsModel)
- {
- // Cannot load local files in Chrome App
- if (mxClient.IS_CHROMEAPP)
- {
- this.spinner.stop();
- this.showError(mxResources.get('error'), mxResources.get('dragAndDropNotSupported'),
- mxResources.get('cancel'), mxUtils.bind(this, function()
- {
- // Hides the dialog
- }), null, mxResources.get('ok'), mxUtils.bind(this, function()
- {
- // Redirects to import function
- this.actions.get('import').funct();
- })
- );
- }
- else
- {
- this.loadImage(e.target.result, mxUtils.bind(this, function(img)
- {
- this.resizeImage(img, e.target.result, mxUtils.bind(this, function(data2, w2, h2)
- {
- barrier(index, mxUtils.bind(this, function()
- {
- // Refuses to insert images above a certain size as they kill the app
- if (data2 != null && data2.length < maxBytes)
- {
- var s = (!resizeImages || !this.isResampleImageSize(
- file.size, resampleThreshold)) ? 1 :
- Math.min(1, Math.min(maxSize / w2, maxSize / h2));
-
- return fn(data2, file.type, x + index * gs, y + index * gs,
- Math.round(w2 * s), Math.round(h2 * s), file.name);
- }
- else
- {
- this.handleError({message: mxResources.get('imageTooBig')});
-
- return null;
- }
- }));
- }), resizeImages, maxSize, resampleThreshold, file.size);
- }), mxUtils.bind(this, function()
- {
- this.handleError({message: mxResources.get('invalidOrMissingFile')});
- }));
- }
- }
- }
- }
- else
- {
- var data = e.target.result;
-
- fn(data, file.type, x + index * gs, y + index * gs, 240, 160, file.name, function(cells)
- {
- barrier(index, function()
- {
- return cells;
- });
- }, file);
- }
- }
- catch (e)
- {
- // Ignores file parsing error
- barrier(index, mxUtils.bind(this, function()
- {
- return null;
- }));
- if (window.console != null)
- {
- console.error(e, file);
- }
- }
- }
- else
- {
- // Ignores file and decrements counter
- barrier(index, mxUtils.bind(this, function()
- {
- return null;
- }));
- }
- });
-
- // Handles special cases
- if (EditorUi.isVisioFilename(file.name))
- {
- fn(null, file.type, x + index * gs, y + index * gs, 240, 160, file.name, function(cells)
- {
- barrier(index, function()
- {
- return cells;
- });
- }, file);
- }
- else if (file.type.substring(0, 5) == 'image' || file.type == 'application/pdf')
- {
- reader.readAsDataURL(file);
- }
- else
- {
- reader.readAsText(file);
- }
- }
- }))(i);
- }
- }
- });
-
- if (largeImages)
- {
- // Workaround for lost files array in async code
- var tmp = [];
-
- for (var i = 0; i < files.length; i++)
- {
- tmp.push(files[i]);
- }
-
- files = tmp;
-
- this.confirmImageResize(function(doResize)
- {
- resizeImages = doResize;
- doImportFiles();
- }, resizeDialog);
- }
- else
- {
- doImportFiles();
- }
- };
- /**
- * Returns true if the current file is a blank diagram.
- */
- EditorUi.prototype.isBlankFile = function()
- {
- return this.pages != null && this.pages.length == 1 &&
- this.isDiagramEmpty() && this.currentPage.getName() ==
- mxResources.get('pageWithNumber', [1]);
- };
- /**
- * Parses the file using XHR2 via the server. File can be a blob or file object.
- * Filename is an optional parameter for blobs (that do not have a filename).
- */
- EditorUi.prototype.confirmImageResize = function(fn, force)
- {
- force = (force != null) ? force : false;
- var resume = (this.spinner != null && this.spinner.pause != null) ? this.spinner.pause() : function() {};
- var resizeImages = (isLocalStorage || mxClient.IS_CHROMEAPP) ? mxSettings.getResizeImages() : null;
-
- var wrapper = function(remember, resize)
- {
- if (remember || force)
- {
- mxSettings.setResizeImages((remember) ? resize : null);
- mxSettings.save();
- }
-
- resume();
- fn(resize);
- };
- if (resizeImages != null && !force)
- {
- wrapper(false, resizeImages);
- }
- else
- {
- this.showDialog(new ConfirmDialog(this, mxResources.get('resizeLargeImages'),
- function(remember)
- {
- wrapper(remember, true);
- },
- function(remember)
- {
- wrapper(remember, false);
- }, mxResources.get('resize'), mxResources.get('actualSize'),
- '<img style="margin-top:8px;" src="' + Editor.loResImage + '"/>',
- '<img style="margin-top:8px;" src="' + Editor.hiResImage + '"/>',
- isLocalStorage || mxClient.IS_CHROMEAPP).container, 340,
- (isLocalStorage || mxClient.IS_CHROMEAPP) ? 226 : 200, true, true);
- }
- };
-
- /**
- * Parses the file using XHR2 via the server. File can be a blob or file object.
- * Filename is an optional parameter for blobs (that do not have a filename).
- */
- EditorUi.prototype.parseFile = function(file, fn, filename)
- {
- filename = (filename != null) ? filename : file.name;
- var reader = new FileReader();
- reader.onload = mxUtils.bind(this, function()
- {
- this.parseFileData(reader.result, fn, filename)
- });
- reader.readAsText(file);
- };
- //TODO Use this version of the function instead of creating a Blob then read it again
- EditorUi.prototype.parseFileData = function(data, fn, filename)
- {
- var xhr = new XMLHttpRequest();
- xhr.open('POST', OPEN_URL);
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- xhr.onreadystatechange = function()
- {
- fn(xhr);
- };
-
- xhr.send('format=xml&filename=' + encodeURIComponent(filename) + '&data=' + encodeURIComponent(data));
-
- try
- {
- EditorUi.logEvent({category: 'GLIFFY-IMPORT-FILE',
- action: 'size_' + file.size});
- }
- catch (e)
- {
- // ignore
- }
- };
-
- /**
- *
- */
- EditorUi.prototype.isResampleImageSize = function(size, thresh)
- {
- thresh = (thresh != null) ? thresh : this.resampleThreshold;
- return size > thresh;
- };
-
- /**
- * Resizes the given image if <maxImageBytes> is not null.
- */
- EditorUi.prototype.resizeImage = function(img, data, fn, enabled, maxSize, thresh, fileSize)
- {
- maxSize = (maxSize != null) ? maxSize : this.maxImageSize;
- var w = Math.max(1, img.width);
- var h = Math.max(1, img.height);
- var originalData = data;
- if (enabled && this.isResampleImageSize((fileSize != null) ? fileSize : data.length, thresh))
- {
- try
- {
- var factor = Math.max(w / maxSize, h / maxSize);
-
- if (factor > 1)
- {
- var w2 = Math.round(w / factor);
- var h2 = Math.round(h / factor);
-
- var canvas = document.createElement('canvas');
- canvas.width = w2;
- canvas.height = h2;
-
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0, w2, h2);
- var tmp = canvas.toDataURL();
- // Uses new image if smaller
- if (tmp.length < data.length)
- {
- // Checks if the image is empty by comparing
- // with an empty image of the same size
- var canvas2 = document.createElement('canvas');
- canvas2.width = w2;
- canvas2.height = h2;
- var tmp2 = canvas2.toDataURL();
-
- if (tmp !== tmp2)
- {
- data = tmp;
- w = w2;
- h = h2;
- }
- }
- }
- }
- catch (e)
- {
- // ignores image scaling errors
- }
- }
- if (enabled && originalData != data && maxSize > this.maxImageSize / 2 && data.length > this.maxImageBytes)
- {
- this.resizeImage(img, data, fn, enabled, maxSize / 1.5, thresh, fileSize);
- }
- else
- {
- fn(data, w, h);
- }
- };
-
- /**
- * Extracts the XML from the compressed or non-compressed text chunk.
- */
- EditorUi.prototype.extractGraphModelFromPng = function(data)
- {
- return Editor.extractGraphModelFromPng(data);
- };
- /**
- * Loads the image from the given URI.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.loadImage = function(uri, onload, onerror)
- {
- try
- {
- var img = new Image();
-
- img.onload = function()
- {
- img.width = (img.width > 0) ? img.width : 120;
- img.height = (img.height > 0) ? img.height : 120;
-
- onload(img);
- };
-
- if (onerror != null)
- {
- img.onerror = onerror;
- };
-
- img.src = uri;
- }
- catch (e)
- {
- if (onerror != null)
- {
- onerror(e);
- }
- else
- {
- throw e;
- }
- }
- };
- /**
- * Returns the default value for sketch mode.
- */
- EditorUi.prototype.getDefaultSketchMode = function()
- {
- var defaultValue = urlParams['sketch'] == '1';
- var roughParam = (urlParams['rough'] != null) ? urlParams['rough'] : defaultValue;
-
- return roughParam != '0';
- };
- /**
- * Overridden to set sketch mode before UI is created.
- */
- var editorUiCreateUi = EditorUi.prototype.createUi;
- EditorUi.prototype.createUi = function()
- {
- if (Editor.isSettingsEnabled())
- {
- if (mxSettings.settings.sidebarTitles != null)
- {
- Sidebar.prototype.sidebarTitles = mxSettings.settings.sidebarTitles;
- }
- this.formatWidth = mxSettings.getFormatWidth();
- if ((Editor.config == null || Editor.config.enableCssDarkMode == null) &&
- mxSettings.settings.enableCssDarkMode != null)
- {
- Editor.enableCssDarkMode = mxSettings.settings.enableCssDarkMode;
- }
- }
-
- editorUiCreateUi.apply(this, arguments);
- if (Editor.isSettingsEnabled())
- {
- this.doSetSketchMode((mxSettings.settings.sketchMode != null && urlParams['rough'] == null &&
- urlParams['sketch'] == null) ? mxSettings.settings.sketchMode : this.getDefaultSketchMode());
- }
- };
- /**
- * Initializes the UI.
- */
- var editorUiInit = EditorUi.prototype.init;
- EditorUi.prototype.init = function()
- {
- mxStencilRegistry.allowEval = mxStencilRegistry.allowEval && !this.isOfflineApp();
-
- var ui = this;
- var graph = this.editor.graph;
- var graphIsEnabled = graph.isEnabled;
- graph.isEnabled = function()
- {
- return graphIsEnabled.apply(this, arguments) && !ui.isLocked();
- };
- // Stops panning while freehand is active
- if (Graph.touchStyle)
- {
- graph.panningHandler.isPanningTrigger = function(me)
- {
- var evt = me.getEvent();
-
- return (me.getState() == null && (!mxEvent.isMouseEvent(evt) &&
- !graph.freehand.isDrawing())) ||
- (mxEvent.isPopupTrigger(evt) && (me.getState() == null ||
- mxEvent.isControlDown(evt) || mxEvent.isShiftDown(evt)));
- };
- }
-
- // Starts editing PlantUML data
- graph.cellEditor.editPlantUmlData = function(cell, trigger, data)
- {
- var obj = JSON.parse(data);
-
- var dlg = new TextareaDialog(ui, mxResources.get('plantUml') + ':',
- obj.data, function(text)
- {
- if (text != null)
- {
- if (ui.spinner.spin(document.body, mxResources.get('inserting')))
- {
- ui.generatePlantUmlImage(text, obj.format, function(data, w, h)
- {
- ui.spinner.stop();
- graph.getModel().beginUpdate();
- try
- {
- if (obj.format == 'txt')
- {
- graph.labelChanged(cell, '<pre>' + data + '</pre>');
- graph.updateCellSize(cell, true);
- }
- else
- {
- graph.setCellStyles('image', ui.convertDataUri(data), [cell]);
- var geo = graph.model.getGeometry(cell);
-
- if (geo != null)
- {
- geo = geo.clone();
- geo.width = w;
- geo.height = h;
- graph.cellsResized([cell], [geo], false);
- }
- }
-
- graph.setAttributeForCell(cell, 'plantUmlData',
- JSON.stringify({data: text, format: obj.format}));
- }
- finally
- {
- graph.getModel().endUpdate();
- }
- }, function(e)
- {
- ui.handleError(e);
- });
- }
- }
- }, null, null, 400, 220);
- ui.showDialog(dlg.container, 420, 300, true, true);
- dlg.init();
- };
-
- // Starts editing Mermaid data
- graph.cellEditor.editMermaidData = function(cell, trigger, data)
- {
- var obj = JSON.parse(data);
-
- var dlg = new TextareaDialog(ui, mxResources.get('mermaid') + ':',
- obj.data, function(text)
- {
- if (text != null)
- {
- if (ui.spinner.spin(document.body, mxResources.get('inserting')))
- {
- ui.generateMermaidImage(text, obj.config, function(data, w, h)
- {
- ui.spinner.stop();
- graph.getModel().beginUpdate();
- try
- {
- graph.setCellStyles('image', data, [cell]);
- var geo = graph.model.getGeometry(cell);
-
- if (geo != null)
- {
- geo = geo.clone();
- geo.width = Math.max(geo.width, w);
- geo.height = Math.max(geo.height, h);
- graph.cellsResized([cell], [geo], false);
- }
-
- graph.setAttributeForCell(cell, 'mermaidData',
- JSON.stringify({data: text, config:
- obj.config}, null, 2));
- }
- finally
- {
- graph.getModel().endUpdate();
- }
- }, function(e)
- {
- ui.handleError(e);
- });
- }
- }
- }, null, null, 400, 220);
- ui.showDialog(dlg.container, 420, 300, true, true);
- dlg.init();
- };
-
- // Overrides function to add editing for Plant UML.
- var cellEditorStartEditing = graph.cellEditor.startEditing;
- graph.cellEditor.startEditing = function(cell, trigger)
- {
- try
- {
- var data = this.graph.getAttributeForCell(cell, 'plantUmlData');
-
- if (data != null)
- {
- this.editPlantUmlData(cell, trigger, data);
- }
- else
- {
- data = this.graph.getAttributeForCell(cell, 'mermaidData');
-
- if (data != null && window.isMermaidEnabled)
- {
- this.editMermaidData(cell, trigger, data);
- }
- else
- {
- var style = graph.getCellStyle(cell);
-
- if (mxUtils.getValue(style, 'metaEdit', '0') == '1')
- {
- ui.showDataDialog(cell);
- }
- else
- {
- cellEditorStartEditing.apply(this, arguments);
- }
- }
- }
- }
- catch (e)
- {
- ui.handleError(e);
- }
- };
- // Redirects custom link title via UI for page links
- graph.getLinkTitle = function(href)
- {
- return ui.getLinkTitle(href);
- };
-
- // Redirects custom link via UI for page link handling
- graph.customLinkClicked = function(link, associatedCell)
- {
- var done = false;
-
- try
- {
- var bounds = this.getGraphBounds();
- ui.handleCustomLink(link, associatedCell);
- done = true;
- if (ui.chromelessResize && !bounds.equals(this.getGraphBounds()))
- {
- ui.chromelessResize();
- this.scrollCellToVisible(associatedCell);
- }
- }
- catch (e)
- {
- ui.handleError(e);
- }
-
- return done;
- };
- // Parses background page references
- var graphParseBackgroundImage = graph.parseBackgroundImage;
-
- graph.parseBackgroundImage = function(json)
- {
- var result = graphParseBackgroundImage.apply(this, arguments);
- if (result != null && result.src != null && Graph.isPageLink(result.src))
- {
- result = {originalSrc: result.src};
- }
- return result;
- };
- // Updates background page SVG
- var graphSetBackgroundImage = graph.setBackgroundImage;
-
- graph.setBackgroundImage = function(img)
- {
- if (img != null && img.originalSrc != null)
- {
- img = ui.createImageForPageLink(img.originalSrc, ui.currentPage, this);
- }
- graphSetBackgroundImage.apply(this, arguments);
- };
- // Updates background to update placeholders for page title
- this.editor.addListener('pageRenamed', mxUtils.bind(this, function()
- {
- graph.refreshBackgroundImage();
- }));
- // Updates background to update placeholders for page number
- this.editor.addListener('pageMoved', mxUtils.bind(this, function()
- {
- graph.refreshBackgroundImage();
- }));
- // Updates background image after remote changes to the referenced page
- this.editor.addListener('pagesPatched', mxUtils.bind(this, function(sender, evt)
- {
- var ref = (graph.backgroundImage != null) ? graph.backgroundImage.originalSrc : null;
- if (ref != null)
- {
- var comma = ref.indexOf(',');
-
- if (comma > 0)
- {
- var id = ref.substring(comma + 1);
- var patches = evt.getProperty('patches');
-
- for (var i = 0; i < patches.length; i++)
- {
- if ((patches[i][EditorUi.DIFF_UPDATE] != null &&
- patches[i][EditorUi.DIFF_UPDATE][id] != null) ||
- (patches[i][EditorUi.DIFF_REMOVE] != null &&
- mxUtils.indexOf(patches[i][EditorUi.DIFF_REMOVE], id) >= 0))
- {
- graph.refreshBackgroundImage();
- break;
- }
- }
- }
- }
- }));
- // Restores background page reference in output data or
- // replaces dark mode page image with normal mode image
- var graphGetBackgroundImageObject = graph.getBackgroundImageObject;
-
- graph.getBackgroundImageObject = function(obj, resolveReferences)
- {
- var result = graphGetBackgroundImageObject.apply(this, arguments);
- if (result != null && result.originalSrc != null)
- {
- if (!resolveReferences)
- {
- result = {src: result.originalSrc};
- }
- else
- {
- var temp = this.stylesheet;
- var tempFg = this.shapeForegroundColor;
- var tempBg = this.shapeBackgroundColor;
- if (this.themes != null && this.defaultThemeName == 'darkTheme')
- {
- this.stylesheet = this.getDefaultStylesheet();
- this.shapeBackgroundColor = '#ffffff';
- this.shapeForegroundColor = '#000000';
- }
- result = ui.createImageForPageLink(result.originalSrc, null, null, true);
-
- this.shapeBackgroundColor = tempBg;
- this.shapeForegroundColor = tempFg;
- this.stylesheet = temp;
- }
- }
- return result;
- };
-
- // Sets help link for placeholders
- if (!this.isOffline() && typeof window.EditDataDialog !== 'undefined')
- {
- EditDataDialog.placeholderHelpLink = 'https://www.drawio.com/doc/faq/predefined-placeholders';
- }
-
- if (/viewer\.diagrams\.net$/.test(window.location.hostname) ||
- /embed\.diagrams\.net$/.test(window.location.hostname))
- {
- this.editor.editBlankUrl = 'https://app.diagrams.net/';
- }
-
- // Passes dev mode to new window
- var editorGetEditBlankUrl = ui.editor.getEditBlankUrl;
-
- this.editor.getEditBlankUrl = function(params)
- {
- params = (params != null) ? params : '';
- if (urlParams['dev'] == '1')
- {
- params += ((params.length > 0) ? '&' : '?') + 'dev=1';
- }
-
- return editorGetEditBlankUrl.apply(this, arguments);
- };
- // For chromeless mode and lightbox mode in viewer
- // Must be overridden before supercall to be applied
- // in case of chromeless initialization
- var graphAddClickHandler = graph.addClickHandler;
- graph.addClickHandler = function(highlight, beforeClick, onClick)
- {
- var tmp = beforeClick;
- beforeClick = function(evt, href, cell)
- {
- if (href == null)
- {
- var source = mxEvent.getSource(evt);
-
- if (source.nodeName.toLowerCase() == 'a')
- {
- href = source.getAttribute('href');
- }
- }
-
- if (href != null && graph.isCustomLink(href) &&
- (mxEvent.isTouchEvent(evt) ||
- !mxEvent.isPopupTrigger(evt)) &&
- graph.customLinkClicked(href, cell))
- {
- mxEvent.consume(evt);
- }
-
- if (tmp != null)
- {
- tmp(evt, href, cell);
- }
- };
-
- // For some reason, local argument override is not enough in this case...
- graphAddClickHandler.call(this, highlight, beforeClick, onClick);
- };
- editorUiInit.apply(this, arguments);
-
- if (mxClient.IS_SVG)
- {
- // LATER: Add shadow for labels in graph.container (eg. math, NO_FO), scaling
- this.editor.graph.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);
- }
- if (this.menus != null)
- {
- var menusAddPopupMenuItems = Menus.prototype.addPopupMenuItems;
- // Inserts zoomIn/zoomOut into popup menu
- this.menus.addPopupMenuItems = function(menu, cell, evt)
- {
- if (graph.isSelectionEmpty() && Editor.currentTheme == 'simple')
- {
- this.addMenuItems(menu, ['zoomIn', 'zoomOut', '-'], null, evt);
- }
- menusAddPopupMenuItems.apply(this, arguments);
- // Shows add to scratchpad option
- if (!graph.isSelectionEmpty() &&
- ui.addSelectionToScratchpad != null)
- {
- this.addMenuItems(menu, ['-', 'addToScratchpad'], null, evt);
- }
- if (graph.isSelectionEmpty() && Editor.currentTheme == 'simple')
- {
- this.addMenuItems(menu, ['-', 'exitGroup', 'home'], null, evt);
- }
- };
- var menusAddPopupMenuEditItems = Menus.prototype.addPopupMenuEditItems;
-
- // Inserts copyAsImage into popup menu
- this.menus.addPopupMenuEditItems = function(menu, cell, evt)
- {
- if (ui.editor.graph.isSelectionEmpty())
- {
- menusAddPopupMenuEditItems.apply(this, arguments);
- ui.menus.addMenuItems(menu, ['copyAsImage'], null, evt);
- }
- else
- {
- if (this.isShowCellEditItems())
- {
- this.addPopupDeleteItem(menu, cell, evt);
- }
- else
- {
- this.addPopupMenuArrangeItems(menu, cell, evt);
- }
-
- this.addMenuItems(menu, ['-', 'cut', 'copy', 'copyAsImage',
- 'duplicate', '-'], null, evt);
- if (!this.isShowCellEditItems())
- {
- this.addPopupDeleteItem(menu, cell, evt);
- }
- this.addMenuItems(menu, ['lockUnlock', '-'], null, evt);
- if (!this.isShowStyleItems())
- {
- if (graph.getSelectionCount() == 1 && !graph.isCellLocked(cell) &&
- graph.isCellEditable(cell))
- {
- this.addSubmenu('editCell', menu, null, mxResources.get('edit'));
- menu.addSeparator();
- // Shows line submenu for edges
- if (graph.getModel().isEdge(cell))
- {
- this.addSubmenu('line', menu);
- var geo = graph.getModel().getGeometry(cell);
- if (geo != null && geo.points != null && geo.points.length > 0)
- {
- this.addMenuItems(menu, ['clearWaypoints'], null, evt);
- }
- }
- }
- if (graph.getSelectionCount() == 1)
- {
- this.addMenuItems(menu, ['enterGroup'], null, evt);
- }
- // Shows table cell options
- var ss = ui.getSelectionState();
- if (ss.mergeCell != null)
- {
- var item = this.addMenuItem(menu, 'mergeCells');
- }
- else if (ss.style['colspan'] > 1 || ss.style['rowspan'] > 1)
- {
- var item = this.addMenuItem(menu, 'unmergeCells');
- }
- }
- }
- };
- this.menus.isShowStyleItems = function()
- {
- return Editor.currentTheme != 'simple' &&
- Editor.currentTheme != 'sketch' &&
- Editor.currentTheme != 'min';
- };
- this.menus.isShowHistoryItems = function()
- {
- return Editor.currentTheme != 'simple';
- };
-
- this.menus.isShowArrangeItems = this.menus.isShowStyleItems;
- this.menus.isShowCellEditItems = this.menus.isShowStyleItems;
- }
- // Specifies the default filename
- this.defaultFilename = mxResources.get('untitledDiagram');
- // Adds export for %page%, %pagenumber% and %pagecount% placeholders
- var graphGetExportVariables = graph.getExportVariables;
-
- graph.getExportVariables = function()
- {
- var vars = graphGetExportVariables.apply(this, arguments);
- var file = ui.getCurrentFile();
-
- if (file != null)
- {
- vars['filename'] = file.getTitle();
- }
-
- vars['pagecount'] = (ui.pages != null) ? ui.pages.length : 1;
- vars['page'] = (ui.currentPage != null) ? ui.currentPage.getName() : '';
- vars['pagenumber'] = (ui.pages != null && ui.currentPage != null) ?
- mxUtils.indexOf(ui.pages, ui.currentPage) + 1 : 1;
-
- return vars;
- };
- // Adds %page%, %pagenumber% and %pagecount% placeholders
- var graphGetGlobalVariable = graph.getGlobalVariable;
-
- graph.getGlobalVariable = function(name)
- {
- var file = ui.getCurrentFile();
-
- if (name == 'filename' && file != null)
- {
- return file.getTitle();
- }
- else if (name == 'page' && ui.currentPage != null)
- {
- return ui.currentPage.getName();
- }
- else if (name == 'pagenumber')
- {
- if (ui.currentPage != null && ui.pages != null)
- {
- return mxUtils.indexOf(ui.pages, ui.currentPage) + 1;
- }
- else
- {
- return 1;
- }
- }
- else if (name == 'pagecount')
- {
- return (ui.pages != null) ? ui.pages.length : 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
- // Forces update of filename placeholder
- var lastFilename = null;
- var lastFile = null;
- this.addListener('fileDescriptorChanged', mxUtils.bind(this, function()
- {
- var file = this.getCurrentFile();
- var filename = (file != null && file.getTitle() != null) ?
- file.getTitle() : this.defaultFilename;
-
- if (lastFilename != filename && file == lastFile)
- {
- graph.invalidateDescendantsWithPlaceholders(
- graph.model.getRoot());
- graph.view.validate();
- }
- lastFilename = filename;
- lastFile = file;
- }));
- var graphLabelLinkClicked = graph.labelLinkClicked;
-
- graph.labelLinkClicked = function(state, elt, evt)
- {
- var href = elt.getAttribute('href');
-
- if (href != null && graph.isCustomLink(href) &&
- (mxEvent.isTouchEvent(evt) ||
- !mxEvent.isPopupTrigger(evt)))
- {
- // Active links are moved to the hint
- if (!graph.isEnabled() || (state != null && graph.isCellLocked(state.cell)))
- {
- graph.customLinkClicked(href);
-
- // Resets rubberband after click on locked cell
- graph.getRubberband().reset();
- }
-
- mxEvent.consume(evt);
- }
- else
- {
- graphLabelLinkClicked.apply(this, arguments);
- }
- };
- // Overrides editor filename
- this.editor.getOrCreateFilename = function()
- {
- var filename = ui.defaultFilename;
- var file = ui.getCurrentFile();
-
- if (file != null)
- {
- filename = (file.getTitle() != null) ? file.getTitle() : filename;
- }
-
- return filename;
- };
- // Disables print action for standalone apps on iOS
- // because there is no way to close the new window
- // LATER: Use iframe for print, disable preview
- var printAction = this.actions.get('print');
- printAction.setEnabled(!mxClient.IS_IOS || !navigator.standalone);
- printAction.visible = printAction.isEnabled();
-
- // Installs additional keyboard shortcuts for editor
- if (!this.editor.chromeless || this.editor.editable)
- {
- // Defines additional hotkeys
- this.keyHandler.bindAction(70, true, 'findReplace'); // Ctrl+F
- this.keyHandler.bindAction(77, true, 'editGeometry', true); // Ctrl+Shift+M
- this.keyHandler.bindAction(75, true, 'tags'); // Ctrl+K
- this.keyHandler.bindAction(65, false, 'insertText'); // A
- this.keyHandler.bindAction(83, false, 'insertNote'); // S
- this.keyHandler.bindAction(68, false, 'insertRectangle'); // D
- this.keyHandler.bindAction(70, false, 'insertEllipse'); // F
- this.keyHandler.bindAction(76, false, 'insertLink'); // L
- this.keyHandler.bindAction(82, false, 'insertRhombus'); // R
- this.keyHandler.bindAction(67, false, 'insertEdge'); // C
- this.keyHandler.bindAction(88, false, 'insertFreehand'); // X
- this.keyHandler.bindAction(75, true, 'toggleShapes', true); // Ctrl+Shift+K
- this.keyHandler.bindAction(54, true, 'convertDarkModeColors', true); // Ctrl+Shift+6
- this.altShiftActions[81] = 'copyStyle'; // Alt+Shift+Q
- this.altShiftActions[87] = 'pasteStyle'; // Alt+Shift+W
- this.altShiftActions[83] = 'synchronize'; // Alt+Shift+S
-
- if (urlParams['embedInline'] == '1')
- {
- graph.addListener(mxEvent.ESCAPE, function(sender, evt)
- {
- if (evt != null && graph.isEnabled() && !graph.isEditing() &&
- evt.getProperty('event') != null)
- {
- ui.actions.get('exit').funct();
- }
- });
- }
- this.installImagePasteHandler();
- this.installNativeClipboardHandler();
- };
- // Updates realtime state icon
- this.addListener('realtimeStateChanged', mxUtils.bind(this, function()
- {
- this.updateUserElement();
- }));
- // Creates the spinner
- this.spinner = this.createSpinner(null, null, 24);
-
- // Installs drag and drop handler for rich text editor
- if (Graph.fileSupport)
- {
- graph.addListener(mxEvent.EDITING_STARTED, mxUtils.bind(this, function(evt)
- {
- // Setup the dnd listeners
- var textElt = graph.cellEditor.text2;
- var dropElt = null;
-
- if (textElt != null)
- {
- mxEvent.addListener(textElt, 'dragleave', function(evt)
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- });
-
- mxEvent.addListener(textElt, 'dragover', mxUtils.bind(this, function(evt)
- {
- // IE 10 does not implement pointer-events so it can't have a drop highlight
- if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10))
- {
- dropElt = this.highlightElement(textElt);
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- }));
-
- mxEvent.addListener(textElt, 'drop', mxUtils.bind(this, function(evt)
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
- if (evt.dataTransfer.files.length > 0)
- {
- this.importFiles(evt.dataTransfer.files, 0, 0, this.maxImageSize, function(data, mimeType, x, y, w, h)
- {
- // Inserts image into current text box
- graph.insertImage(data, w, h);
- }, function()
- {
- // No post processing
- }, function(file)
- {
- // Handles only images
- return file.type.substring(0, 6) == 'image/';
- }, function(queue)
- {
- // Invokes elements of queue in order
- for (var i = 0; i < queue.length; i++)
- {
- queue[i]();
- }
- }, mxEvent.isControlDown(evt));
- }
- else if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0)
- {
- var uri = evt.dataTransfer.getData('text/uri-list');
-
- if ((/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(uri))
- {
- this.loadImage(decodeURIComponent(uri), mxUtils.bind(this, function(img)
- {
- var w = Math.max(1, img.width);
- var h = Math.max(1, img.height);
- var maxSize = this.maxImageSize;
- var s = Math.min(1, Math.min(maxSize / Math.max(1, w)), maxSize / Math.max(1, h));
- graph.insertImage(decodeURIComponent(uri), w * s, h * s);
- }));
- }
- else
- {
- document.execCommand('insertHTML', false, evt.dataTransfer.getData('text/plain'));
- }
- }
- else
- {
- if (mxUtils.indexOf(evt.dataTransfer.types, 'text/html') >= 0)
- {
- document.execCommand('insertHTML', false, evt.dataTransfer.getData('text/html'));
- }
- else if (mxUtils.indexOf(evt.dataTransfer.types, 'text/plain') >= 0)
- {
- document.execCommand('insertHTML', false, evt.dataTransfer.getData('text/plain'));
- }
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- }));
- }
- }));
- }
-
- // Adding mxRuler to editor
- if (Editor.isSettingsEnabled())
- {
- var view = this.editor.graph.view;
- var unit = mxSettings.getUnit();
- view.setUnit(unit);
- // Updates page size unit (using mm instead of m)
- Editor.pageSizeUnit = (unit == mxConstants.METERS) ?
- mxConstants.MILLIMETERS : unit;
-
- view.addListener('unitChanged', function(sender, evt)
- {
- var unit = evt.getProperty('unit');
- mxSettings.setUnit(unit);
- mxSettings.save();
-
- // Updates page size unit (using mm instead of m)
- Editor.pageSizeUnit = (unit == mxConstants.METERS) ?
- mxConstants.MILLIMETERS : unit;
- });
- var showRuler = this.canvasSupported && document.documentMode != 9 &&
- (urlParams['ruler'] == '1' || mxSettings.isRulerOn()) &&
- (!this.editor.isChromelessView() || this.editor.editable);
-
- this.ruler = (showRuler) ? new mxDualRuler(this, view.unit) : null;
- this.refresh();
- }
-
- // Adds an element to edit the style in the footer in test mode
- if (urlParams['styledev'] == '1')
- {
- var footer = document.getElementById('geFooter');
- if (footer != null)
- {
- this.styleInput = document.createElement('input');
- this.styleInput.setAttribute('type', 'text');
- this.styleInput.style.position = 'absolute';
- this.styleInput.style.top = '14px';
- this.styleInput.style.left = '2px';
- // Workaround for ignore right CSS property in FF
- this.styleInput.style.width = '98%';
- this.styleInput.style.visibility = 'hidden';
- this.styleInput.style.opacity = '0.9';
- mxEvent.addListener(this.styleInput, 'change', mxUtils.bind(this, function()
- {
- this.editor.graph.getModel().setStyle(this.editor.graph.getSelectionCell(), this.styleInput.value);
- }));
- footer.appendChild(this.styleInput);
- this.editor.graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function(sender, evt)
- {
- if (this.editor.graph.getSelectionCount() > 0)
- {
- var cell = this.editor.graph.getSelectionCell();
- var style = this.editor.graph.getModel().getStyle(cell);
- this.styleInput.value = style || '';
- this.styleInput.style.visibility = 'visible';
- }
- else
- {
- this.styleInput.style.visibility = 'hidden';
- }
- }));
- }
- var isSelectionAllowed = this.isSelectionAllowed;
- this.isSelectionAllowed = function(evt)
- {
- if (mxEvent.getSource(evt) == this.styleInput)
- {
- return true;
- }
- return isSelectionAllowed.apply(this, arguments);
- };
- }
- // Removes info text in page
- var info = document.getElementById('geInfo');
- if (info != null)
- {
- info.parentNode.removeChild(info);
- }
- // Installs drag and drop handler for files
- // Enables dropping files
- if (Graph.fileSupport && (!this.editor.chromeless || this.editor.editable))
- {
- // Setup the dnd listeners
- var dropElt = null;
- mxEvent.addListener(graph.container, 'dragleave', function(evt)
- {
- if (graph.isEnabled())
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- }
- });
-
- mxEvent.addListener(graph.container, 'dragover', mxUtils.bind(this, function(evt)
- {
- // IE 10 does not implement pointer-events so it can't have a drop highlight
- if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10))
- {
- dropElt = this.highlightElement(graph.container);
- }
-
- if (this.sidebar != null)
- {
- this.sidebar.hideTooltip();
- }
- evt.stopPropagation();
- evt.preventDefault();
- }));
-
- mxEvent.addListener(graph.container, 'drop', mxUtils.bind(this, function(evt)
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
-
- if (graph.isEnabled())
- {
- var pt = mxUtils.convertPoint(graph.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt));
- var files = evt.dataTransfer.files;
- var tr = graph.view.translate;
- var scale = graph.view.scale;
- var x = pt.x / scale - tr.x;
- var y = pt.y / scale - tr.y;
-
- if (files.length > 0)
- {
- if (urlParams['embed'] != '1' && mxEvent.isShiftDown(evt))
- {
- // Closes current file if blank and no undoable changes
- if (this.isBlankFile() && !this.canUndo() &&
- this.getCurrentFile() != null)
- {
- this.fileLoaded(null);
- }
- this.openFiles(files, true);
- }
- else
- {
- if (mxEvent.isAltDown(evt))
- {
- x = null;
- y = null;
- }
-
- this.importFiles(files, x, y, this.maxImageSize, null, null, null,
- null, mxEvent.isControlDown(evt), null, null,
- mxEvent.isShiftDown(evt), evt);
- }
- }
- else
- {
- if (mxEvent.isAltDown(evt))
- {
- x = 0;
- y = 0;
- }
-
- var uri = (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) ?
- evt.dataTransfer.getData('text/uri-list') : null;
- var data = this.extractGraphModelFromEvent(evt, this.pages != null);
-
- if (data != null)
- {
- graph.setSelectionCells(this.importXml(data, x, y, true));
- }
- else if (mxUtils.indexOf(evt.dataTransfer.types, 'text/html') >= 0)
- {
- var html = evt.dataTransfer.getData('text/html');
- var div = document.createElement('div');
- div.innerHTML = Graph.sanitizeHtml(html);
-
- // The default is based on the extension
- var asImage = null;
-
- // Extracts single image
- var imgs = div.getElementsByTagName('img');
- if (imgs != null && imgs.length == 1)
- {
- html = imgs[0].getAttribute('src');
-
- if (html == null)
- {
- html = imgs[0].getAttribute('srcset');
- }
-
- // Handles special case where the src attribute has no valid extension
- // in which case the text would be inserted as text with a link
- if (!(/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(html))
- {
- asImage = true;
- }
- }
- else
- {
- // Extracts single link
- var a = div.getElementsByTagName('a');
- if (a != null && a.length == 1)
- {
- html = a[0].getAttribute('href');
- }
- else
- {
- // Extracts preformatted text
- var pre = div.getElementsByTagName('pre');
-
- if (pre != null && pre.length == 1)
- {
- html = mxUtils.getTextContent(pre[0]);
- }
- }
- }
-
- var resizeImages = true;
-
- var doInsert = mxUtils.bind(this, function()
- {
- graph.setSelectionCells(this.insertTextAt(html, x, y, true,
- asImage, null, resizeImages, mxEvent.isControlDown(evt)));
- });
-
- if (asImage && html != null && html.length > this.resampleThreshold)
- {
- this.confirmImageResize(function(doResize)
- {
- resizeImages = doResize;
- doInsert();
- }, mxEvent.isControlDown(evt));
- }
- else
- {
- doInsert();
- }
- }
- else if (uri != null && (/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(uri))
- {
- this.loadImage(decodeURIComponent(uri), mxUtils.bind(this, function(img)
- {
- var w = Math.max(1, img.width);
- var h = Math.max(1, img.height);
- var maxSize = this.maxImageSize;
- var s = Math.min(1, Math.min(maxSize / Math.max(1, w)), maxSize / Math.max(1, h));
- graph.setSelectionCell(graph.insertVertex(null, null, '', x, y, w * s, h * s,
- 'shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;' +
- 'verticalAlign=top;aspect=fixed;imageAspect=0;image=' + uri + ';'));
- }), mxUtils.bind(this, function(img)
- {
- graph.setSelectionCells(this.insertTextAt(uri, x, y, true));
- }));
- }
- else if (mxUtils.indexOf(evt.dataTransfer.types, 'text/plain') >= 0)
- {
- graph.setSelectionCells(this.insertTextAt(evt.dataTransfer.getData('text/plain'), x, y, true));
- }
- }
- }
- evt.stopPropagation();
- evt.preventDefault();
- }), false);
- }
- this.initPages();
- // Embedded mode
- if (urlParams['embed'] == '1')
- {
- this.initializeEmbedMode();
- }
- var themeChangeListener = mxUtils.bind(this, function()
- {
- graph.refresh();
- graph.view.validateBackground();
- this.updateDocumentTitle();
- this.updateTabContainer();
- this.hideShapePicker();
- });
- this.addListener('darkModeChanged', themeChangeListener);
- this.addListener('currentThemeChanged', mxUtils.bind(this, function()
- {
- if (this.sidebar != null)
- {
- this.sidebar.updateEntries();
- }
- this.updateButtonContainer();
- this.updateDocumentTitle();
- this.refresh();
- }));
-
- graph.addListener('enabledChanged', mxUtils.bind(this, function()
- {
- if (!graph.isEnabled())
- {
- this.hideShapePicker();
- }
- }));
- // Overrides mxWindow.fit to allow for embedViewport
- var ui = this;
- mxWindow.prototype.fit = function()
- {
- if (!Editor.inlineFullscreen && ui.embedViewport != null)
- {
- var left = parseInt(this.div.offsetLeft);
- var width = parseInt(this.div.offsetWidth);
- var right = ui.embedViewport.x + ui.embedViewport.width;
- var top = parseInt(this.div.offsetTop);
- var height = parseInt(this.div.offsetHeight);
- var bottom = ui.embedViewport.y + ui.embedViewport.height;
- this.div.style.left = Math.max(ui.embedViewport.x, Math.min(left, right - width)) + 'px';
- this.div.style.top = Math.max(ui.embedViewport.y, Math.min(top, bottom - height)) + 'px';
- this.div.style.height = Math.min(ui.embedViewport.height, parseInt(this.div.style.height)) + 'px';
- this.div.style.width = Math.min(ui.embedViewport.width, parseInt(this.div.style.width)) + 'px';
- }
- else
- {
- mxUtils.fit(this.div);
- }
- };
- if (!this.editor.chromeless || this.editor.editable)
- {
- // Activates scheme in UI
- if (Editor.currentTheme == 'simple' ||
- Editor.currentTheme == 'sketch')
- {
- var theme = Editor.currentTheme;
- Editor.currentTheme = '';
- this.doSetCurrentTheme(theme, 0, mxUtils.bind(this, function()
- {
- if (urlParams['embedInline'] == '1')
- {
- // Inline embed mode must be initialized after setting current theme
- this.initializeInlineEmbedMode();
- }
- else
- {
- // Initial state of format panel
- var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
-
- if (iw < Editor.smallScreenWidth)
- {
- this.toggleFormatPanel(false);
- }
- }
- this.fireEvent(new mxEventObject('themeInitialized'));
- }));
- }
- }
- if (Editor.currentTheme != 'atlas')
- {
- if (!mxClient.IS_IE && !mxClient.IS_IE11 && urlParams['dark'] != '0' &&
- Editor.currentTheme != 'atlas' && (urlParams['embed'] != '1' ||
- urlParams['dark'] == '1' || urlParams['dark'] == 'auto' ||
- Editor.currentTheme == 'dark' || urlParams['atlas'] == '1'))
- {
- var darkMode = false;
- if (this.isAutoDarkModeSupported() && this.isAutoDarkMode())
- {
- darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
- }
- else if (urlParams['dark'] == null && Editor.isSettingsEnabled() &&
- mxSettings.settings.darkMode === true)
- {
- darkMode = true;
- }
-
- if (darkMode || urlParams['dark'] == '1' ||
- Editor.currentTheme == 'dark')
- {
- if (urlParams['contrast'] != null)
- {
- Editor.enableCssDarkMode = urlParams['contrast'] != '0';
- }
-
- this.setDarkMode(true);
- }
- }
- if (this.isAutoDarkModeSupported())
- {
- try
- {
- // Automatically updates theme when system setting changes
- window.matchMedia('(prefers-color-scheme: dark)')
- .addEventListener('change', mxUtils.bind(this, function (e)
- {
- if (this.isAutoDarkMode())
- {
- this.setDarkMode(e.matches);
- }
- }));
- }
- catch (e)
- {
- // Ignores object doesn't support addEventListener and disables auto dark mode
- this.actions.get('autoMode').setEnabled(false);
- }
- }
- else if (Editor.isSettingsEnabled() && mxSettings.settings.darkMode === true)
- {
- darkMode = true;
- }
- }
- if (urlParams['high-contrast'] == '1')
- {
- this.setHighContrast(true);
- }
- else if (Editor.isSettingsEnabled() && !this.editor.graph.isLightboxView() &&
- mxSettings.settings.highContrast != null)
- {
- this.setHighContrast(mxSettings.settings.highContrast);
- }
-
- this.installSettings();
- // Animations
- this.addListener('enableAnimationsChanged', mxUtils.bind(this, function(sender, evt)
- {
- graph.enableFlowAnimation = Editor.enableAnimations;
- graph.refresh();
- }));
- graph.enableFlowAnimation = Editor.enableAnimations;
-
- if (screen.width <= Editor.smallScreenWidth)
- {
- this.formatWidth = 0;
- }
-
- if (urlParams['prefetchFonts'] == '1')
- {
- ui.editor.loadFonts();
- }
- };
- /**
- * Adapts the UI elements when the window size changes.
- */
- var editorUiWindowResized = EditorUi.prototype.windowResized;
- EditorUi.prototype.windowResized = function()
- {
- if (Editor.currentTheme == 'simple')
- {
- var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
- var limit = Editor.smallScreenWidth;
- if (this.lastWindowWidth != null && this.lastWindowWidth >= limit && iw < limit)
- {
- if (this.isFormatPanelVisible())
- {
- this.toggleFormatPanel(false);
- }
- }
- else if (this.lastWindowWidth != null && this.lastWindowWidth < limit && iw >= limit)
- {
- if (!this.isFormatPanelVisible())
- {
- this.toggleFormatPanel(true);
- }
- }
- this.lastWindowWidth = iw;
- }
- editorUiWindowResized.apply(this, arguments);
- };
- /**
- * Initializes embed inline mode.
- */
- EditorUi.prototype.initializeInlineEmbedMode = function()
- {
- var footer = this.sketchFooterMenuElt;
- var toolbar = this.sketchMainMenuElt;
- var picker = this.sketchPickerMenuElt;
- var graph = this.editor.graph;
- picker.style.transform = '';
- mxEvent.addGestureListeners(this.diagramContainer.parentNode, mxUtils.bind(this, function(evt)
- {
- if (mxEvent.getSource(evt) == this.diagramContainer.parentNode)
- {
- this.embedExitPoint = new mxPoint(
- mxEvent.getClientX(evt),
- mxEvent.getClientY(evt));
- this.sendEmbeddedSvgExport();
- }
- }));
- document.body.style.cursor = 'text';
- var div = document.createElement('div');
- div.style.position = 'absolute';
- div.style.width = '10px';
- div.style.height = '10px';
- div.style.borderRadius = '5px';
- div.style.border = '1px solid gray';
- div.style.background = '#ffffff';
- div.style.cursor = 'row-resize';
- this.diagramContainer.parentNode.appendChild(div);
- this.bottomResizer = div;
- var x0 = null;
- var y0 = null;
- var w0 = null;
- var h0 = null;
- mxEvent.addGestureListeners(div, mxUtils.bind(this, function(evt)
- {
- h0 = parseInt(this.diagramContainer.style.height);
- y0 = mxEvent.getClientY(evt);
- graph.popupMenuHandler.hideMenu();
- mxEvent.consume(evt);
- }));
- div = div.cloneNode(false);
- div.style.cursor = 'col-resize';
- this.diagramContainer.parentNode.appendChild(div);
- this.rightResizer = div;
- mxEvent.addGestureListeners(div, mxUtils.bind(this, function(evt)
- {
- w0 = parseInt(this.diagramContainer.style.width);
- x0 = mxEvent.getClientX(evt);
- graph.popupMenuHandler.hideMenu();
- mxEvent.consume(evt);
- }));
- mxEvent.addGestureListeners(document.body, null, mxUtils.bind(this, function(evt)
- {
- var changed = false;
- if (x0 != null)
- {
- this.diagramContainer.style.width = Math.max(20,
- w0 + mxEvent.getClientX(evt) - x0) + 'px';
- changed = true;
- }
- if (y0 != null)
- {
- this.diagramContainer.style.height = Math.max(20,
- h0 + mxEvent.getClientY(evt) - y0) + 'px';
- changed = true;
- }
- if (changed)
- {
- var parent = window.opener || window.parent;
- parent.postMessage(JSON.stringify({
- event: 'resize',
- fullscreen: Editor.inlineFullscreen,
- rect: this.diagramContainer.getBoundingClientRect()
- }), '*');
- this.inlineSizeChanged();
- this.refresh();
- }
- }), function(evt)
- {
- if (x0 != null || y0 != null)
- {
- mxEvent.consume(evt);
- }
- x0 = null;
- y0 = null;
- });
- document.body.style.backgroundColor = 'transparent';
- this.diagramContainer.style.borderRadius = '4px';
- this.bottomResizer.style.visibility = 'hidden';
- this.rightResizer.style.visibility = 'hidden';
- this.sketchMenubarElt.style.display = 'none';
- toolbar.style.visibility = 'hidden';
- footer.style.visibility = 'hidden';
- picker.style.display = 'none';
-
- this.addListener('editInlineStart', mxUtils.bind(this, function(evt)
- {
- this.inlineSizeChanged();
- this.fitWindows();
- }));
- this.addListener('darkModeChanged', mxUtils.bind(this, function(evt)
- {
- if (!Editor.enableCssDarkMode)
- {
- this.inlineSizeChanged();
- }
- }));
- this.addListener('editInlineStop', mxUtils.bind(this, function(evt)
- {
- this.diagramContainer.style.width = '10px';
- this.diagramContainer.style.height = '10px';
- this.diagramContainer.style.border = '';
- this.bottomResizer.style.visibility = 'hidden';
- this.rightResizer.style.visibility = 'hidden';
- toolbar.style.visibility = 'hidden';
- footer.style.visibility = 'hidden';
- picker.style.display = 'none';
- }));
- // Overridden to avoid reset of scrollbars
- this.windowResized = mxUtils.bind(this, function()
- {
- // do nothing
- });
- this.inlineSizeChanged();
- };
-
- /**
- * Installs handler for pasting image from clipboard.
- */
- EditorUi.prototype.installImagePasteHandler = function()
- {
- if (!mxClient.IS_IE)
- {
- var graph = this.editor.graph;
-
- graph.container.addEventListener('paste', mxUtils.bind(this, function(evt)
- {
- if (!mxEvent.isConsumed(evt))
- {
- try
- {
- var data = (evt.clipboardData || evt.originalEvent.clipboardData);
- var containsText = false;
-
- // Workaround for asynchronous paste event processing in textInput
- // is to ignore this event if it contains text/html/rtf (see below).
- // NOTE: Image is not pasted into textInput so can't listen there.
- for (var i = 0; i < data.types.length; i++)
- {
- if (data.types[i].substring(0, 5) === 'text/')
- {
- containsText = true;
- break;
- }
- }
-
- if (!containsText)
- {
- var items = data.items;
-
- for (index in items)
- {
- var item = items[index];
-
- if (item.kind === 'file')
- {
- if (graph.isEditing())
- {
- this.importFiles([item.getAsFile()], 0, 0, this.maxImageSize, function(data, mimeType, x, y, w, h)
- {
- // Inserts image into current text box
- graph.insertImage(data, w, h);
- }, function()
- {
- // No post processing
- }, function(file)
- {
- // Handles only images
- return file.type.substring(0, 6) == 'image/';
- }, function(queue)
- {
- // Invokes elements of queue in order
- for (var i = 0; i < queue.length; i++)
- {
- queue[i]();
- }
- });
- }
- else
- {
- var pt = this.editor.graph.getInsertPoint();
- this.importFiles([item.getAsFile()], pt.x, pt.y, this.maxImageSize);
- mxEvent.consume(evt);
- }
-
- break;
- }
- }
- }
- }
- catch (e)
- {
- // ignore
- }
- }
- }), false);
- }
- };
-
- /**
- * Installs the native clipboard support.
- */
- EditorUi.prototype.installNativeClipboardHandler = function()
- {
- var graph = this.editor.graph;
- // Focused but invisible textarea during control or meta key events
- // LATER: Disable text rendering to avoid delay while keeping focus
- var textInput = document.createElement('div');
- textInput.setAttribute('autocomplete', 'off');
- textInput.setAttribute('autocorrect', 'off');
- textInput.setAttribute('autocapitalize', 'off');
- textInput.setAttribute('spellcheck', 'false');
- textInput.style.textRendering = 'optimizeSpeed';
- textInput.style.fontFamily = 'monospace';
- textInput.style.wordBreak = 'break-all';
- textInput.style.background = 'transparent';
- textInput.style.color = 'transparent';
- textInput.style.position = 'absolute';
- textInput.style.whiteSpace = 'nowrap';
- textInput.style.overflow = 'hidden';
- textInput.style.display = 'block';
- textInput.style.fontSize = '1';
- textInput.style.zIndex = '-1';
- textInput.style.resize = 'none';
- textInput.style.outline = 'none';
- textInput.style.width = '1px';
- textInput.style.height = '1px';
- mxUtils.setOpacity(textInput, 0);
- textInput.contentEditable = true;
- textInput.innerHTML = ' ';
- var restoreFocus = false;
-
- // Disables built-in cut, copy and paste shortcuts
- this.keyHandler.bindControlKey(88, null);
- this.keyHandler.bindControlKey(67, null);
- this.keyHandler.bindControlKey(86, null);
- // Shows a textare when control/cmd is pressed to handle native clipboard actions
- mxEvent.addListener(document, 'keydown', mxUtils.bind(this, function(evt)
- {
- // No dialog visible
- var source = mxEvent.getSource(evt);
-
- if (graph.container != null && graph.isEnabled() && !graph.isMouseDown && !graph.isEditing() &&
- this.dialog == null && source.nodeName != 'INPUT' && source.nodeName != 'TEXTAREA' &&
- source.contentEditable != 'true')
- {
- if (evt.keyCode == 224 /* FF */ || (!mxClient.IS_MAC && evt.keyCode == 17 /* Control */) ||
- (mxClient.IS_MAC && (evt.keyCode == 91 || evt.keyCode == 93) /* Left/Right Meta */))
- {
- // Cannot use parentNode for check in IE
- if (!restoreFocus)
- {
- // Avoid autoscroll but allow handling of all pass-through ctrl shortcuts
- textInput.style.left = (graph.container.scrollLeft + 10) + 'px';
- textInput.style.top = (graph.container.scrollTop + 10) + 'px';
- var x0 = graph.container.scrollLeft;
- var y0 = graph.container.scrollTop;
- graph.container.appendChild(textInput);
- restoreFocus = true;
-
- textInput.focus();
- document.execCommand('selectAll', false, null);
- // Workaround for Safari 16 scroll after CMD key press
- graph.container.scrollLeft = x0;
- graph.container.scrollTop = y0;
- }
- }
- }
- }));
- // Clears input and restores focus and selection
- function clearInput()
- {
- window.setTimeout(function()
- {
- textInput.innerHTML = ' ';
- textInput.focus();
- document.execCommand('selectAll', false, null);
- }, 0);
- };
-
- mxEvent.addListener(document, 'keyup', mxUtils.bind(this, function(evt)
- {
- // Workaround for asynchronous event read invalid in IE quirks mode
- var keyCode = evt.keyCode;
-
- // Asynchronous workaround for scroll to origin after paste if the
- // Ctrl-key is not pressed for long enough in FF on Windows
- window.setTimeout(mxUtils.bind(this, function()
- {
- if (restoreFocus && (keyCode == 224 /* FF */ || keyCode == 17 /* Control */ ||
- keyCode == 91 /* MetaLeft */ || keyCode == 93 /* MetaRight */))
- {
- restoreFocus = false;
-
- if (!graph.isEditing() && this.dialog == null && graph.container != null)
- {
- graph.container.focus();
- }
-
- textInput.parentNode.removeChild(textInput);
-
- // Workaround for lost cursor in focused element
- if (this.dialog == null)
- {
- mxUtils.clearSelection();
- }
- }
- }), 0);
- }));
-
- mxEvent.addListener(textInput, 'copy', mxUtils.bind(this, function(evt)
- {
- if (graph.isEnabled())
- {
- try
- {
- mxClipboard.copy(graph);
- this.copyCells(textInput);
- clearInput();
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }));
-
- mxEvent.addListener(textInput, 'cut', mxUtils.bind(this, function(evt)
- {
- if (graph.isEnabled())
- {
- try
- {
- mxClipboard.copy(graph);
- this.copyCells(textInput, true);
- clearInput();
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }));
-
- mxEvent.addListener(textInput, 'paste', mxUtils.bind(this, function(evt)
- {
- if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
- {
- try
- {
- textInput.innerHTML = ' ';
- textInput.focus();
-
- if (evt.clipboardData != null)
- {
- Graph.removePasteFormatting(textInput.firstChild);
- this.pasteCells(evt, textInput, true, true);
- }
- if (!mxEvent.isConsumed(evt))
- {
- var x0 = graph.container.scrollLeft;
- var y0 = graph.container.scrollTop;
- window.setTimeout(mxUtils.bind(this, function()
- {
- try
- {
- // Workaround for Safari 16 scroll after paste
- graph.container.scrollLeft = x0;
- graph.container.scrollTop = y0;
- Graph.removePasteFormatting(textInput.firstChild);
- this.pasteCells(evt, textInput, false, true);
- }
- catch (e)
- {
- this.handleError(e);
- }
- }), 0);
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }), true);
-
- // Needed for IE11
- var isSelectionAllowed2 = this.isSelectionAllowed;
- this.isSelectionAllowed = function(evt)
- {
- if (mxEvent.getSource(evt) == textInput)
- {
- return true;
- }
- return isSelectionAllowed2.apply(this, arguments);
- };
- };
- /**
- * Sets the current UI theme. Possible values are null, "kennedy" and "sketch".
- */
- EditorUi.prototype.setCurrentTheme = function(value, noRestart)
- {
- mxSettings.setUi(value);
- noRestart = this.doSetCurrentTheme(value) || noRestart;
- if (!noRestart)
- {
- this.alert(mxResources.get('restartForChangeRequired'));
- }
- };
- /**
- * Changes the current UI theme.
- */
- EditorUi.prototype.isDefaultTheme = function(theme)
- {
- return theme == '' || theme == 'dark' || theme == 'default' ||
- theme == 'kennedy' || theme == null;
- };
-
- /**
- * Changes the current UI theme.
- */
- EditorUi.prototype.doSetCurrentTheme = function(value, delay, post)
- {
- delay = (delay != null) ? delay : 150;
- function isSimple(theme)
- {
- return theme == 'simple' || (delay == 0 &&
- theme == 'sketch');
- };
- // From kennedy to simple or sketch or vice versa
- var curr = Editor.currentTheme;
- var transition = (isSimple(curr) && this.isDefaultTheme(value)) ||
- (this.isDefaultTheme(curr) && isSimple(value));
- var noRestart = transition && (value != 'sketch' && curr != 'sketch');
- if (transition && !this.themeSwitching)
- {
- Editor.currentTheme = value;
- this.themeSwitching = true;
- mxUtils.setPrefixedStyle(this.container.style, 'transition',
- 'all ' + delay + 'ms ease-in-out');
- if (delay == 0)
- {
- this.container.style.opacity = '0';
- }
- window.setTimeout(mxUtils.bind(this, function()
- {
- var scrollState = this.saveScrollState();
- this.editor.graph.stopEditing(false);
- this.container.style.opacity = '0';
- window.setTimeout(mxUtils.bind(this, function()
- {
- if (isSimple(curr) && this.isDefaultTheme(value))
- {
- this.menubarContainer.style.display = '';
- this.toolbarContainer.style.display = 'block';
- this.tabContainer.style.display = 'flex';
- this.hsplit.style.display = 'block';
- this.menubarHeight = App.prototype.menubarHeight;
- }
- else if (this.isDefaultTheme(curr) && isSimple(value))
- {
- this.menubarContainer.style.display = 'none';
- this.toolbarContainer.style.display = 'none';
- this.menubarHeight = 0;
- if (value != 'simple')
- {
- this.tabContainer.style.display = 'none';
- this.hsplit.style.display = 'none';
- this.hsplitPosition = 0;
- }
- else
- {
- this.tabContainer.style.display = 'flex';
- }
- }
- this.switchTheme(value);
- window.setTimeout(mxUtils.bind(this, function()
- {
- this.fireEvent(new mxEventObject('currentThemeChanged'));
- this.editor.fireEvent(new mxEventObject('statusChanged'));
- this.editor.graph.refresh();
- this.restoreScrollState(scrollState);
- this.container.style.opacity = '';
- window.setTimeout(mxUtils.bind(this, function()
- {
- mxUtils.setPrefixedStyle(this.container.style, 'transition', null);
- delete this.themeSwitching;
- if (isLocalStorage && isSimple(value))
- {
- this.setTabContainerVisible((mxSettings.settings.pages != null) ?
- mxSettings.settings.pages : true);
- }
- if (post != null)
- {
- post();
- }
- }), delay);
- }), delay);
- }), delay);
- }), 0);
- }
- return noRestart;
- };
- /**
- * Saves scroll position
- */
- EditorUi.prototype.saveScrollState = function()
- {
- var t = this.editor.graph.view.translate;
- var x = this.diagramContainer.scrollLeft;
- var y = this.diagramContainer.scrollTop;
- if (this.embedViewport != null)
- {
- if (!Editor.inlineFullscreen)
- {
- x += this.embedViewport.x;
- y += this.embedViewport.y;
- }
- else
- {
- x -= this.embedViewport.x;
- y -= this.embedViewport.y;
- }
- }
- return {x: x, y: y, tx: t.x, ty: t.y};
- };
-
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.restoreScrollState = function(state)
- {
- var s = this.editor.graph.view.scale;
- var t = this.editor.graph.view.translate;
-
- this.diagramContainer.scrollLeft = state.x + (t.x - state.tx) * s;
- this.diagramContainer.scrollTop = state.y + (t.y - state.ty) * s;
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.installStatusMinimizer = function(parent)
- {
- parent = (parent != null) ? parent : this.statusContainer.parentNode;
- var visible = false;
-
- mxEvent.addListener(parent, 'mouseenter', mxUtils.bind(this, function()
- {
- if (Editor.currentTheme == 'sketch' && this.editor.getStatus() != '')
- {
- this.statusContainer.style.display = 'inline-flex';
- }
- }));
-
- mxEvent.addListener(parent, 'mouseleave', mxUtils.bind(this, function()
- {
- if (Editor.currentTheme == 'sketch' && !visible)
- {
- this.statusContainer.style.display = 'none';
- }
- }));
- var statusChanged = mxUtils.bind(this, function()
- {
- if (Editor.currentTheme == 'sketch')
- {
- var elt = (this.statusContainer.firstChild != null &&
- typeof this.statusContainer.firstChild.getAttribute === 'function') ?
- this.statusContainer.firstChild : null;
- visible = elt != null && elt.getAttribute('class') != null;
-
- if (!visible && elt != null)
- {
- var title = elt.getAttribute('title');
- var file = this.getCurrentFile();
- var key = (file != null) ? file.savingStatusKey :
- DrawioFile.prototype.savingStatusKey;
-
- // Shows animated spinner while saving
- if (title == mxResources.get(key) + '...')
- {
- this.statusContainer.innerHTML = '<div><img title="' + mxUtils.htmlEntities(
- mxResources.get(key)) + '...' + '"src="' + Editor.tailSpin + '"></div>';
- visible = true;
- }
- }
- // Checks size of container without status
- this.statusContainer.style.display = 'none';
- var empty = parent.clientWidth <= 32;
- // Hides container if empty and no status
- parent.style.visibility = (empty && this.editor.getStatus() == '') ?
- 'hidden' : '';
- // Shows status if container empty or status relevant
- if (empty || visible)
- {
- this.statusContainer.style.display = 'inline-flex';
- visible = true;
- }
- }
- else if (Editor.currentTheme == 'simple')
- {
- // Required for flex layout gaps to be applied correctly
- this.statusContainer.style.display = 'inline-flex';
- this.statusContainer.style.display = (this.statusContainer.clientWidth == 0)
- ? 'none' : 'inline-flex';
- }
- else
- {
- this.statusContainer.style.display = 'inline-flex';
- }
- });
-
- this.editor.addListener('statusChanged', statusChanged);
- statusChanged();
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.switchTheme = function(value)
- {
- // Removes containers before destroying windows
- if (this.isDefaultTheme(value))
- {
- // Format window
- if (this.formatContainer != null)
- {
- this.formatContainer.style.left = '';
- this.formatContainer.style.zIndex = '1';
- this.formatContainer.style.border = '';
- if (this.footerContainer != null)
- {
- if (this.footerContainer.parentNode !=
- this.formatContainer.parentNode)
- {
- this.footerContainer.parentNode.insertBefore(
- this.formatContainer, this.footerContainer);
- }
- }
- // Shapes window
- if (this.sidebarContainer != null)
- {
- if (this.formatContainer.parentNode !=
- this.sidebarContainer.parentNode)
- {
- this.formatContainer.parentNode.insertBefore(
- this.sidebarContainer, this.formatContainer);
- }
- }
- }
- }
- this.destroyWindows();
- this.updateUserElement();
- this.updateDefaultStyles();
- this.switchThemeConstants(value);
- this.switchCssForTheme(value);
- this.createWrapperForTheme(value);
- this.createMainMenuForTheme(value);
- this.createFooterMenuForTheme(value);
- this.createPickerMenuForTheme(value);
- this.createMenubarForTheme(value);
- // TODO: Check what hides sidebarContainer
- this.sidebarContainer.style.display = '';
- if (value == 'sketch')
- {
- // Format window
- this.createFormatWindow();
- this.formatContainer.style.left = '0px';
- this.formatContainer.style.top = '0px';
- this.formatContainer.style.width = '';
- this.formatContainer.style.zIndex = '';
- this.formatContainer.style.border = 'none';
- // Shapes window
- var libs = Editor.enableCustomLibraries && (urlParams['embed'] != '1' ||
- urlParams['libraries'] == '1');
-
- this.createShapesWindow();
- this.sidebarContainer.className = '';
- this.sidebarContainer.style.position = 'absolute';
- this.sidebarContainer.style.left = '0px';
- this.sidebarContainer.style.top = '0px';
- this.sidebarContainer.style.bottom = (libs) ? '32px' : '0px';
- this.sidebarContainer.style.width = '100%';
- }
- // Format panel close button
- if (this.format != null)
- {
- var closeButton = this.isDefaultTheme(value) || value == 'atlas';
- if (this.format.showCloseButton != closeButton)
- {
- this.format.showCloseButton = closeButton;
- this.format.refresh();
- }
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.getWindows = function()
- {
- var wnd = [this.sidebarWindow, this.formatWindow, this.freehandWindow];
- if (this.actions != null)
- {
- wnd = wnd.concat([this.actions.outlineWindow, this.actions.layersWindow]);
- }
- if (this.menus != null)
- {
- wnd = wnd.concat([this.menus.tagsWindow, this.menus.findWindow,
- this.menus.findReplaceWindow, this.menus.commentsWindow]);
- }
- return wnd;
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.fitWindows = function()
- {
- var wnd = this.getWindows();
- for (var i = 0; i < wnd.length; i++)
- {
- if (wnd[i] != null)
- {
- wnd[i].window.fit();
- }
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.hideWindows = function()
- {
- var wnd = this.getWindows();
- for (var i = 0; i < wnd.length; i++)
- {
- if (wnd[i] != null)
- {
- wnd[i].window.setVisible(false);
- }
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.destroyWindows = function()
- {
- if (this.sidebarWindow != null)
- {
- this.sidebarWindow.destroy();
- this.sidebarWindow = null;
- }
-
- if (this.formatWindow != null)
- {
- this.formatWindow.destroy();
- this.formatWindow = null;
- }
- if (this.freehandWindow != null)
- {
- this.freehandWindow.destroy();
- this.freehandWindow = null;
- }
- if (this.actions.outlineWindow != null)
- {
- this.actions.outlineWindow.destroy();
- this.actions.outlineWindow = null;
- }
- if (this.actions.layersWindow != null)
- {
- this.actions.layersWindow.destroy();
- this.actions.layersWindow = null;
- }
- if (this.menus != null)
- {
- if (this.menus.chatWindow != null)
- {
- this.menus.chatWindow.destroy();
- this.menus.chatWindow = null;
- }
- if (this.menus.tagsWindow != null)
- {
- this.menus.tagsWindow.destroy();
- this.menus.tagsWindow = null;
- }
- if (this.menus.findWindow != null)
- {
- this.menus.findWindow.destroy();
- this.menus.findWindow = null;
- }
- if (this.menus.findReplaceWindow != null)
- {
- this.menus.findReplaceWindow.destroy();
- this.menus.findReplaceWindow = null;
- }
- if (this.menus.commentsWindow != null)
- {
- this.menus.commentsWindow.destroy();
- this.menus.commentsWindow = null;
- }
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.switchThemeConstants = function(value)
- {
- var graph = this.editor.graph;
- graph.defaultEdgeLength = Graph.prototype.defaultEdgeLength;
- graph.defaultGridEnabled = Graph.prototype.defaultGridEnabled;
- graph.defaultPageVisible = Graph.prototype.defaultPageVisible;
- if (this.menus != null)
- {
- this.menus.autoPopup = value != 'simple' && value != 'sketch';
- }
- if (value == 'simple' || value == 'sketch')
- {
- Editor.fitWindowBorders = new mxRectangle(60, 30, 30, 30);
- if (Editor.config == null || Editor.config.defaultEdgeLength == null)
- {
- graph.defaultEdgeLength = 120;
- }
- if (urlParams['grid'] == null)
- {
- graph.defaultGridEnabled = false;
- }
- if (urlParams['pv'] == null)
- {
- graph.defaultPageVisible = false;
- }
- }
- else
- {
- Editor.fitWindowBorders = null;
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.switchCssForTheme = function(value)
- {
- if (this.sketchStyleElt != null)
- {
- this.sketchStyleElt.parentNode.removeChild(this.sketchStyleElt);
- this.sketchStyleElt = null;
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createWrapperForTheme = function(value)
- {
- if (this.sketchWrapperElt != null && this.sketchWrapperElt.parentNode != null)
- {
- this.tabContainer.parentNode.insertBefore(this.diagramContainer, this.tabContainer);
- this.sketchWrapperElt.parentNode.removeChild(this.sketchWrapperElt);
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createMainMenuForTheme = function(value)
- {
- // hook for subclassers
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.isPageMenuVisible = function()
- {
- return this.pages != null && (urlParams['pages'] != '0' ||
- this.pages.length > 1 || Editor.pagesVisible);
- };
-
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createFooterMenuForTheme = function(value)
- {
- // hook for subclassers
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createPickerMenuForTheme = function(value)
- {
- // hook for subclassers
- };
- /**
- *
- */
- EditorUi.prototype.getNetworkStatus = function()
- {
- var status = null;
- if (this.isOffline(true))
- {
- status = mxResources.get('offline');
- }
- else
- {
- var file = this.getCurrentFile();
- if (file != null)
- {
- if (file.invalidChecksum)
- {
- status = mxResources.get('error') + ': ' +
- mxResources.get('checksum');
- }
- else if (file.sync != null && (!file.sync.enabled ||
- !file.sync.isRealtimeActive()) && this.getServiceName() != 'atlassian') // Atlassian app has pulling sync (no RT)
- {
- status = mxResources.get('realtimeCollaboration') +
- ': ' + mxResources.get('disabled');
- }
- else if (file.sync != null && !file.sync.isConnected())
- {
- status = mxResources.get('notConnected');
- }
- else if (file.isRealtimeEnabled() &&
- file.isRealtimeSupported() &&
- file.getRealtimeState() > 1)
- {
- var err = file.getRealtimeError();
- status = mxResources.get('realtimeCollaboration') + ': ' +
- ((err != null && err.message != null) ?
- err.message : mxResources.get('error'));
- }
- }
- }
- return status;
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createMenubarForTheme = function(value)
- {
- if (this.statusContainer != null)
- {
- this.statusContainer.style.flexGrow = '';
- this.statusContainer.style.flexShrink = '';
- this.statusContainer.style.width = '';
- this.statusContainer.style.marginTop = '';
- this.statusContainer.style.justifyContent = '';
- this.statusContainer.style.opacity = '';
- this.menubar.container.appendChild(this.statusContainer);
- }
- if (this.userElement != null)
- {
- this.menubarContainer.appendChild(this.userElement);
- }
- var elt = this.menubar.langIcon;
- if (elt != null)
- {
- elt.style.position = 'absolute';
- elt.style.height = '18px';
- elt.style.width = '18px';
- elt.style.flexShrink = '';
- elt.style.opacity = '';
- this.menubarContainer.parentNode.insertBefore(elt,
- this.menubarContainer);
- }
- if (this.buttonContainer != null)
- {
- this.buttonContainer.style.display = '';
- this.buttonContainer.style.padding = '';
- this.menubar.container.appendChild(this.buttonContainer);
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createMenu = function(key, img, className, clickFn)
- {
- className = (className != null) ? className : 'geToolbarButton';
- var menu = this.menus.get(key);
- var elt = this.menubar.addMenu(mxResources.get(key), menu.funct, null, clickFn);
-
- elt.className = className;
- elt.style.display = 'inline-block';
- elt.style.cursor = 'pointer';
- elt.style.height = '24px';
- elt.setAttribute('title', mxResources.get(key));
- this.menus.menuCreated(menu, elt, className);
-
- if (img != null)
- {
- elt.style.backgroundImage = 'url(' + img + ')';
- elt.style.backgroundPosition = 'center center';
- elt.style.backgroundRepeat = 'no-repeat';
- elt.style.backgroundSize = '100% 100%';
- elt.style.width = '24px';
- elt.innerText = '';
- }
- return elt;
- };
- /**
- * Create toolbar button.
- */
- EditorUi.prototype.createToolbarButton = function(img, title, fn, size)
- {
- size = (size != null) ? size : 24;
- var btn = document.createElement('a');
- btn.className = 'geToolbarButton geAdaptiveAsset';
- btn.setAttribute('title', title);
- btn.style.backgroundImage = 'url(' + img + ')';
- btn.style.backgroundPosition = 'center center';
- btn.style.backgroundRepeat = 'no-repeat';
- btn.style.backgroundSize = '100% 100%';
- btn.style.display = 'inline-block';
- btn.style.cursor = 'pointer';
- btn.style.marginLeft = '6px';
- btn.style.width = size + 'px';
- btn.style.height = size + 'px';
- btn.style.flexShrink = '0';
- btn.style.flexGrow = '0';
- if (fn != null)
- {
- // Prevents focus
- mxEvent.addListener(btn, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
- mxUtils.bind(this, function(evt)
- {
- evt.preventDefault();
- }));
-
- mxEvent.addListener(btn, 'click', function(evt)
- {
- if (btn.getAttribute('disabled') != 'disabled')
- {
- fn(evt);
- }
-
- mxEvent.consume(evt);
- });
- }
- return btn;
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createMenuItem = function(key, img, ignoreState)
- {
- var action = this.actions.get(key);
- var fn = (action != null) ? action.funct : null;
- var btn = this.createToolbarButton(img, mxResources.get(key) +
- ((action != null && action.shortcut != null) ? ' (' +
- action.shortcut + ')' : ''), fn);
- if (action != null)
- {
- if (!ignoreState)
- {
- function updateState()
- {
- if (action.isEnabled())
- {
- btn.removeAttribute('disabled');
- btn.style.cursor = 'pointer';
- }
- else
- {
- btn.setAttribute('disabled', 'disabled');
- btn.style.cursor = 'default';
- }
- btn.style.opacity = (action.isEnabled()) ? '' : '0.2';
- };
-
- this.editor.graph.addListener('enabledChanged', updateState);
- action.addListener('stateChanged', updateState);
- updateState();
- }
- }
-
- return btn;
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createFormatWindow = function()
- {
- if (this.formatWindow == null)
- {
- var x = Math.max(10, this.diagramContainer.parentNode.clientWidth - 256);
- var y = 60;
- var h = (urlParams['embedInline'] == '1') ? 580 :
- ((urlParams['sketch'] == '1') ? 580 : Math.min(566,
- this.editor.graph.container.clientHeight - 10));
-
- this.formatWindow = new WrapperWindow(this, mxResources.get('format'), x, y, 240, h,
- mxUtils.bind(this, function(container)
- {
- container.appendChild(this.formatContainer);
- }));
- this.formatWindow.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function()
- {
- this.formatWindow.window.fit();
- }));
- var toggleMinimized = this.formatWindow.window.toggleMinimized;
- var w = 240;
-
- this.formatWindow.window.toggleMinimized = function()
- {
- toggleMinimized.apply(this, arguments);
-
- if (this.minimized)
- {
- w = parseInt(this.div.style.width);
- this.div.style.width = '140px';
- this.table.style.width = '140px';
- this.div.style.left = (parseInt(this.div.style.left) + w - 140) + 'px';
- }
- else
- {
- this.div.style.width = w + 'px';
- this.table.style.width = this.div.style.width;
- this.div.style.left = (Math.max(0, parseInt(this.div.style.left) - w + 140)) + 'px';
- }
-
- this.fit();
- };
- mxEvent.addListener(this.formatWindow.window.title, 'dblclick', mxUtils.bind(this, function(evt)
- {
- if (mxEvent.getSource(evt) == this.formatWindow.window.title)
- {
- this.formatWindow.window.toggleMinimized();
- }
- }));
-
- this.formatWindow.window.minimumSize = new mxRectangle(0, 0, 240, 80);
- // Sets initial state for format window
- if (Editor.currentTheme == 'sketch')
- {
- var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
- var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
-
- if (iw < 1200 || ih < 708)
- {
- this.formatWindow.window.toggleMinimized();
- }
- }
- else
- {
- this.formatWindow.window.setVisible(false);
- }
- }
- };
- /**
- *
- */
- var editorUiToggleFormatPanel = EditorUi.prototype.toggleFormatPanel;
- EditorUi.prototype.toggleFormatPanel = function(visible)
- {
- var wnd = this.formatWindow;
-
- if (wnd != null)
- {
- wnd.window.setVisible((visible != null) ? visible :
- !this.isFormatPanelVisible());
- }
- else
- {
- editorUiToggleFormatPanel.apply(this, arguments);
- }
- };
- /**
- *
- */
- EditorUi.prototype.toggleShapesPanel = function(visible)
- {
- if (this.isShapesPanelVisible() != visible)
- {
- var size = EditorUi.prototype.hsplitPosition;
- // On smaller screens this is set to 0
- if (size == 0)
- {
- size = this.defaultSidebarWidth;
- }
- var x = this.hsplitPosition;
- var doRefresh = mxUtils.bind(this, function()
- {
- this.hsplitPosition = tmp;
- this.refresh();
- this.diagramContainer.scrollLeft -= x - this.hsplitPosition;
- });
-
- var tmp = (visible) ? size : 0;
- var delay = 0.3;
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transform', (tmp == 0) ? 'translateX(0)' : 'translateX(-100%)');
- if (tmp != 0)
- {
- doRefresh();
- }
- window.setTimeout(mxUtils.bind(this, function()
- {
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transform', (tmp == 0) ? 'translateX(-100%)' : 'translateX(0)');
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transition', 'transform ' + delay + 's ease-in-out');
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transform-origin', 'top left');
- window.setTimeout(mxUtils.bind(this, function()
- {
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transition', null);
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transform', null);
- mxUtils.setPrefixedStyle(this.sidebarContainer.style, 'transform-origin', null);
-
- if (tmp == 0)
- {
- doRefresh();
- }
- }), delay * 1000);
- }), 10);
- }
- };
- /**
- *
- */
- EditorUi.prototype.isShapesPanelVisible = function()
- {
- return this.hsplitPosition > 0;
- };
- /**
- *
- */
- var editorUiIsFormatPanelVisible = EditorUi.prototype.isFormatPanelVisible;
- EditorUi.prototype.isFormatPanelVisible = function()
- {
- var wnd = this.formatWindow;
-
- if (wnd != null)
- {
- return wnd.window.isVisible();
- }
- else
- {
- return editorUiIsFormatPanelVisible.apply(this, arguments);
- }
- };
-
- var editorUiRefresh = EditorUi.prototype.refresh;
-
- /**
- * Changes refresh to only update the diagram container in sketch mode.
- */
- EditorUi.prototype.refresh = function(sizeDidChange)
- {
- if (this.sketchWrapperElt != null && this.sketchWrapperElt.parentNode != null)
- {
- sizeDidChange = (sizeDidChange != null) ? sizeDidChange : true;
- if (urlParams['embedInline'] != '1')
- {
- var w = this.container.clientWidth;
- var h = this.container.clientHeight;
- var off = this.zeroOffset;
- var x = off.x;
- var y = off.y;
- if (this.container == document.body)
- {
- w = document.body.clientWidth || document.documentElement.clientWidth;
- h = document.documentElement.clientHeight;
- }
-
- off = this.getDiagramContainerOffset();
- x = off.x;
- y = off.y;
- if (Editor.currentTheme == 'simple')
- {
- y += this.sketchMainMenuElt.offsetHeight;
- }
-
- this.diagramContainer.style.top = y + 'px';
- this.diagramContainer.style.bottom = '0';
- if (Editor.currentTheme == 'simple')
- {
- this.hsplit.style.top = this.sketchMainMenuElt.offsetHeight + 'px';
- this.sidebarContainer.style.top = this.hsplit.style.top;
- this.formatContainer.style.top = this.hsplit.style.top;
-
- var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
- var effHsplitPosition = Math.max(0, Math.min(iw - this.formatWidth,
- Math.min(this.hsplitPosition, w - this.splitSize - 40)));
- this.sidebarContainer.style.width = effHsplitPosition + 'px';
- this.diagramContainer.style.left = (effHsplitPosition + x) + 'px';
- this.tabContainer.style.left = effHsplitPosition + 'px';
- this.tabContainer.style.right = this.formatWidth + 'px';
- this.sketchMainMenuElt.style.left = '0px';
- this.sketchMainMenuElt.style.right = '0px';
- this.hsplit.style.left = effHsplitPosition + 'px';
- this.diagramContainer.style.right = this.formatWidth + 'px';
- this.formatContainer.style.width = this.formatWidth + 'px';
- this.hsplit.style.bottom = this.tabContainer.offsetHeight + 'px';
- this.diagramContainer.style.bottom = this.hsplit.style.bottom;
- this.checkTabScrollerOverflow();
- }
- else
- {
- this.diagramContainer.style.left = off.x + 'px';
- this.diagramContainer.style.right = '0';
- }
- }
- if (sizeDidChange)
- {
- this.editor.graph.sizeDidChange();
- }
- }
- else
- {
- editorUiRefresh.apply(this, arguments);
- }
- };
- /**
- *
- */
- EditorUi.prototype.createShapesPanel = function(container)
- {
- var css = 'position:absolute;border-width:1px;cusor:pointer;border-style:none;' +
- 'height:24px;bottom:0px;text-align:center;padding:8px 6px 0 6px;border-top-style:solid;' +
- 'width:50%;height:32px;box-sizing:border-box;font-size:11px;';
- var addMenu = mxUtils.bind(this, function(id)
- {
- var elt = this.createMenu(id, null, 'geTitle');
- elt.style.cssText = css;
- container.appendChild(elt);
- return elt;
- });
-
- if (Editor.enableCustomLibraries && (urlParams['embed'] != '1' || urlParams['libraries'] == '1'))
- {
- // Defined in native apps together with openLibrary
- if (this.actions.get('newLibrary') != null)
- {
- var div = document.createElement('div');
- div.style.cssText = css;
- div.className = 'geTitle';
- mxUtils.write(div, mxResources.get('newLibrary'));
- container.appendChild(div);
-
- mxEvent.addListener(div, 'click', this.actions.get('newLibrary').funct);
-
- var div = div.cloneNode(false);
- div.style.left = '50%';
- div.style.borderLeftStyle = 'solid';
- mxUtils.write(div, mxResources.get('openLibrary'));
- container.appendChild(div);
-
- mxEvent.addListener(div, 'click', this.actions.get('openLibrary').funct);
- }
- else
- {
- var elt = addMenu('newLibrary');
- elt.style.fontSize = '11px';
- elt.style.left = '0';
-
- var elt = addMenu('openLibraryFrom');
- elt.style.borderLeftStyle = 'solid';
- elt.style.fontSize = '11px';
- elt.style.left = '50%';
- }
- }
- container.appendChild(this.sidebarContainer);
- container.style.overflow = 'hidden';
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.createShapesWindow = function()
- {
- if (this.sidebarWindow == null)
- {
- var w = Math.min(this.diagramContainer.parentNode.clientWidth - 10, 218);
- var h = (urlParams['embedInline'] == '1') ? 650 :
- Math.min(this.diagramContainer.parentNode.clientHeight, 650);
- var simpleTheme = Editor.currentTheme == 'simple' ||
- Editor.currentTheme == 'sketch';
-
- this.sidebarWindow = new WrapperWindow(this, mxResources.get('shapes'),
- (simpleTheme && urlParams['embedInline'] != '1') ? 66 : 10,
- (simpleTheme && urlParams['embedInline'] != '1') ?
- Math.max(30, (this.diagramContainer.parentNode.clientHeight - h) / 2) : 56,
- w - 6, h - 6, mxUtils.bind(this, function(container)
- {
- this.createShapesPanel(container);
- }));
-
- this.sidebarWindow.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function()
- {
- this.sidebarWindow.window.fit();
- }));
- this.sidebarWindow.window.minimumSize = new mxRectangle(0, 0, 90, 90);
- this.sidebarWindow.window.setVisible(false);
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.setSketchMode = function(value)
- {
- if (this.spinner.spin(document.body, mxResources.get('working') + '...'))
- {
- window.setTimeout(mxUtils.bind(this, function()
- {
- this.spinner.stop();
- this.doSetSketchMode(value);
-
- // Persist setting
- if (urlParams['rough'] == null)
- {
- mxSettings.settings.sketchMode = value;
- mxSettings.save();
- }
-
- this.fireEvent(new mxEventObject('sketchModeChanged'));
- }), 0);
- }
- };
- /**
- * Dynamic change of dark mode.
- */
- Editor.createMinimalCss = function()
- {
- // Dark mode styles
- return (Editor.isDarkMode() ?
- 'html body .geMenubarContainer .geMenuItem .geMenuItem, html body .geMenubarContainer a.geMenuItem { color: #353535; }' +
- 'html body .geToolbarContainer .geMenuItem, html body .geToolbarContainer .geToolbarButton, ' +
- 'html body .geMenubarContainer .geMenuItem .geMenuItem, html body .geMenubarContainer a.geMenuItem,' +
- 'html body .geMenubarContainer .geToolbarButton { filter: invert(1); }' +
- 'html > body.geEditor > div > a.geItem { background-color: ' + Editor.darkColor + '; color: #cccccc; border-color: #505759; }' +
- 'html body .mxCellEditor { color: #f0f0f0; }'
- :
- // Non-dark mode styles
- 'div.diagramContainer button.gePrimaryBtn, .mxWindow button.gePrimaryBtn, .geDialog button.gePrimaryBtn, html body .gePrimaryBtn ' +
- '{ background: #29b6f2 !important; color: #fff !important; border: none !important; box-shadow: none !important; }' +
- 'html body .gePrimaryBtn:hover:not([disabled]) { background: #12a2e0 !important; }'
- ) +
- // End of custom styles
- 'html body .geStatus { overflow: hidden; text-overflow: ellipsis; }' +
- 'html body .geStatus > *:not([class]) { vertical-align:top; }' +
- 'html > body > div > a.geItem { background-color: #ffffff; color: #707070; border-top: 1px solid lightgray; border-left: 1px solid lightgray; }' +
- 'html body .mxWindow { z-index: 3; font-size: 12px; }' +
- 'html body table.mxWindow { font-size: 12px; }' +
- 'html body button.geBtn:active { opacity: 0.6; }' +
- 'html body a.geMenuItem { opacity: 0.75; cursor: pointer; user-select: none; }' +
- 'html body a.geMenuItem[disabled] { opacity: 0.2; }' +
- 'html body a.geMenuItem[disabled]:active { opacity: 0.2; }' +
- 'html body a.geMenuItem:active { opacity: 0.2; }' +
- 'html body .geToolbarButton:active { opacity: 0.15; }' +
- 'html body .geStatus:active { opacity: 0.5; }' +
- '.geStatus > div { box-sizing: border-box; max-width: 100%; text-overflow: ellipsis; }' +
- 'html table.mxPopupMenu tr.mxPopupMenuItemHover:active { opacity: 0.7; }' +
- 'html body .mxWindow input[type="checkbox"] {padding: 0px; }' +
- '.mxWindow button, .geDialog select, .mxWindow select { display:inline-block; }' +
- 'html body .mxWindow .geColorBtn, html body .geDialog .geColorBtn { background: none; }' +
- 'html body div.diagramContainer button:active, html body .mxWindow button:active, html body .geDialog button:active { opacity: 0.6; }' +
- '.geBtn button { min-width:72px !important; }' +
- 'div.geToolbarContainer a.geButton { margin:0px; padding: 0 2px 4px 2px; } ' +
- 'html body div.geToolbarContainer a.geColorBtn { margin: 2px; } ' +
- 'table.mxWindow td.mxWindowPane button.geColorBtn { padding:0px; box-sizing: border-box; }' +
- 'html body .geMenuItem { font-size:14px; text-decoration: none; font-weight: normal; padding: 6px 10px 6px 10px; border: none; border-radius: 5px; color: #353535; box-shadow: inset 0 0 0 1px rgba(0,0,0,.11), inset 0 -1px 0 0 rgba(0,0,0,.08), 0 1px 2px 0 rgba(0,0,0,.04); ' + (EditorUi.isElectronApp? 'app-region: no-drag; ' : '') + '}' +
- 'div.mxWindow td.mxWindowPane button { background-image: none; float: none; }' +
- 'html div.geVerticalHandle { position:absolute;bottom:0px;left:50%;cursor:row-resize;width:11px;height:11px;background:white;margin-bottom:-6px; margin-left:-6px; border: none; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.11), inset 0 -1px 0 0 rgba(0,0,0,.08), 0 1px 2px 0 rgba(0,0,0,.04); }' +
- 'html div.mxRubberband { border:1px solid; border-color: #29b6f2 !important; background:rgba(41,182,242,0.4) !important; } ' +
- 'html body div.mxPopupMenu { border-radius:5px; border:1px solid #c0c0c0; padding:5px 0 5px 0; box-shadow: 0px 4px 17px -4px rgba(96,96,96,1); } ' +
- 'html table.mxPopupMenu td.mxPopupMenuItem { color: ' + (Editor.isDarkMode() ? '#cccccc' : '#353535') + '; font-size: 14px; padding-top: 4px; padding-bottom: 4px; }' +
- 'html table.mxPopupMenu tr.mxPopupMenuItemHover { background-color: ' + (Editor.isDarkMode() ? '#000000' : '#29b6f2') + '; }' +
- 'html tr.mxPopupMenuItemHover td.mxPopupMenuItem, html tr.mxPopupMenuItemHover td.mxPopupMenuItem span { color: ' + (Editor.isDarkMode() ? '#cccccc' : '#ffffff') + ' !important; }' +
- 'html tr.mxPopupMenuItem, html td.mxPopupMenuItem { transition-property: none !important; }' +
- 'html body td.mxWindowTitle { padding-right: 14px; }';
- };
- /**
- * Sets dark mode and persists the setting.
- */
- EditorUi.prototype.setAndPersistDarkMode = function(value)
- {
- var actual = value;
- if (value == 'auto')
- {
- actual = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
- }
- this.setDarkMode(actual);
- mxSettings.settings.darkMode = value;
- mxSettings.save();
-
- var theme = mxSettings.getUi();
- if (urlParams['ui'] == null && value != 'auto' && theme != 'atlas' &&
- theme != 'min' && theme != 'sketch' && theme != 'simple')
- {
- this.setCurrentTheme((!value) ? 'kennedy' : 'dark', true);
- }
- };
- /**
- * Sets dark mode and persists the setting.
- */
- EditorUi.prototype.setAndPersistLanguage = function(value)
- {
- mxSettings.setLanguage(value);
- mxSettings.save();
-
- // Shows dialog in new language
- mxClient.language = value;
- mxResources.loadDefaultBundle = false;
- mxResources.add(RESOURCE_BASE);
- };
- /**
- * Sets dark mode and persists the setting.
- */
- EditorUi.prototype.setAndPersistHighContrast = function(value)
- {
- this.setHighContrast(value);
- if (Editor.isSettingsEnabled())
- {
- mxSettings.settings.highContrast = value;
- mxSettings.save();
- }
- };
- /**
- * Sets dark mode and persists the setting.
- */
- EditorUi.prototype.isHighContrast = function()
- {
- var highContrastStylesheet = document.getElementById('high-contrast-stylesheet');
- if (highContrastStylesheet != null && window.matchMedia)
- {
- var media = highContrastStylesheet.getAttribute('media');
- return media == 'all' || (media != 'not all' &&
- window.matchMedia('(forced-colors: active)').matches);
- }
- else
- {
- return false;
- }
- };
- /**
- * Sets dark mode and persists the setting.
- */
- EditorUi.prototype.setHighContrast = function(value)
- {
- var highContrastStylesheet = document.getElementById('high-contrast-stylesheet');
- if (highContrastStylesheet != null && window.matchMedia)
- {
- var match = window.matchMedia('(forced-colors: active)').matches;
- var media = highContrastStylesheet.getAttribute('media');
- var on = media == 'all' || (media != 'not all' && match);
-
- if (on && !value)
- {
- highContrastStylesheet.setAttribute('media', (match) ?
- 'not all' : '(forced-colors: active)');
- }
- else if (!on && value)
- {
- highContrastStylesheet.setAttribute('media', (match) ?
- '(forced-colors: active)' : 'all');
- }
- }
- };
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.isRulerVisible = function()
- {
- return this.ruler != null;
- };
-
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.setRulerVisible = function(visible)
- {
- var before = this.getDiagramContainerOffset();
- mxSettings.setRulerOn(visible);
- mxSettings.save();
-
- if (!visible && this.ruler != null)
- {
- this.ruler.destroy();
- this.ruler = null;
- }
- else if (visible && this.ruler == null)
- {
- this.ruler = new mxDualRuler(this, this.editor.graph.view.unit);
- }
- this.refresh();
- this.fireEvent(new mxEventObject('rulerVisibleChanged'));
- var after = this.getDiagramContainerOffset();
- this.diagramContainer.scrollLeft += after.x - before.x;
- this.diagramContainer.scrollTop += after.x - before.x;
- };
-
- /**
- * Returns true if automatic dark mode is supported.
- */
- EditorUi.prototype.isAutoDarkModeSupported = function()
- {
- return window.matchMedia != null;
- };
-
- /**
- * Returns the current state of the dark mode.
- */
- EditorUi.prototype.isAutoDarkMode = function(ignoreUrl)
- {
- return (!ignoreUrl && urlParams['dark'] == 'auto') ||
- (Editor.isSettingsEnabled() && (mxSettings.settings.darkMode == 'auto' ||
- (this.getServiceName() == 'draw.io' && urlParams['embed'] != '1' &&
- (!this.editor.chromeless || this.editor.editable) &&
- mxSettings.settings.darkMode == null)));
- };
-
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.setCssDarkModeEnabled = function(value)
- {
- var prev = Editor.isDarkMode() || Editor.cssDarkMode;
- if (prev)
- {
- this.setDarkMode(false);
- }
- Editor.enableCssDarkMode = value;
- mxSettings.settings.enableCssDarkMode = Editor.enableCssDarkMode;
- mxSettings.save();
- this.setDarkMode(prev);
- this.fireEvent(new mxEventObject('cssDarkModeEnabledChanged'));
- };
-
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.setDarkMode = function(value)
- {
- this.doSetDarkMode(value, mxUtils.bind(this, function()
- {
- this.fireEvent(new mxEventObject('darkModeChanged'));
- }), mxUtils.bind(this, function(e)
- {
- if (window.console != null)
- {
- console.error(e);
- }
- this.editor.setStatus(e.message);
- }));
- };
-
- /**
- * Creates dark mode style node.
- */
- EditorUi.prototype.createDarkStyle = function()
- {
- var darkStyle = document.createElement('link');
- darkStyle.setAttribute('rel', 'stylesheet');
- darkStyle.setAttribute('href', STYLE_PATH + '/dark.css');
- darkStyle.setAttribute('charset', 'UTF-8');
- darkStyle.setAttribute('type', 'text/css');
- return darkStyle;
- };
-
- // Sets instance graph stylesheet
- EditorUi.setGraphDarkMode = function(graph, container, darkMode)
- {
- graph.view.defaultGridColor = darkMode ?
- mxGraphView.prototype.defaultDarkGridColor : mxGraphView.prototype.defaultGridColor;
- graph.view.gridColor = graph.view.defaultGridColor;
- graph.defaultPageBackgroundColor = (urlParams['embedInline'] == '1') ? 'transparent' :
- darkMode ? Editor.darkColor : '#ffffff';
- graph.defaultPageBorderColor = darkMode ? '#000000' : '#ffffff';
- graph.shapeBackgroundColor = darkMode ? Editor.darkColor : '#ffffff';
- graph.shapeForegroundColor = darkMode ? Editor.lightColor : '#000000';
- graph.defaultThemeName = darkMode ? 'darkTheme' : 'default-style2';
- graph.graphHandler.previewColor = darkMode ? '#cccccc' : 'black';
- mxGraphHandler.prototype.previewColor = graph.graphHandler.previewColor;
-
- if (container != null)
- {
- container.style.backgroundColor = (urlParams['embedInline'] == '1') ? 'transparent' :
- (darkMode ? Editor.darkColor : '#ffffff');
- }
- graph.loadStylesheet();
- // Sets global vars
- Graph.prototype.defaultPageBackgroundColor = graph.defaultPageBackgroundColor;
- Graph.prototype.defaultPageBorderColor = graph.defaultPageBorderColor;
- Graph.prototype.shapeBackgroundColor = graph.shapeBackgroundColor;
- Graph.prototype.shapeForegroundColor = graph.shapeForegroundColor;
- Graph.prototype.defaultThemeName = graph.defaultThemeName;
- };
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.setCssDarkMode = function(value)
- {
- var node = (mxUtils.isAncestorNode(document.body, this.container)) ?
- this.container : this.editor.graph.container;
- if (node != null)
- {
- if (value)
- {
- node.classList.add('geDarkMode');
- }
- else
- {
- node.classList.remove('geDarkMode');
- }
- }
- };
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.doSetDarkMode = function(value, success, error)
- {
- if (Editor.enableCssDarkMode)
- {
- this.setCssDarkMode(value);
- Editor.cssDarkMode = value;
- success();
- }
- else
- {
- var delayed = mxUtils.bind(this, function()
- {
- if (Editor.darkMode != value)
- {
- var graph = this.editor.graph;
- Editor.darkMode = value;
- // Sets instance vars and graph stylesheet
- this.spinner.opts.color = Editor.isDarkMode() ? '#c0c0c0' : '#000';
- EditorUi.setGraphDarkMode(graph, document.body, Editor.isDarkMode());
-
- // Destroys windows with code for dark mode
- if (this.actions.layersWindow != null)
- {
- var wasVisible = this.actions.layersWindow.window.isVisible();
-
- this.actions.layersWindow.window.setVisible(false);
- this.actions.layersWindow.destroy();
- this.actions.layersWindow = null;
- if (wasVisible)
- {
- window.setTimeout(this.actions.get('layers').funct, 0);
- }
- }
- if (this.menus != null && this.menus.commentsWindow != null)
- {
- this.menus.commentsWindow.window.setVisible(false);
- this.menus.commentsWindow.destroy();
- this.menus.commentsWindow = null;
- }
-
- if (this.ruler != null)
- {
- this.ruler.updateStyle();
- }
- if (window.StyleFormatPanel != null)
- {
- StyleFormatPanel.prototype.defaultStrokeColor = Editor.isDarkMode() ? '#cccccc' : 'black';
- }
- if (window.Format != null)
- {
- Format.inactiveTabBackgroundColor = Editor.isDarkMode() ? '#000000' : '#e4e4e4';
- }
- mxConstants.DROP_TARGET_COLOR = Editor.isDarkMode() ? '#00ff00' : '#0000FF';
- Editor.checkmarkImage = (Editor.isDarkMode() && mxClient.IS_SVG) ?
- Editor.darkCheckmarkImage : Editor.lightCheckmarkImage;
-
- // Updates CSS
- if (this.sketchStyleElt != null)
- {
- this.sketchStyleElt.innerHTML = Editor.createMinimalCss();
- }
- else if (Editor.styleElt != null)
- {
- Editor.styleElt.innerHTML = Editor.createMinimalCss();
- }
- }
-
- var node = (mxUtils.isAncestorNode(document.body, this.container)) ?
- this.container : this.editor.graph.container;
-
- if (node != null)
- {
- if (Editor.isDarkMode())
- {
- node.classList.add('geLegacyDarkMode');
- }
- else
- {
- node.classList.remove('geLegacyDarkMode');
- }
- }
- // Adds or removes link to CSS
- if (Editor.isDarkMode())
- {
- if (this.darkStyle.parentNode == null)
- {
- var head = document.getElementsByTagName('head')[0];
- head.appendChild(this.darkStyle);
- }
- }
- else if (this.darkStyle.parentNode != null)
- {
- this.darkStyle.parentNode.removeChild(this.darkStyle);
- }
- success();
- });
- if (this.darkStyle != null)
- {
- delayed();
- }
- else
- {
- var darkStyle = this.createDarkStyle();
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- darkStyle.onerror = mxUtils.bind(this, function(e)
- {
- if (timeout.clear())
- {
- error(new Error(mxResources.get('errorLoadingFile') +
- ' ' + darkStyle.getAttribute('href')));
- }
- });
- darkStyle.onload = mxUtils.bind(this, function()
- {
- if (timeout.clear())
- {
- this.darkStyle = darkStyle;
- delayed();
- }
- });
- var head = document.getElementsByTagName('head')[0];
- head.appendChild(darkStyle);
- }), mxUtils.bind(this, function()
- {
- error(new Error(mxResources.get('timeout') +
- ' ' + darkStyle.getAttribute('href')));
- }));
- };
- }
- };
- /**
- * Changes Editor.pagesVisible.
- */
- EditorUi.prototype.setPagesVisible = function(value)
- {
- if (Editor.pagesVisible != value)
- {
- Editor.pagesVisible = value;
- // Persist setting
- mxSettings.settings.pagesVisible = value;
- mxSettings.save();
- this.fireEvent(new mxEventObject('pagesVisibleChanged'));
- }
- };
-
- /**
- * Changes Sidebar.sidebarTitles.
- */
- EditorUi.prototype.setSidebarTitles = function(value, remember)
- {
- if (this.sidebar.sidebarTitles != value)
- {
- this.sidebar.sidebarTitles = value;
- this.sidebar.refresh();
- // Persist setting
- if (Editor.isSettingsEnabled() && remember)
- {
- mxSettings.settings.sidebarTitles = value;
- mxSettings.save();
- }
- this.fireEvent(new mxEventObject('sidebarTitlesChanged'));
- }
- };
-
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.setInlineFullscreen = function(value)
- {
- if (Editor.inlineFullscreen != value)
- {
- this.diagramContainer.setAttribute('data-scrollState',
- JSON.stringify(this.saveScrollState()));
-
- // Send request for fullscreen to parent
- var parent = window.opener || window.parent;
- parent.postMessage(JSON.stringify({
- event: 'resize',
- fullscreen: value,
- rect: this.diagramContainer.getBoundingClientRect()
- }), '*');
- }
- };
- /**
- * Invokes to update the UI after a size change in inline embed mode.
- */
- EditorUi.prototype.inlineSizeChanged = function()
- {
- var footer = this.sketchFooterMenuElt;
- var toolbar = this.sketchMainMenuElt;
- var picker = this.sketchPickerMenuElt;
- var graph = this.editor.graph;
-
- if (Editor.inlineFullscreen)
- {
- toolbar.style.left = '10px';
- toolbar.style.top = '10px';
-
- picker.style.left = '10px';
- picker.style.top = '60px';
- footer.style.top = '10px';
- footer.style.right = '12px';
- footer.style.left = '';
- if (this.diagramContainer.getAttribute('data-bounds') == null)
- {
- this.diagramContainer.setAttribute('data-bounds', this.diagramContainer.style.top + ' ' +
- this.diagramContainer.style.left + ' ' + this.diagramContainer.style.width + ' ' +
- this.diagramContainer.style.height);
- this.diagramContainer.style.top = '0px';
- this.diagramContainer.style.left = '0px';
- this.diagramContainer.style.bottom = '0px';
- this.diagramContainer.style.right = '0px';
- this.diagramContainer.style.width = '';
- this.diagramContainer.style.height = '';
- }
- }
- else
- {
- var bounds = this.diagramContainer.getAttribute('data-bounds');
- if (bounds != null)
- {
- this.diagramContainer.removeAttribute('data-bounds');
- var gb = graph.getGraphBounds();
- var tokens = bounds.split(' ');
- var ds = mxUtils.getDocumentSize();
- this.diagramContainer.style.top = tokens[0];
- this.diagramContainer.style.left = tokens[1];
- var w = parseInt(tokens[2]);
- var h = parseInt(tokens[3]);
- w = Math.min((this.minInlineWidth != null) ? Math.max(
- this.minInlineWidth, w) : w, ds.width - 80);
- h = Math.min((this.minInlineHeight != null) ? Math.max(
- this.minInlineHeight, h) : h, ds.height - 80);
-
- this.diagramContainer.style.width = w + 'px';
- this.diagramContainer.style.height = h + 'px';
- this.diagramContainer.style.bottom = '';
- this.diagramContainer.style.right = '';
- var parent = window.opener || window.parent;
- parent.postMessage(JSON.stringify({
- event: 'resize',
- rect: this.diagramContainer.getBoundingClientRect()
- }), '*');
- }
-
- toolbar.style.left = this.diagramContainer.offsetLeft + 'px';
- toolbar.style.top = (this.diagramContainer.offsetTop -
- toolbar.offsetHeight - 4) + 'px';
-
- picker.style.display = '';
- picker.style.left = (this.diagramContainer.offsetLeft -
- picker.offsetWidth - 4) + 'px';
- picker.style.top = this.diagramContainer.offsetTop + 'px';
- footer.style.left = (this.diagramContainer.offsetLeft +
- this.diagramContainer.offsetWidth -
- footer.offsetWidth) + 'px';
- footer.style.top = toolbar.style.top;
- footer.style.right = '';
- this.bottomResizer.style.left = (this.diagramContainer.offsetLeft +
- (this.diagramContainer.offsetWidth -
- this.bottomResizer.offsetWidth) / 2) + 'px';
- this.bottomResizer.style.top = (this.diagramContainer.offsetTop +
- this.diagramContainer.offsetHeight -
- this.bottomResizer.offsetHeight / 2 - 1) + 'px';
- this.rightResizer.style.left = (this.diagramContainer.offsetLeft +
- this.diagramContainer.offsetWidth -
- this.rightResizer.offsetWidth / 2 - 1) + 'px';
- this.rightResizer.style.top = (this.diagramContainer.offsetTop +
- (this.diagramContainer.offsetHeight -
- this.bottomResizer.offsetHeight) / 2) + 'px';
- }
- this.bottomResizer.style.visibility = (Editor.inlineFullscreen) ? 'hidden' : '';
- this.rightResizer.style.visibility = this.bottomResizer.style.visibility;
- toolbar.style.visibility = '';
- footer.style.visibility = '';
- };
- /**
- * Dynamic change of dark mode.
- */
- EditorUi.prototype.doSetSketchMode = function(value)
- {
- if (Editor.sketchMode != value)
- {
- Editor.sketchMode = value;
- this.updateDefaultStyles();
- }
- };
- /**
- * Overrides image dialog to add image search and Google+.
- */
- EditorUi.prototype.updateDefaultStyles = function()
- {
- function setStyle(style, key, value)
- {
- style[key] = value;
- };
-
- var graph = this.editor.graph;
- graph.defaultVertexStyle = mxUtils.clone(Graph.prototype.defaultVertexStyle);
- graph.defaultEdgeStyle = mxUtils.clone(Graph.prototype.defaultEdgeStyle);
-
- // Skipped if defaultVertexStyle or defaultEdgeStyle configured
- if (Editor.config == null || (Editor.config.defaultVertexStyle == null &&
- Editor.config.defaultEdgeStyle == null))
- {
- if (Editor.currentTheme == 'sketch')
- {
- graph.vertexFontSize = 20;
- graph.edgeFontSize = graph.vertexFontSize - 4;
- }
- else if (Editor.currentTheme == 'simple')
- {
- graph.vertexFontSize = 16;
- graph.edgeFontSize = graph.vertexFontSize - 4;
- }
- else
- {
- graph.vertexFontSize = Graph.prototype.vertexFontSize;
- graph.edgeFontSize = Graph.prototype.edgeFontSize;
- }
- // Font size for edges is applied to all inserted edges
- setStyle(graph.defaultEdgeStyle, 'fontSize', graph.edgeFontSize);
- }
- // Skipped if defaultEdgeStyle configured
- if (Editor.config == null || Editor.config.defaultEdgeStyle == null)
- {
- if (Editor.currentTheme == 'simple')
- {
- setStyle(graph.defaultEdgeStyle, 'edgeStyle', 'none');
- setStyle(graph.defaultEdgeStyle, 'curved', '1');
- setStyle(graph.defaultEdgeStyle, 'rounded', '0');
- setStyle(graph.defaultEdgeStyle, 'endSize', '8');
- setStyle(graph.defaultEdgeStyle, 'startSize', '8');
- }
- else if (Editor.currentTheme == 'sketch')
- {
- setStyle(graph.defaultEdgeStyle, 'edgeStyle', 'none');
- setStyle(graph.defaultEdgeStyle, 'curved', '1');
- setStyle(graph.defaultEdgeStyle, 'rounded', '0');
- setStyle(graph.defaultEdgeStyle, 'jettySize', 'auto');
- setStyle(graph.defaultEdgeStyle, 'orthogonalLoop', '1');
- setStyle(graph.defaultEdgeStyle, 'endArrow', 'open');
- setStyle(graph.defaultEdgeStyle, 'endSize', '14');
- setStyle(graph.defaultEdgeStyle, 'startSize', '14');
- setStyle(graph.defaultEdgeStyle, 'sourcePerimeterSpacing', '8');
- setStyle(graph.defaultEdgeStyle, 'targetPerimeterSpacing', '8');
- }
- }
- // Skipped if defaultFonts configured
- if (Editor.config == null || Editor.config.defaultFonts == null)
- {
- // Skipped if defaultFonts, defaultVertexStyle or defaultEdgeStyle configured
- if (Editor.config == null || (Editor.config.defaultVertexStyle == null &&
- Editor.config.defaultEdgeStyle == null))
- {
- if (Editor.currentTheme == 'sketch' || Editor.sketchMode)
- {
- setStyle(graph.defaultVertexStyle, 'fontFamily', Editor.sketchFontFamily);
- setStyle(graph.defaultVertexStyle, 'fontSource', Editor.sketchFontSource);
- setStyle(graph.defaultEdgeStyle, 'fontFamily', Editor.sketchFontFamily);
- setStyle(graph.defaultEdgeStyle, 'fontSource', Editor.sketchFontSource);
- }
- if (Editor.sketchMode)
- {
- setStyle(graph.defaultVertexStyle, 'sketch', '1');
- setStyle(graph.defaultVertexStyle, 'curveFitting', Editor.sketchDefaultCurveFitting);
- setStyle(graph.defaultVertexStyle, 'jiggle', Editor.sketchDefaultJiggle);
- setStyle(graph.defaultVertexStyle, 'hachureGap', '4');
-
- setStyle(graph.defaultEdgeStyle, 'sketch', '1');
- setStyle(graph.defaultEdgeStyle, 'curveFitting', Editor.sketchDefaultCurveFitting);
- setStyle(graph.defaultEdgeStyle, 'jiggle', Editor.sketchDefaultJiggle);
- setStyle(graph.defaultEdgeStyle, 'hachureGap', '4');
- }
- }
- if (Editor.currentTheme == 'sketch')
- {
- this.menus.defaultFonts = Menus.prototype.defaultFonts.concat(Editor.sketchFonts);
- }
- else
- {
- this.menus.defaultFonts = Menus.prototype.defaultFonts;
- }
- }
- graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle);
- graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle);
- this.clearDefaultStyle();
- };
- /**
- *
- */
- EditorUi.prototype.getLinkTitle = function(href)
- {
- var title = Graph.prototype.getLinkTitle.apply(this, arguments);
- if (Graph.isPageLink(href))
- {
- var comma = href.indexOf(',');
-
- if (comma > 0)
- {
- var page = this.getPageById(href.substring(comma + 1));
-
- if (page != null)
- {
- title = page.getName();
- }
- else
- {
- title = mxResources.get('pageNotFound');
- }
- }
- }
- else if (href.substring(0, 5) == 'data:')
- {
- title = this.getCustomLinkTitle(href);
- }
-
- return title;
- };
- /**
- *
- */
- EditorUi.prototype.getCustomLinkTitle = function(href)
- {
- var result = mxResources.get('action');
- if (href.substring(0, 17) == 'data:action/json,')
- {
- try
- {
- var link = JSON.parse(href.substring(17));
- if (link != null && link.title != null)
- {
- result = link.title;
- }
- }
- catch (e)
- {
- // ignore
- }
- }
- return result;
- };
- /**
- *
- */
- EditorUi.prototype.handleCustomLink = function(href, cell)
- {
- if (Graph.isPageLink(href))
- {
- var comma = href.indexOf(',');
- var page = this.getPageById(href.substring(comma + 1));
-
- if (page)
- {
- this.selectPage(page)
- }
- else
- {
- // Needs fallback for missing resource in case of viewer lightbox
- throw new Error(mxResources.get('pageNotFound') || 'Page not found');
- }
- }
- else
- {
- this.editor.graph.handleCustomLink(href, cell);
- }
- };
- /**
- * Creates the format panel and adds overrides.
- */
- EditorUi.prototype.installSettings = function()
- {
- if (Editor.isSettingsEnabled())
- {
- // Sets global switch for sketch mode
- Editor.pagesVisible = mxSettings.settings.pagesVisible;
- // Gets recent colors from settings
- ColorDialog.recentColors = mxSettings.getRecentColors();
- // Avoids overridden values for changes in
- // multiple windows and updates shared values
- if (isLocalStorage)
- {
- try
- {
- window.addEventListener('storage', mxUtils.bind(this, function(evt)
- {
- if (evt.key == mxSettings.key)
- {
- mxSettings.load();
-
- // Updates values
- ColorDialog.recentColors = mxSettings.getRecentColors();
- this.menus.customFonts = mxSettings.getCustomFonts();
- }
- }), false);
- }
- catch (e)
- {
- // ignore
- }
- }
- // Updates UI to reflect current edge style
- this.fireEvent(new mxEventObject('styleChanged', 'keys', [], 'values', [], 'cells', []));
-
- /**
- * Persists custom fonts.
- */
- this.menus.customFonts = mxSettings.getCustomFonts();
-
- this.addListener('customFontsChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.setCustomFonts(this.menus.customFonts);
- mxSettings.save();
- }));
-
- /**
- * Persists copy on connect switch.
- */
- this.editor.graph.connectionHandler.setCreateTarget(mxSettings.isCreateTarget());
- this.fireEvent(new mxEventObject('copyConnectChanged'));
-
- this.addListener('copyConnectChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.setCreateTarget(this.editor.graph.connectionHandler.isCreateTarget());
- mxSettings.save();
- }));
-
- /**
- * Persists default page format.
- */
- this.editor.graph.pageFormat = (this.editor.graph.defaultPageFormat != null) ?
- this.editor.graph.defaultPageFormat : mxSettings.getPageFormat();
- this.addListener('pageFormatChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.setPageFormat(this.editor.graph.pageFormat);
- mxSettings.save();
- }));
- /**
- * Persists default grid color.
- */
- this.editor.graph.view.gridColor = mxSettings.getGridColor(Editor.isDarkMode());
- this.editor.graph.view.defaultDarkGridColor = mxSettings.getGridColor(true);
- this.editor.graph.view.defaultGridColor = mxSettings.getGridColor(false);
- this.addListener('gridColorChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.setGridColor(this.editor.graph.view.gridColor, Editor.isDarkMode());
- mxSettings.save();
- }));
- /**
- * Persists autosave switch in Chrome app.
- */
- if (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)
- {
- this.editor.addListener('autosaveChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.setAutosave(this.editor.autosave);
- mxSettings.save();
- }));
-
- this.editor.autosave = mxSettings.getAutosave();
- }
- if (!this.editor.chromeless || this.editor.editable)
- {
- /**
- * Persists animations switch.
- */
- if (mxSettings.settings.enableAnimations != null)
- {
- Editor.enableAnimations = mxSettings.settings.enableAnimations;
- }
-
- this.addListener('enableAnimationsChanged', mxUtils.bind(this, function(sender, evt)
- {
- mxSettings.settings.enableAnimations = Editor.enableAnimations;
- mxSettings.save();
- }));
- }
-
- if (this.sidebar != null)
- {
- if (urlParams['search-shapes'] != null && this.sidebar.searchShapes != null)
- {
- this.sidebar.searchShapes(decodeURIComponent(urlParams['search-shapes']));
- this.sidebar.showEntries('search');
- }
- else
- {
- this.sidebar.showPalette('search', mxSettings.settings.search);
-
- /**
- * Shows scratchpad if never shown.
- */
- if ((!this.editor.chromeless || this.editor.editable) && (mxSettings.settings.isNew ||
- parseInt(mxSettings.settings.version || 0) <= 8))
- {
- this.toggleScratchpad();
- mxSettings.save();
- }
- }
- }
- // Saves app defaults for UI
- this.addListener('formatWidthChanged', function()
- {
- mxSettings.setFormatWidth(this.formatWidth);
- mxSettings.save();
- });
- }
- };
-
- /**
- * Copies the given cells and XML to the clipboard as an embedded image.
- */
- EditorUi.prototype.copyImage = function(cells, xml, scale)
- {
- try
- {
- if (navigator.clipboard != null && typeof window.ClipboardItem === 'function' &&
- this.spinner.spin(document.body, mxResources.get('exporting')))
- {
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas, svgRoot)
- {
- try
- {
- this.spinner.stop();
-
- // KNOWN: SVG and delayed content currently not supported
- var dataUrl = this.createImageDataUri(canvas, xml, 'png');
- var w = parseInt(svgRoot.getAttribute('width'));
- var h = parseInt(svgRoot.getAttribute('height'));
- this.writeImageToClipboard(dataUrl, w, h, mxUtils.bind(this, function(e)
- {
- this.handleError(e);
- }));
- }
- catch (e)
- {
- this.handleError(e);
- }
- }), null, null, null, mxUtils.bind(this, function(e)
- {
- this.spinner.stop();
- this.handleError(e);
- }), null, null, (scale != null) ? scale : 4,
- this.editor.graph.background == null ||
- this.editor.graph.background == mxConstants.NONE,
- null, null, null, 10, null, null, false, null,
- (cells.length > 0) ? cells : null);
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- };
- /**
- * Creates the format panel and adds overrides.
- */
- EditorUi.prototype.copyCells = function(elt, removeCells)
- {
- var graph = this.editor.graph;
-
- if (!graph.isSelectionEmpty())
- {
- // Fixes cross-platform clipboard UTF8 issues by encoding as URI
- var cells = mxUtils.sortCells(graph.model.getTopmostCells(graph.getSelectionCells()));
- var xml = mxUtils.getXml(graph.encodeCells(cells));
- mxUtils.setTextContent(elt, encodeURIComponent(xml));
- if (removeCells)
- {
- graph.removeCells(cells, false);
- graph.lastPasteXml = null;
- }
- else
- {
- graph.lastPasteXml = xml;
- graph.pasteCounter = 0;
- }
- elt.focus();
- document.execCommand('selectAll', false, null);
- }
- else
- {
- // Disables copy on focused element
- elt.innerText = '';
- }
- };
- /**
- * Creates the format panel and adds overrides.
- */
- EditorUi.prototype.copyXml = function()
- {
- var cells = null;
-
- if (Editor.enableNativeCipboard)
- {
- var graph = this.editor.graph;
-
- if (!graph.isSelectionEmpty())
- {
- cells = mxUtils.sortCells(graph.getExportableCells(
- graph.model.getTopmostCells(graph.getSelectionCells())));
- var xml = mxUtils.getXml(graph.encodeCells(cells));
- navigator.clipboard.writeText(xml);
- }
- }
-
- return cells;
- };
-
- /**
- * Creates the format panel and adds overrides.
- */
- EditorUi.prototype.pasteXml = function(xml, pasteAsLabel, compat, evt, html)
- {
- html = (html != null) ? html : true;
- var graph = this.editor.graph;
- var cells = null;
-
- if (graph.lastPasteXml == xml)
- {
- graph.pasteCounter++;
- }
- else
- {
- graph.lastPasteXml = xml;
- graph.pasteCounter = 0;
- }
-
- var dx = graph.pasteCounter * graph.gridSize;
-
- if (compat || this.isCompatibleString(xml))
- {
- cells = this.importXml(xml, dx, dx);
- graph.setSelectionCells(cells);
- }
- else if (pasteAsLabel && graph.getSelectionCount() == 1)
- {
- var cell = graph.getStartEditingCell(graph.getSelectionCell(), evt);
-
- if ((/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(xml) &&
- graph.getCurrentCellStyle(cell)[mxConstants.STYLE_SHAPE] == 'image')
- {
- graph.setCellStyles(mxConstants.STYLE_IMAGE, xml, [cell]);
- }
- else
- {
- graph.model.beginUpdate();
- try
- {
- graph.labelChanged(cell, xml);
-
- if (Graph.isLink(xml))
- {
- graph.setLinkForCell(cell, xml);
- }
- }
- finally
- {
- graph.model.endUpdate();
- }
- }
-
- graph.setSelectionCell(cell);
- }
- else
- {
- var pt = graph.getInsertPoint();
-
- if (graph.isMouseInsertPoint())
- {
- dx = 0;
-
- // No offset for insert at mouse position
- if (graph.lastPasteXml == xml && graph.pasteCounter > 0)
- {
- graph.pasteCounter--;
- }
- }
- cells = this.insertTextAt(xml, pt.x + dx, pt.y + dx, html);
- graph.setSelectionCells(cells);
- }
-
- if (!graph.isSelectionEmpty())
- {
- graph.scrollCellToVisible(graph.getSelectionCell());
-
- if (this.hoverIcons != null)
- {
- this.hoverIcons.update(graph.view.getState(graph.getSelectionCell()));
- }
- }
-
- return cells;
- };
-
- /**
- * Creates the format panel and adds overrides.
- */
- EditorUi.prototype.pasteCells = function(evt, realElt, useEvent, pasteAsLabel)
- {
- if (!mxEvent.isConsumed(evt))
- {
- var elt = realElt;
- var asHtml = false;
-
- if (useEvent && evt.clipboardData != null && evt.clipboardData.getData)
- {
- // Workaround for paste from IE11 where the page is copied
- // as HTML while the data is only available via text/plain
- var plain = evt.clipboardData.getData('text/plain');
- var override = false;
-
- if (plain != null && plain.length > 0 && plain.substring(0, 18) == '%3CmxGraphModel%3E')
- {
- try
- {
- var tmp = decodeURIComponent(plain);
-
- if (this.isCompatibleString(tmp))
- {
- override = true;
- plain = tmp;
- }
- }
- catch (e)
- {
- // ignore
- }
- }
-
- var data = (!override) ? evt.clipboardData.getData('text/html') : null;
-
- if (data != null && data.length > 0)
- {
- elt = this.parseHtmlData(data);
- asHtml = elt.getAttribute('data-type') != 'text/plain';
- }
- else if (plain != null && plain.length > 0)
- {
- elt = document.createElement('div');
- mxUtils.setTextContent(elt, data);
- }
- }
-
- var spans = elt.getElementsByTagName('span');
-
- if (spans != null && spans.length > 0 && spans[0].getAttribute('data-lucid-type') ===
- 'application/vnd.lucid.chart.objects')
- {
- var content = spans[0].getAttribute('data-lucid-content');
-
- if (content != null && content.length > 0)
- {
- this.convertLucidChart(content, mxUtils.bind(this, function(xml)
- {
- var graph = this.editor.graph;
-
- if (graph.lastPasteXml == xml)
- {
- graph.pasteCounter++;
- }
- else
- {
- graph.lastPasteXml = xml;
- graph.pasteCounter = 0;
- }
-
- var dx = graph.pasteCounter * graph.gridSize;
- graph.setSelectionCells(this.importXml(xml, dx, dx));
- graph.scrollCellToVisible(graph.getSelectionCell());
- }), mxUtils.bind(this, function(e)
- {
- this.handleError(e);
- }));
-
- mxEvent.consume(evt);
- }
- }
- //Miro is using unkown encoding instead of BASE64 as before
- /*else if (spans != null && spans.length > 0 && spans[0].hasAttribute('data-meta')
- && spans[0].getAttribute('data-meta').substring(0, 14) == '<--(miro-data)')
- {
- var miroData = spans[0].getAttribute('data-meta');
- miroData = miroData.substring(14, miroData.length - 15);
- console.log(miroData);
- }*/
- else
- {
- // KNOWN: Paste from IE11 to other browsers on Windows
- // seems to paste the contents of index.html
- var compat = false;
- var xml = '';
- if (asHtml)
- {
- // Extracts compatible XML data
- if (elt.textContent != null &&
- (elt.textContent.substring(0, 7) == '<mxfile' &&
- elt.textContent.substring(elt.textContent.length - 9) == '</mxfile>') ||
- (elt.textContent.substring(0, 13) == '<mxGraphModel' &&
- elt.textContent.substring(elt.textContent.length - 15) == '</mxGraphModel>'))
- {
- // Replaces in text content with normal spaces
- xml = elt.textContent.replace(/\u00a0/g, ' ');
- }
- else
- {
- xml = elt.innerHTML;
- }
- }
- else
- {
- xml = mxUtils.trim((elt.innerText == null) ?
- mxUtils.getTextContent(elt) : elt.innerText);
- }
- // Workaround for junk after XML in VM
- try
- {
- var idx = xml.lastIndexOf('%3E');
-
- if (idx >= 0 && idx < xml.length - 3)
- {
- xml = xml.substring(0, idx + 3);
- }
- }
- catch (e)
- {
- // ignore
- }
-
- // Checks for embedded XML content
- try
- {
- var spans = elt.getElementsByTagName('span');
- var tmp = (spans != null && spans.length > 0) ?
- mxUtils.trim(decodeURIComponent(spans[0].textContent)) :
- decodeURIComponent(xml);
-
- if (tmp && (this.isCompatibleString(tmp) ||
- tmp.substring(0, 20).replace(/\s/g, '').indexOf('{"isProtected":') == 0))
- {
- compat = true;
- xml = tmp;
- }
- }
- catch (e)
- {
- // ignore
- }
- try
- {
- if (xml != null && xml.length > 0)
- {
- if (xml.substring(0, 20).replace(/\s/g, '').indexOf('{"isProtected":') == 0)
- {
- var delayed = mxUtils.bind(this, function ()
- {
- try
- {
- var miro = new MiroImporter();
- xml = miro.importMiroJson(JSON.parse(xml));
- this.pasteXml(xml, pasteAsLabel, compat, evt);
- }
- catch(e)
- {
- console.log('Miro import error:', e);
- }
- });
- if (typeof MiroImporter === 'undefined')
- {
- mxscript('js/diagramly/miro/MiroImporter.js', delayed);
- }
- else
- {
- delayed();
- }
- }
- else
- {
- this.pasteXml(xml, pasteAsLabel, compat, evt, asHtml);
- }
- try
- {
- mxEvent.consume(evt);
- }
- catch (e)
- {
- // ignore event no longer exists in async handler in IE8-
- }
- }
- else if (!useEvent)
- {
- var graph = this.editor.graph;
-
- graph.lastPasteXml = null;
- graph.pasteCounter = 0;
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }
-
- realElt.innerHTML = ' ';
- };
- /**
- *
- */
- EditorUi.prototype.showPrintDialog = function(title, fn)
- {
- var h = 320;
- if (this.editor.graph.isEnabled())
- {
- h += 40;
- }
- if (this.pages != null && this.pages.length > 1)
- {
- h += 40;
- // Additional height in lightbox for pages
- if (!this.editor.graph.isEnabled())
- {
- h += 10;
- }
- }
- // Additional height for include diagram
- if (fn != null && !mxClient.IS_CHROMEAPP &&
- this.getServiceName() == 'draw.io')
- {
- h += 20;
- }
- this.showDialog(new PrintDialog(this, title, fn).container, 320, h, true, true);
- };
- /**
- *
- */
- EditorUi.prototype.print = function(preview, args)
- {
- var editor = this.editor;
- var graph = editor.graph;
- var printScale = 1;
- var idx = this.getPageIndex(this.currentPage);
- var currentPage = (idx != null) ? idx + 1 : 1;
-
- // Disables dark mode while printing
- var darkStylesheet = null;
- var darkFg = graph.shapeForegroundColor;
- var darkBg = graph.shapeBackgroundColor;
- if (graph.themes != null && graph.defaultThemeName == 'darkTheme')
- {
- darkStylesheet = graph.stylesheet;
- graph.stylesheet = graph.getDefaultStylesheet()
- graph.shapeForegroundColor = '#000000';
- graph.shapeBackgroundColor = '#ffffff';
- graph.refresh();
- }
- var printGraph = mxUtils.bind(this, function(thisGraph, pv, forcePageBreaks, pageId)
- {
- // Workaround for CSS transforms affecting the print output
- // is to disable during print output and restore after
- var prev = thisGraph.useCssTransforms;
- var prevTranslate = thisGraph.currentTranslate;
- var prevScale = thisGraph.currentScale;
- var prevViewTranslate = thisGraph.view.translate;
- var prevViewScale = thisGraph.view.scale;
- if (thisGraph.useCssTransforms)
- {
- thisGraph.useCssTransforms = false;
- thisGraph.currentTranslate = new mxPoint(0,0);
- thisGraph.currentScale = 1;
- thisGraph.view.translate = new mxPoint(0,0);
- thisGraph.view.scale = 1;
- }
- // Negative coordinates are cropped or shifted if page visible
- var gb = thisGraph.getGraphBounds();
- var border = 0;
- var x0 = 0;
- var y0 = 0;
- var pf = mxRectangle.fromRectangle(thisGraph.pageFormat);
- var autoOrigin = args.fit || args.crop || !thisGraph.pageVisible;
- var temp = args.scale;
- pf.width = Math.ceil(pf.width * thisGraph.pageScale);
- pf.height = Math.ceil(pf.height * thisGraph.pageScale);
- var scale = 1;
- if (args.fit)
- {
- var h = args.sheetsAcross;
- var v = args.sheetsDown;
- if (!isNaN(temp))
- {
- pf.width = Math.ceil(pf.width * temp);
- pf.height = Math.ceil(pf.height * temp);
- }
- scale = Math.min((pf.height * v) / (gb.height / thisGraph.view.scale),
- (pf.width * h) / (gb.width / thisGraph.view.scale));
- }
- else
- {
- scale = !isNaN(temp) ? temp : 1;
- }
- // Applies print scale
- scale *= printScale;
-
- // Starts at first visible page
- if (!autoOrigin && thisGraph.pageVisible)
- {
- var layout = thisGraph.getPageLayout();
- x0 -= layout.x * pf.width;
- y0 -= layout.y * pf.height;
- }
- else
- {
- autoOrigin = true;
- }
- if (args.crop)
- {
- if (args.selection)
- {
- gb = graph.getBoundingBox(graph.getSelectionCells());
- }
-
- pf.width = (gb.width + 1) * scale / thisGraph.view.scale;
- pf.height = (gb.height + 1) * scale / thisGraph.view.scale;
- }
- pf.width = Math.ceil(pf.width * printScale);
- pf.height = Math.ceil(pf.height * printScale);
- var anchorId = (pageId != null) ? 'page/id,' + pageId : null;
- if (pv == null)
- {
- pv = PrintDialog.createPrintPreview(thisGraph, scale, null, border, x0, y0, autoOrigin);
- pv.title = this.getBaseFilename(true);
- pv.pageSelector = false;
- pv.mathEnabled = false;
- var pageMargin = args.border;
-
- if (!isNaN(pageMargin))
- {
- pv.pageMargin = pageMargin;
- }
- if (args.selection)
- {
- pv.isCellVisible = function(cell)
- {
- return thisGraph.isCellSelected(cell);
- };
- }
- var writeHead = pv.writeHead;
-
- // Overridden to add custom fonts
- pv.writeHead = function(doc)
- {
- writeHead.apply(this, arguments);
-
- // Fixes bold math when exported to PDF
- if (mxClient.IS_GC)
- {
- doc.writeln('<style type="text/css">');
- doc.writeln('@media print {');
- doc.writeln('.MathJax svg { shape-rendering: crispEdges; }');
- doc.writeln('}');
- doc.writeln('</style>');
- }
- if (editor.fontCss != null)
- {
- doc.writeln('<style type="text/css">');
- doc.writeln(mxUtils.htmlEntities(editor.fontCss,
- false, false, false));
- doc.writeln('</style>');
- }
-
- var fonts = thisGraph.getCustomFonts();
-
- for (var i = 0; i < fonts.length; i++)
- {
- var fontName = fonts[i].name;
- var fontUrl = fonts[i].url;
-
- if (Graph.isCssFontUrl(fontUrl))
- {
- doc.writeln('<link rel="stylesheet" href="' +
- mxUtils.htmlEntities(Graph.rewriteGoogleFontUrl(fontUrl)) +
- '" charset="UTF-8" type="text/css">');
- }
- else
- {
- doc.writeln('<style type="text/css">');
- doc.writeln('@font-face {\n' +
- 'font-family: "' + mxUtils.htmlEntities(fontName) + '";\n' +
- 'src: url("' + mxUtils.htmlEntities(fontUrl) + '");\n}');
- doc.writeln('</style>');
- }
- }
- };
- // Adapts background images
- if (Editor.enableCssDarkMode)
- {
- var printGetBackgroundImage = pv.getBackgroundImage;
-
- pv.getBackgroundImage = function()
- {
- return graph.adaptBackgroundPage(
- printGetBackgroundImage.apply(
- this, arguments));
- };
- }
- // Replaces background images with SVG subtrees
- if (Editor.replaceSvgDataUris)
- {
- var printDrawBackgroundImage = pv.drawBackgroundImage;
- pv.drawBackgroundImage = function(img)
- {
- printDrawBackgroundImage.apply(this, arguments);
- if (img.node != null)
- {
- EditorUi.embedSvgImages(img.node);
- graph.disableSvgLinks(img.node, function(link)
- {
- link.setAttribute('href', 'javascript:void(0)');
- });
- }
- };
- }
- // Renders grid and handles math
- var printPreviewAddGraphFragment = pv.addGraphFragment;
- pv.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)
- {
- printPreviewAddGraphFragment.apply(this, arguments);
-
- if (this.graph.mathEnabled)
- {
- this.mathEnabled = this.mathEnabled || true;
- }
- else
- {
- div.classList.add('geDisableMathJax');
- }
- };
-
- // Switches stylesheet for print output in dark mode
- var temp = null;
- var tempFg = graph.shapeForegroundColor;
- var tempBg = graph.shapeBackgroundColor;
-
- if (graph.themes != null && graph.defaultThemeName == 'darkTheme')
- {
- temp = graph.stylesheet;
- graph.stylesheet = graph.getDefaultStylesheet()
- graph.shapeForegroundColor = '#000000';
- graph.shapeBackgroundColor = '#ffffff';
- graph.refresh();
- }
-
- // Generates the print output
- if (args.grid)
- {
- pv.gridSize = thisGraph.gridSize;
- pv.gridSteps = thisGraph.view.gridSteps;
- pv.gridColor = Editor.isDarkMode() ?
- mxGraphView.prototype.defaultGridColor :
- thisGraph.view.gridColor;
- }
- pv.open(null, null, forcePageBreaks, true, anchorId, pf,
- (args.selection) ? thisGraph.getSelectionCells() : null);
-
- // Restores the stylesheet
- if (temp != null)
- {
- graph.shapeForegroundColor = tempFg;
- graph.shapeBackgroundColor = tempBg;
- graph.stylesheet = temp;
- graph.refresh();
- }
- }
- else
- {
- var bg = thisGraph.background;
-
- if (bg == null || bg == '' || bg == mxConstants.NONE)
- {
- bg = '#ffffff';
- }
- pv.backgroundColor = bg;
- pv.autoOrigin = autoOrigin;
-
- if (args.grid)
- {
- pv.gridSize = thisGraph.gridSize;
- pv.gridSteps = thisGraph.view.gridSteps;
- pv.gridColor = Editor.isDarkMode() ?
- mxGraphView.prototype.defaultGridColor :
- thisGraph.view.gridColor;
- }
- pv.appendGraph(thisGraph, scale, x0, y0, forcePageBreaks, true, anchorId, pf,
- (args.selection) ? thisGraph.getSelectionCells() : null);
-
- var extFonts = thisGraph.getCustomFonts();
-
- if (pv.wnd != null)
- {
- for (var i = 0; i < extFonts.length; i++)
- {
- var fontName = extFonts[i].name;
- var fontUrl = extFonts[i].url;
-
- if (Graph.isCssFontUrl(fontUrl))
- {
- pv.wnd.document.writeln('<link rel="stylesheet" href="' +
- mxUtils.htmlEntities(fontUrl) +
- '" charset="UTF-8" type="text/css">');
- }
- else
- {
- pv.wnd.document.writeln('<style type="text/css">');
- pv.wnd.document.writeln('@font-face {\n' +
- 'font-family: "' + mxUtils.htmlEntities(fontName) + '";\n' +
- 'src: url("' + mxUtils.htmlEntities(fontUrl) + '");\n}');
- pv.wnd.document.writeln('</style>');
- }
- }
- }
- }
-
- // Restores state if css transforms are used
- if (prev)
- {
- thisGraph.useCssTransforms = prev;
- thisGraph.currentTranslate = prevTranslate;
- thisGraph.currentScale = prevScale;
- thisGraph.view.translate = prevViewTranslate;
- thisGraph.view.scale = prevViewScale;
- }
-
- return pv;
- });
-
- var pagesFrom = args.pagesFrom;
- var pagesTo = args.pagesTo;
- var ignorePages = !args.allPages;
- var pv = null;
- if (EditorUi.isElectronApp)
- {
- PrintDialog.electronPrint(this, args);
-
- return;
- }
-
- if (ignorePages)
- {
- ignorePages = args.selection ||
- (pagesFrom == currentPage &&
- pagesTo == currentPage);
- }
-
- if (!ignorePages && this.pages != null && this.pages.length)
- {
- var i0 = 0;
- var imax = this.pages.length - 1;
-
- if (!args.allPages)
- {
- i0 = parseInt(pagesFrom) - 1;
- imax = parseInt(pagesTo) - 1;
- }
-
- for (var i = i0; i <= imax; i++)
- {
- var page = this.pages[i];
- var tempGraph = (page == this.currentPage) ? graph : null;
- if (tempGraph == null)
- {
- tempGraph = this.createTemporaryGraph(graph.stylesheet);
- tempGraph.shapeForegroundColor = graph.shapeForegroundColor;
- tempGraph.shapeBackgroundColor = graph.shapeBackgroundColor;
- // Restores graph settings that are relevant for printing
- var pageVisible = true;
- var mathEnabled = false;
- var bg = null;
- var bgImage = null;
-
- if (page.viewState == null)
- {
- // Workaround to extract view state from XML node
- // This changes the state of the page and parses
- // the XML for the graph model even if not needed.
- if (page.root == null)
- {
- this.updatePageRoot(page);
- }
- }
-
- if (page.viewState != null)
- {
- pageVisible = page.viewState.pageVisible;
- mathEnabled = page.viewState.mathEnabled;
- bg = page.viewState.background;
- bgImage = page.viewState.backgroundImage;
- tempGraph.pageFormat = page.viewState.pageFormat;
- tempGraph.gridSize = page.viewState.gridSize;
- }
- // Forces update of background page image in offscreen page
- if (bgImage != null && bgImage.originalSrc != null)
- {
- bgImage = this.createImageForPageLink(
- bgImage.originalSrc, page);
- }
-
- tempGraph.background = bg;
- tempGraph.backgroundImage = (bgImage != null) ? new mxImage(
- bgImage.src, bgImage.width, bgImage.height,
- bgImage.x, bgImage.y) : null;
- tempGraph.pageVisible = pageVisible;
- tempGraph.mathEnabled = mathEnabled;
- // Overrides graph bounds to include background images
- var graphGetGraphBounds = tempGraph.getGraphBounds;
- tempGraph.getGraphBounds = function()
- {
- var bounds = graphGetGraphBounds.apply(this, arguments);
- var img = this.backgroundImage;
-
- if (img != null && img.width != null && img.height != null)
- {
- var t = this.view.translate;
- var s = this.view.scale;
- bounds = mxRectangle.fromRectangle(bounds);
- bounds.add(new mxRectangle(
- (t.x + img.x) * s, (t.y + img.y) * s,
- img.width * s, img.height * s));
- }
- return bounds;
- };
- // Redirects placeholders to current page
- var graphGetGlobalVariable = tempGraph.getGlobalVariable;
-
- tempGraph.getGlobalVariable = function(name)
- {
- if (name == 'page')
- {
- return page.getName();
- }
- else if (name == 'pagenumber')
- {
- return i + 1;
- }
- else if (name == 'pagecount')
- {
- return (this.pages != null) ? this.pages.length : 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
-
- document.body.appendChild(tempGraph.container);
- this.updatePageRoot(page);
- tempGraph.model.setRoot(page.root);
- }
-
- pv = printGraph(tempGraph, pv, i != imax, page.getId());
- if (tempGraph != graph)
- {
- tempGraph.container.parentNode.removeChild(tempGraph.container);
- }
- }
- }
- else
- {
- pv = printGraph(graph);
- }
-
- if (pv == null || pv.wnd == null)
- {
- this.handleError({message: mxResources.get('errorUpdatingPreview')});
- }
- else
- {
- if (pv.mathEnabled)
- {
- var doc = pv.wnd.document;
-
- // Adds asynchronous printing when MathJax finishes rendering
- // via global variable that is checked in math-print.js to
- // avoid generating unsafe-inline script or adding SHA to CSP
- if (!preview)
- {
- pv.wnd.IMMEDIATE_PRINT = true;
- }
- doc.writeln('<script type="text/javascript" src="js/math-print.js"></script>');
- }
-
- pv.closeDocument();
-
- // Rewrites page links to point to internal anchors
- Graph.rewritePageLinks(pv.wnd.document, true);
-
- if (!pv.mathEnabled && !preview)
- {
- PrintDialog.printPreview(pv);
- }
- }
-
- // Restores dark mode
- if (darkStylesheet != null)
- {
- graph.shapeForegroundColor = darkFg;
- graph.shapeBackgroundColor = darkBg;
- graph.stylesheet = darkStylesheet;
- graph.refresh();
- }
- return pv;
- };
- /**
- * Adds a file drop handler for opening local files.
- */
- EditorUi.prototype.addFileDropHandler = function(elts)
- {
- // Installs drag and drop handler for files
- if (Graph.fileSupport)
- {
- var dropElt = null;
-
- for (var i = 0; i < elts.length; i++)
- {
- // Setup the dnd listeners
- mxEvent.addListener(elts[i], 'dragleave', function(evt)
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- });
-
- mxEvent.addListener(elts[i], 'dragover', mxUtils.bind(this, function(evt)
- {
- if (this.editor.graph.isEnabled() || urlParams['embed'] != '1')
- {
- // IE 10 does not implement pointer-events so it can't have a drop highlight
- if (dropElt == null && (!mxClient.IS_IE || (document.documentMode > 10 && document.documentMode < 12)))
- {
- dropElt = this.highlightElement();
- }
- }
- evt.stopPropagation();
- evt.preventDefault();
- }));
-
- mxEvent.addListener(elts[i], 'drop', mxUtils.bind(this, function(evt)
- {
- if (dropElt != null)
- {
- dropElt.parentNode.removeChild(dropElt);
- dropElt = null;
- }
-
- if (this.editor.graph.isEnabled() || urlParams['embed'] != '1')
- {
- if (evt.dataTransfer.files.length > 0)
- {
- this.hideDialog();
-
- // Never open files in embed mode
- if (urlParams['embed'] == '1')
- {
- this.importFiles(evt.dataTransfer.files, 0, 0, this.maxImageSize, null, null,
- null, null, !mxEvent.isControlDown(evt) && !mxEvent.isShiftDown(evt));
- }
- else
- {
- this.openFiles(evt.dataTransfer.files, true);
- }
- }
- else
- {
- // Handles open special files via text drag and drop
- var data = this.extractGraphModelFromEvent(evt);
-
- // Tries additional and async parsing of text content such as HTML, Gliffy data
- if (data == null)
- {
- var provider = (evt.dataTransfer != null) ? evt.dataTransfer : evt.clipboardData;
-
- if (provider != null)
- {
- if (document.documentMode == 10 || document.documentMode == 11)
- {
- data = provider.getData('Text');
- }
- else
- {
- var data = null;
-
- if (mxUtils.indexOf(provider.types, 'text/uri-list') >= 0)
- {
- data = evt.dataTransfer.getData('text/uri-list');
- }
- else
- {
- data = (mxUtils.indexOf(provider.types, 'text/html') >= 0) ? provider.getData('text/html') : null;
- }
-
- if (data != null && data.length > 0)
- {
- var div = document.createElement('div');
- div.innerHTML = Graph.sanitizeHtml(data);
-
- // Extracts single image
- var imgs = div.getElementsByTagName('img');
-
- if (imgs.length > 0)
- {
- data = imgs[0].getAttribute('src');
- }
- }
- else if (mxUtils.indexOf(provider.types, 'text/plain') >= 0)
- {
- data = provider.getData('text/plain');
- }
- }
-
- if (data != null)
- {
- // Checks for embedded XML in PNG
- if (Editor.isPngDataUrl(data))
- {
- var xml = Editor.extractGraphModelFromPng(data);
-
- if (xml != null && xml.length > 0)
- {
- this.openLocalFile(xml, null, true);
- }
- }
- else if (this.isRemoteFileFormat(data))
- {
- if (this.isOffline())
- {
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- else
- {
- new mxXmlRequest(OPEN_URL, 'format=xml&data=' + encodeURIComponent(data)).send(mxUtils.bind(this, function(req)
- {
- if (req.getStatus() >= 200 && req.getStatus() <= 299)
- {
- this.openLocalFile(req.getText(), null, true);
- }
- else
- {
- this.showError(mxResources.get('error'), req.getStatus() == 413? mxResources.get('diagramTooLarge') :
- mxResources.get('unknownError'));
- }
- }));
- }
- }
- else if (/^https?:\/\//.test(data))
- {
- if (this.getCurrentFile() == null)
- {
- window.location.hash = '#U' + encodeURIComponent(data);
- }
- else
- {
- window.geOpenWindow(((mxClient.IS_CHROMEAPP) ?
- (EditorUi.drawHost + '/') : 'https://' + location.host + '/') +
- window.location.search + '#U' + encodeURIComponent(data));
- }
- }
- }
- }
- }
- else
- {
- this.openLocalFile(data, null, true);
- }
- }
- }
- evt.stopPropagation();
- evt.preventDefault();
- }));
- }
- }
- };
-
- /**
- * Highlights the given element
- */
- EditorUi.prototype.highlightElement = function(elt)
- {
- var x = 0;
- var y = 0;
- var w = 0;
- var h = 0;
-
- if (elt == null)
- {
- var b = document.body;
- var d = document.documentElement;
-
- w = (b.clientWidth || d.clientWidth) - 3;
- h = Math.max(b.clientHeight || 0, d.clientHeight) - 3;
- }
- else
- {
- x = elt.offsetTop;
- y = elt.offsetLeft;
- w = elt.clientWidth;
- h = elt.clientHeight;
- }
-
- var hl = document.createElement('div');
- hl.style.zIndex = mxPopupMenu.prototype.zIndex + 2;
- hl.style.border = '3px dotted rgb(254, 137, 12)';
- hl.style.pointerEvents = 'none';
- hl.style.position = 'absolute';
- hl.style.top = x + 'px';
- hl.style.left = y + 'px';
- hl.style.width = Math.max(0, w - 3) + 'px';
- hl.style.height = Math.max(0, h - 3) + 'px';
-
- if (elt != null && elt.parentNode == this.editor.graph.container)
- {
- this.editor.graph.container.appendChild(hl);
- }
- else
- {
- document.body.appendChild(hl);
- }
-
- return hl;
- };
-
- /**
- * Highlights the given element
- */
- EditorUi.prototype.stringToCells = function(xml)
- {
- var doc = mxUtils.parseXml(xml);
- var node = this.editor.extractGraphModel(doc.documentElement);
- var cells = [];
-
- if (node != null)
- {
- var codec = new mxCodec(node.ownerDocument);
- var model = new mxGraphModel();
- codec.decode(node, model);
-
- var parent = model.getChildAt(model.getRoot(), 0);
-
- for (var j = 0; j < model.getChildCount(parent); j++)
- {
- cells.push(model.getChildAt(parent, j));
- }
- }
-
- return cells;
- };
-
- /**
- * Opens the given files in the editor.
- */
- EditorUi.prototype.openFileHandle = function(data, name, file, temp, fileHandle, editable)
- {
- if (name != null && name.length > 0)
- {
- if ((!this.useCanvasForExport && /(\.png)$/i.test(name)) ||
- /(\.pdf)$/i.test(name))
- {
- name = name.substring(0, name.length - 4);
-
- if (!/(\.drawio)$/i.test(name))
- {
- name = name + '.drawio';
- }
- }
-
- var handleResult = mxUtils.bind(this, function(xml)
- {
- var dot = name.lastIndexOf('.');
-
- if (dot >= 0)
- {
- name = name.substring(0, name.lastIndexOf('.')) + '.drawio';
- }
- else
- {
- name = name + '.drawio';
- }
-
- if (xml.substring(0, 10) == '<mxlibrary')
- {
- // Creates new temporary file if library is dropped in splash screen
- if (this.getCurrentFile() == null && urlParams['embed'] != '1')
- {
- this.openLocalFile(this.emptyDiagramXml, this.defaultFilename, temp);
- }
-
- try
- {
- this.loadLibrary(new LocalLibrary(this, xml, name));
- this.showSidebar();
- }
- catch (e)
- {
- this.handleError(e, mxResources.get('errorLoadingFile'));
- }
- }
- else
- {
- this.openLocalFile(xml, name, temp);
- }
- });
-
- if (EditorUi.isVisioFilename(name))
- {
- this.importVisio(file, mxUtils.bind(this, function(xml)
- {
- this.spinner.stop();
- handleResult(xml);
- }));
- }
- else if (/(\.*<graphml )/.test(data))
- {
- this.importGraphML(data, mxUtils.bind(this, function(xml)
- {
- this.spinner.stop();
- handleResult(xml);
- }));
- }
- else if (Graph.fileSupport && new XMLHttpRequest().upload &&
- this.isRemoteFileFormat(data, name))
- {
- if (this.isOffline())
- {
- this.spinner.stop();
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- else
- {
- this.parseFile(file, mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4)
- {
- this.spinner.stop();
-
- if (xhr.status >= 200 && xhr.status <= 299)
- {
- handleResult(xhr.responseText);
- }
- else
- {
- this.handleError({message: mxResources.get((xhr.status == 413) ?
- 'drawingTooLarge' : 'invalidOrMissingFile')},
- mxResources.get('errorLoadingFile'));
- }
- }
- }));
- }
- }
- else if (this.isLucidChartData(data))
- {
- if (/(\.json)$/i.test(name))
- {
- name = name.substring(0, name.length - 5) + '.drawio';
- }
- // LATER: Add import step that produces cells and use callback
- this.convertLucidChart(data, mxUtils.bind(this, function(xml)
- {
- this.spinner.stop();
- this.openLocalFile(xml, name, temp);
- }), mxUtils.bind(this, function(e)
- {
- this.spinner.stop();
- this.handleError(e);
- }));
- }
- else if (data.substring(0, 10) == '<mxlibrary')
- {
- this.spinner.stop();
-
- // Creates new temporary file if library is dropped in splash screen
- if (this.getCurrentFile() == null && urlParams['embed'] != '1')
- {
- this.openLocalFile(this.emptyDiagramXml, this.defaultFilename, temp);
- }
-
- try
- {
- this.loadLibrary(new LocalLibrary(this, data, file.name));
- this.showSidebar();
- }
- catch (e)
- {
- this.handleError(e, mxResources.get('errorLoadingFile'));
- }
- }
- else if (data.indexOf('PK') == 0)
- {
- this.importZipFile(file, mxUtils.bind(this, function(xml)
- {
- this.spinner.stop();
- handleResult(xml);
- }), mxUtils.bind(this, function()
- {
- this.spinner.stop();
- this.openLocalFile(data, name, temp);
- }));
- }
- else
- {
- if (file.type.substring(0, 9) == 'image/png')
- {
- data = this.extractGraphModelFromPng(data);
- }
- else if (file.type == 'application/pdf')
- {
- var xml = Editor.extractGraphModelFromPdf(data);
-
- if (xml != null)
- {
- fileHandle = null;
- temp = true;
- data = xml;
- }
- }
-
- this.spinner.stop();
- this.openLocalFile(data, name, temp, fileHandle,
- (fileHandle != null) ? file : null, editable);
- }
- }
- };
-
- /**
- * Opens the given files in the editor.
- */
- EditorUi.prototype.openFiles = function(files, temp)
- {
- if (this.spinner.spin(document.body, mxResources.get('loading')))
- {
- for (var i = 0; i < files.length; i++)
- {
- (mxUtils.bind(this, function(file)
- {
- var reader = new FileReader();
-
- reader.onload = mxUtils.bind(this, function(e)
- {
- try
- {
- this.openFileHandle(e.target.result, file.name, file, temp);
- }
- catch (e)
- {
- this.handleError(e);
- }
- });
-
- reader.onerror = mxUtils.bind(this, function(e)
- {
- this.spinner.stop();
- this.handleError(e);
- window.openFile = null;
- });
-
- if ((file.type.substring(0, 5) === 'image' ||
- file.type === 'application/pdf') &&
- file.type.substring(0, 9) !== 'image/svg')
- {
- reader.readAsDataURL(file);
- }
- else
- {
- reader.readAsText(file);
- }
- }))(files[i]);
- }
- }
- };
- /**
- * Shows the layers dialog if the graph has more than one layer.
- */
- EditorUi.prototype.openLocalFile = function(data, name, temp, fileHandle, desc, editable)
- {
- var currentFile = this.getCurrentFile();
-
- var fn = mxUtils.bind(this, function()
- {
- window.openFile = null;
-
- if (name == null && this.getCurrentFile() != null && this.isDiagramEmpty())
- {
- var doc = mxUtils.parseXml(data);
-
- if (doc != null)
- {
- this.editor.setGraphXml(doc.documentElement);
- this.editor.graph.selectAll();
- }
- }
- else
- {
- this.fileLoaded(new LocalFile(this, data,
- name || this.defaultFilename, temp,
- fileHandle, desc, editable));
- }
- });
- if (data != null && data.length > 0)
- {
- if (currentFile == null || (!currentFile.isModified() &&
- (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp || fileHandle != null)))
- {
- fn();
- }
- else if ((mxClient.IS_CHROMEAPP || EditorUi.isElectronApp || fileHandle != null) &&
- currentFile != null && currentFile.isModified())
- {
- this.confirm(mxResources.get('allChangesLost'), null, fn,
- mxResources.get('cancel'), mxResources.get('discardChanges'));
- }
- else
- {
- window.openFile = new OpenFile(function()
- {
- window.openFile = null;
- });
-
- window.openFile.setData(data, name);
- window.geOpenWindow(this.getUrl(), null, mxUtils.bind(this, function()
- {
- if (currentFile != null && currentFile.isModified())
- {
- this.confirm(mxResources.get('allChangesLost'), null, fn,
- mxResources.get('cancel'), mxResources.get('discardChanges'));
- }
- else
- {
- fn();
- }
- }));
- }
- }
- else
- {
- throw new Error(mxResources.get('notADiagramFile'));
- }
- };
-
- /**
- * Returns a list of all shapes used in the current file.
- */
- EditorUi.prototype.getBasenames = function()
- {
- var basenames = {};
- if (this.pages != null)
- {
- for (var i = 0; i < this.pages.length; i++)
- {
- this.updatePageRoot(this.pages[i]);
- this.addBasenamesForCell(this.pages[i].root, basenames);
- }
- }
- else
- {
- this.addBasenamesForCell(this.editor.graph.model.getRoot(), basenames);
- }
-
- var result = [];
-
- for (var key in basenames)
- {
- result.push(key);
- }
-
- return result;
- };
-
- /**
- * Returns a list of all shapes used in the current file.
- */
- EditorUi.prototype.addBasenamesForCell = function(cell, basenames)
- {
- function addName(name)
- {
- if (name != null)
- {
- // LATER: Check if this case exists
- var dot = name.lastIndexOf('.');
-
- if (dot > 0)
- {
- name = name.substring(dot + 1, name.length);
- }
-
- if (basenames[name] == null)
- {
- basenames[name] = true;
- }
- }
- };
-
- var graph = this.editor.graph;
- var style = graph.getCellStyle(cell);
- var shape = style[mxConstants.STYLE_SHAPE];
- addName(mxStencilRegistry.getBasenameForStencil(shape));
-
- // Adds package names for markers in edges
- if (graph.model.isEdge(cell))
- {
- addName(mxMarker.getPackageForType(style[mxConstants.STYLE_STARTARROW]));
- addName(mxMarker.getPackageForType(style[mxConstants.STYLE_ENDARROW]));
- }
- var childCount = graph.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- this.addBasenamesForCell(graph.model.getChildAt(cell, i), basenames);
- }
- };
-
- /**
- * Shows the layers dialog if the graph has more than one layer.
- */
- EditorUi.prototype.setGraphEnabled = function(enabled)
- {
- this.diagramContainer.style.visibility = (enabled) ? '' : 'hidden';
- this.formatContainer.style.visibility = (enabled) ? '' : 'hidden';
- this.sidebarContainer.style.display = (enabled) ? '' : 'none';
- this.hsplit.style.display = (enabled && Editor.currentTheme != 'sketch' &&
- Editor.currentTheme != 'min') ? '' : 'none';
- this.editor.graph.setEnabled(enabled);
-
- if (this.tabContainer != null)
- {
- this.tabContainer.style.visibility = (enabled) ? '' : 'hidden';
- }
-
- if (this.ruler != null)
- {
- this.ruler.hRuler.container.style.visibility = (enabled) ? '' : 'hidden';
- this.ruler.vRuler.container.style.visibility = (enabled) ? '' : 'hidden';
- }
-
- if (!enabled)
- {
- this.hideWindows();
- }
- };
-
- /**
- * Shows the layers dialog if the graph has more than one layer.
- */
- EditorUi.prototype.initializeEmbedMode = function()
- {
- this.setGraphEnabled(false);
- var parent = window.opener || window.parent;
- if (parent != window)
- {
- if (urlParams['spin'] != '1' || this.spinner.spin(document.body, mxResources.get('loading')))
- {
- var initialized = false;
- this.installMessageHandler(mxUtils.bind(this, function(xml, evt, modified, convertToSketch)
- {
- if (!initialized)
- {
- initialized = true;
-
- this.spinner.stop();
- this.addEmbedButtons();
- this.setGraphEnabled(true);
- }
-
- if (xml == null || xml.length == 0)
- {
- xml = this.emptyDiagramXml;
- }
-
- // Creates temporary file for diff sync in embed mode
- this.setCurrentFile(new EmbedFile(this, xml, {}));
- this.mode = App.MODE_EMBED;
- this.setFileData(xml);
-
- // TODO: Check if cellsInserted should be fired instead here
- if (convertToSketch)
- {
- try
- {
- //Disable grid and page view
- var graph = this.editor.graph;
- graph.setGridEnabled(false);
- graph.pageVisible = false;
- var cells = graph.model.cells;
-
- //Add sketch style and font to all cells
- for (var id in cells)
- {
- var cell = cells[id];
-
- if (cell != null && cell.style != null)
- {
- cell.style += ';sketch=1;' + (cell.style.indexOf('fontFamily=') == -1 || cell.style.indexOf('fontFamily=Helvetica;') > -1?
- 'fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;' : '');
- }
- }
- }
- catch(e)
- {
- console.log(e); //Ignore
- }
- }
-
- if (!this.editor.isChromelessView())
- {
- this.showLayersDialog();
- }
- else if (this.editor.graph.isLightboxView())
- {
- this.lightboxFit();
- }
-
- if (this.chromelessResize)
- {
- this.chromelessResize();
- }
-
- this.editor.undoManager.clear();
- this.editor.modified = (modified != null) ? modified : false;
- this.updateUi();
-
- // Workaround for no initial focus in FF
- // (does not work in Conf Cloud with FF)
- if (window.self !== window.top)
- {
- window.focus();
- }
-
- if (this.format != null)
- {
- this.format.refresh();
- }
- }));
- }
- }
- };
-
- /**
- * Shows the layers dialog if the graph has more than one layer.
- */
- EditorUi.prototype.showLayersDialog = function()
- {
- if (this.editor.graph.getModel().getChildCount(this.editor.graph.getModel().getRoot()) > 1)
- {
- if (this.actions.layersWindow == null)
- {
- this.actions.get('layers').funct();
- }
- else
- {
- this.actions.layersWindow.window.setVisible(true);
- }
- }
- };
- /**
- * Tries to find a public URL for the given file.
- */
- EditorUi.prototype.getPublicUrl = function(file, fn)
- {
- if (file != null)
- {
- if (this.spinner.spin(document.body, mxResources.get('loading')))
- {
- file.getPublicUrl(mxUtils.bind(this, function(url)
- {
- this.spinner.stop();
- fn(url);
- }));
- }
- }
- else
- {
- fn(null);
- }
- };
- /**
- * Adds the buttons for embedded mode.
- */
- EditorUi.prototype.createLoadMessage = function(eventName)
- {
- var graph = this.editor.graph;
-
- return {event: eventName, pageVisible: graph.pageVisible, translate: graph.view.translate,
- bounds: graph.getGraphBounds(), currentPage: this.getSelectedPageIndex(),
- scale: graph.view.scale, page: graph.view.getBackgroundPageBounds()};
- };
-
- /**
- * Adds the buttons for embedded mode.
- */
- EditorUi.prototype.sendEmbeddedSvgExport = function(noExit)
- {
- try
- {
- var graph = this.editor.graph;
- if (graph.isEditing())
- {
- graph.stopEditing(!graph.isInvokesStopCellEditing());
- }
- var parent = window.opener || window.parent;
-
- if (!this.editor.modified)
- {
- if (!noExit)
- {
- parent.postMessage(JSON.stringify({event: 'exit',
- point: this.embedExitPoint}), '*');
- }
- }
- else
- {
- var bg = graph.background;
-
- if (bg == null || bg == mxConstants.NONE)
- {
- bg = this.embedExportBackground;
- }
- this.getEmbeddedSvg(this.getFileData(true, null, null, null, null,
- null, null, null, null, false), graph, null, true,
- mxUtils.bind(this, function(svg)
- {
- parent.postMessage(JSON.stringify({
- event: 'export', point: this.embedExitPoint,
- exit: (noExit != null) ? !noExit : true,
- data: Editor.createSvgDataUri(svg)
- }), '*');
- }), null, null, true, bg, 1, this.embedExportBorder);
- }
- if (!noExit)
- {
- this.diagramContainer.removeAttribute('data-bounds');
- Editor.inlineFullscreen = false;
- graph.model.clear();
- this.editor.undoManager.clear();
- this.setBackgroundImage(null);
- this.editor.modified = false;
- if (urlParams['embed'] != '1')
- {
- this.fireEvent(new mxEventObject('editInlineStop'));
- }
- }
- }
- catch (e)
- {
- if (!noExit)
- {
- this.handleError(e);
- }
- }
- };
-
- /**
- * Adds the buttons for embedded mode.
- */
- EditorUi.prototype.installMessageHandler = function(fn)
- {
- var changeListener = null;
- var ignoreChange = false;
- var autosave = false;
- var lastData = null;
-
- var updateStatus = mxUtils.bind(this, function(sender, eventObject)
- {
- if (!this.editor.modified || urlParams['modified'] == '0')
- {
- this.editor.setStatus('');
- }
- else if (urlParams['modified'] != null)
- {
- this.editor.setStatus(mxUtils.htmlEntities(mxResources.get(urlParams['modified'])));
- }
- });
-
- this.editor.graph.model.addListener(mxEvent.CHANGE, updateStatus);
-
- // Receives XML message from opener and puts it into the graph
- mxEvent.addListener(window, 'message', mxUtils.bind(this, function(evt)
- {
- var validSource = window.opener || window.parent;
-
- if (evt.source != validSource)
- {
- return;
- }
-
- var data = evt.data;
- var afterLoad = null;
-
- var extractDiagramXml = mxUtils.bind(this, function(data)
- {
- if (data != null && typeof data.charAt === 'function' && data.charAt(0) != '<')
- {
- try
- {
- if (Editor.isPngDataUrl(data))
- {
- data = Editor.extractGraphModelFromPng(data);
- }
- else if (data.substring(0, 26) == 'data:image/svg+xml;base64,')
- {
- data = atob(data.substring(26));
- }
- else if (data.substring(0, 24) == 'data:image/svg+xml;utf8,')
- {
- data = data.substring(24);
- }
-
- if (data != null)
- {
- if (data.trim() == '')
- {
- data = null; // nextcloud case
- }
- else if (data.charAt(0) == '%')
- {
- data = decodeURIComponent(data);
- }
- else if (data.charAt(0) != '<')
- {
- data = Graph.decompress(data);
- }
- }
- }
- catch (e)
- {
- // ignore compression errors and use empty data
- }
- }
-
- return data;
- });
- if (urlParams['proto'] == 'json')
- {
- var convertToSketch = false;
-
- try
- {
- data = JSON.parse(data);
- EditorUi.debug('EditorUi.installMessageHandler',
- [this], 'evt', [evt], 'data', [data]);
- }
- catch (e)
- {
- data = null;
- }
-
- try
- {
- if (data == null)
- {
- // Ignore
- return;
- }
- else if (data.action == 'dialog')
- {
- this.showError((data.titleKey != null) ? mxResources.get(data.titleKey) :
- data.title, (data.messageKey != null) ? mxResources.get(data.messageKey) :
- mxUtils.htmlEntities(data.message), (data.buttonKey != null) ?
- mxResources.get(data.buttonKey) : data.button);
-
- if (data.modified != null)
- {
- this.editor.modified = data.modified;
- }
-
- return;
- }
- else if (data.action == 'layout')
- {
- this.executeLayouts(this.editor.graph.createLayouts(data.layouts));
- return;
- }
- else if (data.action == 'prompt')
- {
- this.spinner.stop();
-
- var dlg = new FilenameDialog(this, data.defaultValue || '',
- (data.okKey != null) ? mxResources.get(data.okKey) : data.ok, function(value)
- {
- if (value != null)
- {
- parent.postMessage(JSON.stringify({event: 'prompt', value: value, message: data}), '*');
- }
- else
- {
- parent.postMessage(JSON.stringify({event: 'prompt-cancel', message: data}), '*');
- }
- }, (data.titleKey != null) ? mxResources.get(data.titleKey) : data.title,
- null, null, null, null, function()
- {
- parent.postMessage(JSON.stringify({event: 'prompt-cancel', message: data}), '*');
- });
- this.showDialog(dlg.container, 300, 80, true, false);
- dlg.init();
-
- return;
- }
- else if (data.action == 'draft')
- {
- var tmp = extractDiagramXml(data.xml);
- this.spinner.stop();
-
- var dlg = new DraftDialog(this, mxResources.get('draftFound',
- [data.name || this.defaultFilename]),
- tmp, mxUtils.bind(this, function()
- {
- this.hideDialog();
- parent.postMessage(JSON.stringify({event: 'draft',
- result: 'edit', message: data}), '*');
- }), mxUtils.bind(this, function()
- {
- this.hideDialog();
- parent.postMessage(JSON.stringify({event: 'draft',
- result: 'discard', message: data}), '*');
- }), (data.editKey) ? mxResources.get(data.editKey) : null,
- (data.discardKey) ? mxResources.get(data.discardKey) : null,
- (data.ignore) ? mxUtils.bind(this, function()
- {
- this.hideDialog();
- parent.postMessage(JSON.stringify({event: 'draft',
- result: 'ignore', message: data}), '*');
- }) : null);
- this.showDialog(dlg.container, 640, 480, true, false, mxUtils.bind(this, function(cancel)
- {
- if (cancel)
- {
- this.actions.get('exit').funct();
- }
- }));
-
- try
- {
- dlg.init();
- }
- catch (e)
- {
- parent.postMessage(JSON.stringify({event: 'draft',
- error: e.toString(), message: data}), '*');
- }
-
- return;
- }
- else if (data.action == 'template')
- {
- this.spinner.stop();
-
- var enableRecentDocs = data.enableRecent == 1;
- var enableSearchDocs = data.enableSearch == 1;
- var enableCustomTemp = data.enableCustomTemp == 1;
-
- var dlg = new NewDialog(this, false, data.templatesOnly? false : data.callback != null,
- mxUtils.bind(this, function(xml, name, url, libs)
- {
- xml = xml || this.emptyDiagramXml;
-
- // LATER: Add autosave option in template message
- if (data.callback != null)
- {
- parent.postMessage(JSON.stringify({event: 'template', xml: xml,
- blank: xml == this.emptyDiagramXml, name: name,
- tempUrl: url, libs: libs, builtIn: true,
- message: data}), '*');
- }
- else
- {
- fn(xml, evt, xml != this.emptyDiagramXml, data.toSketch);
-
- // Workaround for status updated before modified applied
- if (!this.editor.modified)
- {
- this.editor.setStatus('');
- }
- }
- }), null, null, null, null, null, null, null,
- enableRecentDocs? mxUtils.bind(this, function(recentReadyCallback)
- {
- this.remoteInvoke('getRecentDiagrams', [null], null, recentReadyCallback, function()
- {
- recentReadyCallback(null, 'Network Error!');
- });
- }) : null,
- enableSearchDocs? mxUtils.bind(this, function(searchStr, searchReadyCallback)
- {
- this.remoteInvoke('searchDiagrams', [searchStr, null], null, searchReadyCallback, function()
- {
- searchReadyCallback(null, 'Network Error!');
- });
- }) : null,
- mxUtils.bind(this, function(url, info, name)
- {
- //If binary files are possible, we can get the file content using remote invokation, imported it, and send final mxFile back
- parent.postMessage(JSON.stringify({event: 'template', docUrl: url, info: info,
- name: name}), '*');
- }), null, null,
- enableCustomTemp? mxUtils.bind(this, function(customTempCallback)
- {
- this.remoteInvoke('getCustomTemplates', null, null, customTempCallback, function()
- {
- customTempCallback({}, 0); //ignore error by sending empty templates
- });
- }) : null, data.withoutType == 1);
-
- this.showDialog(dlg.container, 620, 460, true, false, mxUtils.bind(this, function(cancel)
- {
- if (this.sidebar != null)
- {
- this.sidebar.hideTooltip();
- }
-
- if (cancel)
- {
- this.actions.get('exit').funct();
- }
- }));
- dlg.init();
-
- return;
- }
- else if (data.action == 'textContent')
- {
- //TODO Remove this message and use remote invokation instead
- var allPagesTxt = this.getDiagramTextContent();
- parent.postMessage(JSON.stringify({event: 'textContent',
- data: allPagesTxt, message: data}), '*');
- return;
- }
- else if (data.action == 'status')
- {
- if (data.messageKey != null)
- {
- this.editor.setStatus(mxUtils.htmlEntities(mxResources.get(data.messageKey)));
- }
- else if (data.message != null)
- {
- this.editor.setStatus(mxUtils.htmlEntities(data.message));
- }
-
- if (data.modified != null)
- {
- this.editor.modified = data.modified;
- }
-
- return;
- }
- else if (data.action == 'spinner')
- {
- var msg = (data.messageKey != null) ? mxResources.get(data.messageKey) : data.message;
-
- if (data.show != null && !data.show)
- {
- this.spinner.stop();
- }
- else
- {
- this.spinner.spin(document.body, msg)
- }
-
- return;
- }
- else if (data.action == 'exit')
- {
- this.actions.get('exit').funct();
- return;
- }
- else if (data.action == 'viewport')
- {
- if (data.viewport != null)
- {
- this.embedViewport = data.viewport;
- this.editor.graph.refresh();
- this.fireEvent(new mxEventObject('embedViewportChanged'));
- }
- return;
- }
- else if (data.action == 'fullscreenChanged')
- {
- var scrollState = null;
- try
- {
- var temp = this.diagramContainer.getAttribute('data-scrollState');
- if (temp != null)
- {
- this.diagramContainer.removeAttribute('data-scrollState');
- scrollState = JSON.parse(temp);
- }
- }
- catch (e)
- {
- // ignore
- }
- Editor.inlineFullscreen = data.value;
- this.fireEvent(new mxEventObject('inlineFullscreenChanged'));
- if (scrollState != null)
- {
- this.restoreScrollState(scrollState);
- }
- return;
- }
- else if (data.action == 'snapshot')
- {
- this.sendEmbeddedSvgExport(true);
- return;
- }
- else if (data.action == 'export')
- {
- if (data.format == 'png' || data.format == 'xmlpng')
- {
- if ((data.spin == null && data.spinKey == null) || this.spinner.spin(document.body,
- (data.spinKey != null) ? mxResources.get(data.spinKey) : data.spin))
- {
- var xml = (data.xml != null) ? data.xml : this.getFileData(true);
- this.editor.graph.setEnabled(false);
- var graph = this.editor.graph;
-
- var postDataBack = mxUtils.bind(this, function(uri, svg)
- {
- if (data.withSvg)
- {
- data.withSvg = false;
- this.getEmbeddedSvg(xml, this.editor.graph, null, true, function(svg)
- {
- postDataBack(uri, svg);
- }, null, null, data.embedImages, this.editor.graph.background,
- data.scale, data.border, data.shadow);
- return;
- }
- this.editor.graph.setEnabled(true);
- this.spinner.stop();
-
- var msg = this.createLoadMessage('export');
- msg.format = data.format;
- msg.message = data;
- msg.data = uri;
- msg.svg = svg;
- msg.xml = xml;
- parent.postMessage(JSON.stringify(msg), '*');
- });
-
- var processUri = mxUtils.bind(this, function(uri)
- {
- if (uri == null)
- {
- uri = Editor.blankImage;
- }
-
- if (data.format == 'xmlpng')
- {
- uri = Editor.writeGraphModelToPng(uri, 'tEXt', 'mxfile',
- encodeURIComponent(xml));
- }
-
- // Removes temporary graph from DOM
- if (graph != this.editor.graph)
- {
- graph.container.parentNode.removeChild(graph.container);
- }
-
- postDataBack(uri);
- });
-
- var pageId = data.pageId || (this.pages != null? ((data.currentPage) ?
- this.currentPage.getId() : this.pages[0].getId()) : null);
-
- if (this.isExportToCanvas())
- {
- var graphReady = mxUtils.bind(this, function()
- {
- // Exports PNG for first/specific page while other page is visible by creating a graph
- // LATER: Add caching for the graph or SVG while not on first page
- if (this.pages != null && this.currentPage.getId() != pageId)
- {
- var graphGetGlobalVariable = graph.getGlobalVariable;
- graph = this.createTemporaryGraph(graph.getStylesheet());
- var page;
-
- for (var i = 0; i < this.pages.length; i++)
- {
- if (this.pages[i].getId() == pageId)
- {
- page = this.updatePageRoot(this.pages[i]);
- break;
- }
- }
-
- //If pageId info is incorrect
- if (page == null)
- {
- page = this.currentPage;
- }
-
- graph.getGlobalVariable = function(name)
- {
- if (name == 'page')
- {
- return page.getName();
- }
- else if (name == 'pagenumber')
- {
- return 1;
- }
-
- return graphGetGlobalVariable.apply(this, arguments);
- };
-
- document.body.appendChild(graph.container);
- graph.model.setRoot(page.root);
- }
-
- // Set visible layers based on message setting
- if (data.layerIds != null)
- {
- var graphModel = graph.model;
- var layers = graphModel.getChildCells(graphModel.getRoot());
- var layerIdsMap = {};
-
- for (var i = 0; i < data.layerIds.length; i++)
- {
- layerIdsMap[data.layerIds[i]] = true;
- }
-
- for (var i = 0; i < layers.length; i++)
- {
- graphModel.setVisible(layers[i], layerIdsMap[layers[i].id] || false);
- }
- }
- var theme = null;
- if (data.keepTheme)
- {
- theme = (Editor.cssDarkMode || Editor.isDarkMode()) ? 'dark' : 'light'
- }
- this.editor.exportToCanvas(mxUtils.bind(this, function(canvas)
- {
- processUri(canvas.toDataURL('image/png'));
- }), data.width, null, data.background, mxUtils.bind(this, function()
- {
- processUri(null);
- }), null, null, data.scale, data.transparent, data.shadow,
- null, graph, data.border, null, data.grid, theme);
- });
- // Uses optional XML from incoming message
- if (data.xml != null && data.xml.length > 0)
- {
- ignoreChange = true;
- this.setFileData(xml);
- ignoreChange = false;
- }
- graphReady();
- }
- else
- {
- // Data from server is base64 encoded to avoid binary XHR
- // Double encoding for XML arg is needed for UTF8 encoding
- var req = new mxXmlRequest(EXPORT_URL, 'format=png&embedXml=' +
- ((data.format == 'xmlpng') ? '1' : '0') +
- (pageId != null? '&pageId=' + pageId : '') +
- (data.layerIds != null && data.layerIds.length > 0?
- '&extras=' + encodeURIComponent(JSON.stringify({layerIds: data.layerIds})) : '') +
- (data.scale != null? '&scale=' + data.scale : '') +'&base64=1&xml=' +
- encodeURIComponent(xml));
-
- req.send(mxUtils.bind(this, function(req)
- {
- // Temp graph was never created at this point so we can
- // skip processUri since it already contains the XML
- if (req.getStatus() >= 200 && req.getStatus() <= 299)
- {
- postDataBack('data:image/png;base64,' + req.getText());
- }
- else
- {
- processUri(null);
- }
- }), mxUtils.bind(this, function()
- {
- processUri(null);
- }));
- }
- }
- }
- else
- {
- var graphReady = mxUtils.bind(this, function()
- {
- var msg = this.createLoadMessage('export');
-
- // Attaches incoming message
- msg.message = data;
-
- // Forces new HTML format if pages exists
- if (data.format == 'html2' || (data.format == 'html' && (urlParams['pages'] != '0' ||
- (this.pages != null && this.pages.length > 1))))
- {
- var node = this.getXmlFileData();
- msg.xml = mxUtils.getXml(node);
- msg.data = this.getFileData(null, null, true, null, null, null, node);
- msg.format = data.format;
- }
- else if (data.format == 'html')
- {
- var xml = this.editor.getGraphXml();
- msg.data = this.getHtml(xml, this.editor.graph);
- msg.xml = mxUtils.getXml(xml);
- msg.format = data.format;
- }
- else
- {
- // Creates a preview with no alt text for unsupported browsers
- mxSvgCanvas2D.prototype.foAltText = null;
-
- var bg = (data.background != null) ? data.background : this.editor.graph.background;
-
- if (bg == mxConstants.NONE)
- {
- bg = null;
- }
-
- msg.xml = this.getFileData(true, null, null, null, null,
- null, null, null, null, false);
- msg.format = 'svg';
-
- var postResult = mxUtils.bind(this, function(svg)
- {
- this.editor.graph.setEnabled(true);
- this.spinner.stop();
-
- msg.data = Editor.createSvgDataUri(svg);
- parent.postMessage(JSON.stringify(msg), '*');
- });
- var theme = null;
- if (data.keepTheme)
- {
- theme = (Editor.cssDarkMode || Editor.isDarkMode()) ? 'dark' : 'light'
- }
-
- if (data.format == 'xmlsvg')
- {
- if ((data.spin == null && data.spinKey == null) || this.spinner.spin(document.body,
- (data.spinKey != null) ? mxResources.get(data.spinKey) : data.spin))
- {
- this.getEmbeddedSvg(msg.xml, this.editor.graph, null, true, postResult, null, null,
- data.embedImages, bg, data.scale, data.border, data.shadow, theme);
- }
- }
- else
- {
- if ((data.spin == null && data.spinKey == null) || this.spinner.spin(document.body,
- (data.spinKey != null) ? mxResources.get(data.spinKey) : data.spin))
- {
- this.editor.graph.setEnabled(false);
- var svgRoot = this.editor.graph.getSvg(bg, data.scale, data.border, null, null,
- null, null, null, null, this.editor.graph.shadowVisible || data.shadow,
- null, theme);
-
- if (this.editor.graph.shadowVisible || data.shadow)
- {
- this.editor.graph.addSvgShadow(svgRoot);
- }
- this.embedFonts(svgRoot, mxUtils.bind(this, function(svgRoot)
- {
- if (data.embedImages || data.embedImages == null)
- {
- this.editor.convertImages(svgRoot, mxUtils.bind(this, function(svgRoot)
- {
- postResult(mxUtils.getXml(svgRoot));
- }));
- }
- else
- {
- postResult(mxUtils.getXml(svgRoot));
- }
- }));
- }
- }
-
- return;
- }
-
- parent.postMessage(JSON.stringify(msg), '*');
- });
- // SVG is generated from graph so parse optional XML
- if (data.xml != null && data.xml.length > 0)
- {
- if (this.editor.graph.mathEnabled)
- {
- // Waits for MathJax autoloading and rendering
- var editorOnMathJaxDone = Editor.onMathJaxDone;
- Editor.onMathJaxDone = function()
- {
- editorOnMathJaxDone.apply(this, arguments);
- graphReady();
- };
- }
- ignoreChange = true;
- this.setFileData(data.xml);
- ignoreChange = false;
- if (!this.editor.graph.mathEnabled)
- {
- graphReady();
- }
- }
- else
- {
- graphReady();
- }
- }
- return;
- }
- else if (data.action == 'load')
- {
- convertToSketch = data.toSketch;
- autosave = data.autosave == 1;
- this.hideDialog();
-
- if (data.modified != null && urlParams['modified'] == null)
- {
- urlParams['modified'] = data.modified;
- }
-
- if (data.saveAndExit != null && urlParams['saveAndExit'] == null)
- {
- urlParams['saveAndExit'] = data.saveAndExit;
- }
-
- if (data.noSaveBtn != null && urlParams['noSaveBtn'] == null)
- {
- urlParams['noSaveBtn'] = data.noSaveBtn;
- }
- if (data.rough != null)
- {
- var initial = Editor.sketchMode;
- this.doSetSketchMode(data.rough);
- if (initial != Editor.sketchMode)
- {
- this.fireEvent(new mxEventObject('sketchModeChanged'));
- }
- }
- if (data.dark != null)
- {
- this.setDarkMode(data.dark);
- }
-
- if (data.border != null)
- {
- this.embedExportBorder = data.border;
- }
- if (data.background != null)
- {
- this.embedExportBackground = data.background;
- }
- if (data.viewport != null)
- {
- this.embedViewport = data.viewport;
- }
- this.embedExitPoint = null;
- if (data.rect != null)
- {
- var border = this.embedExportBorder;
- this.diagramContainer.style.left = Math.max(60, data.rect.left) + 'px';
- this.diagramContainer.style.top = Math.max(40, data.rect.top) + 'px';
- // Inline min width and height
- this.minInlineWidth = data.minWidth;
- this.minInlineHeight = data.minHeight;
- this.diagramContainer.style.border = '2px solid #295fcc';
- this.diagramContainer.style.bottom = '';
- this.diagramContainer.style.right = '';
-
- // Data is extracted diagram in async code
- var maxFitScale = data.maxFitScale;
- var w = data.rect.width + 2 * border;
- var h0 = data.rect.height + 2 * border;
- afterLoad = mxUtils.bind(this, function()
- {
- var ds = mxUtils.getDocumentSize();
- w = Math.min((this.minInlineWidth != null) ? Math.max(
- this.minInlineWidth, w) : w, ds.width - 80);
- var h = Math.min((this.minInlineHeight != null) ? Math.max(
- this.minInlineHeight, h0) : h0, ds.height - 80);
-
- this.diagramContainer.style.width = w + 'px';
- this.diagramContainer.style.height = h + 'px';
-
- var graph = this.editor.graph;
- var prev = graph.maxFitScale;
- graph.maxFitScale = maxFitScale;
-
- graph.fit(2 * border, null, null, null, null, null, h0);
- this.setPageVisible(false);
- if (this.minInlineWidth != null &&
- graph.getGraphBounds().width < this.minInlineWidth)
- {
- var dy = graph.container.scrollTop;
- this.resetScrollbars();
- graph.container.scrollTop = dy;
- }
-
- graph.maxFitScale = prev;
- graph.container.scrollTop -= border;
- graph.container.scrollLeft -= border;
- window.setTimeout(mxUtils.bind(this, function()
- {
- this.fireEvent(new mxEventObject('editInlineStart', 'data', [data]));
- graph.container.focus();
-
- // Moves format window to top of graph
- if (this.formatWindow != null &&
- this.formatWindow.window != null &&
- this.formatWindow.window.isVisible())
- {
- this.formatWindow.window.div.style.top =
- graph.container.style.top;
- }
- // Centers horizontally
- var bounds = graph.getGraphBounds();
- if (graph.container.clientWidth > bounds.width + 2 * border)
- {
- graph.container.scrollLeft = bounds.x + ((bounds.width +
- border) - graph.container.clientWidth) / 2;
- }
- }), 10);
- });
- }
-
- if (data.noExitBtn != null && urlParams['noExitBtn'] == null)
- {
- urlParams['noExitBtn'] = data.noExitBtn;
- }
-
- if (data.title != null && this.buttonContainer != null)
- {
- var tmp = this.createStatusDiv('');
- mxUtils.write(tmp, data.title);
- if (this.embedFilenameSpan != null)
- {
- this.embedFilenameSpan.parentNode.removeChild(this.embedFilenameSpan);
- }
-
- this.buttonContainer.appendChild(tmp);
- this.embedFilenameSpan = tmp;
- }
-
- try
- {
- if (data.libs)
- {
- this.sidebar.showEntries(data.libs);
- }
- }
- catch(e){}
- if (data.xmlpng != null)
- {
- data = this.extractGraphModelFromPng(data.xmlpng);
- }
- else if (data.descriptor != null)
- {
- data = data.descriptor;
- }
- else
- {
- data = data.xml;
- }
- }
- else if (data.action == 'merge')
- {
- var file = this.getCurrentFile();
-
- if (file != null)
- {
- var tmp = extractDiagramXml(data.xml);
- if (tmp != null && tmp != '')
- {
- file.mergeFile(new LocalFile(this, tmp), function()
- {
- parent.postMessage(JSON.stringify({event: 'merge', message: data}), '*');
- }, function(err)
- {
- parent.postMessage(JSON.stringify({event: 'merge', message: data, error: err}), '*');
- });
- }
- }
-
- return;
- }
- else if (data.action == 'remoteInvokeReady')
- {
- this.handleRemoteInvokeReady(parent);
- return;
- }
- else if (data.action == 'remoteInvoke')
- {
- this.handleRemoteInvoke(data, evt.origin);
- return;
- }
- else if (data.action == 'remoteInvokeResponse')
- {
- this.handleRemoteInvokeResponse(data);
- return;
- }
- else
- {
- // Unknown message must stop execution
- parent.postMessage(JSON.stringify({error: 'unknownMessage', data: JSON.stringify(data)}), '*');
-
- return;
- }
- }
- catch (e)
- {
- // TODO: Block handling of more messages when in error state
- this.handleError(e);
- }
- }
-
- var getData = mxUtils.bind(this, function()
- {
- return (urlParams['pages'] != '0' || (this.pages != null && this.pages.length > 1)) ?
- this.getFileData(true): mxUtils.getXml(this.editor.getGraphXml());
- });
-
- var doLoad = mxUtils.bind(this, function(data, evt)
- {
- ignoreChange = true;
- try
- {
- fn(data, evt, null, convertToSketch);
- }
- catch (e)
- {
- this.handleError(e);
- }
- ignoreChange = false;
-
- if (urlParams['modified'] != null)
- {
- this.editor.setStatus('');
- }
- lastData = getData();
- if (autosave && changeListener == null)
- {
- changeListener = mxUtils.bind(this, function(sender, eventObject)
- {
- var data = getData();
-
- if (data != lastData && !ignoreChange)
- {
- var msg = this.createLoadMessage('autosave');
- msg.xml = data;
- var parent = window.opener || window.parent;
- parent.postMessage(JSON.stringify(msg), '*');
- }
-
- lastData = data;
- });
-
- this.editor.graph.model.addListener(mxEvent.CHANGE, changeListener);
- // Some options trigger autosave
- this.editor.graph.addListener('gridSizeChanged', changeListener);
- this.editor.graph.addListener('shadowVisibleChanged', changeListener);
- this.addListener('pageFormatChanged', changeListener);
- this.addListener('pageScaleChanged', changeListener);
- this.addListener('backgroundColorChanged', changeListener);
- this.addListener('backgroundImageChanged', changeListener);
- this.addListener('foldingEnabledChanged', changeListener);
- this.addListener('mathEnabledChanged', changeListener);
- this.addListener('gridEnabledChanged', changeListener);
- this.addListener('guidesEnabledChanged', changeListener);
- this.addListener('pageViewChanged', changeListener);
- }
-
- // Sends the bounds of the graph to the host after parsing
- if (urlParams['returnbounds'] == '1' || urlParams['proto'] == 'json')
- {
- var resp = this.createLoadMessage('load');
-
- // Attaches XML to response
- resp.xml = data;
-
- parent.postMessage(JSON.stringify(resp), '*');
- }
- if (afterLoad != null)
- {
- afterLoad();
- }
- });
-
- if (data != null && typeof data.substring === 'function' && data.substring(0, 34) == 'data:application/vnd.visio;base64,')
- {
- // Checks VND binary magic number in base64
- var filename = (data.substring(34, 45) == '0M8R4KGxGuE') ? 'raw.vsd' : 'raw.vsdx';
-
- this.importVisio(this.base64ToBlob(data.substring(data.indexOf(',') + 1)), function(xml)
- {
- doLoad(xml, evt);
- }, mxUtils.bind(this, function(e)
- {
- this.handleError(e);
- }), filename);
- }
- else if (data != null && typeof data.substring === 'function' && new XMLHttpRequest().upload && this.isRemoteFileFormat(data, ''))
- {
- if (this.isOffline())
- {
- this.showError(mxResources.get('error'), mxResources.get('notInOffline'));
- }
- else
- {
- // Asynchronous parsing via server
- this.parseFileData(data, mxUtils.bind(this, function(xhr)
- {
- if (xhr.readyState == 4)
- {
- if (xhr.status >= 200 && xhr.status <= 299 &&
- xhr.responseText.substring(0, 13) == '<mxGraphModel')
- {
- doLoad(xhr.responseText, evt);
- }
- else
- {
- this.handleError({message: xhr.status == 413? mxResources.get('diagramTooLarge') :
- mxResources.get('unknownError')});
- }
- }
- }), '');
- }
- }
- else if (data != null && typeof data.substring === 'function' && this.isLucidChartData(data))
- {
- this.convertLucidChart(data, mxUtils.bind(this, function(xml)
- {
- doLoad(xml);
- }), mxUtils.bind(this, function(e)
- {
- this.handleError(e);
- }));
- }
- else if (data != null && typeof data === 'object' && data.format != null && (data.data != null || data.url != null))
- {
- this.loadDescriptor(data, mxUtils.bind(this, function(e)
- {
- doLoad(getData(), evt);
- }), mxUtils.bind(this, function(e)
- {
- this.handleError(e, mxResources.get('errorLoadingFile'));
- }));
- }
- else
- {
- data = extractDiagramXml(data);
- doLoad(data, evt);
- }
- }));
-
- // Requests data from the sender. This is a workaround for not allowing
- // the opener to listen for the onload event if not in the same origin.
- var parent = window.opener || window.parent;
- var msg = (urlParams['proto'] == 'json') ? JSON.stringify({event: 'init'}) : (urlParams['ready'] || 'ready');
- parent.postMessage(msg, '*');
-
- // Adds JSON event for opening links
- if (urlParams['proto'] == 'json')
- {
- var graphOpenLink = this.editor.graph.openLink;
-
- this.editor.graph.openLink = function(href, target, allowOpener)
- {
- graphOpenLink.apply(this, arguments);
-
- parent.postMessage(JSON.stringify({event: 'openLink', href: href, target: target, allowOpener: allowOpener}), '*');
- };
- }
- };
-
- /**
- * Adds the buttons for embedded mode.
- */
- EditorUi.prototype.createEmbedButton = function(title, fn, shortcut, primary)
- {
- var modern = (Editor.currentTheme == 'simple' ||
- Editor.currentTheme == 'sketch' ||
- Editor.currentTheme == 'min');
- var btn = document.createElement('button');
- btn.setAttribute('title', title + ((shortcut != null) ?
- ' (' + shortcut + ')' : ''));
- btn.style.marginLeft = '6px';
- mxUtils.write(btn, title);
- if (modern)
- {
- btn.className = ((primary) ? 'gePrimaryBtn' : '');
- btn.style.marginLeft = '8px';
- btn.style.padding = '6px';
- }
- else
- {
- btn.className = 'geBigButton' + ((!primary) ?
- ' geBigStandardButton' : '');
- }
- mxEvent.addListener(btn, 'click', fn);
- return btn;
- };
- /**
- * Adds the buttons for embedded mode.
- */
- EditorUi.prototype.addEmbedButtons = function()
- {
- var div = document.createElement('div');
- div.style.display = 'inline-flex';
- div.style.alignItems = 'center';
- div.style.marginLeft = 'auto';
- if (Editor.currentTheme != 'simple' &&
- Editor.currentTheme != 'sketch' &&
- Editor.currentTheme != 'min')
- {
- div.style.marginRight = '66px';
- div.style['float'] = 'right';
- if (Editor.currentTheme == 'atlas')
- {
- div.style.marginTop = '2px';
- }
- }
- var button = document.createElement('button');
- button.className = 'geBigButton';
- if (urlParams['noSaveBtn'] == '1')
- {
- if (urlParams['saveAndExit'] != '0')
- {
- div.appendChild(this.createEmbedButton(urlParams['publishClose'] == '1' ?
- mxResources.get('publish') : mxResources.get('saveAndExit'),
- this.actions.get('saveAndExit').funct, null, true));
- }
- }
- else
- {
- div.appendChild(this.createEmbedButton(mxResources.get('save'), mxUtils.bind(this, function()
- {
- this.actions.get('save').funct(false);
- }), Editor.ctrlKey + '+S', true));
-
- if (urlParams['saveAndExit'] == '1')
- {
- div.appendChild(this.createEmbedButton(mxResources.get('saveAndExit'),
- this.actions.get('saveAndExit').funct));
- }
- }
- if (urlParams['noExitBtn'] != '1')
- {
- div.appendChild(this.createEmbedButton(urlParams['publishClose'] == '1' ?
- mxResources.get('close') : mxResources.get('exit'),
- this.actions.get('exit').funct));
- }
- if ((Editor.currentTheme == 'simple' ||
- Editor.currentTheme == 'sketch' ||
- Editor.currentTheme == 'min') &&
- this.buttonContainer != null)
- {
- this.buttonContainer.appendChild(div);
- this.editor.fireEvent(new mxEventObject('statusChanged'));
- }
- else if (this.menubar != null)
- {
- this.toolbar.container.appendChild(div);
- this.toolbar.staticElements.push(div);
- }
- };
- /**
- *
- */
- EditorUi.prototype.showImportCsvDialog = function()
- {
- if (this.importCsvDialog == null)
- {
- this.importCsvDialog = new TextareaDialog(this, mxResources.get('csv') + ':',
- Editor.defaultCsvValue, mxUtils.bind(this, function(newValue)
- {
- this.importCsv(newValue);
- }), null, null, 620, 430, null, true, true, mxResources.get('import'),
- !this.isOffline() ? 'https://drawio-app.com/import-from-csv-to-drawio/' : null);
- }
-
- this.showDialog(this.importCsvDialog.container, 640, 520, true, true, null, null, null, null, true);
- this.importCsvDialog.init();
- };
-
- /**
- * Loads orgchart layouts and executes the given function.
- */
- EditorUi.prototype.showCustomLayoutDialog = function(value)
- {
- this.loadOrgChartLayouts(mxUtils.bind(this, function()
- {
- var dlg = new TextareaDialog(this, mxResources.get('layout'),
- value, mxUtils.bind(this, function(newValue)
- {
- if (newValue.length > 0)
- {
- try
- {
- var list = JSON.parse(newValue);
- this.executeLayouts(this.editor.graph.createLayouts(list));
- this.customLayoutConfig = list;
- this.hideDialog();
- }
- catch (e)
- {
- this.handleError(e);
- }
- }
- }), null, null, null, null, mxUtils.bind(this, function(buttons, input)
- {
- var copyBtn = mxUtils.button(mxResources.get('copy'), mxUtils.bind(this, function()
- {
- try
- {
- var orig = input.value;
- input.value = JSON.stringify(JSON.parse(orig));
- input.focus();
-
- if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5)
- {
- input.select();
- }
- else
- {
- document.execCommand('selectAll', false, null);
- }
-
- document.execCommand('copy');
- this.alert(mxResources.get('copiedToClipboard'));
- input.value = orig;
- }
- catch (e)
- {
- this.handleError(e);
- }
- }));
- copyBtn.setAttribute('title', 'copy');
- copyBtn.className = 'geBtn';
- buttons.appendChild(copyBtn);
- }), true, null, null, 'https://www.drawio.com/doc/faq/apply-layouts');
- this.showDialog(dlg.container, 620, 460, true, true);
- dlg.init();
- }));
- };
- /**
- * Loads orgchart layouts and executes the given function.
- */
- EditorUi.prototype.loadOrgChartLayouts = function(fn)
- {
- this.createTimeout(null, mxUtils.bind(this, function(timeout)
- {
- var onload = mxUtils.bind(this, function()
- {
- this.loadingOrgChart = false;
- if (timeout.clear())
- {
- Graph.layoutNames.push('mxOrgChartLayout');
- this.spinner.stop();
- fn();
- }
- });
- var onerror = mxUtils.bind(this, function(e)
- {
- this.loadingOrgChart = false;
- if (timeout.clear())
- {
- this.handleError(e);
- }
- });
- if (typeof mxOrgChartLayout === 'undefined' && !this.loadingOrgChart && !this.isOffline(true))
- {
- if (this.spinner.spin(document.body, mxResources.get('loading')))
- {
- this.loadingOrgChart = true;
- if (urlParams['dev'] == '1')
- {
- mxscript('js/orgchart/bridge.min.js', function()
- {
- mxscript('js/orgchart/bridge.collections.min.js', function()
- {
- mxscript('js/orgchart/OrgChart.Layout.min.js', function()
- {
- mxscript('js/orgchart/mxOrgChartLayout.js',
- onload, null, null, null, onerror);
- }, null, null, null, onerror);
- }, null, null, null, onerror);
- }, null, null, null, onerror);
- }
- else
- {
- mxscript(window.DRAWIO_SERVER_URL + 'js/orgchart.min.js', onload, null, null, null, onerror);
- }
- }
- }
- else
- {
- onload();
- }
- }), onerror);
- };
-
- /**
- *
- */
- EditorUi.prototype.importCsv = function(text, done)
- {
- this.loadOrgChartLayouts(mxUtils.bind(this, function()
- {
- this.doImportCsv(text, done);
- }));
- };
- /**
- *
- */
- EditorUi.prototype.doImportCsv = function(text, done)
- {
- try
- {
- var lines = text.split('\n');
- var allCells = [];
- var parents = [];
- var cells = [];
- var dups = {};
-
- if (lines.length > 0)
- {
- // Internal lookup table
- var lookups = {};
-
- // Default values
- var graph = this.editor.graph;
- var vars = null;
- var style = null;
- var styles = null;
- var stylename = null;
- var labelname = null;
- var unknownStyle = null;
- var labels = null;
- var parentstyle = 'whiteSpace=wrap;html=1;';
- var identity = null;
- var parent = null;
- var namespace = '';
- var width = 'auto';
- var height = 'auto';
- var collapsed = false;
- var left = null;
- var top = null;
- var edgespacing = 40;
- var nodespacing = 40;
- var levelspacing = 100;
- var padding = 0;
- // Delayed after optional layout
- var afterInsert = mxUtils.bind(this, function()
- {
- if (done != null)
- {
- done(select);
- }
- else
- {
- graph.setSelectionCells(select);
- graph.scrollCellToVisible(graph.getSelectionCell());
- }
-
- if (this.chromelessResize != null)
- {
- window.setTimeout(mxUtils.bind(this, function()
- {
- this.chromelessResize(true);
- }), 0);
- }
- });
-
- // Computes unscaled, untranslated graph bounds
- var pt = graph.getFreeInsertPoint();
- var x0 = pt.x;
- var y0 = pt.y;
- var y = y0;
- // Default label value depends on column names
- var label = null;
-
- // Default layout to run.
- var layout = 'auto';
-
- // Name of the attribute that contains the parent reference
- var parent = null;
-
- // Name of the attribute that contains the references for creating edges
- var edges = [];
- // Name of the column for hyperlinks
- var link = null;
-
- // String array of names to remove from metadata
- var ignore = null;
-
- // Read processing instructions first
- var index = 0;
-
- while (index < lines.length && lines[index].charAt(0) == '#')
- {
- var text = lines[index].replace(/\r$/,''); // Remove trailing \r if the file uses \r\n line breaks
- index++;
-
- while (index < lines.length && text.charAt(text.length - 1) == '\\' &&
- lines[index].charAt(0) == '#')
- {
- text = text.substring(0, text.length - 1) + mxUtils.trim(lines[index].substring(1));
- index++;
- }
-
- if (text.charAt(1) != '#')
- {
- // Processing instruction
- var idx = text.indexOf(':');
-
- if (idx > 0)
- {
- var key = mxUtils.trim(text.substring(1, idx));
- var value = mxUtils.trim(text.substring(idx + 1));
-
- if (key == 'label')
- {
- label = Graph.sanitizeHtml(value);
- }
- else if (key == 'labelname' && value.length > 0 && value != '-')
- {
- labelname = value;
- }
- else if (key == 'labels' && value.length > 0 && value != '-')
- {
- labels = JSON.parse(value);
- }
- else if (key == 'style')
- {
- style = value;
- }
- else if (key == 'parentstyle')
- {
- parentstyle = value;
- }
- else if (key == 'unknownStyle' && value != '-')
- {
- unknownStyle = value;
- }
- else if (key == 'stylename' && value.length > 0 && value != '-')
- {
- stylename = value;
- }
- else if (key == 'styles' && value.length > 0 && value != '-')
- {
- styles = JSON.parse(value);
- }
- else if (key == 'vars' && value.length > 0 && value != '-')
- {
- vars = JSON.parse(value);
- }
- else if (key == 'identity' && value.length > 0 && value != '-')
- {
- identity = value;
- }
- else if (key == 'parent' && value.length > 0 && value != '-')
- {
- parent = value;
- }
- else if (key == 'namespace' && value.length > 0 && value != '-')
- {
- namespace = value;
- }
- else if (key == 'width')
- {
- width = value;
- }
- else if (key == 'height')
- {
- height = value;
- }
- else if (key == 'collapsed' && value != '-')
- {
- collapsed = value == 'true';
- }
- else if (key == 'left' && value.length > 0)
- {
- left = value;
- }
- else if (key == 'top' && value.length > 0)
- {
- top = value;
- }
- else if (key == 'ignore')
- {
- ignore = value.split(',');
- }
- else if (key == 'connect')
- {
- edges.push(JSON.parse(value));
- }
- else if (key == 'link')
- {
- link = value;
- }
- else if (key == 'padding')
- {
- padding = parseFloat(value);
- }
- else if (key == 'edgespacing')
- {
- edgespacing = parseFloat(value);
- }
- else if (key == 'nodespacing')
- {
- nodespacing = parseFloat(value);
- }
- else if (key == 'levelspacing')
- {
- levelspacing = parseFloat(value);
- }
- else if (key == 'layout')
- {
- layout = value;
- }
- }
- }
- }
-
- if (lines[index] == null)
- {
- throw new Error(mxResources.get('invalidOrMissingFile'));
- }
-
- // Converts identity and parent to index and validates XML attribute names
- var keys = this.editor.csvToArray(lines[index].replace(/\r$/,''));
- var identityIndex = null;
- var parentIndex = null;
- var attribs = [];
-
- for (var i = 0; i < keys.length; i++)
- {
- if (identity == keys[i])
- {
- identityIndex = i;
- }
-
- if (parent == keys[i])
- {
- parentIndex = i;
- }
-
- attribs.push(mxUtils.trim(keys[i]).replace(/[^a-z0-9]+/ig, '_').
- replace(/^\d+/, '').replace(/_+$/, ''));
- }
-
- if (label == null)
- {
- label = '%' + attribs[0] + '%';
- }
-
- if (edges != null)
- {
- for (var e = 0; e < edges.length; e++)
- {
- if (lookups[edges[e].to] == null)
- {
- lookups[edges[e].to] = {};
- }
- }
- }
-
- // Parse and validate input
- var arrays = [];
-
- for (var i = index + 1; i < lines.length; i++)
- {
- var values = this.editor.csvToArray(lines[i].replace(/\r$/,''));
-
- if (values == null)
- {
- var short = (lines[i].length > 40) ? lines[i].substring(0, 40) + '...' : lines[i];
-
- throw new Error(short + ' (' + i + '):\n' + mxResources.get('containsValidationErrors'));
- }
- else if (values.length > 0)
- {
- arrays.push(values);
- }
- }
-
- graph.model.beginUpdate();
- try
- {
- for (var i = 0; i < arrays.length; i++)
- {
- var values = arrays[i];
- var cell = null;
- var id = (identityIndex != null) ? namespace + values[identityIndex] : null;
- var ignoreCell = false;
-
- if (id != null)
- {
- cell = graph.model.getCell(id);
- // Bypasses update of cells inserted during this run
- ignoreCell = cell == null || mxUtils.indexOf(
- allCells, cell) >= 0;
- }
-
- var newCell = new mxCell(label, new mxGeometry(x0, y,
- 0, 0), style || 'whiteSpace=wrap;html=1;');
- newCell.collapsed = collapsed;
- newCell.vertex = true;
- newCell.id = id;
-
- if (cell != null && !ignoreCell)
- {
- graph.model.setCollapsed(cell, collapsed);
- }
-
- for (var j = 0; j < values.length; j++)
- {
- graph.setAttributeForCell(newCell, attribs[j], values[j]);
- if (cell != null && !ignoreCell)
- {
- graph.setAttributeForCell(cell, attribs[j], values[j]);
- }
- }
-
- if (labelname != null && labels != null)
- {
- var tempLabel = labels[newCell.getAttribute(labelname)];
-
- if (tempLabel != null)
- {
- graph.labelChanged(newCell, tempLabel);
- if (cell != null && !ignoreCell)
- {
- graph.cellLabelChanged(cell, tempLabel);
- }
- }
- }
- if (stylename != null && styles != null)
- {
- var tempStyle = styles[newCell.getAttribute(stylename)];
-
- if (tempStyle != null)
- {
- newCell.style = tempStyle;
- }
- }
- graph.setAttributeForCell(newCell, 'placeholders', '1');
- newCell.style = graph.replacePlaceholders(newCell, newCell.style, vars);
- if (cell != null && !ignoreCell)
- {
- graph.model.setStyle(cell, newCell.style);
- if (mxUtils.indexOf(cells, cell) < 0)
- {
- cells.push(cell);
- }
- graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [cell]));
- }
- else
- {
- graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [newCell]));
- }
- var exists = cell != null;
- cell = newCell;
-
- if (!exists)
- {
- for (var e = 0; e < edges.length; e++)
- {
- lookups[edges[e].to][cell.getAttribute(edges[e].to)] = cell;
- }
- }
-
- if (link != null && link != 'link')
- {
- graph.setLinkForCell(cell, cell.getAttribute(link));
-
- // Removes attribute
- graph.setAttributeForCell(cell, link, null);
- }
-
- // Sets the geometry
- var size = this.editor.graph.getPreferredSizeForCell(cell);
- var parent = (parentIndex != null) ? graph.model.getCell(
- namespace + values[parentIndex]) : null;
- if (cell.vertex)
- {
- var originX = (parent != null) ? 0 : x0;
- var originY = (parent != null) ? 0 : y0;
- if (left != null && cell.getAttribute(left) != null)
- {
- cell.geometry.x = originX + parseFloat(cell.getAttribute(left));
- }
- if (top != null && cell.getAttribute(top) != null)
- {
- cell.geometry.y = originY + parseFloat(cell.getAttribute(top));
- }
- var widthValue = (width.charAt(0) == '@') ? cell.getAttribute(width.substring(1)) : null;
- if (widthValue != null && widthValue != 'auto')
- {
- cell.geometry.width = parseFloat(cell.getAttribute(width.substring(1)));
- }
- else
- {
- cell.geometry.width = (width == 'auto' || widthValue == 'auto') ?
- size.width + padding : parseFloat(width);
- }
- var heightValue = (height.charAt(0) == '@') ? cell.getAttribute(height.substring(1)) : null;
-
- if (heightValue != null && heightValue != 'auto' && height != 'width')
- {
- cell.geometry.height = parseFloat(heightValue);
- }
- else
- {
- cell.geometry.height = (height == 'auto' || heightValue == 'auto') ?
- size.height + padding : (height == 'width' ?
- cell.geometry.width : parseFloat(height));
- }
-
- y += cell.geometry.height + nodespacing;
- }
-
- if (!exists)
- {
- allCells.push(cell);
-
- if (parent != null)
- {
- parent.style = graph.replacePlaceholders(parent, parentstyle, vars);
- graph.addCell(cell, parent);
- parents.push(parent);
- }
- else
- {
- cells.push(graph.addCell(cell));
- }
- }
- else
- {
- if (dups[id] == null)
- {
- dups[id] = [];
- }
-
- dups[id].push(cell);
- }
- }
- // Process parents for autosize
- for (var i = 0; i < parents.length; i++)
- {
- var widthValue = (width.charAt(0) == '@') ? parents[i].getAttribute(width.substring(1)) : null;
- var heightValue = (height.charAt(0) == '@') ? parents[i].getAttribute(height.substring(1)) : null;
- if ((width == 'auto' || widthValue == 'auto') &&
- (height == 'auto' || heightValue == 'auto'))
- {
- graph.updateGroupBounds([parents[i]], padding, true);
- }
- }
- var roots = cells.slice();
- var select = cells.slice();
-
- for (var e = 0; e < edges.length; e++)
- {
- var edge = edges[e];
-
- for (var i = 0; i < allCells.length; i++)
- {
- var cell = allCells[i];
-
- var insertEdge = mxUtils.bind(this, function(realCell, dataCell, edge)
- {
- var tmp = dataCell.getAttribute(edge.from);
-
- if (tmp != null && tmp != '')
- {
- var refs = tmp.split(',');
-
- for (var j = 0; j < refs.length; j++)
- {
- var ref = lookups[edge.to][refs[j]];
- if (ref == null && unknownStyle != null)
- {
- ref = new mxCell(refs[j], new mxGeometry(x0, y0, 0, 0), unknownStyle);
- ref.style = graph.replacePlaceholders(dataCell, ref.style, vars);
- var refSize = this.editor.graph.getPreferredSizeForCell(ref);
- ref.geometry.width = refSize.width + padding;
- ref.geometry.height = refSize.height + padding;
- lookups[edge.to][refs[j]] = ref;
- ref.vertex = true;
- ref.id = refs[j];
- cells.push(graph.addCell(ref));
- }
- if (ref != null)
- {
- var label = edge.label;
-
- if (edge.fromlabel != null)
- {
- label = (dataCell.getAttribute(edge.fromlabel) || '') + (label || '');
- }
-
- if (edge.sourcelabel != null)
- {
- label = graph.replacePlaceholders(dataCell,
- edge.sourcelabel, vars) + (label || '');
- }
-
- if (edge.tolabel != null)
- {
- label = (label || '') + (ref.getAttribute(edge.tolabel) || '');
- }
-
- if (edge.targetlabel != null)
- {
- label = (label || '') + graph.replacePlaceholders(
- ref, edge.targetlabel, vars);
- }
-
- var placeholders = ((edge.placeholders == 'target') ==
- !edge.invert) ? ref : realCell;
- var edgeStyle = (edge.style != null) ?
- graph.replacePlaceholders(placeholders, edge.style, vars) :
- graph.createCurrentEdgeStyle();
- var edgeCell = graph.insertEdge(null, null, label || '', (edge.invert) ?
- ref : realCell, (edge.invert) ? realCell : ref, edgeStyle);
-
- // Adds additional edge labels
- if (edge.labels != null)
- {
- for (var k = 0; k < edge.labels.length; k++)
- {
- var def = edge.labels[k];
- var elx = (def.x != null) ? def.x : 0;
- var ely = (def.y != null) ? def.y : 0;
- var st = 'resizable=0;html=1;';
- var el = new mxCell(def.label || k,
- new mxGeometry(elx, ely, 0, 0), st);
- el.vertex = true;
- el.connectable = false;
- el.geometry.relative = true;
-
- if (def.placeholders != null)
- {
- el.value = graph.replacePlaceholders(
- ((def.placeholders == 'target') ==
- !edge.invert) ? ref : realCell,
- el.value, vars)
- }
-
- if (def.dx != null || def.dy != null)
- {
- el.geometry.offset = new mxPoint(
- (def.dx != null) ? def.dx : 0,
- (def.dy != null) ? def.dy : 0);
- }
-
- edgeCell.insert(el);
- }
- }
- // Adds edge metadata
- if (edge.data != null)
- {
- for (var key in edge.data)
- {
- graph.setAttributeForCell(edgeCell, key, edge.data[key]);
- }
- }
-
- select.push(edgeCell);
- mxUtils.remove((edge.invert) ? realCell : ref, roots);
- }
- }
- }
- });
-
- insertEdge(cell, cell, edge);
- // Checks more entries
- if (dups[cell.id] != null)
- {
- for (var j = 0; j < dups[cell.id].length; j++)
- {
- insertEdge(cell, dups[cell.id][j], edge);
- }
- }
- }
- }
-
- // Removes ignored attributes after processing above
- if (ignore != null)
- {
- for (var i = 0; i < allCells.length; i++)
- {
- var cell = allCells[i];
-
- for (var j = 0; j < ignore.length; j++)
- {
- graph.setAttributeForCell(cell, mxUtils.trim(ignore[j]), null);
- }
- }
- }
-
- if (cells.length > 0)
- {
- var edgeLayout = new mxParallelEdgeLayout(graph);
- edgeLayout.spacing = edgespacing;
- edgeLayout.checkOverlap = true;
-
- var postProcess = function()
- {
- if (edgeLayout.spacing > 0)
- {
- edgeLayout.execute(graph.getDefaultParent());
- }
-
- // Aligns cells to grid and/or rounds positions
- for (var i = 0; i < cells.length; i++)
- {
- var geo = graph.getCellGeometry(cells[i]);
- geo.x = Math.round(graph.snap(geo.x));
- geo.y = Math.round(graph.snap(geo.y));
-
- if (width == 'auto')
- {
- geo.width = Math.round(graph.snap(geo.width));
- }
-
- if (height == 'auto')
- {
- geo.height = Math.round(graph.snap(geo.height));
- }
- }
- };
-
- if (layout.charAt(0) == '[')
- {
- // Required for layouts to work with new cells
- var temp = afterInsert;
- graph.view.validate();
- this.executeLayouts(graph.createLayouts(JSON.parse(layout)), function()
- {
- postProcess();
- temp();
- });
- afterInsert = null;
- }
- else if (layout == 'circle')
- {
- var circleLayout = new mxCircleLayout(graph);
- circleLayout.disableEdgeStyle = false;
- circleLayout.resetEdges = false;
-
- var circleLayoutIsVertexIgnored = circleLayout.isVertexIgnored;
-
- // Ignore other cells
- circleLayout.isVertexIgnored = function(vertex)
- {
- return circleLayoutIsVertexIgnored.apply(this, arguments) ||
- mxUtils.indexOf(cells, vertex) < 0;
- };
-
- this.executeLayout(function()
- {
- circleLayout.execute(graph.getDefaultParent());
- postProcess();
- }, true, afterInsert);
-
- afterInsert = null;
- }
- else if (layout == 'horizontaltree' || layout == 'verticaltree' ||
- (layout == 'auto' && select.length == 2 * cells.length - 1 && roots.length == 1))
- {
- // Required for layouts to work with new cells
- graph.view.validate();
-
- var treeLayout = new mxCompactTreeLayout(graph, layout == 'horizontaltree');
- treeLayout.levelDistance = nodespacing;
- treeLayout.edgeRouting = false;
- treeLayout.resetEdges = false;
- treeLayout.sortEdges = true;
-
- this.executeLayout(function()
- {
- treeLayout.execute(graph.getDefaultParent(), (roots.length > 0) ? roots[0] : null);
- }, true, afterInsert);
-
- afterInsert = null;
- }
- else if (layout == 'horizontalflow' || layout == 'verticalflow' ||
- (layout == 'auto' && roots.length == 1))
- {
- // Required for layouts to work with new cells
- graph.view.validate();
-
- var flowLayout = new mxHierarchicalLayout(graph,
- (layout == 'horizontalflow') ?
- mxConstants.DIRECTION_WEST :
- mxConstants.DIRECTION_NORTH);
- flowLayout.intraCellSpacing = nodespacing;
- flowLayout.parallelEdgeSpacing = edgespacing;
- flowLayout.interRankCellSpacing = levelspacing;
- flowLayout.disableEdgeStyle = false;
-
- this.executeLayout(function()
- {
- flowLayout.execute(graph.getDefaultParent(), select);
-
- // Workaround for flow layout moving cells to origin
- graph.moveCells(select, x0, y0);
- }, true, afterInsert);
-
- afterInsert = null;
- }
- else if (layout == 'orgchart')
- {
- // Required for layouts to work with new cells
- graph.view.validate();
-
- var orgChartLayout = new mxOrgChartLayout(graph,
- 2, levelspacing, nodespacing);
-
- var orgChartLayoutIsVertexIgnored = orgChartLayout.isVertexIgnored;
-
- // Ignore other cells
- orgChartLayout.isVertexIgnored = function(vertex)
- {
- return orgChartLayoutIsVertexIgnored.apply(this, arguments) ||
- mxUtils.indexOf(cells, vertex) < 0;
- };
-
- this.executeLayout(function()
- {
- orgChartLayout.execute(graph.getDefaultParent());
- postProcess();
- }, true, afterInsert);
-
- afterInsert = null;
- }
- else if (layout == 'organic' || (layout == 'auto' &&
- select.length > cells.length))
- {
- // Required for layouts to work with new cells
- graph.view.validate();
-
- var organicLayout = new mxFastOrganicLayout(graph);
- organicLayout.forceConstant = nodespacing * 3;
- organicLayout.disableEdgeStyle = false;
- organicLayout.resetEdges = false;
-
- var organicLayoutIsVertexIgnored = organicLayout.isVertexIgnored;
-
- // Ignore other cells
- organicLayout.isVertexIgnored = function(vertex)
- {
- return organicLayoutIsVertexIgnored.apply(this, arguments) ||
- mxUtils.indexOf(cells, vertex) < 0;
- };
-
- this.executeLayout(function()
- {
- organicLayout.execute(graph.getDefaultParent());
- postProcess();
- }, true, afterInsert);
-
- afterInsert = null;
- }
- }
-
- this.hideDialog();
- }
- finally
- {
- graph.model.endUpdate();
- }
-
- if (afterInsert != null)
- {
- afterInsert();
- }
- }
- }
- catch (e)
- {
- this.handleError(e);
- }
- };
- /**
- * Translates this point by the given vector.
- *
- * @param {number} dx X-coordinate of the translation.
- * @param {number} dy Y-coordinate of the translation.
- */
- EditorUi.prototype.getSearch = function(exclude)
- {
- var result = '';
-
- if (urlParams['offline'] != '1' && urlParams['demo'] != '1' && exclude != null && window.location.search.length > 0)
- {
- var amp = '?';
-
- for (var key in urlParams)
- {
- if (mxUtils.indexOf(exclude, key) < 0 && urlParams[key] != null)
- {
- result += amp + key + '=' + urlParams[key];
- amp = '&';
- }
- }
- }
- else
- {
- result = window.location.search;
- }
-
- return result;
- };
- /**
- * Returns the URL for a copy of this editor with no state.
- */
- EditorUi.prototype.getUrl = function(pathname)
- {
- var href = (pathname != null) ? pathname : window.location.pathname;
- var parms = (href.indexOf('?') > 0) ? 1 : 0;
- if (urlParams['offline'] == '1')
- {
- href += window.location.search;
- }
- else
- {
- var ignored = ['tmp', 'libs', 'clibs', 'state', 'fileId', 'code', 'share', 'notitle',
- 'data', 'url', 'embed', 'client', 'create', 'title', 'splash'];
-
- // Removes template URL parameter for new blank diagram
- for (var key in urlParams)
- {
- if (mxUtils.indexOf(ignored, key) < 0)
- {
- if (parms == 0)
- {
- href += '?';
- }
- else
- {
- href += '&';
- }
-
- if (urlParams[key] != null)
- {
- href += key + '=' + urlParams[key];
- parms++;
- }
- }
- }
- }
- return href;
- };
- /**
- * Overrides link dialog.
- */
- EditorUi.prototype.showLinkDialog = function(value, btnLabel, fn, showNewWindowOption, linkTarget)
- {
- var dlg = new LinkDialog(this, value, btnLabel, fn, true, showNewWindowOption, linkTarget);
- this.showDialog(dlg.container, 440, 120, true, true);
- dlg.init();
- };
-
- /**
- * Returns the number of storage options enabled
- */
- EditorUi.prototype.getServiceCount = function(allowBrowser)
- {
- var serviceCount = 1;
-
- if (this.drive != null || typeof window.DriveClient === 'function')
- {
- serviceCount++
- }
-
- if (this.dropbox != null || typeof window.DropboxClient === 'function')
- {
- serviceCount++
- }
- if (this.oneDrive != null || typeof window.OneDriveClient === 'function')
- {
- serviceCount++
- }
-
- if (this.gitHub != null)
- {
- serviceCount++
- }
-
- if (this.gitLab != null)
- {
- serviceCount++
- }
-
- if (allowBrowser && isLocalStorage && urlParams['browser'] == '1')
- {
- serviceCount++
- }
-
- return serviceCount;
- }
- /**
- * Updates action and menu states depending on the file.
- */
- EditorUi.prototype.updateUi = function()
- {
- this.updateButtonContainer();
- this.updateActionStates();
-
- // Action states that only need update for new files
- var file = this.getCurrentFile();
- var active = file != null || (urlParams['embed'] == '1' &&
- this.editor.graph.isEnabled());
- this.menus.get('viewPanels').setEnabled(active);
- this.menus.get('viewZoom').setEnabled(active);
-
- var restricted = (urlParams['embed'] != '1' || urlParams['embedRT'] == '1' ||
- !this.editor.graph.isEnabled()) &&
- (file == null || file.isRestricted());
- this.actions.get('makeCopy').setEnabled(!restricted);
- this.actions.get('print').setEnabled(!restricted);
- this.menus.get('exportAs').setEnabled(!restricted);
- this.menus.get('embed').setEnabled(!restricted);
-
- // Disables libraries and extras menu in embed mode
- // while waiting for file data
- var libsEnabled = urlParams['embed'] != '1' ||
- this.editor.graph.isEnabled();
- this.menus.get('extras').setEnabled(libsEnabled);
-
- if (Editor.enableCustomLibraries)
- {
- this.menus.get('openLibraryFrom').setEnabled(libsEnabled);
- this.menus.get('newLibrary').setEnabled(libsEnabled);
- }
-
- // Disables actions in the toolbar
- var editable = (urlParams['embed'] == '1' &&
- this.editor.graph.isEnabled()) ||
- (file != null && file.isEditable());
- this.actions.get('image').setEnabled(active);
- this.actions.get('zoomIn').setEnabled(active);
- this.actions.get('zoomOut').setEnabled(active);
- this.actions.get('smartFit').setEnabled(active);
- this.actions.get('resetView').setEnabled(active);
- this.actions.get('darkMode').setEnabled(Editor.currentTheme != 'atlas');
- this.actions.get('lightMode').setEnabled(Editor.currentTheme != 'atlas');
- var autoModeAction = this.actions.get('autoMode');
- autoModeAction.setEnabled(autoModeAction.isEnabled() && Editor.currentTheme != 'atlas');
-
- // Disables menus
- this.menus.get('edit').setEnabled(active);
- this.menus.get('view').setEnabled(active);
- this.menus.get('importFrom').setEnabled(editable);
- this.menus.get('arrange').setEnabled(editable);
-
- // Disables connection drop downs in toolbar
- if (this.toolbar != null)
- {
- if (this.toolbar.edgeShapeMenu != null)
- {
- this.toolbar.edgeShapeMenu.setEnabled(editable);
- }
-
- if (this.toolbar.edgeStyleMenu != null)
- {
- this.toolbar.edgeStyleMenu.setEnabled(editable);
- }
- }
- this.updateUserElement();
- };
-
- /**
- * Hook for subclassers
- */
- EditorUi.prototype.updateButtonContainer = function()
- {
- // do nothing
- };
-
- /**
- * Hook for subclassers
- */
- EditorUi.prototype.updateUserElement = function()
- {
- // do nothing
- };
-
- /**
- * Hook for subclassers
- */
- EditorUi.prototype.scheduleSanityCheck = function()
- {
- // do nothing
- };
-
- /**
- * Hook for subclassers
- */
- EditorUi.prototype.stopSanityCheck = function()
- {
- // do nothing
- };
- /**
- * Returns true if a diagram is cative and editable.
- */
- EditorUi.prototype.isDiagramActive = function()
- {
- var file = this.getCurrentFile();
-
- return (file != null && file.isEditable()) ||
- (urlParams['embed'] == '1' && this.editor.graph.isEnabled());
- };
- /**
- * Extends sidebar construction to add listeners for theme changes.
- */
- var editorUiCreateSidebar = EditorUi.prototype.createSidebar;
- EditorUi.prototype.createSidebar = function(container)
- {
- var sidebar = editorUiCreateSidebar.apply(this, arguments);
- var refreshSidebar = mxUtils.bind(this, function()
- {
- sidebar.refresh();
- this.restoreOpenLibraries();
- });
- this.addListener('darkModeChanged', mxUtils.bind(this, function()
- {
- if (!Editor.enableCssDarkMode)
- {
- refreshSidebar();
- }
- }));
- this.addListener('sketchModeChanged', refreshSidebar);
- this.addListener('currentThemeChanged', refreshSidebar);
-
- return sidebar;
- };
- /**
- * Extends sidebar construction to add listeners for theme changes.
- */
- EditorUi.prototype.restoreOpenLibraries = function()
- {
- var temp = this.openLibraries;
- this.openLibraries = null;
- if (temp != null)
- {
- for (var i = 0; i < temp.length; i++)
- {
- this.libraryLoaded(temp[i].file, temp[i].images,
- temp[i].title, (temp[i].div != null) ?
- temp[i].div.style.display != 'none' :
- temp[i].expand);
- }
- }
- };
- /**
- * Updates action states depending on the selection.
- */
- var editorUiUpdateActionStates = EditorUi.prototype.updateActionStates;
- EditorUi.prototype.updateActionStates = function()
- {
- editorUiUpdateActionStates.apply(this, arguments);
- var graph = this.editor.graph;
- var file = this.getCurrentFile();
- var ss = this.getSelectionState();
- var active = this.isDiagramActive();
- var editable = (urlParams['embed'] == '1' &&
- this.editor.graph.isEnabled()) ||
- (file != null && file.isEditable());
-
- this.actions.get('undo').setEnabled(this.canUndo() && editable);
- this.actions.get('redo').setEnabled(this.canRedo() && editable);
- this.actions.get('autosave').setEnabled(file != null && file.isEditable() && file.isAutosaveOptional());
- this.actions.get('guides').setEnabled(active);
- this.actions.get('editData').setEnabled(graph.isEnabled());
- this.actions.get('editConnectionPoints').setEnabled(active && ss.edges.length == 0 && ss.vertices.length == 1);
- this.actions.get('editImage').setEnabled(active && ss.image && ss.cells.length > 0);
- this.actions.get('crop').setEnabled(active && ss.image && ss.cells.length > 0);
- this.actions.get('pageSetup').setEnabled(active);
- this.actions.get('shadowVisible').setEnabled(active);
- this.actions.get('connectionArrows').setEnabled(active);
- this.actions.get('connectionPoints').setEnabled(active);
- this.actions.get('copyStyle').setEnabled(active && !graph.isSelectionEmpty());
- this.actions.get('pasteStyle').setEnabled(this.copiedStyle != null && active && ss.cells.length > 0);
- this.actions.get('editGeometry').setEnabled(ss.vertices.length > 0);
- this.actions.get('createShape').setEnabled(active);
- this.actions.get('createRevision').setEnabled(active);
- this.actions.get('moveToFolder').setEnabled(file != null);
- this.actions.get('makeCopy').setEnabled(file != null && !file.isRestricted());
- this.actions.get('editDiagram').setEnabled(active && (file == null || !file.isRestricted()));
- this.actions.get('publishLink').setEnabled(file != null && !file.isRestricted());
- this.actions.get('removeFormat').setEnabled(graph.isEnabled() &&
- !graph.isSelectionEmpty() && !graph.isEditing());
- this.actions.get('tags').setEnabled(this.diagramContainer.style.visibility != 'hidden');
- this.actions.get('layers').setEnabled(this.diagramContainer.style.visibility != 'hidden');
- this.actions.get('outline').setEnabled(this.diagramContainer.style.visibility != 'hidden');
- this.actions.get('rename').setEnabled((file != null && file.isRenamable()) || urlParams['embed'] == '1');
- this.actions.get('close').setEnabled(file != null);
- this.actions.get('properties').setEnabled(file != null);
- this.menus.get('publish').setEnabled(file != null && !file.isRestricted());
-
- var findReplace = this.actions.get('findReplace');
- findReplace.setEnabled(this.diagramContainer.style.visibility != 'hidden');
- findReplace.label = mxResources.get('find') + ((graph.isEnabled()) ?
- '/' + mxResources.get('replace') : '');
-
- var state = graph.view.getState(graph.getSelectionCell());
- this.actions.get('editShape').setEnabled(active && state != null &&
- state.shape != null && state.shape.stencil != null);
- };
- /**
- * Overridden to remove export dialog in chromeless lightbox.
- */
- var editoUiDestroy = EditorUi.prototype.destroy;
- EditorUi.prototype.destroy = function()
- {
- if (this.exportDialog != null)
- {
- this.exportDialog.parentNode.removeChild(this.exportDialog);
- this.exportDialog = null;
- }
-
- editoUiDestroy.apply(this, arguments);
- };
-
- /**
- * Overrides export dialog for using ui functions for save and setting global switches.
- */
- if (window.ExportDialog != null)
- {
- ExportDialog.showXmlOption = false;
- ExportDialog.showGifOption = false;
-
- ExportDialog.exportFile = function(editorUi, name, format, bg, s, b, dpi, grid)
- {
- var graph = editorUi.editor.graph;
-
- if (format == 'xml')
- {
- editorUi.hideDialog();
- editorUi.saveData(name, 'xml', mxUtils.getXml(editorUi.editor.getGraphXml()), 'text/xml');
- }
- else if (format == 'svg')
- {
- editorUi.hideDialog();
- editorUi.saveData(name, 'svg', mxUtils.getXml(graph.getSvg(bg, s, b)), 'image/svg+xml');
- }
- else
- {
- var data = editorUi.getFileData(true, null, null, null, null, true);
- var bounds = graph.getGraphBounds();
- var w = Math.floor(bounds.width * s / graph.view.scale);
- var h = Math.floor(bounds.height * s / graph.view.scale);
-
- if (data.length <= MAX_REQUEST_SIZE && w * h < MAX_AREA)
- {
- editorUi.hideDialog();
-
- if ((format == 'png' || format == 'jpg' || format == 'jpeg') && editorUi.isExportToCanvas())
- {
- if (format == 'png')
- {
- editorUi.exportImage(s, bg == null || bg == 'none', true,
- false, false, b, true, false, null, grid, dpi);
- }
- else
- {
- editorUi.exportImage(s, false, true,
- false, false, b, true, false, 'jpeg', grid);
- }
- }
- else
- {
- var extras = {globalVars: graph.getExportVariables()};
-
- if (grid)
- {
- extras.grid = {
- size: graph.gridSize,
- steps: graph.view.gridSteps,
- color: graph.view.gridColor
- };
- }
-
- editorUi.saveRequest(name, format,
- function(newTitle, base64)
- {
- return new mxXmlRequest(EXPORT_URL, 'format=' + format + '&base64=' + (base64 || '0') +
- ((newTitle != null) ? '&filename=' + encodeURIComponent(newTitle) : '') +
- '&extras=' + encodeURIComponent(JSON.stringify(extras)) +
- (dpi > 0? '&dpi=' + dpi : '') +
- '&bg=' + ((bg != null) ? bg : 'none') + '&w=' + w + '&h=' + h +
- '&border=' + b + '&xml=' + encodeURIComponent(data));
- });
- }
- }
- else
- {
- mxUtils.alert(mxResources.get('drawingTooLarge'));
- }
- }
- };
- }
- EditorUi.prototype.getDiagramTextContent = function()
- {
- this.editor.graph.setEnabled(false);
- var graph = this.editor.graph;
-
- var allPagesTxt = '';
-
- if (this.pages != null)
- {
- for (var i = 0; i < this.pages.length; i++)
- {
- var pageGraph = graph;
-
- if (this.currentPage != this.pages[i])
- {
- pageGraph = this.createTemporaryGraph(graph.getStylesheet());
- this.updatePageRoot(this.pages[i]);
- pageGraph.model.setRoot(this.pages[i].root);
- }
- allPagesTxt += this.pages[i].getName() + ' ' + pageGraph.getIndexableText() + ' ';
- }
- }
- else
- {
- allPagesTxt = graph.getIndexableText();
- }
-
- this.editor.graph.setEnabled(true);
- return allPagesTxt;
- };
-
- EditorUi.prototype.showRemotelyStoredLibrary = function(title)
- {
- var selectedLibs = {};
- var div = document.createElement('div');
- div.style.whiteSpace = 'nowrap';
- var graph = this.editor.graph;
-
- var hd = document.createElement('h3');
- mxUtils.write(hd, mxUtils.htmlEntities(title));
- hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:12px';
- div.appendChild(hd);
- var libsSection = document.createElement('div');
- libsSection.style.cssText = 'border:1px solid lightGray;overflow: auto;height:300px';
- libsSection.innerHTML = '<div style="text-align:center;padding:8px;"><img src="' + IMAGE_PATH + '/spin.gif"></div>';
-
- var loadedLibs = {};
-
- try
- {
- var custLibs = mxSettings.getCustomLibraries();
-
- for (var j = 0; j < custLibs.length; j++)
- {
- var l = custLibs[j];
-
- if (l.substring(0, 1) == 'R')
- {
- var libDesc = JSON.parse(decodeURIComponent(l.substring(1)));
- loadedLibs[libDesc[0]] = {
- id: libDesc[0],
- title: libDesc[1],
- downloadUrl: libDesc[2]
- };
- }
- }
- }
- catch(e){}
- this.remoteInvoke('getCustomLibraries', null, null, function(libsList)
- {
- libsSection.innerText = '';
-
- if (libsList.length == 0)
- {
- libsSection.innerHTML = '<div style="text-align:center;padding-top:20px;color:gray;">' +
- mxUtils.htmlEntities(mxResources.get('noLibraries')) + '</div>';
- }
- else
- {
- for (var i = 0; i < libsList.length; i++)
- {
- var lib = libsList[i];
-
- if (loadedLibs[lib.id])
- {
- selectedLibs[lib.id] = lib;
- }
-
- var libCheck = this.addCheckbox(libsSection, lib.title, loadedLibs[lib.id]);
-
- (function(lib2, check)
- {
- mxEvent.addListener(check, 'change', function()
- {
- if (this.checked)
- {
- selectedLibs[lib2.id] = lib2;
- }
- else
- {
- delete selectedLibs[lib2.id];
- }
- });
- })(lib, libCheck)
- }
- }
- }, mxUtils.bind(this, function(e)
- {
- libsSection.innerText = '';
- var status = document.createElement('div');
- status.style.padding = '8px';
- status.style.textAlign = 'center';
- mxUtils.write(status, mxResources.get('error') + ': ');
- mxUtils.write(status, (e != null && e.message != null) ?
- e.message : mxResources.get('unknownError'));
- libsSection.appendChild(status);
- }));
- div.appendChild(libsSection);
-
- var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
- {
- this.spinner.spin(document.body, mxResources.get('loading'));
- var pendingLibs = 0;
-
- for (var id in selectedLibs)
- {
- if (loadedLibs[id] != null) continue; //already loaded!
-
- pendingLibs++;
-
- (mxUtils.bind(this, function(lib)
- {
- this.remoteInvoke('getFileContent', [lib.downloadUrl], null, mxUtils.bind(this, function(libContent)
- {
- pendingLibs--;
-
- if (pendingLibs == 0) this.spinner.stop();
-
- try
- {
- this.loadLibrary(new RemoteLibrary(this, libContent, lib));
- this.showSidebar();
- }
- catch (e)
- {
- this.handleError(e, mxResources.get('errorLoadingFile'));
- }
- }), mxUtils.bind(this, function()
- {
- pendingLibs--;
-
- if (pendingLibs == 0) this.spinner.stop();
-
- this.handleError(null, mxResources.get('errorLoadingFile'));
- }));
- }))(selectedLibs[id]);
- }
-
- for (var id in loadedLibs)
- {
- if (!selectedLibs[id]) //Removed
- {
- this.closeLibrary(new RemoteLibrary(this, null, loadedLibs[id])); //create a dummy library such that we can call closeLibrary
- }
- }
-
- if (pendingLibs == 0) this.spinner.stop();
- }), null, null, 'https://www.drawio.com/doc/faq/custom-libraries-confluence-cloud');
- this.showDialog(dlg.container, 340, 390, true, true, null, null, null, null, true);
- };
-
- //Remote invokation, currently limited to functions in EditorUi (and its sub objects) for security reasons
- //White-listed functions and some info about it
- EditorUi.prototype.remoteInvokableFns = {
- getDiagramTextContent: {isAsync: false},
- getLocalStorageFile: {isAsync: false, allowedDomains: ['app.diagrams.net']},
- getLocalStorageFileNames: {isAsync: false, allowedDomains: ['app.diagrams.net']},
- setMigratedFlag: {isAsync: false, allowedDomains: ['app.diagrams.net']}
- };
-
- EditorUi.prototype.remoteInvokeCallbacks = [];
- EditorUi.prototype.remoteInvokeQueue = [];
- EditorUi.prototype.handleRemoteInvokeReady = function(remoteWin)
- {
- this.remoteWin = remoteWin;
-
- for (var i = 0; i < this.remoteInvokeQueue.length; i++)
- {
- remoteWin.postMessage(this.remoteInvokeQueue[i], '*');
- }
-
- this.remoteInvokeQueue = [];
- };
-
- EditorUi.prototype.handleRemoteInvokeResponse = function(msg)
- {
- var msgMarkers = msg.msgMarkers;
- var callback = this.remoteInvokeCallbacks[msgMarkers.callbackId];
-
- if (callback == null)
- {
- throw new Error('No callback for ' + ((msgMarkers != null) ? msgMarkers.callbackId : 'null'));
- }
- else if (msg.error)
- {
- if (callback.error) callback.error(msg.error.errResp);
- }
- else if (callback.callback)
- {
- callback.callback.apply(this, msg.resp);
- }
-
- this.remoteInvokeCallbacks[msgMarkers.callbackId] = null; //set it to null only to keep the index
- };
- EditorUi.prototype.remoteInvoke = function(remoteFn, remoteFnArgs, msgMarkers, callback, error)
- {
- var acceptResponse = true;
-
- var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
- {
- acceptResponse = false;
- error({code: App.ERROR_TIMEOUT, message: mxResources.get('timeout')});
- }), this.timeout);
- var wrapper = mxUtils.bind(this, function()
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- callback.apply(this, arguments);
- }
- });
-
- var errWrapper = mxUtils.bind(this, function()
- {
- window.clearTimeout(timeoutThread);
-
- if (acceptResponse)
- {
- error.apply(this, arguments);
- }
- });
-
- msgMarkers = msgMarkers || {};
- msgMarkers.callbackId = this.remoteInvokeCallbacks.length;
- this.remoteInvokeCallbacks.push({callback: wrapper, error: errWrapper});
- var msg = JSON.stringify({event: 'remoteInvoke', funtionName: remoteFn, functionArgs: remoteFnArgs, msgMarkers: msgMarkers});
-
- if (this.remoteWin != null) //remote invoke is ready
- {
- this.remoteWin.postMessage(msg, '*');
- }
- else
- {
- this.remoteInvokeQueue.push(msg);
- }
- };
- EditorUi.prototype.handleRemoteInvoke = function(msg, origin)
- {
- var sendResponse = mxUtils.bind(this, function(resp, error)
- {
- var respMsg = {event: 'remoteInvokeResponse', msgMarkers: msg.msgMarkers};
-
- if (error != null)
- {
- respMsg.error = {errResp: error};
- }
- else if (resp != null)
- {
- respMsg.resp = resp;
- }
-
- this.remoteWin.postMessage(JSON.stringify(respMsg), '*');
- });
-
- try
- {
- //Remote invoke are allowed to call functions in AC
- var funtionName = msg.funtionName;
- var functionInfo = this.remoteInvokableFns[funtionName];
-
- if (functionInfo != null && typeof this[funtionName] === 'function')
- {
- if (functionInfo.allowedDomains)
- {
- var allowed = false;
-
- for (var i = 0; i < functionInfo.allowedDomains.length; i++)
- {
- if (origin == 'https://' + functionInfo.allowedDomains[i])
- {
- allowed = true;
- break;
- }
- }
-
- if (!allowed)
- {
- sendResponse(null, 'Invalid Call: ' + funtionName + ' is not allowed.');
- return;
- }
- }
-
- var functionArgs = msg.functionArgs;
-
- //Confirm functionArgs are not null and is array, otherwise, discard it
- if (!Array.isArray(functionArgs))
- {
- functionArgs = [];
- }
-
- //for functions with callbacks (async) we assume last two arguments are success, error
- if (functionInfo.isAsync)
- {
- //success
- functionArgs.push(function()
- {
- sendResponse(Array.prototype.slice.apply(arguments));
- });
-
- //error
- functionArgs.push(function(err)
- {
- sendResponse(null, err || 'Unkown Error');
- });
-
- this[funtionName].apply(this, functionArgs);
- }
- else
- {
- var resp = this[funtionName].apply(this, functionArgs);
-
- sendResponse([resp]);
- }
- }
- else
- {
- sendResponse(null, 'Invalid Call: ' + funtionName + ' is not found.');
- }
- }
- catch(e)
- {
- sendResponse(null, 'Invalid Call: An error occurred, ' + e.message);
- }
- };
-
- /**
- * Opens the application keystore.
- */
- EditorUi.prototype.openDatabase = function(success, error)
- {
- if (this.database == null)
- {
- var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB;
-
- if (indexedDB != null)
- {
- try
- {
- var req = indexedDB.open('database', 2);
-
- req.onupgradeneeded = function(e)
- {
- try
- {
- var db = req.result;
-
- if (e.oldVersion < 1)
- {
- // Version 1 is the first version of the database.
- db.createObjectStore('objects', {keyPath: 'key'});
- }
-
- if (e.oldVersion < 2)
- {
- // Version 2 introduces browser file storage.
- db.createObjectStore('files', {keyPath: 'title'});
- db.createObjectStore('filesInfo', {keyPath: 'title'});
- EditorUi.migrateStorageFiles = isLocalStorage;
- }
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }
-
- req.onsuccess = mxUtils.bind(this, function(e)
- {
- try
- {
- var db = req.result;
- this.database = db;
- if (EditorUi.migrateStorageFiles)
- {
- StorageFile.migrate(db);
- EditorUi.migrateStorageFiles = false;
- }
- if (location.host == 'app.diagrams.net' && !this.drawioMigrationStarted)
- {
- this.drawioMigrationStarted = true;
-
- this.getDatabaseItem('.drawioMigrated3', mxUtils.bind(this, function(value)
- {
- if (value && urlParams['forceMigration'] != '1') //Already migrated
- {
- return;
- }
-
- var drawioFrame = document.createElement('iframe');
- drawioFrame.style.display = 'none';
- drawioFrame.setAttribute('src', 'https://www.draw.io?embed=1&proto=json&forceMigration=' + urlParams['forceMigration']);
- document.body.appendChild(drawioFrame);
- var collectNames = true, allDone = false;
- var fileNames, index = 0;
-
- var markAsMigrated = mxUtils.bind(this, function()
- {
- allDone = true;
- this.setDatabaseItem('.drawioMigrated3', true);
- drawioFrame.contentWindow.postMessage(JSON.stringify({action: 'remoteInvoke', funtionName: 'setMigratedFlag'}), '*');
- });
-
- var next = mxUtils.bind(this, function()
- {
- index++;
- fetchOneFile();
- });
-
- var fetchOneFile = mxUtils.bind(this, function()
- {
- try
- {
- if (index >= fileNames.length)
- {
- markAsMigrated();
- return;
- }
-
- var fileTitle = fileNames[index];
-
- StorageFile.getFileContent(this, fileTitle, mxUtils.bind(this, function(data)
- {
- if (data == null || (fileTitle == '.scratchpad' && data == this.emptyLibraryXml)) //Don't overwrite
- {
- drawioFrame.contentWindow.postMessage(JSON.stringify({action: 'remoteInvoke', funtionName: 'getLocalStorageFile', functionArgs: [fileTitle]}), '*');
- }
- else
- {
- next();
- }
- }), next); //Ignore errors
- }
- catch(e)
- {
- //Log error
- console.log(e);
- }
- });
-
- var importOneFile = mxUtils.bind(this, function(file)
- {
- try
- {
- this.setDatabaseItem(null, [{
- title: file.title,
- size: file.data.length,
- lastModified: Date.now(),
- type: file.isLib? 'L' : 'F'
- }, {
- title: file.title,
- data: file.data
- }], next, next /* Ignore errors */, ['filesInfo', 'files']);
- }
- catch(e)
- {
- //Log error
- console.log(e);
- }
- });
-
- var messageListener = mxUtils.bind(this, function(evt)
- {
- try
- {
- //Only accept messages from migration iframe
- if (evt.source != drawioFrame.contentWindow)
- {
- return;
- }
-
- var drawMsg = {};
-
- try
- {
- drawMsg = JSON.parse(evt.data);
- }
- catch(e){} //Ignore
-
- if (drawMsg.event == 'init')
- {
- drawioFrame.contentWindow.postMessage(JSON.stringify({action: 'remoteInvokeReady'}), '*');
- drawioFrame.contentWindow.postMessage(JSON.stringify({action: 'remoteInvoke', funtionName: 'getLocalStorageFileNames'}), '*');
- }
- else if (drawMsg.event == 'remoteInvokeResponse' && !allDone)
- {
- if (collectNames)
- {
- if (drawMsg.resp != null && drawMsg.resp.length > 0 && drawMsg.resp[0] != null)
- {
- fileNames = drawMsg.resp[0];
- collectNames = false;
- fetchOneFile();
- }
- else
- {
- //Nothing in draw.io localStorage
- markAsMigrated();
- }
- }
- else
- {
- //Add the file, then move to the next
- if (drawMsg.resp != null && drawMsg.resp.length > 0 && drawMsg.resp[0] != null)
- {
- importOneFile(drawMsg.resp[0]);
- }
- else
- {
- next();
- }
- }
- }
- }
- catch(e)
- {
- console.log(e);
- }
- });
-
- window.addEventListener('message', messageListener);
- })); //Ignore errors
- }
-
- success(db);
-
- db.onversionchange = function()
- {
- //TODO Handle DB revision update while code is running
- // Save open file and request a page reload before closing the DB
- db.close();
- };
- }
- catch (e)
- {
- // Warn if error handler is not set
- if (error != null)
- {
- error(e);
- }
- else if (window.console != null)
- {
- console.warn(e);
- }
- };
- });
-
- req.onerror = error;
-
- req.onblocked = function()
- {
- //TODO Use this when a new version is introduced
- // there's another open connection to same database
- // and it wasn't closed after db.onversionchange triggered for them
- };
- }
- catch (e)
- {
- // Warn if error handler is not set
- if (error != null)
- {
- error(e);
- }
- else if (window.console != null)
- {
- console.error(e);
- }
- }
- }
- else if (error != null)
- {
- error(new Error('IndexedDB not supported'));
- }
- }
- else
- {
- success(this.database);
- }
- };
-
- /**
- * Add/Update item(s) in the database. It supports multiple stores transactions by sending an array of data, storeName
- * (key is optional, can be an array also if multiple stores are needed)
- */
- EditorUi.prototype.setDatabaseItem = function(key, data, success, error, storeName)
- {
- this.openDatabase(mxUtils.bind(this, function(db)
- {
- try
- {
- storeName = storeName || 'objects';
-
- if (!Array.isArray(storeName))
- {
- storeName = [storeName];
- key = [key];
- data = [data];
- }
-
- var trx = db.transaction(storeName, 'readwrite');
- trx.oncomplete = success;
- trx.onerror = error;
-
- for (var i = 0; i < storeName.length; i++)
- {
- trx.objectStore(storeName[i]).put(key != null && key[i] != null? {key: key[i], data: data[i]} : data[i]);
- }
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), error);
- };
- /**
- * Removes the item for the given key from the database.
- */
- EditorUi.prototype.removeDatabaseItem = function(key, success, error, storeName)
- {
- this.openDatabase(mxUtils.bind(this, function(db)
- {
- try
- {
- storeName = storeName || 'objects';
-
- if (!Array.isArray(storeName))
- {
- storeName = [storeName];
- key = [key];
- }
-
- var trx = db.transaction(storeName, 'readwrite');
- trx.oncomplete = success;
- trx.onerror = error;
-
- for (var i = 0; i < storeName.length; i++)
- {
- trx.objectStore(storeName[i]).delete(key[i]);
- }
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), error);
- };
-
- /**
- * Returns one item from the database.
- */
- EditorUi.prototype.getDatabaseItem = function(key, success, error, storeName)
- {
- this.openDatabase(mxUtils.bind(this, function(db)
- {
- try
- {
- storeName = storeName || 'objects';
- var trx = db.transaction([storeName], 'readonly');
- var req = trx.objectStore(storeName).get(key);
-
- req.onsuccess = function()
- {
- success(req.result);
- };
-
- req.onerror = error;
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), error);
- };
-
- /**
- * Returns all items from the database.
- */
- EditorUi.prototype.getDatabaseItems = function(success, error, storeName)
- {
- this.openDatabase(mxUtils.bind(this, function(db)
- {
- try
- {
- storeName = storeName || 'objects';
- var trx = db.transaction([storeName], 'readonly');
- var req = trx.objectStore(storeName).openCursor(
- IDBKeyRange.lowerBound(0));
- var items = [];
-
- req.onsuccess = function(e)
- {
- if (e.target.result == null)
- {
- success(items);
- }
- else
- {
- items.push(e.target.result.value);
- e.target.result.continue();
- }
- };
-
- req.onerror = error;
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), error);
- };
-
- /**
- * Returns all item keys from the database.
- */
- EditorUi.prototype.getDatabaseItemKeys = function(success, error, storeName)
- {
- this.openDatabase(mxUtils.bind(this, function(db)
- {
- try
- {
- storeName = storeName || 'objects';
- var trx = db.transaction([storeName], 'readonly');
- var req = trx.objectStore(storeName).getAllKeys();
-
- req.onsuccess = function()
- {
- success(req.result);
- };
-
- req.onerror = error;
- }
- catch (e)
- {
- if (error != null)
- {
- error(e);
- }
- }
- }), error);
- };
- /**
- * Comments: We need these functions as wrapper of File functions in order to facilitate
- * overriding them if comments are needed without having a file (e.g. Confluence Plugin)
- */
-
- /**
- * Are comments supported
- */
- EditorUi.prototype.commentsSupported = function()
- {
- var file = this.getCurrentFile();
-
- return file != null? file.commentsSupported() : false;
- };
- /**
- * Show refresh button?
- */
- EditorUi.prototype.commentsRefreshNeeded = function()
- {
- var file = this.getCurrentFile();
-
- return file != null? file.commentsRefreshNeeded() : true;
- };
-
- /**
- * Show save button?
- */
- EditorUi.prototype.commentsSaveNeeded = function()
- {
- var file = this.getCurrentFile();
- return file != null? file.commentsSaveNeeded() : false;
- };
-
- /**
- * Get comments
- */
- EditorUi.prototype.getComments = function(success, error)
- {
- var file = this.getCurrentFile();
-
- if (file != null)
- {
- file.getComments(success, error);
- }
- else
- {
- success([]); //placeholder
- }
- };
- /**
- * Add a comment
- */
- EditorUi.prototype.addComment = function(comment, success, error)
- {
- var file = this.getCurrentFile();
-
- if (file != null)
- {
- file.addComment(comment, success, error);
- }
- else
- {
- success(Date.now()); //placeholder
- }
- };
- /**
- * Can add a reply to a reply
- */
- EditorUi.prototype.canReplyToReplies = function()
- {
- var file = this.getCurrentFile();
-
- return file != null? file.canReplyToReplies() : true;
- };
- /**
- * Can add comments (The permission to comment)
- */
- EditorUi.prototype.canComment = function()
- {
- var file = this.getCurrentFile();
-
- return file != null? file.canComment() : true;
- };
- /**
- * Get a new comment object
- */
- EditorUi.prototype.newComment = function(content, user)
- {
- var file = this.getCurrentFile();
-
- if (file != null)
- {
- return file.newComment(content, user)
- }
- else
- {
- return new DrawioComment(this, null, content, Date.now(), Date.now(), false, user);
- }
- };
-
- //==================================================== End of comments =================================================================
-
- /**
- * Does revisions history available
- */
- EditorUi.prototype.isRevisionHistorySupported = function()
- {
- var file = this.getCurrentFile();
-
- return file != null && file.isRevisionHistorySupported();
- };
- /**
- * Get revisions of current file
- */
- EditorUi.prototype.getRevisions = function(success, error)
- {
- var file = this.getCurrentFile();
-
- if (file != null && file.getRevisions)
- {
- file.getRevisions(success, error);
- }
- else
- {
- error({message: mxResources.get('unknownError')});
- }
- };
-
- /**
- * Is revisions history enabled
- */
- EditorUi.prototype.isRevisionHistoryEnabled = function()
- {
- var file = this.getCurrentFile();
-
- return file != null &&
- ((file.constructor == DriveFile && file.isEditable()) ||
- file.constructor == DropboxFile);
- };
-
- //===========Adding methods to find the service running draw.io and allowing calling draw.io remote services
- EditorUi.prototype.getServiceName = function()
- {
- return 'draw.io';
- };
-
- EditorUi.prototype.addRemoteServiceSecurityCheck = function(xhr)
- {
- //Using a standard header with specific sequence
- xhr.setRequestHeader('Content-Language', 'da, mi, en, de-DE');
- };
-
- //===========To Be Removed Soon==========
- EditorUi.prototype.loadUrl = function(url, success, error, forceBinary, retry, dataUriPrefix, noBinary, headers)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: loadUrl');
- return this.editor.loadUrl(url, success, error, forceBinary, retry, dataUriPrefix, noBinary, headers);
- };
-
- EditorUi.prototype.loadFonts = function(then)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: loadFonts');
- return this.editor.loadFonts(then);
- };
-
- EditorUi.prototype.createSvgDataUri = function(svg)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: createSvgDataUri');
- return Editor.createSvgDataUri(svg);
- };
-
- EditorUi.prototype.embedCssFonts = function(fontCss, then)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: embedCssFonts');
- return this.editor.embedCssFonts(fontCss, then);
- };
-
- EditorUi.prototype.embedExtFonts = function(callback)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: embedExtFonts');
- return this.editor.embedExtFonts(callback);
- };
-
- EditorUi.prototype.exportToCanvas = function(callback, width, imageCache, background, error, limitHeight,
- ignoreSelection, scale, transparentBackground, addShadow, converter, graph, border, noCrop, grid, theme)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: exportToCanvas');
- return this.editor.exportToCanvas(callback, width, imageCache, background, error, limitHeight,
- ignoreSelection, scale, transparentBackground, addShadow, converter, graph, border,
- noCrop, grid, theme);
- };
-
- EditorUi.prototype.createImageUrlConverter = function()
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: createImageUrlConverter');
- return this.editor.createImageUrlConverter();
- };
-
- EditorUi.prototype.convertImages = function(svgRoot, callback, imageCache, converter)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: convertImages');
- return this.editor.convertImages(svgRoot, callback, imageCache, converter);
- };
-
- EditorUi.prototype.convertImageToDataUri = function(url, callback)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: convertImageToDataUri');
- return this.editor.convertImageToDataUri(url, callback);
- };
-
- EditorUi.prototype.base64Encode = function(str)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: base64Encode');
- return Editor.base64Encode(str);
- };
-
- EditorUi.prototype.updateCRC = function(crc, data, off, len)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: updateCRC');
- return Editor.updateCRC(crc, data, off, len);
- };
-
- EditorUi.prototype.crc32 = function(str)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: crc32');
- return Editor.crc32(str);
- };
-
- EditorUi.prototype.writeGraphModelToPng = function(data, type, key, value, error)
- {
- EditorUi.logEvent('SHOULD NOT BE CALLED: writeGraphModelToPng');
- return Editor.writeGraphModelToPng(data, type, key, value, error);
- };
-
- //=======End of To Be Removed Soon==========
-
- EditorUi.prototype.getLocalStorageFileNames = function()
- {
- if (localStorage.getItem('.localStorageMigrated') == '1' && urlParams['forceMigration'] != '1')
- {
- return null;
- }
-
- var files = [];
-
- for (var i = 0; i < localStorage.length; i++)
- {
- var key = localStorage.key(i);
- var value = localStorage.getItem(key);
-
- if (key.length > 0 && (key == '.scratchpad' || key.charAt(0) != '.') && value.length > 0)
- {
- var isFile = (value.substring(0, 8) === '<mxfile ' ||
- value.substring(0, 5) === '<?xml' || value.substring(0, 12) === '<!--[if IE]>');
- var isLib = (value.substring(0, 11) === '<mxlibrary>');
- if (isFile || isLib)
- {
- files.push(key);
- }
- }
- }
-
- return files;
- };
-
- EditorUi.prototype.getLocalStorageFile = function(key)
- {
- if (localStorage.getItem('.localStorageMigrated') == '1' && urlParams['forceMigration'] != '1')
- {
- return null;
- }
-
- var value = localStorage.getItem(key);
- return {title: key, data: value, isLib: value.substring(0, 11) === '<mxlibrary>'};
- };
-
- EditorUi.prototype.setMigratedFlag = function()
- {
- localStorage.setItem('.localStorageMigrated', '1');
- };
- })();
- /**
- * Comments Window, It is used by both editor and viewer. So, it is here in a common place
- */
- var CommentsWindow = function(editorUi, x, y, w, h, saveCallback)
- {
- var readOnly = !editorUi.canComment();
- var canReplyToReplies = editorUi.canReplyToReplies();
- var curEdited = null;
-
- var div = document.createElement('div');
- div.className = 'geCommentsWin';
- div.style.background = (Editor.isDarkMode()) ?
- Editor.darkColor : 'whiteSmoke';
- var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px';
-
- var listDiv = document.createElement('div');
- listDiv.className = 'geCommentsList';
- listDiv.style.backgroundColor = (Editor.isDarkMode()) ?
- Editor.darkColor : 'whiteSmoke';
- listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px';
- div.appendChild(listDiv);
-
- var noComments = document.createElement('span');
- noComments.style.cssText = 'display:none;padding-top:10px;text-align:center;';
- mxUtils.write(noComments, mxResources.get('noCommentsFound'));
-
- var selectionComment = null;
-
- var ldiv = document.createElement('div');
-
- ldiv.className = 'geToolbarContainer geCommentsToolbar';
- ldiv.style.height = tbarHeight;
- ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px';
-
- var link = document.createElement('a');
- link.className = 'geButton';
-
- function updateNoComments()
- {
- var divs = listDiv.getElementsByTagName('div');
- var visibleCount = 0;
-
- for (var i = 0; i < divs.length; i++)
- {
- if (divs[i].style.display != 'none' && divs[i].parentNode == listDiv)
- {
- visibleCount++;
- }
- }
-
- noComments.style.display = (visibleCount == 0) ? 'block' : 'none';
- };
-
- function editComment(comment, cdiv, saveCallback, deleteOnCancel)
- {
- curEdited = {div: cdiv, comment: comment, saveCallback: saveCallback, deleteOnCancel: deleteOnCancel};
-
- var commentTxt = cdiv.querySelector('.geCommentTxt');
- var actionsDiv = cdiv.querySelector('.geCommentActionsList');
-
- var textArea = document.createElement('textarea');
- textArea.className = 'geCommentEditTxtArea';
- textArea.style.minHeight = commentTxt.offsetHeight + 'px';
- textArea.value = comment.content;
- cdiv.insertBefore(textArea, commentTxt);
-
- var btnDiv = document.createElement('div');
- btnDiv.className = 'geCommentEditBtns';
-
- function reset()
- {
- cdiv.removeChild(textArea);
- cdiv.removeChild(btnDiv);
- actionsDiv.style.display = 'block';
- commentTxt.style.display = 'block';
- };
-
- var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
- {
- if (deleteOnCancel)
- {
- cdiv.parentNode.removeChild(cdiv);
- updateNoComments();
- }
- else
- {
- reset();
- }
-
- curEdited = null;
- });
-
- cancelBtn.className = 'geCommentEditBtn';
- btnDiv.appendChild(cancelBtn);
-
- var saveBtn = mxUtils.button(mxResources.get('save'), function()
- {
- commentTxt.innerText = '';
- comment.content = textArea.value;
- mxUtils.write(commentTxt, comment.content);
- reset();
- saveCallback(comment);
- curEdited = null;
- });
-
- // Updates modified state and handles placeholder text
- mxEvent.addListener(textArea, 'keydown', mxUtils.bind(this, function(evt)
- {
- if (!mxEvent.isConsumed(evt))
- {
- if ((mxEvent.isControlDown(evt) || (mxClient.IS_MAC &&
- mxEvent.isMetaDown(evt))) && evt.keyCode == 13 /* Ctrl+Enter */)
- {
- saveBtn.click();
- mxEvent.consume(evt);
- }
- else if (evt.keyCode == 27 /* Escape */)
- {
- cancelBtn.click();
- mxEvent.consume(evt);
- }
- }
- }));
-
- // Focused to include in viewport before focusin textbox
- saveBtn.focus();
- saveBtn.className = 'geCommentEditBtn gePrimaryBtn';
- btnDiv.appendChild(saveBtn);
- cdiv.insertBefore(btnDiv, commentTxt);
- actionsDiv.style.display = 'none';
- commentTxt.style.display = 'none';
- textArea.focus();
- };
-
- function writeCommentDate(comment, dateDiv)
- {
- dateDiv.innerText = '';
- var ts = new Date(comment.modifiedDate);
- var str = editorUi.timeSince(ts);
-
- if (str == null)
- {
- str = mxResources.get('lessThanAMinute');
- }
-
- mxUtils.write(dateDiv, mxResources.get('timeAgo', [str], '{1} ago'));
- dateDiv.setAttribute('title', ts.toLocaleDateString() + ' ' +
- ts.toLocaleTimeString());
- };
-
- function showBusy(commentDiv)
- {
- var busyImg = document.createElement('img');
- busyImg.className = 'geCommentBusyImg';
- busyImg.src= IMAGE_PATH + '/spin.gif';
- commentDiv.appendChild(busyImg);
- commentDiv.busyImg = busyImg;
- };
-
- function showError(commentDiv)
- {
- commentDiv.style.border = '1px solid red';
- if (commentDiv.busyImg.parentNode == commentDiv)
- {
- commentDiv.removeChild(commentDiv.busyImg);
- }
- };
-
- function showDone(commentDiv)
- {
- commentDiv.style.border = '';
-
- if (commentDiv.busyImg.parentNode == commentDiv)
- {
- commentDiv.removeChild(commentDiv.busyImg);
- }
- };
- function addComment(comment, parentArr, parent, level, showResolved)
- {
- //Skip resolved comments if showResolved is not set
- if (!showResolved && comment.isResolved)
- {
- return;
- }
- noComments.style.display = 'none';
-
- var cdiv = document.createElement('div');
- cdiv.className = 'geCommentContainer';
- cdiv.setAttribute('data-commentId', comment.id);
- cdiv.style.marginLeft = (level * 20 + 5) + 'px';
- if (comment.isResolved && !Editor.isDarkMode())
- {
- cdiv.style.backgroundColor = 'ghostWhite';
- }
-
- var headerDiv = document.createElement('div');
- headerDiv.className = 'geCommentHeader';
-
- var userImg = document.createElement('img');
- userImg.className = 'geCommentUserImg';
- userImg.src = (comment.user != null &&
- comment.user.pictureUrl != null) ?
- comment.user.pictureUrl : Editor.userImage;
- headerDiv.appendChild(userImg);
-
- var headerTxt = document.createElement('div');
- headerTxt.className = 'geCommentHeaderTxt';
- headerDiv.appendChild(headerTxt);
-
- var usernameDiv = document.createElement('div');
- usernameDiv.className = 'geCommentUsername';
- mxUtils.write(usernameDiv, (comment.user != null) ?
- comment.user.displayName : mxResources.get('unknownUser'));
- headerTxt.appendChild(usernameDiv);
-
- var dateDiv = document.createElement('div');
- dateDiv.className = 'geCommentDate';
- dateDiv.setAttribute('data-commentId', comment.id);
- writeCommentDate(comment, dateDiv);
- headerTxt.appendChild(dateDiv);
- cdiv.appendChild(headerDiv);
-
- var commentTxtDiv = document.createElement('div');
- commentTxtDiv.className = 'geCommentTxt';
- mxUtils.write(commentTxtDiv, comment.content || '');
- cdiv.appendChild(commentTxtDiv);
-
- if (comment.isLocked)
- {
- cdiv.style.opacity = '0.5';
- }
-
- var actionsDiv = document.createElement('div');
- actionsDiv.className = 'geCommentActions';
- var actionsList = document.createElement('ul');
- actionsList.className = 'geCommentActionsList';
- actionsDiv.appendChild(actionsList);
-
- function addAction(name, evtHandler, hide)
- {
- var action = document.createElement('li');
- action.className = 'geCommentAction';
- var actionLnk = document.createElement('a');
- actionLnk.className = 'geCommentActionLnk';
- mxUtils.write(actionLnk, name);
- action.appendChild(actionLnk);
-
- mxEvent.addListener(actionLnk, 'click', function(evt)
- {
- evtHandler(evt, comment);
- evt.preventDefault();
- mxEvent.consume(evt);
- });
-
- actionsList.appendChild(action);
-
- if (hide) action.style.display = 'none';
- };
-
- function collectReplies()
- {
- var replies = [];
- var pdiv = cdiv;
-
- function collectReplies(comment)
- {
- replies.push(pdiv);
-
- if (comment.replies != null)
- {
- for (var i = 0; i < comment.replies.length; i++)
- {
- pdiv = pdiv.nextSibling;
- collectReplies(comment.replies[i]);
- }
- }
- }
-
- collectReplies(comment);
-
- return {pdiv: pdiv, replies: replies};
- };
-
- function addReply(initContent, editIt, saveCallback, doResolve, doReopen)
- {
- var pdiv = collectReplies().pdiv;
-
- var newReply = editorUi.newComment(initContent, editorUi.getCurrentUser());
- newReply.pCommentId = comment.id;
-
- if (comment.replies == null) comment.replies = [];
-
- var replyComment = addComment(newReply, comment.replies, pdiv, level + 1);
- function doAddReply()
- {
- showBusy(replyComment);
-
- comment.addReply(newReply, function(id)
- {
- newReply.id = id;
- comment.replies.push(newReply);
- showDone(replyComment);
-
- if (saveCallback) saveCallback();
-
- }, function(err)
- {
- doEdit();
- showError(replyComment);
- editorUi.handleError(err, null, null, null,
- mxUtils.htmlEntities(mxResources.get('objectNotFound')));
- }, doResolve, doReopen);
- };
-
- function doEdit()
- {
- editComment(newReply, replyComment, function(newReply)
- {
- doAddReply();
- }, true);
- };
- if (editIt)
- {
- doEdit();
- }
- else
- {
- doAddReply();
- }
- };
-
- if (!readOnly && !comment.isLocked && (level == 0 || canReplyToReplies))
- {
- addAction(mxResources.get('reply'), function()
- {
- addReply('', true);
- }, comment.isResolved);
- }
-
- var user = editorUi.getCurrentUser();
-
- if (user != null && comment.user != null &&
- user.id == comment.user.id &&
- !readOnly && !comment.isLocked)
- {
- addAction(mxResources.get('edit'), function()
- {
- function doEditComment()
- {
- editComment(comment, cdiv, function()
- {
- showBusy(cdiv);
-
- comment.editComment(comment.content, function()
- {
- showDone(cdiv);
- }, function(err)
- {
- showError(cdiv);
- doEditComment();
- editorUi.handleError(err, null, null, null,
- mxUtils.htmlEntities(mxResources.get('objectNotFound')));
- });
- });
- };
-
- doEditComment();
- }, comment.isResolved);
-
- addAction(mxResources.get('delete'), function()
- {
- editorUi.confirm(mxResources.get('areYouSure'), function()
- {
- showBusy(cdiv);
-
- comment.deleteComment(function(markedOnly)
- {
- if (markedOnly === true)
- {
- var commentTxt = cdiv.querySelector('.geCommentTxt');
- commentTxt.innerText = '';
- mxUtils.write(commentTxt, mxResources.get('msgDeleted'));
-
- var actions = cdiv.querySelectorAll('.geCommentAction');
-
- for (var i = 0; i < actions.length; i++)
- {
- actions[i].parentNode.removeChild(actions[i]);
- }
-
- showDone(cdiv);
- cdiv.style.opacity = '0.5';
- }
- else
- {
- var replies = collectReplies(comment).replies;
-
- for (var i = 0; i < replies.length; i++)
- {
- listDiv.removeChild(replies[i]);
- }
-
- for (var i = 0; i < parentArr.length; i++)
- {
- if (parentArr[i] == comment)
- {
- parentArr.splice(i, 1);
- break;
- }
- }
-
- noComments.style.display = (listDiv.getElementsByTagName('div').length == 0) ? 'block' : 'none';
- }
- }, function(err)
- {
- showError(cdiv);
- editorUi.handleError(err, null, null, null,
- mxUtils.htmlEntities(mxResources.get('objectNotFound')));
- });
- });
- }, comment.isResolved);
- }
-
- if (!readOnly && !comment.isLocked && level == 0) //Resolve is a top-level action only
- {
- function toggleResolve(evt)
- {
- function doToggle()
- {
- var resolveActionLnk = evt.target;
- resolveActionLnk.innerText = '';
- comment.isResolved = !comment.isResolved;
- mxUtils.write(resolveActionLnk, comment.isResolved? mxResources.get('reopen') : mxResources.get('resolve'));
- var actionsDisplay = comment.isResolved? 'none' : '';
- var replies = collectReplies(comment).replies;
- var color = (Editor.isDarkMode()) ? 'transparent' : (comment.isResolved? 'ghostWhite' : 'white');
-
- for (var i = 0; i < replies.length; i++)
- {
- replies[i].style.backgroundColor = color;
-
- var forOpenActions = replies[i].querySelectorAll('.geCommentAction');
-
- for (var j = 0; j < forOpenActions.length; j ++)
- {
- if (forOpenActions[j] == resolveActionLnk.parentNode) continue;
-
- forOpenActions[j].style.display = actionsDisplay;
- }
- if (!resolvedChecked)
- {
- replies[i].style.display = 'none';
- }
- }
-
- updateNoComments();
- };
-
- if (comment.isResolved)
- {
- addReply(mxResources.get('reOpened') + ': ', true, doToggle, false, true);
- }
- else
- {
- addReply(mxResources.get('markedAsResolved'), false, doToggle, true);
- }
- };
-
- addAction(comment.isResolved? mxResources.get('reopen') : mxResources.get('resolve'), toggleResolve);
- }
-
- cdiv.appendChild(actionsDiv);
-
- if (parent != null)
- {
- listDiv.insertBefore(cdiv, parent.nextSibling);
- }
- else
- {
- listDiv.appendChild(cdiv);
- }
-
- for (var i = 0; comment.replies != null && i < comment.replies.length; i++)
- {
- var reply = comment.replies[i];
- reply.isResolved = comment.isResolved; //copy isResolved to child comments (replies)
- addComment(reply, comment.replies, null, level + 1, showResolved);
- }
-
- if (curEdited != null)
- {
- if (curEdited.comment.id == comment.id)
- {
- var origContent = comment.content;
- comment.content = curEdited.comment.content;
- editComment(comment, cdiv, curEdited.saveCallback, curEdited.deleteOnCancel);
- comment.content = origContent;
- }
- else if (curEdited.comment.id == null && curEdited.comment.pCommentId == comment.id)
- {
- listDiv.appendChild(curEdited.div);
- editComment(curEdited.comment, curEdited.div, curEdited.saveCallback, curEdited.deleteOnCancel);
- }
- }
- return cdiv;
- };
- if (!readOnly)
- {
- var addLink = link.cloneNode();
- addLink.innerHTML = '<div class="geSprite geSprite-plus" style="display:inline-block;"></div>';
- addLink.setAttribute('title', mxResources.get('create') + '...');
-
- mxEvent.addListener(addLink, 'click', function(evt)
- {
- var newComment = editorUi.newComment('', editorUi.getCurrentUser());
- var newCommentDiv = addComment(newComment, comments, null, 0);
-
- function doAddComment()
- {
- editComment(newComment, newCommentDiv, function(newComment)
- {
- showBusy(newCommentDiv);
-
- editorUi.addComment(newComment, function(id)
- {
- newComment.id = id;
- comments.push(newComment);
- showDone(newCommentDiv);
- }, function(err)
- {
- showError(newCommentDiv);
- doAddComment();
- editorUi.handleError(err, null, null, null,
- mxUtils.htmlEntities(mxResources.get('objectNotFound')));
- });
- }, true);
- }
-
- doAddComment();
- evt.preventDefault();
- mxEvent.consume(evt);
- });
-
- ldiv.appendChild(addLink);
- }
- var resolvedLink = link.cloneNode();
- resolvedLink.innerHTML = '<img class="geAdaptiveAsset" src="' + IMAGE_PATH + '/check.png" style="width: 16px; padding: 2px;">';
- resolvedLink.setAttribute('title', mxResources.get('showResolved'));
- resolvedLink.className = 'geButton';
- var resolvedChecked = false;
-
- mxEvent.addListener(resolvedLink, 'click', function(evt)
- {
- resolvedChecked = !resolvedChecked;
-
- this.className = resolvedChecked? 'geButton geCheckedBtn' : 'geButton';
- refresh();
-
- evt.preventDefault();
- mxEvent.consume(evt);
- });
-
- ldiv.appendChild(resolvedLink);
-
- if (editorUi.commentsRefreshNeeded())
- {
- var refreshLink = link.cloneNode();
- refreshLink.innerHTML = '<img class="geAdaptiveAsset" src="' + IMAGE_PATH + '/update16.png" style="width: 16px; padding: 2px;">';
- refreshLink.setAttribute('title', mxResources.get('refresh'));
- refreshLink.className = 'geButton';
-
- mxEvent.addListener(refreshLink, 'click', function(evt)
- {
- refresh();
-
- evt.preventDefault();
- mxEvent.consume(evt);
- });
-
- ldiv.appendChild(refreshLink);
- }
-
- if (editorUi.commentsSaveNeeded())
- {
- var saveLink = link.cloneNode();
- saveLink.innerHTML = '<img src="' + IMAGE_PATH + '/save.png" style="width: 20px; padding: 2px;">';
- saveLink.setAttribute('title', mxResources.get('save'));
- saveLink.className = 'geButton geAdaptiveAsset';
-
- mxEvent.addListener(saveLink, 'click', function(evt)
- {
- saveCallback();
-
- evt.preventDefault();
- mxEvent.consume(evt);
- });
-
- ldiv.appendChild(saveLink);
- }
- div.appendChild(ldiv);
- var comments = [];
- var refresh = mxUtils.bind(this, function()
- {
- this.hasError = false;
-
- if (curEdited != null)
- {
- try
- {
- curEdited.div = curEdited.div.cloneNode(true);
- var commentEditTxt = curEdited.div.querySelector('.geCommentEditTxtArea');
- var commentEditBtns = curEdited.div.querySelector('.geCommentEditBtns');
-
- curEdited.comment.content = commentEditTxt.value;
- commentEditTxt.parentNode.removeChild(commentEditTxt);
- commentEditBtns.parentNode.removeChild(commentEditBtns);
- }
- catch (e)
- {
- editorUi.handleError(e);
- }
- }
-
- listDiv.innerHTML = '<div style="padding-top:10px;text-align:center;"><img src="' + IMAGE_PATH + '/spin.gif" valign="middle"> ' +
- mxUtils.htmlEntities(mxResources.get('loading')) + '...</div>';
-
- canReplyToReplies = editorUi.canReplyToReplies();
-
- if (editorUi.commentsSupported())
- {
- editorUi.getComments(function(list)
- {
- function sortReplies(replies)
- {
- if (replies != null)
- {
- //Sort replies old to new
- replies.sort(function(r1, r2)
- {
- return new Date(r1.modifiedDate) - new Date(r2.modifiedDate);
- });
-
- for (var i = 0; i < replies.length; i++)
- {
- sortReplies(replies[i].replies);
- }
- }
- };
-
- //Sort comments old to new
- list.sort(function(c1, c2)
- {
- return new Date(c1.modifiedDate) - new Date(c2.modifiedDate);
- });
- listDiv.innerText = '';
- listDiv.appendChild(noComments);
- noComments.style.display = 'block';
- comments = list;
-
- for (var i = 0; i < comments.length; i++)
- {
- sortReplies(comments[i].replies);
- addComment(comments[i], comments, null, 0, resolvedChecked);
- }
-
- //New comment case
- if (curEdited != null && curEdited.comment.id == null && curEdited.comment.pCommentId == null)
- {
- listDiv.appendChild(curEdited.div);
- editComment(curEdited.comment, curEdited.div, curEdited.saveCallback, curEdited.deleteOnCancel);
- }
-
- }, mxUtils.bind(this, function(err)
- {
- listDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('error') + (err && err.message? ': ' + err.message : ''));
- this.hasError = true;
- }));
- }
- else
- {
- //TODO if comments are not supported, close the dialog
- listDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('error'));
- }
- });
- refresh();
-
- this.refreshComments = refresh;
- //Refresh the modified date of each comment if the window is visible
- var refreshCommentsTime = mxUtils.bind(this, function()
- {
- if (!this.window.isVisible()) return; //only update if it is visible
-
- var modDateDivs = listDiv.querySelectorAll('.geCommentDate');
- var modDateDivsMap = {};
-
- for (var i = 0; i < modDateDivs.length; i++)
- {
- var div = modDateDivs[i];
- modDateDivsMap[div.getAttribute('data-commentId')] = div;
- }
-
- function processComment(comment)
- {
- var div = modDateDivsMap[comment.id];
-
- if (div == null) return; //resolved comments
-
- writeCommentDate(comment, div);
-
- for (var i = 0; comment.replies != null && i < comment.replies.length; i++)
- {
- processComment(comment.replies[i]);
- }
- };
-
- for (var i = 0; i < comments.length; i++)
- {
- processComment(comments[i]);
- }
- });
- //Periodically refresh time every one minute
- setInterval(refreshCommentsTime, 60000);
- this.refreshCommentsTime = refreshCommentsTime;
-
- this.window = new mxWindow(mxResources.get('comments'), div, x, y, w, h, true, true);
- this.window.minimumSize = new mxRectangle(0, 0, 260, 200);
- this.window.destroyOnClose = false;
- this.window.setMaximizable(false);
- this.window.setResizable(true);
- this.window.setClosable(true);
- this.window.setVisible(true);
-
- this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function()
- {
- this.window.fit();
- }));
-
- editorUi.installResizeHandler(this, true);
- };
- /**
- *
- */
- var ConfirmDialog = function(editorUi, message, okFn, cancelFn, okLabel, cancelLabel,
- okImg, cancelImg, showRememberOption, imgSrc, maxHeight)
- {
- var div = document.createElement('div');
- div.style.textAlign = 'center';
- maxHeight = (maxHeight != null) ? maxHeight : 44;
-
- var p2 = document.createElement('div');
- p2.style.padding = '6px';
- p2.style.overflow = 'auto';
- p2.style.maxHeight = maxHeight + 'px';
- p2.style.lineHeight = '1.2em';
-
- mxUtils.write(p2, message);
- div.appendChild(p2);
-
- if (imgSrc != null)
- {
- var p3 = document.createElement('div');
- p3.style.padding = '6px 0 6px 0';
- var img = document.createElement('img');
- img.setAttribute('src', imgSrc);
- p3.appendChild(img);
- div.appendChild(p3);
- }
-
- var btns = document.createElement('div');
- btns.style.textAlign = 'center';
- btns.style.whiteSpace = 'nowrap';
- var cb = document.createElement('input');
- cb.setAttribute('type', 'checkbox');
- var cancelBtn = mxUtils.button(cancelLabel || mxResources.get('cancel'), function()
- {
- editorUi.hideDialog();
-
- if (cancelFn != null)
- {
- cancelFn(cb.checked);
- }
- });
- cancelBtn.className = 'geBtn';
-
- if (cancelImg != null)
- {
- cancelBtn.innerHTML = cancelImg + '<br>' + cancelBtn.innerHTML;
- cancelBtn.style.paddingBottom = '8px';
- cancelBtn.style.paddingTop = '8px';
- cancelBtn.style.height = 'auto';
- cancelBtn.style.width = '40%';
- }
-
- if (editorUi.editor.cancelFirst)
- {
- btns.appendChild(cancelBtn);
- }
-
- var okBtn = mxUtils.button(okLabel || mxResources.get('ok'), function()
- {
- editorUi.hideDialog();
-
- if (okFn != null)
- {
- okFn(cb.checked);
- }
- });
- btns.appendChild(okBtn);
-
- if (okImg != null)
- {
- okBtn.innerHTML = okImg + '<br>' + okBtn.innerHTML + '<br>';
- okBtn.style.paddingBottom = '8px';
- okBtn.style.paddingTop = '8px';
- okBtn.style.height = 'auto';
- okBtn.className = 'geBtn';
- okBtn.style.width = '40%';
- }
- else
- {
- okBtn.className = 'geBtn gePrimaryBtn';
- }
-
- if (!editorUi.editor.cancelFirst)
- {
- btns.appendChild(cancelBtn);
- }
- div.appendChild(btns);
-
- if (showRememberOption)
- {
- btns.style.marginTop = '10px';
- var p2 = document.createElement('p');
- p2.style.marginTop = '20px';
- p2.style.marginBottom = '0px';
- p2.appendChild(cb);
- var span = document.createElement('span');
- mxUtils.write(span, ' ' + mxResources.get('rememberThisSetting'));
- p2.appendChild(span);
- div.appendChild(p2);
-
- mxEvent.addListener(span, 'click', function(evt)
- {
- cb.checked = !cb.checked;
- mxEvent.consume(evt);
- });
- }
- else
- {
- btns.style.marginTop = '12px';
- }
- this.init = function()
- {
- okBtn.focus();
- };
-
- this.container = div;
- };
- /**
- * Headless Editor UI class for offscreen editor instances.
- */
- var HeadlessEditorUi = function()
- {
- EditorUi.call(this, new Editor(true), document.createElement('div'), true);
- };
- /**
- * Extends EditorUi.
- */
- mxUtils.extend(HeadlessEditorUi, EditorUi);
- /**
- * Avoid creating UI and event listeners.
- */
- HeadlessEditorUi.prototype.createUi = function() {};
- HeadlessEditorUi.prototype.addTrees = function() {};
- HeadlessEditorUi.prototype.onBeforeUnload = function() {};
- HeadlessEditorUi.prototype.updateActionStates = function() {};
|