functions.js 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362
  1. /**
  2. * Copyright (c) UNA, Inc - https://una.io
  3. * MIT License - https://opensource.org/licenses/MIT
  4. *
  5. * @defgroup UnaCore UNA Core
  6. * @{
  7. */
  8. function processJsonData(oData) {
  9. var fContinue = function(oData) {
  10. if(oData && oData.reload != undefined && parseInt(oData.reload) == 1)
  11. document.location = document.location;
  12. if(oData && oData.redirect != undefined && oData.redirect.length != 0)
  13. document.location = oData.redirect;
  14. if(oData && oData.popup != undefined) {
  15. var oPopup = null;
  16. var oOptions = {
  17. fog: {
  18. color: '#fff',
  19. opacity: .7
  20. },
  21. closeOnOuterClick: false
  22. };
  23. if(typeof(oData.popup) == 'object') {
  24. oOptions = $.extend({}, oOptions, oData.popup.options);
  25. oPopup = $(oData.popup.html);
  26. }
  27. else
  28. oPopup = $(oData.popup);
  29. if ('undefined' !== typeof(bx_editor_remove_all))
  30. bx_editor_remove_all($('#' + oPopup.attr('id')));
  31. $('#' + oPopup.attr('id')).remove();
  32. oPopup.hide().prependTo('body').bxProcessHtml().dolPopup(oOptions);
  33. }
  34. if(oData && oData.form != undefined && oData.form_id != undefined)
  35. $('form#' + oData.form_id).replaceWith(oData.form).bxProcessHtml();
  36. if (oData && oData.eval != undefined)
  37. eval(oData.eval);
  38. };
  39. // A reply can name its interface sound: 'error', or {cue: 'success', emphasis: 'subtle'}. It plays now, before any alert.
  40. if(oData && oData.cue != undefined)
  41. bx_sound(typeof(oData.cue) == 'object' ? oData.cue.cue : oData.cue, typeof(oData.cue) == 'object' ? oData.cue : undefined);
  42. if(oData && oData.object != undefined && oData.grid != undefined)
  43. if(glGrids[oData.object] != undefined)
  44. glGrids[oData.object].processJson(oData);
  45. if(oData && oData.message != undefined && oData.message.length != 0)
  46. bx_alert(oData.message, function() {
  47. fContinue(oData);
  48. });
  49. else if(oData && oData.msg != undefined && oData.msg.length != 0)
  50. bx_alert(oData.msg, function() {
  51. fContinue(oData);
  52. });
  53. else if(oData && oData.toast != undefined && oData.toast.length != 0) {
  54. bx_toast(oData.toast);
  55. fContinue(oData);
  56. }
  57. else
  58. fContinue(oData);
  59. };
  60. function getHtmlData( elem, url, callback, method, confirmation, values, loading)
  61. {
  62. var fPerform = function() {
  63. // in most cases it is element ID, in other cases - object of jQuery
  64. if (typeof elem == 'string')
  65. elem = '#' + elem; // create selector from ID
  66. var $block = $(elem);
  67. var blockPos = $block.css('position');
  68. $block.css('position', 'relative'); // set temporarily for displaying "loading icon"
  69. if ('undefined' === typeof(loading) || loading) {
  70. bx_loading_content($block, true);
  71. var $loadingDiv = $block.find('.bx-loading-ajax');
  72. var iLeftOff = parseInt(($block.innerWidth() / 2.0) - ($loadingDiv.outerWidth() / 2.0));
  73. var iTopOff = parseInt(($block.innerHeight() / 2.0) - ($loadingDiv.outerHeight()));
  74. if (iTopOff<0) iTopOff = 0;
  75. $loadingDiv.css({
  76. position: 'absolute',
  77. left: iLeftOff,
  78. top: iTopOff
  79. });
  80. }
  81. if (undefined != method && (method == 'post' || method == 'POST')) {
  82. if (typeof values == 'undefined')
  83. values = {};
  84. $.post(url, values, function(data) {
  85. $block.html(data);
  86. $block.css('position', blockPos).bxProcessHtml();
  87. if (typeof callback == 'function')
  88. callback.apply($block);
  89. });
  90. }
  91. else {
  92. $block.load(url + '&_r=' + Math.random(), function() {
  93. $(this).css('position', blockPos).bxProcessHtml();
  94. if (typeof callback == 'function')
  95. callback.apply(this);
  96. });
  97. }
  98. };
  99. if (typeof(confirmation) != 'undefined' && confirmation)
  100. bx_confirm(_t('_Are_you_sure'), fPerform);
  101. else
  102. fPerform();
  103. }
  104. function loadDynamicBlockAutoPaginate (e, iStart, iPerPage, sAdditionalUrlParams, sStartParamName, sPerPageParamName) {
  105. sUrl = location.href;
  106. sStartParamName = typeof sStartParamName !== 'undefined' ? sStartParamName : 'start';
  107. sPerPageParamName = typeof sPerPageParamName !== 'undefined' ? sPerPageParamName : 'per_page';
  108. sUrl = sUrl.replace(/start=\d+/, '').replace(/per_page=\d+/, '').replace(/[&\?]+$/, '');
  109. sUrl = bx_append_url_params(sUrl, sStartParamName + '=' + parseInt(iStart) + '&' + sPerPageParamName + '=' + parseInt(iPerPage));
  110. if ('undefined' != typeof(sAdditionalUrlParams))
  111. sUrl = bx_append_url_params(sUrl, sAdditionalUrlParams);
  112. if ($(e).parents('.bx-search-result-block-pagination').length > 0){
  113. $([document.documentElement, document.body]).animate({
  114. scrollTop: $(e).parents('.bx-search-result-block-pagination').first().offset().top
  115. }, 500);
  116. }
  117. return loadDynamicBlockAuto(e, sUrl);
  118. }
  119. function loadDynamicBlockAutoSort (e, sort, sAdditionalUrlParams) {
  120. sUrl = location.href;
  121. console.log(sort);
  122. sUrl = sUrl.replace(/sort=\d+/, '').replace(/[&\?]+$/, '');
  123. sUrl = bx_append_url_params(sUrl, 'sort' + '=' + sort);
  124. if ('undefined' != typeof(sAdditionalUrlParams))
  125. sUrl = bx_append_url_params(sUrl, sAdditionalUrlParams);
  126. if ($(e).parents('.bx-search-result-block-pagination').length > 0){
  127. $([document.documentElement, document.body]).animate({
  128. scrollTop: $(e).parents('.bx-search-result-block-pagination').first().offset().top
  129. }, 500);
  130. }
  131. return loadDynamicBlockAuto(e, sUrl);
  132. }
  133. /**
  134. * This function reloads page block automatically,
  135. * just provide any element inside the block and this function will reload the block.
  136. * @param e - element inside the block
  137. * @return true on success, or false on error - particularly, if block isn't found
  138. */
  139. function loadDynamicBlockAuto(e, sUrl, oPostData) {
  140. var oContainer = $(e).parents('.bx-page-block-container:first:not(.no-dynamic)');
  141. var sId = oContainer.attr('id');
  142. if ('undefined' == typeof(sUrl))
  143. sUrl = location.href;
  144. if (!sId || !sId.length)
  145. return false;
  146. var aMatches = sId.match(/\-(\d+)$/);
  147. if (!aMatches || !aMatches[1])
  148. return false;
  149. var oPaginate = oContainer.find('.bx-paginate');
  150. if(oPaginate.length > 0) {
  151. var iStart = parseInt(oPaginate.attr('bx-data-start'));
  152. var iPerPage = parseInt(oPaginate.attr('bx-data-perpage'));
  153. var oParams = {};
  154. if(!isNaN(iStart) && sUrl.indexOf('start') == -1)
  155. oParams.start = iStart;
  156. if(!isNaN(iPerPage) && sUrl.indexOf('per_page') == -1)
  157. oParams.per_page = iPerPage;
  158. sUrl = bx_append_url_params(sUrl, oParams);
  159. }
  160. loadDynamicBlock(parseInt(aMatches[1]), sUrl, oPostData);
  161. return true;
  162. }
  163. /**
  164. * Reload the current page block to show or hide a form secret field.
  165. * Reveal is POSTed with a CSRF token so it cannot be triggered by a GET param alone.
  166. * When the form is not inside a page block, the whole page is reloaded via POST.
  167. */
  168. function bx_form_secret_toggle(e) {
  169. var oToggle = $(e);
  170. var bHide = oToggle.closest('.bx-form-secret').hasClass('bx-form-secret-shown');
  171. var sField = oToggle.attr('bx_form_secret_field');
  172. var sCsrfToken = oToggle.attr('bx_form_secret_csrf');
  173. var sUrl = location.href;
  174. try {
  175. var oUrl = new URL(sUrl);
  176. oUrl.searchParams.delete('form_secret_reveal');
  177. oUrl.searchParams.delete('csrf_token');
  178. sUrl = oUrl.toString();
  179. } catch (oErr) {
  180. sUrl = sUrl.replace(/([?&])form_secret_reveal=[^&]*/g, '$1').replace(/([?&])csrf_token=[^&]*/g, '$1').replace(/[?&]$/, '');
  181. }
  182. if (bHide) {
  183. if (!loadDynamicBlockAuto(e, sUrl))
  184. location.href = sUrl;
  185. return false;
  186. }
  187. var oData = {form_secret_reveal: sField};
  188. if (sCsrfToken)
  189. oData.csrf_token = sCsrfToken;
  190. return loadDynamicBlockAuto(e, sUrl, oData);
  191. }
  192. function loadDynamicBlock(iBlockID, sUrl, oPostData) {
  193. var oCallback = null;
  194. if($('#bx-page-block-' + iBlockID + ' .bx-base-unit-showcase-wrapper').length){
  195. oCallback = bx_showcase_view_init;
  196. }
  197. if (typeof oPostData != 'undefined' && oPostData)
  198. getHtmlData($('#bx-page-block-' + iBlockID), bx_append_url_params(sUrl, 'dynamic=tab&pageBlock=' + iBlockID), oCallback, 'post', false, oPostData);
  199. else
  200. getHtmlData($('#bx-page-block-' + iBlockID), bx_append_url_params(sUrl, 'dynamic=tab&pageBlock=' + iBlockID), oCallback);
  201. return true;
  202. }
  203. function loadDynamicPopupBlock(iBlockID, sUrl) {
  204. if (!$('#dynamicPopup').length) {
  205. $('<div id="dynamicPopup" style="display:none;"></div>').prependTo('body');
  206. }
  207. $('#dynamicPopup').load(
  208. (sUrl + '&dynamic=popup&pageBlock=' + iBlockID),
  209. function() {
  210. $(this).dolPopup({
  211. left: 0,
  212. top: 0
  213. });
  214. }
  215. );
  216. }
  217. function closeDynamicPopupBlock() {
  218. $('#dynamicPopup').dolPopupHide();
  219. }
  220. function loadDynamicBlockAsync(iBlockID, sUrl, fCallback, bLoading, bLazyLoad) {
  221. if(!sUrl)
  222. sUrl = document.location.href;
  223. var fLoadBlock = function(iBlockID, sUrl) {
  224. if(fCallback == undefined || fCallback == true)
  225. fCallback = onAsyncBlockLoad;
  226. getHtmlData($('#bx-page-block-' + iBlockID), bx_append_url_params(sUrl, 'dynamic=tab&pageBlock=' + iBlockID), fCallback, 'post', false, {
  227. includedCss: JSON.stringify(aIncludedCss),
  228. includedJs: JSON.stringify(aIncludedJs)
  229. }, (bLoading != undefined && bLoading == true));
  230. };
  231. if(bLazyLoad == undefined || bLazyLoad == true) {
  232. const oObserver = new IntersectionObserver((aEntries, obs) => {
  233. aEntries.forEach(oEntry => {
  234. if(oEntry.isIntersecting) {
  235. const oElement = oEntry.target;
  236. fLoadBlock(iBlockID, sUrl);
  237. obs.unobserve(oElement);
  238. }
  239. });
  240. }, {
  241. root: null, // viewport
  242. threshold: 0.1 // trigger when 10% visible
  243. });
  244. document.querySelectorAll('.bx-db-placeholder-async').forEach(oElement => {
  245. oObserver.observe(oElement);
  246. });
  247. }
  248. else
  249. $(document).ready(function() {
  250. fLoadBlock(iBlockID, sUrl);
  251. });
  252. }
  253. function onAsyncBlockLoad() {
  254. var oBlock = $(this);
  255. if(!oBlock.html())
  256. oBlock.remove();
  257. }
  258. /**
  259. * Translate string
  260. */
  261. function _t(s, arg0, arg1, arg2) {
  262. if (!window.aDolLang || !aDolLang[s])
  263. return s;
  264. cs = aDolLang[s];
  265. cs = cs.replace(/\{0\}/g, arg0);
  266. cs = cs.replace(/\{1\}/g, arg1);
  267. cs = cs.replace(/\{2\}/g, arg2);
  268. return cs;
  269. }
  270. function showPopupAnyHtml(sUrl, sId) {
  271. var oPopupOptions = {};
  272. if (!sId || !sId.length)
  273. sId = 'login_div';
  274. $('#' + sId).remove();
  275. $('<div id="' + sId + '" style="display: none;"></div>').prependTo('body').load(
  276. sUrl.match('^http[s]{0,1}:\/\/') ? sUrl : sUrlRoot + sUrl,
  277. function() {
  278. $(this).dolPopup(oPopupOptions);
  279. }
  280. );
  281. }
  282. function bx_loading_svg(sType, sClass) {
  283. sClass = sClass != undefined && sClass.length > 0 ? sClass : '';
  284. if(sUseSvgLoading != undefined && sUseSvgLoading.length > 0)
  285. return sUseSvgLoading.replace(new RegExp('__type__','g'), sType).replace(new RegExp('__class__','g'), sClass);
  286. var s = '';
  287. s += '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 520 520" class="' + sClass + '" style="enable-background:new 0 0 520 520;" xml:space="preserve">';
  288. s += '<g class="' + sType + '">';
  289. s += '<g class="inner">';
  290. s += '<path class="p1" d="M152,260C152,260,152,260,152,260c0-59.8,48.4-108.4,108.1-108.4c43.3,0,80.9,25.6,97.9,62.4V107.3c-28-18.3-61.9-28.9-98.1-28.9C159.8,78.4,78.6,159.9,78.6,260c0,59.5,28.4,112.4,73.4,145.6V260z"/>';
  291. s += '<path class="p2" d="M368,114.4V260c0,59.8-48.4,108.4-108.1,108.4c-43.3,0-80.9-25.6-97.9-62.4v106.7c28,18.3,61.9,28.9,98.1,28.9c100.1,0,181.3-81.4,181.3-181.6C441.4,200.5,413,147.6,368,114.4z"/>';
  292. s += '</g>';
  293. s += '<g class="outer">';
  294. s += '<path class="p1" d="M146.9,106.7c-8.1-15-36.9-29.9-68.9-27.3v124.2C90,164.3,114.6,130.5,146.9,106.7z"/>';
  295. s += '<path class="p2" d="M373.1,413.3c8.1,15,36.7,29.9,68.9,27.3V316.4C429.8,355.7,405.4,389.5,373.1,413.3z"/>';
  296. s += '</g>';
  297. s += '</g>';
  298. s += '</svg>';
  299. return s;
  300. }
  301. function bx_loading_animate (e, aOptions) {
  302. e = $(e);
  303. if (!e.length)
  304. return false;
  305. if (e.find('.bx-sys-spinner').length)
  306. return false;
  307. return new Spinner(aOptions).spin(e.get(0));
  308. }
  309. function bx_loading_btn (oElement, bEnable) {
  310. var oButton = $(oElement);
  311. var sClassHeight = oButton.hasClass('bx-btn-small') ? 'bx-btn-small-height' : 'bx-btn-height';
  312. if (oButton.children('div').size())
  313. oButton = oButton.children('div').first();
  314. if(!bEnable)
  315. oButton.find('.bx-loading-ajax-btn').remove();
  316. else if (!oButton.find('.bx-loading-ajax-btn').length)
  317. oButton.append('<b class="bx-loading-ajax-btn ' + sClassHeight + '">' + (bUseSvgLoading ? bx_loading_svg('colored', sClassHeight) : '') + '</b>');
  318. if(!bUseSvgLoading)
  319. bx_loading_animate(oButton.find('.bx-loading-ajax-btn'), aSpinnerSmallOpts);
  320. }
  321. function bx_loading_content (oElement, bEnable, isReplace) {
  322. var oBlock = $(oElement);
  323. var oLoading = $('<div class="bx-loading-ajax bx-def-z-index-front">' + (bUseSvgLoading ? bx_loading_svg('colored') : '') + '</div>');
  324. if(!bEnable)
  325. oBlock.find(".bx-loading-ajax").remove();
  326. else if(!oBlock.find('.bx-loading-ajax').length) {
  327. if('undefined' != typeof(isReplace) && isReplace)
  328. oBlock.html(oLoading.addClass('static'));
  329. else
  330. oBlock.append(oLoading);
  331. if(!bUseSvgLoading)
  332. bx_loading_animate(oBlock.find('.bx-loading-ajax'), aSpinnerOpts);
  333. }
  334. }
  335. function bx_loading (elem, b) {
  336. if (typeof elem == 'string')
  337. elem = '#' + elem;
  338. var block = $(elem);
  339. if (block.hasClass('bx-btn')) {
  340. bx_loading_btn (block, b);
  341. return;
  342. }
  343. if (1 == b || true == b) {
  344. bx_loading_content(block, b);
  345. e = block.find(".bx-loading-ajax");
  346. e.css('left', parseInt(block.width()/2.0 - e.width()/2.0));
  347. var he = e.outerHeight();
  348. var hc = block.outerHeight();
  349. if (block.css('position') != 'relative' && block.css('position') != 'absolute') {
  350. if (!block.data('css-save-position'))
  351. block.data('css-save-position', block.css('position'));
  352. block.css('position', 'relative');
  353. }
  354. if (hc > he) {
  355. e.css('top', parseInt(hc/2.0 - he/2.0));
  356. }
  357. if (hc < he) {
  358. if (!block.data('css-save-min-height'))
  359. block.data('css-save-min-height', block.css('min-height'));
  360. block.css('min-height', he);
  361. e.css('top', 0);
  362. }
  363. } else {
  364. if (block.data('css-save-position'))
  365. block.css('position', block.data('css-save-position'));
  366. if (block.data('css-save-min-height'))
  367. block.css('min-height', block.data('css-save-min-height'));
  368. bx_loading_content(block, b);
  369. }
  370. }
  371. /**
  372. * Center content with floating blocks.
  373. * sSel - jQuery selector of content to be centered
  374. * sBlockSel - jquery selector of blocks
  375. */
  376. function bx_center_content (sSel, sBlockStyle, bListenOnResize) {
  377. var sId;
  378. if ($(sSel).parent().hasClass('bx-center-content-wrapper')) {
  379. sId = $(sSel).parent().attr('id');
  380. } else {
  381. sId = 'id' + (new Date()).getTime();
  382. $(sSel).wrap('<div id="'+sId+'" class="bx-center-content-wrapper"></div>');
  383. }
  384. var eCenter = $('#' + sId);
  385. var iAll = $('#' + sId + ' ' + sBlockStyle).size();
  386. var iWidthUnit = $('#' + sId + ' ' + sBlockStyle + ':first').outerWidth(true);
  387. var iWidthContainer = eCenter.innerWidth();
  388. var iPerRow = parseInt(iWidthContainer/iWidthUnit);
  389. var iLeft = (iWidthContainer - (iAll > iPerRow ? iPerRow * iWidthUnit : iAll * iWidthUnit)) / 2;
  390. if (iWidthUnit > iWidthContainer)
  391. return;
  392. if ('undefined' != typeof(bListenOnResize) && bListenOnResize) {
  393. $(window).on('resize.bx-center-content', function () {
  394. bx_center_content(sSel, sBlockStyle);
  395. });
  396. }
  397. eCenter.css("padding-left", iLeft);
  398. }
  399. /**
  400. * Show pointer popup with menu from URL.
  401. * @param o - menu object name
  402. * @param e - element to show popup at
  403. * @param options - popup options
  404. * @param vars - additional GET variables
  405. */
  406. function bx_menu_popup (o, e, options, vars) {
  407. var options = options || {};
  408. var vars = vars || {};
  409. if ('undefined' == typeof(e))
  410. e = window;
  411. //--- Only a trigger that names no target of its own is this popup's disclosure. A trigger carrying
  412. //--- aria-controls belongs to whatever it names - a sidebar, the search box - and that owner keeps
  413. //--- its state; writing to it from here would leave it describing the wrong thing.
  414. var bExpanded = e !== window && $(e).length != 0 && $(e).is('[aria-expanded]') && !$(e).is('[aria-controls]');
  415. var fOnShow = options.onShow, fOnHide = options.onHide;
  416. // a string onShow is handed back to dolPopup, which evaluates it; a string onHide has never been
  417. // supported on this path, since dolPopupAjax replaces onHide before dolPopup sees it
  418. var aExpanded = {};
  419. if(typeof fOnShow != 'string')
  420. aExpanded.onShow = function(oPopup) {
  421. if(bExpanded)
  422. $(e).attr('aria-expanded', 'true');
  423. // remembered on the popup: the hide handler dolPopup keeps is the one from the first show, whichever trigger opened it since
  424. oPopup.data('bx-popup-trigger', bExpanded ? $(e) : null);
  425. if(typeof fOnShow == 'function')
  426. fOnShow(oPopup);
  427. };
  428. if(typeof fOnHide != 'string')
  429. aExpanded.onHide = function(oPopup) {
  430. var oTrigger = oPopup.data('bx-popup-trigger');
  431. if(oTrigger)
  432. oTrigger.attr('aria-expanded', 'false');
  433. if(typeof fOnHide == 'function')
  434. fOnHide(oPopup);
  435. };
  436. var o = $.extend({}, $.fn.dolPopupDefaultOptions, {
  437. id: o,
  438. url: bx_append_url_params('menu.php', $.extend({o:o}, vars)),
  439. cssClass: 'bx-popup-menu'
  440. }, options, aExpanded);
  441. $(e).dolPopupAjax(o);
  442. }
  443. /**
  444. * Show pointer popup with menu from existing HTML.
  445. * @param jSel - jQuery selector for html to show in popup
  446. * @param e - element to show popup at
  447. * @param options - popup options
  448. */
  449. function bx_menu_popup_inline (jSel, e, options) {
  450. if ($(jSel + ':visible').length)
  451. $(jSel).dolPopupHide();
  452. else {
  453. var options = options || {};
  454. var bElement = e != undefined && $(e).length != 0;
  455. var bExpanded = bElement && $(e).is('[aria-expanded]');
  456. var fOnShow = options.onShow, fOnHide = options.onHide;
  457. var o = $.extend({}, $.fn.dolPopupDefaultOptions, options, {
  458. pointer: bElement ? {el:$(e)} : false,
  459. cssClass: 'bx-popup-menu',
  460. onShow: function(oPopup) {
  461. if(bExpanded)
  462. $(e).attr('aria-expanded', 'true');
  463. // remembered on the popup: the hide handler dolPopup keeps is the one from the first show, whichever trigger opened it since
  464. oPopup.data('bx-popup-trigger', bExpanded ? $(e) : null);
  465. oPopup.find('a').each(function () {
  466. $(this).off('click.bx-popup-menu');
  467. $(this).on('click.bx-popup-menu', function() {
  468. $(jSel).dolPopupHide();
  469. });
  470. });
  471. // keyboard focus leaving the menu (Shift+Tab from its first item) closes it, as Escape and an outer click do
  472. oPopup.off('focusout.bx-popup-menu').on('focusout.bx-popup-menu', function(oEvent) {
  473. var oTarget = oEvent.relatedTarget;
  474. if(!oTarget || oPopup.get(0).contains(oTarget) || (bElement && $(e).get(0).contains && $(e).get(0).contains(oTarget)))
  475. return;
  476. $(jSel).dolPopupHide();
  477. });
  478. if(typeof fOnShow == 'function')
  479. fOnShow(oPopup);
  480. },
  481. onHide: function(oPopup) {
  482. oPopup.off('focusout.bx-popup-menu');
  483. var oTrigger = oPopup.data('bx-popup-trigger');
  484. if(oTrigger && oTrigger.length)
  485. oTrigger.attr('aria-expanded', 'false');
  486. if(typeof fOnHide == 'function')
  487. fOnHide(oPopup);
  488. },
  489. });
  490. $(jSel).dolPopup(o);
  491. }
  492. }
  493. /**
  494. * Show pointer popup with menu from URL.
  495. * @param sObject - menu object name
  496. * @param oElement - element to show popup at
  497. * @param oOptions - popup options
  498. * @param oVars - additional GET variables
  499. */
  500. function bx_menu_slide (sObject, oElement, sPosition, oOptions, oVars) {
  501. var oVars = oVars || {};
  502. var oOptions = oOptions || {};
  503. oOptions = $.extend({}, {parent: 'body', container: '.bx-sliding-menu-content'}, oOptions);
  504. var sId = '';
  505. var sIdSel = '';
  506. var sIdPfx = 'bx-sliding-menu-wrapper-';
  507. if(typeof(oOptions.id) != 'undefined')
  508. switch(typeof(oOptions.id)) {
  509. case 'string':
  510. sId = sIdPfx + oOptions.id;
  511. break;
  512. case 'object':
  513. sId = typeof(oOptions.id.force) != 'undefined' && oOptions.id.force ? oOptions.id.value : sIdPfx + oOptions.id.value;
  514. break;
  515. }
  516. else
  517. sId = sIdPfx + parseInt(2147483647 * Math.random());
  518. sIdSel = '#' + sId;
  519. //--- If slider exists
  520. if ($(sIdSel).length) {
  521. bx_menu_slide_inline (sIdSel, oElement, sPosition);
  522. return;
  523. }
  524. //--- If slider doesn't exists
  525. else {
  526. var oMenuLoading = $('#bx-sliding-menu-loading');
  527. if(!oMenuLoading || !oMenuLoading.length)
  528. return;
  529. $('<div id="' + sId + '" style="display:none;">' + oMenuLoading.html() + '</div>').addClass(oMenuLoading.attr('class')).appendTo(oOptions.parent);
  530. var oLoading = $(sIdSel + ' .bx-sliding-menu-loading');
  531. bx_loading_content(oLoading, true, true);
  532. bx_menu_slide_inline (sIdSel, oElement, sPosition);
  533. var fOnLoad = function() {
  534. bx_loading_content(oLoading, false);
  535. $(sIdSel).bxProcessHtml().show();
  536. };
  537. $(sIdSel).find(oOptions.container).load(bx_append_url_params('menu.php', $.extend({o:sObject}, oVars)), function () {
  538. var f = function () {
  539. if($(sIdSel).find('img').length > 0 && !$(sIdSel).find('img').get(0).complete)
  540. $(sIdSel).find('img').load(fOnLoad);
  541. else
  542. fOnLoad();
  543. };
  544. setTimeout(f, 100); // TODO: better way is to check if item is animating before positioning it in the code where popup is positioning
  545. });
  546. }
  547. }
  548. /**
  549. * Show sliding menu from existing HTML.
  550. * @param sMenu - jQuery object or selector for html to show in popup
  551. * @param e - element to click to open/close slider
  552. * @param sPosition - 'block' for sliding menu in blocks, 'site' - for sliding main menu
  553. */
  554. function bx_menu_slide_inline (sMenu, e, sPosition) {
  555. var options = options || {};
  556. var eSlider = $(sMenu);
  557. if ('undefined' !== typeof (bx_menu_slide_inline_custom)) {
  558. return bx_menu_slide_inline_custom(sMenu, e, sPosition);
  559. }
  560. if ('undefined' == typeof(e))
  561. e = eSlider.data('data-control-btn');
  562. var eIcon = $(e).find('.sys-icon-a');
  563. var fPositionElement = function (oElement) {
  564. eSlider.css({
  565. position: 'absolute',
  566. top: oElement.outerHeight(true),
  567. left: 0
  568. });
  569. };
  570. var fPositionBlock = function () {
  571. var eBlock = eSlider.parents('.bx-page-block-container');
  572. eSlider.css({
  573. position: 'absolute',
  574. top: eBlock.find('.bx-db-header').outerHeight(true),
  575. left: 0
  576. //width: eBlock.width() TODO: Commited to test. Remove if everything is OK.
  577. });
  578. };
  579. var fPositionSite = function () {
  580. var eToolbar = $('#bx-toolbar');
  581. eSlider.css({
  582. position: 'fixed',
  583. top: eToolbar.outerHeight(true) + (typeof iToolbarSubmenuTopOffset != 'undefined' ? parseInt(iToolbarSubmenuTopOffset) : 0),
  584. left: 0
  585. });
  586. };
  587. var fClose = function () {
  588. if (eIcon.length)
  589. (new Marka(eIcon[0])).set(eIcon.attr('data-icon-orig'));
  590. eSlider.slideUp()
  591. };
  592. var fOpen = function () {
  593. if (eIcon.length) {
  594. eIcon.attr('data-icon-orig', eIcon.attr('data-icon'));
  595. (new Marka(eIcon[0])).set('times');
  596. }
  597. if(typeof sPosition == 'object')
  598. fPositionElement(sPosition);
  599. else if(typeof sPosition == 'string' && sPosition == 'block')
  600. fPositionBlock();
  601. else
  602. fPositionSite();
  603. eSlider.slideDown();
  604. eSlider.data('data-control-btn', e);
  605. };
  606. if ('undefined' == typeof(sPosition))
  607. sPosition = 'block';
  608. if (eSlider.is(':visible')) {
  609. if(typeof(bx_sound) == 'function')
  610. bx_sound('close', {emphasis: 'subtle'});
  611. fClose();
  612. $(e).focus();
  613. $(document).off('click.bx-sliding-menu touchend.bx-sliding-menu');
  614. $(window).off('resize.bx-sliding-menu');
  615. }
  616. else {
  617. bx_menu_slide_close_all_opened();
  618. $(document).off('click.bx-sliding-menu touchend.bx-sliding-menu');
  619. $(window).off('resize.bx-sliding-menu');
  620. // Some templates (Artificer) draw a sidebar instead and keep this call on the trigger: no menu, no sound.
  621. if(eSlider.length && typeof(bx_sound) == 'function')
  622. bx_sound('open', {emphasis: 'subtle'});
  623. fOpen();
  624. eSlider.find('.bx-popup-content a:first').focus();
  625. eSlider.find('a').each(function () {
  626. $(this).off('click.bx-sliding-menu');
  627. $(this).on('click.bx-sliding-menu', function () {
  628. fClose();
  629. });
  630. });
  631. setTimeout(function () {
  632. var iWidthPrev = $(window).width();
  633. $(window).on('resize.bx-sliding-menu', function () {
  634. if ($(this).width() == iWidthPrev)
  635. return;
  636. iWidthPrev = $(this).width();
  637. bx_menu_slide_close_all_opened();
  638. });
  639. $(document).on('click.bx-sliding-menu touchend.bx-sliding-menu', function (event) {
  640. if ($(event.target).parents('.bx-sliding-menu, .bx-sliding-menu-main, .bx-popup-slide-wrapper, .bx-db-header').length || $(event.target).filter('.bx-sliding-menu-main, .bx-popup-slide-wrapper, .bx-db-header').length || e === event.target)
  641. event.stopPropagation();
  642. else
  643. bx_menu_slide_close_all_opened();
  644. });
  645. }, 10);
  646. }
  647. }
  648. /**
  649. * Close all opened sliding menus
  650. */
  651. function bx_menu_slide_close_all_opened () {
  652. $('.bx-sliding-menu:visible, .bx-sliding-menu-main:visible, .bx-popup-slide-wrapper:visible').each(function () {
  653. bx_menu_slide_inline('#' + this.id);
  654. });
  655. }
  656. /*
  657. * Note. oData.count_old and oData.count_new are also available and can be checked or used in notification popup.
  658. */
  659. function bx_menu_show_live_update(oData) {
  660. var sSelectorAddon = '.bx-menu-item-addon';
  661. // Something arrived (a notification, a message, a request): one ready, however many counters went up.
  662. if(parseInt(oData.count_new) > parseInt(oData.count_old))
  663. bx_sound('ready');
  664. //--- Update Child Menu Item
  665. if(oData.mi_child) {
  666. var oMenuItem = $('.bx-menu-object-' + oData.mi_child.menu_object + ' .bx-menu-item-' + oData.mi_child.menu_item);
  667. var oMenuItemAddon = oMenuItem.find(sSelectorAddon);
  668. if(oMenuItemAddon.length > 0)
  669. oMenuItemAddon.html(oData.count_new);
  670. else
  671. oMenuItem.append(oData.code.replace('{count}', oData.count_new));
  672. //+++ Check for 0 value
  673. oMenuItemAddon = oMenuItem.find(sSelectorAddon);
  674. if(parseInt(oData.count_new) > 0)
  675. oMenuItemAddon.show();
  676. else
  677. oMenuItemAddon.hide();
  678. }
  679. //--- Update Parent Menu Item
  680. if(oData.mi_parent) {
  681. var oMenuItem = $('.bx-menu-object-' + oData.mi_parent.menu_object + ' .bx-menu-item-' + oData.mi_parent.menu_item);
  682. var oMenuItemAddon = oMenuItem.find(sSelectorAddon);
  683. var sMediaType = bx_check_mq();
  684. if(sMediaType == 'mobile')
  685. sMediaType = 'phone';
  686. var iSum = 0;
  687. $('.bx-media-' + sMediaType + ' .bx-menu-object-' + oData.mi_child.menu_object + ' .bx-menu-item:not(.bx-def-media-' + sMediaType + '-hide) .bx-menu-item-addon').each(function() {
  688. iValue = parseInt($(this).html());
  689. if(iValue && iValue > 0)
  690. iSum += iValue;
  691. });
  692. if(oMenuItemAddon.length > 0)
  693. oMenuItemAddon.html(iSum);
  694. else
  695. oMenuItem.append(oData.code.replace('{count}', iSum));
  696. //+++ Check for 0 value
  697. oMenuItemAddon = oMenuItem.find(sSelectorAddon);
  698. if(iSum > 0)
  699. oMenuItemAddon.show();
  700. else
  701. oMenuItemAddon.hide();
  702. }
  703. }
  704. function bx_menu_show_more_less(oLink, sMenu, sSelectorParent) {
  705. if(!sSelectorParent)
  706. sSelectorParent = 'ul';
  707. var sClass = 'bx-mi-hidden';
  708. $(oLink).parents(sSelectorParent + ':first').find('.bx-mi-aux, .bx-psmi-show-more, .bx-psmi-show-less').toggleClass(sClass);
  709. $.get(sUrlRoot + 'menu.php', {
  710. o: sMenu,
  711. a: 'set_collapsed',
  712. v: $(oLink).parents(sSelectorParent + ':first').find('.bx-mi-aux').hasClass(sClass) ? 1 : 0
  713. });
  714. }
  715. function bx_menu_toggle_contents(oLink, sSelectorParent) {
  716. if(!sSelectorParent)
  717. sSelectorParent = '.bx-menu-contents';
  718. var sClass = 'bx-m-collapsed';
  719. var oParent = $(oLink).parents(sSelectorParent + ':first');
  720. oParent.toggleClass(sClass);
  721. oParent.siblings('.bx-menu-content').toggle();
  722. return false;
  723. }
  724. function bx_menu_toggle(oLink, sMenu, sMenuItem, sSelectorParent) {
  725. if(!sSelectorParent)
  726. sSelectorParent = 'li';
  727. var sClass = 'bx-mi-collapsed';
  728. var oParent = $(oLink).parents(sSelectorParent + ':first');
  729. oParent.toggleClass(sClass);
  730. $.get(sUrlRoot + 'menu.php', {
  731. o: sMenu,
  732. a: 'set_collapsed_submenu',
  733. i: sMenuItem,
  734. v: oParent.hasClass(sClass) ? 1 : 0
  735. });
  736. return false;
  737. }
  738. function bx_menu_followings_load_more(oLink, sMenu, sContextModule, iStart, iPerPage) {
  739. var oLoadingParent = $(oLink).length ? $(oLink).parents('.bx-menu-item:first') : $('body');
  740. bx_loading(oLoadingParent, true);
  741. $.get(sUrlRoot + 'menu.php', {
  742. o: sMenu,
  743. a: 'load_more',
  744. v: sContextModule,
  745. start: iStart,
  746. per_page: iPerPage
  747. }, function(oData) {
  748. bx_loading(oLoadingParent, false);
  749. if(oData && oData.content != undefined)
  750. $(oLink).parents('.bx-menu-subitem:first').replaceWith(oData.content);
  751. }, 'json');
  752. }
  753. /**
  754. * Set ACL level for specified profile
  755. * @param iProfileId - profile id to set acl level for
  756. * @param iAclLevel - acl level id to assign to a given rofile
  757. */
  758. function bx_set_acl_level (iProfileId, iAclLevel, mixedLoadingElement) {
  759. var bBulk = !$.isNumeric(iProfileId);
  760. var iAclCard = !bBulk && $('#sys-acl-profile-' + iProfileId).length > 0 ? 1 : 0;
  761. var bLoading = typeof(mixedLoadingElement) != 'undefined';
  762. if(bLoading)
  763. bx_loading($(mixedLoadingElement), true);
  764. $.post(sUrlRoot + 'menu.php', {o:'sys_set_acl_level', profile_id: iProfileId, level_id: iAclLevel, card:iAclCard}, function(oData) {
  765. bx_on_set_acl_level(oData, mixedLoadingElement);
  766. }, 'json');
  767. }
  768. /**
  769. * Reset ACL level for specified profile to Standard.
  770. * @param iProfileId - profile id to reset acl level for
  771. */
  772. function bx_reset_acl_level (iProfileId, mixedLoadingElement) {
  773. var bBulk = !$.isNumeric(iProfileId);
  774. var iAclCard = !bBulk && $('#sys-acl-profile-' + iProfileId).length > 0 ? 1 : 0;
  775. var fPerform = function() {
  776. var bLoading = typeof(mixedLoadingElement) != 'undefined';
  777. if(bLoading)
  778. bx_loading($(mixedLoadingElement), true);
  779. $.post(sUrlRoot + 'menu.php', {o:'sys_set_acl_level', profile_id: iProfileId, level_id: 3, card: iAclCard}, function(oData) {
  780. bx_on_set_acl_level(oData, mixedLoadingElement);
  781. }, 'json');
  782. };
  783. bx_confirm(_t('_Are_you_sure'), fPerform);
  784. }
  785. function bx_on_set_acl_level(oData, oLoadingElement) {
  786. if(typeof(oLoadingElement) != 'undefined')
  787. bx_loading($(oLoadingElement), false);
  788. if(oData.msg != undefined && oData.msg.length) {
  789. bx_alert(oData.msg);
  790. return;
  791. }
  792. if($(oLoadingElement).hasClass('bx-popup-applied'))
  793. $(oLoadingElement).dolPopupHide().remove();
  794. if(typeof(oData.card) == 'object')
  795. for(var iField in oData.card) {
  796. var oCard = $(oData.card[iField]);
  797. $('#' + oCard.attr('id')).replaceWith(oCard.bxTime());
  798. }
  799. }
  800. /**
  801. * Set badge for specified content
  802. * @param sModule - current module for content
  803. * @param iContentId - content id to set badge for
  804. * @param iBadgeId - badge id to assign to a given content
  805. */
  806. function bx_set_badge (sModule, iContentId, iBadgeId, mixedLoadingElement) {
  807. var bLoading = typeof(mixedLoadingElement) != 'undefined';
  808. if(bLoading)
  809. bx_loading($(mixedLoadingElement), true);
  810. $.post(sUrlRoot + 'menu.php', {o:'sys_set_badges', module: sModule, content_id: iContentId, badge_id: iBadgeId}, function(oData) {
  811. if(bLoading)
  812. bx_loading($(mixedLoadingElement), false);
  813. if(oData.msg != undefined && oData.msg.length) {
  814. bx_alert(oData.msg);
  815. return;
  816. }
  817. if($(mixedLoadingElement).hasClass('bx-popup-applied'))
  818. $(mixedLoadingElement).dolPopupHide().remove();
  819. if(typeof(oData.card) == 'object')
  820. for(var iField in oData.card) {
  821. var oCard = $(oData.card[iField]);
  822. $('#' + oCard.attr('id')).replaceWith(oCard);
  823. }
  824. $('.bx-base-bages-container').html(oData.html);
  825. }, 'json');
  826. }
  827. function bx_get_notes(oSource, sModule, iContentId, oOptions, oVars) {
  828. var oOptions = oOptions || {};
  829. var oVars = oVars || {};
  830. $.post(
  831. sUrlRoot + 'modules/?r=' + sModule + '/get_notes/',
  832. $.extend({content_id: iContentId}, oVars),
  833. function(oData) {
  834. if(oData && oData.popup != undefined) {
  835. if(typeof(oData.popup) == 'string')
  836. oData.popup = {html: oData.popup, options: {}};
  837. oData.popup.options = $.extend({}, $.fn.dolPopupDefaultOptions, oData.popup.options, {
  838. id: sModule + '_notes_' + iContentId,
  839. closeOnOuterClick: false,
  840. removeOnClose: true,
  841. onBeforeShow: function(oPopup) {
  842. $(oPopup).find('.bx-popup-element-close').removeClass('bx-def-media-desktop-hide bx-def-media-tablet-hide');
  843. }
  844. }, oOptions);
  845. }
  846. processJsonData(oData);
  847. },
  848. 'json'
  849. );
  850. }
  851. function bx_approve(oSource, sModule, iContentId, oOptions, oVars) {
  852. var oOptions = oOptions || {};
  853. var oVars = oVars || {};
  854. var fPopupInit = function(oData) {
  855. if(oData && oData.popup != undefined) {
  856. if(typeof(oData.popup) == 'string')
  857. oData.popup = {html: oData.popup, options: {}};
  858. oData.popup.options = $.extend({}, $.fn.dolPopupDefaultOptions, oData.popup.options, {
  859. id: sModule + '_approve_' + iContentId,
  860. closeOnOuterClick: false,
  861. removeOnClose: true,
  862. onBeforeShow: function(oPopup) {
  863. $(oPopup).find('.bx-popup-element-close').removeClass('bx-def-media-desktop-hide bx-def-media-tablet-hide');
  864. var oForm = $(oPopup).find('form');
  865. if(oForm.length > 0) {
  866. oForm.ajaxForm({
  867. dataType: 'json',
  868. beforeSubmit: function(aFormData, oForm, oOptions) {
  869. bx_loading(oForm, true);
  870. },
  871. success: function(oData) {
  872. $('.bx-popup-applied:visible').dolPopupHide();
  873. fPopupInit(oData);
  874. processJsonData(oData);
  875. }
  876. });
  877. }
  878. }
  879. }, oOptions);
  880. }
  881. };
  882. $.post(
  883. sUrlRoot + 'modules/?r=' + sModule + '/approve/',
  884. $.extend({content_id: iContentId}, oVars),
  885. function(oData) {
  886. fPopupInit(oData);
  887. processJsonData(oData);
  888. },
  889. 'json'
  890. );
  891. }
  892. function bx_showcase_init(sSelWrapper, sSelItem) {
  893. var oWrapper = $(sSelWrapper);
  894. if(oWrapper.closest('.bx-popup-wrapper').length != 0)
  895. return;
  896. oWrapper.each(function() {
  897. var sClassEnabled = 'bx-sc-enabled';
  898. var oShowcase = $(this);
  899. var oCells = oShowcase.find(sSelItem);
  900. if(oCells.width() * oCells.length <= oShowcase.parent().width()) {
  901. if(oShowcase.hasClass('flickity-enabled'))
  902. oShowcase.flickity('destroy').removeClass(sClassEnabled);
  903. return;
  904. }
  905. if(oShowcase.hasClass('flickity-enabled'))
  906. return;
  907. var oShowcaseOptions = {
  908. cellSelector: sSelItem,
  909. cellAlign: 'left',
  910. imagesLoaded: true,
  911. wrapAround: true,
  912. pageDots: false
  913. };
  914. var iGroupCells = oShowcase.attr('bx-sc-group-cells');
  915. if(iGroupCells != undefined)
  916. oShowcaseOptions.groupCells = parseInt(iGroupCells);
  917. oShowcase.on('ready.flickity', function() {
  918. $(this).addClass(sClassEnabled);
  919. }).flickity(oShowcaseOptions);
  920. });
  921. }
  922. function validateLoginForm(eForm) {
  923. return true;
  924. }
  925. /**
  926. * convert <time> tags to human readable format
  927. * @param sLang - use sLang localization
  928. * @param isAutoupdate - for internal usage only
  929. */
  930. function bx_time(sLang, isAutoupdate, sRootSel) {
  931. if ('undefined' == typeof(sRootSel))
  932. sRootSel = 'body';
  933. var iAutoupdate = 22*60*60; // autoupdate time in realtime if less than 22 hours
  934. var sSel = 'time';
  935. if (typeof(sLang) != 'undefined')
  936. glBxTimeLang = sLang;
  937. else if(typeof(glBxTimeLang) != 'undefined')
  938. sLang = glBxTimeLang;
  939. if(!sLang)
  940. return;
  941. if ('undefined' != typeof(isAutoupdate) && isAutoupdate)
  942. sSel += '.bx-time-autoupdate';
  943. $(sRootSel).find(sSel).each(function () {
  944. var s;
  945. var sTime = $(this).attr('datetime');
  946. var iSecondsDiff = moment(sTime).unix() - moment().unix();
  947. if (iSecondsDiff < 0)
  948. iSecondsDiff = -iSecondsDiff;
  949. if (iSecondsDiff < $(this).attr('data-bx-autoformat'))
  950. s = moment(sTime).locale(sLang).fromNow(); // 'time ago' format
  951. else if (1 == $(this).attr('data-bx-utc'))
  952. s = moment.utc(sTime).locale(sLang).format($(this).attr('data-bx-format'));
  953. else
  954. s = moment(sTime).locale(sLang).format($(this).attr('data-bx-format'));
  955. if (iSecondsDiff < iAutoupdate)
  956. $(this).addClass('bx-time-autoupdate');
  957. else
  958. $(this).removeClass('bx-time-autoupdate');
  959. $(this).html(s);
  960. });
  961. if ($('time.bx-time-autoupdate').size()) {
  962. setTimeout(function () {
  963. bx_time(sLang, true);
  964. }, 20000);
  965. }
  966. }
  967. (function($) {
  968. $.fn.bxTime = function() {
  969. bx_time(undefined, undefined, this);
  970. return this;
  971. };
  972. /**
  973. * process HTML which was added dynamicly on the page
  974. */
  975. $.fn.bxProcessHtml = function (oCallback) {
  976. var eElement = $(this);
  977. if ('undefined' !== typeof(eElement)) {
  978. // process js time
  979. eElement.bxTime();
  980. bx_process_links();
  981. // process web forms
  982. if ($.isFunction($.fn.addWebForms))
  983. eElement.addWebForms();
  984. // process animating icons
  985. bx_activate_anim_icons();
  986. // process syntax hightlighing
  987. if ('undefined' !== typeof(Prism) && eElement[0] && eElement[0].querySelectorAll)
  988. Prism.highlightAllUnder(eElement[0]);
  989. // process links
  990. bx_redirect_for_external_links(eElement);
  991. // flickity update
  992. setTimeout(function () {
  993. const oFlickity = eElement.find('.flickity-enabled');
  994. if (oFlickity.length)
  995. oFlickity.flickity('resize');
  996. }, 250);
  997. }
  998. if ('undefined' !== typeof(oCallback)) {
  999. oCallback(eElement);
  1000. }
  1001. if (typeof glOnProcessHtml !== 'undefined' && glOnProcessHtml instanceof Array) {
  1002. for (var i = 0; i < glOnProcessHtml.length; i++)
  1003. if (typeof glOnProcessHtml[i] === "function")
  1004. glOnProcessHtml[i](this);
  1005. }
  1006. return this;
  1007. };
  1008. $.fn.bxCheckOverflowHeight = function(sClass, onFind) {
  1009. if(!sClass)
  1010. sClass = 'bx-overflow';
  1011. return this.each(function() {
  1012. var oElement = $(this);
  1013. if(oElement.hasClass(sClass) || oElement.css('overflow') != 'hidden')
  1014. return;
  1015. if(oElement.find('img').length > 0) {
  1016. var oImg = oElement.find('img:first').get(0);
  1017. const oImgCopy = new Image();
  1018. oImgCopy.src = oImg.src;
  1019. oImgCopy.onload = function() {
  1020. var iHeightParent = oElement.height();
  1021. var iHeightImg = oImg.clientHeight;
  1022. var iOffsetTopImg = oImg.offsetTop;
  1023. if(iOffsetTopImg < iHeightParent && iOffsetTopImg + iHeightImg > iHeightParent)
  1024. oElement.css('max-height', (iOffsetTopImg + iHeightImg + 50) + 'px');
  1025. if(oElement.prop('scrollHeight') <= Math.ceil(oElement.height()))
  1026. return;
  1027. oElement.addClass(sClass);
  1028. if(typeof onFind === 'function')
  1029. onFind(oElement);
  1030. }
  1031. }
  1032. else {
  1033. if(oElement.prop('scrollHeight') <= Math.ceil(oElement.height()))
  1034. return;
  1035. oElement.addClass(sClass);
  1036. if(typeof onFind === 'function')
  1037. onFind(oElement);
  1038. }
  1039. });
  1040. };
  1041. $.fn.bxConvertEmbeds = function() {
  1042. return this.each(function() {
  1043. $(this).find(".bx-embed-link").each(function() {
  1044. var oItem = $(this);
  1045. oItem.html(oItem.attr('source'));
  1046. $.getJSON(sUrlRoot + '/embed.php?', {a: 'get_link', l: oItem.attr('source')}, function(oData) {
  1047. oItem.html(oData.code)
  1048. if(oItem.find('a').length > 0)
  1049. bx_embed_link(oItem.find('a')[0]);
  1050. });
  1051. });
  1052. });
  1053. };
  1054. } (jQuery));
  1055. /**
  1056. * Perform connections AJAX request.
  1057. * In case of error - it shows js alert with error message.
  1058. * In case of success - the page is reloaded.
  1059. *
  1060. * @param sObj - connections object
  1061. * @param sAction - 'add', 'remove' or 'reject'
  1062. * @param iContentId - content id, initiator is always current logged in user
  1063. * @param bConfirm - show confirmation dialog
  1064. */
  1065. function bx_conn_action(e, sObj, sAction, iContentId, bConfirm, fOnComplete) {
  1066. var fPerform = function() {
  1067. var aParams = {
  1068. obj: sObj,
  1069. act: sAction,
  1070. id: iContentId
  1071. };
  1072. var fCallback = function (data) {
  1073. bx_loading_btn(e, 0);
  1074. if ('object' != typeof(data))
  1075. return;
  1076. if (data.err) {
  1077. bx_alert(data.msg);
  1078. } else {
  1079. if ('function' == typeof(fOnComplete))
  1080. fOnComplete(data, e);
  1081. else if (!loadDynamicBlockAuto(e))
  1082. location.reload();
  1083. else
  1084. $('#bx-popup-ajax-wrapper-bx_persons_view_actions_more').remove();
  1085. }
  1086. };
  1087. bx_loading_btn(e, 1);
  1088. $.ajax({
  1089. dataType: 'json',
  1090. url: sUrlRoot + 'conn.php',
  1091. data: aParams,
  1092. type: 'POST',
  1093. success: fCallback
  1094. });
  1095. };
  1096. if (typeof(bConfirm) != 'undefined' && bConfirm)
  1097. bx_confirm(_t('_Are_you_sure'), fPerform);
  1098. else
  1099. fPerform();
  1100. }
  1101. /**
  1102. * Perform recommendations AJAX request.
  1103. * In case of error - it shows js alert with error message.
  1104. * In case of success - the page is reloaded.
  1105. *
  1106. * @param sObj - recommendation object
  1107. * @param sAction - 'ignore'
  1108. * @param iContentId - content id, initiator is always current logged in user
  1109. * @param bConfirm - show confirmation dialog
  1110. */
  1111. function bx_recommendation_action(e, sObj, sAction, iContentId, bConfirm, fOnComplete) {
  1112. var fPerform = function() {
  1113. var aParams = {
  1114. obj: sObj,
  1115. act: sAction,
  1116. id: iContentId
  1117. };
  1118. var fCallback = function (data) {
  1119. bx_loading_btn(e, 0);
  1120. if ('object' != typeof(data))
  1121. return;
  1122. if (data.msg)
  1123. bx_alert(data.msg);
  1124. else {
  1125. if ('function' == typeof(fOnComplete))
  1126. fOnComplete(data, e);
  1127. else if (!loadDynamicBlockAuto(e))
  1128. location.reload();
  1129. }
  1130. };
  1131. bx_loading_btn(e, 1);
  1132. $.ajax({
  1133. dataType: 'json',
  1134. url: sUrlRoot + 'recommendation.php',
  1135. data: aParams,
  1136. type: 'POST',
  1137. success: fCallback
  1138. });
  1139. };
  1140. if (typeof(bConfirm) != 'undefined' && bConfirm)
  1141. bx_confirm(_t('_Are_you_sure'), fPerform);
  1142. else
  1143. fPerform();
  1144. }
  1145. function bx_append_url_params (sUrl, mixedParams) {
  1146. var sParams = sUrl.indexOf('?') == -1 ? '?' : '&';
  1147. var sHash = '';
  1148. if(sUrl.indexOf('#') != -1) {
  1149. var oUrl = new URL(sUrl);
  1150. if(oUrl.hash.length > 0) {
  1151. sHash = oUrl.hash;
  1152. sUrl = sUrl.replace(sHash, '');
  1153. }
  1154. else
  1155. sUrl = sUrl.replace('#', '');
  1156. }
  1157. if(mixedParams instanceof Array) {
  1158. for(var i in mixedParams)
  1159. sParams += i + '=' + mixedParams[i] + '&';
  1160. sParams = sParams.substr(0, sParams.length-1);
  1161. }
  1162. else if(mixedParams instanceof Object) {
  1163. $.each(mixedParams, function(sKey, sValue) {
  1164. sParams += sKey + '=' + sValue + '&';
  1165. });
  1166. sParams = sParams.substr(0, sParams.length-1);
  1167. }
  1168. else
  1169. sParams += mixedParams;
  1170. return sUrl + sParams + sHash;
  1171. }
  1172. function bx_search_extnded_sort(obj, url){
  1173. location.href = url + '&sort=' +$(obj).val();
  1174. }
  1175. function bx_search_on_type (e, n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults, iMinLen, onComplete, onCancel) {
  1176. var oForm = $(e.target).parents(sFormSel + ':first');
  1177. if ('undefined' != typeof(e)) {
  1178. if(e.keyCode == 13) {
  1179. oForm.find('input[name=live_search]').val(0);
  1180. oForm.submit();
  1181. return false;
  1182. }
  1183. if(e.keyCode == 27) {
  1184. if(typeof onCancel === 'function')
  1185. onCancel(oForm);
  1186. }
  1187. }
  1188. iMinLen = typeof(iMinLen) != 'undefined' ? parseInt(iMinLen) : 0;
  1189. if(iMinLen > 0 && iMinLen > (oForm.find('input[name=keyword]').val().length + 1))
  1190. return true;
  1191. if ('undefined' != typeof(glBxSearchTimeoutHandler) && glBxSearchTimeoutHandler)
  1192. clearTimeout(glBxSearchTimeoutHandler);
  1193. glBxSearchTimeoutHandler = setTimeout(function () {
  1194. bx_search (n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults, onComplete);
  1195. }, 500);
  1196. return true;
  1197. }
  1198. function bx_search (n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults, onComplete) {
  1199. if ('undefined' == typeof(sLoadingContSel))
  1200. sLoadingContSel = sResultsContSel;
  1201. if ('undefined' == typeof(bSortResults))
  1202. bSortResults = false;
  1203. var sQuery = $('input', sFormSel).serialize();
  1204. bx_loading($(sLoadingContSel), true);
  1205. $.post(sUrlRoot + 'searchKeywordContent.php', sQuery, function(data) {
  1206. bx_loading($(sLoadingContSel), false);
  1207. if(!data)
  1208. return;
  1209. if (bSortResults) {
  1210. var sAsk = $(data).filter(".bx-btn.sys-agents-ask");
  1211. var aSortedUnits = $(data).find(".bx-def-unit-live-search").toArray().sort(function (a, b) {
  1212. return b.getAttribute('data-ts') - a.getAttribute('data-ts');
  1213. });
  1214. data = '';
  1215. if(sAsk.length)
  1216. data += sAsk.get(0).outerHTML;
  1217. $.each(aSortedUnits.slice(0, n), function (i, e) {
  1218. data += e.outerHTML;
  1219. });
  1220. }
  1221. var oContainer = $(sResultsContSel);
  1222. oContainer.html(data).bxProcessHtml();
  1223. if(typeof onComplete === 'function')
  1224. onComplete(oContainer, data);
  1225. });
  1226. return false;
  1227. }
  1228. function on_filter_apply(e, sInputId, sFilterName)
  1229. {
  1230. var oRegExp = new RegExp("[&]{0,1}" + sFilterName + "=.*");
  1231. var s = ('' + document.location).replace(oRegExp, ''); // remove filter
  1232. s = s.replace(/page=\d+/, 'page=1'); // goto 1st page
  1233. if (e.checked && $('#' + sInputId).val().length > 2)
  1234. s += (-1 == s.indexOf('?') ? '?' : '&') + sFilterName + '=' + $('#' + sInputId).val(); // append filter
  1235. document.location = s;
  1236. }
  1237. function on_filter_key_up (e, sCheckboxId)
  1238. {
  1239. if (13 == e.keyCode) {
  1240. $('#' + sCheckboxId).click();
  1241. return false;
  1242. }
  1243. else {
  1244. $('#' + sCheckboxId).removeAttr('checked');
  1245. return true;
  1246. }
  1247. }
  1248. function on_copyright_click()
  1249. {
  1250. oDate = new Date();
  1251. $(document).dolPopupAlert({
  1252. message: _t('_copyright', oDate.getFullYear())
  1253. });
  1254. }
  1255. function bx_activate_anim_icons(sColor)
  1256. {
  1257. if(typeof(sColor) != 'undefined')
  1258. glBxAnimIconColor = sColor;
  1259. else if(typeof(glBxAnimIconColor) != 'undefined')
  1260. sColor = glBxAnimIconColor;
  1261. if(!sColor)
  1262. return;
  1263. $('.sys-icon-a').not('.marka').each(function () {
  1264. var e = $(this);
  1265. var m = new Marka(e.get(0)), r = e.attr('data-rotate'), c = e.attr('data-color');
  1266. m.set(e.attr('data-icon')).rotate(r ? r : 'down').color(c ? c : sColor);
  1267. });
  1268. }
  1269. function bx_get_param(s) {
  1270. if (!window.aDolOptions || !aDolOptions[s])
  1271. return false;
  1272. return aDolOptions[s];
  1273. }
  1274. function bx_autocomplete_fields(iId, sUrl, sName, bShowImg, bOnlyOnce, onSelect){
  1275. if (!$('#' + iId).hasClass('bx-form-input-autotoken'))
  1276. $('#' + iId).addClass('bx-form-input-autotoken');
  1277. $('#' + iId + ' input[type=text]').autocomplete({
  1278. source: sUrl,
  1279. focus: function(e, ui ) {
  1280. $(this).val(ui.item.label);
  1281. e.preventDefault();
  1282. },
  1283. select: function(e, ui) {
  1284. $(this).val(ui.item.label);
  1285. $(this).trigger('superselect', ui.item);
  1286. if (typeof onSelect === 'function')
  1287. onSelect(ui);
  1288. e.preventDefault();
  1289. }
  1290. });
  1291. if ($('#' + iId + ' .val').length > 0 && bOnlyOnce)
  1292. $('#' + iId + ' input[type=text]').hide();
  1293. $('#' + iId + ' input[type=text]').on('superselect', function(e, item) {
  1294. $(this).hide();
  1295. if ('undefined' != typeof(item)) {
  1296. var sImage = '';
  1297. if(bShowImg && item.unit != undefined)
  1298. sImage = item.unit;
  1299. else if(bShowImg && item.thumb != undefined)
  1300. sImage = '<img class="bx-def-thumb bx-def-icon-size bx-def-margin-sec-right" src="' + item.thumb + '">';
  1301. $(this).before('<b class="val bx-def-color-bg-hl bx-def-round-corners">' + sImage + item.label + '<i class="sys-icon times ltr:ml-1 rtl:mr-1"></i><input type="hidden" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" name="' + sName + (bOnlyOnce ? '' : '[]') + '" value="' + item.value + '" /></b>');
  1302. }
  1303. if (!bOnlyOnce)
  1304. {
  1305. fAutoShrinkInput();
  1306. $(this).show();
  1307. this.value = '';
  1308. }
  1309. }).on('keydown', function(e) {
  1310. // if: comma,enter (delimit more keyCodes with | pipe)
  1311. if (/(13)/.test(e.which))
  1312. e.preventDefault();
  1313. });
  1314. $('#' + iId).on('click', 'b', function() {
  1315. $(this).remove();
  1316. if (bOnlyOnce)
  1317. $('#' + iId + ' input[type=text]').val('').show();
  1318. else
  1319. fAutoShrinkInput();
  1320. });
  1321. fAutoShrinkInput = function () {
  1322. var iWidthCont = $('#' + iId + '.bx-form-input-autotoken').innerWidth();
  1323. var iWidthExisting = 0;
  1324. $('#' + iId + '.bx-form-input-autotoken b').each(function () {
  1325. iWidthExisting += $(this).outerWidth(true);
  1326. });
  1327. $('#' + iId + '.bx-form-input-autotoken input').innerWidth(parseInt(iWidthCont - iWidthExisting > 180 ? iWidthCont - iWidthExisting : 180) - 5);
  1328. };
  1329. if (!bOnlyOnce)
  1330. fAutoShrinkInput();
  1331. else
  1332. $('#' + iId + '.bx-form-input-autotoken input').outerWidth('100%');
  1333. };
  1334. function bx_alert(sMessage, fOnClickOk, oParams)
  1335. {
  1336. $(document).dolPopupAlert({
  1337. message: sMessage,
  1338. onClickOk: fOnClickOk,
  1339. params: oParams
  1340. });
  1341. }
  1342. function bx_confirm(sMessage, fOnClickYes, fOnClickNo, oParams)
  1343. {
  1344. $(document).dolPopupConfirm({
  1345. message: sMessage,
  1346. onClickYes: fOnClickYes,
  1347. onClickNo: fOnClickNo,
  1348. params: oParams
  1349. });
  1350. }
  1351. function bx_prompt(sMessage, sValue, fOnClickOk, fOnClickCancel, oParams)
  1352. {
  1353. $(document).dolPopupPrompt({
  1354. message: sMessage,
  1355. value: sValue,
  1356. onClickOk: fOnClickOk,
  1357. onClickCancel: fOnClickCancel,
  1358. params: oParams
  1359. });
  1360. }
  1361. function bx_toast(sMessage, oParams)
  1362. {
  1363. var oDate = new Date();
  1364. var sId = 'bx-popup-toast';
  1365. var sIdClone = sId + '-' + oDate.getTime();
  1366. var oHolder = $('#' + sId + '-holder');
  1367. var oAlert = oHolder.find('#' + sId).clone();
  1368. oAlert.attr('id', sIdClone).find('.bx-pt-text').html(sMessage);
  1369. oHolder.append(oAlert.bx_anim('show', 'fade', 'slow', function() {
  1370. if(oParams && typeof(oParams.onShow) == 'function')
  1371. oParams.onShow();
  1372. }));
  1373. setTimeout(function() {
  1374. $('#' + sIdClone).bx_anim('hide', 'fade', 'slow', function() {
  1375. $(this).remove();
  1376. if(oParams && typeof(oParams.onHide) == 'function')
  1377. oParams.onHide();
  1378. })
  1379. }, 1000 * (oParams && oParams.autoHide !== undefined ? parseInt(oParams.autoHide) : 3));
  1380. };
  1381. function bx_is_color_scheme_dark()
  1382. {
  1383. return localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
  1384. }
  1385. function bx_get_color_scheme()
  1386. {
  1387. return 'theme' in localStorage ? localStorage.theme : 'auto';
  1388. }
  1389. function bx_set_color_scheme(iCode)
  1390. {
  1391. switch(iCode) {
  1392. case 0:
  1393. localStorage.removeItem('theme');
  1394. break;
  1395. case 1:
  1396. localStorage.theme = 'sun'
  1397. break;
  1398. case 2:
  1399. localStorage.theme = 'dark'
  1400. break;
  1401. }
  1402. //--- Change scheme.
  1403. bx_set_color_scheme_html();
  1404. //--- Notify global handlers.
  1405. if(typeof glOnColorSchemeChange === 'undefined' || !(glOnColorSchemeChange instanceof Array))
  1406. return;
  1407. if(typeof iCode === 'undefined' || !iCode)
  1408. iCode = bx_is_color_scheme_dark() ? 2 : 1;
  1409. for(var i = 0; i < glOnColorSchemeChange.length; i++)
  1410. if(typeof glOnColorSchemeChange[i] === 'function')
  1411. glOnColorSchemeChange[i](iCode);
  1412. }
  1413. function bx_set_color_scheme_html()
  1414. {
  1415. if(bx_is_color_scheme_dark())
  1416. $('html').addClass('dark')
  1417. else
  1418. $('html').removeClass('dark')
  1419. }
  1420. /*
  1421. * Interface sounds are Cuelume's cues (plugins_public/cuelume, MIT): one cue per
  1422. * interface job, picked by the "Which cue?" table at https://cuelume.dev/agents.md.
  1423. * Controls carry data-cuelume-* attributes, which Cuelume's bind() plays as they
  1424. * are pressed; outcomes call bx_sound(). Nothing plays on hover, on page load, or
  1425. * per item of a burst.
  1426. *
  1427. * Settings > Global > Sounds (BxDolSounds, passed to bx_sound_init) picks where
  1428. * sounds play, the sound set, the volume, a set or silence for each kind of action,
  1429. * and the level people start at. Each visitor picks their own level for this
  1430. * browser: full, quiet or off (bx_sound_set_level). The module loads only once a
  1431. * sound can play.
  1432. */
  1433. var glBxSounds = {url: '', config: null, module: null, cuelume: null, readyAt: 0, restore: null};
  1434. /**
  1435. * The kind of action each cue belongs to (its set or silence in the settings). Likes, reactions, ratings, favorites
  1436. * and follows say so themselves: group 'reactions' in bx_sound(), or data-bx-sound-group on a control.
  1437. */
  1438. var aBxSoundGroups = {tap: 'buttons', select: 'choices', toggle: 'choices', open: 'menus', close: 'menus', navigate: 'navigation', loading: 'navigation', success: 'results', warning: 'results', error: 'results', ready: 'notifications', attention: 'notifications'};
  1439. /**
  1440. * The attributes bind() plays, and the cue each one plays when it doesn't name another.
  1441. */
  1442. var aBxSoundAttributes = {'data-cuelume-tap': 'tap', 'data-cuelume-select': 'select', 'data-cuelume-toggle': 'toggle', 'data-cuelume-open': 'open', 'data-cuelume-close': 'close', 'data-cuelume-navigate': 'navigate'};
  1443. /**
  1444. * The cue Settings > Sounds plays for each kind of action as it is changed.
  1445. */
  1446. var aBxSoundSamples = {buttons: 'tap', choices: 'toggle', reactions: 'toggle', menus: 'open', navigation: 'navigate', results: 'success', notifications: 'ready'};
  1447. /**
  1448. * Quiet plays at this share of the volume.
  1449. */
  1450. var BX_SOUND_QUIET = 0.4;
  1451. function bx_sound_init(sUrl, oConfig)
  1452. {
  1453. glBxSounds.url = sUrl;
  1454. glBxSounds.config = oConfig;
  1455. // A module that fails to load leaves the page silent; nothing else depends on it.
  1456. if(bx_sound_is_on())
  1457. bx_sound_load().catch(function() {});
  1458. $(document).ready(function() {
  1459. bx_sound_mark_switches();
  1460. });
  1461. }
  1462. function bx_sound_load()
  1463. {
  1464. if(!glBxSounds.url)
  1465. return Promise.reject(new Error('Interface sounds are not set up on this page.'));
  1466. if(!glBxSounds.module)
  1467. glBxSounds.module = import(glBxSounds.url).then(function(oCuelume) {
  1468. glBxSounds.cuelume = oCuelume;
  1469. bx_sound_apply();
  1470. // Ahead of bind(), which listens on document: the kind of action decides how this one press sounds.
  1471. ['pointerdown', 'click', 'keydown', 'keyup', 'change'].forEach(function(sEvent) {
  1472. window.addEventListener(sEvent, bx_sound_gate, true);
  1473. });
  1474. oCuelume.bind();
  1475. return oCuelume;
  1476. });
  1477. return glBxSounds.module;
  1478. }
  1479. /**
  1480. * The visitor's level: their own pick in this browser, or where the settings start everyone; off where sounds are off.
  1481. */
  1482. function bx_sound_level()
  1483. {
  1484. var oConfig = glBxSounds.config;
  1485. if(!oConfig || !oConfig.on)
  1486. return 'off';
  1487. try {
  1488. var sLevel = localStorage.getItem('bx-sounds');
  1489. if(sLevel == 'full' || sLevel == 'quiet' || sLevel == 'off')
  1490. return sLevel;
  1491. }
  1492. catch(e) {}
  1493. return oConfig.level;
  1494. }
  1495. function bx_sound_is_on()
  1496. {
  1497. return bx_sound_level() != 'off';
  1498. }
  1499. function bx_sound_volume()
  1500. {
  1501. return glBxSounds.config.volume * (bx_sound_level() == 'quiet' ? BX_SOUND_QUIET : 1);
  1502. }
  1503. /**
  1504. * The set a kind of action plays: its own, the main one, or 'off'.
  1505. */
  1506. function bx_sound_set(sGroup)
  1507. {
  1508. var oGroups = glBxSounds.config.groups || {};
  1509. return sGroup && oGroups[sGroup] ? oGroups[sGroup] : glBxSounds.config.set;
  1510. }
  1511. /**
  1512. * Cuelume's settings for this page. A press that needs other ones changes them for itself (bx_sound_gate).
  1513. */
  1514. function bx_sound_apply()
  1515. {
  1516. glBxSounds.restore = null;
  1517. if(!glBxSounds.cuelume)
  1518. return;
  1519. glBxSounds.cuelume.setTheme(glBxSounds.config.set);
  1520. glBxSounds.cuelume.setVolume(bx_sound_volume());
  1521. }
  1522. /**
  1523. * Runs before bind() for each event that can press a marked control: a kind of action with a set of its own plays in
  1524. * it, and one that is off, or a visitor who turned sounds off, plays nothing. Cuelume reads the set and volume as it
  1525. * plays, so they are back to the page's own on the next tick.
  1526. */
  1527. function bx_sound_gate(oEvent)
  1528. {
  1529. if(!glBxSounds.cuelume || !(oEvent.target instanceof Element))
  1530. return;
  1531. var oControl = oEvent.target.closest(Object.keys(aBxSoundAttributes).map(function(sAttribute) {
  1532. return '[' + sAttribute + ']';
  1533. }).join(','));
  1534. if(!oControl)
  1535. return;
  1536. var sSet = bx_sound_is_on() ? bx_sound_set(bx_sound_group(oControl)) : 'off';
  1537. if(sSet == glBxSounds.config.set)
  1538. return;
  1539. if(sSet == 'off')
  1540. glBxSounds.cuelume.setVolume(0);
  1541. else
  1542. glBxSounds.cuelume.setTheme(sSet);
  1543. if(!glBxSounds.restore)
  1544. glBxSounds.restore = setTimeout(bx_sound_apply, 0);
  1545. }
  1546. /**
  1547. * The kind of action of a marked control: data-bx-sound-group on it or around it, otherwise the one of its cue.
  1548. */
  1549. function bx_sound_group(oControl)
  1550. {
  1551. var oGroup = oControl.closest('[data-bx-sound-group]');
  1552. if(oGroup)
  1553. return oGroup.getAttribute('data-bx-sound-group');
  1554. for(var sAttribute in aBxSoundAttributes) {
  1555. if(!oControl.hasAttribute(sAttribute))
  1556. continue;
  1557. var sCue = oControl.getAttribute(sAttribute);
  1558. return aBxSoundGroups[aBxSoundGroups[sCue] ? sCue : aBxSoundAttributes[sAttribute]];
  1559. }
  1560. return '';
  1561. }
  1562. /**
  1563. * Play the cue for an outcome, e.g. bx_sound('success', {emphasis: 'subtle'}) once a form is saved. `group` names
  1564. * the kind of action when the cue alone doesn't ('reactions' for a like). Arrivals ('ready') that come together,
  1565. * like several live updates in one poll, play once.
  1566. */
  1567. function bx_sound(sCue, oOptions)
  1568. {
  1569. if(!bx_sound_is_on())
  1570. return;
  1571. var sSet = bx_sound_set(oOptions && oOptions.group ? oOptions.group : aBxSoundGroups[sCue]);
  1572. if(sSet == 'off')
  1573. return;
  1574. if(sCue == 'ready') {
  1575. var iNow = Date.now();
  1576. if(iNow - glBxSounds.readyAt < 2000)
  1577. return;
  1578. glBxSounds.readyAt = iNow;
  1579. }
  1580. bx_sound_load().then(function(oCuelume) {
  1581. oCuelume.play(sCue, $.extend({}, oOptions, {theme: sSet}));
  1582. }, function() {});
  1583. }
  1584. /**
  1585. * The reply to an AJAX form: error when UNA refuses it (a non-zero code, or the form or a popup back with a field
  1586. * error), a subtle success when it says the form went through (code 0, a redirect, a reload or a script to run).
  1587. * A reply that names its own sound (`cue`) gets only that one, from processJsonData; a reply that only carries a
  1588. * message plays nothing, since a message alone can say either.
  1589. */
  1590. function bx_sound_form_reply(oData)
  1591. {
  1592. if(!oData || oData.cue != undefined)
  1593. return;
  1594. var mixedPopup = oData.popup && typeof(oData.popup) == 'object' ? oData.popup.html : oData.popup;
  1595. if((oData.code != undefined && Number.parseInt(oData.code) != 0) || bx_sound_has_field_error(oData.form) || bx_sound_has_field_error(mixedPopup))
  1596. bx_sound('error');
  1597. else if(oData.code != undefined || oData.redirect != undefined || oData.reload != undefined || oData.eval != undefined)
  1598. bx_sound('success', {emphasis: 'subtle'});
  1599. }
  1600. /**
  1601. * Whether a form's HTML came back with a field error in it.
  1602. */
  1603. function bx_sound_has_field_error(sHtml)
  1604. {
  1605. return typeof(sHtml) == 'string' && $('<div>' + sHtml + '</div>').find('.bx-form-warn').toArray().some(function(oWarn) {
  1606. return oWarn.textContent.trim().length > 0;
  1607. });
  1608. }
  1609. /**
  1610. * The visitor's level of interface sounds in this browser: 'full', 'quiet' or 'off'. A new level that can be heard
  1611. * answers from the same click, which also lets the browser start audio; off is heard as silence.
  1612. */
  1613. function bx_sound_set_level(sLevel)
  1614. {
  1615. if(!glBxSounds.config || !glBxSounds.config.on)
  1616. return;
  1617. var aLevels = ['off', 'quiet', 'full'];
  1618. var iFrom = aLevels.indexOf(bx_sound_level());
  1619. try {
  1620. localStorage.setItem('bx-sounds', sLevel);
  1621. }
  1622. catch(e) {}
  1623. bx_sound_mark_switches();
  1624. if(sLevel == 'off') {
  1625. // Whatever still rings stops at once; the module stays ready for the next level.
  1626. if(glBxSounds.cuelume) {
  1627. glBxSounds.cuelume.setEnabled(false);
  1628. glBxSounds.cuelume.setEnabled(true);
  1629. }
  1630. return;
  1631. }
  1632. bx_sound_load().then(function() {
  1633. bx_sound_apply();
  1634. bx_sound('toggle', {direction: aLevels.indexOf(sLevel) > iFrom ? 'forward' : 'back'});
  1635. }, function() {});
  1636. }
  1637. /**
  1638. * The account menu's Sounds item steps through the levels: full, quiet, off.
  1639. */
  1640. function bx_sound_toggle(oSwitch)
  1641. {
  1642. bx_sound_set_level({full: 'quiet', quiet: 'off', off: 'full'}[bx_sound_level()]);
  1643. }
  1644. /**
  1645. * Show the visitor's level on every Sounds control: the account menu item's name and icon (volume-up, volume-down,
  1646. * volume-mute; the Lucide iconset redraws it from a new <i>), and the checked option of Studio's radio group. Where
  1647. * sounds are off for everyone, the item goes away.
  1648. */
  1649. function bx_sound_mark_switches()
  1650. {
  1651. var oConfig = glBxSounds.config;
  1652. var sLevel = bx_sound_level();
  1653. var sIcon = {full: 'volume-up', quiet: 'volume-down', off: 'volume-mute'}[sLevel];
  1654. var bRedraw = false;
  1655. $('[data-bx-sounds-switch]').each(function() {
  1656. if(!oConfig || !oConfig.on) {
  1657. $(this).closest('li').hide();
  1658. return;
  1659. }
  1660. $(this).attr({'aria-label': oConfig.labels[sLevel], title: oConfig.labels[sLevel]});
  1661. $(this).find('.sys-icon.volume-up, .sys-icon.volume-down, .sys-icon.volume-mute').each(function() {
  1662. if($(this).hasClass(sIcon))
  1663. return;
  1664. if(this.tagName.toLowerCase() == 'i')
  1665. $(this).removeClass('volume-up volume-down volume-mute').addClass(sIcon);
  1666. else {
  1667. $(this).replaceWith($('<i>').addClass('sys-icon ' + sIcon));
  1668. bRedraw = true;
  1669. }
  1670. });
  1671. });
  1672. $('[data-bx-sounds-level]').each(function() {
  1673. var bChecked = $(this).attr('data-bx-sounds-level') == sLevel;
  1674. $(this).attr('aria-checked', bChecked ? 'true' : 'false').attr('tabindex', bChecked ? '0' : '-1');
  1675. });
  1676. if(bRedraw && typeof(bx_iconset_init_lucide) == 'function')
  1677. bx_iconset_init_lucide();
  1678. }
  1679. /**
  1680. * Settings > Sounds: a choice plays as it is made, with the set and volume picked in the form, before it is saved and
  1681. * even while sounds are off here.
  1682. */
  1683. function bx_sound_preview(oSelect)
  1684. {
  1685. var oForm = $(oSelect).closest('form');
  1686. var fValue = function(sName) {
  1687. return oForm.find('[name="sys_sounds_' + sName + '"]').val();
  1688. };
  1689. var sOption = $(oSelect).attr('data-bx-sound-option');
  1690. var sValue = $(oSelect).val();
  1691. var sSet = fValue('set') || 'default';
  1692. var fVolume = (parseInt(fValue('volume')) || 100) / 100;
  1693. var sCue = 'toggle';
  1694. if(sOption == 'level') {
  1695. if(sValue == 'off')
  1696. return;
  1697. if(sValue == 'quiet')
  1698. fVolume *= BX_SOUND_QUIET;
  1699. }
  1700. else if(aBxSoundSamples[sOption]) {
  1701. if(sValue == 'off')
  1702. return;
  1703. if(sValue)
  1704. sSet = sValue;
  1705. sCue = aBxSoundSamples[sOption];
  1706. }
  1707. bx_sound_load().then(function(oCuelume) {
  1708. // play() takes the volume now; the page's own is back at once.
  1709. oCuelume.setVolume(fVolume);
  1710. oCuelume.play(sCue, {theme: sSet});
  1711. bx_sound_apply();
  1712. }, function() {});
  1713. }
  1714. $(document).ready(function() {
  1715. // A cached menu can still carry the Sounds item where sounds are off for everyone.
  1716. if(!glBxSounds.config)
  1717. $('[data-bx-sounds-switch]').closest('li').hide();
  1718. });
  1719. function bx_is_mouse()
  1720. {
  1721. return matchMedia('(pointer:fine)').matches;
  1722. }
  1723. function bx_is_touch_device()
  1724. {
  1725. return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
  1726. }
  1727. /**
  1728. * Check if mobile or desktop device.
  1729. */
  1730. function bx_check_mq()
  1731. {
  1732. if (typeof glOnCheckMq === 'function') {
  1733. return glOnCheckMq();
  1734. }
  1735. return window.getComputedStyle(document.querySelector('body'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/"/g, "");
  1736. }
  1737. /**
  1738. * This function loads the passed list of CSS files. In case when this function is called for dynamic loading
  1739. * of the same file but many times it allows to avoid multiply loadings of the same file.
  1740. */
  1741. function bx_get_style(mixedUrl)
  1742. {
  1743. if(typeof(mixedUrl) === 'string')
  1744. mixedUrl = new Array(mixedUrl);
  1745. for(let i = 0; i < mixedUrl.length; i++) {
  1746. if(mixedUrl[i] === undefined || !mixedUrl[i].length || $("link[href = '" + mixedUrl[i] + "']").length > 0)
  1747. continue;
  1748. $("head").append($("<link rel='stylesheet' href='" + mixedUrl[i] + "' type='text/css' />"));
  1749. }
  1750. }
  1751. /**
  1752. * This function loads the passed list of js files and executes callback function when the all files are loaded.
  1753. * In case when this function is called for dynamic loading of the same file but many times (like Plyr video init)
  1754. * it allows to avoid multiply loadings of the same file.
  1755. */
  1756. function bx_get_scripts (aFiles, fCallback)
  1757. {
  1758. let iCounter = 0;
  1759. const
  1760. iLength = aFiles.length,
  1761. fHandler = function() {
  1762. if (iCounter++ >= (iLength-1) && typeof(fCallback) === 'function')
  1763. fCallback();
  1764. else
  1765. fExecute(aFiles[iCounter]);
  1766. },
  1767. // Check if js file is not loaded yet then start to load it and returns Promise.
  1768. // All functions which try to load the same file will wait for loading of the file
  1769. // and then continue to work
  1770. fExecute = function(sFile) {
  1771. if ('undefined' === typeof(glBxLoadedScripts[sFile]))
  1772. glBxLoadedScripts[sFile] = { complete : $.bxGetCachedScript(sFile).done(fHandler) };
  1773. else
  1774. glBxLoadedScripts[sFile].complete.then(fHandler);
  1775. };
  1776. if ('undefined' === typeof(glBxLoadedScripts))
  1777. glBxLoadedScripts = {};
  1778. fExecute(aFiles[iCounter]);
  1779. }
  1780. jQuery.bxGetCachedScript = function(sUrl, oOptions)
  1781. {
  1782. oOptions = $.extend(oOptions || {}, {
  1783. dataType: "script",
  1784. cache: true,
  1785. url: sUrl
  1786. });
  1787. return jQuery.ajax(oOptions);
  1788. };
  1789. function bx_is_selector_in_stylesheet(sSel)
  1790. {
  1791. var a = [];
  1792. var aSs = document.styleSheets;
  1793. for (var sSheet = 0; sSheet < aSs.length; sSheet++) {
  1794. var ruleList = document.styleSheets[sSheet].cssRules;
  1795. for (var rule = 0; rule < ruleList.length; rule++)
  1796. a.push(ruleList[rule].selectorText);
  1797. }
  1798. return -1 !== a.indexOf(sSel);
  1799. }
  1800. function bx_copy_to_clipboard(s, onSuccess, onFail)
  1801. {
  1802. if ("undefined" === typeof(onSuccess))
  1803. onSuccess = function (){};
  1804. if ("undefined" === typeof(onFail))
  1805. onFail = function (){};
  1806. const oClip = navigator.clipboard;
  1807. if ("undefined" !== typeof(oClip)) {
  1808. navigator.clipboard.writeText(s).then(onSuccess, onFail);
  1809. }
  1810. else {
  1811. if (document.execCommand) {
  1812. const el = document.createElement("input");
  1813. el.value = s;
  1814. document.body.append(el);
  1815. el.select();
  1816. el.setSelectionRange(0, s.length);
  1817. if (document.execCommand("copy")) {
  1818. onSuccess();
  1819. }
  1820. el.remove();
  1821. }
  1822. else {
  1823. onFail();
  1824. }
  1825. }
  1826. }
  1827. function bx_regexp_escape(s)
  1828. {
  1829. return s
  1830. .replace(/[|\\{}()[\]^$+*?.]/mg, '\\$&')
  1831. .replace(/-/mg, '\\x2d');
  1832. }
  1833. function bx_get_regexp(sType)
  1834. {
  1835. var o = {
  1836. tag:"(<([^>]+bx-tag[^>]+)>)",
  1837. mention: "(<([^>]+bx-mention-link[^>]+)>)",
  1838. url: "(([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[\\-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9\\.\\-]+|(?:www\\.|[\\-;:&=\\+\\$,\\w]+@)[A-Za-z0-9\\.\\-]+)((?:\\/[\\+~%#\\/\\.\\w\\-_!\\(\\)]*)?\\??(?:[\\-\\+=&;%@\\.\\w_]*)#?(?:[\\.\\!\\/\\w]*))?"
  1839. };
  1840. return o[sType];
  1841. }
  1842. function bx_process_links()
  1843. {
  1844. // process links in container
  1845. $('.bx-lnk-cnt .bx-lnk-src:not(.bx-inited)').each(function() {
  1846. $(this).addClass('bx-inited');
  1847. var sUrl = $(this).prop('href');
  1848. $(this).parents('.bx-lnk-cnt').first().addClass('cursor-pointer').click(function(oEvent){
  1849. if ($(oEvent.target).closest('a').length == 0 && $(oEvent.target).closest('button').length == 0 && $(oEvent.target).closest('form').length == 0 && $(oEvent.target).closest('input').length == 0){
  1850. location.href = sUrl;
  1851. }
  1852. });
  1853. });
  1854. }
  1855. function bx_redirect_for_external_links (e)
  1856. {
  1857. if ('undefined' === typeof(aDolOptions.sys_confirmation_before_redirect) || 'on' !== aDolOptions.sys_confirmation_before_redirect)
  1858. return;
  1859. var aExclude = ['javascript:', 'about:', sUrlRoot];
  1860. var sPattern = 'a';
  1861. $(aExclude).each(function () {
  1862. sPattern += ':not([href^="' + this + '"])';
  1863. });
  1864. if ('undefined' !== typeof(aDolOptions.sys_storage_s3_endpoint) && aDolOptions.sys_storage_s3_endpoint.length)
  1865. sPattern += ':not([href^="https://' + aDolOptions.sys_storage_s3_endpoint + '"])';
  1866. sPattern += ":is([href^='http://'],[href^='https://'])";
  1867. if ('undefined' === typeof(e))
  1868. e = $('document');
  1869. e.find(sPattern).each(function() {
  1870. if ($(this).attr('href').match(/s3\.amazonaws\.com/))
  1871. return;
  1872. $(this).on('contextmenu', function(e) {
  1873. e.preventDefault();
  1874. });
  1875. $(this).on('click', function() {
  1876. var $this = $(this);
  1877. bx_redirect_for_external_links_open($this.attr('href'));
  1878. return false;
  1879. });
  1880. });
  1881. }
  1882. function bx_redirect_for_external_links_open (sHref)
  1883. {
  1884. //bx_confirm(_t('_sys_redirect_confirmation', sHref).replace(/\&quot;/g, ''), function () {
  1885. // window.open(sHref, '_blank').focus();
  1886. //});
  1887. window.open(sUrlRoot + 'page.php?i=redirect&url=' + encodeURIComponent(sHref), '_blank').focus();
  1888. }
  1889. function bx_agents_action(oElement, sTool, sAction, aParams)
  1890. {
  1891. var oDate = new Date();
  1892. var sQueryUrl = sUrlRoot + 'agents.php';
  1893. var oQueryParams = jQuery.extend({}, {t: sTool, a: sAction, _t:oDate.getTime()}, aParams != undefined ? aParams : {});
  1894. bx_loading_btn(oElement, true);
  1895. jQuery.post (
  1896. sQueryUrl,
  1897. oQueryParams,
  1898. function(oData) {
  1899. bx_loading_btn(oElement, false);
  1900. processJsonData(oData);
  1901. },
  1902. 'json'
  1903. );
  1904. }
  1905. function bx_get_focusable(oContext = 'document')
  1906. {
  1907. return Array.from(oContext.querySelectorAll('button, [href], input:not([type="hidden"]), textarea, select, [tabindex]:not(.bx-focus-trap, [tabindex="-1"])')).filter(function(oElement) {
  1908. return !$(oElement).closest(':hidden').length;
  1909. });
  1910. }
  1911. function bx_clicked_stack_register()
  1912. {
  1913. const iStackMaxLenth = 3;
  1914. $('a,button').filter(function(iIndex, oElement) {
  1915. return !$(oElement).closest('.bx-sidebar').length;
  1916. }).on('click', function() {
  1917. if(typeof window.glClickedStack === 'undefined')
  1918. window.glClickedStack = [];
  1919. window.glClickedStack.push(this);
  1920. if(window.glClickedStack && window.glClickedStack.length > iStackMaxLenth)
  1921. window.glClickedStack = window.glClickedStack.slice(-iStackMaxLenth);
  1922. });
  1923. }
  1924. function bx_clicked_stack_lenth()
  1925. {
  1926. return window.glClickedStack ? window.glClickedStack.length : 0;
  1927. }
  1928. function bx_clicked_stack_get()
  1929. {
  1930. const iLenth = bx_clicked_stack_lenth();
  1931. return iLenth > 0 ? window.glClickedStack[iLenth - 1] : false;
  1932. }
  1933. function bx_clicked_stack_pop()
  1934. {
  1935. return window.glClickedStack && window.glClickedStack.length > 0 ? window.glClickedStack.pop() : false;
  1936. }
  1937. /** @} */