gui_utils.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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. GUIUtils = function(){
  17. /**
  18. * This is the old getElementById function
  19. */
  20. this.$$ = function( id ){
  21. return document.getElementById( id );
  22. };
  23. /**
  24. * Converts from page centric X coordinates to canvas centric X coordinates
  25. */
  26. this.convertToCanvasX = function(pageX){
  27. return pageX + $('#div_container').scrollLeft() - $('#contentDiv').offset().left;
  28. };
  29. /**
  30. * Converts from page centric Y coordinates to canvas centric Y coordinates
  31. */
  32. this.convertToCanvasY = function(pageY){
  33. return pageY + $('#div_container').scrollTop() - $('#contentDiv').offset().top;
  34. };
  35. /**
  36. * Disables the dock bar
  37. */
  38. this.disableDock = function(){
  39. $('#div_dock').attr('class', 'dock disabled_dock');
  40. };
  41. /**
  42. * Enables the dock bar
  43. */
  44. this.enableDock = function(){
  45. $('#div_dock').attr('class', 'dock');
  46. };
  47. /**
  48. * Constructs and returns a checked checkbox
  49. */
  50. this.getCheckbox = function(checked){
  51. var cb = $('<input>');
  52. cb.attr("type", 'checkbox');
  53. cb.prop("checked", checked);
  54. return cb;
  55. };
  56. /**
  57. * Returns a <div> with an interactive file browser within it
  58. *
  59. * @param fnames - a complete list of all files in the directory tree
  60. * structure
  61. * @param draggable - when true, files and folders can be meaningfully
  62. * dragged
  63. * @param newfile when true, an editable new file icon is present
  64. * @param startfolder if set, starts navigation at the specified folder
  65. */
  66. this.getFileBrowser = function(fnames, draggable, newfile, startfolder){
  67. var maxFnameLength = utils.max(fnames,function(_) {return _.length;}),
  68. fileb = $("<div>"),
  69. navdiv = $("<div>"),
  70. input = $("<input>"),
  71. selection = undefined,
  72. currfolder = '/',
  73. clearSelection =
  74. function()
  75. {
  76. if( selection )
  77. {
  78. selection.attr("class", 'fileb_icon');
  79. selection = undefined;
  80. input.val('');
  81. }
  82. },
  83. navbuttononclick =
  84. /* 1 construct the full path associated to the clicked button
  85. 2 remove 'deeper' buttons
  86. 3 chdir to selected folder */
  87. function(ev)
  88. {
  89. var path = '';
  90. for( var i=0; i<navdiv.children("button").length; i++ )
  91. {
  92. path += $(navdiv.children()[i]).html() +'/';
  93. if( $(navdiv.children()[i]).html() == $(ev.target).html() )
  94. break;
  95. }
  96. setCurrentFileBrowserFolder(path.substring(1), fnames);
  97. },
  98. setCurrentFileBrowserFolder =
  99. /* 1 determine files and folders from the given folder
  100. 2 produce icons for them and add them to to-be content div
  101. 3 create navigation toolbar for complete directory hierarchy
  102. 4 replace previous content div, if any, with new one
  103. 5 clear past selection, if any, and remember current folder */
  104. function(folder,fnames)
  105. {
  106. var div = $('#div_fileb-contents'),
  107. folders = [],
  108. files = [],
  109. maxFnameLength = 0,
  110. exists = false;
  111. // If it already exists, remove everything!
  112. if( div.length > 0 ) {
  113. $('#div_fileb-contents').remove();
  114. // exists = true;
  115. }
  116. div = $("<div>");
  117. div.attr("class", 'fileb_pane')
  118. .attr("id", 'div_fileb-contents');
  119. fnames.forEach( function(fname) {
  120. var _folder = utils.regexpe(folder);
  121. if( (matches=fname.match('^'+_folder+'(.+?/)')) )
  122. {
  123. if( ! utils.contains(folders,matches[1]) )
  124. folders.push(matches[1]);
  125. else
  126. return;
  127. }
  128. else if( (matches=fname.match('^'+_folder+'(.*)')) ) {
  129. if (matches[1].length > 0) {
  130. files.push(matches[1]);
  131. } else {
  132. return;
  133. }
  134. }
  135. else
  136. return;
  137. maxFnameLength =
  138. Math.max(maxFnameLength,matches[1].length);
  139. });
  140. // var tmpDiv = $("<div>");
  141. folders.concat(files).forEach( function(fname) {
  142. var icon = HttpUtils.getFileIcon(fname);
  143. if( icon )
  144. {
  145. icon.css("width", 8+maxFnameLength+'ex');
  146. icon.click( function(ev) {
  147. clearSelection();
  148. if( fname.match(/\/$/) )
  149. setCurrentFileBrowserFolder(folder+fname,fnames);
  150. else
  151. {
  152. input.val( folder+fname );
  153. selection = icon;
  154. selection.attr("class", 'fileb_icon_selected');
  155. }
  156. });
  157. if( draggable )
  158. {
  159. //icon.setAttribute('draggable',true);
  160. icon.attr("draggable", true);
  161. icon.get(0).ondragstart =
  162. function(event)
  163. {
  164. var uri = HttpUtils.url(folder+fname+'.file',__NO_WID);
  165. event.dataTransfer.effectAllowed = 'copyMove';
  166. event.dataTransfer.setData(
  167. 'DownloadURL',
  168. 'application/zip:'+fname+'.zip:'+
  169. window.location.origin+uri);
  170. event.dataTransfer.setData('uri',uri);
  171. };
  172. }
  173. div.append(icon);
  174. }
  175. });
  176. if( newfile )
  177. {
  178. var icon = HttpUtils.getNewFileIcon( function(ev) {
  179. input.val( folder+ev.target.textContent );
  180. });
  181. icon.css("width", 8+maxFnameLength+'ex');
  182. div.append(icon);
  183. }
  184. navdiv.empty();
  185. // while (navdiv.children().length > 0) {
  186. // navdiv.find(navdiv.lastChild).remove();
  187. // }
  188. tmpDiv = $("<div>");
  189. var subfolders = folder.split('/').filter(function(subf) {return subf != '';});
  190. subfolders.unshift('/');
  191. subfolders.forEach(function(subfolder) {
  192. var navbutton = $('<button>');
  193. navbutton.html( subfolder );
  194. navbutton.click(navbuttononclick);
  195. navdiv.append(navbutton);
  196. });
  197. //navdiv.html( tmpDiv.html() );
  198. if( exists ) {
  199. // $('#div_fileb-contents').html( div.html() );
  200. // fileb.append(div);
  201. $('#div_fileb-contents').remove();
  202. fileb.append(div);
  203. } else
  204. fileb.append(div);
  205. clearSelection();
  206. currfolder = folder;
  207. };
  208. fileb.css("width", maxFnameLength+'ex')
  209. .css("maxWidth", '100%');
  210. navdiv.css("align", 'left');
  211. input.attr("type", 'hidden');
  212. fileb.append(navdiv);
  213. fileb.append(input);
  214. setCurrentFileBrowserFolder(startfolder ? startfolder : '/',fnames);
  215. return {'filebrowser': fileb,
  216. 'filepane': function() {return $('#div_fileb-contents');},
  217. 'getcurrfolder': function() {return currfolder;},
  218. 'getselection': function() {return input.val();},
  219. 'clearselection': function() {clearSelection()},
  220. 'refresh': function(fnames, the_folder) {
  221. setCurrentFileBrowserFolder(the_folder || currfolder, fnames);
  222. }};
  223. };
  224. // TODO: replace the bundled function with an actual object generation. The
  225. // current method is sloppy.
  226. /**
  227. * Constructs and returns an input field given an attribute's type and value,
  228. * bundled in the return value is a function to retrieve the input fields
  229. * content as well as its initial value
  230. *
  231. * For Maps and Lists, onfocus/onblur toggle a default entry to be shown.
  232. */
  233. this.getInputField = function (type,value){
  234. /* recursively expand specialTypes, if any */
  235. function explodeType(t)
  236. {
  237. var exploded = __specialTypes[t] || t;
  238. while( exploded.match(/\$/) )
  239. exploded = exploded.replace(
  240. /(\$.+?)([,\]>])/g,
  241. function(s,p1,p2){return __specialTypes[p1]+p2;});
  242. return exploded;
  243. }
  244. /* return a default value for the given exploded type string */
  245. function defaultEntry(et)
  246. {
  247. if( et == 'string' || et == 'code' )
  248. return "";
  249. else if( et == 'int' )
  250. return 0;
  251. else if( et == 'boolean' )
  252. return true;
  253. else if( et == 'double' )
  254. return 0.0;
  255. else if( et.match(/^ENUM/) )
  256. return et;
  257. else if( (matches=et.match(/^list<(.*)>$/)) )
  258. return [defaultEntry(matches[1])];
  259. else if( (matches=et.match(/^map<\[(.*?)\],\[(.*)\]>$/)) )
  260. {
  261. var m = {},
  262. keys = matches[1].split(','),
  263. types = [],
  264. depth = 0,
  265. index = 0;
  266. for( var i=0; i<matches[2].length; i++ )
  267. {
  268. if( matches[2].charAt(i) == '(' ||
  269. matches[2].charAt(i) == '<' )
  270. depth++;
  271. else if( matches[2].charAt(i) == ')' ||
  272. matches[2].charAt(i) == '>' )
  273. depth--;
  274. if( matches[2].charAt(i) == ',' && depth == 0 )
  275. {
  276. types.push( matches[2].substring(index,i) );
  277. index = i+1;
  278. }
  279. }
  280. types.push( matches[2].substring(index) );
  281. for( var i=0; i<keys.length; i++ )
  282. m[keys[i]] = defaultEntry(types[i]);
  283. return m;
  284. }
  285. else if( (matches=et.match(/^map<(.*),(.*)>$/)) )
  286. {
  287. var m = {};
  288. m[defaultEntry(matches[1])] = defaultEntry(matches[2]);
  289. return m;
  290. }
  291. }
  292. if( type == 'code' )
  293. var input = GUIUtils.getTextInput(utils.jsond(value,'\n')),
  294. getinput = function(_){return _.val();};
  295. else if( type.match(/^map/) ||
  296. type.match(/^list/) )
  297. {
  298. var input = GUIUtils.getTextInput(
  299. utils.jsond(utils.jsons(value,undefined,' '))),
  300. getinput = function(_){
  301. return utils.jsonp(utils.jsone(_.val()));
  302. };
  303. var de = defaultEntry( explodeType(type) ),
  304. isMap = type.match(/^map/),
  305. matches = undefined;
  306. input.focus(
  307. (isMap ?
  308. function()
  309. {
  310. var newVal = utils.mergeDicts([getinput(input),de]);
  311. input.val( utils.jsond(utils.jsons(newVal,undefined,' ')));
  312. } :
  313. function()
  314. {
  315. var newVal = getinput(input).concat(de);
  316. input.val( utils.jsond(utils.jsons(newVal,undefined,' ')));
  317. }));
  318. input.blur(
  319. (isMap ?
  320. function()
  321. {
  322. var val = getinput(input);
  323. utils.splitDict(val,utils.keys(de));
  324. input.val( utils.jsond(utils.jsons(val,undefined,' ')));
  325. } :
  326. function()
  327. {
  328. var val = getinput(input);
  329. if( utils.jsons(utils.tail(val)) == utils.jsons(de[0]) )
  330. val.pop();
  331. input.val( utils.jsond(utils.jsons(val,undefined,' ')));
  332. }));
  333. }
  334. else if( type.match(/^ENUM/) )
  335. {
  336. var vals = type.match(/^ENUM\((.*)\)$/)[1],
  337. input = GUIUtils.getSelector(vals.split(','),false,[value]),
  338. getinput =
  339. function(_){return HttpUtils.getSelectorSelection(_)[0]};
  340. }
  341. else if( type.match(/^boolean$/) )
  342. {
  343. var input = GUIUtils.getCheckbox(value),
  344. getinput =
  345. function(_){return _.prop("checked");};
  346. }
  347. else if( type.match(/^\$/) )
  348. return GUIUtils.getInputField(__specialTypes[type],value);
  349. else if (matches = type.match("^file<(.*)>")) {
  350. var input = GUIUtils.getFileInput(value,matches[1],"code_style string_input",1),
  351. getinput = function(_){return _.val();}
  352. }
  353. else
  354. var input = GUIUtils.getTextInput(value,"code_style string_input",1),
  355. getinput = (type == 'string' ?
  356. function(_){return _.val();} :
  357. function(_){return utils.jsonp(_.val());});
  358. input.title = explodeType(type);
  359. return {'input':input, 'getinput':getinput, 'oldVal':getinput(input)};
  360. };
  361. /**
  362. * Constructs and returns a <select> element with the choices list converted into
  363. * a list of <option> elements.
  364. *
  365. * @param choices - the choices for the <select> element
  366. * @param multipleChoice - if true, allows for multiple options to be selected
  367. * @param defaultSelect - sets the default selection for the list
  368. * @param numVisibleOptions - sets the number of visible options
  369. */
  370. this.getSelector = function(choices,multipleChoice,defaultSelection,numVisibleOptions){
  371. var select = $('<select>');
  372. select.attr("class", 'default_style');
  373. select.attr("size", Math.min(choices.length,numVisibleOptions || __MAX_SELECT_OPTIONS));
  374. select.attr("multiple", multipleChoice);
  375. choices.forEach(
  376. function(choice)
  377. {
  378. var option = $('<option>');
  379. option.val( choice );
  380. option.html( choice );
  381. select.append(option);
  382. if( defaultSelection != undefined &&
  383. utils.contains(defaultSelection,choice) )
  384. option.prop("selected", true);
  385. });
  386. return select;
  387. };
  388. /* constructs an INPUT given some text and a CSS class */
  389. /**
  390. * Constructs an <input> element
  391. *
  392. * @param text - the default text for the input element
  393. * @param className - the default class name for the input element
  394. * @param width - the default width of the element. If this is omitted
  395. * then the <input> defaults to 400px wide.
  396. */
  397. this.getStringInput = function(text,className,width){
  398. var input = $('<input>');
  399. input.attr("type", 'text');
  400. input.attr("class", className || 'default_style');
  401. input.val( text );
  402. input.css("width", width || '400px');
  403. return input;
  404. };
  405. this.getFileInput = function(code,pattern,className,rows){
  406. var string_input = this.getTextInput(code, className, rows);
  407. var extra_el = $('<button>');
  408. extra_el.attr("width", 16);
  409. extra_el.attr("height", 16);
  410. extra_el.html("...");
  411. extra_el.click(function(event) {
  412. var options = {'extensions':[pattern],
  413. 'multipleChoice':false,
  414. 'manualInput':false,
  415. 'title':'choose a rule model',
  416. 'startDir':'model'},
  417. callback =
  418. function(fnames)
  419. {
  420. string_input.val(fnames[0]);
  421. };
  422. WindowManagement.openDialog(_FILE_BROWSER,options,callback);
  423. event.stopPropagation();
  424. event.preventDefault();
  425. });
  426. string_input.extra_el = extra_el;
  427. return string_input;
  428. }
  429. /**
  430. * Constructs a <textarea> element. In this element, Alt + Right Arrow
  431. * is treated as a tab.
  432. *
  433. * @param code - the default code for this text area
  434. * @param className - the default class name for the <textarea>
  435. * @param rows - the default number of rows for this <textarea>
  436. */
  437. this.getTextInput = function(code,className,rows){
  438. var input = $('<textarea>');
  439. input.attr("cols", 80);
  440. rows = rows || 7;
  441. input.attr("rows", (rows || 7));
  442. input.val(code);
  443. input.attr("class", className || 'code_style');
  444. input.keydown( function(event) {
  445. if( event.keyCode == KEY_RIGHT_ARROW /* This is to simulate a tab press */ ) {
  446. if( currentKeys[ KEY_ALT ] == 1 && currentKeys[ KEY_CTRL ] != 1){
  447. var cursorPos = event.target.selectionStart,
  448. lineStart = input.val().lastIndexOf('\n',cursorPos-1)+1,
  449. tabBy = __TAB_WIDTH - (cursorPos-lineStart)%__TAB_WIDTH,
  450. tab = '';
  451. for(var i=0; i<tabBy; i++) tab += ' ';
  452. input.val(
  453. input.val().substring(0,cursorPos)+tab+
  454. input.val().substring(cursorPos));
  455. input.get(0).setSelectionRange(cursorPos+tabBy,cursorPos+tabBy);
  456. return true;
  457. }
  458. return true;
  459. }
  460. // https://media.giphy.com/media/12XMGIWtrHBl5e/giphy.gif
  461. else if( event.keyCode == KEY_ENTER )
  462. {
  463. if (rows > 1) {
  464. // only for multi-line input fields
  465. var cursorPos = event.target.selectionStart;
  466. input.val(
  467. input.val().substring(0,cursorPos)+'\r\n'+
  468. input.val().substring(cursorPos));
  469. input.get(0).setSelectionRange(cursorPos+1,cursorPos+1);
  470. }
  471. event.stopPropagation();
  472. event.preventDefault();
  473. return true;
  474. }
  475. });
  476. input.keyup( function (event) {
  477. if( event.keyCode == KEY_ENTER )
  478. {
  479. event.stopPropagation();
  480. event.preventDefault();
  481. }
  482. });
  483. return input;
  484. };
  485. /**
  486. * Constructs a <span> element.
  487. *
  488. * @param text - the default text to be displayed
  489. * @param className - the default class name
  490. */
  491. this.getTextSpan = function(text,className)
  492. {
  493. var span = $('<span>');
  494. span.html( text.replace(/\n/g,'<br/>') );
  495. span.attr("class", className || 'default_style');
  496. return span;
  497. };
  498. /**
  499. * Finds and removes the specified toolbar, if present
  500. *
  501. * @param tb - the toolbar to be removed
  502. */
  503. this.removeToolbar = function(tb){
  504. tb_fixed = tb.replace(/\//g, "\\/");
  505. if( $('#div_toolbar_'+tb_fixed) )
  506. {
  507. //Find the toolbar in the dock bar and remove it
  508. //from the DOM
  509. $("#div_dock").children("div").each( function() {
  510. if( this.id == "div_toolbar_" + tb ){
  511. $(this).remove();
  512. }
  513. });
  514. // Now delete it from the list of loaded toolbars
  515. delete __loadedToolbars[tb];
  516. }
  517. };
  518. /**
  519. * Throw out the current canvas, replace it with a new one.
  520. * 1. Unselect any current selection
  521. * 2. Clear canvas
  522. * 3. Clear icon and edge data
  523. * 4. reset canvas statechart
  524. */
  525. this.resetCanvas = function (){
  526. __select();
  527. __canvas.clear();
  528. __icons = {};
  529. __edges = {};
  530. __canvasBehaviourStatechart.init();
  531. };
  532. /**
  533. * Sets up a model popup window and displays it.
  534. *
  535. * @param elements - DOM elements to be displayed in order
  536. * @param getinput - function that retrieves the desired user-input
  537. * from "elements".
  538. * @param type - Can be __NO_BUTTONS, __ONE_BUTTON,
  539. * or __TWO_BUTTONS and controls the number of displayed buttons
  540. * @param title - the dialog title
  541. * @param callback - called with the result of getinput when the ok
  542. * button is clicked
  543. */
  544. this.setupAndShowDialog = function(elements,getinput,type,title,callback){
  545. // BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  546. var dialog = $('#div_dialog'),
  547. the_id = __dialog_stack.length;
  548. dialog = dialog.clone().attr("id", 'div_dialog_'+the_id);
  549. dim_bg = $('#div_dim_bg'),
  550. div_title = $('<div>');
  551. __dialog_stack.push(dialog);
  552. dialog.appendTo(document.body);
  553. div_title.attr("class", 'dialog_title');
  554. div_title.append(GUIUtils.getTextSpan(title || ''));
  555. dialog.append(div_title);
  556. elements.forEach(
  557. function(e)
  558. {
  559. dialog.append(e);
  560. dialog.append( $('<br>') );
  561. });
  562. dialog.append( $('<br>') );
  563. if( type != __NO_BUTTONS )
  564. {
  565. var ok = $('<button class="okbutton">'); // HUSEYIN-ENTER
  566. ok.click( function(ev) {
  567. if( getinput == undefined )
  568. {
  569. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  570. if( callback != undefined )
  571. callback();
  572. }
  573. else
  574. {
  575. try{
  576. var input = getinput();
  577. } catch(err) {
  578. console.error('failed to retrieve dialog input :: '+err);
  579. return;
  580. }
  581. input = (utils.isArray(input) ?
  582. input.map(function(i) {return String(i);}) :
  583. input);
  584. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  585. callback(input);
  586. }
  587. });
  588. ok.html('ok');
  589. dialog.append(ok);
  590. }
  591. if( type == __TWO_BUTTONS )
  592. {
  593. var cancel = $('<button>');
  594. cancel.click(function(ev) {
  595. BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  596. });
  597. cancel.html('cancel');
  598. dialog.append(cancel);
  599. }
  600. BehaviorManager.setActiveBehaviourStatechart(__SC_DIALOG);
  601. BehaviorManager.handleUserEvent(__EVENT_SHOW_DIALOG);
  602. };
  603. /*
  604. NOTE:: the sortedButtonNames() function sorts icon definition metamodels and
  605. button models s.t. buttons appear in an order that reflects their
  606. model... to be perfectly clean, this should be absorbed into icon
  607. definition and button model compilers, but since button models aren't
  608. compiled, it was simpler to just let this relatively simple logic live
  609. here
  610. */
  611. /**
  612. * Sets up and shows a toolbar according to the given button model or metamodel.
  613. *
  614. * This method:
  615. * 1. Removes any old instance of the toolbar if it currently exists
  616. * 2. Creates a <div> to hold the buttons
  617. * 3. Creates each button and appends it to the <div>
  618. * 4. Add the <div> to the dock
  619. * 5. Map the toolbar to its data
  620. *
  621. * @param tb - the toolbar to setup and show
  622. * @param data - the data to bind the toolbar to
  623. * @param type - the toolbar type, can be __BUTTON_TOOLBAR or __METAMODEL_TOOLBAR
  624. */
  625. this.setupAndShowToolbar = function(tb,data,type)
  626. {
  627. var imgSrc =
  628. function(name)
  629. {
  630. return (type == __BUTTON_TOOLBAR ?
  631. tb.substring(0,tb.lastIndexOf('/')+1)+name+'.icon.png' :
  632. '/Formalisms/default.icon.png');
  633. },
  634. className =
  635. function()
  636. {return (type == __BUTTON_TOOLBAR ? 'toolbar_bm' : 'toolbar_mm');},
  637. buttons =
  638. (type == __BUTTON_TOOLBAR ? data.asm.nodes : data.types),
  639. sortedButtons =
  640. function()
  641. {
  642. return (type == __BUTTON_TOOLBAR ?
  643. /* sort button names according to their position in their
  644. associated buttons model */
  645. utils.sortDict(data.csm.nodes,
  646. function(b1,b2)
  647. {
  648. var pos1 = b1['position']['value'],
  649. pos2 = b2['position']['value'];
  650. if( (pos1[1] < pos2[1]) ||
  651. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  652. return -1;
  653. return 1;
  654. }) :
  655. utils.sortDict(data.types,
  656. /* sort type names according to their IconIcon's position in
  657. the associated icon definition model */
  658. function(b1,b2)
  659. {
  660. var pos1 = undefined,
  661. pos2 = undefined;
  662. b1.some( function(attr)
  663. {
  664. if(attr['name'] == 'position')
  665. pos1 = attr['default'];
  666. return pos1;
  667. });
  668. b2.some( function(attr)
  669. {
  670. if(attr['name'] == 'position')
  671. pos2 = attr['default'];
  672. return pos2;
  673. });
  674. if( (pos1[1] < pos2[1]) ||
  675. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  676. return -1;
  677. return 1;
  678. }) );
  679. },
  680. createButton =
  681. function(name,tooltip,code)
  682. {
  683. var div = $('<div>'),
  684. img = $('<img>');
  685. div.addClass( 'toolbar_button' );
  686. div.attr("id", tb+'/'+name);
  687. div.attr("title", tooltip);
  688. div.click( function(ev){
  689. var res = HttpUtils.safeEval(code);
  690. if( res['$uerr'] )
  691. WindowManagement.openDialog(
  692. _ERROR,
  693. 'unexpected error in button code ::\n '+res['$uerr']);
  694. else if( res['$err'] )
  695. WindowManagement.openDialog(
  696. _ERROR,
  697. 'error in button code ::\n '+res['$err']);
  698. });
  699. // var url = HttpUtils.url(imgSrc(name),__NO_WID);
  700. img.attr("src", HttpUtils.url(imgSrc(name),__NO_WID));
  701. div.append(img);
  702. return div;
  703. };
  704. GUIUtils.removeToolbar(tb);
  705. var tb_div = $('<div>');
  706. tb_div.attr("id", 'div_toolbar_'+tb);
  707. tb_div.attr("class", className()+' toolbar unselectable' );
  708. tb_div.attr("title", tb);
  709. sortedButtons().forEach(
  710. function(b)
  711. {
  712. if( type == __METAMODEL_TOOLBAR && b.match(/(.*)Link$/) )
  713. return;
  714. var spc1 = $('<span>'),
  715. spc2 = $('<span>');
  716. // spc1.className = spc2.className = 'toolbar_space';
  717. spc1.attr("class", "toolbar_space" );
  718. spc2.attr("class", "toolbar_space" );
  719. tb_div.append(spc1);
  720. if( type == __BUTTON_TOOLBAR )
  721. tb_div.append(
  722. createButton(
  723. buttons[b]['name']['value'],
  724. buttons[b]['tooltip']['value'],
  725. buttons[b]['code']['value']) );
  726. else if( (matches = b.match(/(.*)Icon/)) )
  727. tb_div.append(
  728. createButton(
  729. b,
  730. 'create instance(s) of '+b.match(/(.*)Icon/)[1],
  731. '_setTypeToCreate("'+
  732. tb.substring(0,tb.length-'.metamodel'.length)+
  733. '/'+b+'");') );
  734. tb_div.append(spc2);
  735. } );
  736. if( tb_div.children().length == 0 )
  737. tb_div.append( GUIUtils.getTextSpan(tb,'toolbar_alt') );
  738. $('#div_dock').append(tb_div);
  739. __loadedToolbars[tb] = data;
  740. };
  741. return this;
  742. }();