gui_utils.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  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. input.focus(
  306. (isMap ?
  307. function()
  308. {
  309. var newVal = utils.mergeDicts([getinput(input),de]);
  310. input.val( utils.jsond(utils.jsons(newVal,undefined,' ')));
  311. } :
  312. function()
  313. {
  314. var newVal = getinput(input).concat(de);
  315. input.val( utils.jsond(utils.jsons(newVal,undefined,' ')));
  316. }));
  317. input.blur(
  318. (isMap ?
  319. function()
  320. {
  321. var val = getinput(input);
  322. utils.splitDict(val,utils.keys(de));
  323. input.val( utils.jsond(utils.jsons(val,undefined,' ')));
  324. } :
  325. function()
  326. {
  327. var val = getinput(input);
  328. if( utils.jsons(utils.tail(val)) == utils.jsons(de[0]) )
  329. val.pop();
  330. input.val( utils.jsond(utils.jsons(val,undefined,' ')));
  331. }));
  332. }
  333. else if( type.match(/^ENUM/) )
  334. {
  335. var vals = type.match(/^ENUM\((.*)\)$/)[1],
  336. input = GUIUtils.getSelector(vals.split(','),false,[value]),
  337. getinput =
  338. function(_){return HttpUtils.getSelectorSelection(_)[0]};
  339. }
  340. else if( type.match(/^boolean$/) )
  341. {
  342. var input = GUIUtils.getCheckbox(value),
  343. getinput =
  344. function(_){return _.prop("checked");};
  345. }
  346. else if( type.match(/^\$/) )
  347. return GUIUtils.getInputField(__specialTypes[type],value);
  348. else
  349. var input = GUIUtils.getTextInput(value,"code_style string_input",1),
  350. getinput = (type == 'string' ?
  351. function(_){return _.val();} :
  352. function(_){return utils.jsonp(_.val());});
  353. input.title = explodeType(type);
  354. return {'input':input, 'getinput':getinput, 'oldVal':getinput(input)};
  355. };
  356. /**
  357. * Constructs and returns a <select> element with the choices list converted into
  358. * a list of <option> elements.
  359. *
  360. * @param choices - the choices for the <select> element
  361. * @param multipleChoice - if true, allows for multiple options to be selected
  362. * @param defaultSelect - sets the default selection for the list
  363. * @param numVisibleOptions - sets the number of visible options
  364. */
  365. this.getSelector = function(choices,multipleChoice,defaultSelection,numVisibleOptions){
  366. var select = $('<select>');
  367. select.attr("class", 'default_style');
  368. select.attr("size", Math.min(choices.length,numVisibleOptions || __MAX_SELECT_OPTIONS));
  369. select.attr("multiple", multipleChoice);
  370. choices.forEach(
  371. function(choice)
  372. {
  373. var option = $('<option>');
  374. option.val( choice );
  375. option.html( choice );
  376. select.append(option);
  377. if( defaultSelection != undefined &&
  378. utils.contains(defaultSelection,choice) )
  379. option.prop("selected", true);
  380. });
  381. return select;
  382. };
  383. /* constructs an INPUT given some text and a CSS class */
  384. /**
  385. * Constructs an <input> element
  386. *
  387. * @param text - the default text for the input element
  388. * @param className - the default class name for the input element
  389. * @param width - the default width of the element. If this is omitted
  390. * then the <input> defaults to 400px wide.
  391. */
  392. this.getStringInput = function(text,className,width){
  393. var input = $('<input>');
  394. input.attr("type", 'text');
  395. input.attr("class", className || 'default_style');
  396. input.val( text );
  397. input.css("width", width || '400px');
  398. return input;
  399. };
  400. /**
  401. * Constructs a <textarea> element. In this element, Alt + Right Arrow
  402. * is treated as a tab.
  403. *
  404. * @param code - the default code for this text area
  405. * @param className - the default class name for the <textarea>
  406. * @param rows - the default number of rows for this <textarea>
  407. */
  408. this.getTextInput = function(code,className,rows){
  409. var input = $('<textarea>');
  410. input.attr("cols", 80);
  411. rows = rows || 7;
  412. input.attr("rows", (rows || 7));
  413. input.val(code);
  414. input.attr("class", className || 'code_style');
  415. input.keydown( function(event) {
  416. if( event.keyCode == KEY_RIGHT_ARROW /* This is to simulate a tab press */ ) {
  417. if( currentKeys[ KEY_ALT ] == 1 && currentKeys[ KEY_CTRL ] != 1){
  418. var cursorPos = event.target.selectionStart,
  419. lineStart = input.val().lastIndexOf('\n',cursorPos-1)+1,
  420. tabBy = __TAB_WIDTH - (cursorPos-lineStart)%__TAB_WIDTH,
  421. tab = '';
  422. for(var i=0; i<tabBy; i++) tab += ' ';
  423. input.val(
  424. input.val().substring(0,cursorPos)+tab+
  425. input.val().substring(cursorPos));
  426. input.get(0).setSelectionRange(cursorPos+tabBy,cursorPos+tabBy);
  427. return true;
  428. }
  429. return true;
  430. }
  431. else if( event.keyCode == KEY_ENTER )
  432. {
  433. if (rows > 1) {
  434. // only for multi-line input fields
  435. var cursorPos = event.target.selectionStart;
  436. input.val(
  437. input.val().substring(0,cursorPos)+'\r\n'+
  438. input.val().substring(cursorPos));
  439. input.get(0).setSelectionRange(cursorPos+1,cursorPos+1);
  440. }
  441. event.stopPropagation();
  442. event.preventDefault();
  443. return true;
  444. }
  445. });
  446. input.keyup( function (event) {
  447. if( event.keyCode == KEY_ENTER )
  448. {
  449. event.stopPropagation();
  450. event.preventDefault();
  451. }
  452. });
  453. return input;
  454. };
  455. /**
  456. * Constructs a <span> element.
  457. *
  458. * @param text - the default text to be displayed
  459. * @param className - the default class name
  460. */
  461. this.getTextSpan = function(text,className)
  462. {
  463. var span = $('<span>');
  464. span.html( text.replace(/\n/g,'<br/>') );
  465. span.attr("class", className || 'default_style');
  466. return span;
  467. };
  468. /**
  469. * Finds and removes the specified toolbar, if present
  470. *
  471. * @param tb - the toolbar to be removed
  472. */
  473. this.removeToolbar = function(tb){
  474. tb_fixed = tb.replace(/\//g, "\\/");
  475. if( $('#div_toolbar_'+tb_fixed) )
  476. {
  477. //Find the toolbar in the dock bar and remove it
  478. //from the DOM
  479. $("#div_dock").children("div").each( function() {
  480. if( this.id == "div_toolbar_" + tb ){
  481. $(this).remove();
  482. }
  483. });
  484. // Now delete it from the list of loaded toolbars
  485. delete __loadedToolbars[tb];
  486. }
  487. };
  488. /**
  489. * Throw out the current canvas, replace it with a new one.
  490. * 1. Unselect any current selection
  491. * 2. Clear canvas
  492. * 3. Clear icon and edge data
  493. * 4. reset canvas statechart
  494. */
  495. this.resetCanvas = function (){
  496. __select();
  497. __canvas.clear();
  498. __icons = {};
  499. __edges = {};
  500. __canvasBehaviourStatechart.init();
  501. };
  502. /**
  503. * Sets up a model popup window and displays it.
  504. *
  505. * @param elements - DOM elements to be displayed in order
  506. * @param getinput - function that retrieves the desired user-input
  507. * from "elements".
  508. * @param type - Can be __NO_BUTTONS, __ONE_BUTTON,
  509. * or __TWO_BUTTONS and controls the number of displayed buttons
  510. * @param title - the dialog title
  511. * @param callback - called with the result of getinput when the ok
  512. * button is clicked
  513. */
  514. this.setupAndShowDialog = function(elements,getinput,type,title,callback){
  515. BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  516. var dialog = $('#div_dialog'),
  517. dim_bg = $('#div_dim_bg'),
  518. div_title = $('<div>');
  519. div_title.attr("class", 'dialog_title');
  520. div_title.append(GUIUtils.getTextSpan(title || ''));
  521. dialog.append(div_title);
  522. elements.forEach(
  523. function(e)
  524. {
  525. dialog.append(e);
  526. dialog.append( $('<br>') );
  527. });
  528. dialog.append( $('<br>') );
  529. if( type != __NO_BUTTONS )
  530. {
  531. //var ok = $('<button>');
  532. var ok = $('<button id="okbutton">'); // HUSEYIN-ENTER
  533. ok.click( function(ev) {
  534. if( getinput == undefined )
  535. {
  536. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  537. if( callback != undefined )
  538. callback();
  539. }
  540. else
  541. {
  542. try{
  543. var input = getinput();
  544. } catch(err) {
  545. console.error('failed to retrieve dialog input :: '+err);
  546. return;
  547. }
  548. input = (utils.isArray(input) ?
  549. input.map(function(i) {return String(i);}) :
  550. input);
  551. BehaviorManager.handleUserEvent(__EVENT_OKAYED_DIALOG);
  552. callback(input);
  553. }
  554. });
  555. ok.html('ok');
  556. dialog.append(ok);
  557. }
  558. if( type == __TWO_BUTTONS )
  559. {
  560. var cancel = $('<button>');
  561. cancel.click(function(ev) {
  562. BehaviorManager.handleUserEvent(__EVENT_CANCELED_DIALOG);
  563. });
  564. cancel.html('cancel');
  565. dialog.append(cancel);
  566. }
  567. BehaviorManager.setActiveBehaviourStatechart(__SC_DIALOG);
  568. BehaviorManager.handleUserEvent(__EVENT_SHOW_DIALOG);
  569. };
  570. /*
  571. NOTE:: the sortedButtonNames() function sorts icon definition metamodels and
  572. button models s.t. buttons appear in an order that reflects their
  573. model... to be perfectly clean, this should be absorbed into icon
  574. definition and button model compilers, but since button models aren't
  575. compiled, it was simpler to just let this relatively simple logic live
  576. here
  577. */
  578. /**
  579. * Sets up and shows a toolbar according to the given button model or metamodel.
  580. *
  581. * This method:
  582. * 1. Removes any old instance of the toolbar if it currently exists
  583. * 2. Creates a <div> to hold the buttons
  584. * 3. Creates each button and appends it to the <div>
  585. * 4. Add the <div> to the dock
  586. * 5. Map the toolbar to its data
  587. *
  588. * @param tb - the toolbar to setup and show
  589. * @param data - the data to bind the toolbar to
  590. * @param type - the toolbar type, can be __BUTTON_TOOLBAR or __METAMODEL_TOOLBAR
  591. */
  592. this.setupAndShowToolbar = function(tb,data,type)
  593. {
  594. var imgSrc =
  595. function(name)
  596. {
  597. return (type == __BUTTON_TOOLBAR ?
  598. tb.substring(0,tb.lastIndexOf('/')+1)+name+'.icon.png' :
  599. '/Formalisms/default.icon.png');
  600. },
  601. className =
  602. function()
  603. {return (type == __BUTTON_TOOLBAR ? 'toolbar_bm' : 'toolbar_mm');},
  604. buttons =
  605. (type == __BUTTON_TOOLBAR ? data.asm.nodes : data.types),
  606. sortedButtons =
  607. function()
  608. {
  609. return (type == __BUTTON_TOOLBAR ?
  610. /* sort button names according to their position in their
  611. associated buttons model */
  612. utils.sortDict(data.csm.nodes,
  613. function(b1,b2)
  614. {
  615. var pos1 = b1['position']['value'],
  616. pos2 = b2['position']['value'];
  617. if( (pos1[1] < pos2[1]) ||
  618. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  619. return -1;
  620. return 1;
  621. }) :
  622. utils.sortDict(data.types,
  623. /* sort type names according to their IconIcon's position in
  624. the associated icon definition model */
  625. function(b1,b2)
  626. {
  627. var pos1 = undefined,
  628. pos2 = undefined;
  629. b1.some( function(attr)
  630. {
  631. if(attr['name'] == 'position')
  632. pos1 = attr['default'];
  633. return pos1;
  634. });
  635. b2.some( function(attr)
  636. {
  637. if(attr['name'] == 'position')
  638. pos2 = attr['default'];
  639. return pos2;
  640. });
  641. if( (pos1[1] < pos2[1]) ||
  642. (pos1[1] == pos2[1] && pos1[0] < pos2[0]) )
  643. return -1;
  644. return 1;
  645. }) );
  646. },
  647. createButton =
  648. function(name,tooltip,code)
  649. {
  650. var div = $('<div>'),
  651. img = $('<img>');
  652. div.addClass( 'toolbar_button' );
  653. div.attr("id", tb+'/'+name);
  654. div.attr("title", tooltip);
  655. div.click( function(ev){
  656. var res = HttpUtils.safeEval(code);
  657. if( res['$uerr'] )
  658. WindowManagement.openDialog(
  659. _ERROR,
  660. 'unexpected error in button code ::\n '+res['$uerr']);
  661. else if( res['$err'] )
  662. WindowManagement.openDialog(
  663. _ERROR,
  664. 'error in button code ::\n '+res['$err']);
  665. });
  666. // var url = HttpUtils.url(imgSrc(name),__NO_WID);
  667. img.attr("src", HttpUtils.url(imgSrc(name),__NO_WID));
  668. div.append(img);
  669. return div;
  670. };
  671. GUIUtils.removeToolbar(tb);
  672. var tb_div = $('<div>');
  673. tb_div.attr("id", 'div_toolbar_'+tb);
  674. tb_div.attr("class", className()+' toolbar unselectable' );
  675. tb_div.attr("title", tb);
  676. sortedButtons().forEach(
  677. function(b)
  678. {
  679. if( type == __METAMODEL_TOOLBAR && b.match(/(.*)Link$/) )
  680. return;
  681. var spc1 = $('<span>'),
  682. spc2 = $('<span>');
  683. // spc1.className = spc2.className = 'toolbar_space';
  684. spc1.attr("class", "toolbar_space" );
  685. spc2.attr("class", "toolbar_space" );
  686. tb_div.append(spc1);
  687. if( type == __BUTTON_TOOLBAR )
  688. tb_div.append(
  689. createButton(
  690. buttons[b]['name']['value'],
  691. buttons[b]['tooltip']['value'],
  692. buttons[b]['code']['value']) );
  693. else if( (matches = b.match(/(.*)Icon/)) )
  694. tb_div.append(
  695. createButton(
  696. b,
  697. 'create instance(s) of '+b.match(/(.*)Icon/)[1],
  698. '_setTypeToCreate("'+
  699. tb.substring(0,tb.length-'.metamodel'.length)+
  700. '/'+b+'");') );
  701. tb_div.append(spc2);
  702. } );
  703. if( tb_div.children().length == 0 )
  704. tb_div.append( GUIUtils.getTextSpan(tb,'toolbar_alt') );
  705. $('#div_dock').append(tb_div);
  706. __loadedToolbars[tb] = data;
  707. };
  708. return this;
  709. }();