modal.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. var DBG = 0;
  2. var DBG1 = 1;
  3. // Multiple modal require global state with list of opened modal:
  4. // - open (example: btn: p5Modal.openModal(event, this))
  5. // - close (ESC key, 'x' btn, click on overlay behind modal, ?cancel btn)
  6. // - only one overlay - under top modal
  7. // - when top modal closed, and has other modal, then show overlay under next top modal
  8. function p5UI__openModal__TORM(event, targetNode, props) {
  9. event.stopPropagation()
  10. event.preventDefault()
  11. var defaultProps = {
  12. source: null, // node id or null
  13. };
  14. var props = props || defaultProps;
  15. if (!targetNode) targetNode = event.target;
  16. // https://dev.to/aleksandrhovhannisyan/multiple-modals-on-one-page-using-html-css-and-javascript-353b#4-closing-a-stacked-modal-with-the-escape-key
  17. // IDEA: global p5Modal manager which generates unique ids and manage close and other actions
  18. p5Modal.openModal(targetNode)
  19. // var name = targetNode.getAttribute('data-name');
  20. // if (!name) throw "Missing 'name' in sidebar panel button";
  21. // var idHtmlNode = 'p5__js-p5-side_panel-' + name;
  22. // var panelNode = document.getElementById(idHtmlNode)
  23. // if (!panelNode) throw "Missing content node";
  24. // // if (panelNode.parentNode !== document.body)
  25. // DBG1 && console.log("DBG:p5UI__openModal__TORM", {
  26. // targetNode,
  27. // name,
  28. // parent: panelNode.parentNode,
  29. // parentDiffBody: (panelNode.parentNode !== document.body),
  30. // });
  31. // if (panelNode.parentNode !== document.body) { // mv at the end of body if not
  32. // document.body.appendChild(panelNode)
  33. // }
  34. // if (!panelNode._p5_onClick) panelNode._p5_onClick = function (event) {
  35. // // DBG1 && console.log("DBG:_p5_onClick", { self: this })
  36. // if (hasClass(event.target, 'p5-side_panel--js-close') || hasId(event.target, idHtmlNode)) {
  37. // event.preventDefault();
  38. // // removeClass(panelNode, 'p5-side_panel--is-visible');
  39. // _closeSidePanel(panelNode); // TODO: check use this, not panelNode
  40. // }
  41. // }
  42. // if (!panelNode._p5_onEsc) panelNode._p5_onEsc = function (event) {
  43. // // DBG1 && console.log("DBG:_p5_onEsc", { self: this })
  44. // if (event.keyCode == 27) {
  45. // _closeSidePanel(panelNode); // TODO: check use this, not panelNode
  46. // }
  47. // }
  48. // // p5-side_panel p5-side_panel--from-right js-p5-side_panel-main p5-side_panel--is-visible
  49. // addClass(panelNode, 'p5-side_panel--from-right'); // TODO: from props: left | right
  50. // if (hasClass(panelNode, 'p5-side_panel--is-visible')) {
  51. // // removeClass(panelNode, 'p5-side_panel--is-visible');
  52. // _closeSidePanel(panelNode);
  53. // } else {
  54. // setTimeout(function () {
  55. // // addClass(panelNode, 'p5-side_panel--is-visible')
  56. // _openSidePanel(panelNode)
  57. // }, 10)
  58. // }
  59. }
  60. // https://dev.to/aleksandrhovhannisyan/multiple-modals-on-one-page-using-html-css-and-javascript-353b#4-closing-a-stacked-modal-with-the-escape-key
  61. // IDEA: global p5Modal manager which generates unique ids and manage close and other actions (refresh page/widget, go to url, etc.)
  62. if (!global.p5Modal) { // TODO: mv to global file
  63. global.p5Modal = (function () {
  64. var _openedModal = []; // [ props, ... ]
  65. var _actionAfterClose = []; // [ '' | 'reload-page' | 'reload-#id' | 'reload-.class' , ... ]
  66. // fire every _actionAfterClose after close last modal
  67. // - if 'reload-page' in _actionAfterClose then only reload page
  68. // fill _actionAfterClose after every modal close
  69. var _escKeyListener = null;
  70. function _addEscKeyListener() {
  71. DBG && console.log('DBG:p5Modal:_addEscKeyListener #1');
  72. if (_escKeyListener) return;
  73. DBG && console.log('DBG:p5Modal:_addEscKeyListener #2');
  74. _escKeyListener = true;
  75. document.addEventListener('keyup', _handleEscKey);
  76. }
  77. function _removeEscKeyListener() {
  78. document.removeEventListener('keyup', _handleEscKey);
  79. _escKeyListener = false;
  80. }
  81. function _handleEscKey(event) {
  82. DBG && console.log('DBG:p5Modal:onkeyup:_handleEscKey', {
  83. keyCode: event.keyCode,
  84. code: event.code,
  85. key: event.key,
  86. _openedModal: _openedModal,
  87. });
  88. // if ("Escape" === event.key)
  89. if (27 === event.keyCode) {
  90. _closeLastOpenedModal();
  91. }
  92. }
  93. function _closeLastOpenedModal() {
  94. if (!_openedModal.length) {
  95. // TODO: remove key listener
  96. return;
  97. }
  98. var lastModalProps = _openedModal.pop();
  99. var lastModalId = _openedModal.length;
  100. var lastModalNode = document.getElementById(_modalHtmlId(lastModalId));
  101. DBG && console.log('DBG:p5Modal:_closeLastOpenedModal', {
  102. _openedModal: _openedModal,
  103. lastModalProps: lastModalProps,
  104. lastModalId: lastModalId,
  105. });
  106. // TODO: check props - is close allowed?
  107. if (lastModalNode) {
  108. lastModalNode.parentNode.removeChild(lastModalNode);
  109. }
  110. if (!_openedModal.length) _removeEscKeyListener();
  111. }
  112. function _px(size) { return '' + size + 'px'; }
  113. var style = {};
  114. // { w: window.innerWidth, h: window.innerHeight }
  115. style.baseModal = {};
  116. style.baseModal.overlay = {
  117. display: 'block',
  118. position: 'fixed',
  119. zIndex: 10,
  120. left: 0,
  121. top: 0,
  122. width: '100%',
  123. height: '100%',
  124. overflow: 'auto',
  125. backgroundColor: '#000',
  126. backgroundColor: '#0009',
  127. };
  128. style.baseModal.content = {
  129. boxSizing: 'border-box',
  130. backgroundColor: '#fefefe',
  131. padding: '24px 32px',
  132. };
  133. style.baseModal.title = {
  134. boxSizing: 'border-box',
  135. backgroundColor: '#fefefe',
  136. margin: '0',
  137. padding: '6px 12px',
  138. borderBottom: '1px solid #ddd',
  139. fontSize: '14px',
  140. };
  141. style.baseModal.body = {
  142. boxSizing: 'border-box',
  143. backgroundColor: '#fff',
  144. margin: '0',
  145. padding: '6px 12px',
  146. fontSize: '14px',
  147. lineHeight: '1.6em',
  148. };
  149. style.baseModal.footer = {
  150. boxSizing: 'border-box',
  151. backgroundColor: '#fefefe',
  152. margin: '0',
  153. padding: '6px 12px',
  154. borderTop: '1px solid #ddd',
  155. fontSize: '14px',
  156. };
  157. var sidePanelWidth = Math.round(window.innerWidth * 0.8); // "80%",
  158. style.side_panel = Object.assign({}, style.baseModal, {
  159. overlay: Object.assign({}, style.baseModal.overlay),
  160. content: Object.assign({}, style.baseModal.content, {
  161. marginLeft: 'auto',
  162. width: _px(sidePanelWidth),
  163. height: _px(window.innerHeight),
  164. border: 'none',
  165. padding: '0',
  166. }),
  167. title: Object.assign({}, style.baseModal.title, {
  168. padding: '0 24px',
  169. height: '40px',
  170. lineHeight: '40px',
  171. }),
  172. body: Object.assign({}, style.baseModal.body, {
  173. height: _px(window.innerHeight - 40 - 40),
  174. overflow: 'auto', // TODO: only Y ?
  175. padding: '12px 24px',
  176. }),
  177. footer: Object.assign({}, style.baseModal.footer, {
  178. width: _px(sidePanelWidth),
  179. padding: '0 24px',
  180. height: '40px',
  181. lineHeight: '40px',
  182. }),
  183. });
  184. style.modal = Object.assign({}, style.baseModal, {
  185. overlay: Object.assign({}, style.baseModal.overlay),
  186. content: Object.assign({}, style.baseModal.content, {
  187. marginTop: _px(Math.round(window.innerHeight * 0.1)),
  188. marginBottom: _px(Math.round(window.innerHeight * 0.1)),
  189. marginLeft: 'auto',
  190. marginRight: 'auto',
  191. width: '80%',
  192. maxWidth: '800px',
  193. border: '1px solid #888',
  194. borderRadius: '8px',
  195. }),
  196. title: Object.assign({}, style.baseModal.title, {
  197. padding: '0 12px 24px 12px',
  198. fontSize: '18px',
  199. lineHeight: '2em',
  200. textAlign: 'center',
  201. border: 'none',
  202. }),
  203. body: Object.assign({}, style.baseModal.body, {
  204. textAlign: 'center',
  205. border: 'none',
  206. }),
  207. footer: Object.assign({}, style.baseModal.footer, {
  208. padding: '24px 12px 0 12px',
  209. textAlign: 'center',
  210. border: 'none',
  211. }),
  212. });
  213. // TODO: if body scroll
  214. style.alert = Object.assign({}, style.baseModal, {
  215. overlay: Object.assign({}, style.baseModal.overlay),
  216. content: Object.assign({}, style.baseModal.content, {
  217. width: '60%',
  218. maxWidth: '800px',
  219. marginTop: _px(Math.round(window.innerHeight * 0.3)),
  220. marginBottom: _px(Math.round(window.innerHeight * 0.3)),
  221. marginLeft: 'auto',
  222. marginRight: 'auto',
  223. padding: '24px',
  224. border: '1px solid #888',
  225. borderRadius: '8px',
  226. }),
  227. title: Object.assign({}, style.baseModal.title, {
  228. padding: '0 12px 24px 12px',
  229. fontSize: '20px',
  230. lineHeight: '2em',
  231. textAlign: 'center',
  232. border: 'none',
  233. }),
  234. body: Object.assign({}, style.baseModal.body, {
  235. textAlign: 'center',
  236. border: 'none',
  237. }),
  238. footer: Object.assign({}, style.baseModal.footer, {
  239. padding: '24px 12px 0 12px',
  240. textAlign: 'center',
  241. border: 'none',
  242. }),
  243. });
  244. style.dropdownUnder = Object.assign({}, style.baseModal, {
  245. overlay: Object.assign({}, style.baseModal.overlay, {
  246. // display: 'block',
  247. position: 'absolute',
  248. // zIndex: 10,
  249. // left: 0,
  250. // top: 0,
  251. // { w: window.innerWidth, h: window.innerHeight }
  252. width: '100%',
  253. height: '100%',
  254. // overflow: 'auto',
  255. // backgroundColor: '#000',
  256. backgroundColor: '#0003', // '#0009',
  257. }),
  258. content: Object.assign({}, style.baseModal.content, {
  259. position: 'absolute',
  260. // top, left: under targetNode (button)
  261. // width: '60%',
  262. // maxWidth: '800px',
  263. padding: '8px 0',
  264. border: '1px solid #ccc',
  265. borderRadius: '8px',
  266. boxShadow: '0 6px 12px rgba(0,0,0,.175)',
  267. }),
  268. title: Object.assign({}, style.baseModal.title, {
  269. display: 'none',
  270. }),
  271. body: Object.assign({}, style.baseModal.body, {
  272. padding: '0 12px',
  273. textAlign: 'left',
  274. border: 'none',
  275. }),
  276. footer: Object.assign({}, style.baseModal.footer, {
  277. display: 'none',
  278. }),
  279. });
  280. function _style(type) {
  281. return (type in style) ? style[type] : style.baseModal;
  282. }
  283. function _getPropsFromAttr(node) {
  284. // 'title' => V::get('title', '', $props),
  285. // 'data-url' => V::get('href', '', $props),
  286. // 'data-cancel' => V::get('cancel', '', $props),
  287. // 'data-cancel-label' => V::get('cancel-label', '', $props),
  288. // 'data-success-reload' => V::get('success-reload', '', $props),
  289. var props = {};
  290. if (node.hasAttribute('data-source')) props.source = node.getAttribute('data-source');
  291. return props;
  292. }
  293. function _openModal(event, targetNode, props) {
  294. event.stopPropagation();
  295. event.preventDefault();
  296. targetNode.blur(); // loose focus from element
  297. var modal = _createModal('modal', targetNode || event.target, props || {});
  298. _setModalContent('modal', modal);
  299. document.body.appendChild(modal.overlay);
  300. modal.content.focus(); // TODO: BUG hit Enter after open modal will open the same modal on top of last
  301. }
  302. function _openSidePanel(event, targetNode, props) {
  303. event.stopPropagation();
  304. event.preventDefault();
  305. DBG && console.log('DBG:p5Modal:_openSidePanel:', { targetNode, props });
  306. targetNode.blur(); // loose focus from element
  307. var modal = _createModal('side_panel', targetNode || event.target, props || {});
  308. _setModalContent('side_panel', modal, props || {}); // require props.body = function (modal)
  309. document.body.appendChild(modal.overlay);
  310. modal.content.focus(); // TODO: BUG hit Enter after open modal will open the same modal on top of last
  311. }
  312. function _openAlert(event, targetNode, props) {
  313. event.stopPropagation();
  314. event.preventDefault();
  315. targetNode.blur(); // loose focus from element
  316. var modal = _createModal('alert', targetNode || event.target, props || {});
  317. _setModalContent('alert', modal);
  318. document.body.appendChild(modal.overlay);
  319. modal.content.focus(); // TODO: BUG hit Enter after open modal will open the same modal on top of last
  320. }
  321. function _openDropdownUnder(event, targetNode, props) {
  322. event.stopPropagation();
  323. event.preventDefault();
  324. var props = ('function' === typeof props) ? props() : props;
  325. var targetNode = targetNode || event.target;
  326. var w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
  327. var h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  328. var rect = targetNode.getBoundingClientRect();
  329. DBG && console.log('DBG:p5Modal:_openDropdownUnder:rect', { rect, w, h });
  330. targetNode.blur();
  331. if (event.target !== targetNode) event.target.blur();
  332. var bodyNode = document.body;
  333. var htmlNode = document.documentElement;
  334. var height = Math.max(bodyNode.scrollHeight, bodyNode.offsetHeight, htmlNode.clientHeight, htmlNode.scrollHeight, htmlNode.offsetHeight);
  335. DBG && console.log('DBG:p5Modal:_openDropdownUnder:rect', {
  336. height,
  337. 'bodyNode.scrollHeight': bodyNode.scrollHeight,
  338. 'bodyNode.offsetHeight': bodyNode.offsetHeight,
  339. 'htmlNode.clientHeight': htmlNode.clientHeight,
  340. 'htmlNode.scrollHeight': htmlNode.scrollHeight,
  341. 'htmlNode.offsetHeight': htmlNode.offsetHeight,
  342. });
  343. var modal = _createModal('dropdownUnder', targetNode, props);
  344. _setModalContent('dropdownUnder', modal, props);
  345. var posY = window.scrollY + rect.y + rect.height + 2;
  346. modal.content.style.top = _px(posY);
  347. modal.content.style.left = _px(rect.x);
  348. modal.content.style.maxHeight = _px(height - posY);
  349. modal.body.style.maxHeight = _px(height - posY - 8 - 8 - 20);
  350. modal.body.style.overflowY = 'auto';
  351. DBG && console.log('DBG:p5Modal:_openDropdownUnder:rect', { outerWidth: window.outerWidth, outerHeight: window.outerHeight, posY });
  352. var _handleDropdownUnderClick = (function (nr) {
  353. return function ___handleDropdownUnderClick() {
  354. DBG && console.log('DBG:p5Modal:_handleDropdownUnderClick', { nr: nr });
  355. _closeModalByNr(nr);
  356. }
  357. })(modal.nr)
  358. modal.content.addEventListener('click', _handleDropdownUnderClick, false); // useCapture = false - trigger after inside node event callbacks
  359. document.body.appendChild(modal.overlay);
  360. modal.content.focus(); // TODO: BUG hit Enter after open modal will open the same modal on top of last
  361. // https://dev.to/aleksandrhovhannisyan/multiple-modals-on-one-page-using-html-css-and-javascript-353b#4-closing-a-stacked-modal-with-the-escape-key
  362. // IDEA: global p5Modal manager which generates unique ids and manage close and other actions
  363. // 'data-url'
  364. // glyphicon glyphicon-chevron-left - for cancel btn
  365. // glyphicon glyphicon-chevron-right - for continue btn
  366. }
  367. function _setModalContent(type, modal, props) {
  368. DBG && console.log('DBG:p5Modal:_setModalContent(', {type, modal, props}, ')');
  369. if (!props.body) {
  370. DBG && console.log('DBG:p5Modal:_setModalContent:props.body missing props.body!');
  371. }
  372. else {
  373. if ('function' === typeof props.body) {
  374. DBG && console.log('DBG:p5Modal:_setModalContent:props.body ...');
  375. props.body(modal);
  376. } else if ('string' === typeof props.body) {
  377. // TODO: clone node document.getElementById(props.body)
  378. // or innerHTML ('#...')
  379. // or ajax call ('http...') + loading... text or anim
  380. // - callback to handle response (success, fail) and modify modal content
  381. DBG && console.log('DBG:p5Modal:_setModalContent:props.body string ...');
  382. } else {
  383. DBG && console.log('DBG:p5Modal:_setModalContent:props.body not implemented props.body ...');
  384. }
  385. return;
  386. }
  387. if (type === 'dropdownUnder') {
  388. modal.body.appendChild(document.createTextNode('TODO: dropdownUnder Body ...')); // TODO: DBG
  389. } else {
  390. modal.header.appendChild(document.createTextNode('TODO: Title ...')); // TODO: DBG
  391. for (var i = 0; i < 100; i++) {
  392. modal.body.appendChild(document.createTextNode('TODO: Body ...')); // TODO: DBG
  393. modal.body.appendChild(document.createElement('br')); // TODO: DBG
  394. }
  395. modal.footer.appendChild(document.createTextNode('TODO: Footer ...')); // TODO: DBG
  396. }
  397. }
  398. function _createModal(type, targetNode, props) {
  399. _addEscKeyListener();
  400. var defaultProps = {
  401. // type: 'modal', // modal | sidePanel | alert
  402. source: null, // node id | url | js function | null
  403. };
  404. var attrProps = _getPropsFromAttr(targetNode);
  405. var props = Object.assign(defaultProps, attrProps, props || {});
  406. props.type = type;
  407. DBG && console.log('DBG:p5Modal:openModal:' + props.type + ':', { targetNode, props });
  408. // add to _openedModal
  409. // get _openedModal to set id attribute on overlay node
  410. var modal = {
  411. nr: _insertModal(props),
  412. overlay: document.createElement('div'),
  413. content: document.createElement('div'),
  414. header: document.createElement('div'),
  415. body: document.createElement('div'),
  416. footer: document.createElement('div'),
  417. };
  418. {
  419. modal.overlay.setAttribute('id', _modalHtmlId(modal.nr));
  420. _addStyle(modal.overlay, props.type, 'overlay', props);
  421. jQuery(modal.overlay).on('click', _makeCloseModal(modal.nr));
  422. {
  423. modal.content.setAttribute('id', _modalContentHtmlId(modal.nr));
  424. _addStyle(modal.content, props.type, 'content', props);
  425. jQuery(modal.content).on('click', function (event) {
  426. event.stopPropagation();
  427. });
  428. }
  429. modal.overlay.appendChild(modal.content);
  430. {
  431. modal.header.setAttribute('id', _modalTitleHtmlId(modal.nr));
  432. _addStyle(modal.header, props.type, 'title', props);
  433. }
  434. modal.content.appendChild(modal.header);
  435. {
  436. modal.body.setAttribute('id', _modalBodyHtmlId(modal.nr));
  437. _addStyle(modal.body, props.type, 'body', props);
  438. }
  439. modal.content.appendChild(modal.body);
  440. {
  441. modal.footer.setAttribute('id', _modalFooterHtmlId(modal.nr));
  442. _addStyle(modal.footer, props.type, 'footer', props);
  443. }
  444. modal.content.appendChild(modal.footer);
  445. }
  446. return modal;
  447. }
  448. function _insertModal(props) {
  449. _openedModal.push(props);
  450. return _openedModal.length - 1;
  451. }
  452. function _modalHtmlId(nr) { return 'p5-modal-' + nr; }
  453. function _modalContentHtmlId(nr) { return 'p5-modal-' + nr + '--content'; }
  454. function _modalTitleHtmlId(nr) { return 'p5-modal-' + nr + '--title'; }
  455. function _modalBodyHtmlId(nr) { return 'p5-modal-' + nr + '--body'; }
  456. function _modalFooterHtmlId(nr) { return 'p5-modal-' + nr + '--footer'; }
  457. function _closeModalByNr(nr) {
  458. // TODO: validate if nr is last
  459. if (!_openedModal.length) {
  460. DBG && console.log('DBG:p5Modal:_closeModalByNr: empty _openedModal');
  461. return;
  462. }
  463. if (nr != _openedModal.length - 1) {
  464. DBG && console.log('DBG:p5Modal:_closeModalByNr: modal nr(' + nr + ') is not last _openedModal');
  465. return;
  466. }
  467. _closeLastOpenedModal();
  468. // var idHtml = _modalHtmlId(nr);
  469. // // TODO: validte if may close - read props
  470. // // - if not -> blink to get user focus
  471. // var modalNode = document.getElementById(idHtml);
  472. // if (!modalNode) DBG && console.log('DBG:p5Modal:BUG: missing modal node in html tree!');
  473. // if (modalNode) document.body.removeChild(modalNode);
  474. }
  475. function _makeCloseModal(nr) {
  476. return function __closeModalByNr(event) {
  477. _closeModalByNr(nr);
  478. };
  479. }
  480. function _addStyle(node, type, nodeName, props) {
  481. if (type === 'dropdownUnder') {
  482. node.className += 'p5-dropdown--' + nodeName;
  483. return;
  484. }
  485. var style = _style(type)[nodeName];
  486. for (name in style) {
  487. node.style[name] = style[name];
  488. }
  489. }
  490. return {
  491. getTotal: function () { return _openedModal.length; },
  492. openModal: _openModal,
  493. openSidePanel: _openSidePanel,
  494. openAlert: _openAlert,
  495. openDropdownUnder: _openDropdownUnder,
  496. closeLast: _closeLastOpenedModal,
  497. };
  498. })();
  499. }
  500. global.p5Modal__sidePanel_from_ButtonPost = function __p5Modal__sidePanel_from_ButtonPost(event, node) {
  501. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost', { event, node })
  502. event.stopPropagation();
  503. event.preventDefault();
  504. if (!node.form) {
  505. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost -> !node.form -> return');
  506. return;
  507. }
  508. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost: form', { form: node.form, elements: node.form.elements });
  509. // node.form.method: "post"
  510. // node.form.action: "https://biuro.biall.com.pl/dev-pl/se-rsync/index.php?id_order=3324&_route=UrlAction_KioskPanel"
  511. // node.form.elements: HTMLFormControlsCollection(3)
  512. // - 0: input
  513. // - 1: input
  514. // - 2: button.btn.btn.btn-default.btn-xs
  515. var actionURL = node.form.action;
  516. var values = {};
  517. for (var idx = 0; idx < node.form.elements.length; idx++) {
  518. var el = node.form.elements[idx];
  519. var name = el.name;
  520. if (name) {
  521. values[name] = node.form[name].value;
  522. }
  523. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost: form.elements['+idx+']', { name, value: el.value, el });
  524. }
  525. // form.elements[0] {name: "_postTask", value: "printLogs", el: input}
  526. // form.elements[1] {name: "id_order", value: "10", el: input}
  527. // form.elements[2] {name: "", value: "", el: button.btn.btn.btn-default.btn-xs}
  528. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost: values', { values, actionURL });
  529. // '_postTask' -> '_modalAction' or just add _doModalAction=1 to form.action URL
  530. if ('_postTask' in values) {
  531. values['_modalAction'] = values['_postTask'];
  532. delete values['_postTask'];
  533. }
  534. function _initFetchSidePanelContent(modal) {
  535. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost: _initFetchSidePanelContent', { modal, values, actionURL });
  536. var formData = new FormData();
  537. for (var key in values) {
  538. formData.append(key, values[key]);
  539. }
  540. global.fetch(actionURL
  541. , (!values)
  542. ? { method: 'GET',
  543. // headers: { 'Content-Type': 'application/json' },
  544. credentials: 'same-origin',
  545. }
  546. : { method: 'POST',
  547. // headers: { 'Content-Type': 'application/json' },
  548. credentials: 'same-origin',
  549. body: formData
  550. }
  551. ).then(function (response) {
  552. return response.json()
  553. }).then(function (response) {
  554. DBG && console.log('DBG:p5Modal__sidePanel_from_ButtonPost: _initFetchSidePanelContent -> response', { response, modal, values, actionURL });
  555. if (!response) {
  556. p5UI__buildDom([ 'div', { className: 'alert alert-danger' }, 'Error: empty response' ], modal.body);
  557. } else if ('error' === response.type) {
  558. p5UI__buildDom([ 'div', { className: 'alert alert-danger' }, response.msg || 'Error: empty response' ], modal.body);
  559. } else if (response.body && response.body.modalBodyReactNode) {
  560. p5UI__buildDom(response.body.modalBodyReactNode, modal.body);
  561. if (response.body.modalHeaderReactNode) {
  562. p5UI__buildDom(response.body.modalHeaderReactNode, modal.header);
  563. } else {
  564. // TODO: btn 'x'
  565. }
  566. if (response.body.modalFooterReactNode) {
  567. p5UI__buildDom(response.body.modalFooterReactNode, modal.footer);
  568. } else {
  569. // TODO: modal.footer ?
  570. }
  571. } else {
  572. p5UI__buildDom([ 'div', { className: 'alert alert-danger' }, 'Error' ], modal.body);
  573. }
  574. })
  575. }
  576. return p5Modal.openSidePanel(event, node, props = {
  577. body: _initFetchSidePanelContent,
  578. });
  579. };
  580. function _openSidePanel(panelNode) {
  581. addClass(panelNode, 'p5-side_panel--is-visible');
  582. document.addEventListener('keyup', panelNode._p5_onEsc);
  583. panelNode.addEventListener('click', panelNode._p5_onClick);
  584. // fix content scrollTop
  585. var contentNode = panelNode.getElementsByClassName('p5-side_panel__content')
  586. if (contentNode && contentNode[0]) {
  587. contentNode[0].scrollTop = 0;
  588. }
  589. }
  590. function _closeSidePanel(panelNode) {
  591. removeClass(panelNode, 'p5-side_panel--is-visible');
  592. document.removeEventListener('keyup', panelNode._p5_onEsc)
  593. panelNode.removeEventListener('click', panelNode._p5_onClick);
  594. }
  595. function hasId(el, id) {
  596. return (id === el.getAttribute('id'));
  597. }
  598. //class manipulations - needed if classList is not supported
  599. //https://jaketrent.com/post/addremove-classes-raw-javascript/
  600. function hasClass(el, className) {
  601. if (el.classList) return el.classList.contains(className);
  602. else return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
  603. }
  604. function addClass(el, className) {
  605. if (el.classList) el.classList.add(className);
  606. else if (!hasClass(el, className)) el.className += ' ' + className;
  607. }
  608. function removeClass(el, className) {
  609. if (el.classList) el.classList.remove(className);
  610. else if (hasClass(el, className)) {
  611. var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
  612. el.className = el.className.replace(reg, ' ');
  613. }
  614. }
  615. // module.exports['p5UI__openModal__TORM'] = p5UI__openModal__TORM;
  616. // --------------
  617. function __buyIndeksAjax(ind, url) {
  618. var n = document.getElementById('basket__' + ind + '_buy_input'),
  619. wrap = document.getElementById('basket__' + ind),
  620. val = n.value;
  621. if (!n || !wrap) return true;
  622. var tmpNodes = uslugiNode.getElementsByClassName('buy__info__' + ind)
  623. var indeksInfoNode = tmpNodes ? tmpNodes[0] : null;
  624. var tmpNodes = uslugiNode.getElementsByClassName('buy__noservice__' + ind)
  625. var noServiceNode = tmpNodes ? tmpNodes[0] : null;
  626. var serviceNodes = uslugiNode.getElementsByClassName('buy__usluga__' + ind)
  627. var totalServiceNodes = serviceNodes.length;
  628. if (!totalServiceNodes || !indeksInfoNode || !noServiceNode) {
  629. return __buyIndeksAjax_simple(ind, url);
  630. }
  631. var buyModal = document.getElementById('modal__buy');
  632. if (buyModal) document.body.removeChild(buyModal);
  633. buyModal = document.createElement("div");
  634. buyModal.setAttribute('id', 'modal__buy');
  635. buyModal.style.display = "block";
  636. buyModal.style.position = "fixed";
  637. buyModal.style.zIndex = 1;
  638. buyModal.style.left = 0;
  639. buyModal.style.top = 0;
  640. buyModal.style.width = "100%";
  641. buyModal.style.height = "100%";
  642. buyModal.style.overflow = "auto";
  643. buyModal.style.backgroundColor = "#000";
  644. buyModal.style.backgroundColor = "#0009";
  645. jQuery(buyModal).on('click', __closeBuyIdneksModal)
  646. var content = document.createElement("div");
  647. content.style.backgroundColor = "#fefefe"
  648. content.style.margin = "15% auto"
  649. content.style.padding = "24px 32px"
  650. content.style.border = "1px solid #888"
  651. content.style.width = "80%"
  652. content.style.maxWidth = "800px"
  653. content.style.textAlign = "left"
  654. content.style.fontSize = "14px"
  655. jQuery(content).on('click', function (event) {
  656. event.stopPropagation();
  657. })
  658. var nodeH3 = document.createElement("h3");
  659. nodeH3.style.fontSize = "18px";
  660. nodeH3.style.lineHeight = "1.6em";
  661. nodeH3.style.marginBottom = "16px";
  662. nodeH3.appendChild(document.createTextNode("Wybierz dodatkowe us�ugi:"));
  663. var nodeInfo = document.createElement("div");
  664. nodeInfo.style.marginBottom = "24px";
  665. nodeInfo.appendChild(indeksInfoNode.cloneNode(true));
  666. var nodeTable = document.createElement("table");
  667. nodeTable.setAttribute('cellspacing', '0')
  668. nodeTable.setAttribute('cellpadding', '0')
  669. nodeTable.style.borderCollapse = "collapse";
  670. nodeTable.style.width = "100%";
  671. var nodeTr, nodeTd, nodeTdSelect;
  672. for (var i = 0; i < totalServiceNodes; i++) {
  673. idService = serviceNodes[i].getAttribute('data-service')
  674. if (!idService) {
  675. continue;
  676. }
  677. nodeTr = document.createElement("tr"); {
  678. nodeTd = document.createElement("td");
  679. nodeTd.style.padding = "24px";
  680. nodeTd.style.border = "1px solid #ddd";
  681. nodeTd.appendChild(serviceNodes[i].cloneNode(true));
  682. }
  683. nodeTr.appendChild(nodeTd); {
  684. nodeTdSelect = document.createElement("td");
  685. nodeTdSelect.style.padding = "24px";
  686. nodeTdSelect.style.border = "1px solid #ddd";
  687. nodeTdSelect.style.textAlign = "center";
  688. var selectBtn = __createBuyWithServiceAjaxBtn(ind, idService, url);
  689. nodeTdSelect.appendChild(selectBtn);
  690. }
  691. nodeTr.appendChild(nodeTdSelect);
  692. }
  693. nodeTable.appendChild(nodeTr);
  694. { // no service
  695. nodeTr = document.createElement("tr"); {
  696. nodeTd = document.createElement("td");
  697. nodeTd.style.padding = "24px";
  698. nodeTd.style.border = "1px solid #ddd";
  699. nodeTd.style.color = "#666";
  700. nodeTd.appendChild(noServiceNode.cloneNode(true));
  701. }
  702. nodeTr.appendChild(nodeTd); {
  703. nodeTdSelect = document.createElement("td");
  704. nodeTdSelect.style.padding = "24px";
  705. nodeTdSelect.style.border = "1px solid #ddd";
  706. nodeTdSelect.style.textAlign = "center";
  707. var selectBtn = __createSimpleBuyAjaxBtn(ind, url);
  708. nodeTdSelect.appendChild(selectBtn);
  709. }
  710. nodeTr.appendChild(nodeTdSelect);
  711. }
  712. nodeTable.appendChild(nodeTr);
  713. content.appendChild(nodeH3);
  714. content.appendChild(nodeInfo);
  715. content.appendChild(nodeTable);
  716. buyModal.appendChild(content);
  717. document.body.appendChild(buyModal);
  718. return false;
  719. }
  720. function __createSimpleBuyAjaxBtn(ind, url) {
  721. var nodeBtn = document.createElement("a");
  722. nodeBtn.setAttribute('href', url + 'buy,' + ind + '.html')
  723. nodeBtn.setAttribute('onclick', "return __buyIndeksAjax_simpleCloseModal('" + ind + "', '" + url + "');")
  724. nodeBtn.setAttribute('class', "btn-shop")
  725. nodeBtn.style.padding = "6px 16px";
  726. nodeBtn.appendChild(document.createTextNode("Wybieram"));
  727. return nodeBtn;
  728. }
  729. function __createBuyWithServiceAjaxBtn(ind, service, url) {
  730. var nodeBtn = document.createElement("a");
  731. nodeBtn.setAttribute('href', url + 'buy,' + ind + '.html')
  732. nodeBtn.setAttribute('onclick', "return __buyIndeksAjax_withService('" + ind + "', '" + service + "', '" + url + "');")
  733. nodeBtn.setAttribute('class', "btn-shop")
  734. nodeBtn.style.padding = "6px 16px";
  735. nodeBtn.appendChild(document.createTextNode("Wybieram"));
  736. return nodeBtn;
  737. }
  738. function __buyIndeksAjax_simpleCloseModal(ind, url) {
  739. __closeBuyIdneksModal();
  740. __buyIndeksAjax_simple(ind, url);
  741. return false;
  742. }
  743. function __closeBuyIdneksModal() {
  744. var buyModal = document.getElementById('modal__buy');
  745. if (buyModal) document.body.removeChild(buyModal);
  746. }