mxUtils.js 104 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. var mxUtils =
  6. {
  7. /**
  8. * Class: mxUtils
  9. *
  10. * A singleton class that provides cross-browser helper methods.
  11. * This is a global functionality. To access the functions in this
  12. * class, use the global classname appended by the functionname.
  13. * You may have to load chrome://global/content/contentAreaUtils.js
  14. * to disable certain security restrictions in Mozilla for the <open>,
  15. * <save>, <saveAs> and <copy> function.
  16. *
  17. * For example, the following code displays an error message:
  18. *
  19. * (code)
  20. * mxUtils.error('Browser is not supported!', 200, false);
  21. * (end)
  22. *
  23. * Variable: errorResource
  24. *
  25. * Specifies the resource key for the title of the error window. If the
  26. * resource for this key does not exist then the value is used as
  27. * the title. Default is 'error'.
  28. */
  29. errorResource: (mxClient.language != 'none') ? 'error' : '',
  30. /**
  31. * Variable: closeResource
  32. *
  33. * Specifies the resource key for the label of the close button. If the
  34. * resource for this key does not exist then the value is used as
  35. * the label. Default is 'close'.
  36. */
  37. closeResource: (mxClient.language != 'none') ? 'close' : '',
  38. /**
  39. * Variable: errorImage
  40. *
  41. * Defines the image used for error dialogs.
  42. */
  43. errorImage: mxClient.imageBasePath + '/error.gif',
  44. /**
  45. * Function: removeCursors
  46. *
  47. * Removes the cursors from the style of the given DOM node and its
  48. * descendants.
  49. *
  50. * Parameters:
  51. *
  52. * element - DOM node to remove the cursor style from.
  53. */
  54. removeCursors: function(element)
  55. {
  56. if (element.style != null)
  57. {
  58. element.style.cursor = '';
  59. }
  60. var children = element.childNodes;
  61. if (children != null)
  62. {
  63. var childCount = children.length;
  64. for (var i = 0; i < childCount; i += 1)
  65. {
  66. mxUtils.removeCursors(children[i]);
  67. }
  68. }
  69. },
  70. /**
  71. * Function: getCurrentStyle
  72. *
  73. * Returns the current style of the specified element.
  74. *
  75. * Parameters:
  76. *
  77. * element - DOM node whose current style should be returned.
  78. */
  79. getCurrentStyle: function()
  80. {
  81. if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 9))
  82. {
  83. return function(element)
  84. {
  85. return (element != null) ? element.currentStyle : null;
  86. };
  87. }
  88. else
  89. {
  90. return function(element)
  91. {
  92. return (element != null) ?
  93. window.getComputedStyle(element, '') :
  94. null;
  95. };
  96. }
  97. }(),
  98. /**
  99. * Function: parseCssNumber
  100. *
  101. * Parses the given CSS numeric value adding handling for the values thin,
  102. * medium and thick (2, 4 and 6).
  103. */
  104. parseCssNumber: function(value)
  105. {
  106. if (value == 'thin')
  107. {
  108. value = '2';
  109. }
  110. else if (value == 'medium')
  111. {
  112. value = '4';
  113. }
  114. else if (value == 'thick')
  115. {
  116. value = '6';
  117. }
  118. value = parseFloat(value);
  119. if (isNaN(value))
  120. {
  121. value = 0;
  122. }
  123. return value;
  124. },
  125. /**
  126. * Function: setPrefixedStyle
  127. *
  128. * Adds the given style with the standard name and an optional vendor prefix for the current
  129. * browser.
  130. *
  131. * (code)
  132. * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
  133. * (end)
  134. */
  135. setPrefixedStyle: function()
  136. {
  137. var prefix = null;
  138. if (mxClient.IS_OT)
  139. {
  140. prefix = 'O';
  141. }
  142. else if (mxClient.IS_SF || mxClient.IS_GC)
  143. {
  144. prefix = 'Webkit';
  145. }
  146. else if (mxClient.IS_MT)
  147. {
  148. prefix = 'Moz';
  149. }
  150. else if (mxClient.IS_IE && document.documentMode >= 9 && document.documentMode < 10)
  151. {
  152. prefix = 'ms';
  153. }
  154. return function(style, name, value)
  155. {
  156. style[name] = value;
  157. if (prefix != null && name.length > 0)
  158. {
  159. name = prefix + name.substring(0, 1).toUpperCase() + name.substring(1);
  160. style[name] = value;
  161. }
  162. };
  163. }(),
  164. /**
  165. * Function: hasScrollbars
  166. *
  167. * Returns true if the overflow CSS property of the given node is either
  168. * scroll or auto.
  169. *
  170. * Parameters:
  171. *
  172. * node - DOM node whose style should be checked for scrollbars.
  173. */
  174. hasScrollbars: function(node)
  175. {
  176. var style = mxUtils.getCurrentStyle(node);
  177. return style != null && (style.overflow == 'scroll' || style.overflow == 'auto');
  178. },
  179. /**
  180. * Function: bind
  181. *
  182. * Returns a wrapper function that locks the execution scope of the given
  183. * function to the specified scope. Inside funct, the "this" keyword
  184. * becomes a reference to that scope.
  185. */
  186. bind: function(scope, funct)
  187. {
  188. return function()
  189. {
  190. return funct.apply(scope, arguments);
  191. };
  192. },
  193. /**
  194. * Function: eval
  195. *
  196. * Evaluates the given expression using eval and returns the JavaScript
  197. * object that represents the expression result. Supports evaluation of
  198. * expressions that define functions and returns the function object for
  199. * these expressions.
  200. *
  201. * Parameters:
  202. *
  203. * expr - A string that represents a JavaScript expression.
  204. */
  205. eval: function(expr)
  206. {
  207. var result = null;
  208. if (expr.indexOf('function') >= 0)
  209. {
  210. try
  211. {
  212. eval('var _mxJavaScriptExpression='+expr);
  213. result = _mxJavaScriptExpression;
  214. // TODO: Use delete here?
  215. _mxJavaScriptExpression = null;
  216. }
  217. catch (e)
  218. {
  219. mxLog.warn(e.message + ' while evaluating ' + expr);
  220. }
  221. }
  222. else
  223. {
  224. try
  225. {
  226. result = eval(expr);
  227. }
  228. catch (e)
  229. {
  230. mxLog.warn(e.message + ' while evaluating ' + expr);
  231. }
  232. }
  233. return result;
  234. },
  235. /**
  236. * Function: findNode
  237. *
  238. * Returns the first node where attr equals value.
  239. * This implementation does not use XPath.
  240. */
  241. findNode: function(node, attr, value)
  242. {
  243. if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
  244. {
  245. var tmp = node.getAttribute(attr);
  246. if (tmp != null && tmp == value)
  247. {
  248. return node;
  249. }
  250. }
  251. node = node.firstChild;
  252. while (node != null)
  253. {
  254. var result = mxUtils.findNode(node, attr, value);
  255. if (result != null)
  256. {
  257. return result;
  258. }
  259. node = node.nextSibling;
  260. }
  261. return null;
  262. },
  263. /**
  264. * Function: getFunctionName
  265. *
  266. * Returns the name for the given function.
  267. *
  268. * Parameters:
  269. *
  270. * f - JavaScript object that represents a function.
  271. */
  272. getFunctionName: function(f)
  273. {
  274. var str = null;
  275. if (f != null)
  276. {
  277. if (f.name != null)
  278. {
  279. str = f.name;
  280. }
  281. else
  282. {
  283. str = mxUtils.trim(f.toString());
  284. if (/^function\s/.test(str))
  285. {
  286. str = mxUtils.ltrim(str.substring(9));
  287. var idx2 = str.indexOf('(');
  288. if (idx2 > 0)
  289. {
  290. str = str.substring(0, idx2);
  291. }
  292. }
  293. }
  294. }
  295. return str;
  296. },
  297. /**
  298. * Function: indexOf
  299. *
  300. * Returns the index of obj in array or -1 if the array does not contain
  301. * the given object.
  302. *
  303. * Parameters:
  304. *
  305. * array - Array to check for the given obj.
  306. * obj - Object to find in the given array.
  307. */
  308. indexOf: function(array, obj)
  309. {
  310. if (array != null && obj != null)
  311. {
  312. for (var i = 0; i < array.length; i++)
  313. {
  314. if (array[i] == obj)
  315. {
  316. return i;
  317. }
  318. }
  319. }
  320. return -1;
  321. },
  322. /**
  323. * Function: forEach
  324. *
  325. * Calls the given function for each element of the given array and returns
  326. * the array.
  327. *
  328. * Parameters:
  329. *
  330. * array - Array that contains the elements.
  331. * fn - Function to be called for each object.
  332. */
  333. forEach: function(array, fn)
  334. {
  335. if (array != null && fn != null)
  336. {
  337. for (var i = 0; i < array.length; i++)
  338. {
  339. fn(array[i]);
  340. }
  341. }
  342. return array;
  343. },
  344. /**
  345. * Function: remove
  346. *
  347. * Removes all occurrences of the given object in the given array or
  348. * object. If there are multiple occurrences of the object, be they
  349. * associative or as an array entry, all occurrences are removed from
  350. * the array or deleted from the object. By removing the object from
  351. * the array, all elements following the removed element are shifted
  352. * by one step towards the beginning of the array.
  353. *
  354. * The length of arrays is not modified inside this function.
  355. *
  356. * Parameters:
  357. *
  358. * obj - Object to find in the given array.
  359. * array - Array to check for the given obj.
  360. */
  361. remove: function(obj, array)
  362. {
  363. var result = null;
  364. if (typeof(array) == 'object')
  365. {
  366. var index = mxUtils.indexOf(array, obj);
  367. while (index >= 0)
  368. {
  369. array.splice(index, 1);
  370. result = obj;
  371. index = mxUtils.indexOf(array, obj);
  372. }
  373. }
  374. for (var key in array)
  375. {
  376. if (array[key] == obj)
  377. {
  378. delete array[key];
  379. result = obj;
  380. }
  381. }
  382. return result;
  383. },
  384. /**
  385. * Function: isNode
  386. *
  387. * Returns true if the given value is an XML node with the node name
  388. * and if the optional attribute has the specified value.
  389. *
  390. * This implementation assumes that the given value is a DOM node if the
  391. * nodeType property is numeric, that is, if isNaN returns false for
  392. * value.nodeType.
  393. *
  394. * Parameters:
  395. *
  396. * value - Object that should be examined as a node.
  397. * nodeName - String that specifies the node name.
  398. * attributeName - Optional attribute name to check.
  399. * attributeValue - Optional attribute value to check.
  400. */
  401. isNode: function(value, nodeName, attributeName, attributeValue)
  402. {
  403. if (value != null && !isNaN(value.nodeType) && (nodeName == null ||
  404. value.nodeName.toLowerCase() == nodeName.toLowerCase()))
  405. {
  406. return attributeName == null ||
  407. value.getAttribute(attributeName) == attributeValue;
  408. }
  409. return false;
  410. },
  411. /**
  412. * Function: isAncestorNode
  413. *
  414. * Returns true if the given ancestor is an ancestor of the
  415. * given DOM node in the DOM. This also returns true if the
  416. * child is the ancestor.
  417. *
  418. * Parameters:
  419. *
  420. * ancestor - DOM node that represents the ancestor.
  421. * child - DOM node that represents the child.
  422. */
  423. isAncestorNode: function(ancestor, child)
  424. {
  425. var parent = child;
  426. while (parent != null)
  427. {
  428. if (parent == ancestor)
  429. {
  430. return true;
  431. }
  432. parent = parent.parentNode;
  433. }
  434. return false;
  435. },
  436. /**
  437. * Function: getChildNodes
  438. *
  439. * Returns an array of child nodes that are of the given node type.
  440. *
  441. * Parameters:
  442. *
  443. * node - Parent DOM node to return the children from.
  444. * nodeType - Optional node type to return. Default is
  445. * <mxConstants.NODETYPE_ELEMENT>.
  446. */
  447. getChildNodes: function(node, nodeType)
  448. {
  449. nodeType = nodeType || mxConstants.NODETYPE_ELEMENT;
  450. var children = [];
  451. var tmp = node.firstChild;
  452. while (tmp != null)
  453. {
  454. if (tmp.nodeType == nodeType)
  455. {
  456. children.push(tmp);
  457. }
  458. tmp = tmp.nextSibling;
  459. }
  460. return children;
  461. },
  462. /**
  463. * Function: importNode
  464. *
  465. * Cross browser implementation for document.importNode. Uses document.importNode
  466. * in all browsers but IE, where the node is cloned by creating a new node and
  467. * copying all attributes and children into it using importNode, recursively.
  468. *
  469. * Parameters:
  470. *
  471. * doc - Document to import the node into.
  472. * node - Node to be imported.
  473. * allChildren - If all children should be imported.
  474. */
  475. importNode: function(doc, node, allChildren)
  476. {
  477. if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 10))
  478. {
  479. return mxUtils.importNodeImplementation(doc, node, allChildren);
  480. }
  481. else
  482. {
  483. return doc.importNode(node, allChildren);
  484. }
  485. },
  486. /**
  487. * Function: importNodeImplementation
  488. *
  489. * Full DOM API implementation for importNode without using importNode API call.
  490. *
  491. * Parameters:
  492. *
  493. * doc - Document to import the node into.
  494. * node - Node to be imported.
  495. * allChildren - If all children should be imported.
  496. */
  497. importNodeImplementation: function(doc, node, allChildren)
  498. {
  499. switch (node.nodeType)
  500. {
  501. case 1: /* element */
  502. {
  503. var newNode = doc.createElement(node.nodeName);
  504. if (node.attributes && node.attributes.length > 0)
  505. {
  506. for (var i = 0; i < node.attributes.length; i++)
  507. {
  508. newNode.setAttribute(node.attributes[i].nodeName,
  509. node.getAttribute(node.attributes[i].nodeName));
  510. }
  511. }
  512. if (allChildren && node.childNodes && node.childNodes.length > 0)
  513. {
  514. for (var i = 0; i < node.childNodes.length; i++)
  515. {
  516. newNode.appendChild(mxUtils.importNodeImplementation(doc, node.childNodes[i], allChildren));
  517. }
  518. }
  519. return newNode;
  520. break;
  521. }
  522. case 3: /* text */
  523. case 4: /* cdata-section */
  524. case 8: /* comment */
  525. {
  526. return doc.createTextNode((node.nodeValue != null) ? node.nodeValue : node.value);
  527. break;
  528. }
  529. };
  530. },
  531. /**
  532. * Function: createXmlDocument
  533. *
  534. * Returns a new, empty XML document.
  535. */
  536. createXmlDocument: function()
  537. {
  538. var doc = null;
  539. if (document.implementation && document.implementation.createDocument)
  540. {
  541. doc = document.implementation.createDocument('', '', null);
  542. }
  543. else if ("ActiveXObject" in window)
  544. {
  545. doc = mxUtils.createMsXmlDocument();
  546. }
  547. return doc;
  548. },
  549. /**
  550. * Function: createMsXmlDocument
  551. *
  552. * Returns a new, empty Microsoft.XMLDOM document using ActiveXObject.
  553. */
  554. createMsXmlDocument: function()
  555. {
  556. var doc = new ActiveXObject('Microsoft.XMLDOM');
  557. doc.async = false;
  558. // Workaround for parsing errors with SVG DTD
  559. doc.validateOnParse = false;
  560. doc.resolveExternals = false;
  561. return doc;
  562. },
  563. /**
  564. * Function: parseXml
  565. *
  566. * Parses the specified XML string into a new XML document and returns the
  567. * new document.
  568. *
  569. * Example:
  570. *
  571. * (code)
  572. * var doc = mxUtils.parseXml(
  573. * '<mxGraphModel><root><MyDiagram id="0"><mxCell/></MyDiagram>'+
  574. * '<MyLayer id="1"><mxCell parent="0" /></MyLayer><MyObject id="2">'+
  575. * '<mxCell style="strokeColor=blue;fillColor=red" parent="1" vertex="1">'+
  576. * '<mxGeometry x="10" y="10" width="80" height="30" as="geometry"/>'+
  577. * '</mxCell></MyObject></root></mxGraphModel>');
  578. * (end)
  579. *
  580. * Parameters:
  581. *
  582. * xml - String that contains the XML data.
  583. */
  584. parseXml: function()
  585. {
  586. if (window.DOMParser)
  587. {
  588. return function(xml)
  589. {
  590. var parser = new DOMParser();
  591. return parser.parseFromString(xml, 'text/xml');
  592. };
  593. }
  594. else // IE<=9
  595. {
  596. return function(xml)
  597. {
  598. var doc = mxUtils.createMsXmlDocument();
  599. doc.loadXML(xml);
  600. return doc;
  601. };
  602. }
  603. }(),
  604. /**
  605. * Function: clearSelection
  606. *
  607. * Clears the current selection in the page.
  608. */
  609. clearSelection: function()
  610. {
  611. if (document.selection)
  612. {
  613. return function()
  614. {
  615. document.selection.empty();
  616. };
  617. }
  618. else if (window.getSelection)
  619. {
  620. return function()
  621. {
  622. if (window.getSelection().empty)
  623. {
  624. window.getSelection().empty();
  625. }
  626. else if (window.getSelection().removeAllRanges)
  627. {
  628. window.getSelection().removeAllRanges();
  629. }
  630. };
  631. }
  632. else
  633. {
  634. return function() { };
  635. }
  636. }(),
  637. /**
  638. * Function: removeWhitespace
  639. *
  640. * Removes the sibling text nodes for the given node that only consists
  641. * of tabs, newlines and spaces.
  642. *
  643. * Parameters:
  644. *
  645. * node - DOM node whose siblings should be removed.
  646. * before - Optional boolean that specifies the direction of the traversal.
  647. */
  648. removeWhitespace: function(node, before)
  649. {
  650. var tmp = (before) ? node.previousSibling : node.nextSibling;
  651. while (tmp != null && tmp.nodeType == mxConstants.NODETYPE_TEXT)
  652. {
  653. var next = (before) ? tmp.previousSibling : tmp.nextSibling;
  654. var text = mxUtils.getTextContent(tmp);
  655. if (mxUtils.trim(text).length == 0)
  656. {
  657. tmp.parentNode.removeChild(tmp);
  658. }
  659. tmp = next;
  660. }
  661. },
  662. /**
  663. * Function: htmlEntities
  664. *
  665. * Replaces characters (less than, greater than, newlines and quotes) with
  666. * their HTML entities in the given string and returns the result.
  667. *
  668. * Parameters:
  669. *
  670. * s - String that contains the characters to be converted.
  671. * newline - If newlines should be replaced. Default is true.
  672. */
  673. htmlEntities: function(s, newline)
  674. {
  675. s = String(s || '');
  676. s = s.replace(/&/g,'&amp;'); // 38 26
  677. s = s.replace(/"/g,'&quot;'); // 34 22
  678. s = s.replace(/\'/g,'&#39;'); // 39 27
  679. s = s.replace(/</g,'&lt;'); // 60 3C
  680. s = s.replace(/>/g,'&gt;'); // 62 3E
  681. if (newline == null || newline)
  682. {
  683. s = s.replace(/\n/g, '&#xa;');
  684. }
  685. return s;
  686. },
  687. /**
  688. * Function: isVml
  689. *
  690. * Returns true if the given node is in the VML namespace.
  691. *
  692. * Parameters:
  693. *
  694. * node - DOM node whose tag urn should be checked.
  695. */
  696. isVml: function(node)
  697. {
  698. return node != null && node.tagUrn == 'urn:schemas-microsoft-com:vml';
  699. },
  700. /**
  701. * Function: getXml
  702. *
  703. * Returns the XML content of the specified node. For Internet Explorer,
  704. * all \r\n\t[\t]* are removed from the XML string and the remaining \r\n
  705. * are replaced by \n. All \n are then replaced with linefeed, or &#xa; if
  706. * no linefeed is defined.
  707. *
  708. * Parameters:
  709. *
  710. * node - DOM node to return the XML for.
  711. * linefeed - Optional string that linefeeds are converted into. Default is
  712. * &#xa;
  713. */
  714. getXml: function(node, linefeed)
  715. {
  716. var xml = '';
  717. if (mxClient.IS_IE || mxClient.IS_IE11)
  718. {
  719. xml = mxUtils.getPrettyXml(node, '', '', '');
  720. }
  721. else if (window.XMLSerializer != null)
  722. {
  723. var xmlSerializer = new XMLSerializer();
  724. xml = xmlSerializer.serializeToString(node);
  725. }
  726. else if (node.xml != null)
  727. {
  728. xml = node.xml.replace(/\r\n\t[\t]*/g, '').
  729. replace(/>\r\n/g, '>').
  730. replace(/\r\n/g, '\n');
  731. }
  732. // Replaces linefeeds with HTML Entities.
  733. linefeed = linefeed || '&#xa;';
  734. xml = xml.replace(/\n/g, linefeed);
  735. return xml;
  736. },
  737. /**
  738. * Function: getPrettyXML
  739. *
  740. * Returns a pretty printed string that represents the XML tree for the
  741. * given node. This method should only be used to print XML for reading,
  742. * use <getXml> instead to obtain a string for processing.
  743. *
  744. * Parameters:
  745. *
  746. * node - DOM node to return the XML for.
  747. * tab - Optional string that specifies the indentation for one level.
  748. * Default is two spaces.
  749. * indent - Optional string that represents the current indentation.
  750. * Default is an empty string.
  751. * newline - Option string that represents a linefeed. Default is '\n'.
  752. */
  753. getPrettyXml: function(node, tab, indent, newline, ns)
  754. {
  755. var result = [];
  756. if (node != null)
  757. {
  758. tab = (tab != null) ? tab : ' ';
  759. indent = (indent != null) ? indent : '';
  760. newline = (newline != null) ? newline : '\n';
  761. if (node.namespaceURI != null && node.namespaceURI != ns)
  762. {
  763. ns = node.namespaceURI;
  764. if (node.getAttribute('xmlns') == null)
  765. {
  766. node.setAttribute('xmlns', node.namespaceURI);
  767. }
  768. }
  769. if (node.nodeType == mxConstants.NODETYPE_DOCUMENT)
  770. {
  771. result.push(mxUtils.getPrettyXml(node.documentElement, tab, indent, newline, ns));
  772. }
  773. else if (node.nodeType == mxConstants.NODETYPE_DOCUMENT_FRAGMENT)
  774. {
  775. var tmp = node.firstChild;
  776. if (tmp != null)
  777. {
  778. while (tmp != null)
  779. {
  780. result.push(mxUtils.getPrettyXml(tmp, tab, indent, newline, ns));
  781. tmp = tmp.nextSibling;
  782. }
  783. }
  784. }
  785. else if (node.nodeType == mxConstants.NODETYPE_COMMENT)
  786. {
  787. var value = mxUtils.getTextContent(node);
  788. if (value.length > 0)
  789. {
  790. result.push(indent + '<!--' + value + '-->' + newline);
  791. }
  792. }
  793. else if (node.nodeType == mxConstants.NODETYPE_TEXT)
  794. {
  795. var value = mxUtils.trim(mxUtils.getTextContent(node));
  796. if (value.length > 0)
  797. {
  798. result.push(indent + mxUtils.htmlEntities(value, false) + newline);
  799. }
  800. }
  801. else if (node.nodeType == mxConstants.NODETYPE_CDATA)
  802. {
  803. var value = mxUtils.getTextContent(node);
  804. if (value.length > 0)
  805. {
  806. result.push(indent + '<![CDATA[' + value + ']]' + newline);
  807. }
  808. }
  809. else
  810. {
  811. result.push(indent + '<' + node.nodeName);
  812. // Creates the string with the node attributes
  813. // and converts all HTML entities in the values
  814. var attrs = node.attributes;
  815. if (attrs != null)
  816. {
  817. for (var i = 0; i < attrs.length; i++)
  818. {
  819. var val = mxUtils.htmlEntities(attrs[i].value);
  820. result.push(' ' + attrs[i].nodeName + '="' + val + '"');
  821. }
  822. }
  823. // Recursively creates the XML string for each child
  824. // node and appends it here with an indentation
  825. var tmp = node.firstChild;
  826. if (tmp != null)
  827. {
  828. result.push('>' + newline);
  829. while (tmp != null)
  830. {
  831. result.push(mxUtils.getPrettyXml(tmp, tab, indent + tab, newline, ns));
  832. tmp = tmp.nextSibling;
  833. }
  834. result.push(indent + '</'+ node.nodeName + '>' + newline);
  835. }
  836. else
  837. {
  838. result.push(' />' + newline);
  839. }
  840. }
  841. }
  842. return result.join('');
  843. },
  844. /**
  845. * Function: extractTextWithWhitespace
  846. *
  847. * Returns the text content of the specified node.
  848. *
  849. * Parameters:
  850. *
  851. * elems - DOM nodes to return the text for.
  852. */
  853. extractTextWithWhitespace: function(elems)
  854. {
  855. // Known block elements for handling linefeeds (list is not complete)
  856. var blocks = ['BLOCKQUOTE', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'OL', 'P', 'PRE', 'TABLE', 'UL'];
  857. var ret = [];
  858. function doExtract(elts)
  859. {
  860. // Single break should be ignored
  861. if (elts.length == 1 && (elts[0].nodeName == 'BR' ||
  862. elts[0].innerHTML == '\n'))
  863. {
  864. return;
  865. }
  866. for (var i = 0; i < elts.length; i++)
  867. {
  868. var elem = elts[i];
  869. // DIV with a br or linefeed forces a linefeed
  870. if (elem.nodeName == 'BR' || elem.innerHTML == '\n' ||
  871. ((elts.length == 1 || i == 0) && (elem.nodeName == 'DIV' &&
  872. elem.innerHTML.toLowerCase() == '<br>')))
  873. {
  874. ret.push('\n');
  875. }
  876. else
  877. {
  878. if (elem.nodeType === 3 || elem.nodeType === 4)
  879. {
  880. if (elem.nodeValue.length > 0)
  881. {
  882. ret.push(elem.nodeValue);
  883. }
  884. }
  885. else if (elem.nodeType !== 8 && elem.childNodes.length > 0)
  886. {
  887. doExtract(elem.childNodes);
  888. }
  889. if (i < elts.length - 1 && mxUtils.indexOf(blocks, elts[i + 1].nodeName) >= 0)
  890. {
  891. ret.push('\n');
  892. }
  893. }
  894. }
  895. };
  896. doExtract(elems);
  897. return ret.join('');
  898. },
  899. /**
  900. * Function: replaceTrailingNewlines
  901. *
  902. * Replaces each trailing newline with the given pattern.
  903. */
  904. replaceTrailingNewlines: function(str, pattern)
  905. {
  906. // LATER: Check is this can be done with a regular expression
  907. var postfix = '';
  908. while (str.length > 0 && str.charAt(str.length - 1) == '\n')
  909. {
  910. str = str.substring(0, str.length - 1);
  911. postfix += pattern;
  912. }
  913. return str + postfix;
  914. },
  915. /**
  916. * Function: getTextContent
  917. *
  918. * Returns the text content of the specified node.
  919. *
  920. * Parameters:
  921. *
  922. * node - DOM node to return the text content for.
  923. */
  924. getTextContent: function(node)
  925. {
  926. // Only IE10-
  927. if (mxClient.IS_IE && node.innerText !== undefined)
  928. {
  929. return node.innerText;
  930. }
  931. else
  932. {
  933. return (node != null) ? node[(node.textContent === undefined) ? 'text' : 'textContent'] : '';
  934. }
  935. },
  936. /**
  937. * Function: setTextContent
  938. *
  939. * Sets the text content of the specified node.
  940. *
  941. * Parameters:
  942. *
  943. * node - DOM node to set the text content for.
  944. * text - String that represents the text content.
  945. */
  946. setTextContent: function(node, text)
  947. {
  948. if (node.innerText !== undefined)
  949. {
  950. node.innerText = text;
  951. }
  952. else
  953. {
  954. node[(node.textContent === undefined) ? 'text' : 'textContent'] = text;
  955. }
  956. },
  957. /**
  958. * Function: getInnerHtml
  959. *
  960. * Returns the inner HTML for the given node as a string or an empty string
  961. * if no node was specified. The inner HTML is the text representing all
  962. * children of the node, but not the node itself.
  963. *
  964. * Parameters:
  965. *
  966. * node - DOM node to return the inner HTML for.
  967. */
  968. getInnerHtml: function()
  969. {
  970. if (mxClient.IS_IE)
  971. {
  972. return function(node)
  973. {
  974. if (node != null)
  975. {
  976. return node.innerHTML;
  977. }
  978. return '';
  979. };
  980. }
  981. else
  982. {
  983. return function(node)
  984. {
  985. if (node != null)
  986. {
  987. var serializer = new XMLSerializer();
  988. return serializer.serializeToString(node);
  989. }
  990. return '';
  991. };
  992. }
  993. }(),
  994. /**
  995. * Function: getOuterHtml
  996. *
  997. * Returns the outer HTML for the given node as a string or an empty
  998. * string if no node was specified. The outer HTML is the text representing
  999. * all children of the node including the node itself.
  1000. *
  1001. * Parameters:
  1002. *
  1003. * node - DOM node to return the outer HTML for.
  1004. */
  1005. getOuterHtml: function()
  1006. {
  1007. if (mxClient.IS_IE)
  1008. {
  1009. return function(node)
  1010. {
  1011. if (node != null)
  1012. {
  1013. if (node.outerHTML != null)
  1014. {
  1015. return node.outerHTML;
  1016. }
  1017. else
  1018. {
  1019. var tmp = [];
  1020. tmp.push('<'+node.nodeName);
  1021. var attrs = node.attributes;
  1022. if (attrs != null)
  1023. {
  1024. for (var i = 0; i < attrs.length; i++)
  1025. {
  1026. var value = attrs[i].value;
  1027. if (value != null && value.length > 0)
  1028. {
  1029. tmp.push(' ');
  1030. tmp.push(attrs[i].nodeName);
  1031. tmp.push('="');
  1032. tmp.push(value);
  1033. tmp.push('"');
  1034. }
  1035. }
  1036. }
  1037. if (node.innerHTML.length == 0)
  1038. {
  1039. tmp.push('/>');
  1040. }
  1041. else
  1042. {
  1043. tmp.push('>');
  1044. tmp.push(node.innerHTML);
  1045. tmp.push('</'+node.nodeName+'>');
  1046. }
  1047. return tmp.join('');
  1048. }
  1049. }
  1050. return '';
  1051. };
  1052. }
  1053. else
  1054. {
  1055. return function(node)
  1056. {
  1057. if (node != null)
  1058. {
  1059. var serializer = new XMLSerializer();
  1060. return serializer.serializeToString(node);
  1061. }
  1062. return '';
  1063. };
  1064. }
  1065. }(),
  1066. /**
  1067. * Function: write
  1068. *
  1069. * Creates a text node for the given string and appends it to the given
  1070. * parent. Returns the text node.
  1071. *
  1072. * Parameters:
  1073. *
  1074. * parent - DOM node to append the text node to.
  1075. * text - String representing the text to be added.
  1076. */
  1077. write: function(parent, text)
  1078. {
  1079. var doc = parent.ownerDocument;
  1080. var node = doc.createTextNode(text);
  1081. if (parent != null)
  1082. {
  1083. parent.appendChild(node);
  1084. }
  1085. return node;
  1086. },
  1087. /**
  1088. * Function: writeln
  1089. *
  1090. * Creates a text node for the given string and appends it to the given
  1091. * parent with an additional linefeed. Returns the text node.
  1092. *
  1093. * Parameters:
  1094. *
  1095. * parent - DOM node to append the text node to.
  1096. * text - String representing the text to be added.
  1097. */
  1098. writeln: function(parent, text)
  1099. {
  1100. var doc = parent.ownerDocument;
  1101. var node = doc.createTextNode(text);
  1102. if (parent != null)
  1103. {
  1104. parent.appendChild(node);
  1105. parent.appendChild(document.createElement('br'));
  1106. }
  1107. return node;
  1108. },
  1109. /**
  1110. * Function: br
  1111. *
  1112. * Appends a linebreak to the given parent and returns the linebreak.
  1113. *
  1114. * Parameters:
  1115. *
  1116. * parent - DOM node to append the linebreak to.
  1117. */
  1118. br: function(parent, count)
  1119. {
  1120. count = count || 1;
  1121. var br = null;
  1122. for (var i = 0; i < count; i++)
  1123. {
  1124. if (parent != null)
  1125. {
  1126. br = parent.ownerDocument.createElement('br');
  1127. parent.appendChild(br);
  1128. }
  1129. }
  1130. return br;
  1131. },
  1132. /**
  1133. * Function: button
  1134. *
  1135. * Returns a new button with the given level and function as an onclick
  1136. * event handler.
  1137. *
  1138. * (code)
  1139. * document.body.appendChild(mxUtils.button('Test', function(evt)
  1140. * {
  1141. * alert('Hello, World!');
  1142. * }));
  1143. * (end)
  1144. *
  1145. * Parameters:
  1146. *
  1147. * label - String that represents the label of the button.
  1148. * funct - Function to be called if the button is pressed.
  1149. * doc - Optional document to be used for creating the button. Default is the
  1150. * current document.
  1151. */
  1152. button: function(label, funct, doc)
  1153. {
  1154. doc = (doc != null) ? doc : document;
  1155. var button = doc.createElement('button');
  1156. mxUtils.write(button, label);
  1157. mxEvent.addListener(button, 'click', function(evt)
  1158. {
  1159. funct(evt);
  1160. });
  1161. return button;
  1162. },
  1163. /**
  1164. * Function: para
  1165. *
  1166. * Appends a new paragraph with the given text to the specified parent and
  1167. * returns the paragraph.
  1168. *
  1169. * Parameters:
  1170. *
  1171. * parent - DOM node to append the text node to.
  1172. * text - String representing the text for the new paragraph.
  1173. */
  1174. para: function(parent, text)
  1175. {
  1176. var p = document.createElement('p');
  1177. mxUtils.write(p, text);
  1178. if (parent != null)
  1179. {
  1180. parent.appendChild(p);
  1181. }
  1182. return p;
  1183. },
  1184. /**
  1185. * Function: addTransparentBackgroundFilter
  1186. *
  1187. * Adds a transparent background to the filter of the given node. This
  1188. * background can be used in IE8 standards mode (native IE8 only) to pass
  1189. * events through the node.
  1190. */
  1191. addTransparentBackgroundFilter: function(node)
  1192. {
  1193. node.style.filter += 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' +
  1194. mxClient.imageBasePath + '/transparent.gif\', sizingMethod=\'scale\')';
  1195. },
  1196. /**
  1197. * Function: linkAction
  1198. *
  1199. * Adds a hyperlink to the specified parent that invokes action on the
  1200. * specified editor.
  1201. *
  1202. * Parameters:
  1203. *
  1204. * parent - DOM node to contain the new link.
  1205. * text - String that is used as the link label.
  1206. * editor - <mxEditor> that will execute the action.
  1207. * action - String that defines the name of the action to be executed.
  1208. * pad - Optional left-padding for the link. Default is 0.
  1209. */
  1210. linkAction: function(parent, text, editor, action, pad)
  1211. {
  1212. return mxUtils.link(parent, text, function()
  1213. {
  1214. editor.execute(action);
  1215. }, pad);
  1216. },
  1217. /**
  1218. * Function: linkInvoke
  1219. *
  1220. * Adds a hyperlink to the specified parent that invokes the specified
  1221. * function on the editor passing along the specified argument. The
  1222. * function name is the name of a function of the editor instance,
  1223. * not an action name.
  1224. *
  1225. * Parameters:
  1226. *
  1227. * parent - DOM node to contain the new link.
  1228. * text - String that is used as the link label.
  1229. * editor - <mxEditor> instance to execute the function on.
  1230. * functName - String that represents the name of the function.
  1231. * arg - Object that represents the argument to the function.
  1232. * pad - Optional left-padding for the link. Default is 0.
  1233. */
  1234. linkInvoke: function(parent, text, editor, functName, arg, pad)
  1235. {
  1236. return mxUtils.link(parent, text, function()
  1237. {
  1238. editor[functName](arg);
  1239. }, pad);
  1240. },
  1241. /**
  1242. * Function: link
  1243. *
  1244. * Adds a hyperlink to the specified parent and invokes the given function
  1245. * when the link is clicked.
  1246. *
  1247. * Parameters:
  1248. *
  1249. * parent - DOM node to contain the new link.
  1250. * text - String that is used as the link label.
  1251. * funct - Function to execute when the link is clicked.
  1252. * pad - Optional left-padding for the link. Default is 0.
  1253. */
  1254. link: function(parent, text, funct, pad)
  1255. {
  1256. var a = document.createElement('span');
  1257. a.style.color = 'blue';
  1258. a.style.textDecoration = 'underline';
  1259. a.style.cursor = 'pointer';
  1260. if (pad != null)
  1261. {
  1262. a.style.paddingLeft = pad+'px';
  1263. }
  1264. mxEvent.addListener(a, 'click', funct);
  1265. mxUtils.write(a, text);
  1266. if (parent != null)
  1267. {
  1268. parent.appendChild(a);
  1269. }
  1270. return a;
  1271. },
  1272. /**
  1273. * Function: getDocumentSize
  1274. *
  1275. * Returns the client size for the current document as an <mxRectangle>.
  1276. */
  1277. getDocumentSize: function()
  1278. {
  1279. var b = document.body;
  1280. var d = document.documentElement;
  1281. try
  1282. {
  1283. return new mxRectangle(0, 0, b.clientWidth || d.clientWidth, Math.max(b.clientHeight || 0, d.clientHeight));
  1284. }
  1285. catch (e)
  1286. {
  1287. return new mxRectangle();
  1288. }
  1289. },
  1290. /**
  1291. * Function: fit
  1292. *
  1293. * Makes sure the given node is inside the visible area of the window. This
  1294. * is done by setting the left and top in the style.
  1295. */
  1296. fit: function(node)
  1297. {
  1298. var ds = mxUtils.getDocumentSize();
  1299. var left = parseInt(node.offsetLeft);
  1300. var width = parseInt(node.offsetWidth);
  1301. var offset = mxUtils.getDocumentScrollOrigin(node.ownerDocument);
  1302. var sl = offset.x;
  1303. var st = offset.y;
  1304. var b = document.body;
  1305. var d = document.documentElement;
  1306. var right = (sl) + ds.width;
  1307. if (left + width > right)
  1308. {
  1309. node.style.left = Math.max(sl, right - width) + 'px';
  1310. }
  1311. var top = parseInt(node.offsetTop);
  1312. var height = parseInt(node.offsetHeight);
  1313. var bottom = st + ds.height;
  1314. if (top + height > bottom)
  1315. {
  1316. node.style.top = Math.max(st, bottom - height) + 'px';
  1317. }
  1318. },
  1319. /**
  1320. * Function: load
  1321. *
  1322. * Loads the specified URL *synchronously* and returns the <mxXmlRequest>.
  1323. * Throws an exception if the file cannot be loaded. See <mxUtils.get> for
  1324. * an asynchronous implementation.
  1325. *
  1326. * Example:
  1327. *
  1328. * (code)
  1329. * try
  1330. * {
  1331. * var req = mxUtils.load(filename);
  1332. * var root = req.getDocumentElement();
  1333. * // Process XML DOM...
  1334. * }
  1335. * catch (ex)
  1336. * {
  1337. * mxUtils.alert('Cannot load '+filename+': '+ex);
  1338. * }
  1339. * (end)
  1340. *
  1341. * Parameters:
  1342. *
  1343. * url - URL to get the data from.
  1344. */
  1345. load: function(url)
  1346. {
  1347. var req = new mxXmlRequest(url, null, 'GET', false);
  1348. req.send();
  1349. return req;
  1350. },
  1351. /**
  1352. * Function: get
  1353. *
  1354. * Loads the specified URL *asynchronously* and invokes the given functions
  1355. * depending on the request status. Returns the <mxXmlRequest> in use. Both
  1356. * functions take the <mxXmlRequest> as the only parameter. See
  1357. * <mxUtils.load> for a synchronous implementation.
  1358. *
  1359. * Example:
  1360. *
  1361. * (code)
  1362. * mxUtils.get(url, function(req)
  1363. * {
  1364. * var node = req.getDocumentElement();
  1365. * // Process XML DOM...
  1366. * });
  1367. * (end)
  1368. *
  1369. * So for example, to load a diagram into an existing graph model, the
  1370. * following code is used.
  1371. *
  1372. * (code)
  1373. * mxUtils.get(url, function(req)
  1374. * {
  1375. * var node = req.getDocumentElement();
  1376. * var dec = new mxCodec(node.ownerDocument);
  1377. * dec.decode(node, graph.getModel());
  1378. * });
  1379. * (end)
  1380. *
  1381. * Parameters:
  1382. *
  1383. * url - URL to get the data from.
  1384. * onload - Optional function to execute for a successful response.
  1385. * onerror - Optional function to execute on error.
  1386. * binary - Optional boolean parameter that specifies if the request is
  1387. * binary.
  1388. * timeout - Optional timeout in ms before calling ontimeout.
  1389. * ontimeout - Optional function to execute on timeout.
  1390. * headers - Optional with headers, eg. {'Authorization': 'token xyz'}
  1391. */
  1392. get: function(url, onload, onerror, binary, timeout, ontimeout, headers)
  1393. {
  1394. var req = new mxXmlRequest(url, null, 'GET');
  1395. var setRequestHeaders = req.setRequestHeaders;
  1396. if (headers)
  1397. {
  1398. req.setRequestHeaders = function(request, params)
  1399. {
  1400. setRequestHeaders.apply(this, arguments);
  1401. for (var key in headers)
  1402. {
  1403. request.setRequestHeader(key, headers[key]);
  1404. }
  1405. };
  1406. }
  1407. if (binary != null)
  1408. {
  1409. req.setBinary(binary);
  1410. }
  1411. req.send(onload, onerror, timeout, ontimeout);
  1412. return req;
  1413. },
  1414. /**
  1415. * Function: getAll
  1416. *
  1417. * Loads the URLs in the given array *asynchronously* and invokes the given function
  1418. * if all requests returned with a valid 2xx status. The error handler is invoked
  1419. * once on the first error or invalid response.
  1420. *
  1421. * Parameters:
  1422. *
  1423. * urls - Array of URLs to be loaded.
  1424. * onload - Callback with array of <mxXmlRequests>.
  1425. * onerror - Optional function to execute on error.
  1426. */
  1427. getAll: function(urls, onload, onerror)
  1428. {
  1429. var remain = urls.length;
  1430. var result = [];
  1431. var errors = 0;
  1432. var err = function()
  1433. {
  1434. if (errors == 0 && onerror != null)
  1435. {
  1436. onerror();
  1437. }
  1438. errors++;
  1439. };
  1440. for (var i = 0; i < urls.length; i++)
  1441. {
  1442. (function(url, index)
  1443. {
  1444. mxUtils.get(url, function(req)
  1445. {
  1446. var status = req.getStatus();
  1447. if (status < 200 || status > 299)
  1448. {
  1449. err();
  1450. }
  1451. else
  1452. {
  1453. result[index] = req;
  1454. remain--;
  1455. if (remain == 0)
  1456. {
  1457. onload(result);
  1458. }
  1459. }
  1460. }, err);
  1461. })(urls[i], i);
  1462. }
  1463. if (remain == 0)
  1464. {
  1465. onload(result);
  1466. }
  1467. },
  1468. /**
  1469. * Function: post
  1470. *
  1471. * Posts the specified params to the given URL *asynchronously* and invokes
  1472. * the given functions depending on the request status. Returns the
  1473. * <mxXmlRequest> in use. Both functions take the <mxXmlRequest> as the
  1474. * only parameter. Make sure to use encodeURIComponent for the parameter
  1475. * values.
  1476. *
  1477. * Example:
  1478. *
  1479. * (code)
  1480. * mxUtils.post(url, 'key=value', function(req)
  1481. * {
  1482. * mxUtils.alert('Ready: '+req.isReady()+' Status: '+req.getStatus());
  1483. * // Process req.getDocumentElement() using DOM API if OK...
  1484. * });
  1485. * (end)
  1486. *
  1487. * Parameters:
  1488. *
  1489. * url - URL to get the data from.
  1490. * params - Parameters for the post request.
  1491. * onload - Optional function to execute for a successful response.
  1492. * onerror - Optional function to execute on error.
  1493. */
  1494. post: function(url, params, onload, onerror)
  1495. {
  1496. return new mxXmlRequest(url, params).send(onload, onerror);
  1497. },
  1498. /**
  1499. * Function: submit
  1500. *
  1501. * Submits the given parameters to the specified URL using
  1502. * <mxXmlRequest.simulate> and returns the <mxXmlRequest>.
  1503. * Make sure to use encodeURIComponent for the parameter
  1504. * values.
  1505. *
  1506. * Parameters:
  1507. *
  1508. * url - URL to get the data from.
  1509. * params - Parameters for the form.
  1510. * doc - Document to create the form in.
  1511. * target - Target to send the form result to.
  1512. */
  1513. submit: function(url, params, doc, target)
  1514. {
  1515. return new mxXmlRequest(url, params).simulate(doc, target);
  1516. },
  1517. /**
  1518. * Function: loadInto
  1519. *
  1520. * Loads the specified URL *asynchronously* into the specified document,
  1521. * invoking onload after the document has been loaded. This implementation
  1522. * does not use <mxXmlRequest>, but the document.load method.
  1523. *
  1524. * Parameters:
  1525. *
  1526. * url - URL to get the data from.
  1527. * doc - The document to load the URL into.
  1528. * onload - Function to execute when the URL has been loaded.
  1529. */
  1530. loadInto: function(url, doc, onload)
  1531. {
  1532. if (mxClient.IS_IE)
  1533. {
  1534. doc.onreadystatechange = function ()
  1535. {
  1536. if (doc.readyState == 4)
  1537. {
  1538. onload();
  1539. }
  1540. };
  1541. }
  1542. else
  1543. {
  1544. doc.addEventListener('load', onload, false);
  1545. }
  1546. doc.load(url);
  1547. },
  1548. /**
  1549. * Function: getValue
  1550. *
  1551. * Returns the value for the given key in the given associative array or
  1552. * the given default value if the value is null.
  1553. *
  1554. * Parameters:
  1555. *
  1556. * array - Associative array that contains the value for the key.
  1557. * key - Key whose value should be returned.
  1558. * defaultValue - Value to be returned if the value for the given
  1559. * key is null.
  1560. */
  1561. getValue: function(array, key, defaultValue)
  1562. {
  1563. var value = (array != null) ? array[key] : null;
  1564. if (value == null)
  1565. {
  1566. value = defaultValue;
  1567. }
  1568. return value;
  1569. },
  1570. /**
  1571. * Function: getNumber
  1572. *
  1573. * Returns the numeric value for the given key in the given associative
  1574. * array or the given default value (or 0) if the value is null. The value
  1575. * is converted to a numeric value using the Number function.
  1576. *
  1577. * Parameters:
  1578. *
  1579. * array - Associative array that contains the value for the key.
  1580. * key - Key whose value should be returned.
  1581. * defaultValue - Value to be returned if the value for the given
  1582. * key is null. Default is 0.
  1583. */
  1584. getNumber: function(array, key, defaultValue)
  1585. {
  1586. var value = (array != null) ? array[key] : null;
  1587. if (value == null)
  1588. {
  1589. value = defaultValue || 0;
  1590. }
  1591. return Number(value);
  1592. },
  1593. /**
  1594. * Function: getColor
  1595. *
  1596. * Returns the color value for the given key in the given associative
  1597. * array or the given default value if the value is null. If the value
  1598. * is <mxConstants.NONE> then null is returned.
  1599. *
  1600. * Parameters:
  1601. *
  1602. * array - Associative array that contains the value for the key.
  1603. * key - Key whose value should be returned.
  1604. * defaultValue - Value to be returned if the value for the given
  1605. * key is null. Default is null.
  1606. */
  1607. getColor: function(array, key, defaultValue)
  1608. {
  1609. var value = (array != null) ? array[key] : null;
  1610. if (value == null)
  1611. {
  1612. value = defaultValue;
  1613. }
  1614. else if (value == mxConstants.NONE)
  1615. {
  1616. value = null;
  1617. }
  1618. return value;
  1619. },
  1620. /**
  1621. * Function: clone
  1622. *
  1623. * Recursively clones the specified object ignoring all fieldnames in the
  1624. * given array of transient fields. <mxObjectIdentity.FIELD_NAME> is always
  1625. * ignored by this function.
  1626. *
  1627. * Parameters:
  1628. *
  1629. * obj - Object to be cloned.
  1630. * transients - Optional array of strings representing the fieldname to be
  1631. * ignored.
  1632. * shallow - Optional boolean argument to specify if a shallow clone should
  1633. * be created, that is, one where all object references are not cloned or,
  1634. * in other words, one where only atomic (strings, numbers) values are
  1635. * cloned. Default is false.
  1636. */
  1637. clone: function(obj, transients, shallow)
  1638. {
  1639. shallow = (shallow != null) ? shallow : false;
  1640. var clone = null;
  1641. if (obj != null && typeof(obj.constructor) == 'function')
  1642. {
  1643. clone = new obj.constructor();
  1644. for (var i in obj)
  1645. {
  1646. if (i != mxObjectIdentity.FIELD_NAME && (transients == null ||
  1647. mxUtils.indexOf(transients, i) < 0))
  1648. {
  1649. if (!shallow && typeof(obj[i]) == 'object')
  1650. {
  1651. clone[i] = mxUtils.clone(obj[i]);
  1652. }
  1653. else
  1654. {
  1655. clone[i] = obj[i];
  1656. }
  1657. }
  1658. }
  1659. }
  1660. return clone;
  1661. },
  1662. /**
  1663. * Function: equalPoints
  1664. *
  1665. * Compares all mxPoints in the given lists.
  1666. *
  1667. * Parameters:
  1668. *
  1669. * a - Array of <mxPoints> to be compared.
  1670. * b - Array of <mxPoints> to be compared.
  1671. */
  1672. equalPoints: function(a, b)
  1673. {
  1674. if ((a == null && b != null) || (a != null && b == null) ||
  1675. (a != null && b != null && a.length != b.length))
  1676. {
  1677. return false;
  1678. }
  1679. else if (a != null && b != null)
  1680. {
  1681. for (var i = 0; i < a.length; i++)
  1682. {
  1683. if ((a[i] != null && b[i] == null) ||
  1684. (a[i] == null && b[i] != null) ||
  1685. (a[i] != null && b[i] != null &&
  1686. (a[i].x != b[i].x || a[i].y != b[i].y)))
  1687. {
  1688. return false;
  1689. }
  1690. }
  1691. }
  1692. return true;
  1693. },
  1694. /**
  1695. * Function: equalEntries
  1696. *
  1697. * Returns true if all properties of the given objects are equal. Values
  1698. * with NaN are equal to NaN and unequal to any other value.
  1699. *
  1700. * Parameters:
  1701. *
  1702. * a - First object to be compared.
  1703. * b - Second object to be compared.
  1704. */
  1705. equalEntries: function(a, b)
  1706. {
  1707. // Counts keys in b to check if all values have been compared
  1708. var count = 0;
  1709. if ((a == null && b != null) || (a != null && b == null) ||
  1710. (a != null && b != null && a.length != b.length))
  1711. {
  1712. return false;
  1713. }
  1714. else if (a != null && b != null)
  1715. {
  1716. for (var key in b)
  1717. {
  1718. count++;
  1719. }
  1720. for (var key in a)
  1721. {
  1722. count--
  1723. if ((!mxUtils.isNaN(a[key]) || !mxUtils.isNaN(b[key])) && a[key] != b[key])
  1724. {
  1725. return false;
  1726. }
  1727. }
  1728. }
  1729. return count == 0;
  1730. },
  1731. /**
  1732. * Function: removeDuplicates
  1733. *
  1734. * Removes all duplicates from the given array.
  1735. */
  1736. removeDuplicates: function(arr)
  1737. {
  1738. var dict = new mxDictionary();
  1739. var result = [];
  1740. for (var i = 0; i < arr.length; i++)
  1741. {
  1742. if (!dict.get(arr[i]))
  1743. {
  1744. result.push(arr[i]);
  1745. dict.put(arr[i], true);
  1746. }
  1747. }
  1748. return result;
  1749. },
  1750. /**
  1751. * Function: isNaN
  1752. *
  1753. * Returns true if the given value is of type number and isNaN returns true.
  1754. */
  1755. isNaN: function(value)
  1756. {
  1757. return typeof(value) == 'number' && isNaN(value);
  1758. },
  1759. /**
  1760. * Function: extend
  1761. *
  1762. * Assigns a copy of the superclass prototype to the subclass prototype.
  1763. * Note that this does not call the constructor of the superclass at this
  1764. * point, the superclass constructor should be called explicitely in the
  1765. * subclass constructor. Below is an example.
  1766. *
  1767. * (code)
  1768. * MyGraph = function(container, model, renderHint, stylesheet)
  1769. * {
  1770. * mxGraph.call(this, container, model, renderHint, stylesheet);
  1771. * }
  1772. *
  1773. * mxUtils.extend(MyGraph, mxGraph);
  1774. * (end)
  1775. *
  1776. * Parameters:
  1777. *
  1778. * ctor - Constructor of the subclass.
  1779. * superCtor - Constructor of the superclass.
  1780. */
  1781. extend: function(ctor, superCtor)
  1782. {
  1783. var f = function() {};
  1784. f.prototype = superCtor.prototype;
  1785. ctor.prototype = new f();
  1786. ctor.prototype.constructor = ctor;
  1787. },
  1788. /**
  1789. * Function: toString
  1790. *
  1791. * Returns a textual representation of the specified object.
  1792. *
  1793. * Parameters:
  1794. *
  1795. * obj - Object to return the string representation for.
  1796. */
  1797. toString: function(obj)
  1798. {
  1799. var output = '';
  1800. for (var i in obj)
  1801. {
  1802. try
  1803. {
  1804. if (obj[i] == null)
  1805. {
  1806. output += i + ' = [null]\n';
  1807. }
  1808. else if (typeof(obj[i]) == 'function')
  1809. {
  1810. output += i + ' => [Function]\n';
  1811. }
  1812. else if (typeof(obj[i]) == 'object')
  1813. {
  1814. var ctor = mxUtils.getFunctionName(obj[i].constructor);
  1815. output += i + ' => [' + ctor + ']\n';
  1816. }
  1817. else
  1818. {
  1819. output += i + ' = ' + obj[i] + '\n';
  1820. }
  1821. }
  1822. catch (e)
  1823. {
  1824. output += i + '=' + e.message;
  1825. }
  1826. }
  1827. return output;
  1828. },
  1829. /**
  1830. * Function: toRadians
  1831. *
  1832. * Converts the given degree to radians.
  1833. */
  1834. toRadians: function(deg)
  1835. {
  1836. return Math.PI * deg / 180;
  1837. },
  1838. /**
  1839. * Function: toDegree
  1840. *
  1841. * Converts the given radians to degree.
  1842. */
  1843. toDegree: function(rad)
  1844. {
  1845. return rad * 180 / Math.PI;
  1846. },
  1847. /**
  1848. * Function: arcToCurves
  1849. *
  1850. * Converts the given arc to a series of curves.
  1851. */
  1852. arcToCurves: function(x0, y0, r1, r2, angle, largeArcFlag, sweepFlag, x, y)
  1853. {
  1854. x -= x0;
  1855. y -= y0;
  1856. if (r1 === 0 || r2 === 0)
  1857. {
  1858. return result;
  1859. }
  1860. var fS = sweepFlag;
  1861. var psai = angle;
  1862. r1 = Math.abs(r1);
  1863. r2 = Math.abs(r2);
  1864. var ctx = -x / 2;
  1865. var cty = -y / 2;
  1866. var cpsi = Math.cos(psai * Math.PI / 180);
  1867. var spsi = Math.sin(psai * Math.PI / 180);
  1868. var rxd = cpsi * ctx + spsi * cty;
  1869. var ryd = -1 * spsi * ctx + cpsi * cty;
  1870. var rxdd = rxd * rxd;
  1871. var rydd = ryd * ryd;
  1872. var r1x = r1 * r1;
  1873. var r2y = r2 * r2;
  1874. var lamda = rxdd / r1x + rydd / r2y;
  1875. var sds;
  1876. if (lamda > 1)
  1877. {
  1878. r1 = Math.sqrt(lamda) * r1;
  1879. r2 = Math.sqrt(lamda) * r2;
  1880. sds = 0;
  1881. }
  1882. else
  1883. {
  1884. var seif = 1;
  1885. if (largeArcFlag === fS)
  1886. {
  1887. seif = -1;
  1888. }
  1889. sds = seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd));
  1890. }
  1891. var txd = sds * r1 * ryd / r2;
  1892. var tyd = -1 * sds * r2 * rxd / r1;
  1893. var tx = cpsi * txd - spsi * tyd + x / 2;
  1894. var ty = spsi * txd + cpsi * tyd + y / 2;
  1895. var rad = Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1) - Math.atan2(0, 1);
  1896. var s1 = (rad >= 0) ? rad : 2 * Math.PI + rad;
  1897. rad = Math.atan2((-ryd - tyd) / r2, (-rxd - txd) / r1) - Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1);
  1898. var dr = (rad >= 0) ? rad : 2 * Math.PI + rad;
  1899. if (fS == 0 && dr > 0)
  1900. {
  1901. dr -= 2 * Math.PI;
  1902. }
  1903. else if (fS != 0 && dr < 0)
  1904. {
  1905. dr += 2 * Math.PI;
  1906. }
  1907. var sse = dr * 2 / Math.PI;
  1908. var seg = Math.ceil(sse < 0 ? -1 * sse : sse);
  1909. var segr = dr / seg;
  1910. var t = 8/3 * Math.sin(segr / 4) * Math.sin(segr / 4) / Math.sin(segr / 2);
  1911. var cpsir1 = cpsi * r1;
  1912. var cpsir2 = cpsi * r2;
  1913. var spsir1 = spsi * r1;
  1914. var spsir2 = spsi * r2;
  1915. var mc = Math.cos(s1);
  1916. var ms = Math.sin(s1);
  1917. var x2 = -t * (cpsir1 * ms + spsir2 * mc);
  1918. var y2 = -t * (spsir1 * ms - cpsir2 * mc);
  1919. var x3 = 0;
  1920. var y3 = 0;
  1921. var result = [];
  1922. for (var n = 0; n < seg; ++n)
  1923. {
  1924. s1 += segr;
  1925. mc = Math.cos(s1);
  1926. ms = Math.sin(s1);
  1927. x3 = cpsir1 * mc - spsir2 * ms + tx;
  1928. y3 = spsir1 * mc + cpsir2 * ms + ty;
  1929. var dx = -t * (cpsir1 * ms + spsir2 * mc);
  1930. var dy = -t * (spsir1 * ms - cpsir2 * mc);
  1931. // CurveTo updates x0, y0 so need to restore it
  1932. var index = n * 6;
  1933. result[index] = Number(x2 + x0);
  1934. result[index + 1] = Number(y2 + y0);
  1935. result[index + 2] = Number(x3 - dx + x0);
  1936. result[index + 3] = Number(y3 - dy + y0);
  1937. result[index + 4] = Number(x3 + x0);
  1938. result[index + 5] = Number(y3 + y0);
  1939. x2 = x3 + dx;
  1940. y2 = y3 + dy;
  1941. }
  1942. return result;
  1943. },
  1944. /**
  1945. * Function: getBoundingBox
  1946. *
  1947. * Returns the bounding box for the rotated rectangle.
  1948. *
  1949. * Parameters:
  1950. *
  1951. * rect - <mxRectangle> to be rotated.
  1952. * angle - Number that represents the angle (in degrees).
  1953. * cx - Optional <mxPoint> that represents the rotation center. If no
  1954. * rotation center is given then the center of rect is used.
  1955. */
  1956. getBoundingBox: function(rect, rotation, cx)
  1957. {
  1958. var result = null;
  1959. if (rect != null && rotation != null && rotation != 0)
  1960. {
  1961. var rad = mxUtils.toRadians(rotation);
  1962. var cos = Math.cos(rad);
  1963. var sin = Math.sin(rad);
  1964. cx = (cx != null) ? cx : new mxPoint(rect.x + rect.width / 2, rect.y + rect.height / 2);
  1965. var p1 = new mxPoint(rect.x, rect.y);
  1966. var p2 = new mxPoint(rect.x + rect.width, rect.y);
  1967. var p3 = new mxPoint(p2.x, rect.y + rect.height);
  1968. var p4 = new mxPoint(rect.x, p3.y);
  1969. p1 = mxUtils.getRotatedPoint(p1, cos, sin, cx);
  1970. p2 = mxUtils.getRotatedPoint(p2, cos, sin, cx);
  1971. p3 = mxUtils.getRotatedPoint(p3, cos, sin, cx);
  1972. p4 = mxUtils.getRotatedPoint(p4, cos, sin, cx);
  1973. result = new mxRectangle(p1.x, p1.y, 0, 0);
  1974. result.add(new mxRectangle(p2.x, p2.y, 0, 0));
  1975. result.add(new mxRectangle(p3.x, p3.y, 0, 0));
  1976. result.add(new mxRectangle(p4.x, p4.y, 0, 0));
  1977. }
  1978. return result;
  1979. },
  1980. /**
  1981. * Function: getRotatedPoint
  1982. *
  1983. * Rotates the given point by the given cos and sin.
  1984. */
  1985. getRotatedPoint: function(pt, cos, sin, c)
  1986. {
  1987. c = (c != null) ? c : new mxPoint();
  1988. var x = pt.x - c.x;
  1989. var y = pt.y - c.y;
  1990. var x1 = x * cos - y * sin;
  1991. var y1 = y * cos + x * sin;
  1992. return new mxPoint(x1 + c.x, y1 + c.y);
  1993. },
  1994. /**
  1995. * Returns an integer mask of the port constraints of the given map
  1996. * @param dict the style map to determine the port constraints for
  1997. * @param defaultValue Default value to return if the key is undefined.
  1998. * @return the mask of port constraint directions
  1999. *
  2000. * Parameters:
  2001. *
  2002. * terminal - <mxCelState> that represents the terminal.
  2003. * edge - <mxCellState> that represents the edge.
  2004. * source - Boolean that specifies if the terminal is the source terminal.
  2005. * defaultValue - Default value to be returned.
  2006. */
  2007. getPortConstraints: function(terminal, edge, source, defaultValue)
  2008. {
  2009. var value = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT,
  2010. mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_SOURCE_PORT_CONSTRAINT :
  2011. mxConstants.STYLE_TARGET_PORT_CONSTRAINT, null));
  2012. if (value == null)
  2013. {
  2014. return defaultValue;
  2015. }
  2016. else
  2017. {
  2018. var directions = value.toString();
  2019. var returnValue = mxConstants.DIRECTION_MASK_NONE;
  2020. var constraintRotationEnabled = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT_ROTATION, 0);
  2021. var rotation = 0;
  2022. if (constraintRotationEnabled == 1)
  2023. {
  2024. rotation = mxUtils.getValue(terminal.style, mxConstants.STYLE_ROTATION, 0);
  2025. }
  2026. var quad = 0;
  2027. if (rotation > 45)
  2028. {
  2029. quad = 1;
  2030. if (rotation >= 135)
  2031. {
  2032. quad = 2;
  2033. }
  2034. }
  2035. else if (rotation < -45)
  2036. {
  2037. quad = 3;
  2038. if (rotation <= -135)
  2039. {
  2040. quad = 2;
  2041. }
  2042. }
  2043. if (directions.indexOf(mxConstants.DIRECTION_NORTH) >= 0)
  2044. {
  2045. switch (quad)
  2046. {
  2047. case 0:
  2048. returnValue |= mxConstants.DIRECTION_MASK_NORTH;
  2049. break;
  2050. case 1:
  2051. returnValue |= mxConstants.DIRECTION_MASK_EAST;
  2052. break;
  2053. case 2:
  2054. returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
  2055. break;
  2056. case 3:
  2057. returnValue |= mxConstants.DIRECTION_MASK_WEST;
  2058. break;
  2059. }
  2060. }
  2061. if (directions.indexOf(mxConstants.DIRECTION_WEST) >= 0)
  2062. {
  2063. switch (quad)
  2064. {
  2065. case 0:
  2066. returnValue |= mxConstants.DIRECTION_MASK_WEST;
  2067. break;
  2068. case 1:
  2069. returnValue |= mxConstants.DIRECTION_MASK_NORTH;
  2070. break;
  2071. case 2:
  2072. returnValue |= mxConstants.DIRECTION_MASK_EAST;
  2073. break;
  2074. case 3:
  2075. returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
  2076. break;
  2077. }
  2078. }
  2079. if (directions.indexOf(mxConstants.DIRECTION_SOUTH) >= 0)
  2080. {
  2081. switch (quad)
  2082. {
  2083. case 0:
  2084. returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
  2085. break;
  2086. case 1:
  2087. returnValue |= mxConstants.DIRECTION_MASK_WEST;
  2088. break;
  2089. case 2:
  2090. returnValue |= mxConstants.DIRECTION_MASK_NORTH;
  2091. break;
  2092. case 3:
  2093. returnValue |= mxConstants.DIRECTION_MASK_EAST;
  2094. break;
  2095. }
  2096. }
  2097. if (directions.indexOf(mxConstants.DIRECTION_EAST) >= 0)
  2098. {
  2099. switch (quad)
  2100. {
  2101. case 0:
  2102. returnValue |= mxConstants.DIRECTION_MASK_EAST;
  2103. break;
  2104. case 1:
  2105. returnValue |= mxConstants.DIRECTION_MASK_SOUTH;
  2106. break;
  2107. case 2:
  2108. returnValue |= mxConstants.DIRECTION_MASK_WEST;
  2109. break;
  2110. case 3:
  2111. returnValue |= mxConstants.DIRECTION_MASK_NORTH;
  2112. break;
  2113. }
  2114. }
  2115. return returnValue;
  2116. }
  2117. },
  2118. /**
  2119. * Function: reversePortConstraints
  2120. *
  2121. * Reverse the port constraint bitmask. For example, north | east
  2122. * becomes south | west
  2123. */
  2124. reversePortConstraints: function(constraint)
  2125. {
  2126. var result = 0;
  2127. result = (constraint & mxConstants.DIRECTION_MASK_WEST) << 3;
  2128. result |= (constraint & mxConstants.DIRECTION_MASK_NORTH) << 1;
  2129. result |= (constraint & mxConstants.DIRECTION_MASK_SOUTH) >> 1;
  2130. result |= (constraint & mxConstants.DIRECTION_MASK_EAST) >> 3;
  2131. return result;
  2132. },
  2133. /**
  2134. * Function: findNearestSegment
  2135. *
  2136. * Finds the index of the nearest segment on the given cell state for
  2137. * the specified coordinate pair.
  2138. */
  2139. findNearestSegment: function(state, x, y)
  2140. {
  2141. var index = -1;
  2142. if (state.absolutePoints.length > 0)
  2143. {
  2144. var last = state.absolutePoints[0];
  2145. var min = null;
  2146. for (var i = 1; i < state.absolutePoints.length; i++)
  2147. {
  2148. var current = state.absolutePoints[i];
  2149. var dist = mxUtils.ptSegDistSq(last.x, last.y,
  2150. current.x, current.y, x, y);
  2151. if (min == null || dist < min)
  2152. {
  2153. min = dist;
  2154. index = i - 1;
  2155. }
  2156. last = current;
  2157. }
  2158. }
  2159. return index;
  2160. },
  2161. /**
  2162. * Function: getDirectedBounds
  2163. *
  2164. * Adds the given margins to the given rectangle and rotates and flips the
  2165. * rectangle according to the respective styles in style.
  2166. */
  2167. getDirectedBounds: function (rect, m, style, flipH, flipV)
  2168. {
  2169. var d = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
  2170. flipH = (flipH != null) ? flipH : mxUtils.getValue(style, mxConstants.STYLE_FLIPH, false);
  2171. flipV = (flipV != null) ? flipV : mxUtils.getValue(style, mxConstants.STYLE_FLIPV, false);
  2172. m.x = Math.round(Math.max(0, Math.min(rect.width, m.x)));
  2173. m.y = Math.round(Math.max(0, Math.min(rect.height, m.y)));
  2174. m.width = Math.round(Math.max(0, Math.min(rect.width, m.width)));
  2175. m.height = Math.round(Math.max(0, Math.min(rect.height, m.height)));
  2176. if ((flipV && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) ||
  2177. (flipH && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST)))
  2178. {
  2179. var tmp = m.x;
  2180. m.x = m.width;
  2181. m.width = tmp;
  2182. }
  2183. if ((flipH && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) ||
  2184. (flipV && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST)))
  2185. {
  2186. var tmp = m.y;
  2187. m.y = m.height;
  2188. m.height = tmp;
  2189. }
  2190. var m2 = mxRectangle.fromRectangle(m);
  2191. if (d == mxConstants.DIRECTION_SOUTH)
  2192. {
  2193. m2.y = m.x;
  2194. m2.x = m.height;
  2195. m2.width = m.y;
  2196. m2.height = m.width;
  2197. }
  2198. else if (d == mxConstants.DIRECTION_WEST)
  2199. {
  2200. m2.y = m.height;
  2201. m2.x = m.width;
  2202. m2.width = m.x;
  2203. m2.height = m.y;
  2204. }
  2205. else if (d == mxConstants.DIRECTION_NORTH)
  2206. {
  2207. m2.y = m.width;
  2208. m2.x = m.y;
  2209. m2.width = m.height;
  2210. m2.height = m.x;
  2211. }
  2212. return new mxRectangle(rect.x + m2.x, rect.y + m2.y, rect.width - m2.width - m2.x, rect.height - m2.height - m2.y);
  2213. },
  2214. /**
  2215. * Function: getPerimeterPoint
  2216. *
  2217. * Returns the intersection between the polygon defined by the array of
  2218. * points and the line between center and point.
  2219. */
  2220. getPerimeterPoint: function (pts, center, point)
  2221. {
  2222. var min = null;
  2223. for (var i = 0; i < pts.length - 1; i++)
  2224. {
  2225. var pt = mxUtils.intersection(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y,
  2226. center.x, center.y, point.x, point.y);
  2227. if (pt != null)
  2228. {
  2229. var dx = point.x - pt.x;
  2230. var dy = point.y - pt.y;
  2231. var ip = {p: pt, distSq: dy * dy + dx * dx};
  2232. if (ip != null && (min == null || min.distSq > ip.distSq))
  2233. {
  2234. min = ip;
  2235. }
  2236. }
  2237. }
  2238. return (min != null) ? min.p : null;
  2239. },
  2240. /**
  2241. * Function: rectangleIntersectsSegment
  2242. *
  2243. * Returns true if the given rectangle intersects the given segment.
  2244. *
  2245. * Parameters:
  2246. *
  2247. * bounds - <mxRectangle> that represents the rectangle.
  2248. * p1 - <mxPoint> that represents the first point of the segment.
  2249. * p2 - <mxPoint> that represents the second point of the segment.
  2250. */
  2251. rectangleIntersectsSegment: function(bounds, p1, p2)
  2252. {
  2253. var top = bounds.y;
  2254. var left = bounds.x;
  2255. var bottom = top + bounds.height;
  2256. var right = left + bounds.width;
  2257. // Find min and max X for the segment
  2258. var minX = p1.x;
  2259. var maxX = p2.x;
  2260. if (p1.x > p2.x)
  2261. {
  2262. minX = p2.x;
  2263. maxX = p1.x;
  2264. }
  2265. // Find the intersection of the segment's and rectangle's x-projections
  2266. if (maxX > right)
  2267. {
  2268. maxX = right;
  2269. }
  2270. if (minX < left)
  2271. {
  2272. minX = left;
  2273. }
  2274. if (minX > maxX) // If their projections do not intersect return false
  2275. {
  2276. return false;
  2277. }
  2278. // Find corresponding min and max Y for min and max X we found before
  2279. var minY = p1.y;
  2280. var maxY = p2.y;
  2281. var dx = p2.x - p1.x;
  2282. if (Math.abs(dx) > 0.0000001)
  2283. {
  2284. var a = (p2.y - p1.y) / dx;
  2285. var b = p1.y - a * p1.x;
  2286. minY = a * minX + b;
  2287. maxY = a * maxX + b;
  2288. }
  2289. if (minY > maxY)
  2290. {
  2291. var tmp = maxY;
  2292. maxY = minY;
  2293. minY = tmp;
  2294. }
  2295. // Find the intersection of the segment's and rectangle's y-projections
  2296. if (maxY > bottom)
  2297. {
  2298. maxY = bottom;
  2299. }
  2300. if (minY < top)
  2301. {
  2302. minY = top;
  2303. }
  2304. if (minY > maxY) // If Y-projections do not intersect return false
  2305. {
  2306. return false;
  2307. }
  2308. return true;
  2309. },
  2310. /**
  2311. * Function: contains
  2312. *
  2313. * Returns true if the specified point (x, y) is contained in the given rectangle.
  2314. *
  2315. * Parameters:
  2316. *
  2317. * bounds - <mxRectangle> that represents the area.
  2318. * x - X-coordinate of the point.
  2319. * y - Y-coordinate of the point.
  2320. */
  2321. contains: function(bounds, x, y)
  2322. {
  2323. return (bounds.x <= x && bounds.x + bounds.width >= x &&
  2324. bounds.y <= y && bounds.y + bounds.height >= y);
  2325. },
  2326. /**
  2327. * Function: intersects
  2328. *
  2329. * Returns true if the two rectangles intersect.
  2330. *
  2331. * Parameters:
  2332. *
  2333. * a - <mxRectangle> to be checked for intersection.
  2334. * b - <mxRectangle> to be checked for intersection.
  2335. */
  2336. intersects: function(a, b)
  2337. {
  2338. var tw = a.width;
  2339. var th = a.height;
  2340. var rw = b.width;
  2341. var rh = b.height;
  2342. if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0)
  2343. {
  2344. return false;
  2345. }
  2346. var tx = a.x;
  2347. var ty = a.y;
  2348. var rx = b.x;
  2349. var ry = b.y;
  2350. rw += rx;
  2351. rh += ry;
  2352. tw += tx;
  2353. th += ty;
  2354. return ((rw < rx || rw > tx) &&
  2355. (rh < ry || rh > ty) &&
  2356. (tw < tx || tw > rx) &&
  2357. (th < ty || th > ry));
  2358. },
  2359. /**
  2360. * Function: intersectsHotspot
  2361. *
  2362. * Returns true if the state and the hotspot intersect.
  2363. *
  2364. * Parameters:
  2365. *
  2366. * state - <mxCellState>
  2367. * x - X-coordinate.
  2368. * y - Y-coordinate.
  2369. * hotspot - Optional size of the hostpot.
  2370. * min - Optional min size of the hostpot.
  2371. * max - Optional max size of the hostpot.
  2372. */
  2373. intersectsHotspot: function(state, x, y, hotspot, min, max)
  2374. {
  2375. hotspot = (hotspot != null) ? hotspot : 1;
  2376. min = (min != null) ? min : 0;
  2377. max = (max != null) ? max : 0;
  2378. if (hotspot > 0)
  2379. {
  2380. var cx = state.getCenterX();
  2381. var cy = state.getCenterY();
  2382. var w = state.width;
  2383. var h = state.height;
  2384. var start = mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE) * state.view.scale;
  2385. if (start > 0)
  2386. {
  2387. if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, true))
  2388. {
  2389. cy = state.y + start / 2;
  2390. h = start;
  2391. }
  2392. else
  2393. {
  2394. cx = state.x + start / 2;
  2395. w = start;
  2396. }
  2397. }
  2398. w = Math.max(min, w * hotspot);
  2399. h = Math.max(min, h * hotspot);
  2400. if (max > 0)
  2401. {
  2402. w = Math.min(w, max);
  2403. h = Math.min(h, max);
  2404. }
  2405. var rect = new mxRectangle(cx - w / 2, cy - h / 2, w, h);
  2406. var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0);
  2407. if (alpha != 0)
  2408. {
  2409. var cos = Math.cos(-alpha);
  2410. var sin = Math.sin(-alpha);
  2411. var cx = new mxPoint(state.getCenterX(), state.getCenterY());
  2412. var pt = mxUtils.getRotatedPoint(new mxPoint(x, y), cos, sin, cx);
  2413. x = pt.x;
  2414. y = pt.y;
  2415. }
  2416. return mxUtils.contains(rect, x, y);
  2417. }
  2418. return true;
  2419. },
  2420. /**
  2421. * Function: getOffset
  2422. *
  2423. * Returns the offset for the specified container as an <mxPoint>. The
  2424. * offset is the distance from the top left corner of the container to the
  2425. * top left corner of the document.
  2426. *
  2427. * Parameters:
  2428. *
  2429. * container - DOM node to return the offset for.
  2430. * scollOffset - Optional boolean to add the scroll offset of the document.
  2431. * Default is false.
  2432. */
  2433. getOffset: function(container, scrollOffset)
  2434. {
  2435. var offsetLeft = 0;
  2436. var offsetTop = 0;
  2437. // Ignores document scroll origin for fixed elements
  2438. var fixed = false;
  2439. var node = container;
  2440. var b = document.body;
  2441. var d = document.documentElement;
  2442. while (node != null && node != b && node != d && !fixed)
  2443. {
  2444. var style = mxUtils.getCurrentStyle(node);
  2445. if (style != null)
  2446. {
  2447. fixed = fixed || style.position == 'fixed';
  2448. }
  2449. node = node.parentNode;
  2450. }
  2451. if (!scrollOffset && !fixed)
  2452. {
  2453. var offset = mxUtils.getDocumentScrollOrigin(container.ownerDocument);
  2454. offsetLeft += offset.x;
  2455. offsetTop += offset.y;
  2456. }
  2457. var r = container.getBoundingClientRect();
  2458. if (r != null)
  2459. {
  2460. offsetLeft += r.left;
  2461. offsetTop += r.top;
  2462. }
  2463. return new mxPoint(offsetLeft, offsetTop);
  2464. },
  2465. /**
  2466. * Function: getDocumentScrollOrigin
  2467. *
  2468. * Returns the scroll origin of the given document or the current document
  2469. * if no document is given.
  2470. */
  2471. getDocumentScrollOrigin: function(doc)
  2472. {
  2473. if (mxClient.IS_QUIRKS)
  2474. {
  2475. return new mxPoint(doc.body.scrollLeft, doc.body.scrollTop);
  2476. }
  2477. else
  2478. {
  2479. var wnd = doc.defaultView || doc.parentWindow;
  2480. var x = (wnd != null && window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
  2481. var y = (wnd != null && window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
  2482. return new mxPoint(x, y);
  2483. }
  2484. },
  2485. /**
  2486. * Function: getScrollOrigin
  2487. *
  2488. * Returns the top, left corner of the viewrect as an <mxPoint>.
  2489. *
  2490. * Parameters:
  2491. *
  2492. * node - DOM node whose scroll origin should be returned.
  2493. * includeAncestors - Whether the scroll origin of the ancestors should be
  2494. * included. Default is false.
  2495. * includeDocument - Whether the scroll origin of the document should be
  2496. * included. Default is true.
  2497. */
  2498. getScrollOrigin: function(node, includeAncestors, includeDocument)
  2499. {
  2500. includeAncestors = (includeAncestors != null) ? includeAncestors : false;
  2501. includeDocument = (includeDocument != null) ? includeDocument : true;
  2502. var doc = (node != null) ? node.ownerDocument : document;
  2503. var b = doc.body;
  2504. var d = doc.documentElement;
  2505. var result = new mxPoint();
  2506. var fixed = false;
  2507. while (node != null && node != b && node != d)
  2508. {
  2509. if (!isNaN(node.scrollLeft) && !isNaN(node.scrollTop))
  2510. {
  2511. result.x += node.scrollLeft;
  2512. result.y += node.scrollTop;
  2513. }
  2514. var style = mxUtils.getCurrentStyle(node);
  2515. if (style != null)
  2516. {
  2517. fixed = fixed || style.position == 'fixed';
  2518. }
  2519. node = (includeAncestors) ? node.parentNode : null;
  2520. }
  2521. if (!fixed && includeDocument)
  2522. {
  2523. var origin = mxUtils.getDocumentScrollOrigin(doc);
  2524. result.x += origin.x;
  2525. result.y += origin.y;
  2526. }
  2527. return result;
  2528. },
  2529. /**
  2530. * Function: convertPoint
  2531. *
  2532. * Converts the specified point (x, y) using the offset of the specified
  2533. * container and returns a new <mxPoint> with the result.
  2534. *
  2535. * (code)
  2536. * var pt = mxUtils.convertPoint(graph.container,
  2537. * mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  2538. * (end)
  2539. *
  2540. * Parameters:
  2541. *
  2542. * container - DOM node to use for the offset.
  2543. * x - X-coordinate of the point to be converted.
  2544. * y - Y-coordinate of the point to be converted.
  2545. */
  2546. convertPoint: function(container, x, y)
  2547. {
  2548. var origin = mxUtils.getScrollOrigin(container, false);
  2549. var offset = mxUtils.getOffset(container);
  2550. offset.x -= origin.x;
  2551. offset.y -= origin.y;
  2552. return new mxPoint(x - offset.x, y - offset.y);
  2553. },
  2554. /**
  2555. * Function: ltrim
  2556. *
  2557. * Strips all whitespaces from the beginning of the string. Without the
  2558. * second parameter, this will trim these characters:
  2559. *
  2560. * - " " (ASCII 32 (0x20)), an ordinary space
  2561. * - "\t" (ASCII 9 (0x09)), a tab
  2562. * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
  2563. * - "\r" (ASCII 13 (0x0D)), a carriage return
  2564. * - "\0" (ASCII 0 (0x00)), the NUL-byte
  2565. * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
  2566. */
  2567. ltrim: function(str, chars)
  2568. {
  2569. chars = chars || "\\s";
  2570. return (str != null) ? str.replace(new RegExp("^[" + chars + "]+", "g"), "") : null;
  2571. },
  2572. /**
  2573. * Function: rtrim
  2574. *
  2575. * Strips all whitespaces from the end of the string. Without the second
  2576. * parameter, this will trim these characters:
  2577. *
  2578. * - " " (ASCII 32 (0x20)), an ordinary space
  2579. * - "\t" (ASCII 9 (0x09)), a tab
  2580. * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
  2581. * - "\r" (ASCII 13 (0x0D)), a carriage return
  2582. * - "\0" (ASCII 0 (0x00)), the NUL-byte
  2583. * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
  2584. */
  2585. rtrim: function(str, chars)
  2586. {
  2587. chars = chars || "\\s";
  2588. return (str != null) ? str.replace(new RegExp("[" + chars + "]+$", "g"), "") : null;
  2589. },
  2590. /**
  2591. * Function: trim
  2592. *
  2593. * Strips all whitespaces from both end of the string.
  2594. * Without the second parameter, Javascript function will trim these
  2595. * characters:
  2596. *
  2597. * - " " (ASCII 32 (0x20)), an ordinary space
  2598. * - "\t" (ASCII 9 (0x09)), a tab
  2599. * - "\n" (ASCII 10 (0x0A)), a new line (line feed)
  2600. * - "\r" (ASCII 13 (0x0D)), a carriage return
  2601. * - "\0" (ASCII 0 (0x00)), the NUL-byte
  2602. * - "\x0B" (ASCII 11 (0x0B)), a vertical tab
  2603. */
  2604. trim: function(str, chars)
  2605. {
  2606. return mxUtils.ltrim(mxUtils.rtrim(str, chars), chars);
  2607. },
  2608. /**
  2609. * Function: isNumeric
  2610. *
  2611. * Returns true if the specified value is numeric, that is, if it is not
  2612. * null, not an empty string, not a HEX number and isNaN returns false.
  2613. *
  2614. * Parameters:
  2615. *
  2616. * n - String representing the possibly numeric value.
  2617. */
  2618. isNumeric: function(n)
  2619. {
  2620. return !isNaN(parseFloat(n)) && isFinite(n) && (typeof(n) != 'string' || n.toLowerCase().indexOf('0x') < 0);
  2621. },
  2622. /**
  2623. * Function: isInteger
  2624. *
  2625. * Returns true if the given value is an valid integer number.
  2626. *
  2627. * Parameters:
  2628. *
  2629. * n - String representing the possibly numeric value.
  2630. */
  2631. isInteger: function(n)
  2632. {
  2633. return String(parseInt(n)) === String(n);
  2634. },
  2635. /**
  2636. * Function: mod
  2637. *
  2638. * Returns the remainder of division of n by m. You should use this instead
  2639. * of the built-in operation as the built-in operation does not properly
  2640. * handle negative numbers.
  2641. */
  2642. mod: function(n, m)
  2643. {
  2644. return ((n % m) + m) % m;
  2645. },
  2646. /**
  2647. * Function: intersection
  2648. *
  2649. * Returns the intersection of two lines as an <mxPoint>.
  2650. *
  2651. * Parameters:
  2652. *
  2653. * x0 - X-coordinate of the first line's startpoint.
  2654. * y0 - X-coordinate of the first line's startpoint.
  2655. * x1 - X-coordinate of the first line's endpoint.
  2656. * y1 - Y-coordinate of the first line's endpoint.
  2657. * x2 - X-coordinate of the second line's startpoint.
  2658. * y2 - Y-coordinate of the second line's startpoint.
  2659. * x3 - X-coordinate of the second line's endpoint.
  2660. * y3 - Y-coordinate of the second line's endpoint.
  2661. */
  2662. intersection: function (x0, y0, x1, y1, x2, y2, x3, y3)
  2663. {
  2664. var denom = ((y3 - y2) * (x1 - x0)) - ((x3 - x2) * (y1 - y0));
  2665. var nume_a = ((x3 - x2) * (y0 - y2)) - ((y3 - y2) * (x0 - x2));
  2666. var nume_b = ((x1 - x0) * (y0 - y2)) - ((y1 - y0) * (x0 - x2));
  2667. var ua = nume_a / denom;
  2668. var ub = nume_b / denom;
  2669. if(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0)
  2670. {
  2671. // Get the intersection point
  2672. var x = x0 + ua * (x1 - x0);
  2673. var y = y0 + ua * (y1 - y0);
  2674. return new mxPoint(x, y);
  2675. }
  2676. // No intersection
  2677. return null;
  2678. },
  2679. /**
  2680. * Function: ptSegDistSq
  2681. *
  2682. * Returns the square distance between a segment and a point. To get the
  2683. * distance between a point and a line (with infinite length) use
  2684. * <mxUtils.ptLineDist>.
  2685. *
  2686. * Parameters:
  2687. *
  2688. * x1 - X-coordinate of the startpoint of the segment.
  2689. * y1 - Y-coordinate of the startpoint of the segment.
  2690. * x2 - X-coordinate of the endpoint of the segment.
  2691. * y2 - Y-coordinate of the endpoint of the segment.
  2692. * px - X-coordinate of the point.
  2693. * py - Y-coordinate of the point.
  2694. */
  2695. ptSegDistSq: function(x1, y1, x2, y2, px, py)
  2696. {
  2697. x2 -= x1;
  2698. y2 -= y1;
  2699. px -= x1;
  2700. py -= y1;
  2701. var dotprod = px * x2 + py * y2;
  2702. var projlenSq;
  2703. if (dotprod <= 0.0)
  2704. {
  2705. projlenSq = 0.0;
  2706. }
  2707. else
  2708. {
  2709. px = x2 - px;
  2710. py = y2 - py;
  2711. dotprod = px * x2 + py * y2;
  2712. if (dotprod <= 0.0)
  2713. {
  2714. projlenSq = 0.0;
  2715. }
  2716. else
  2717. {
  2718. projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2);
  2719. }
  2720. }
  2721. var lenSq = px * px + py * py - projlenSq;
  2722. if (lenSq < 0)
  2723. {
  2724. lenSq = 0;
  2725. }
  2726. return lenSq;
  2727. },
  2728. /**
  2729. * Function: ptLineDist
  2730. *
  2731. * Returns the distance between a line defined by two points and a point.
  2732. * To get the distance between a point and a segment (with a specific
  2733. * length) use <mxUtils.ptSeqDistSq>.
  2734. *
  2735. * Parameters:
  2736. *
  2737. * x1 - X-coordinate of point 1 of the line.
  2738. * y1 - Y-coordinate of point 1 of the line.
  2739. * x2 - X-coordinate of point 1 of the line.
  2740. * y2 - Y-coordinate of point 1 of the line.
  2741. * px - X-coordinate of the point.
  2742. * py - Y-coordinate of the point.
  2743. */
  2744. ptLineDist: function(x1, y1, x2, y2, px, py)
  2745. {
  2746. return Math.abs((y2 - y1) * px - (x2 - x1) * py + x2 * y1 - y2 * x1) /
  2747. Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
  2748. },
  2749. /**
  2750. * Function: relativeCcw
  2751. *
  2752. * Returns 1 if the given point on the right side of the segment, 0 if its
  2753. * on the segment, and -1 if the point is on the left side of the segment.
  2754. *
  2755. * Parameters:
  2756. *
  2757. * x1 - X-coordinate of the startpoint of the segment.
  2758. * y1 - Y-coordinate of the startpoint of the segment.
  2759. * x2 - X-coordinate of the endpoint of the segment.
  2760. * y2 - Y-coordinate of the endpoint of the segment.
  2761. * px - X-coordinate of the point.
  2762. * py - Y-coordinate of the point.
  2763. */
  2764. relativeCcw: function(x1, y1, x2, y2, px, py)
  2765. {
  2766. x2 -= x1;
  2767. y2 -= y1;
  2768. px -= x1;
  2769. py -= y1;
  2770. var ccw = px * y2 - py * x2;
  2771. if (ccw == 0.0)
  2772. {
  2773. ccw = px * x2 + py * y2;
  2774. if (ccw > 0.0)
  2775. {
  2776. px -= x2;
  2777. py -= y2;
  2778. ccw = px * x2 + py * y2;
  2779. if (ccw < 0.0)
  2780. {
  2781. ccw = 0.0;
  2782. }
  2783. }
  2784. }
  2785. return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
  2786. },
  2787. /**
  2788. * Function: animateChanges
  2789. *
  2790. * See <mxEffects.animateChanges>. This is for backwards compatibility and
  2791. * will be removed later.
  2792. */
  2793. animateChanges: function(graph, changes)
  2794. {
  2795. // LATER: Deprecated, remove this function
  2796. mxEffects.animateChanges.apply(this, arguments);
  2797. },
  2798. /**
  2799. * Function: cascadeOpacity
  2800. *
  2801. * See <mxEffects.cascadeOpacity>. This is for backwards compatibility and
  2802. * will be removed later.
  2803. */
  2804. cascadeOpacity: function(graph, cell, opacity)
  2805. {
  2806. mxEffects.cascadeOpacity.apply(this, arguments);
  2807. },
  2808. /**
  2809. * Function: fadeOut
  2810. *
  2811. * See <mxEffects.fadeOut>. This is for backwards compatibility and
  2812. * will be removed later.
  2813. */
  2814. fadeOut: function(node, from, remove, step, delay, isEnabled)
  2815. {
  2816. mxEffects.fadeOut.apply(this, arguments);
  2817. },
  2818. /**
  2819. * Function: setOpacity
  2820. *
  2821. * Sets the opacity of the specified DOM node to the given value in %.
  2822. *
  2823. * Parameters:
  2824. *
  2825. * node - DOM node to set the opacity for.
  2826. * value - Opacity in %. Possible values are between 0 and 100.
  2827. */
  2828. setOpacity: function(node, value)
  2829. {
  2830. if (mxUtils.isVml(node))
  2831. {
  2832. if (value >= 100)
  2833. {
  2834. node.style.filter = '';
  2835. }
  2836. else
  2837. {
  2838. // TODO: Why is the division by 5 needed in VML?
  2839. node.style.filter = 'alpha(opacity=' + (value/5) + ')';
  2840. }
  2841. }
  2842. else if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9))
  2843. {
  2844. if (value >= 100)
  2845. {
  2846. node.style.filter = '';
  2847. }
  2848. else
  2849. {
  2850. node.style.filter = 'alpha(opacity=' + value + ')';
  2851. }
  2852. }
  2853. else
  2854. {
  2855. node.style.opacity = (value / 100);
  2856. }
  2857. },
  2858. /**
  2859. * Function: createImage
  2860. *
  2861. * Creates and returns an image (IMG node) or VML image (v:image) in IE6 in
  2862. * quirks mode.
  2863. *
  2864. * Parameters:
  2865. *
  2866. * src - URL that points to the image to be displayed.
  2867. */
  2868. createImage: function(src)
  2869. {
  2870. var imageNode = null;
  2871. if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat')
  2872. {
  2873. imageNode = document.createElement(mxClient.VML_PREFIX + ':image');
  2874. imageNode.setAttribute('src', src);
  2875. imageNode.style.borderStyle = 'none';
  2876. }
  2877. else
  2878. {
  2879. imageNode = document.createElement('img');
  2880. imageNode.setAttribute('src', src);
  2881. imageNode.setAttribute('border', '0');
  2882. }
  2883. return imageNode;
  2884. },
  2885. /**
  2886. * Function: sortCells
  2887. *
  2888. * Sorts the given cells according to the order in the cell hierarchy.
  2889. * Ascending is optional and defaults to true.
  2890. */
  2891. sortCells: function(cells, ascending)
  2892. {
  2893. ascending = (ascending != null) ? ascending : true;
  2894. var lookup = new mxDictionary();
  2895. cells.sort(function(o1, o2)
  2896. {
  2897. var p1 = lookup.get(o1);
  2898. if (p1 == null)
  2899. {
  2900. p1 = mxCellPath.create(o1).split(mxCellPath.PATH_SEPARATOR);
  2901. lookup.put(o1, p1);
  2902. }
  2903. var p2 = lookup.get(o2);
  2904. if (p2 == null)
  2905. {
  2906. p2 = mxCellPath.create(o2).split(mxCellPath.PATH_SEPARATOR);
  2907. lookup.put(o2, p2);
  2908. }
  2909. var comp = mxCellPath.compare(p1, p2);
  2910. return (comp == 0) ? 0 : (((comp > 0) == ascending) ? 1 : -1);
  2911. });
  2912. return cells;
  2913. },
  2914. /**
  2915. * Function: getStylename
  2916. *
  2917. * Returns the stylename in a style of the form [(stylename|key=value);] or
  2918. * an empty string if the given style does not contain a stylename.
  2919. *
  2920. * Parameters:
  2921. *
  2922. * style - String of the form [(stylename|key=value);].
  2923. */
  2924. getStylename: function(style)
  2925. {
  2926. if (style != null)
  2927. {
  2928. var pairs = style.split(';');
  2929. var stylename = pairs[0];
  2930. if (stylename.indexOf('=') < 0)
  2931. {
  2932. return stylename;
  2933. }
  2934. }
  2935. return '';
  2936. },
  2937. /**
  2938. * Function: getStylenames
  2939. *
  2940. * Returns the stylenames in a style of the form [(stylename|key=value);]
  2941. * or an empty array if the given style does not contain any stylenames.
  2942. *
  2943. * Parameters:
  2944. *
  2945. * style - String of the form [(stylename|key=value);].
  2946. */
  2947. getStylenames: function(style)
  2948. {
  2949. var result = [];
  2950. if (style != null)
  2951. {
  2952. var pairs = style.split(';');
  2953. for (var i = 0; i < pairs.length; i++)
  2954. {
  2955. if (pairs[i].indexOf('=') < 0)
  2956. {
  2957. result.push(pairs[i]);
  2958. }
  2959. }
  2960. }
  2961. return result;
  2962. },
  2963. /**
  2964. * Function: indexOfStylename
  2965. *
  2966. * Returns the index of the given stylename in the given style. This
  2967. * returns -1 if the given stylename does not occur (as a stylename) in the
  2968. * given style, otherwise it returns the index of the first character.
  2969. */
  2970. indexOfStylename: function(style, stylename)
  2971. {
  2972. if (style != null && stylename != null)
  2973. {
  2974. var tokens = style.split(';');
  2975. var pos = 0;
  2976. for (var i = 0; i < tokens.length; i++)
  2977. {
  2978. if (tokens[i] == stylename)
  2979. {
  2980. return pos;
  2981. }
  2982. pos += tokens[i].length + 1;
  2983. }
  2984. }
  2985. return -1;
  2986. },
  2987. /**
  2988. * Function: addStylename
  2989. *
  2990. * Adds the specified stylename to the given style if it does not already
  2991. * contain the stylename.
  2992. */
  2993. addStylename: function(style, stylename)
  2994. {
  2995. if (mxUtils.indexOfStylename(style, stylename) < 0)
  2996. {
  2997. if (style == null)
  2998. {
  2999. style = '';
  3000. }
  3001. else if (style.length > 0 && style.charAt(style.length - 1) != ';')
  3002. {
  3003. style += ';';
  3004. }
  3005. style += stylename;
  3006. }
  3007. return style;
  3008. },
  3009. /**
  3010. * Function: removeStylename
  3011. *
  3012. * Removes all occurrences of the specified stylename in the given style
  3013. * and returns the updated style. Trailing semicolons are not preserved.
  3014. */
  3015. removeStylename: function(style, stylename)
  3016. {
  3017. var result = [];
  3018. if (style != null)
  3019. {
  3020. var tokens = style.split(';');
  3021. for (var i = 0; i < tokens.length; i++)
  3022. {
  3023. if (tokens[i] != stylename)
  3024. {
  3025. result.push(tokens[i]);
  3026. }
  3027. }
  3028. }
  3029. return result.join(';');
  3030. },
  3031. /**
  3032. * Function: removeAllStylenames
  3033. *
  3034. * Removes all stylenames from the given style and returns the updated
  3035. * style.
  3036. */
  3037. removeAllStylenames: function(style)
  3038. {
  3039. var result = [];
  3040. if (style != null)
  3041. {
  3042. var tokens = style.split(';');
  3043. for (var i = 0; i < tokens.length; i++)
  3044. {
  3045. // Keeps the key, value assignments
  3046. if (tokens[i].indexOf('=') >= 0)
  3047. {
  3048. result.push(tokens[i]);
  3049. }
  3050. }
  3051. }
  3052. return result.join(';');
  3053. },
  3054. /**
  3055. * Function: setCellStyles
  3056. *
  3057. * Assigns the value for the given key in the styles of the given cells, or
  3058. * removes the key from the styles if the value is null.
  3059. *
  3060. * Parameters:
  3061. *
  3062. * model - <mxGraphModel> to execute the transaction in.
  3063. * cells - Array of <mxCells> to be updated.
  3064. * key - Key of the style to be changed.
  3065. * value - New value for the given key.
  3066. */
  3067. setCellStyles: function(model, cells, key, value)
  3068. {
  3069. if (cells != null && cells.length > 0)
  3070. {
  3071. model.beginUpdate();
  3072. try
  3073. {
  3074. for (var i = 0; i < cells.length; i++)
  3075. {
  3076. if (cells[i] != null)
  3077. {
  3078. var style = mxUtils.setStyle(model.getStyle(cells[i]), key, value);
  3079. model.setStyle(cells[i], style);
  3080. }
  3081. }
  3082. }
  3083. finally
  3084. {
  3085. model.endUpdate();
  3086. }
  3087. }
  3088. },
  3089. /**
  3090. * Function: setStyle
  3091. *
  3092. * Adds or removes the given key, value pair to the style and returns the
  3093. * new style. If value is null or zero length then the key is removed from
  3094. * the style. This is for cell styles, not for CSS styles.
  3095. *
  3096. * Parameters:
  3097. *
  3098. * style - String of the form [(stylename|key=value);].
  3099. * key - Key of the style to be changed.
  3100. * value - New value for the given key.
  3101. */
  3102. setStyle: function(style, key, value)
  3103. {
  3104. var isValue = value != null && (typeof(value.length) == 'undefined' || value.length > 0);
  3105. if (style == null || style.length == 0)
  3106. {
  3107. if (isValue)
  3108. {
  3109. style = key + '=' + value + ';';
  3110. }
  3111. }
  3112. else
  3113. {
  3114. if (style.substring(0, key.length + 1) == key + '=')
  3115. {
  3116. var next = style.indexOf(';');
  3117. if (isValue)
  3118. {
  3119. style = key + '=' + value + ((next < 0) ? ';' : style.substring(next));
  3120. }
  3121. else
  3122. {
  3123. style = (next < 0 || next == style.length - 1) ? '' : style.substring(next + 1);
  3124. }
  3125. }
  3126. else
  3127. {
  3128. var index = style.indexOf(';' + key + '=');
  3129. if (index < 0)
  3130. {
  3131. if (isValue)
  3132. {
  3133. var sep = (style.charAt(style.length - 1) == ';') ? '' : ';';
  3134. style = style + sep + key + '=' + value + ';';
  3135. }
  3136. }
  3137. else
  3138. {
  3139. var next = style.indexOf(';', index + 1);
  3140. if (isValue)
  3141. {
  3142. style = style.substring(0, index + 1) + key + '=' + value + ((next < 0) ? ';' : style.substring(next));
  3143. }
  3144. else
  3145. {
  3146. style = style.substring(0, index) + ((next < 0) ? ';' : style.substring(next));
  3147. }
  3148. }
  3149. }
  3150. }
  3151. return style;
  3152. },
  3153. /**
  3154. * Function: setCellStyleFlags
  3155. *
  3156. * Sets or toggles the flag bit for the given key in the cell's styles.
  3157. * If value is null then the flag is toggled.
  3158. *
  3159. * Example:
  3160. *
  3161. * (code)
  3162. * var cells = graph.getSelectionCells();
  3163. * mxUtils.setCellStyleFlags(graph.model,
  3164. * cells,
  3165. * mxConstants.STYLE_FONTSTYLE,
  3166. * mxConstants.FONT_BOLD);
  3167. * (end)
  3168. *
  3169. * Toggles the bold font style.
  3170. *
  3171. * Parameters:
  3172. *
  3173. * model - <mxGraphModel> that contains the cells.
  3174. * cells - Array of <mxCells> to change the style for.
  3175. * key - Key of the style to be changed.
  3176. * flag - Integer for the bit to be changed.
  3177. * value - Optional boolean value for the flag.
  3178. */
  3179. setCellStyleFlags: function(model, cells, key, flag, value)
  3180. {
  3181. if (cells != null && cells.length > 0)
  3182. {
  3183. model.beginUpdate();
  3184. try
  3185. {
  3186. for (var i = 0; i < cells.length; i++)
  3187. {
  3188. if (cells[i] != null)
  3189. {
  3190. var style = mxUtils.setStyleFlag(
  3191. model.getStyle(cells[i]),
  3192. key, flag, value);
  3193. model.setStyle(cells[i], style);
  3194. }
  3195. }
  3196. }
  3197. finally
  3198. {
  3199. model.endUpdate();
  3200. }
  3201. }
  3202. },
  3203. /**
  3204. * Function: setStyleFlag
  3205. *
  3206. * Sets or removes the given key from the specified style and returns the
  3207. * new style. If value is null then the flag is toggled.
  3208. *
  3209. * Parameters:
  3210. *
  3211. * style - String of the form [(stylename|key=value);].
  3212. * key - Key of the style to be changed.
  3213. * flag - Integer for the bit to be changed.
  3214. * value - Optional boolean value for the given flag.
  3215. */
  3216. setStyleFlag: function(style, key, flag, value)
  3217. {
  3218. if (style == null || style.length == 0)
  3219. {
  3220. if (value || value == null)
  3221. {
  3222. style = key+'='+flag;
  3223. }
  3224. else
  3225. {
  3226. style = key+'=0';
  3227. }
  3228. }
  3229. else
  3230. {
  3231. var index = style.indexOf(key+'=');
  3232. if (index < 0)
  3233. {
  3234. var sep = (style.charAt(style.length-1) == ';') ? '' : ';';
  3235. if (value || value == null)
  3236. {
  3237. style = style + sep + key + '=' + flag;
  3238. }
  3239. else
  3240. {
  3241. style = style + sep + key + '=0';
  3242. }
  3243. }
  3244. else
  3245. {
  3246. var cont = style.indexOf(';', index);
  3247. var tmp = '';
  3248. if (cont < 0)
  3249. {
  3250. tmp = style.substring(index+key.length+1);
  3251. }
  3252. else
  3253. {
  3254. tmp = style.substring(index+key.length+1, cont);
  3255. }
  3256. if (value == null)
  3257. {
  3258. tmp = parseInt(tmp) ^ flag;
  3259. }
  3260. else if (value)
  3261. {
  3262. tmp = parseInt(tmp) | flag;
  3263. }
  3264. else
  3265. {
  3266. tmp = parseInt(tmp) & ~flag;
  3267. }
  3268. style = style.substring(0, index) + key + '=' + tmp +
  3269. ((cont >= 0) ? style.substring(cont) : '');
  3270. }
  3271. }
  3272. return style;
  3273. },
  3274. /**
  3275. * Function: getAlignmentAsPoint
  3276. *
  3277. * Returns an <mxPoint> that represents the horizontal and vertical alignment
  3278. * for numeric computations. X is -0.5 for center, -1 for right and 0 for
  3279. * left alignment. Y is -0.5 for middle, -1 for bottom and 0 for top
  3280. * alignment. Default values for missing arguments is top, left.
  3281. */
  3282. getAlignmentAsPoint: function(align, valign)
  3283. {
  3284. var dx = -0.5;
  3285. var dy = -0.5;
  3286. // Horizontal alignment
  3287. if (align == mxConstants.ALIGN_LEFT)
  3288. {
  3289. dx = 0;
  3290. }
  3291. else if (align == mxConstants.ALIGN_RIGHT)
  3292. {
  3293. dx = -1;
  3294. }
  3295. // Vertical alignment
  3296. if (valign == mxConstants.ALIGN_TOP)
  3297. {
  3298. dy = 0;
  3299. }
  3300. else if (valign == mxConstants.ALIGN_BOTTOM)
  3301. {
  3302. dy = -1;
  3303. }
  3304. return new mxPoint(dx, dy);
  3305. },
  3306. /**
  3307. * Function: getSizeForString
  3308. *
  3309. * Returns an <mxRectangle> with the size (width and height in pixels) of
  3310. * the given string. The string may contain HTML markup. Newlines should be
  3311. * converted to <br> before calling this method. The caller is responsible
  3312. * for sanitizing the HTML markup.
  3313. *
  3314. * Example:
  3315. *
  3316. * (code)
  3317. * var label = graph.getLabel(cell).replace(/\n/g, "<br>");
  3318. * var size = graph.getSizeForString(label);
  3319. * (end)
  3320. *
  3321. * Parameters:
  3322. *
  3323. * text - String whose size should be returned.
  3324. * fontSize - Integer that specifies the font size in pixels. Default is
  3325. * <mxConstants.DEFAULT_FONTSIZE>.
  3326. * fontFamily - String that specifies the name of the font family. Default
  3327. * is <mxConstants.DEFAULT_FONTFAMILY>.
  3328. * textWidth - Optional width for text wrapping.
  3329. * fontStyle - Optional font style.
  3330. */
  3331. getSizeForString: function(text, fontSize, fontFamily, textWidth, fontStyle)
  3332. {
  3333. fontSize = (fontSize != null) ? fontSize : mxConstants.DEFAULT_FONTSIZE;
  3334. fontFamily = (fontFamily != null) ? fontFamily : mxConstants.DEFAULT_FONTFAMILY;
  3335. var div = document.createElement('div');
  3336. // Sets the font size and family
  3337. div.style.fontFamily = fontFamily;
  3338. div.style.fontSize = Math.round(fontSize) + 'px';
  3339. div.style.lineHeight = Math.round(fontSize * mxConstants.LINE_HEIGHT) + 'px';
  3340. // Sets the font style
  3341. if (fontStyle != null)
  3342. {
  3343. if ((fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
  3344. {
  3345. div.style.fontWeight = 'bold';
  3346. }
  3347. if ((fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
  3348. {
  3349. div.style.fontStyle = 'italic';
  3350. }
  3351. var txtDecor = [];
  3352. if ((fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
  3353. {
  3354. txtDecor.push('underline');
  3355. }
  3356. if ((fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH)
  3357. {
  3358. txtDecor.push('line-through');
  3359. }
  3360. if (txtDecor.length > 0)
  3361. {
  3362. div.style.textDecoration = txtDecor.join(' ');
  3363. }
  3364. }
  3365. // Disables block layout and outside wrapping and hides the div
  3366. div.style.position = 'absolute';
  3367. div.style.visibility = 'hidden';
  3368. div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
  3369. div.style.zoom = '1';
  3370. if (textWidth != null)
  3371. {
  3372. div.style.width = textWidth + 'px';
  3373. div.style.whiteSpace = 'normal';
  3374. }
  3375. else
  3376. {
  3377. div.style.whiteSpace = 'nowrap';
  3378. }
  3379. // Adds the text and inserts into DOM for updating of size
  3380. div.innerHTML = text;
  3381. document.body.appendChild(div);
  3382. // Gets the size and removes from DOM
  3383. var size = new mxRectangle(0, 0, div.offsetWidth, div.offsetHeight);
  3384. document.body.removeChild(div);
  3385. return size;
  3386. },
  3387. /**
  3388. * Function: getViewXml
  3389. */
  3390. getViewXml: function(graph, scale, cells, x0, y0)
  3391. {
  3392. x0 = (x0 != null) ? x0 : 0;
  3393. y0 = (y0 != null) ? y0 : 0;
  3394. scale = (scale != null) ? scale : 1;
  3395. if (cells == null)
  3396. {
  3397. var model = graph.getModel();
  3398. cells = [model.getRoot()];
  3399. }
  3400. var view = graph.getView();
  3401. var result = null;
  3402. // Disables events on the view
  3403. var eventsEnabled = view.isEventsEnabled();
  3404. view.setEventsEnabled(false);
  3405. // Workaround for label bounds not taken into account for image export.
  3406. // Creates a temporary draw pane which is used for rendering the text.
  3407. // Text rendering is required for finding the bounds of the labels.
  3408. var drawPane = view.drawPane;
  3409. var overlayPane = view.overlayPane;
  3410. if (graph.dialect == mxConstants.DIALECT_SVG)
  3411. {
  3412. view.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g');
  3413. view.canvas.appendChild(view.drawPane);
  3414. // Redirects cell overlays into temporary container
  3415. view.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g');
  3416. view.canvas.appendChild(view.overlayPane);
  3417. }
  3418. else
  3419. {
  3420. view.drawPane = view.drawPane.cloneNode(false);
  3421. view.canvas.appendChild(view.drawPane);
  3422. // Redirects cell overlays into temporary container
  3423. view.overlayPane = view.overlayPane.cloneNode(false);
  3424. view.canvas.appendChild(view.overlayPane);
  3425. }
  3426. // Resets the translation
  3427. var translate = view.getTranslate();
  3428. view.translate = new mxPoint(x0, y0);
  3429. // Creates the temporary cell states in the view
  3430. var temp = new mxTemporaryCellStates(graph.getView(), scale, cells);
  3431. try
  3432. {
  3433. var enc = new mxCodec();
  3434. result = enc.encode(graph.getView());
  3435. }
  3436. finally
  3437. {
  3438. temp.destroy();
  3439. view.translate = translate;
  3440. view.canvas.removeChild(view.drawPane);
  3441. view.canvas.removeChild(view.overlayPane);
  3442. view.drawPane = drawPane;
  3443. view.overlayPane = overlayPane;
  3444. view.setEventsEnabled(eventsEnabled);
  3445. }
  3446. return result;
  3447. },
  3448. /**
  3449. * Function: getScaleForPageCount
  3450. *
  3451. * Returns the scale to be used for printing the graph with the given
  3452. * bounds across the specifies number of pages with the given format. The
  3453. * scale is always computed such that it given the given amount or fewer
  3454. * pages in the print output. See <mxPrintPreview> for an example.
  3455. *
  3456. * Parameters:
  3457. *
  3458. * pageCount - Specifies the number of pages in the print output.
  3459. * graph - <mxGraph> that should be printed.
  3460. * pageFormat - Optional <mxRectangle> that specifies the page format.
  3461. * Default is <mxConstants.PAGE_FORMAT_A4_PORTRAIT>.
  3462. * border - The border along each side of every page.
  3463. */
  3464. getScaleForPageCount: function(pageCount, graph, pageFormat, border)
  3465. {
  3466. if (pageCount < 1)
  3467. {
  3468. // We can't work with less than 1 page, return no scale
  3469. // change
  3470. return 1;
  3471. }
  3472. pageFormat = (pageFormat != null) ? pageFormat : mxConstants.PAGE_FORMAT_A4_PORTRAIT;
  3473. border = (border != null) ? border : 0;
  3474. var availablePageWidth = pageFormat.width - (border * 2);
  3475. var availablePageHeight = pageFormat.height - (border * 2);
  3476. // Work out the number of pages required if the
  3477. // graph is not scaled.
  3478. var graphBounds = graph.getGraphBounds().clone();
  3479. var sc = graph.getView().getScale();
  3480. graphBounds.width /= sc;
  3481. graphBounds.height /= sc;
  3482. var graphWidth = graphBounds.width;
  3483. var graphHeight = graphBounds.height;
  3484. var scale = 1;
  3485. // The ratio of the width/height for each printer page
  3486. var pageFormatAspectRatio = availablePageWidth / availablePageHeight;
  3487. // The ratio of the width/height for the graph to be printer
  3488. var graphAspectRatio = graphWidth / graphHeight;
  3489. // The ratio of horizontal pages / vertical pages for this
  3490. // graph to maintain its aspect ratio on this page format
  3491. var pagesAspectRatio = graphAspectRatio / pageFormatAspectRatio;
  3492. // Factor the square root of the page count up and down
  3493. // by the pages aspect ratio to obtain a horizontal and
  3494. // vertical page count that adds up to the page count
  3495. // and has the correct aspect ratio
  3496. var pageRoot = Math.sqrt(pageCount);
  3497. var pagesAspectRatioSqrt = Math.sqrt(pagesAspectRatio);
  3498. var numRowPages = pageRoot * pagesAspectRatioSqrt;
  3499. var numColumnPages = pageRoot / pagesAspectRatioSqrt;
  3500. // These value are rarely more than 2 rounding downs away from
  3501. // a total that meets the page count. In cases of one being less
  3502. // than 1 page, the other value can be too high and take more iterations
  3503. // In this case, just change that value to be the page count, since
  3504. // we know the other value is 1
  3505. if (numRowPages < 1 && numColumnPages > pageCount)
  3506. {
  3507. var scaleChange = numColumnPages / pageCount;
  3508. numColumnPages = pageCount;
  3509. numRowPages /= scaleChange;
  3510. }
  3511. if (numColumnPages < 1 && numRowPages > pageCount)
  3512. {
  3513. var scaleChange = numRowPages / pageCount;
  3514. numRowPages = pageCount;
  3515. numColumnPages /= scaleChange;
  3516. }
  3517. var currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages);
  3518. var numLoops = 0;
  3519. // Iterate through while the rounded up number of pages comes to
  3520. // a total greater than the required number
  3521. while (currentTotalPages > pageCount)
  3522. {
  3523. // Round down the page count (rows or columns) that is
  3524. // closest to its next integer down in percentage terms.
  3525. // i.e. Reduce the page total by reducing the total
  3526. // page area by the least possible amount
  3527. var roundRowDownProportion = Math.floor(numRowPages) / numRowPages;
  3528. var roundColumnDownProportion = Math.floor(numColumnPages) / numColumnPages;
  3529. // If the round down proportion is, work out the proportion to
  3530. // round down to 1 page less
  3531. if (roundRowDownProportion == 1)
  3532. {
  3533. roundRowDownProportion = Math.floor(numRowPages-1) / numRowPages;
  3534. }
  3535. if (roundColumnDownProportion == 1)
  3536. {
  3537. roundColumnDownProportion = Math.floor(numColumnPages-1) / numColumnPages;
  3538. }
  3539. // Check which rounding down is smaller, but in the case of very small roundings
  3540. // try the other dimension instead
  3541. var scaleChange = 1;
  3542. // Use the higher of the two values
  3543. if (roundRowDownProportion > roundColumnDownProportion)
  3544. {
  3545. scaleChange = roundRowDownProportion;
  3546. }
  3547. else
  3548. {
  3549. scaleChange = roundColumnDownProportion;
  3550. }
  3551. numRowPages = numRowPages * scaleChange;
  3552. numColumnPages = numColumnPages * scaleChange;
  3553. currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages);
  3554. numLoops++;
  3555. if (numLoops > 10)
  3556. {
  3557. break;
  3558. }
  3559. }
  3560. // Work out the scale from the number of row pages required
  3561. // The column pages will give the same value
  3562. var posterWidth = availablePageWidth * numRowPages;
  3563. scale = posterWidth / graphWidth;
  3564. // Allow for rounding errors
  3565. return scale * 0.99999;
  3566. },
  3567. /**
  3568. * Function: show
  3569. *
  3570. * Copies the styles and the markup from the graph's container into the
  3571. * given document and removes all cursor styles. The document is returned.
  3572. *
  3573. * This function should be called from within the document with the graph.
  3574. * If you experience problems with missing stylesheets in IE then try adding
  3575. * the domain to the trusted sites.
  3576. *
  3577. * Parameters:
  3578. *
  3579. * graph - <mxGraph> to be copied.
  3580. * doc - Document where the new graph is created.
  3581. * x0 - X-coordinate of the graph view origin. Default is 0.
  3582. * y0 - Y-coordinate of the graph view origin. Default is 0.
  3583. * w - Optional width of the graph view.
  3584. * h - Optional height of the graph view.
  3585. */
  3586. show: function(graph, doc, x0, y0, w, h)
  3587. {
  3588. x0 = (x0 != null) ? x0 : 0;
  3589. y0 = (y0 != null) ? y0 : 0;
  3590. if (doc == null)
  3591. {
  3592. var wnd = window.open();
  3593. doc = wnd.document;
  3594. }
  3595. else
  3596. {
  3597. doc.open();
  3598. }
  3599. // Workaround for missing print output in IE9 standards
  3600. if (document.documentMode == 9)
  3601. {
  3602. doc.writeln('<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]-->');
  3603. }
  3604. var bounds = graph.getGraphBounds();
  3605. var dx = Math.ceil(x0 - bounds.x);
  3606. var dy = Math.ceil(y0 - bounds.y);
  3607. if (w == null)
  3608. {
  3609. w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x);
  3610. }
  3611. if (h == null)
  3612. {
  3613. h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y);
  3614. }
  3615. // Needs a special way of creating the page so that no click is required
  3616. // to refresh the contents after the external CSS styles have been loaded.
  3617. // To avoid a click or programmatic refresh, the styleSheets[].cssText
  3618. // property is copied over from the original document.
  3619. if (mxClient.IS_IE || document.documentMode == 11)
  3620. {
  3621. var html = '<html><head>';
  3622. var base = document.getElementsByTagName('base');
  3623. for (var i = 0; i < base.length; i++)
  3624. {
  3625. html += base[i].outerHTML;
  3626. }
  3627. html += '<style>';
  3628. // Copies the stylesheets without having to load them again
  3629. for (var i = 0; i < document.styleSheets.length; i++)
  3630. {
  3631. try
  3632. {
  3633. html += document.styleSheets[i].cssText;
  3634. }
  3635. catch (e)
  3636. {
  3637. // ignore security exception
  3638. }
  3639. }
  3640. html += '</style></head><body style="margin:0px;">';
  3641. // Copies the contents of the graph container
  3642. html += '<div style="position:absolute;overflow:hidden;width:' + w + 'px;height:' + h + 'px;"><div style="position:relative;left:' + dx + 'px;top:' + dy + 'px;">';
  3643. html += graph.container.innerHTML;
  3644. html += '</div></div></body><html>';
  3645. doc.writeln(html);
  3646. doc.close();
  3647. }
  3648. else
  3649. {
  3650. doc.writeln('<html><head>');
  3651. var base = document.getElementsByTagName('base');
  3652. for (var i = 0; i < base.length; i++)
  3653. {
  3654. doc.writeln(mxUtils.getOuterHtml(base[i]));
  3655. }
  3656. var links = document.getElementsByTagName('link');
  3657. for (var i = 0; i < links.length; i++)
  3658. {
  3659. doc.writeln(mxUtils.getOuterHtml(links[i]));
  3660. }
  3661. var styles = document.getElementsByTagName('style');
  3662. for (var i = 0; i < styles.length; i++)
  3663. {
  3664. doc.writeln(mxUtils.getOuterHtml(styles[i]));
  3665. }
  3666. doc.writeln('</head><body style="margin:0px;"></body></html>');
  3667. doc.close();
  3668. var outer = doc.createElement('div');
  3669. outer.position = 'absolute';
  3670. outer.overflow = 'hidden';
  3671. outer.style.width = w + 'px';
  3672. outer.style.height = h + 'px';
  3673. // Required for HTML labels if foreignObjects are disabled
  3674. var div = doc.createElement('div');
  3675. div.style.position = 'absolute';
  3676. div.style.left = dx + 'px';
  3677. div.style.top = dy + 'px';
  3678. var node = graph.container.firstChild;
  3679. var svg = null;
  3680. while (node != null)
  3681. {
  3682. var clone = node.cloneNode(true);
  3683. if (node == graph.view.drawPane.ownerSVGElement)
  3684. {
  3685. outer.appendChild(clone);
  3686. svg = clone;
  3687. }
  3688. else
  3689. {
  3690. div.appendChild(clone);
  3691. }
  3692. node = node.nextSibling;
  3693. }
  3694. doc.body.appendChild(outer);
  3695. if (div.firstChild != null)
  3696. {
  3697. doc.body.appendChild(div);
  3698. }
  3699. if (svg != null)
  3700. {
  3701. svg.style.minWidth = '';
  3702. svg.style.minHeight = '';
  3703. svg.firstChild.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
  3704. }
  3705. }
  3706. mxUtils.removeCursors(doc.body);
  3707. return doc;
  3708. },
  3709. /**
  3710. * Function: printScreen
  3711. *
  3712. * Prints the specified graph using a new window and the built-in print
  3713. * dialog.
  3714. *
  3715. * This function should be called from within the document with the graph.
  3716. *
  3717. * Parameters:
  3718. *
  3719. * graph - <mxGraph> to be printed.
  3720. */
  3721. printScreen: function(graph)
  3722. {
  3723. var wnd = window.open();
  3724. var bounds = graph.getGraphBounds();
  3725. mxUtils.show(graph, wnd.document);
  3726. var print = function()
  3727. {
  3728. wnd.focus();
  3729. wnd.print();
  3730. wnd.close();
  3731. };
  3732. // Workaround for Google Chrome which needs a bit of a
  3733. // delay in order to render the SVG contents
  3734. if (mxClient.IS_GC)
  3735. {
  3736. wnd.setTimeout(print, 500);
  3737. }
  3738. else
  3739. {
  3740. print();
  3741. }
  3742. },
  3743. /**
  3744. * Function: popup
  3745. *
  3746. * Shows the specified text content in a new <mxWindow> or a new browser
  3747. * window if isInternalWindow is false.
  3748. *
  3749. * Parameters:
  3750. *
  3751. * content - String that specifies the text to be displayed.
  3752. * isInternalWindow - Optional boolean indicating if an mxWindow should be
  3753. * used instead of a new browser window. Default is false.
  3754. */
  3755. popup: function(content, isInternalWindow)
  3756. {
  3757. if (isInternalWindow)
  3758. {
  3759. var div = document.createElement('div');
  3760. div.style.overflow = 'scroll';
  3761. div.style.width = '636px';
  3762. div.style.height = '460px';
  3763. var pre = document.createElement('pre');
  3764. pre.innerHTML = mxUtils.htmlEntities(content, false).
  3765. replace(/\n/g,'<br>').replace(/ /g, '&nbsp;');
  3766. div.appendChild(pre);
  3767. var w = document.body.clientWidth;
  3768. var h = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight)
  3769. var wnd = new mxWindow('Popup Window', div,
  3770. w/2-320, h/2-240, 640, 480, false, true);
  3771. wnd.setClosable(true);
  3772. wnd.setVisible(true);
  3773. }
  3774. else
  3775. {
  3776. // Wraps up the XML content in a textarea
  3777. if (mxClient.IS_NS)
  3778. {
  3779. var wnd = window.open();
  3780. wnd.document.writeln('<pre>'+mxUtils.htmlEntities(content)+'</pre');
  3781. wnd.document.close();
  3782. }
  3783. else
  3784. {
  3785. var wnd = window.open();
  3786. var pre = wnd.document.createElement('pre');
  3787. pre.innerHTML = mxUtils.htmlEntities(content, false).
  3788. replace(/\n/g,'<br>').replace(/ /g, '&nbsp;');
  3789. wnd.document.body.appendChild(pre);
  3790. }
  3791. }
  3792. },
  3793. /**
  3794. * Function: alert
  3795. *
  3796. * Displayss the given alert in a new dialog. This implementation uses the
  3797. * built-in alert function. This is used to display validation errors when
  3798. * connections cannot be changed or created.
  3799. *
  3800. * Parameters:
  3801. *
  3802. * message - String specifying the message to be displayed.
  3803. */
  3804. alert: function(message)
  3805. {
  3806. alert(message);
  3807. },
  3808. /**
  3809. * Function: prompt
  3810. *
  3811. * Displays the given message in a prompt dialog. This implementation uses
  3812. * the built-in prompt function.
  3813. *
  3814. * Parameters:
  3815. *
  3816. * message - String specifying the message to be displayed.
  3817. * defaultValue - Optional string specifying the default value.
  3818. */
  3819. prompt: function(message, defaultValue)
  3820. {
  3821. return prompt(message, (defaultValue != null) ? defaultValue : '');
  3822. },
  3823. /**
  3824. * Function: confirm
  3825. *
  3826. * Displays the given message in a confirm dialog. This implementation uses
  3827. * the built-in confirm function.
  3828. *
  3829. * Parameters:
  3830. *
  3831. * message - String specifying the message to be displayed.
  3832. */
  3833. confirm: function(message)
  3834. {
  3835. return confirm(message);
  3836. },
  3837. /**
  3838. * Function: error
  3839. *
  3840. * Displays the given error message in a new <mxWindow> of the given width.
  3841. * If close is true then an additional close button is added to the window.
  3842. * The optional icon specifies the icon to be used for the window. Default
  3843. * is <mxUtils.errorImage>.
  3844. *
  3845. * Parameters:
  3846. *
  3847. * message - String specifying the message to be displayed.
  3848. * width - Integer specifying the width of the window.
  3849. * close - Optional boolean indicating whether to add a close button.
  3850. * icon - Optional icon for the window decoration.
  3851. */
  3852. error: function(message, width, close, icon)
  3853. {
  3854. var div = document.createElement('div');
  3855. div.style.padding = '20px';
  3856. var img = document.createElement('img');
  3857. img.setAttribute('src', icon || mxUtils.errorImage);
  3858. img.setAttribute('valign', 'bottom');
  3859. img.style.verticalAlign = 'middle';
  3860. div.appendChild(img);
  3861. div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
  3862. div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
  3863. div.appendChild(document.createTextNode('\u00a0')); // &nbsp;
  3864. mxUtils.write(div, message);
  3865. var w = document.body.clientWidth;
  3866. var h = (document.body.clientHeight || document.documentElement.clientHeight);
  3867. var warn = new mxWindow(mxResources.get(mxUtils.errorResource) ||
  3868. mxUtils.errorResource, div, (w-width)/2, h/4, width, null,
  3869. false, true);
  3870. if (close)
  3871. {
  3872. mxUtils.br(div);
  3873. var tmp = document.createElement('p');
  3874. var button = document.createElement('button');
  3875. if (mxClient.IS_IE)
  3876. {
  3877. button.style.cssText = 'float:right';
  3878. }
  3879. else
  3880. {
  3881. button.setAttribute('style', 'float:right');
  3882. }
  3883. mxEvent.addListener(button, 'click', function(evt)
  3884. {
  3885. warn.destroy();
  3886. });
  3887. mxUtils.write(button, mxResources.get(mxUtils.closeResource) ||
  3888. mxUtils.closeResource);
  3889. tmp.appendChild(button);
  3890. div.appendChild(tmp);
  3891. mxUtils.br(div);
  3892. warn.setClosable(true);
  3893. }
  3894. warn.setVisible(true);
  3895. return warn;
  3896. },
  3897. /**
  3898. * Function: makeDraggable
  3899. *
  3900. * Configures the given DOM element to act as a drag source for the
  3901. * specified graph. Returns a a new <mxDragSource>. If
  3902. * <mxDragSource.guideEnabled> is enabled then the x and y arguments must
  3903. * be used in funct to match the preview location.
  3904. *
  3905. * Example:
  3906. *
  3907. * (code)
  3908. * var funct = function(graph, evt, cell, x, y)
  3909. * {
  3910. * if (graph.canImportCell(cell))
  3911. * {
  3912. * var parent = graph.getDefaultParent();
  3913. * var vertex = null;
  3914. *
  3915. * graph.getModel().beginUpdate();
  3916. * try
  3917. * {
  3918. * vertex = graph.insertVertex(parent, null, 'Hello', x, y, 80, 30);
  3919. * }
  3920. * finally
  3921. * {
  3922. * graph.getModel().endUpdate();
  3923. * }
  3924. *
  3925. * graph.setSelectionCell(vertex);
  3926. * }
  3927. * }
  3928. *
  3929. * var img = document.createElement('img');
  3930. * img.setAttribute('src', 'editors/images/rectangle.gif');
  3931. * img.style.position = 'absolute';
  3932. * img.style.left = '0px';
  3933. * img.style.top = '0px';
  3934. * img.style.width = '16px';
  3935. * img.style.height = '16px';
  3936. *
  3937. * var dragImage = img.cloneNode(true);
  3938. * dragImage.style.width = '32px';
  3939. * dragImage.style.height = '32px';
  3940. * mxUtils.makeDraggable(img, graph, funct, dragImage);
  3941. * document.body.appendChild(img);
  3942. * (end)
  3943. *
  3944. * Parameters:
  3945. *
  3946. * element - DOM element to make draggable.
  3947. * graphF - <mxGraph> that acts as the drop target or a function that takes a
  3948. * mouse event and returns the current <mxGraph>.
  3949. * funct - Function to execute on a successful drop.
  3950. * dragElement - Optional DOM node to be used for the drag preview.
  3951. * dx - Optional horizontal offset between the cursor and the drag
  3952. * preview.
  3953. * dy - Optional vertical offset between the cursor and the drag
  3954. * preview.
  3955. * autoscroll - Optional boolean that specifies if autoscroll should be
  3956. * used. Default is mxGraph.autoscroll.
  3957. * scalePreview - Optional boolean that specifies if the preview element
  3958. * should be scaled according to the graph scale. If this is true, then
  3959. * the offsets will also be scaled. Default is false.
  3960. * highlightDropTargets - Optional boolean that specifies if dropTargets
  3961. * should be highlighted. Default is true.
  3962. * getDropTarget - Optional function to return the drop target for a given
  3963. * location (x, y). Default is mxGraph.getCellAt.
  3964. */
  3965. makeDraggable: function(element, graphF, funct, dragElement, dx, dy, autoscroll,
  3966. scalePreview, highlightDropTargets, getDropTarget)
  3967. {
  3968. var dragSource = new mxDragSource(element, funct);
  3969. dragSource.dragOffset = new mxPoint((dx != null) ? dx : 0,
  3970. (dy != null) ? dy : mxConstants.TOOLTIP_VERTICAL_OFFSET);
  3971. dragSource.autoscroll = autoscroll;
  3972. // Cannot enable this by default. This needs to be enabled in the caller
  3973. // if the funct argument uses the new x- and y-arguments.
  3974. dragSource.setGuidesEnabled(false);
  3975. if (highlightDropTargets != null)
  3976. {
  3977. dragSource.highlightDropTargets = highlightDropTargets;
  3978. }
  3979. // Overrides function to find drop target cell
  3980. if (getDropTarget != null)
  3981. {
  3982. dragSource.getDropTarget = getDropTarget;
  3983. }
  3984. // Overrides function to get current graph
  3985. dragSource.getGraphForEvent = function(evt)
  3986. {
  3987. return (typeof(graphF) == 'function') ? graphF(evt) : graphF;
  3988. };
  3989. // Translates switches into dragSource customizations
  3990. if (dragElement != null)
  3991. {
  3992. dragSource.createDragElement = function()
  3993. {
  3994. return dragElement.cloneNode(true);
  3995. };
  3996. if (scalePreview)
  3997. {
  3998. dragSource.createPreviewElement = function(graph)
  3999. {
  4000. var elt = dragElement.cloneNode(true);
  4001. var w = parseInt(elt.style.width);
  4002. var h = parseInt(elt.style.height);
  4003. elt.style.width = Math.round(w * graph.view.scale) + 'px';
  4004. elt.style.height = Math.round(h * graph.view.scale) + 'px';
  4005. return elt;
  4006. };
  4007. }
  4008. }
  4009. return dragSource;
  4010. }
  4011. };