edit_utils.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /* This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
  2. * Copyright 2011 by the AToMPM team and licensed under the LGPL
  3. * See COPYING.lesser and README.md in the root of this project for full details
  4. */
  5. // DEPRECATED FUNCTIONS
  6. ///////////////////////////////////////////////////////////////////////////////
  7. function _copy(){
  8. AtomPMClient.alertDeprecatedFunctionCall("_copy");
  9. EditUtils.copy();
  10. }
  11. function _paste(){
  12. AtomPMClient.alertDeprecatedFunctionCall("_paste");
  13. EditUtils.paste();
  14. }
  15. function _redo(){
  16. AtomPMClient.alertDeprecatedFunctionCall("_redo");
  17. EditUtils.redo();
  18. }
  19. function _undo(){
  20. AtomPMClient.alertDeprecatedFunctionCall("_undo");
  21. EditUtils.undo();
  22. }
  23. ///////////////////////////////////////////////////////////////////////////////
  24. // DEPRECATED FUNCTIONS
  25. ///////////////////////////////////////////////////////////////////////////////
  26. /* copies the full AS and CS of selected icons into a cross-tab/window storage
  27. area
  28. NOTE: the use of window.localStorage enables copy-pasting across atompm
  29. instances... this could also have been accomplished via cookies albeit
  30. less efficiently since cookies are logged around during every backend
  31. request/response */
  32. EditUtils = function(){
  33. /**
  34. * This copies the currently selected element(s) to the local
  35. * clipboard object
  36. */
  37. this.copy = function() {
  38. if( __selection == undefined || __selection['items'].length == 0 )
  39. return;
  40. else if( ! GeometryUtils.areTransformationsAllowed() )
  41. {
  42. console.warn('copy-pasting is only enabled if all of the ends of selected'
  43. +' edges are also selected, and if the geometry controls are'
  44. +' inactive');
  45. return;
  46. }
  47. var icons =
  48. __selection['items'].filter( function(it) {return it in __icons;} ),
  49. todo = icons.length,
  50. cpdata = {};
  51. icons.forEach(
  52. function(uri)
  53. {
  54. if( cpdata == undefined )
  55. return;
  56. HttpUtils.httpReq(
  57. 'GET',
  58. HttpUtils.url(uri,__NO_USERNAME),
  59. '&full=1',
  60. function(statusCode,resp)
  61. {
  62. if( ! utils.isHttpSuccessCode(statusCode) )
  63. {
  64. WindowManagement.openDialog(_ERROR,'failed to retrieve copy data :: '+resp);
  65. cpdata = undefined;
  66. return;
  67. }
  68. else
  69. {
  70. var data = utils.jsonp(resp)['data'],
  71. cs = utils.jsonp(data['cs']),
  72. as = utils.jsonp(data['as']),
  73. csattrs = {},
  74. asattrs = {};
  75. for( var attr in cs )
  76. csattrs[attr] = cs[attr]['value'];
  77. for( var attr in as )
  78. asattrs[attr] = as[attr]['value'];
  79. delete csattrs['$type'];
  80. delete csattrs['$asuri'];
  81. delete asattrs['$type'];
  82. cpdata[uri] = {'cs':csattrs,'as':asattrs};
  83. }
  84. if( --todo == 0 )
  85. window.localStorage.setItem('clipboard',utils.jsons(cpdata));
  86. });
  87. });
  88. };
  89. /* paste clipboard contents by building [and sending] batchEdit query filled
  90. with node creation/connection requests
  91. 0. produce metamodel loading requests for missing clipboard entry metamodels,
  92. if any
  93. 1. sort contents such that non-Links get handled first... this is merely an
  94. optimization that optimizes step 2.
  95. 2. while clipboard items remain,
  96. .) shift first item
  97. a) if item is a non-connection type, produce a creation request for it
  98. and remember its index in 'it2creq'
  99. a) otherwise,
  100. i. if both connection ends have entries in 'it2creq', produce a
  101. creation request for item and remember its index in 'it2creq'
  102. i. otherwise, push item at the end of the list of remaining items
  103. clipboard data for the given item is used to populate the request's
  104. 'attrs', ['src', 'dest'] and 'hitchhiker.clone' parameters
  105. 3. send off the batchEdit request
  106. 4. on completion, select newly created nodes and edges
  107. NOTE 1:: to connect to-be created nodes and to select them, we need their ids
  108. ... unfortunately, the csworker can not return the csid of a newly
  109. requested node (this csid will only come to be when the csworker
  110. handles the asworker's MKNODE)... for this reason, paste batchEdits
  111. are directed to our asworker (who responds to creation requests with
  112. the asids of newly created nodes)
  113. NOTE 2:: the 'it2creq' data structure is used to remember which request is
  114. associated to which to-be node... this is needed for connection
  115. requests with parameterized source and destination ids
  116. NOTE 3:: a limitation of the current implementation (which could fairly
  117. easily be overcome) is that Link '$segments' are expected to always
  118. contain exactly 2 entries
  119. NOTE 4:: selection of pasted elements requires that all icons be created...
  120. the batchEdit response only indicates that all asworker entities
  121. have been created: their associated icons might not all exist yet...
  122. to this end, a timed-loop checks for all the icons to be created
  123. before triggering selection... this works but it isn't perfect for
  124. 3 reasons
  125. 1. there might be a visible lag before selecting
  126. 2. a malicious user could cause the timed-loop to run forever
  127. by deleting pasted entities before the loop detects their
  128. creation
  129. 3. the loop almost always runs in O(||__icons||) */
  130. /**
  131. * Pastes the current contents of the clipboard.
  132. */
  133. this.paste = function() {
  134. var clipboard = utils.jsonp(window.localStorage.getItem('clipboard'));
  135. if( clipboard == null )
  136. {
  137. console.warn('clipboard is empty');
  138. return;
  139. }
  140. var toload = {},
  141. requests = [],
  142. it2creq = {},
  143. tmpasuri =
  144. function(csuri,reqi)
  145. {
  146. var matches = csuri.match(
  147. /^(.*)\..*Icons(\.pattern){0,1}(\/.*)Link\/[a-zA-Z0-9]*\.instance$/) ||
  148. csuri.match(
  149. /^(.*)\..*Icons(\.pattern){0,1}(\/.*)Icon\/[a-zA-Z0-9]*\.instance$/);
  150. return matches[1]+
  151. (matches[2] || '')+
  152. matches[3]+'/$'+reqi+'$.instance';
  153. },
  154. cburis = utils.keys(clipboard);
  155. cburis.forEach(
  156. function(uri)
  157. {
  158. var imm = __getMetamodel(uri)+'.metamodel',
  159. asmm = __iconMetamodelToMetamodel(imm);
  160. if( (!(asmm in __loadedToolbars) || !(imm in __loadedToolbars)) &&
  161. !(imm in toload) )
  162. {
  163. requests.push(
  164. {'method':'PUT',
  165. 'uri':HttpUtils.url('/current.metamodels', __NO_USERNAME+__NO_WID),
  166. 'reqData':
  167. {'mm':HttpUtils.url(asmm,__NO_WID),
  168. 'hitchhiker':{'path':HttpUtils.url(imm,__NO_WID)}}});
  169. toload[imm] = 1;
  170. }
  171. });
  172. cburis.sort( function(a,b) {return (__isConnectionType(a) ? 1 : -1);} );
  173. while( cburis.length > 0 )
  174. {
  175. var uri = cburis.shift(),
  176. matches = uri.match(
  177. /^(.*)\..*Icons(\.pattern){0,1}(\/.*)Link\/[a-zA-Z0-9]*\.instance$/) ||
  178. uri.match(
  179. /^(.*)\..*Icons(\.pattern){0,1}(\/.*)Icon\/[a-zA-Z0-9]*\.instance$/),
  180. type = matches[1]+(matches[2] || '')+matches[3]+'.type';
  181. if( ! __isConnectionType(uri) )
  182. {
  183. it2creq[uri] = requests.length;
  184. requests.push(
  185. {'method':'POST',
  186. 'uri':HttpUtils.url(type,__NO_USERNAME+__NO_WID),
  187. 'reqData':
  188. {'attrs':clipboard[uri]['as'],
  189. 'hitchhiker':{'clone':clipboard[uri]['cs']}}});
  190. }
  191. else
  192. {
  193. var segments = clipboard[uri]['cs']['$segments'],
  194. src = undefined,
  195. dest = undefined,
  196. asSrc,
  197. asDest;
  198. for( var edgeId in segments )
  199. {
  200. var ends = __edgeId2ends(edgeId);
  201. if( ends[0] == uri )
  202. dest = ends[1];
  203. else
  204. src = ends[0];
  205. }
  206. src = (src || uri);
  207. dest = (dest || uri);
  208. if( it2creq[src] == undefined || it2creq[dest] == undefined )
  209. cburis.push(uri);
  210. else
  211. {
  212. delete clipboard[uri]['cs']['$segments'];
  213. it2creq[uri] = requests.length;
  214. asSrc = tmpasuri(src,it2creq[src]);
  215. asDest = tmpasuri(dest,it2creq[dest]);
  216. requests.push(
  217. {'method':'POST',
  218. 'uri':HttpUtils.url(type,__NO_USERNAME+__NO_WID),
  219. 'reqData':
  220. {'attrs':clipboard[uri]['as'],
  221. 'src': asSrc,
  222. 'dest': asDest,
  223. 'hitchhiker':
  224. {'clone':clipboard[uri]['cs'],
  225. 'asSrc':asSrc,
  226. 'asDest':asDest,
  227. 'segments':
  228. [segments[src+'--'+uri],
  229. segments[uri+'--'+dest]]}}});
  230. }
  231. }
  232. }
  233. HttpUtils.httpReq(
  234. 'POST',
  235. HttpUtils.url('/batchEdit',__NO_USERNAME+__NO_WID)+'?wid='+__aswid,
  236. requests,
  237. function(statusCode,resp)
  238. {
  239. if( ! utils.isHttpSuccessCode(statusCode) )
  240. {
  241. WindowManagement.openDialog(_ERROR, 'paste failed on :: '+resp);
  242. return;
  243. }
  244. var results = utils.jsonp(resp)['data']['results'],
  245. asids = results.filter(function(r) {return 'data' in r;}).
  246. map(function(r) {return ''+r['data'];}),
  247. csuris = [],
  248. selectResults =
  249. function()
  250. {
  251. for( var uri in __icons )
  252. {
  253. var id = __icons[uri]['icon'].getAttr('__asuri').
  254. match(/.*\/(.*)\.instance/)[1];
  255. if( (idx=asids.indexOf(id)) > -1 )
  256. {
  257. if( __isConnectionType(uri) )
  258. csuris = csuris.concat(
  259. __icons[uri]['edgesIn'],__icons[uri]['edgesOut']);
  260. csuris.push(uri);
  261. asids.splice(idx,1);
  262. }
  263. if( asids.length == 0 )
  264. return BehaviorManager.handleUserEvent(__EVENT_CODED_SELECTION,csuris);
  265. }
  266. window.setTimeout(selectResults,100);
  267. };
  268. selectResults();
  269. });
  270. };
  271. /**
  272. * Redo the last undone action
  273. */
  274. this.redo = function(){
  275. BehaviorManager.handleUserEvent(__EVENT_CODED_CANVAS_EDIT);
  276. HttpUtils.httpReq(
  277. 'POST',
  278. HttpUtils.url('/redo',__NO_USERNAME));
  279. };
  280. /**
  281. * Undo the last performed action
  282. */
  283. this.undo = function() {
  284. BehaviorManager.handleUserEvent(__EVENT_CODED_CANVAS_EDIT);
  285. HttpUtils.httpReq(
  286. 'POST',
  287. HttpUtils.url('/undo',__NO_USERNAME));
  288. };
  289. return this;
  290. }();