gui_utils.js 22 KB

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