dolibarr 23.0.3
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';
58/*
59 * View
60 */
61
62// Define javascript type
63top_httphead('text/javascript; charset=UTF-8');
64// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access.
65if (empty($dolibarr_nocache)) {
66 header('Cache-Control: max-age=10800, public, must-revalidate');
67} else {
68 header('Cache-Control: no-cache');
69}
70
71//var_dump($conf);
72
73
74// Wrapper to show tooltips (html or onclick popup)
75print "\n/* JS CODE TO ENABLE Tooltips on all object with class classfortooltip */
76jQuery(document).ready(function () {\n";
77
78if (empty($conf->dol_no_mouse_hover)) {
79 print '
80 /* for standard tooltip */
81 jQuery(".classfortooltip").tooltip({
82 tooltipClass: "mytooltip",
83 show: { collision: "flipfit", effect:"toggle", delay:50, duration: 20 },
84 hide: { delay: 250, duration: 20 },
85 content: function () {
86 console.log("Return title for popup");
87 return $(this).prop("title"); /* To force to get title as is */
88 }
89 });
90
91 var opendelay = 100;
92 var elemtostoretooltiptimer = jQuery("#dialogforpopup");
93 var currenttoken = jQuery("meta[name=anti-csrf-currenttoken]").attr("content");
94
95 /* for ajax tooltip */
96 target = jQuery(".classforajaxtooltip");
97 target.tooltip({
98 tooltipClass: "mytooltip",
99 show: { collision: "flipfit", effect:"toggle", delay: 0, duration: 20 },
100 hide: { delay: 250, duration: 20 }
101 });
102
103 target.off("mouseover mouseout");
104 target.on("mouseover", function(event) {
105 console.log("we will create timer for ajax call");
106 event.stopImmediatePropagation();
107 clearTimeout(elemtostoretooltiptimer.data("openTimeoutId"));
108
109 var params = JSON.parse($(this).attr("data-params"));
110 params.token = currenttoken;
111 var elemfortooltip = $(this);
112
113 elemtostoretooltiptimer.data("openTimeoutId", setTimeout(function() {
114 target.tooltip("close");
115 $.ajax({
116 url:"'. DOL_URL_ROOT.'/core/ajax/ajaxtooltip.php",
117 type: "post",
118 async: true,
119 data: params,
120 success: function(response){
121 // Setting content option
122 console.log("ajax success");
123 if (elemfortooltip.is(":hover")) {
124 elemfortooltip.tooltip("option","content",response);
125 elemfortooltip.tooltip("open");
126 }
127 }
128 });
129 }, opendelay));
130 });
131 target.on("mouseout", function(event) {
132 console.log("mouse out of a .classforajaxtooltip");
133 event.stopImmediatePropagation();
134 clearTimeout(elemtostoretooltiptimer.data("openTimeoutId"));
135 target.tooltip("close");
136 });
137 ';
138}
139
140print '
141 jQuery(".classfortooltiponclicktext").dialog({
142 /* title: \'No title\', */
143 closeOnEscape: true, classes: { "ui-dialog": "highlight" },
144 maxHeight: window.innerHeight-60, width: '.($conf->browser->layout == 'phone' ? max((empty($_SESSION['dol_screenwidth']) ? 0 : $_SESSION['dol_screenwidth']) - 20, 320) : 700).',
145 modal: true,
146 autoOpen: false
147 }).css("z-index: 5000");
148 jQuery(".classfortooltiponclick").click(function () {
149 console.log("We click on tooltip for element with dolid="+$(this).attr(\'dolid\'));
150 if ($(this).attr(\'dolid\')) {
151 obj=$("#idfortooltiponclick_"+$(this).attr(\'dolid\')); /* obj is a div component */
152 obj.dialog("open");
153 return false;
154 }
155 });
156});
157';
158
159
160// Wrapper to manage dropdown
161if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) {
162 print "\n/* JS CODE TO ENABLE dropdown (hamburger, linkto, ...) */\n";
163 print ' jQuery(document).ready(function () {
164 var lastopendropdown = null;
165
166 // Click onto the link "link to" or "hamburger", toggle dropdown
167 $(document).on(\'click\', \'.dropdown dt a\', function () {
168 console.log("toggle dropdown dt a");
169
170 //$(this).parent().parent().find(\'dd ul\').slideToggle(\'fast\');
171 $(".ulselectedfields").removeClass("open");
172 $(this).parent().parent().find(\'dd ul\').toggleClass("open");
173
174 if ($(this).parent().parent().find(\'dd ul\').hasClass("open")) {
175 lastopendropdown = $(this).parent().parent().find(\'dd ul\');
176 //console.log(lastopendropdown);
177 } else {
178 // We closed the dropdown for hamburger selectfields
179 if ($("input:hidden[name=formfilteraction]").val() == "listafterchangingselectedfields") {
180 console.log("resubmit the form saved into lastopendropdown after clicking on hamburger");
181 //$(".dropdown dt a").parents(\'form:first\').submit();
182 //$(".dropdown dt a").closest("form").submit();
183 lastopendropdown.closest("form").submit();
184 }
185 }
186
187 // Note: Did not find a way to get exact height (value is update at exit) so i calculate a generic from nb of lines
188 heigthofcontent = 21 * $(this).parent().parent().find(\'dd div ul li\').length;
189 if (heigthofcontent > 300) heigthofcontent = 300; // limited by max-height on css .dropdown dd ul
190 posbottom = $(this).parent().parent().find(\'dd\').offset().top + heigthofcontent + 8;
191 var scrollBottom = $(window).scrollTop() + $(window).height();
192 diffoutsidebottom = (posbottom - scrollBottom);
193 console.log("heigthofcontent="+heigthofcontent+", diffoutsidebottom (posbottom="+posbottom+" - scrollBottom="+scrollBottom+") = "+diffoutsidebottom);
194 if (diffoutsidebottom > 0)
195 {
196 pix = "-"+(diffoutsidebottom+8)+"px";
197 console.log("We reposition top by "+pix);
198 $(this).parent().parent().find(\'dd\').css("top", pix);
199 }
200 });
201
202 // Click on a link into the popup "link to" or other dropdown that ask to close drop down on element click, so close dropdown
203 $(".dropdowncloseonclick").on(\'click\', function () {
204 console.log("Link has class dropdowncloseonclick, so we close/hide the popup ul");
205 //$(this).parent().parent().hide(); // $(this).parent().parent() is ul
206 $(this).parent().parent().removeClass("open"); // $(this).parent().parent() is ul
207 });
208
209 // Click outside of any dropdown
210 $(document).bind(\'click\', function (e) {
211 var $clicked = $(e.target); // This is element we click on
212 if (!$clicked.parents().hasClass("dropdown")) {
213 //console.log("close dropdown dd ul - we click outside");
214 //$(".dropdown dd ul").hide();
215 $(".dropdown dd ul").removeClass("open");
216
217 if ($("input:hidden[name=formfilteraction]").val() == "listafterchangingselectedfields") {
218 console.log("resubmit form saved into lastopendropdown after clicking outside of dropdown and having change selectlist from selectlist field of hamburger dropdown");
219 //$(".dropdown dt a").parents(\'form:first\').submit();
220 //$(".dropdown dt a").closest("form").submit();
221 lastopendropdown.closest("form").submit();
222 }
223 }
224 });
225 });
226 ';
227}
228
229// Wrapper to manage document_preview
230if ($conf->browser->layout != 'phone') {
231 print "\n/* JS CODE TO ENABLE document_preview */\n"; // Function document_preview is into header
232 print ' jQuery(document).ready(function () {
233 // Click on the preview picto
234 jQuery(".documentpreview").click(function () {
235 console.log("We click on preview for element with href="+$(this).attr(\'href\')+" mime="+$(this).attr(\'mime\'));
236 var titledocpreview = $(this).attr(\'data-title\');
237 if (titledocpreview == undefined || titledocpreview == "") {
238 titledocpreview = \''.dol_escape_js($langs->transnoentities("Preview")).'\'
239 }
240 document_preview($(this).attr(\'href\'), $(this).attr(\'mime\'), titledocpreview);
241 return false;
242 });
243 });'."\n";
244}
245
246// Code to manage reposition
247print "\n/* JS CODE TO ENABLE reposition management (does not work if a redirect is done after action of submission) */\n";
248print '
249 jQuery(document).ready(function() {
250 /* If page_y set, we set scrollbar with it */
251 page_y = getParameterByName("page_y", 0); /* search in GET parameter */
252 if (page_y == 0) page_y = jQuery("#page_y").text(); /* search in POST parameter that is filed at bottom of page */
253 if (page_y > 0) {
254 console.log("page_y found is "+page_y);
255 jQuery("html, body").scrollTop(page_y);
256 }
257
258 /* Set handler to add page_y param on output (click on href links or submit button) */
259 jQuery(".reposition").click(function(event) {
260 var page_y = jQuery(document).scrollTop();
261 if (page_y > 0) {
262 if (this.href) {
263 console.log("We click on tag with .reposition class. this.ref was "+this.href);
264 var url = new URL(this.href, window.location.origin);
265 url.searchParams.delete("page_y"); /* remove page_y param if already present */
266 url.searchParams.set("page_y", page_y);
267 this.href = url.toString();
268 console.log("We click on tag with .reposition class. this.ref is now "+this.href);
269 } else {
270 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);
271 jQuery("input[type=hidden][name=page_y]").val(page_y);
272 }
273 }
274 });
275 });
276' . "\n";
277
278// Code to manage Copy To Clipboard click
279print "\n/* JS CODE TO ENABLE ClipBoard copy paste */\n";
280print '
281 jQuery(document).ready(function() {
282 jQuery(\'.clipboardCPShowOnHover\').hover(
283 function() {
284 console.log("We hover a value with a copy paste feature");
285 $(this).children(".clipboardCPButton, .clipboardCPText").css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
286 },
287 function() {
288 console.log("We hover out the value with a copy paste feature");
289 $(this).children(".clipboardCPButton, .clipboardCPText").hide();
290 }
291 );
292
293 jQuery(\'.clipboardCPValue, .clipboardCPButton, .clipboardCPValueToPrint\').click(function() {
294 console.log("We click on a clipboardCPButton or clipboardCPValueToPrint class and we want to copy content of clipboardCPValue class");
295
296 if (window.getSelection) {
297 jqobj=$(this).parent().children(".clipboardCPValue");
298 console.log(jqobj.html());
299
300 selection = window.getSelection(); /* get the object used for selection */
301 selection.removeAllRanges(); /* clear current selection */
302
303 /* We select the value to print using the parentNode.firstChild */
304 /* We should use the class clipboardCPValue but it may have several element with copy/paste so class to select is not enough */
305 range = document.createRange();
306 range.selectNodeContents(this.parentNode.firstChild);
307 selection.addRange(range); /* make the new selection with the value to copy */
308
309 /* copy selection into clipboard */
310 var succeed;
311 try {
312 console.log("We set the style display to unset for the span so the copy will work");
313 jqobj.css("display", "unset"); /* Because copy does not work on "block" object */
314
315 succeed = document.execCommand(\'copy\');
316
317 console.log("We set the style display back to inline-block");
318 jqobj.css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
319 } catch(e) {
320 succeed = false;
321 }
322
323 /* Remove the selection to avoid to see the hidden field to copy selected */
324 window.getSelection().removeAllRanges();
325 }
326
327 /* Show result - message */
328 var lastparent = $(this).parent(); /* .parent is clipboardCP */
329 var lastchild = this.parentNode.lastChild; /* .parentNode is clipboardCP and last child is clipboardCPText */
330 var tmp = lastchild.innerHTML
331 if (succeed) {
332 $(this).parent().children(".clipboardCPButton").hide();
333 $(this).parent().children(".clipboardCPTick").css("display", "inline-block"); /* better than .show() because the show set the display to "inline" */
334 //lastchild.innerHTML = \'<div class="clipboardCPTextDivInside opacitymedium">'.dol_escape_js($langs->trans('CopiedToClipboard')).'</div>\';
335 } else {
336 lastchild.innerHTML = \'<div class="clipboardCPTextDivInside opacitymedium">'.dol_escape_js($langs->trans('Error')).'</div>\';
337 }
338 setTimeout(() => { lastchild.innerHTML = tmp; lastparent.children(".clipboardCPTick").hide(); }, 2000);
339 });
340 });'."\n";
341
342// Code to manage clicktodial
343print "\n/* JS CODE TO ENABLE clicktodial call of an URL */\n";
344print '
345 jQuery(document).ready(function() {
346 jQuery(".cssforclicktodial").click(function() {
347 event.preventDefault();
348 var currenttoken = jQuery("meta[name=anti-csrf-currenttoken]").attr("content");
349 console.log("We click on a cssforclicktodial class with href="+this.href);
350 $.ajax({
351 url: this.href,
352 type: \'GET\',
353 data: { token: currenttoken }
354 }).done(function(xhr, textStatus, errorThrown) {
355 /* do nothing */
356 }).fail(function(xhr, textStatus, errorThrown) {
357 alert("Error: "+textStatus);
358 });
359 return false;
360 });
361 });'."\n";
362
363
364// Code to manage the confirm dialog box
365print "\n/* JS CODE TO ENABLE DIALOG CONFIRM POPUP ON ACTION BUTTON */\n";
366print '
367 jQuery(document).ready(function() {
368 $(document).on("click", \'.butActionConfirm\', function(event) {
369 event.preventDefault();
370
371 // I don\'t use jquery $(this).data(\'confirm-url\'); to get $(this).attr(\'data-confirm-url\'); because .data() can doesn\'t work with ajax
372 var confirmUrl = $(this).attr(\'data-confirm-url\');
373 var confirmTitle = $(this).attr(\'data-confirm-title\');
374 var confirmContent = $(this).attr(\'data-confirm-content\');
375 var confirmActionBtnLabel = $(this).attr(\'data-confirm-action-btn-label\');
376 var confirmCancelBtnLabel = $(this).attr(\'data-confirm-cancel-btn-label\');
377 var confirmModal = $(this).attr(\'data-confirm-modal\');
378 if(confirmModal == undefined){ confirmModal = false; }
379
380 var confirmId = \'confirm-dialog-box\';
381 if($(this).attr(\'id\') != undefined){ var confirmId = confirmId + "-" + $(this).attr(\'id\'); }
382 if($("#" + confirmId) != undefined) { $(\'#\' + confirmId).remove(); }
383
384 // Create modal box
385
386 var $confirmBox = $(\'<div/>\', {
387 id: confirmId,
388 title: confirmTitle
389 }).appendTo(\'body\');
390
391 $confirmBox.dialog({
392 autoOpen: true,
393 modal: confirmModal,
394 //width: Math.min($( window ).width() - 50, 1700),
395 width: \'auto\',
396 dialogClass: \'confirm-dialog-box\',
397 buttons: [
398 {
399 text: confirmActionBtnLabel,
400 "class": \'ui-state-information\',
401 click: function () {
402 window.location.replace(confirmUrl);
403 }
404 },
405 {
406 text: confirmCancelBtnLabel,
407 "class": \'ui-state-information\',
408 click: function () {
409 $(this).dialog("close");
410 }
411 }
412 ],
413 close: function( event, ui ) {
414 $(\'#\'+confirmBox).remove();
415 },
416 open: function( event, ui ) {
417 $confirmBox.html(confirmContent);
418 }
419 });
420 });
421 });
422'."\n";
423
424
425// JS CODE USED by form::getSearchFilterToolInput
426include __DIR__ . '/lib_foot_search_tool.js';
427include __DIR__ . '/lib_tooltip-freeze-by-alt-keypress.js';
document_preview(file, type, title)
Function to show a document preview popup.
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.