page.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /**
  2. * Copyright (c) UNA, Inc - https://una.io
  3. * MIT License - https://opensource.org/licenses/MIT
  4. *
  5. * @defgroup UnaStudio UNA Studio
  6. * @{
  7. */
  8. function BxDolStudioPage(oOptions) {
  9. this.sActionsUrl = oOptions.sActionUrl;
  10. this.sObjName = oOptions.sObjName == undefined ? 'oBxDolStudioPage' : oOptions.sObjName;
  11. this.sCodeMirror = oOptions.sCodeMirror == undefined ? '' : oOptions.sCodeMirror;
  12. this.sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
  13. this.iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
  14. this.oPopupOptions = {
  15. fog: {
  16. color: '#fff',
  17. opacity: .7
  18. },
  19. closeOnOuterClick: false
  20. };
  21. var $this = this;
  22. $(document).ready (function () {
  23. if($this.sCodeMirror != '')
  24. $this.initCodeMirror($this.sCodeMirror);
  25. });
  26. }
  27. BxDolStudioPage.prototype.processJson = function (oData) {
  28. bx_loading('bx-std-page-columns', false);
  29. processJsonData(oData);
  30. };
  31. BxDolStudioPage.prototype.togglePopup = function(sName, oLink) {
  32. var $this = this;
  33. var sId = '#bx-std-pcap-menu-popup-' + sName;
  34. if(sName == 'actions' && $(sId).length == 0)
  35. sId = '#bx-std-pmenu-popup-' + sName;
  36. if($(sId + ':visible').length > 0) {
  37. $(sId).dolPopupHide();
  38. return;
  39. }
  40. $(oLink).parent().addClass('bx-menu-tab-active');
  41. var oPopupOptions = {
  42. pointer:{
  43. el:$(oLink)
  44. },
  45. onHide: function() {
  46. $(oLink).parent().removeClass('bx-menu-tab-active');
  47. }
  48. };
  49. switch(sName) {
  50. case 'assistant':
  51. break;
  52. case 'help':
  53. oPopupOptions = $.extend({}, oPopupOptions, {
  54. onBeforeShow: function() {
  55. var oPopup = $(sId);
  56. var oPopupRss = oPopup.find('.RSSAggrCont');
  57. if(oPopupRss.contents().length)
  58. return;
  59. oPopupRss.dolRSSFeed({
  60. onError: function() {
  61. oPopupRss.html(_t('_adm_txt_show_help_content_empty'));
  62. oPopup._dolPopupSetPosition(oPopupOptions);
  63. },
  64. onShow: function() {
  65. oPopup._dolPopupSetPosition(oPopupOptions);
  66. }
  67. });
  68. oPopup._dolPopupSetPosition(oPopupOptions);
  69. }
  70. });
  71. break;
  72. }
  73. if($(sId).html().length > 0)
  74. $(sId).dolPopup(oPopupOptions);
  75. };
  76. BxDolStudioPage.prototype.initCodeMirror = function(sSelector) {
  77. var oSelector = $(sSelector);
  78. for(var i = 0; i < oSelector.length; i++) {
  79. var e = CodeMirror.fromTextArea(oSelector.get(i), {
  80. lineNumbers: true,
  81. mode: "htmlmixed",
  82. htmlMode: true,
  83. matchBrackets: true
  84. });
  85. }
  86. };
  87. /**
  88. * Phone layout: the page menu is a horizontal row of tabs, so the active one is brought into view on load.
  89. */
  90. $(document).ready(function() {
  91. if(!window.matchMedia('(max-width: 767px)').matches)
  92. return;
  93. var oMenu = document.querySelector('.bx-std-page-column.bx-std-page-menu');
  94. var oActive = oMenu ? oMenu.querySelector('.bx-std-pmen-item.bx-menu-tab-active') : null;
  95. if(!oActive || oMenu.scrollWidth <= oMenu.clientWidth)
  96. return;
  97. // the row scrolls on its own; scrollIntoView would move every scrollable ancestor, the page included
  98. oMenu.scrollLeft += oActive.getBoundingClientRect().left - oMenu.getBoundingClientRect().left - (oMenu.clientWidth - oActive.offsetWidth) / 2;
  99. });
  100. /**
  101. * Overlay scrollbar for a block's scroll box (div.bx-std-block-content, see page.css): an SVG over the block draws a track and a thumb
  102. * along the box's right edge and round its rounded corners, the thumb's length and place following the scroll position.
  103. * The native bar is hidden by CSS; the SVG is redrawn on scroll, on resize and when the content changes.
  104. */
  105. function bx_std_scrollbars(sSelector) {
  106. $(sSelector || 'div.bx-std-block-content').each(function() {
  107. bx_std_scrollbar(this);
  108. });
  109. }
  110. function bx_std_scrollbar(oScroller) {
  111. if(oScroller.bxScrollbar)
  112. return;
  113. var oBlock = $(oScroller).closest('div.bx-std-block')[0];
  114. if(!oBlock)
  115. return;
  116. var sNs = 'http://www.w3.org/2000/svg';
  117. var oSvg = document.createElementNS(sNs, 'svg');
  118. oSvg.setAttribute('class', 'bx-std-scrollbar');
  119. oSvg.setAttribute('aria-hidden', 'true');
  120. var oTrack = document.createElementNS(sNs, 'path');
  121. oTrack.setAttribute('class', 'bx-std-scrollbar-track');
  122. var oThumb = document.createElementNS(sNs, 'path');
  123. oThumb.setAttribute('class', 'bx-std-scrollbar-thumb');
  124. oSvg.appendChild(oTrack);
  125. oSvg.appendChild(oThumb);
  126. oBlock.insertBefore(oSvg, oBlock.firstChild); // first, so the scroll box stays the block's :last-child (page.css rounds it by that)
  127. var oState = {scroller: oScroller, block: oBlock, svg: oSvg, track: oTrack, thumb: oThumb, timer: null};
  128. oScroller.bxScrollbar = oState;
  129. var fUpdate = function() {
  130. bx_std_scrollbar_update(oState);
  131. };
  132. oScroller.addEventListener('scroll', function() {
  133. fUpdate();
  134. $(oBlock).addClass('bx-std-scrolling');
  135. clearTimeout(oState.timer);
  136. oState.timer = setTimeout(function() {
  137. $(oBlock).removeClass('bx-std-scrolling');
  138. }, 800);
  139. }, {passive: true});
  140. if(window.ResizeObserver)
  141. new ResizeObserver(fUpdate).observe(oScroller);
  142. if(window.MutationObserver)
  143. new MutationObserver(function() {
  144. clearTimeout(oState.mutation);
  145. oState.mutation = setTimeout(fUpdate, 50);
  146. }).observe(oScroller, {childList: true, subtree: true, attributes: true});
  147. $(window).on('resize', fUpdate);
  148. fUpdate();
  149. }
  150. function bx_std_scrollbar_update(o) {
  151. var oScroller = o.scroller, iRange = oScroller.scrollHeight - oScroller.clientHeight;
  152. if(iRange <= 1) {
  153. o.svg.style.display = 'none';
  154. return;
  155. }
  156. o.svg.style.display = '';
  157. var oRb = o.block.getBoundingClientRect(), oRs = oScroller.getBoundingClientRect();
  158. var fW = oRb.width, fH = oRb.height, fTop = oRs.top - oRb.top, fBottom = oRs.bottom - oRb.top;
  159. var oCss = getComputedStyle(oScroller), oCssBlock = getComputedStyle(o.block);
  160. var fInset = 6; // the bar's centre line, 6px in from the edge: 4px wide with a 4px gap
  161. var fRb = Math.max((parseFloat(oCss.borderBottomRightRadius) || 0) - fInset, 0);
  162. var fRt = fTop < 1 ? Math.max((parseFloat(oCssBlock.borderTopRightRadius) || 0) - fInset, 0) : 0;
  163. var fX = fW - fInset;
  164. // round the top corner when the scroll box starts at the block's top, down the right edge, round the bottom corner
  165. var sPath = fRt > 0 ? 'M' + (fX - fRt) + ' ' + (fTop + fInset) + ' A' + fRt + ' ' + fRt + ' 0 0 1 ' + fX + ' ' + (fTop + fInset + fRt) : 'M' + fX + ' ' + (fTop + fInset);
  166. sPath += ' L' + fX + ' ' + (fBottom - fInset - fRb);
  167. if(fRb > 0)
  168. sPath += ' A' + fRb + ' ' + fRb + ' 0 0 1 ' + (fX - fRb) + ' ' + (fBottom - fInset);
  169. o.svg.setAttribute('viewBox', '0 0 ' + fW + ' ' + fH);
  170. o.track.setAttribute('d', sPath);
  171. o.thumb.setAttribute('d', sPath);
  172. var fLength = o.thumb.getTotalLength();
  173. var fThumb = Math.max(fLength * oScroller.clientHeight / oScroller.scrollHeight, 24);
  174. var fOffset = (fLength - fThumb) * oScroller.scrollTop / iRange;
  175. o.thumb.setAttribute('stroke-dasharray', fThumb + ' ' + fLength);
  176. o.thumb.setAttribute('stroke-dashoffset', -fOffset);
  177. }
  178. $(document).ready(function() {
  179. bx_std_scrollbars();
  180. });
  181. /**
  182. * Button loader (the launcher's tile wave, page.css .bx-std-btn-loading) for a button whose request is in flight: the button is disabled
  183. * and its label gives way to the wave at the button's width, so nothing moves; the label (and the button) comes back when the request
  184. * ends. The label stays the accessible name meanwhile. Replaces the site's bx_loading_btn spinner in Studio.
  185. */
  186. function bx_std_loading_btn(oButton, bShow) {
  187. oButton = $(oButton);
  188. if(!oButton.length)
  189. return;
  190. if(bShow) {
  191. if(oButton.data('bx-loading'))
  192. return;
  193. oButton.data('bx-loading', {html: oButton.html(), label: oButton.attr('aria-label'), disabled: oButton.prop('disabled') || oButton.hasClass('bx-btn-disabled')});
  194. if(!oButton.attr('aria-label'))
  195. oButton.attr('aria-label', oButton.text().trim());
  196. oButton.css('min-width', oButton.outerWidth() + 'px').prop('disabled', true).attr('aria-disabled', 'true').addClass('bx-btn-disabled bx-btn-loading');
  197. oButton.html('<span class="bx-std-btn-loading" aria-hidden="true"></span>');
  198. return;
  199. }
  200. var oSaved = oButton.data('bx-loading');
  201. if(!oSaved)
  202. return;
  203. // a button that was disabled before stays so
  204. oButton.removeData('bx-loading').css('min-width', '').removeClass('bx-btn-loading').html(oSaved.html);
  205. if(!oSaved.disabled)
  206. oButton.prop('disabled', false).removeAttr('aria-disabled').removeClass('bx-btn-disabled');
  207. if(oSaved.label == undefined)
  208. oButton.removeAttr('aria-label');
  209. }
  210. /** @} */