edit_utils.js 10 KB

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