gui_utils.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  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,undefined,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. input.attr("rows", (rows || 7));
  403. input.val(code);
  404. input.attr("class", className || 'code_style');
  405. input.keydown( function(event) {
  406. if( event.keyCode == KEY_RIGHT_ARROW /* This is to simulate a tab press */ ) {
  407. if( currentKeys[ KEY_ALT ] == 1 && currentKeys[ KEY_CTRL ] != 1){
  408. var cursorPos = event.target.selectionStart,
  409. lineStart = input.val().lastIndexOf('\n',cursorPos-1)+1,
  410. tabBy = __TAB_WIDTH - (cursorPos-lineStart)%__TAB_WIDTH,
  411. tab = '';
  412. for(var i=0; i<tabBy; i++) tab += ' ';
  413. input.val(
  414. input.val().substring(0,cursorPos)+tab+
  415. input.val().substring(cursorPos));
  416. input.get(0).setSelectionRange(cursorPos+tabBy,cursorPos+tabBy);
  417. return true;
  418. }
  419. return true;
  420. }
  421. else if( event.keyCode == KEY_ENTER /* ENTER */ && currentKeys[ KEY_SHIFT ] == 1) // HUSEYIN-ENTER
  422. {
  423. var cursorPos = event.target.selectionStart;
  424. input.val(
  425. input.val().substring(0,cursorPos)+'\r\n'+
  426. input.val().substring(cursorPos));
  427. input.get(0).setSelectionRange(cursorPos+1,cursorPos+1);
  428. event.stopPropagation();
  429. event.preventDefault();
  430. return false;
  431. }
  432. else if( event.keyCode == KEY_ENTER ) // HUSEYIN-ENTER
  433. {
  434. event.stopPropagation();
  435. event.preventDefault();
  436. return false;
  437. }
  438. });
  439. return input;
  440. };
  441. /**
  442. * Constructs a <span> element.
  443. *
  444. * @param text - the default text to be displayed
  445. * @param className - the default class name
  446. */
  447. this.getTextSpan = function(text,className)
  448. {
  449. var span = $('<span>');
  450. span.html( text.replace(/\n/g,'<br/>') );
  451. span.attr("class", className || 'default_style');
  452. return span;
  453. };
  454. /**
  455. * Finds and removes the specified toolbar, if present
  456. *
  457. * @param tb - the toolbar to be removed
  458. */
  459. this.removeToolbar = function(tb){
  460. tb_fixed = tb.replace(/\//g, "\\/");
  461. if( $('#div_toolbar_'+tb_fixed) )
  462. {
  463. //Find the toolbar in the dock bar and remove it
  464. //from the DOM
  465. $("#div_dock").children("div").each( function() {
  466. if( this.id == "div_toolbar_" + tb ){
  467. $(this).remove();
  468. }
  469. });
  470. // Now delete it from the list of loaded toolbars
  471. delete __loadedToolbars[tb];
  472. }
  473. };
  474. /**
  475. * Throw out the current canvas, replace it with a new one.
  476. * 1. Unselect any current selection
  477. * 2. Clear canvas
  478. * 3. Clear icon and edge data
  479. * 4. reset canvas statechart
  480. */
  481. this.resetCanvas = function (){
  482. __select();
  483. __canvas.clear();
  484. __icons = {};
  485. __edges = {};
  486. __canvasBehaviourStatechart.init();
  487. };
  488. /**
  489. * Sets up a model popup window and displays it.
  490. *
  491. * @param elements - DOM elements to be displayed in order
  492. * @param getinput - function that retrieves the desired user-input
  493. * from "elements".
  494. * @param type - Can be __NO_BUTTONS, __ONE_BUTTON,
  495. * or __TWO_BUTTONS and controls the number of displayed buttons
  496. * @param title - the dialog title
  497. * @param callback - called with the result of getinput when the ok
  498. * button is clicked
  499. */
  500. this.setupAndShowDialog = function(elements,getinput,type,title,callback){
  501. BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  502. var dialog = $('#div_dialog'),
  503. dim_bg = $('#div_dim_bg'),
  504. div_title = $('<div>');
  505. div_title.attr("class", 'dialog_title');
  506. div_title.append(GUIUtils.getTextSpan(title || ''));
  507. dialog.append(div_title);
  508. elements.forEach(
  509. function(e)
  510. {
  511. dialog.append(e);
  512. dialog.append( $('<br>') );
  513. });
  514. dialog.append( $('<br>') );
  515. if( type != __NO_BUTTONS )
  516. {
  517. //var ok = $('<button>');
  518. var ok = $('<button id="okbutton">'); // HUSEYIN-ENTER
  519. ok.click( function(ev) {
  520. if( getinput == undefined )
  521. {
  522. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  523. if( callback != undefined )
  524. callback();
  525. }
  526. else
  527. {
  528. try{
  529. var input = getinput();
  530. } catch(err) {
  531. console.error('failed to retrieve dialog input :: '+err);
  532. return;
  533. }
  534. input = (utils.isArray(input) ?
  535. input.map(function(i) {return String(i);}) :
  536. input);
  537. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  538. callback(input);
  539. }
  540. });
  541. ok.html('ok');
  542. dialog.append(ok);
  543. }
  544. if( type == __TWO_BUTTONS )
  545. {
  546. var cancel = $('<button>');
  547. cancel.click(function(ev) {
  548. BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  549. });
  550. cancel.html('cancel');
  551. dialog.append(cancel);
  552. }
  553. BehaviorManager.setActiveBehaviourStatechart(__SC_DIALOG);
  554. BehaviorManager.handleUserEvent(__EVENT_SHOW_DIALOG);
  555. };
  556. /*
  557. NOTE:: the sortedButtonNames() function sorts icon definition metamodels and
  558. button models s.t. buttons appear in an order that reflects their
  559. model... to be perfectly clean, this should be absorbed into icon
  560. definition and button model compilers, but since button models aren't
  561. compiled, it was simpler to just let this relatively simple logic live
  562. here
  563. */
  564. /**
  565. * Sets up and shows a toolbar according to the given button model or metamodel.
  566. *
  567. * This method:
  568. * 1. Removes any old instance of the toolbar if it currently exists
  569. * 2. Creates a <div> to hold the buttons
  570. * 3. Creates each button and appends it to the <div>
  571. * 4. Add the <div> to the dock
  572. * 5. Map the toolbar to its data
  573. *
  574. * @param tb - the toolbar to setup and show
  575. * @param data - the data to bind the toolbar to
  576. * @param type - the toolbar type, can be __BUTTON_TOOLBAR or __METAMODEL_TOOLBAR
  577. */
  578. this.setupAndShowToolbar = function(tb,data,type)
  579. {
  580. var imgSrc =
  581. function(name)
  582. {
  583. return (type == __BUTTON_TOOLBAR ?
  584. tb.substring(0,tb.lastIndexOf('/')+1)+name+'.icon.png' :
  585. '/Formalisms/default.icon.png');
  586. },
  587. className =
  588. function()
  589. {return (type == __BUTTON_TOOLBAR ? 'toolbar_bm' : 'toolbar_mm');},
  590. buttons =
  591. (type == __BUTTON_TOOLBAR ? data.asm.nodes : data.types),
  592. sortedButtons =
  593. function()
  594. {
  595. return (type == __BUTTON_TOOLBAR ?
  596. /* sort button names according to their position in their
  597. associated buttons model */
  598. utils.sortDict(data.csm.nodes,
  599. function(b1,b2)
  600. {
  601. var pos1 = b1['position']['value'],
  602. pos2 = b2['position']['value'];
  603. if( (pos1[1] < pos2[1]) ||
  604. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  605. return -1;
  606. return 1;
  607. }) :
  608. utils.sortDict(data.types,
  609. /* sort type names according to their IconIcon's position in
  610. the associated icon definition model */
  611. function(b1,b2)
  612. {
  613. var pos1 = undefined,
  614. pos2 = undefined;
  615. b1.some( function(attr)
  616. {
  617. if(attr['name'] == 'position')
  618. pos1 = attr['default'];
  619. return pos1;
  620. });
  621. b2.some( function(attr)
  622. {
  623. if(attr['name'] == 'position')
  624. pos2 = attr['default'];
  625. return pos2;
  626. });
  627. if( (pos1[1] < pos2[1]) ||
  628. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  629. return -1;
  630. return 1;
  631. }) );
  632. },
  633. createButton =
  634. function(name,tooltip,code)
  635. {
  636. var div = $('<div>'),
  637. img = $('<img>');
  638. div.addClass( 'toolbar_button' );
  639. div.attr("id", tb+'/'+name);
  640. div.attr("title", tooltip);
  641. div.click( function(ev){
  642. var res = HttpUtils.safeEval(code);
  643. if( res['$uerr'] )
  644. WindowManagement.openDialog(
  645. _ERROR,
  646. 'unexpected error in button code ::\n '+res['$uerr']);
  647. else if( res['$err'] )
  648. WindowManagement.openDialog(
  649. _ERROR,
  650. 'error in button code ::\n '+res['$err']);
  651. });
  652. // var url = HttpUtils.url(imgSrc(name),__NO_WID);
  653. img.attr("src", HttpUtils.url(imgSrc(name),__NO_WID));
  654. div.append(img);
  655. return div;
  656. };
  657. GUIUtils.removeToolbar(tb);
  658. var tb_div = $('<div>');
  659. tb_div.attr("id", 'div_toolbar_'+tb);
  660. tb_div.attr("class", className()+' toolbar unselectable' );
  661. tb_div.attr("title", tb);
  662. sortedButtons().forEach(
  663. function(b)
  664. {
  665. if( type == __METAMODEL_TOOLBAR && b.match(/(.*)Link$/) )
  666. return;
  667. var spc1 = $('<span>'),
  668. spc2 = $('<span>');
  669. // spc1.className = spc2.className = 'toolbar_space';
  670. spc1.attr("class", "toolbar_space" );
  671. spc2.attr("class", "toolbar_space" );
  672. tb_div.append(spc1);
  673. if( type == __BUTTON_TOOLBAR )
  674. tb_div.append(
  675. createButton(
  676. buttons[b]['name']['value'],
  677. buttons[b]['tooltip']['value'],
  678. buttons[b]['code']['value']) );
  679. else if( (matches = b.match(/(.*)Icon/)) )
  680. tb_div.append(
  681. createButton(
  682. b,
  683. 'create instance(s) of '+b.match(/(.*)Icon/)[1],
  684. '_setTypeToCreate("'+
  685. tb.substring(0,tb.length-'.metamodel'.length)+
  686. '/'+b+'");') );
  687. tb_div.append(spc2);
  688. } );
  689. if( tb_div.children().length == 0 )
  690. tb_div.append( GUIUtils.getTextSpan(tb,'toolbar_alt') );
  691. $('#div_dock').append(tb_div);
  692. __loadedToolbars[tb] = data;
  693. };
  694. return this;
  695. }();