gui.mjs

  1. import { decodeUrl, settings, constants, gStyle, internals, browser,
  2. findFunction, parse, isFunc, isStr, isObject, isBatchMode, setBatchMode } from './core.mjs';
  3. import { select as d3_select } from './d3.mjs';
  4. import { HierarchyPainter } from './gui/HierarchyPainter.mjs';
  5. import { setStoragePrefix, readSettings, readStyle } from './gui/utils.mjs';
  6. import { setDefaultDrawOpt } from './draw.mjs';
  7. import { createMenu, closeMenu } from './gui/menu.mjs';
  8. /** @summary Read style and settings from URL
  9. * @private */
  10. function readStyleFromURL(url) {
  11. // first try to read settings from local storage
  12. const d = decodeUrl(url),
  13. prefix = d.get('storage_prefix');
  14. if (isStr(prefix) && prefix)
  15. setStoragePrefix(prefix);
  16. if (readSettings())
  17. setDefaultDrawOpt(settings._dflt_drawopt);
  18. readStyle();
  19. function get_bool(name, field, special) {
  20. if (d.has(name)) {
  21. const val = d.get(name);
  22. if (special && (val === special))
  23. settings[field] = special;
  24. else
  25. settings[field] = (val !== '0') && (val !== 'false') && (val !== 'off');
  26. }
  27. }
  28. if (d.has('optimize')) {
  29. settings.OptimizeDraw = 2;
  30. let optimize = d.get('optimize');
  31. if (optimize) {
  32. optimize = parseInt(optimize);
  33. if (Number.isInteger(optimize))
  34. settings.OptimizeDraw = optimize;
  35. }
  36. }
  37. if (d.has('scale')) {
  38. const s = parseInt(d.get('scale'));
  39. settings.CanvasScale = Number.isInteger(s) ? s : 2;
  40. }
  41. const b = d.get('batch');
  42. if (b !== undefined) {
  43. setBatchMode(d !== 'off');
  44. if (b === 'png')
  45. internals.batch_png = true;
  46. }
  47. get_bool('lastcycle', 'OnlyLastCycle');
  48. get_bool('usestamp', 'UseStamp');
  49. get_bool('dark', 'DarkMode');
  50. get_bool('approx_text_size', 'ApproxTextSize');
  51. let mr = d.get('maxranges');
  52. if (mr) {
  53. mr = parseInt(mr);
  54. if (Number.isInteger(mr)) settings.MaxRanges = mr;
  55. }
  56. if (d.has('wrong_http_response'))
  57. settings.HandleWrongHttpResponse = true;
  58. if (d.has('prefer_saved_points'))
  59. settings.PreferSavedPoints = true;
  60. const tf1_style = d.get('tf1');
  61. if (tf1_style === 'curve')
  62. settings.FuncAsCurve = true;
  63. else if (tf1_style === 'line')
  64. settings.FuncAsCurve = false;
  65. if (d.has('with_credentials'))
  66. settings.WithCredentials = true;
  67. let inter = d.get('interactive');
  68. if (inter === 'nomenu')
  69. settings.ContextMenu = false;
  70. else if (inter !== undefined) {
  71. if (!inter || (inter === '1'))
  72. inter = '111111';
  73. else if (inter === '0')
  74. inter = '000000';
  75. if (inter.length === 6) {
  76. switch (inter[0]) {
  77. case '0': settings.ToolBar = false; break;
  78. case '1': settings.ToolBar = 'popup'; break;
  79. case '2': settings.ToolBar = true; break;
  80. }
  81. inter = inter.slice(1);
  82. }
  83. if (inter.length === 5) {
  84. settings.Tooltip = parseInt(inter[0]);
  85. settings.ContextMenu = (inter[1] !== '0');
  86. settings.Zooming = (inter[2] !== '0');
  87. settings.MoveResize = (inter[3] !== '0');
  88. settings.DragAndDrop = (inter[4] !== '0');
  89. }
  90. }
  91. get_bool('tooltip', 'Tooltip');
  92. const mathjax = d.get('mathjax', null);
  93. let latex = d.get('latex', null);
  94. if ((mathjax !== null) && (mathjax !== '0') && (latex === null))
  95. latex = 'math';
  96. if (latex !== null)
  97. settings.Latex = constants.Latex.fromString(latex);
  98. if (d.has('nomenu')) settings.ContextMenu = false;
  99. if (d.has('noprogress'))
  100. settings.ProgressBox = false;
  101. else
  102. get_bool('progress', 'ProgressBox', 'modal');
  103. if (d.has('notouch')) browser.touches = false;
  104. if (d.has('adjframe')) settings.CanAdjustFrame = true;
  105. const has_toolbar = d.has('toolbar');
  106. if (has_toolbar) {
  107. const toolbar = d.get('toolbar', '');
  108. let val = null;
  109. if (toolbar.indexOf('popup') >= 0) val = 'popup';
  110. if (toolbar.indexOf('left') >= 0) { settings.ToolBarSide = 'left'; val = 'popup'; }
  111. if (toolbar.indexOf('right') >= 0) { settings.ToolBarSide = 'right'; val = 'popup'; }
  112. if (toolbar.indexOf('vert') >= 0) { settings.ToolBarVert = true; val = 'popup'; }
  113. if (toolbar.indexOf('show') >= 0) val = true;
  114. settings.ToolBar = val || ((toolbar.indexOf('0') < 0) && (toolbar.indexOf('false') < 0) && (toolbar.indexOf('off') < 0));
  115. }
  116. get_bool('skipsi', 'SkipStreamerInfos');
  117. get_bool('skipstreamerinfos', 'SkipStreamerInfos');
  118. if (d.has('nodraggraphs'))
  119. settings.DragGraphs = false;
  120. if (d.has('palette')) {
  121. const palette = parseInt(d.get('palette'));
  122. if (Number.isInteger(palette) && (palette > 0) && (palette < 113)) settings.Palette = palette;
  123. }
  124. const render3d = d.get('render3d'), embed3d = d.get('embed3d'), geosegm = d.get('geosegm');
  125. if (render3d) settings.Render3D = constants.Render3D.fromString(render3d);
  126. if (embed3d) settings.Embed3D = constants.Embed3D.fromString(embed3d);
  127. if (geosegm) settings.GeoGradPerSegm = Math.max(2, parseInt(geosegm));
  128. get_bool('geocomp', 'GeoCompressComp');
  129. if (d.has('hlimit')) settings.HierarchyLimit = parseInt(d.get('hlimit'));
  130. function get_int_style(name, field, dflt) {
  131. if (!d.has(name)) return;
  132. const val = d.get(name);
  133. if (!val || (val === 'true') || (val === 'on'))
  134. gStyle[field] = dflt;
  135. else if ((val === 'false') || (val === 'off'))
  136. gStyle[field] = 0;
  137. else
  138. gStyle[field] = parseInt(val);
  139. return gStyle[field];
  140. }
  141. function get_float_style(name, field) {
  142. if (!d.has(name)) return;
  143. const val = d.get(name),
  144. flt = Number.parseFloat(val);
  145. if (Number.isFinite(flt))
  146. gStyle[field] = flt;
  147. }
  148. if (d.has('histzero')) gStyle.fHistMinimumZero = true;
  149. if (d.has('histmargin')) gStyle.fHistTopMargin = parseFloat(d.get('histmargin'));
  150. get_int_style('optstat', 'fOptStat', 1111);
  151. get_int_style('optfit', 'fOptFit', 0);
  152. const has_date = get_int_style('optdate', 'fOptDate', 1),
  153. has_file = get_int_style('optfile', 'fOptFile', 1);
  154. if ((has_date || has_file) && !has_toolbar)
  155. settings.ToolBarVert = true;
  156. get_float_style('datex', 'fDateX');
  157. get_float_style('datey', 'fDateY');
  158. get_int_style('opttitle', 'fOptTitle', 1);
  159. if (d.has('utc'))
  160. settings.TimeZone = 'UTC';
  161. if (d.has('cet'))
  162. settings.TimeZone = 'Europe/Berlin';
  163. else if (d.has('timezone')) {
  164. settings.TimeZone = d.get('timezone');
  165. if ((settings.TimeZone === 'default') || (settings.TimeZone === 'dflt'))
  166. settings.TimeZone = 'Europe/Berlin';
  167. else if (settings.TimeZone === 'local')
  168. settings.TimeZone = '';
  169. }
  170. gStyle.fStatFormat = d.get('statfmt', gStyle.fStatFormat);
  171. gStyle.fFitFormat = d.get('fitfmt', gStyle.fFitFormat);
  172. }
  173. /** @summary Build main GUI
  174. * @desc Used in many HTML files to create JSROOT GUI elements
  175. * @param {String} gui_element - id of the `<div>` element
  176. * @param {String} gui_kind - either 'online', 'nobrowser', 'draw'
  177. * @return {Promise} with {@link HierarchyPainter} instance
  178. * @example
  179. * import { buildGUI } from 'https://root.cern/js/latest/modules/gui.mjs';
  180. * buildGUI('guiDiv'); */
  181. async function buildGUI(gui_element, gui_kind = '') {
  182. const myDiv = d3_select(isStr(gui_element) ? `#${gui_element}` : gui_element);
  183. if (myDiv.empty())
  184. return Promise.reject(Error('no div for gui found'));
  185. myDiv.html(''); // clear element
  186. const d = decodeUrl(), getSize = name => {
  187. const res = d.has(name) ? d.get(name).split('x') : [];
  188. if (res.length !== 2)
  189. return null;
  190. res[0] = parseInt(res[0]);
  191. res[1] = parseInt(res[1]);
  192. return res[0] > 0 && res[1] > 0 ? res : null;
  193. };
  194. let online = (gui_kind === 'online'), nobrowser = false, drawing = false;
  195. if (gui_kind === 'draw')
  196. online = drawing = nobrowser = true;
  197. else if ((gui_kind === 'nobrowser') || d.has('nobrowser') || (myDiv.attr('nobrowser') && myDiv.attr('nobrowser') !== 'false'))
  198. nobrowser = true;
  199. if (myDiv.attr('ignoreurl') === 'true')
  200. settings.IgnoreUrlOptions = true;
  201. readStyleFromURL();
  202. if (isBatchMode())
  203. nobrowser = true;
  204. const divsize = getSize('divsize'), canvsize = getSize('canvsize'), smallpad = getSize('smallpad');
  205. if (divsize)
  206. myDiv.style('position', 'relative').style('width', divsize[0] + 'px').style('height', divsize[1] + 'px');
  207. else if (!isBatchMode()) {
  208. d3_select('html').style('height', '100%');
  209. d3_select('body').style('min-height', '100%').style('margin', 0).style('overflow', 'hidden');
  210. myDiv.style('position', 'absolute').style('left', 0).style('top', 0).style('bottom', 0).style('right', 0).style('padding', '1px');
  211. }
  212. if (canvsize) {
  213. settings.CanvasWidth = canvsize[0];
  214. settings.CanvasHeight = canvsize[1];
  215. }
  216. if (smallpad) {
  217. settings.SmallPad.width = smallpad[0];
  218. settings.SmallPad.height = smallpad[1];
  219. }
  220. const hpainter = new HierarchyPainter('root', null);
  221. if (online) hpainter.is_online = drawing ? 'draw' : 'online';
  222. if (drawing || isBatchMode())
  223. hpainter.exclude_browser = true;
  224. hpainter.start_without_browser = nobrowser;
  225. return hpainter.startGUI(myDiv).then(() => {
  226. if (!nobrowser)
  227. return hpainter.initializeBrowser();
  228. if (!drawing)
  229. return;
  230. const func = internals.getCachedObject || findFunction('GetCachedObject'),
  231. obj = isFunc(func) ? parse(func()) : undefined;
  232. if (isObject(obj))
  233. hpainter.setCachedObject(obj);
  234. let opt = d.get('opt', '');
  235. if (d.has('websocket'))
  236. opt += ';websocket';
  237. return hpainter.display('', opt);
  238. }).then(() => hpainter);
  239. }
  240. export { buildGUI, internals, readStyleFromURL, HierarchyPainter, createMenu, closeMenu };