client.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /*******************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
  4. Modified by Conner Hansen (chansen@crimson.ua.edu)
  5. This file is part of AToMPM.
  6. AToMPM is free software: you can redistribute it and/or modify it under the
  7. terms of the GNU Lesser General Public License as published by the Free Software
  8. Foundation, either version 3 of the License, or (at your option) any later
  9. version.
  10. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public License along
  14. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  15. *******************************************************************************/
  16. /*TODO: look into reworking naming convention to be more intuitive
  17. *ie:
  18. * button: btn_someFunc
  19. * window: wnd_someFunc
  20. * private: _someFunc
  21. * etc
  22. */
  23. /* NOTES:
  24. NAMING CONVENTION... functions and variables starting with '_' are meant to
  25. be used from within Button code (most of them are directly associated to a
  26. native back-end operation)... functions starting with '__' are meant to be
  27. private and should only be used from within application code
  28. WINDOW TITLE... window titles are set to pretty-print '__saveas'... if any
  29. changelogs have been received since we loaded/saved the model, the title is
  30. changed to explicit this
  31. the BACKDOOR API section contains methods that are accessible from the
  32. backend (or from anyone who has access to the backend, e.g., a synthesized
  33. application)... they are all void (i.e., they don't return anything or any
  34. feedback to the 'caller') and should only be accessed remotely, via
  35. 'PUT /GET/console {CLIENT_BDAPI:...}' to the backend
  36. TBI:: add caching mechanism to avoid recompiling the same icon models into
  37. the same SVG over and over
  38. TBI:: add helper functions for parsing and manipulating path strings (see
  39. Raphael.parsePathString()... not only would this elevate abstraction,
  40. but a lot of our connection-related operations could be optimized
  41. (including less string matching and splitting) if segments were arrays
  42. of points rather than strings
  43. TBI:: when SVG 1.2 gets Chrome support, the __SVG_TEXT_EDITOR dialog could be
  44. removed in favor of native SVG text editing facilities */
  45. /******************************** GLOBAL VARS *********************************/
  46. var __user = undefined,
  47. __wid,
  48. __aswid,
  49. __prefs,
  50. __typeToCreate,
  51. __loadedToolbars = {},
  52. __icons = {},
  53. __edges = {},
  54. __canvas,
  55. __saveas;
  56. /******************************** GLOBAL VARS *********************************/
  57. AtomPMClient = function(){
  58. /**
  59. * Log deprecated function calls
  60. */
  61. this.alertDeprecatedFunctionCall = function( funcName ){
  62. console.warn("Deprecated function call: " + funcName);
  63. };
  64. return this;
  65. }();
  66. /**
  67. * Automatically saves the current model.
  68. *
  69. * If mode == backup, save the current model into a backup file
  70. * If mode == overwrite, overwrite the current file model
  71. */
  72. function __autosave(mode)
  73. {
  74. if( mode == 'backup' )
  75. {
  76. if( __saveas )
  77. {
  78. var matches = __saveas.match(/(.*\/)(.*)/);
  79. _saveModel(matches[1]+'.autosave.'+matches[2],true,true);
  80. }
  81. else
  82. _saveModel(undefined,true,true);
  83. }
  84. else if( mode == 'overwrite' )
  85. _saveModel(undefined,false,true);
  86. }
  87. /**
  88. * Launches the autosave daemon, which automatically tries to
  89. * save the model based on the interval stored in the preferences
  90. * array
  91. */
  92. function __launchAutosave()
  93. {
  94. if( __prefs['autosave-delay']['value'] > 0 )
  95. window.setInterval(
  96. __autosave,
  97. __prefs['autosave-delay']['value'] * 1000,
  98. __prefs['autosave-mode']['value']);
  99. }
  100. /********************************* 'USER' API *********************************/
  101. /**
  102. * Creates an SVG blob out of the current SVG elements on the canvas
  103. *
  104. * Note 1: an alternative to the current implementation (though less
  105. * efficient) would be to POST the data to the server and then have
  106. * the returned file be downloadable
  107. *
  108. * Note 2: a click is simulated instead of using
  109. * window.location.assign() so that the target filename can be
  110. * chosen
  111. *
  112. * Note 3: the output width of the image is altered in order to fit
  113. * the content. This would be fixed if we utilized a dynamically
  114. * expanding canvas
  115. *
  116. * Note 4: the 'href' tags are changed to 'xlink:href' tags in the
  117. * generated SVG file. All images are embedded as data uris in the
  118. * output in order to increase robustness.
  119. */
  120. function _exportSVG(fname)
  121. {
  122. var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder,
  123. URL = window.URL || window.webkitURL,
  124. a = $('<a>'),
  125. bb = undefined,
  126. iconsbbox = __getGlobalBBox(
  127. utils.keys(__icons).concat(utils.keys(__edges)) ),
  128. svg = $('#div_canvas').html().
  129. replace(/^(<svg.*? width=")(.*?)(".*?>)/,
  130. '$1'+(2*iconsbbox.x+iconsbbox.width)+'$3').
  131. replace(/^(<svg.*? height=")(.*?)(".*?>)/,
  132. '$1'+(2*iconsbbox.y+iconsbbox.height)+'$3'),
  133. exportSVG =
  134. function()
  135. {
  136. bb = new Blob([svg], {"type": "text\/xml"});
  137. a.attr("href", URL.createObjectURL(bb));
  138. a.attr("download", fname || "model.svg");
  139. a.get(0).click();
  140. URL.revokeObjectURL(a.href);
  141. };
  142. if( (images = svg.match(/<image.*? href=".*?".*?>/g)) )
  143. {
  144. var datauris = [];
  145. images.forEach(
  146. function(image,i)
  147. {
  148. HttpUtils.imageToDataURI(
  149. image.match(/^<image.*? href="(.*?)".*?>$/)[1],
  150. function(datauri)
  151. {
  152. datauris[i] = datauri;
  153. if( datauris.length == images.length &&
  154. ! utils.contains(datauris,undefined) )
  155. {
  156. svg = svg.replace(
  157. /(<image.*? )href=".*?"(.*?>)/g,
  158. function(p0,p1,p2)
  159. {
  160. return p1+' xlink:href="'+datauris.shift()+'"'+p2;
  161. });
  162. exportSVG();
  163. }
  164. });
  165. });
  166. }
  167. else
  168. exportSVG();
  169. }
  170. /**
  171. * Retrieves the value that matches the subset, and then passes
  172. * it back into the callback function
  173. *
  174. * @param callback the function that the value is passed to
  175. * @param subset the matching preference entry
  176. */
  177. function _getUserPreferences(callback,subset)
  178. {
  179. console.debug("Get User Preferences");
  180. console.debug(subset);
  181. HttpUtils.httpReq(
  182. 'GET',
  183. HttpUtils.url('/prefs',__NO_WID),
  184. (subset == undefined ?
  185. undefined :
  186. '?subset='+encodeURIComponent(utils.jsons(subset))),
  187. function(statusCode,resp)
  188. {
  189. console.debug("Callback Get User Preferences");
  190. console.debug(statusCode);
  191. console.debug(resp);
  192. if( ! utils.isHttpSuccessCode(statusCode) )
  193. UserManagement.logout();
  194. else
  195. callback(utils.jsonp(resp));
  196. });
  197. }
  198. /**
  199. * Generates an HTTP request
  200. *
  201. * @param method GET/DELETE/POST/PUT
  202. * @param url the URL to hit
  203. * @param params the parameters to pass in
  204. * @param onresponse the callback function to perform on response
  205. * @param sync whether or not this request is synchronous
  206. */
  207. function _httpReq(method,url,params,onresponse,sync)
  208. {
  209. if( method != 'GET' )
  210. BehaviorManager.handleUserEvent(__EVENT_CODED_CANVAS_EDIT);
  211. HttpUtils.httpReq(method,url,params,onresponse,sync);
  212. }
  213. /**
  214. * Inserts another model into the current canvas
  215. */
  216. function _insertModel(fname)
  217. {
  218. if( ! __isModel(fname) )
  219. WindowManagement.openDialog(
  220. _ERROR,
  221. 'invalid extension... loadable models are "*.model" files');
  222. else
  223. DataUtils.loadm(fname,true);
  224. }
  225. /**
  226. * Loads a model from the selected file name. This automatically
  227. * strips out the .autosave portion of the filename, if it is
  228. * present
  229. *
  230. * @param fname the name of the file to load
  231. */
  232. function _loadModel(fname)
  233. {
  234. if( ! __isModel(fname) )
  235. WindowManagement.openDialog(
  236. _ERROR,
  237. 'invalid extension... loadable models are "*.model" files');
  238. else
  239. {
  240. if( (matches = fname.match(/(.*)\.autosave\.(.+\.model)/)) )
  241. __saveas = matches[1]+matches[2];
  242. else
  243. __saveas = fname;
  244. DataUtils.loadm(fname);
  245. }
  246. }
  247. /**
  248. * Loads a new toolbar onto the current canvas
  249. * @param fname the name of the file to load
  250. */
  251. function _loadToolbar(fname)
  252. {
  253. if( __isButtonModel(fname) )
  254. DataUtils.loadbm(fname);
  255. else if( __isIconMetamodel(fname) )
  256. DataUtils.loadmm(fname);
  257. }
  258. /* save model
  259. 1. if no filename is specified,
  260. a) if autosave is specified,
  261. - if __saveas is specified, use it
  262. - otherwise, use __DEFAULT_SAVEAS
  263. b) else, ask the user to choose a file to save to (first time user saves a model)
  264. 2. otherwise, if an incorrect filename is specified, show error and return
  265. 3. save model
  266. 4. if this isn't an automated backup (i.e., the backup flag is unset),
  267. remember filename in __saveas and adjust window title to reflect fact that
  268. all changes are saved */
  269. function _saveModel(fname,backup,autosave)
  270. {
  271. if( fname == undefined ) {
  272. if (!autosave && (__saveas == undefined || __saveas == null)) {
  273. var options = {'extensions':['\\.model'],
  274. 'multipleChoice':false,
  275. 'manualInput':true,
  276. 'title':'specify target model',
  277. 'startDir':'model'},
  278. callback =
  279. function(fnames)
  280. {
  281. _saveModel(fnames[0]);
  282. };
  283. WindowManagement.openDialog(_FILE_BROWSER,options,callback);
  284. return;
  285. } else {
  286. fname = (__saveas || __DEFAULT_SAVEAS);
  287. }
  288. } else if( ! __isModel(fname) ) {
  289. WindowManagement.openDialog(
  290. _ERROR,
  291. 'invalid extension... models must be saved as "*.model" files');
  292. return;
  293. }
  294. DataUtils.savem(fname);
  295. if( ! backup )
  296. {
  297. __saveas = fname;
  298. WindowManagement.setWindowTitle();
  299. }
  300. }
  301. /* TBI::
  302. . unselect invisible items
  303. . remember visibility settings s.t. newly created items (e.g., by
  304. collaborator) inherit them */
  305. function _setInvisibleMetamodels(mms)
  306. {
  307. mms = mms.map( function(mm) {return mm.match(/(.*)\.metamodel/)[1]} );
  308. function hideOrShow(uri,icon)
  309. {
  310. if( ! mms.some(
  311. function(mm)
  312. {
  313. if( uri.match(mm+'/') )
  314. {
  315. icon.hide();
  316. return true;
  317. }
  318. }) )
  319. icon.show();
  320. }
  321. for( var uri in __icons )
  322. hideOrShow(uri,__icons[uri]['icon']);
  323. for( var uri in __edges )
  324. hideOrShow(uri,__edges[uri]['icon']);
  325. }
  326. /**
  327. * Updates the current user preferences and then
  328. * executes the passed in callback function
  329. *
  330. * @param prefs the new user preferences
  331. * @param callback the function to be executed
  332. */
  333. function _setUserPreferences(prefs,callback)
  334. {
  335. HttpUtils.httpReq(
  336. 'PUT',
  337. HttpUtils.url('/prefs',__NO_WID),
  338. prefs,
  339. function(statusCode,resp)
  340. {
  341. if( ! utils.isHttpSuccessCode(statusCode) )
  342. WindowManagement.openDialog(_ERROR, 'failed to update user preferences :: '+resp);
  343. else if( callback )
  344. callback();
  345. });
  346. }
  347. /**
  348. * Creates a new formalism under /Formalisms/ with the specified name.
  349. *
  350. * @param formalism_name the name of the new formalism
  351. */
  352. function _newFormalism(formalism_name) {
  353. HttpUtils.httpReq(
  354. 'POST',
  355. window.localStorage.getItem('user') + "/" + formalism_name + '.formalism',
  356. undefined,
  357. function(statusCode,resp)
  358. {
  359. if( ! utils.isHttpSuccessCode(statusCode) ) {
  360. WindowManagement.openDialog(_ERROR, 'failed to create new formalism :: '+resp);
  361. } else {
  362. WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".model")
  363. WindowManagement.spawnClient("/Formalisms/" + formalism_name + "/" + formalism_name + ".defaultIcons.model")
  364. }
  365. });
  366. }
  367. /**
  368. * Creates a new transformation on the specified location.
  369. *
  370. * @param transformation_loc the location of the new transformation
  371. */
  372. function _newTransformation(transformation_loc) {
  373. if (transformation_loc.match(/.*\/T_.*\.model$/)) {
  374. HttpUtils.httpReq(
  375. 'POST',
  376. window.localStorage.getItem('user') + transformation_loc + '.transformation',
  377. undefined,
  378. function(statusCode,resp)
  379. {
  380. if( ! utils.isHttpSuccessCode(statusCode) ) {
  381. WindowManagement.openDialog(_ERROR, 'failed to create new transformation :: '+resp);
  382. } else {
  383. WindowManagement.spawnClient(transformation_loc)
  384. }
  385. });
  386. } else {
  387. WindowManagement.openDialog(_ERROR, 'failed to create new transformation :: '+transformation_loc+" is not a valid transformation name");
  388. }
  389. }
  390. /**
  391. * Creates a new rule on the specified location.
  392. *
  393. * @param rule_loc the location of the new rule
  394. */
  395. function _newRule(rule_loc) {
  396. if (rule_loc.match(/.*\/R_.*\.model$/)) {
  397. HttpUtils.httpReq(
  398. 'POST',
  399. window.localStorage.getItem('user') + rule_loc + '.rule',
  400. undefined,
  401. function(statusCode,resp)
  402. {
  403. if( ! utils.isHttpSuccessCode(statusCode) ) {
  404. WindowManagement.openDialog(_ERROR, 'failed to create new rule :: '+resp);
  405. } else {
  406. WindowManagement.spawnClient(rule_loc)
  407. }
  408. });
  409. } else {
  410. WindowManagement.openDialog(_ERROR, 'failed to create new rule :: '+rule_loc+" is not a valid rule name");
  411. }
  412. }
  413. /**
  414. * Sets the current type of entity to be created
  415. * @param fulltype the type to be created
  416. */
  417. function _setTypeToCreate(fulltype)
  418. {
  419. __typeToCreate = fulltype;
  420. }
  421. /**
  422. * Unloads the selected toolbar from the current canvas
  423. * @param tb the toolbar to be unloaded
  424. */
  425. function _unloadToolbar(tb)
  426. {
  427. if( __isButtonModel(tb) )
  428. DataUtils.unloadbm(tb);
  429. else if( __isIconMetamodel(tb) )
  430. DataUtils.unloadmm(tb);
  431. }
  432. /**
  433. * Validates the current model
  434. */
  435. function _validate()
  436. {
  437. HttpUtils.httpReq(
  438. 'GET',
  439. HttpUtils.url('/validatem',__NO_USERNAME));
  440. }
  441. /******************************* 'BACKDOOR' API *******************************/
  442. /* highlight the specified node or unhighlight any highlighted nodes... the
  443. 'followCrossFormalismLinks' parameter indicates whether or not (and which)
  444. neighbors along cross-formalism links should also be highlighted... the
  445. 'timeout' parameter, if specified, indicates the duration of the highlight */
  446. function _highlight(args/*asid[,followCrossFormalismLinks,timeout]*/)
  447. {
  448. if( args == undefined )
  449. __unhighlight();
  450. else
  451. {
  452. var uri = __asid2csuri(args['asid']),
  453. fcfl = args['followCrossFormalismLinks'],
  454. timeout = args['timeout'];
  455. __highlight(uri,fcfl,timeout);
  456. }
  457. }
  458. function _highlightState(args/*asid[,followCrossFormalismLinks,timeout]*/)
  459. {
  460. var uri = __asid2csuri(args['asid']),
  461. fcfl = args['followCrossFormalismLinks'],
  462. timeout = args['timeout'];
  463. __highlight(uri,fcfl,timeout);
  464. }
  465. /* unhighlight any highlighted nodes - sadaf */
  466. function _unhighlight()
  467. {
  468. __unhighlight();
  469. }
  470. /* unhighlight any highlighted nodes - sadaf */
  471. function _unhighlightState(args/*asid*/)
  472. {
  473. //var uri = __asid2csuri(args['asid']);
  474. //__icons[uri]['icon'].unhighlight();
  475. __unhighlight(__asid2csuri(args['asid']));
  476. }
  477. /* interface to WindowManagement.spawnClient() 'USER' API function */
  478. function _loadModelInNewWindow(args/*fname[,callback-url]*/)
  479. {
  480. WindowManagement.spawnClient(args['fname'],args['callback-url']);
  481. }
  482. /* tag the specified node with some text, possibly appending it to an existing
  483. tag... the 'timeout' parameter, if specified, indicates how long the tag
  484. should be displayed */
  485. function _tag(args/*asid,text[,style,append,timeout]*/)
  486. {
  487. var uri = __asid2csuri(args['asid']),
  488. text = args['text'],
  489. style = utils.mergeDicts(
  490. [{'font-size':'16px', 'font-style':'italic', 'fill':'#ffffff'},
  491. args['style']]),
  492. append = args['append'],
  493. timeout = args['timeout'];
  494. __tag(uri,text,style,append,timeout);
  495. }
  496. /* update an attribute of the specified node, possibly highlighting the node to
  497. indicate the change (note that this doesn't unhighlight any currently
  498. highlighted nodes) */
  499. function _updateAttr(args/*asid,attr,val[,highlight]*/)
  500. {
  501. var uri = __asid2csuri(args['asid']),
  502. changes = {};
  503. changes[args['attr']] = args['val'];
  504. DataUtils.update(uri,changes);
  505. if( args['highlight'] )
  506. __flash(uri);
  507. }
  508. /******************************** CRUD QUERIES ********************************/
  509. /*************************** HANDLE QUERY RESPONSE ****************************/
  510. /***************************** EDIT CONFLICTS... ******************************/
  511. var __watchList = {};
  512. function __changed(uri,set)
  513. {
  514. if( set )
  515. __watchList[uri] = __EDIT_CONFLICT;
  516. else
  517. return __watchList[uri] == __EDIT_CONFLICT;
  518. }
  519. //TBC place calls to this appropriately (with CS/AS uris...)
  520. function __unwatch(uri)
  521. {
  522. delete __watchList[uri];
  523. }
  524. //TBC place calls to this appropriately (with CS/AS uris...)
  525. function __watch(uri)
  526. {
  527. __watchList[uri] = __NO_CONFLICT;
  528. }
  529. function __watching(uri)
  530. {
  531. return uri in __watchList;
  532. }
  533. /***************************** MMM-RELATED LOGIC ******************************/
  534. /**************************** CANVAS BEHAVIOUR... *****************************/
  535. /*------------------------ BEHAVIOUR-RELATED UTILITIES -----------------------*/
  536. /*------------------------- SELECTING ICONS/EDGES... -------------------------*/
  537. /*--------------------------- DRAWING CONNECTIONS ----------------------------*/
  538. /*------------------------------ HIGHLIGHTING --------------------------------*/
  539. /*--------------------------------- TAGGING ----------------------------------*/
  540. /* tag the specified node and setup delayed tag removal, when applicable */
  541. function __tag(uri,text,style,append,timeout)
  542. {
  543. __icons[uri]['icon'].tag(text,style,append);
  544. if( timeout != undefined )
  545. window.setTimeout(__icons[uri]['icon'].tag,timeout,'');
  546. }
  547. /*------------------------------- LAYERING... --------------------------------*/
  548. function __iconToBack(tgt)
  549. {
  550. __icons[__vobj2uri(tgt)]['icon'].toBack();
  551. }
  552. function __iconToFront(tgt)
  553. {
  554. __icons[__vobj2uri(tgt)]['icon'].toFront();
  555. }
  556. /*---------------------------- SELECTION OVERLAY -----------------------------*/
  557. /*---------------- GEOMETRY CONTROLS AND TRANSFORMATIONS... ------------------*/
  558. /*-------------------------- CONNECTION EDITING... ---------------------------*/
  559. /************************* GRAPH TRAVERSAL UTILITIES **************************/
  560. /* return the ids of edges connected to the specified node */
  561. function __getConnectedEdges(uri)
  562. {
  563. var edgeIds = [];
  564. for( var edgeId in __edges )
  565. if( __edges[edgeId]['start'] == uri ||
  566. __edges[edgeId]['end'] == uri )
  567. edgeIds.push(edgeId);
  568. return edgeIds;
  569. }
  570. /* given an edge, returns an array containing that edge's start and/or end
  571. linktype(s), and its(their) connected edges (which include the given edge) */
  572. function __getConnectionParticipants(edgeId)
  573. {
  574. var start = __edges[edgeId]['start'],
  575. end = __edges[edgeId]['end'],
  576. cm = [];
  577. if( __isConnectionType(start) )
  578. cm = cm.concat(start, __getConnectedEdges(start));
  579. if( __isConnectionType(end) )
  580. cm = cm.concat(end, __getConnectedEdges(end));
  581. return cm;
  582. }
  583. /* return all of the edges and nodes directly or indirectly connected to 'uri'
  584. via cross-formalism links in direction 'dir'... the meaning of 'dir' follows
  585. from the convention that cross-formalism link go from higher-level constructs
  586. to lower-level ones
  587. 1. filter __edges keeping only cross-formalism ones
  588. 2. if dir is '*' or 'DOWN', recursively navigate the edges from step 1.
  589. out of 'uri' marking appropriate nodes and edges
  590. 3. if dir is '*' or 'UP', recursively navigate the edges from step 1.
  591. into 'uri' marking appropriate nodes and edges */
  592. function __getCrossFormalismNeighbors(uri,dir)
  593. {
  594. var crossFormalismEdges = [];
  595. for( var edgeId in __edges )
  596. if( __getMetamodel(__edges[edgeId]['start']) !=
  597. __getMetamodel(__edges[edgeId]['end']) )
  598. crossFormalismEdges.push(edgeId);
  599. function _(neighbors,lookfor,append)
  600. {
  601. var tovisit = [uri];
  602. while( tovisit.length > 0 )
  603. {
  604. var curr = tovisit.shift();
  605. neighbors.nodes.push(curr);
  606. crossFormalismEdges.forEach(
  607. function(edgeId)
  608. {
  609. if( __edges[edgeId][lookfor] == curr )
  610. {
  611. var ext = __edges[edgeId][append];
  612. if( ! utils.contains(neighbors.nodes,ext) )
  613. tovisit.push(ext);
  614. if( ! utils.contains(neighbors.edges,edgeId) )
  615. neighbors.edges.push(edgeId);
  616. }
  617. });
  618. }
  619. return neighbors;
  620. }
  621. var dn = {'nodes':[],'edges':[]}, un = {'nodes':[],'edges':[]};
  622. if( dir == '*' || dir == 'DOWN' )
  623. _(dn,'start','end');
  624. if( dir == '*' || dir == 'UP' )
  625. _(un,'end','start');
  626. return {'nodes':utils.toSet(dn.nodes.concat(un.nodes)),
  627. 'edges':utils.toSet(dn.nodes.concat(un.nodes))};
  628. }
  629. /***************************** HTML-GUI UTILITIES *****************************/
  630. /****************************** SVG... UTILITIES ******************************/
  631. /******************************* MISC UTILITIES *******************************/
  632. /* returns the csuri associated to the given asid */
  633. function __asid2csuri(asid)
  634. {
  635. for( var uri in __icons )
  636. if( __icons[uri]['icon'].getAttr('__asuri').
  637. match(/.*\/(.*)\.instance/)[1] == asid )
  638. return uri;
  639. }
  640. /* returns the edgeId associated to the given edge DOM element */
  641. function __edge2edgeId(edge)
  642. {
  643. return edge.parentNode.getAttribute('__edgeId');
  644. }
  645. /* returns both ends contained in the given edgeId */
  646. function __edgeId2ends(edgeId)
  647. {
  648. if( edgeId in __edges )
  649. return [__edges[edgeId]['start'],__edges[edgeId]['end']];
  650. else
  651. return edgeId.match(/^(.*\.instance)--(.*\.instance)$/).slice(1);
  652. }
  653. /* returns the linkuri associated to the given edgeId */
  654. function __edgeId2linkuri(edgeId)
  655. {
  656. return __edges[edgeId]['icon'].getAttr('__linkuri');
  657. }
  658. /* filter a list of filenames given the specified extensions */
  659. function __filterFilenamesByExtension(fnames,extensions)
  660. {
  661. var ffnames = fnames.filter(
  662. function(fname)
  663. {
  664. return extensions.some(
  665. function(ext)
  666. {
  667. return fname.match(ext+'$');
  668. });
  669. });
  670. return ffnames;
  671. }
  672. /* return the icon associated to the given csuri or edgeid */
  673. function __getIcon(_)
  674. {
  675. return (_ in __icons ?
  676. __icons[_]['icon'] :
  677. (_ in __edges ?
  678. __edges[_]['icon'] :
  679. undefined));
  680. }
  681. /* return true if the current model contains no unsaved changes */
  682. function __isSaved()
  683. {
  684. return ! document.title.match(__TITLE+' - \\+');
  685. }
  686. /* return true if the given element is a toolbar or inside a toolbar */
  687. function __isAToolbar(el)
  688. {
  689. return Boolean(
  690. (el.id && el.id.match(/^div_toolbar_/)) ||
  691. (el.parentNode && __isAToolbar(el.parentNode)) );
  692. }
  693. /* return true if the given element is the canvas or something drawn on it */
  694. function __isCanvasElement(el)
  695. {
  696. return el.attr("id") == 'div_canvas' ||
  697. ( (el.parent().length > 0) && __isCanvasElement(el.parent()));
  698. }
  699. /* returns the $segments hash associated to the given edgeId */
  700. function __linkuri2segments(linkuri)
  701. {
  702. return utils.jsonp(__icons[linkuri]['icon'].getAttr('__segments'));
  703. }
  704. /* truncate './users/<username>' from a list of filenames */
  705. function __localizeFilenames(fnames)
  706. {
  707. return fnames.map(
  708. function(n)
  709. {
  710. return n.match(/^\.\/users\/.*?(\/.*)/)[1];
  711. });
  712. }
  713. /* modify a URL as needed to ensure GETting it will produce desired result:
  714. . prepend username to user files
  715. . do nothing for WWW files */
  716. function __relativizeURL(url)
  717. {
  718. if( url.charAt(0) == '.' || url.charAt(0) == '/' )
  719. return HttpUtils.url(url,__NO_WID);
  720. return url;
  721. }
  722. /* returns the csuri of the icon that contains the specified VisualObject */
  723. function __vobj2uri(vobj)
  724. {
  725. if( vobj != document.body )
  726. return vobj.parentNode.getAttribute('__csuri') ||
  727. __vobj2uri(vobj.parentNode);
  728. }
  729. function __getRecentDir(name) {
  730. return utils.readCookie('recentDir'+name);
  731. }
  732. function __setRecentDir(name,value) {
  733. utils.createCookie('recentDir'+name,value);
  734. }