dolibarr 24.0.0-beta
lib_foot.js.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * or see https://www.gnu.org/
18 */
19
25if (!defined('NOREQUIRESOC')) {
26 define('NOREQUIRESOC', '1');
27}
28if (!defined('NOCSRFCHECK')) {
29 define('NOCSRFCHECK', 1);
30}
31if (!defined('NOTOKENRENEWAL')) {
32 define('NOTOKENRENEWAL', 1);
33}
34if (!defined('NOLOGIN')) {
35 define('NOLOGIN', 1);
36}
37if (!defined('NOREQUIREMENU')) {
38 define('NOREQUIREMENU', 1);
39}
40if (!defined('NOREQUIREHTML')) {
41 define('NOREQUIREHTML', 1);
42}
43if (!defined('NOREQUIREAJAX')) {
44 define('NOREQUIREAJAX', '1');
45}
46
47session_cache_limiter('public');
48
49require_once '../../main.inc.php';
56/*
57 * View
58 */
59
60// Define javascript type
61top_httphead('text/javascript; charset=UTF-8');
62// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access.
63header('Cache-Control: max-age=10800, public, must-revalidate');
64
65
66$jsConst = [
67 'DOL_URL_ROOT' => DOL_URL_ROOT,
68 'classfortooltiponclicktextWidth' => ($conf->browser->layout == 'phone' ? max((empty($_SESSION['dol_screenwidth']) ? 0 : $_SESSION['dol_screenwidth']) - 20, 320) : 700),
69 'dol_no_mouse_hover' => !empty($conf->dol_no_mouse_hover)
70];
71
72
73include __DIR__ . '/lib_initTooltips.js';
74
75?>
76
77// Wrapper to show tooltips (html or onclick popup)
78/* JS CODE TO ENABLE Tooltips on all object with class classfortooltip */
79jQuery(function() {
80
81 const footerConst = <?php print json_encode($jsConst); ?>;
82
83
84 if(!footerConst.dol_no_mouse_hover) {
85 // --------------------------------
86 // Initial page load
87 // --------------------------------
88 initTooltips(document.body); // initialize all tooltips on page load
89 initAjaxTooltips(document.body, footerConst.DOL_URL_ROOT); // initialize all AJAX tooltips on page load
90
91
92 // --------------------------------
93 // Dynamic reload via Dolibarr hook
94 // --------------------------------
95 if (typeof Dolibarr !== "undefined" && Dolibarr.on) {
96 // Because Dolibarr context isn't in all Dolibarr page and lib_foot.js.php can be loaded everywhere
97 // it useful to check Dolibarr context is loaded before but we are already in a jQuery(function() so event Dolibarr:Ready can't be used here
98 Dolibarr.on('initNewContent', ({targets}) => {
99 targets.forEach(container => {
100 initTooltips(container);
101 initAjaxTooltips(container, footerConst.DOL_URL_ROOT);
102 })
103 });
104 };
105 }
106
107 // --------------------------------
108 // Initial page load
109 // --------------------------------
110 jQuery(function() {
111 initTooltipDialogs(document.body);
112 });
113
114 // --------------------------------
115 // Dynamic reload via Dolibarr hook
116 // --------------------------------
117
118 if (typeof Dolibarr !== "undefined" && Dolibarr.on) {
119 // Because Dolibarr context isn't in all Dolibarr page and lib_foot.js.php can be loaded everywhere
120 // it useful to check Dolibarr context is loaded before but we are already in a jQuery(function() so event Dolibarr:Ready can't be used here
121 Dolibarr.on('initNewContent', ({ targets }) => {
122 targets.forEach(container => initTooltipDialogs(container, footerConst.classfortooltiponclicktextWidth));
123 });
124 };
125
126});
127
128
129<?php
130// Wrapper to manage dropdown
131if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) {
132 print "\n/* JS CODE TO ENABLE dropdown (hamburger, linkto, ...) */\n";
133 print ' jQuery(document).ready(function () {
134 var lastopendropdown = null;
135
136 // Click onto the link "link to" or "hamburger", toggle dropdown
137 $(document).on(\'click\', \'.dropdown dt a\', function () {
138 console.log("toggle dropdown dt a");
139
140 //$(this).parent().parent().find(\'dd ul\').slideToggle(\'fast\');
141 $(".ulselectedfields").removeClass("open");
142 $(this).parent().parent().find(\'dd ul\').toggleClass("open");
143
144 if ($(this).parent().parent().find(\'dd ul\').hasClass("open")) {
145 lastopendropdown = $(this).parent().parent().find(\'dd ul\');
146 //console.log(lastopendropdown);
147 } else {
148 // We closed the dropdown for hamburger selectfields
149 if ($("input:hidden[name=formfilteraction]").val() == "listafterchangingselectedfields") {
150 console.log("resubmit the form saved into lastopendropdown after clicking on hamburger");
151 //$(".dropdown dt a").parents(\'form:first\').submit();
152 //$(".dropdown dt a").closest("form").submit();
153 lastopendropdown.closest("form").submit();
154 }
155 }
156
157 // Note: Did not find a way to get exact height (value is update at exit) so i calculate a generic from nb of lines
158 heigthofcontent = 21 * $(this).parent().parent().find(\'dd div ul li\').length;
159 if (heigthofcontent > 300) heigthofcontent = 300; // limited by max-height on css .dropdown dd ul
160 posbottom = $(this).parent().parent().find(\'dd\').offset().top + heigthofcontent + 8;
161 var scrollBottom = $(window).scrollTop() + $(window).height();
162 diffoutsidebottom = (posbottom - scrollBottom);
163 console.log("heigthofcontent="+heigthofcontent+", diffoutsidebottom (posbottom="+posbottom+" - scrollBottom="+scrollBottom+") = "+diffoutsidebottom);
164 if (diffoutsidebottom > 0)
165 {
166 pix = "-"+(diffoutsidebottom+8)+"px";
167 console.log("We reposition top by "+pix);
168 $(this).parent().parent().find(\'dd\').css("top", pix);
169 }
170 });
171
172 // Click on a link into the popup "link to" or other dropdown that ask to close drop down on element click, so close dropdown
173 $(".dropdowncloseonclick").on(\'click\', function () {
174 console.log("Link has class dropdowncloseonclick, so we close/hide the popup ul");
175 //$(this).parent().parent().hide(); // $(this).parent().parent() is ul
176 $(this).parent().parent().removeClass("open"); // $(this).parent().parent() is ul
177 });
178
179 // Click outside of any dropdown
180 $(document).bind(\'click\', function (e) {
181 var $clicked = $(e.target); // This is element we click on
182 if (!$clicked.parents().hasClass("dropdown")) {
183 //console.log("close dropdown dd ul - we click outside");
184 //$(".dropdown dd ul").hide();
185 $(".dropdown dd ul").removeClass("open");
186
187 if ($("input:hidden[name=formfilteraction]").val() == "listafterchangingselectedfields") {
188 console.log("resubmit form saved into lastopendropdown after clicking outside of dropdown and having change selectlist from selectlist field of hamburger dropdown");
189 //$(".dropdown dt a").parents(\'form:first\').submit();
190 //$(".dropdown dt a").closest("form").submit();
191 lastopendropdown.closest("form").submit();
192 }
193 }
194 });
195 });
196 ';
197}
198
199// Wrapper to manage document_preview
200if ($conf->browser->layout != 'phone') {
201 print "\n/* JS CODE TO ENABLE document_preview */\n"; // Function document_preview is into header
202 print ' jQuery(document).ready(function () {
203 // Click on the preview picto
204 jQuery(".documentpreview").click(function () {
205 console.log("We click on preview for element with href="+$(this).attr(\'href\')+" mime="+$(this).attr(\'mime\'));
206 var titledocpreview = $(this).attr(\'data-title\');
207 if (titledocpreview == undefined || titledocpreview == "") {
208 titledocpreview = \''.dol_escape_js($langs->transnoentities("Preview")).'\'
209 }
210 document_preview($(this).attr(\'href\'), $(this).attr(\'mime\'), titledocpreview);
211 return false;
212 });
213 });'."\n";
214}
215
216// Code to manage reposition
217print "\n/* JS CODE TO ENABLE reposition management (does not work if a redirect is done after action of submission) */\n";
218print '
219 jQuery(document).ready(function() {
220 /* If page_y set, we set scrollbar with it */
221 page_y = getParameterByName("page_y", 0); /* search in GET parameter */
222 if (page_y == 0) page_y = jQuery("#page_y").text(); /* search in POST parameter that is filed at bottom of page */
223 if (page_y > 0) {
224 console.log("page_y found is "+page_y);
225 jQuery("html, body").scrollTop(page_y);
226 }
227
228 /* Set handler to add page_y param on output (click on href links or submit button) */
229 jQuery(".reposition").click(function(event) {
230 var page_y = jQuery(document).scrollTop();
231 if (page_y > 0) {
232 if (this.href) {
233 console.log("We click on tag with .reposition class. this.ref was "+this.href);
234 var url = new URL(this.href, window.location.origin);
235 url.searchParams.delete("page_y"); /* remove page_y param if already present */
236 url.searchParams.set("page_y", page_y);
237 this.href = url.toString();
238 console.log("We click on tag with .reposition class. this.ref is now "+this.href);
239 } else {
240 console.log("We click on tag with .reposition class but element is not an <a> html tag, so we try to update input form field with name=page_y with value "+page_y);
241 jQuery("input[type=hidden][name=page_y]").val(page_y);
242 }
243 }
244 });
245 });
246' . "\n";
247
248// Code to manage Copy To Clipboard click
249print "\n/* JS CODE TO ENABLE ClipBoard copy paste */\n";
250print '
251 jQuery(document).ready(function() {
252 jQuery(\'.clipboardCPShowOnHover\').hover(
253 function() {
254 console.log("We hover a value with a copy paste feature");
255 $(this).children(".clipboardCPButton, .clipboardCPText").css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
256 },
257 function() {
258 console.log("We hover out the value with a copy paste feature");
259 $(this).children(".clipboardCPButton, .clipboardCPText").hide();
260 }
261 );
262
263 jQuery(\'.clipboardCPValue, .clipboardCPButton, .clipboardCPValueToPrint\').click(function() {
264 console.log("We click on a clipboardCPButton or clipboardCPValueToPrint class and we want to copy content of clipboardCPValue class");
265
266 if (window.getSelection) {
267 jqobj=$(this).parent().children(".clipboardCPValue");
268 console.log(jqobj.html());
269
270 selection = window.getSelection(); /* get the object used for selection */
271 selection.removeAllRanges(); /* clear current selection */
272
273 /* We select the value to print using the parentNode.firstChild */
274 /* We should use the class clipboardCPValue but it may have several element with copy/paste so class to select is not enough */
275 range = document.createRange();
276 range.selectNodeContents(this.parentNode.firstChild);
277 selection.addRange(range); /* make the new selection with the value to copy */
278
279 /* copy selection into clipboard */
280 var succeed;
281 try {
282 console.log("We set the style display to unset for the span so the copy will work");
283 jqobj.css("display", "unset"); /* Because copy does not work on "block" object */
284
285 succeed = document.execCommand(\'copy\');
286
287 console.log("We set the style display back to inline-block");
288 jqobj.css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
289 } catch(e) {
290 succeed = false;
291 }
292
293 /* Remove the selection to avoid to see the hidden field to copy selected */
294 window.getSelection().removeAllRanges();
295 }
296
297 /* Show result - message */
298 var lastparent = $(this).parent(); /* .parent is clipboardCP */
299 var lastchild = this.parentNode.lastChild; /* .parentNode is clipboardCP and last child is clipboardCPText */
300 var tmp = lastchild.innerHTML
301 if (succeed) {
302 $(this).parent().children(".clipboardCPButton").hide();
303 $(this).parent().children(".clipboardCPTick").css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
304 //lastchild.innerHTML = \'<div class="clipboardCPTextDivInside opacitymedium">'.dol_escape_js($langs->trans('CopiedToClipboard')).'</div>\';
305 } else {
306 lastchild.innerHTML = \'<div class="clipboardCPTextDivInside opacitymedium">'.dol_escape_js($langs->trans('Error')).'</div>\';
307 }
308 setTimeout(() => { lastchild.innerHTML = tmp; lastparent.children(".clipboardCPTick").hide(); }, 2000);
309 });
310 });'."\n";
311
312// Code to manage clicktodial
313print "\n/* JS CODE TO ENABLE clicktodial call of an URL */\n";
314print '
315 jQuery(document).ready(function() {
316 jQuery(".cssforclicktodial").click(function() {
317 event.preventDefault();
318 var currenttoken = jQuery("meta[name=anti-csrf-currenttoken]").attr("content");
319 console.log("We click on a cssforclicktodial class with href="+this.href);
320 $.ajax({
321 url: this.href,
322 type: \'GET\',
323 data: { token: currenttoken }
324 }).done(function(xhr, textStatus, errorThrown) {
325 /* do nothing */
326 }).fail(function(xhr, textStatus, errorThrown) {
327 alert("Error: "+textStatus);
328 });
329 return false;
330 });
331 });'."\n";
332
333
334// Code to manage the confirm dialog box
335print "\n/* JS CODE TO ENABLE DIALOG CONFIRM POPUP ON ACTION BUTTON */\n";
336print '
337 jQuery(document).ready(function() {
338 $(document).on("click", \'.butActionConfirm\', function(event) {
339 event.preventDefault();
340
341 // I don\'t use jquery $(this).data(\'confirm-url\'); to get $(this).attr(\'data-confirm-url\'); because .data() can doesn\'t work with ajax
342 var confirmUrl = $(this).attr(\'data-confirm-url\');
343 var confirmTitle = $(this).attr(\'data-confirm-title\');
344 var confirmContent = $(this).attr(\'data-confirm-content\');
345 var confirmActionBtnLabel = $(this).attr(\'data-confirm-action-btn-label\');
346 var confirmCancelBtnLabel = $(this).attr(\'data-confirm-cancel-btn-label\');
347 var confirmModal = $(this).attr(\'data-confirm-modal\');
348 if(confirmModal == undefined){ confirmModal = false; }
349
350 var confirmId = \'confirm-dialog-box\';
351 if($(this).attr(\'id\') != undefined){ var confirmId = confirmId + "-" + $(this).attr(\'id\'); }
352 if($("#" + confirmId) != undefined) { $(\'#\' + confirmId).remove(); }
353
354 // Create modal box
355
356 var $confirmBox = $(\'<div/>\', {
357 id: confirmId,
358 title: confirmTitle
359 }).appendTo(\'body\');
360
361 $confirmBox.dialog({
362 autoOpen: true,
363 modal: confirmModal,
364 //width: Math.min($( window ).width() - 50, 1700),
365 width: \'auto\',
366 dialogClass: \'confirm-dialog-box\',
367 buttons: [
368 {
369 text: confirmActionBtnLabel,
370 "class": \'ui-state-information\',
371 click: function () {
372 window.location.replace(confirmUrl);
373 }
374 },
375 {
376 text: confirmCancelBtnLabel,
377 "class": \'ui-state-information\',
378 click: function () {
379 $(this).dialog("close");
380 }
381 }
382 ],
383 close: function( event, ui ) {
384 $(\'#\'+confirmBox).remove();
385 },
386 open: function( event, ui ) {
387 $confirmBox.html(confirmContent);
388 }
389 });
390 });
391 });
392'."\n";
393
394
395// JS CODE USED by form::getSearchFilterToolInput
396include __DIR__ . '/lib_foot_search_tool.js';
397include __DIR__ . '/lib_tooltip-freeze-by-alt-keypress.js';
document_preview(file, type, title)
Function to show a document preview popup.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.