modalconsole.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. let modalConsole = function (config) {
  2. config = config || {};
  3. modalConsole.superclass.constructor.call(this, config);
  4. };
  5. Ext.extend(modalConsole, Ext.Component, {
  6. window: null,
  7. dialog: {},
  8. combo: {},
  9. label: null,
  10. config: {},
  11. keys: [],
  12. current: 0,
  13. result: '',
  14. fileName: '',
  15. init: false,
  16. toggle: function(){
  17. if (!this.window) {
  18. this.window = MODx.load({
  19. xtype: 'modalconsole-window',
  20. url: this.config.connectorUrl
  21. });
  22. if (this.window.getHeight() < 200) {
  23. this.window.setHeight(document.body.clientHeight-55);
  24. }
  25. this.window.el.setRight(-this.window.getWidth()).setTop(55).setVisible(true, false);
  26. }
  27. if (!this.window.isVisible) {
  28. this.window.isVisible = true;
  29. this.window.el.addClass('visible').setOpacity(1, {duration: .7, easing: 'easeIn'})
  30. // this.window.editor.getEl().focus(1000);
  31. } else {
  32. this.window.isVisible = false;
  33. this.window.el.removeClass('visible').setOpacity(0.5, {duration: .7, easing: 'easeIn'}).setRight(-this.window.getWidth());
  34. }
  35. },
  36. });
  37. modalConsole = new modalConsole();
  38. /** ************************************************ **/
  39. let modalConsoleWindow = function (config) {
  40. config = config || {};
  41. Ext.applyIf(config, {
  42. renderTo: 'modx-body-tag',
  43. // renderTo: 'modx-content',
  44. title: _('modalconsole'),
  45. id: 'modalconsole-window',
  46. width: 950,
  47. minHeight: 200,
  48. minWidth: 300,
  49. autoScroll: false,
  50. // closeAction: 'hide',
  51. hideMode: 'display',
  52. isVisible: false,
  53. tools: [],
  54. tbar: [{
  55. xtype: 'button',
  56. id: 'modalconsole-open',
  57. cls: 'toolbar-btn',
  58. text: '<i class="icon icon-folder-open-o"></i>',
  59. tooltipType: 'title',
  60. tooltip: _('modalconsole_btn_open'),
  61. disabled: false,
  62. handler: function () {
  63. this.openFile();
  64. },
  65. scope: this
  66. }, {
  67. xtype: 'button',
  68. cls: 'toolbar-btn',
  69. id: 'modalconsole-save',
  70. text: '<i class="icon icon-save"></i>',
  71. tooltipType: 'title',
  72. tooltip: _('modalconsole_btn_save'),
  73. disabled: false,
  74. handler: function () {
  75. this.saveFile();
  76. },
  77. scope: this
  78. }, {
  79. xtype: 'button',
  80. cls: 'toolbar-btn',
  81. id: 'modalconsole-clear',
  82. text: '<i class="icon icon-eraser"></i>',
  83. tooltipType: 'title',
  84. tooltip: _('modalconsole_btn_clear'),
  85. handler: function () {
  86. this.clearContent();
  87. },
  88. scope: this
  89. }, {
  90. xtype: 'tbspacer',
  91. width: 10
  92. }, {
  93. xtype: 'button',
  94. cls: 'toolbar-btn',
  95. id: 'modalconsole-collapse',
  96. text: '<i class="icon icon-columns"></i>',
  97. tooltipType: 'title',
  98. tooltip: _('modalconsole_btn_collapse'),
  99. handler: function() {this.collapsePanel();},
  100. scope: this
  101. }, {
  102. xtype: 'tbspacer',
  103. width: 10
  104. }, {
  105. xtype: 'button',
  106. cls: 'toolbar-btn',
  107. id: 'modalconsole-history-prev',
  108. text: '<i class="icon icon-arrow-left"></i>',
  109. tooltipType: 'title',
  110. tooltip: _('modalconsole_btn_history_prev'),
  111. handler: function() {this.historyPrev();},
  112. disabled: true,
  113. scope: this
  114. }, {
  115. xtype: 'button',
  116. cls: 'toolbar-btn',
  117. id: 'modalconsole-history-next',
  118. text: '<i class="icon icon-arrow-right"></i>',
  119. tooltipType: 'title',
  120. tooltip: _('modalconsole_btn_history_next'),
  121. disabled: true,
  122. handler: function() {this.historyNext();},
  123. scope: this
  124. }, {
  125. xtype: 'button',
  126. cls: 'toolbar-btn',
  127. id: 'modalconsole-history-clear',
  128. text: '<i class="icon icon-trash"></i>',
  129. tooltipType: 'title',
  130. tooltip: _('modalconsole_btn_history_clear'),
  131. disabled: !!modalConsole.keys.length,
  132. handler: function() {this.clearHistory();},
  133. scope: this
  134. }, {
  135. xtype: 'xcheckbox',
  136. boxLabel: _('modalconsole_save_code'),
  137. // boxLabel: 'Сохранить код',
  138. cls: 'toolbar-checkbox',
  139. id: 'modalconsole-save-code',
  140. disabled: !modalConsole.config.limit,
  141. checked: this.initSaveCodeState(),
  142. listeners: {
  143. check: function(o, value) {
  144. this.setSaveCodeState(value);
  145. },
  146. scope: this
  147. }
  148. }, '->', {
  149. xtype: 'xcheckbox',
  150. boxLabel: _('modalconsole_format_code'),
  151. cls: 'toolbar-checkbox',
  152. id: 'modalconsole-format-code',
  153. checked: this.initFormatCodeState(),
  154. listeners: {
  155. check: function(o, value) {
  156. this.resultPanel.update(this.formatCode(modalConsole.result));
  157. this.setFormatCodeState(value);
  158. },
  159. scope: this
  160. }
  161. }],
  162. items: [{
  163. xtype: 'panel',
  164. // hideLabel: true,
  165. id: 'modalconsole-console',
  166. height: 400,
  167. autoWidth: true,
  168. // autoHeight: true,
  169. layout: 'border',
  170. items: [{
  171. region: 'center',
  172. id: 'modalconsole-code',
  173. // autoScroll: true,
  174. // unstyled: true,
  175. bodyStyle: 'background-color:#fff;',
  176. items: [{
  177. xtype: Ext.ComponentMgr.types['modx-texteditor'] ? 'modx-texteditor' : 'textarea',
  178. // xtype: 'textarea',
  179. editor: null,
  180. id: 'modalconsole-editor',
  181. mimeType: 'application/x-php',
  182. height: '100%',
  183. value: this.getHistory(),
  184. enableKeyEvents: true,
  185. listeners: {
  186. keydown: function(editor, e) {
  187. if (e.ctrlKey && Ext.EventObject.getKey() == Ext.EventObject.ENTER) {
  188. this.execute();
  189. } else if (Ext.EventObject.getKey() == Ext.EventObject.ESC) {
  190. modalConsole.toggle();
  191. }
  192. },
  193. scope: this
  194. }
  195. }]
  196. //width: 400
  197. }, {
  198. // title: '',
  199. region: 'east',
  200. id: 'modalconsole-result',
  201. width: 100,
  202. split: true,
  203. collapsible: false,
  204. minSize: 5,
  205. maxSize: 0,
  206. bodyStyle: 'background-color:#fafafa;',
  207. listeners:{
  208. "render": {
  209. fn: function(el){
  210. el.getUpdater().on('update',function(result, response) {
  211. try {
  212. var rObject = JSON.parse(response.responseText);
  213. } catch (Error) {
  214. el.update(response.responseText);
  215. console.warn(Error.message);
  216. return;
  217. }
  218. if (rObject.success) {
  219. modalConsole.result = rObject.output;
  220. const output = this.formatCode(modalConsole.result);
  221. el.update(output);
  222. } else {
  223. MODx.msg.alert(_('error'), rObject.message, Ext.emptyFn);
  224. }
  225. // Если код новый
  226. if (rObject.keys > modalConsole.keys) {
  227. Ext.getCmp('modalconsole-history-next').disable();
  228. modalConsole.keys = rObject.keys;
  229. modalConsole.current = rObject.keys.length - 1;
  230. (rObject.keys.length > 0) ? Ext.getCmp('modalconsole-history-clear').enable() : Ext.getCmp('modalconsole-history-clear').disable();
  231. (rObject.keys.length > 1) ? Ext.getCmp('modalconsole-history-prev').enable() : Ext.getCmp('modalconsole-history-prev').disable();
  232. }
  233. this.parseProfile(rObject);
  234. if (el.collapsed) el.toggleCollapse();
  235. }, this);
  236. }
  237. }
  238. ,scope: this
  239. }
  240. }],
  241. listeners:{
  242. "beforerender": {
  243. fn: function(el){
  244. //el.setHeight(this.getHeight()-113);
  245. }
  246. }
  247. ,scope: this
  248. }
  249. // height: 550,
  250. }],
  251. buttonAlign: "left",
  252. buttons: [{
  253. xtype: 'modalConsole-profile-label',
  254. id: 'modalconsole-result-queries',
  255. tagTitle: 'SQL queries',
  256. profileName: 'Queries',
  257. initValue: '0'
  258. }, {
  259. xtype: 'modalConsole-profile-label',
  260. id: 'modalconsole-result-time',
  261. tagTitle: 'SQL time / PHP time / Total time',
  262. profileName: 'Time',
  263. initValue: '0 s / 0 s / 0 s'
  264. }, {
  265. xtype: 'modalConsole-profile-label',
  266. id: 'modalconsole-result-memory',
  267. tagTitle: 'Current memory / Memory peak',
  268. profileName: 'Memory',
  269. initValue: '0 MB / 0 MB'
  270. }, '->' , {
  271. text: _("modalconsole_close") ? _("modalconsole_close") : 'Close',
  272. cls: 'modalconsole-window-btn',
  273. handler: function () {modalConsole.toggle();},
  274. scope: this
  275. }, {
  276. text: _("modalconsole_btn_execute") ? _("modalconsole_btn_execute") : 'Execute',
  277. cls: 'modalconsole-window-btn modalconsole-exec-btn',
  278. handler: function () {this.execute();},
  279. tooltipType: 'title',
  280. tooltip: '<ctrl>+<Enter>',
  281. scope: this
  282. }],
  283. keys: [{
  284. key: Ext.EventObject.ESC,
  285. shift: false,
  286. fn: function () {
  287. modalConsole.toggle();
  288. },
  289. scope: this
  290. }],
  291. // onEsc: modalConsole.toggle,
  292. listeners: {
  293. 'maximize': function (w) {
  294. w.el.setTop(55);
  295. },
  296. 'hide': function () {
  297. modalConsole.toggle();
  298. },
  299. 'render': function(w) {
  300. let el = w.el.select('.x-tool-close').first();
  301. el.on('click', function (e) {
  302. modalConsole.toggle();
  303. });
  304. },
  305. 'beforerender': function(w) {
  306. this.codePanel = Ext.getCmp('modalconsole-code');
  307. this.editor = Ext.getCmp('modalconsole-editor');
  308. this.resultPanel = Ext.getCmp('modalconsole-result');
  309. },
  310. 'resize': function(o,w,h) {
  311. let p = Ext.getCmp('modalconsole-console');
  312. if (p) {
  313. p.setHeight(h-113);
  314. if (this.editor && this.editor.editor) this.editor.editor.resize();
  315. }
  316. }
  317. }
  318. });
  319. modalConsoleWindow.superclass.constructor.call(this, config);
  320. };
  321. Ext.extend(modalConsoleWindow, MODx.Window, {
  322. init: function(response) {
  323. switch (response.keys.length) {
  324. case 0:
  325. Ext.getCmp('modalconsole-history-clear').disable();
  326. case 1:
  327. Ext.getCmp('modalconsole-history-prev').disable();
  328. break;
  329. default:
  330. Ext.getCmp('modalconsole-history-prev').enable();
  331. modalConsole.keys = response.keys;
  332. modalConsole.current = response.keys.length - 1;
  333. }
  334. /*if (response.keys.length <= 1) {
  335. Ext.getCmp('modalconsole-history-prev').disable();
  336. } else {
  337. Ext.getCmp('modalconsole-history-prev').enable();
  338. modalConsole.keys = response.keys;
  339. modalConsole.current = response.keys.length - 1;
  340. }*/
  341. modalConsole.init = true;
  342. },
  343. getHistory: function(key) {
  344. MODx.Ajax.request({
  345. url: modalConsole.config.connectorUrl,
  346. params: {
  347. action: 'gethistory',
  348. key: key = key || ''
  349. },
  350. listeners: {
  351. success: {
  352. fn: function(response) {
  353. if (response.success) {
  354. if (!modalConsole.init) this.init(response);
  355. this.editor.setValue(response.code);
  356. } else {
  357. this.resultPanel.update(response.message);
  358. }
  359. },
  360. scope: this
  361. },
  362. failure: function(response) {console.log(response);}
  363. }
  364. });
  365. return "<?php\n"
  366. },
  367. collapsePanel: function(){
  368. this.resultPanel.toggleCollapse();
  369. },
  370. clearContent: function(){
  371. this.editor.setValue('<?php\n');
  372. this.resultPanel.update('');
  373. modalConsole.result = '';
  374. },
  375. historyPrev: function(){
  376. Ext.getCmp('modalconsole-history-next').enable();
  377. modalConsole.current--;
  378. if (modalConsole.current == 0) {
  379. Ext.getCmp('modalconsole-history-prev').disable();
  380. }
  381. this.getHistory(modalConsole.keys[modalConsole.current]);
  382. },
  383. historyNext: function(){
  384. Ext.getCmp('modalconsole-history-prev').enable();
  385. modalConsole.current++;
  386. if (modalConsole.current >= modalConsole.keys.length - 1) {
  387. Ext.getCmp('modalconsole-history-next').disable();
  388. }
  389. this.getHistory(modalConsole.keys[modalConsole.current]);
  390. },
  391. clearHistory: function(){
  392. MODx.Ajax.request({
  393. url: modalConsole.config.connectorUrl,
  394. params: {
  395. action: 'clearhistory'
  396. },
  397. listeners: {
  398. success: {
  399. fn: function(response) {
  400. if (response.success) {
  401. Ext.getCmp('modalconsole-history-prev').disable();
  402. Ext.getCmp('modalconsole-history-next').disable();
  403. Ext.getCmp('modalconsole-history-clear').disable();
  404. modalConsole.keys = [];
  405. modalConsole.current = 0;
  406. // this.editor.setValue("<?php\n");
  407. } else {
  408. MODx.msg.alert(_('error'), response.message, Ext.emptyFn);
  409. // this.resultPanel.update(response.message);
  410. }
  411. },
  412. scope: this
  413. },
  414. failure: {
  415. fn: function (response) {
  416. MODx.msg.alert(_('error'), response.message, Ext.emptyFn);
  417. }
  418. }
  419. }
  420. });
  421. },
  422. getCode: function() {
  423. return this.editor.getValue().trim();
  424. },
  425. execute: function() {
  426. //this.resultPanel.el.mask(_('working'));
  427. const code = this.getCode();
  428. if (code) {
  429. let updater = this.resultPanel.getUpdater();
  430. updater.timeout = 0;
  431. updater.update({
  432. url: modalConsole.config.connectorUrl,
  433. params:{
  434. action: 'exec',
  435. code: code,
  436. save: +this.getSaveCodeState()
  437. }
  438. });
  439. }
  440. },
  441. parseProfile: function (result) {
  442. for (let key in result.profile) {
  443. let pItem = Ext.getCmp('modalconsole-result-' + key);
  444. pItem.parseData(result.profile[key]).update(pItem.html);
  445. }
  446. },
  447. initSaveCodeState: function() {
  448. let state = Ext.util.Cookies.get('modalconsoleSaveCode');
  449. if (state === null) {
  450. // state = !!modalConsole.config.limit;
  451. // Ext.util.Cookies.set('modalconsoleSaveCode', +state);
  452. state = this.setSaveCodeState(!!modalConsole.config.limit);
  453. }
  454. return +state;
  455. },
  456. getSaveCodeState: function() {
  457. return Ext.getCmp('modalconsole-save-code').checked;
  458. },
  459. setSaveCodeState: function(state) {
  460. Ext.util.Cookies.set('modalconsoleSaveCode', +state);
  461. return state;
  462. },
  463. initFormatCodeState: function() {
  464. let state = Ext.util.Cookies.get('modalconsoleFormatCode');
  465. if (state === null) {
  466. state = this.setFormatCodeState(true);
  467. }
  468. return +state;
  469. },
  470. getFormatCodeState: function() {
  471. return Ext.getCmp('modalconsole-format-code').checked;
  472. },
  473. setFormatCodeState: function(state) {
  474. Ext.util.Cookies.set('modalconsoleFormatCode', +state);
  475. return state;
  476. },
  477. formatCode: function (output) {
  478. return this.getFormatCodeState() ? '<pre>' + output + '</pre>' : output;
  479. },
  480. openFile: function() {
  481. if (this.dialog) this.dialog.destroy();
  482. this.dialog = MODx.load({
  483. xtype: 'modalconsole-openfile-dialog',
  484. // id: Ext.id(),
  485. listeners: {
  486. success: {
  487. fn: function (response) {
  488. if (response.a.result.success) {
  489. const code = response.a.result.message ? response.a.result.message : '<?php\n';
  490. this.editor.setValue(code);
  491. }
  492. }, scope: this
  493. },
  494. failure: {
  495. fn: function(r){}, scope: this
  496. }
  497. }
  498. });
  499. this.dialog.show(Ext.EventObject.target);
  500. },
  501. saveFile: function() {
  502. Ext.MessageBox.prompt(_('save'), _('modalconsole_enter_filename'), function(res, input) {
  503. if (res == 'ok') {
  504. MODx.Ajax.request({
  505. url: modalConsole.config.connectorUrl,
  506. params: {
  507. action: 'savefile',
  508. code: this.getCode(),
  509. filename: input
  510. },
  511. listeners: {
  512. success: {
  513. fn: function (response) {
  514. if (response.success) {
  515. // alert('OK');
  516. console.log(response);
  517. modalConsole.fileName = response.filename;
  518. } else {
  519. MODx.msg.alert(_('error'), response.message, Ext.emptyFn);
  520. }
  521. },
  522. scope: this
  523. },
  524. failure: {
  525. fn: function (response) {
  526. MODx.msg.alert(_('error'), response.message, Ext.emptyFn);
  527. }
  528. }
  529. }
  530. });
  531. }
  532. }, this, false, modalConsole.fileName || '');
  533. }
  534. });
  535. Ext.reg('modalconsole-window', modalConsoleWindow);
  536. /** ******************************************************* **/
  537. modalConsole.label = function(config) {
  538. config = config || {};
  539. Ext.applyIf(config,{
  540. tag: 'span',
  541. tagCls: 'profile-item',
  542. listeners:{
  543. "beforerender": {
  544. fn: function(el){
  545. el.parseData()
  546. }
  547. }
  548. ,scope: this
  549. }
  550. });
  551. modalConsole.label.superclass.constructor.call(this,config);
  552. };
  553. Ext.extend(modalConsole.label, Ext.form.Label, {
  554. parseData: function (value) {
  555. value = value || this.initValue;
  556. this.html = '{profileName}: <{tag} class="{class}" title="{title}">{value}</{tag}>'
  557. .replace('{profileName}', this.profileName)
  558. .replace(/{tag}/g, this.tag)
  559. .replace('{class}', this.tagCls)
  560. .replace('{title}', this.tagTitle)
  561. .replace('{value}', value);
  562. return this;
  563. }
  564. });
  565. Ext.reg('modalConsole-profile-label', modalConsole.label);
  566. // Open File Dialog
  567. modalConsole.dialog.OpenFile = function (config) {
  568. config = config || {};
  569. if (!config.id) {
  570. config.id = 'modalconsole-openfile-dialog';
  571. }
  572. Ext.applyIf(config, {
  573. title: _('modalconsole_file'),
  574. width: 400,
  575. modal: true,
  576. url: modalConsole.config.connectorUrl,
  577. action: 'loadfile',
  578. fields: [{
  579. xtype: 'modalconsole-combo-files',
  580. name: 'file',
  581. emptyText: _('modalconsole_select_file'),
  582. anchor: '100%'
  583. }],
  584. keys: [{
  585. key: Ext.EventObject.ENTER, shift: true, fn: function () {
  586. let fileName = Ext.getCmp('modalconsole-combo-files').getValue();
  587. modalConsole.fileName = fileName.replace(/\.php$/i, '');
  588. this.submit()
  589. }, scope: this
  590. }],
  591. buttons: [{
  592. text: _('modalconsole_btn_open'),
  593. id: config.id + '-load-btn',
  594. handler: function () {
  595. let fileName = Ext.getCmp('modalconsole-combo-files').getValue();
  596. modalConsole.fileName = fileName.replace(/\.php$/i, '');
  597. this.submit();
  598. },
  599. scope: this
  600. }, {
  601. text: _('close'),
  602. id: config.id + '-close-btn',
  603. handler: function () {
  604. this.hide();
  605. },
  606. scope: this
  607. }]
  608. });
  609. modalConsole.dialog.OpenFile.superclass.constructor.call(this, config);
  610. };
  611. Ext.extend(modalConsole.dialog.OpenFile, MODx.Window);
  612. Ext.reg('modalconsole-openfile-dialog', modalConsole.dialog.OpenFile);
  613. modalConsole.combo.Files = function(config) {
  614. config = config || {};
  615. Ext.applyIf(config,{
  616. id: 'modalconsole-combo-files',
  617. hideMode: 'offsets',
  618. autoScroll: true,
  619. maxHeight: 200,
  620. displayField: 'filename',
  621. valueField: 'filename',
  622. fields: ['filename'],
  623. hiddenName: 'file',
  624. editable: false,
  625. url: modalConsole.config.connectorUrl,
  626. baseParams: {
  627. action: 'getfiles'
  628. },
  629. store: new Ext.data.JsonStore({
  630. url: modalConsole.config.connectorUrl,
  631. root: 'results',
  632. totalProperty: 'total',
  633. fields: ['filename'],
  634. errorReader: MODx.util.JSONReader,
  635. baseParams: {
  636. action: 'getfiles'
  637. },
  638. remoteSort: config.remoteSort || false,
  639. autoDestroy: true,
  640. listeners: {
  641. 'loadexception': {
  642. fn: function(o, trans, resp) {
  643. const status = JSON.parse(resp.responseText);
  644. MODx.msg.alert(_('error'), status.message, Ext.emptyFn);
  645. }}
  646. }
  647. })
  648. });
  649. modalConsole.combo.Files.superclass.constructor.call(this, config);
  650. };
  651. Ext.extend(modalConsole.combo.Files, MODx.combo.ComboBox);
  652. Ext.reg('modalconsole-combo-files', modalConsole.combo.Files);
  653. /** ***************************************************/
  654. Ext.onReady(function() {
  655. let usermenuUl = document.getElementById("modx-user-menu"),
  656. firstLi = usermenuUl.firstChild,
  657. modalconsoleLi = document.createElement("LI"),
  658. title = _('modalconsole_open_console');
  659. modalconsoleLi.id = "modalconsole-li";
  660. modalconsoleLi.innerHTML = "<a id=\"modalconsole-link\" class=\"modalconsole\" href=\"javascript:;\" onclick=\"modalConsole.toggle()\" title=\""+ title +"\"><i class=\"icon icon-terminal\"></i></a>";
  661. usermenuUl.insertBefore(modalconsoleLi, firstLi);
  662. });