dolibarr 22.0.5
lib_head.js.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005-2018 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
5 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * or see https://www.gnu.org/
21 */
22
29if (!defined('NOREQUIRESOC')) {
30 define('NOREQUIRESOC', '1');
31}
32if (!defined('NOCSRFCHECK')) {
33 define('NOCSRFCHECK', 1);
34}
35if (!defined('NOTOKENRENEWAL')) {
36 define('NOTOKENRENEWAL', 1);
37}
38if (!defined('NOLOGIN')) {
39 define('NOLOGIN', 1);
40}
41if (!defined('NOREQUIREMENU')) {
42 define('NOREQUIREMENU', 1);
43}
44if (!defined('NOREQUIREHTML')) {
45 define('NOREQUIREHTML', 1);
46}
47if (!defined('NOREQUIREAJAX')) {
48 define('NOREQUIREAJAX', '1');
49}
50
51session_cache_limiter('public');
52
53require_once '../../main.inc.php';
59/*
60 * View
61 */
62
63// Define javascript type
64top_httphead('text/javascript; charset=UTF-8');
65// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access.
66if (empty($dolibarr_nocache)) {
67 header('Cache-Control: max-age=10800, public, must-revalidate');
68} else {
69 header('Cache-Control: no-cache');
70}
71
72
73
74// Define tradMonths javascript array (we define this in datepicker AND in parent page to avoid errors with IE8)
75$tradMonths = array(
76dol_escape_js($langs->transnoentitiesnoconv("Month01")),
77dol_escape_js($langs->transnoentitiesnoconv("Month02")),
78dol_escape_js($langs->transnoentitiesnoconv("Month03")),
79dol_escape_js($langs->transnoentitiesnoconv("Month04")),
80dol_escape_js($langs->transnoentitiesnoconv("Month05")),
81dol_escape_js($langs->transnoentitiesnoconv("Month06")),
82dol_escape_js($langs->transnoentitiesnoconv("Month07")),
83dol_escape_js($langs->transnoentitiesnoconv("Month08")),
84dol_escape_js($langs->transnoentitiesnoconv("Month09")),
85dol_escape_js($langs->transnoentitiesnoconv("Month10")),
86dol_escape_js($langs->transnoentitiesnoconv("Month11")),
87dol_escape_js($langs->transnoentitiesnoconv("Month12"))
88);
89
90$tradMonthsShort = array(
91$langs->trans("MonthShort01"),
92$langs->trans("MonthShort02"),
93$langs->trans("MonthShort03"),
94$langs->trans("MonthShort04"),
95$langs->trans("MonthShort05"),
96$langs->trans("MonthShort06"),
97$langs->trans("MonthShort07"),
98$langs->trans("MonthShort08"),
99$langs->trans("MonthShort09"),
100$langs->trans("MonthShort10"),
101$langs->trans("MonthShort11"),
102$langs->trans("MonthShort12")
103);
104
105$tradDays = array(
106$langs->trans("Sunday"),
107$langs->trans("Monday"),
108$langs->trans("Tuesday"),
109$langs->trans("Wednesday"),
110$langs->trans("Thursday"),
111$langs->trans("Friday"),
112$langs->trans("Saturday")
113);
114
115$tradDaysShort = array(
116$langs->trans("ShortSunday"),
117$langs->trans("ShortMonday"),
118$langs->trans("ShortTuesday"),
119$langs->trans("ShortWednesday"),
120$langs->trans("ShortThursday"),
121$langs->trans("ShortFriday"),
122$langs->trans("ShortSaturday")
123);
124
125$tradDaysMin = array(
126$langs->trans("SundayMin"),
127$langs->trans("MondayMin"),
128$langs->trans("TuesdayMin"),
129$langs->trans("WednesdayMin"),
130$langs->trans("ThursdayMin"),
131$langs->trans("FridayMin"),
132$langs->trans("SaturdayMin")
133);
134
135
136$dec = ',';
137$thousand = ' ';
138if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
139 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
140}
141if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
142 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
143}
144if ($thousand == 'Space') {
145 $thousand = ' ';
146}
147
148?>
149// Javascript libraries for Dolibarr ERP CRM (https://www.dolibarr.org)
150
151
152// To start/stop Block UI
153function dolBlockUI(message = 'Loading...', indicatorUrl = '<?php echo DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif" ; ?>') {
154 const block = document.getElementById('dol-block-ui');
155 if (block != null) {
156 const msgDiv = block.querySelector('.message');
157 if (msgDiv != null) {
158 msgDiv.innerText = message;
159 msgDiv.style.backgroundImage = `url('${indicatorUrl}')`;
160 block.style.display = 'flex';
161 }
162 }
163}
164function dolUnblockUI() {
165 document.getElementById('dol-block-ui').style.display = 'none';
166}
167
168
169// For jQuery date picker
170var tradMonths = <?php echo json_encode($tradMonths) ?>;
171var tradMonthsShort = <?php echo json_encode($tradMonthsShort) ?>;
172var tradDays = <?php echo json_encode($tradDays) ?>;
173var tradDaysShort = <?php echo json_encode($tradDaysShort) ?>;
174var tradDaysMin = <?php echo json_encode($tradDaysMin) ?>;
175var currencyCache = <?php echo json_encode($langs->cache_currencies) ?>;
176
177// For JQuery date picker
178$(document).ready(function() {
179 $.datepicker.setDefaults({
180 autoSize: true,
181 changeMonth: true,
182 changeYear: true,
183 altField: '#timestamp',
184 altFormat: '@' // Gives a timestamp dateformat
185 });
186});
187
188jQuery(function($){
189 $.datepicker.regional['<?php echo $langs->defaultlang ?>'] = {
190 closeText: '<?php echo $langs->trans("Close2") ?>',
191 prevText: '<?php echo $langs->trans("Previous") ?>',
192 nextText: '<?php echo $langs->trans("Next") ?>',
193 currentText: '<?php echo $langs->trans("Now") ?>',
194 monthNames: tradMonths,
195 monthNamesShort: tradMonthsShort,
196 dayNames: tradDays,
197 dayNamesShort: tradDaysShort,
198 dayNamesMin: tradDaysMin,
199 weekHeader: '<?php echo $langs->trans("Week"); ?>',
200 dateFormat: '<?php echo $langs->trans("FormatDateShortJQuery"); ?>', /* Note dd/mm/yy means year on 4 digit in jquery format */
201 firstDay: <?php echo(isset($conf->global->MAIN_START_WEEK) ? $conf->global->MAIN_START_WEEK : '1'); ?>,
202 isRTL: <?php echo($langs->trans("DIRECTION") == 'rtl' ? 'true' : 'false'); ?>,
203 showMonthAfterYear: false, /* TODO add specific to country */
204 yearSuffix: '' /* TODO add specific to country */
205 };
206 $.datepicker.setDefaults($.datepicker.regional['<?php echo $langs->defaultlang ?>']);
207});
208
209
210
215var select2arrayoflanguage = {
216 matches: function (matches) { return matches + " <?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2ResultFoundUseArrows")); ?>"; },
217 noResults: function () { return "<?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2NotFound")); ?>"; },
218 inputTooShort: function (input) {
219 var n = input.minimum;
220 /*console.log(input); console.log(input.minimum);*/
221 if (n > 1) return "<?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2Enter")); ?> " + n + " <?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2MoreCharacters")); ?>";
222 else return "<?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2Enter")); ?> " + n + " <?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2MoreCharacter")); ?>"
223 },
224 loadMore: function (pageNumber) { return "<?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2LoadingMoreResults")); ?>"; },
225 searching: function () { return "<?php echo dol_escape_js($langs->transnoentitiesnoconv("Select2SearchInProgress")); ?>"; }
226};
227
228
233// Returns an object given an id
234function getObjectFromID(id){
235 var theObject;
236 if(document.getElementById)
237 theObject=document.getElementById(id);
238 else
239 theObject=document.all[id];
240 return theObject;
241}
242
243// Called after the selection or typing of a date to save details into detailed fields
244function dpChangeDay(dateFieldID, format)
245{
246 console.log("Call dpChangeDay, we save date from field "+dateFieldID+" into detailed fields from format = "+format);
247
248 var thefield = getObjectFromID(dateFieldID);
249 var thefieldday = getObjectFromID(dateFieldID+"day");
250 var thefieldmonth = getObjectFromID(dateFieldID+"month");
251 var thefieldyear = getObjectFromID(dateFieldID+"year");
252
253 console.log("string date value is " + thefield.value);
254
255 var date = getDateFromFormat(thefield.value, format);
256
257 if (date)
258 {
259 thefieldday.value = date.getDate();
260 if (thefieldday.onchange) thefieldday.onchange.call(thefieldday);
261 thefieldmonth.value = date.getMonth()+1;
262 if (thefieldmonth.onchange) thefieldmonth.onchange.call(thefieldmonth);
263 thefieldyear.value = date.getFullYear();
264 if (thefieldyear.onchange) thefieldyear.onchange.call(thefieldyear);
265
266 return 1;
267 }
268
269 // Replace yyyy into yy
270 newformat = format.replace(/yyyy/g, 'yy');
271 if (newformat != format) {
272 console.log("dpChangeDay, we try now from format = "+newformat);
273
274 var date = getDateFromFormat(thefield.value, newformat);
275 //console.log(date);
276 if (date)
277 {
278 thefieldday.value = date.getDate();
279 if (thefieldday.onchange) thefieldday.onchange.call(thefieldday);
280 thefieldmonth.value = date.getMonth()+1;
281 if (thefieldmonth.onchange) thefieldmonth.onchange.call(thefieldmonth);
282 thefieldyear.value = date.getFullYear();
283 if (thefieldyear.onchange) thefieldyear.onchange.call(thefieldyear);
284
285 return 2;
286 }
287 }
288
289 thefieldday.value = '';
290 if (thefieldday.onchange) thefieldday.onchange.call(thefieldday);
291 thefieldmonth.value = '';
292 if (thefieldmonth.onchange) thefieldmonth.onchange.call(thefieldmonth);
293 thefieldyear.value = '';
294 if (thefieldyear.onchange) thefieldyear.onchange.call(thefieldyear);
295
296 return 0;
297}
298
299/*
300 * =================================================================
301 * Function: formatDate(javascript object Date(), format)
302 * Purpose: Returns a date in the output format specified. The format string can use the following tags:
303 * Year | yyyy (4 digits), yy (2 digits)
304 * Month | MM (2 digits)
305 * Day of Month | dd (2 digits)
306 * Hour (1-12) | hh (2 digits) Hour (0-23) | HH (2 digits)
307 * Minute | mm (2 digits)
308 * Second | ss (2 digits)
309 * Author: Laurent Destailleur Author: Matelli (see http://matelli.fr/showcases/patchs-dolibarr/update-date-input-in-action-form.html)
310 * Licence: GPL
311 * ==================================================================
312 */
313function formatDate(date,format)
314{
315 // alert('formatDate date='+date+' format='+format);
316
317 // Force parameters en chaine
318 format=format+"";
319
320 var result="";
321
322 var year=date.getFullYear();
323 var month=date.getMonth()+1;
324 var day=date.getDate();
325 var hour=date.getHours();
326 var minute=date.getMinutes();
327 var seconde=date.getSeconds();
328
329 var i=0;
330 while (i < format.length)
331 {
332 c=format.charAt(i); // Recupere char du format
333 var substr = '';
334 j=i;
335 while ((format.charAt(j)==c) && (j < format.length)) // Recupere char successif identiques
336 {
337 substr += format.charAt(j++);
338 }
339
340 // alert('substr='+substr);
341 if (substr == 'yyyy') { result=result+year; }
342 else if (substr == 'yy') { result=result+year.substring(2,4); }
343 else if (substr == 'M') { result=result+month; }
344 else if (substr == 'MM') { result=result+(month<1||month>9?"":"0")+month; }
345 else if (substr == 'd') { result=result+day; }
346 else if (substr == 'dd') { result=result+(day<1||day>9?"":"0")+day; }
347 else if (substr == 'hh') { if (hour > 12) hour-=12; result=result+(hour<0||hour>9?"":"0")+hour; }
348 else if (substr == 'HH') { result=result+(hour<0||hour>9?"":"0")+hour; }
349 else if (substr == 'mm') { result=result+(minute<0||minute>9?"":"0")+minute; }
350 else if (substr == 'ss') { result=result+(seconde<0||seconde>9?"":"0")+seconde; }
351 else { result=result+substr; }
352
353 i+=substr.length;
354 }
355
356 // alert(result);
357 return result;
358}
359
360
361/*
362 * =================================================================
363 * Function: getDateFromFormat(date_string, format_string)
364 * Purpose: This function takes a date string and a format string.
365 * It parses the date string with format and it
366 * returns the date as a javascript Date() object. If date does not match
367 * format, it returns 0. The format string can use the following tags:
368 * Field | Tags
369 * -------------+-----------------------------------
370 * Year | yyyy (4 digits), yy (2 digits)
371 * Month | MM (2 digits)
372 * Day of Month | dd (2 digits)
373 * Hour (1-12) | hh (2 digits)
374 * Hour (0-23) | HH (2 digits)
375 * Minute | mm (2 digits)
376 * Second | ss (2 digits)
377 * Author: Laurent Destailleur
378 * Licence: GPL
379 * ==================================================================
380 */
381function getDateFromFormat(val, format)
382{
383 // alert('getDateFromFormat val='+val+' format='+format);
384
385 // Force parameters en chaine
386 val = val+"";
387 format = format+"";
388
389 if (val == '') return 0;
390
391 var now=new Date();
392 var year=now.getFullYear();
393 var month=now.getMonth()+1;
394 var day=now.getDate();
395 var hour=now.getHours();
396 var minute=now.getMinutes();
397 var seconde=now.getSeconds();
398
399 var i=0;
400 var d=0; // -d- follows the date string while -i- follows the format string
401
402 while (i < format.length)
403 {
404 c=format.charAt(i); // Recupere char du format
405 substr="";
406 j=i;
407 while ((format.charAt(j)==c) && (j < format.length)) // Get successive similar characters
408 {
409 substr += format.charAt(j++);
410 }
411
412 // alert('substr='+substr);
413 if (substr == "yyyy") year=getIntegerInString(val,d,4,4);
414 if (substr == "yy") year=""+(getIntegerInString(val,d,2,2)-0+2000); /* #28334 */
415 if (substr == "MM" ||substr == "M")
416 {
417 month=getIntegerInString(val,d,1,2);
418 if (month) d -= 2- month.length;
419 }
420 if (substr == "dd")
421 {
422 day=getIntegerInString(val,d,1,2);
423 if (day) d -= 2- day.length;
424 }
425 if (substr == "HH" ||substr == "hh" )
426 {
427 hour=getIntegerInString(val,d,1,2);
428 if (dhouray) d -= 2- hour.length;
429 }
430 if (substr == "mm"){
431 minute=getIntegerInString(val,d,1,2);
432 if (minute) d -= 2- minute.length;
433 }
434 if (substr == "ss")
435 {
436 seconde=getIntegerInString(val,d,1,2);
437 if (seconde) d -= 2- seconde.length;
438 }
439
440 i+=substr.length;
441 d+=substr.length;
442 }
443
444 // Check if format param are ok
445 if (year==null||year<1) { return 0; }
446 if (month==null||(month<1)||(month>12)) { return 0; }
447 if (day==null||(day<1)||(day>31)) { return 0; }
448 if (hour==null||(hour<0)||(hour>24)) { return 0; }
449 if (minute==null||(minute<0)||(minute>60)) { return 0; }
450 if (seconde==null||(seconde<0)||(seconde>60)) { return 0; }
451
452 // alert(year+' '+month+' '+day+' '+hour+' '+minute+' '+seconde);
453 return new Date(year,month-1,day,hour,minute,seconde);
454}
455
456/*
457 * =================================================================
458 * Function: stringIsInteger(string)
459 * Purpose: Return true if string is an integer
460 * ==================================================================
461 */
462function stringIsInteger(str)
463{
464 var digits="1234567890";
465 for (var i=0; i < str.length; i++)
466 {
467 if (digits.indexOf(str.charAt(i))==-1)
468 {
469 return false;
470 }
471 }
472 return true;
473}
474
475/*
476 * =================================================================
477 * Function: getIntegerInString(string,pos,minlength,maxlength)
478 * Purpose: Return part of string from position i that is integer
479 * ==================================================================
480 */
481function getIntegerInString(str,i,minlength,maxlength)
482{
483 for (var x=maxlength; x>=minlength; x--)
484 {
485 var substr=str.substring(i,i+x);
486 if (substr.length < minlength) { return null; }
487 if (stringIsInteger(substr)) { return substr; }
488 }
489 return null;
490}
491
492
493/*
494 * =================================================================
495 * Purpose: Clean string to have it url encoded
496 * Input: s
497 * Author: Laurent Destailleur
498 * Licence: GPL
499 * ==================================================================
500 */
501function urlencode(s) {
502 var news = s;
503 news = news.replace(/\+/gi,'%2B');
504 news = news.replace(/&/gi,'%26');
505 return news;
506}
507
508/*
509 * =================================================================
510 * Purpose: Clean string to get a HTML coded string.
511 * Input: s
512 * Author: Laurent Destailleur
513 * Licence: GPL
514 * ==================================================================
515 */
516function htmlEntityDecodeJs(inp){
517 var replacements = {'&lt;':'<','&gt;':'>','&sol;':'/','&quot;':'"','&apos;':'\'','&amp;':'&','&nbsp;':' '};
518 if (inp) {
519 for(var r in replacements){
520 inp = inp.replace(new RegExp(r,'g'),replacements[r]);
521 }
522 return inp.replace(/&#(\d+);/g, function(match, dec) {
523 return String.fromCharCode(dec);
524 });
525 } else {
526 return '';
527 }
528}
529
530
531/*
532 * =================================================================
533 * Purpose: Applique un delai avant execution. Used for autocompletion of companies.
534 * Input: funct, delay
535 * Author: Regis Houssin
536 * Licence: GPL
537 * ==================================================================
538 */
539 function ac_delay(funct,delay) {
540 // delay before start of action
541 setTimeout(funct,delay);
542}
543
544
545/*
546 * =================================================================
547 * Purpose:
548 * Clean values of a "Sortable.serialize". Used by drag and drop.
549 * Input: expr
550 * Author: Regis Houssin
551 * Licence: GPL
552 * ==================================================================
553 */
554function cleanSerialize(expr) {
555 if (typeof(expr) != 'string') {
556 return '';
557 }
558 var reg = new RegExp("(&)", "g");
559 var reg2 = new RegExp("[^A-Z0-9,]", "g");
560 var liste1 = expr.replace(reg, ",");
561 return liste1.replace(reg2, "");
562}
563
564
565/*
566 * =================================================================
567 * Purpose: Display a temporary message in input text fields (For showing help message on
568 * input field).
569 * Input: fieldId
570 * Input: message
571 * Author: Regis Houssin
572 * Licence: GPL
573 * TODO To remove, seems not used
574 * ==================================================================
575 */
576function displayMessage(fieldId,message) {
577 var textbox = document.getElementById(fieldId);
578 if (textbox.value == '') {
579 textbox.style.color = 'grey';
580 textbox.value = message;
581 }
582}
583
584/*
585 * =================================================================
586 * Purpose: Hide a temporary message in input text fields (For showing help message on
587 * input field).
588 * Input: fiedId
589 * Input: message
590 * Author: Regis Houssin
591 * Licence: GPL
592 * TODO To remove, seems not used
593 * ==================================================================
594 */
595function hideMessage(fieldId,message) {
596 var textbox = document.getElementById(fieldId);
597 textbox.style.color = 'black';
598 if (textbox.value == message) textbox.value = '';
599}
600
601
602/*
603 * Used by button to set on/off.
604 * Call url then make complementary action (like show/hide, enable/disable or set another option).
605 *
606 * @param string url Url (warning: as any url called in ajax mode, the url called here must not renew the token)
607 * @param string code Code
608 * @param string input Array of complementary actions to do if success
609 * @param int entity Entity
610 * @param int strict Strict (0=?, 1=?)
611 * @param int forcereload Force reload
612 * @param int userid User id
613 * @param string token Token
614 * @param int value Value to set
615 * @param int userconst 1=On/Off of user constant instead of global const
616 * @return boolean
617 */
618function setConstant(url, code, input, entity, strict, forcereload, userid, token, value, userconst) {
619 var saved_url = url; /* avoid undefined url */
620
621 $.post( url, {
622 action: "set",
623 name: code,
624 entity: entity,
625 token: token,
626 value: value,
627 userconst: userconst
628 },
629 function() { /* handler for success of post */
630 console.log("Ajax url request to set constant is a success. Make complementary actions and then forcereload="+forcereload+" value="+value);
631 if (value == 0) {
632 $("#set_" + code).show();
633 $("#del_" + code).hide();
634 } else {
635 $("#set_" + code).hide();
636 $("#del_" + code).show();
637 }
638 $.each(input, function(type, data) {
639 // Enable another element
640 if (type == "disabled" && strict != 1) {
641 $.each(data, function(key, value) {
642 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
643 $(newvalue).removeAttr("disabled");
644 if ($(newvalue).hasClass("butActionRefused") == true) {
645 $(newvalue).removeClass("butActionRefused");
646 $(newvalue).addClass("butAction");
647 }
648 });
649 } else if (type == "enabled") {
650 $.each(data, function(key, value) {
651 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
652 if (strict == 1)
653 $(newvalue).removeAttr("disabled");
654 else
655 $(newvalue).attr("disabled", true);
656 if ($(newvalue).hasClass("butAction") == true) {
657 $(newvalue).removeClass("butAction");
658 $(newvalue).addClass("butActionRefused");
659 }
660 });
661 // Show another element
662 } else if (type == "showhide" || type == "show") {
663 $.each(data, function(key, value) {
664 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
665 $(newvalue).show();
666 });
667 // Set another constant
668 } else if (type == "set") {
669 $.each(data, function(key, value) {
670 $("#set_" + key).hide();
671 $("#del_" + key).show();
672 $.post( saved_url, {
673 action: "set",
674 name: key,
675 value: value,
676 entity: entity,
677 token: token
678 });
679 });
680 }
681 });
682 if (forcereload) {
683 var url = window.location.href;
684
685 /* reset action param */
686 url = url.replace(/action=\w+/g, '');
687
688 /* reset dol_resetcache param */
689 if (url.indexOf('dol_resetcache') < 0) {
690 if (url.indexOf('?') > -1) {
691 url = url + "&dol_resetcache=1";
692 } else {
693 url = url + "?dol_resetcache=1";
694 }
695 }
696
697 /* reset page_y param */
698 var page_y = $(document).scrollTop();
699 url = url.replace(/page_y=\d+/g, '');
700 if (page_y > 0) {
701 if (url.indexOf('?') > -1) {
702 url = url + "&page_y="+page_y;
703 } else {
704 url = url + "?page_y="+page_y;
705 }
706 }
707 url = url.replace(/&&+/, '&');
708 console.log("url ro redirect = "+url);
709
710 window.location.href = url;
711 //location.reload();
712 return false;
713 }
714 }).fail(function(error) { console.log("Error, we force reload"); location.reload(); }); /* When it fails, we always force reload to have setEventErrorMessages in session visible */
715
716 return true;
717}
718
719/*
720 * Used by button to set on/off
721 * Call url then make complementary action (like show/hide, enable/disable or set another option).
722 *
723 * @param string url Url (warning: as any url called in ajax mode, the url called here must not renew the token)
724 * @param string code Code
725 * @param string input Array of complementary actions to do if success
726 * @param int entity Entity
727 * @param int strict Strict
728 * @param int forcereload Force reload
729 * @param int userid User id
730 * @param string token Token
731 * @param int userconst 1=On/Off of user constant instead of global const
732 * @return boolean
733 */
734function delConstant(url, code, input, entity, strict, forcereload, userid, token, userconst) {
735 var saved_url = url; /* avoid undefined url */
736
737 $.post( url, {
738 action: "del",
739 name: code,
740 entity: entity,
741 token: token,
742 userconst: userconst
743 },
744 function() {
745 console.log("Ajax url request to delete constant is success. Make complementary actions and then forcereload="+forcereload);
746 $("#del_" + code).hide();
747 $("#set_" + code).show();
748 $.each(input, function(type, data) {
749 // Disable another element
750 if (type == "disabled") {
751 $.each(data, function(key, value) {
752 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
753 $(newvalue).attr("disabled", true);
754 if ($(newvalue).hasClass("butAction") == true) {
755 $(newvalue).removeClass("butAction");
756 $(newvalue).addClass("butActionRefused");
757 }
758 });
759 } else if (type == "enabled" && strict != 1) {
760 $.each(data, function(key, value) {
761 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
762 $(newvalue).removeAttr("disabled");
763 if ($(newvalue).hasClass("butActionRefused") == true) {
764 $(newvalue).removeClass("butActionRefused");
765 $(newvalue).addClass("butAction");
766 }
767 });
768 // Hide another element
769 } else if (type == "showhide" || type == "hide") {
770 $.each(data, function(key, value) {
771 var newvalue=((value.search("^#") < 0 && value.search("^\.") < 0) ? "#" : "") + value;
772 $(newvalue).hide();
773 });
774 // Delete another constant
775 } else if (type == "del") {
776 $.each(data, function(key, value) {
777 $("#del_" + value).hide();
778 $("#set_" + value).show();
779 $.post( saved_url, {
780 action: "del",
781 name: value,
782 entity: entity,
783 token: token
784 });
785 });
786 }
787 });
788 if (forcereload) {
789 var url = window.location.href;
790 if (url.indexOf('dol_resetcache') < 0) {
791 if (url.indexOf('?') > -1) {
792 url = url + "&dol_resetcache=1";
793 } else {
794 url = url + "?dol_resetcache=1";
795 }
796 }
797 var page_y = $(document).scrollTop();
798 url = url.replace(/page_y=\d+/g, '');
799 if (page_y > 0) {
800 if (url.indexOf('?') > -1) {
801 url = url + "&page_y="+page_y;
802 } else {
803 url = url + "?page_y="+page_y;
804 }
805 }
806 url = url.replace(/&&+/, '&');
807 console.log("url ro redirect = "+url);
808
809 window.location.href = url;
810 //location.reload();
811 return false;
812 }
813 }).fail(function(error) { console.log("Error, we force reload"); location.reload(); }); /* When it fails, we always force reload to have setEventErrorMessages in session visible */
814
815 return true;
816}
817
818/*
819 * Call the setConstant or delConstant but with a confirmation before.
820 * Used by button to set on/off.
821 *
822 * @param string action Action
823 * @param string url Url
824 * @param string code Code
825 * @param string input Array of complementary actions to do if success
826 * @param string box Box
827 * @param int entity Entity
828 * @param int yesButton yesButton
829 * @param int noButton noButton
830 * @param int strict Strict
831 * @param int userid User id
832 * @param string token Token
833 * @return boolean
834 */
835function confirmConstantAction(action, url, code, input, box, entity, yesButton, noButton, strict, userid, token) {
836 var boxConfirm = box;
837 $("#confirm_" + code)
838 .attr("title", boxConfirm.title)
839 .html(boxConfirm.content)
840 .dialog({
841 resizable: false,
842 height: 170,
843 width: 500,
844 modal: true,
845 buttons: [
846 {
847 id : 'yesButton_' + code,
848 text : yesButton,
849 click : function() {
850 if (action == "set") {
851 setConstant(url, code, input, entity, strict, 0, userid, token, 1);
852 } else if (action == "del") {
853 delConstant(url, code, input, entity, strict, 0, userid, token);
854 }
855 // Close dialog
856 $(this).dialog("close");
857 // Execute another method
858 if (boxConfirm.method) {
859 var fnName = boxConfirm.method;
860 if (window.hasOwnProperty(fnName)) {
861 window[fnName]();
862 }
863 }
864 }
865 },
866 {
867 id : 'noButton_' + code,
868 text : noButton,
869 click : function() {
870 $(this).dialog("close");
871 }
872 }
873 ]
874 });
875 // For information dialog box only, hide the noButton
876 if (boxConfirm.info) {
877 $("#noButton_" + code).button().hide();
878 }
879
880 return true;
881}
882
883
884/*
885 * =================================================================
886 * This is to allow to transform all select box into ajax autocomplete box
887 * with just one line:
888 * $(function() { $( "#idofmylist" ).combobox(); });
889 * Do not use it on large combo boxes
890 * =================================================================
891 */
892(function( $ ) {
893 $.widget( "ui.combobox", {
894 options: {
895 minLengthToAutocomplete: 0
896 },
897 _create: function() {
898 var savMinLengthToAutocomplete = this.options.minLengthToAutocomplete;
899 var self = this,
900 select = this.element.hide(),
901 selected = select.children( ":selected" ),
902 value = selected.val() ? selected.text() : "";
903 var input = this.input = $( "<input>" )
904 .insertAfter( select )
905 .val( value )
906 .attr('id', 'inputautocomplete'+select.attr('id'))
907 .autocomplete({
908 delay: 0,
909 minLength: this.options.minLengthToAutocomplete,
910 source: function( request, response ) {
911 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
912 response( select.children( "option:enabled" ).map(function() {
913 var text = $( this ).text();
914 if ( this.value && ( !request.term || matcher.test(text) ) )
915 return {
916 label: text.replace(
917 new RegExp(
918 "(?![^&;]+;)(?!<[^<>]*)(" +
919 $.ui.autocomplete.escapeRegex(request.term) +
920 ")(?![^<>]*>)(?![^&;]+;)", "gi"
921 ), "<strong>$1</strong>" ),
922 value: text,
923 option: this
924 };
925 }) );
926 },
927 select: function( event, ui ) {
928 ui.item.option.selected = true;
929 self._trigger( "selected", event, {
930 item: ui.item.option
931 });
932 },
933 change: function( event, ui ) {
934 if ( !ui.item ) {
935 var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
936 valid = false;
937 select.children( "option" ).each(function() {
938 if ( $( this ).text().match( matcher ) ) {
939 this.selected = valid = true;
940 return false;
941 }
942 });
943 if ( !valid ) {
944 // remove invalid value, as it didn't match anything
945 $( this ).val( "" );
946 select.val( "" );
947 input.data("ui-autocomplete").term = "";
948 return false;
949 }
950 }
951 }
952 })
953 .addClass( "ui-widget ui-widget-content ui-corner-left dolibarrcombobox" );
954
955 const widgetInstance = input.data("ui-autocomplete");
956 if (widgetInstance) {
957 widgetInstance._renderItem = function( ul, item ) {
958 return $("<li>")
959 .data( "ui-autocomplete-item", item ) // jQuery UI > 1.10.0
960 .append( "<a>" + item.label + "</a>" )
961 .appendTo( ul );
962 };
963 }
964
965 this.button = $( "<button type=\'button\'>&nbsp;</button>" )
966 .attr( "tabIndex", -1 )
967 .attr( "title", "Show All Items" )
968 .insertAfter( input )
969 .button({
970 icons: {
971 primary: "ui-icon-triangle-1-s"
972 },
973 text: false
974 })
975 .removeClass( "ui-corner-all" )
976 .addClass( "ui-corner-right ui-button-icon" )
977 .click(function() {
978 // close if already visible
979 if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
980 input.autocomplete( "close" );
981 return;
982 }
983
984 // pass empty string as value to search for, displaying all results
985 input.autocomplete({ minLength: 0 });
986 input.autocomplete( "search", "" );
987 input.autocomplete({ minLength: savMinLengthToAutocomplete });
988 input.focus();
989 });
990 },
991
992 destroy: function() {
993 this.input.remove();
994 this.button.remove();
995 this.element.show();
996 $.Widget.prototype.destroy.call( this );
997 }
998 });
999})( jQuery );
1000
1001
1002
1009function copyToClipboard(text,text2)
1010{
1011 text = text.replace(/<br>/g,"\n");
1012 var newElem = '<textarea id="coordsforpopup" style="border: none; width: 90%; height: 120px;">'+text+'</textarea><br><br>'+text2;
1013 /* alert(newElem); */
1014 $("#dialogforpopup").html(newElem);
1015 $("#dialogforpopup").dialog();
1016 $("#coordsforpopup").select();
1017
1018 return false;
1019}
1020
1021
1030function newpopup(url, title) {
1031 var argv = newpopup.arguments;
1032 var argc = newpopup.arguments.length;
1033 var tmp = url;
1034 console.log("newpopup "+argv[2]+" "+argv[3]);
1035 var l = (argc > 2) ? argv[2] : 600;
1036 var h = (argc > 3) ? argv[3] : 400;
1037 var left = (screen.width - l)/2;
1038 var top = (screen.height - h)/2;
1039 var wfeatures = "directories=0,menubar=0,status=0,resizable=0,scrollbars=1,toolbar=0,location=0,width=" + l +",height=" + h + ",left=" + left + ",top=" + top;
1040 fen = window.open(tmp, title, wfeatures);
1041
1042 return false;
1043}
1044
1055function document_preview(file, type, title)
1056{
1057 var ValidImageTypes = ["image/gif", "image/jpeg", "image/png", "image/webp"];
1058 var showOriginalSizeButton = false;
1059
1060 console.log("document_preview A click was done: file="+file+", type="+type+", title="+title);
1061
1062 if ($.inArray(type, ValidImageTypes) < 0) {
1063 /* Not an image */
1064 var width='85%';
1065 var object_width='100%';
1066 var height = ($( window ).height() - 60) * 0.90;
1067 var object_height='98%';
1068
1069 show_preview('notimage');
1070
1071 } else {
1072 /* This is an image */
1073 var object_width=0;
1074 var object_height=0;
1075
1076 var img = new Image();
1077
1078 img.onload = function() {
1079 object_width = this.width;
1080 object_height = this.height;
1081
1082 width = $( window ).width()*0.90;
1083 console.log("object_width="+object_width+" window width="+width);
1084 if(object_width < width){
1085 console.log("Object width is small, we set width of popup according to image width.");
1086 width = object_width + 30
1087 }
1088 height = $( window ).height()*0.85;
1089 console.log("object_height="+object_height+" window height="+height);
1090 if(object_height < height){
1091 console.log("Object height is small, we set height of popup according to image height.");
1092 height = object_height + 100
1093 }
1094 else
1095 {
1096 showOriginalSizeButton = true;
1097 }
1098
1099 show_preview('image');
1100
1101 };
1102 img.src = file;
1103 }
1104
1105 /* This function is local to document_preview. Variables like file, type, title, object_width and object_height are global inside this function */
1106 function show_preview(mode) {
1107 /* console.log("mode="+mode+" file="+file+" type="+type+" title=title+" width="+width+" height="+height); */
1108 var newElem = '<object name="objectpreview" data="'+file+'" type="'+type+'" width="'+object_width+'" height="'+object_height+'" param="noparam"></object>';
1109
1110 optionsbuttons = {}
1111 if (mode == 'image' && showOriginalSizeButton)
1112 {
1113 var curRot = 0;
1114 optionsbuttons = {
1115 "<?php echo dol_escape_js($langs->transnoentitiesnoconv("OriginalSize")); ?>": function() { console.log("Click on original size"); jQuery(".ui-dialog-content.ui-widget-content > object").css({ "max-height": "none" }); },
1116 "<?php echo dol_escape_js($langs->transnoentitiesnoconv("RotateImage")); ?>": function() { curRot += 90; jQuery(".ui-dialog-content.ui-widget-content > object").css("transform","rotate(" + curRot + "deg)"); },
1117 "<?php echo dol_escape_js($langs->transnoentitiesnoconv("CloseWindow")); ?>": function() { $( this ).dialog( "close" ); }
1118 };
1119 }
1120
1121 $("#dialogforpopup").addClass("center");
1122 $("#dialogforpopup").html(newElem);
1123
1124 $("#dialogforpopup").dialog({
1125 closeOnEscape: true,
1126 resizable: true,
1127 width: width,
1128 height: height,
1129 modal: true,
1130 title: title,
1131 buttons: optionsbuttons
1132 });
1133
1134 if (showOriginalSizeButton)
1135 {
1136 jQuery(".ui-dialog-content.ui-widget-content > object").css({ "max-height": "100%", "width": "auto", "margin-left": "auto", "margin-right": "auto", "display": "block" });
1137 }
1138 }
1139}
1140
1141/*
1142 * Provide a function to get an URL GET parameter in javascript
1143 *
1144 * @param name Name of parameter
1145 * @param valueifnotfound Value if not found
1146 * @return string Value
1147 */
1148function getParameterByName(name, valueifnotfound)
1149{
1150 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
1151 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
1152 results = regex.exec(location.search);
1153 return results === null ? valueifnotfound : decodeURIComponent(results[1].replace(/\+/g, " "));
1154}
1155
1159function getOperatorsForFieldType(type, maybenull = 0) {
1160 console.log('Get list of operators for type='+type);
1161
1162 // Define the list of operators for each general field category
1163 const operatorList = {
1164 selectlink: {
1165 Is: '<?php print dol_escape_js($langs->trans('Is')); ?>',
1166 IsNot: '<?php print dol_escape_js($langs->trans('IsNot')); ?>',
1167 },
1168 text: {
1169 Contains: '<?php print dol_escape_js($langs->trans('Contains')); ?>',
1170 DoesNotContain: '<?php print dol_escape_js($langs->trans('DoesNotContain')); ?>',
1171 Is: '<?php print dol_escape_js($langs->trans('Is')); ?>',
1172 IsNot: '<?php print dol_escape_js($langs->trans('IsNot')); ?>',
1173 StartsWith: '<?php print dol_escape_js($langs->trans('StartsWith')); ?>',
1174 EndsWith: '<?php print dol_escape_js($langs->trans('EndsWith')); ?>'
1175 },
1176 number: {
1177 '=': '<?php print dol_escape_js($langs->trans('Is')); ?>',
1178 '!=': '<?php print dol_escape_js($langs->trans('IsNot')); ?>',
1179 '<': '<?php print dol_escape_js($langs->trans('IsLowerThan')); ?>',
1180 '>': '<?php print dol_escape_js($langs->trans('IsHigherThan')); ?>',
1181 '<=': '<?php print dol_escape_js($langs->trans('IsLowerThanOrEqual')); ?>',
1182 '>=': '<?php print dol_escape_js($langs->trans('IsHigherThanOrEqual')); ?>',
1183 },
1184 date: {
1185 Is: '<?php print dol_escape_js($langs->trans('Is')); ?>',
1186 IsNot: '<?php print dol_escape_js($langs->trans('IsNot')); ?>',
1187 IsBefore: '<?php print dol_escape_js($langs->trans('IsBefore')); ?>',
1188 IsAfter: '<?php print dol_escape_js($langs->trans('IsAfter')); ?>',
1189 IsOnOrBefore: '<?php print dol_escape_js($langs->trans('IsOnOrBefore')); ?>',
1190 IsOnOrAfter: '<?php print dol_escape_js($langs->trans('IsOnOrAfter')); ?>'
1191 },
1192 html: {
1193 Contains: '<?php print $langs->trans('Contains'); ?>'
1194 }
1195 };
1196
1197 // Determine the general category for the given type using regex
1198 let generalType = "";
1199
1200 if (/^select$/i.test(type) || /^link$/i.test(type)) {
1201 generalType = "selectlink";
1202 } else if (/^(varchar|char|text|blob|nchar|mediumtext|longtext)\(\d+\)$/i.test(type) || /^(varchar|mail|phone|ip)$/i.test(type)) {
1203 generalType = "text";
1204 } else if (/^(int|integer|float|double|decimal|numeric)(\(\d+,\d+\))?$/i.test(type)) {
1205 generalType = "number";
1206 } else if (/^(date|datetime|timestamp)$/i.test(type)) {
1207 generalType = "date";
1208 } else if (/^(tinyint|smallint)\(\d+\)$/i.test(type)) {
1209 generalType = "number";
1210 } else if (/^html$/i.test(type)) {
1211 generalType = "html";
1212 } else {
1213 // Handle unknown or unsupported types
1214 console.log("The type of field "+type+" is not supported");
1215 return [];
1216 }
1217
1218 // If maybenull is true, then append the "IsDefined" and "IsNotDefined" operators
1219 if (maybenull === 1) {
1220 operatorList[generalType]["IsDefined"] = '<?php print dol_escape_js($langs->trans('IsDefined')); ?>';
1221 operatorList[generalType]["IsNotDefined"] = '<?php print dol_escape_js($langs->trans('IsNotDefined')); ?>';
1222 }
1223
1224 // Return the operators for the general type, or an empty array if not found
1225 return operatorList[generalType] || [];
1226}
1227
1231function generateFilterString(column, operator, context, fieldType) {
1232 let filter = "";
1233
1234 console.log("generateFilterString column="+column+" operator="+operator+" context="+context+" fieldType="+fieldType);
1235
1236 switch (operator) {
1237 case "Contains":
1238 filter = column + " like \'%" + context + "%\'";
1239 break;
1240 case "DoesNotContain":
1241 filter = column + " notlike \'%" + context + "%\'";
1242 break;
1243 case "Is":
1244 filter = column + " = \'" + context + "\'";
1245 break;
1246 case "IsNot":
1247 filter = column + " != \'" + context + "\'";
1248 break;
1249 case "StartsWith":
1250 filter = column + " like \'" + context + "%\'";
1251 break;
1252 case "EndsWith":
1253 filter = column + " like \'%" + context + "\'";
1254 break;
1255 case "IsDefined":
1256 filter = column + ":isnot:null";
1257 break;
1258 case "IsNotDefined":
1259 filter = column + ":is:null";
1260 break;
1261 case "=":
1262 filter = column + " = \'" + context + "\'";
1263 break;
1264 case "!=":
1265 filter = column + " != \'" + context + "\'";
1266 break;
1267 case "<":
1268 filter = column + " < \'" + context + "\'";
1269 break;
1270 case ">":
1271 filter = column + " > \'" + context + "\'";
1272 break;
1273 case "<=":
1274 filter = column + " <= \'" + context + "\'";
1275 break;
1276 case ">=":
1277 filter = column + " >= \'" + context + "\'";
1278 break;
1279 case "IsBefore":
1280 filter = column + " < \'" + context + "\'";
1281 break;
1282 case "IsAfter":
1283 filter = column + " > \'" + context + "\'";
1284 break;
1285 case "IsOnOrBefore":
1286 filter = column + " <= \'" + context + "\'";
1287 break;
1288 case "IsOnOrAfter":
1289 filter = column + " >= \'" + context + "\'";
1290 break;
1291 default:
1292 filter = "";
1293 }
1294
1295 return filter;
1296}
1297
1298// Code in the public domain from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
1299(function() {
1308 function decimalAdjust(type, value, exp) {
1309 // If the exp is undefined or zero...
1310 if (typeof exp === 'undefined' || +exp === 0) {
1311 return Math[type](value);
1312 }
1313 value = +value;
1314 exp = +exp;
1315 // If the value is not a number or the exp is not an integer...
1316 if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
1317 return NaN;
1318 }
1319 // Shift
1320 value = value.toString().split('e');
1321 value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
1322 // Shift back
1323 value = value.toString().split('e');
1324 return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
1325 }
1326
1327 // Decimal round
1328 if (!Math.round10) {
1329 Math.round10 = function(value, exp) {
1330 return decimalAdjust('round', value, exp);
1331 };
1332 }
1333 // Decimal floor
1334 if (!Math.floor10) {
1335 Math.floor10 = function(value, exp) {
1336 return decimalAdjust('floor', value, exp);
1337 };
1338 }
1339 // Decimal ceil
1340 if (!Math.ceil10) {
1341 Math.ceil10 = function(value, exp) {
1342 return decimalAdjust('ceil', value, exp);
1343 };
1344 }
1345})();
1346
1347// Another solution, easier, to build a javascript rounding function
1348function dolroundjs(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
1349
1367function pricejs(amount, mode = 'MT', currency_code = '', force_locale = '') {
1368 var main_max_dec_shown = <?php echo (int) str_replace('.', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN')); ?>;
1369 var main_rounding_unit = <?php echo (int) getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); ?>;
1370 var main_rounding_tot = <?php echo (int) getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); ?>;
1371 var main_decimal_separator = <?php echo json_encode($dec) ?>;
1372 var main_thousand_separator = <?php echo json_encode($thousand) ?>;
1373 var locale_code = force_locale || <?php echo json_encode($langs->defaultlang) ?>;
1374 var amountAsLocalizedString;
1375 var useIntl = Boolean(Intl && Intl.NumberFormat);
1376 var nDigits;
1377 if (currency_code === 'auto') currency_code = <?php echo json_encode($conf->currency) ?>;
1378
1379 if (mode === 'MU') nDigits = main_rounding_unit;
1380 else if (mode === 'MT') nDigits = main_rounding_tot;
1381 else return 'Bad value for parameter mode';
1382
1383 if (useIntl) {
1384 // simple version: let the browser decide how to format the number using the provided language / currency
1385 // parameters
1386 var formattingOptions = {
1387 minimumFractionDigits: nDigits,
1388 maximumFractionDigits: nDigits
1389 };
1390 if (currency_code) {
1391 formattingOptions['style'] = 'currency';
1392 formattingOptions['currency'] = currency_code;
1393 }
1394 return Intl.NumberFormat(locale_code.replace('_', '-'), formattingOptions).format(amount);
1395 }
1396
1397 // No Intl -> attempt to format the number in a way similar to Dolibarr PHP's `price()` function
1398 amountAsLocalizedString = amount.toFixed(nDigits).replace(
1399 /((?!^)(?:\d{3})*)(?:\.(\d+))?$/,
1400 (fullMatch, digitsByThree, decimals) =>
1401 digitsByThree.replace(
1402 /\d{3}/g,
1403 (groupOfThree) => main_thousand_separator + groupOfThree
1404 ) + (decimals !== undefined ? main_decimal_separator + decimals : '')
1405 ).replace(/ /, ' ');
1406 if (!currency_code) return amountAsLocalizedString;
1407
1408 // print with currency
1409 var currency_symbol = currency_code;
1410
1411 // codes of languages / currencies where the symbol must be placed before the amount
1412 var currencyBeforeAmountCodes = {
1413 currency: ['AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD'],
1414 language: ['nl_NL']
1415 };
1416
1417 if (currencyCache[currency_code]
1418 && currencyCache[currency_code]['unicode']
1419 && currencyCache[currency_code]['unicode'].length) {
1420 currency_symbol = currencyCache[currency_code]['unicode'].reduce(function (res, cur) {return res + cur}, '');
1421 }
1422
1423 if (currencyBeforeAmountCodes.currency.indexOf(currency_code) >= 0
1424 || currencyBeforeAmountCodes.language.indexOf(locale_code)) {
1425 // if we use a language or a currency where the symbol is placed before the amount
1426 return currency_symbol + amountAsLocalizedString;
1427 }
1428
1429 // by default: currency symbol after the amount
1430 return amountAsLocalizedString + ' ' + currency_symbol;
1431}
1432
1440function price2numjs(amount) {
1441 if (amount == '') return '';
1442
1443 var dec = <?php echo json_encode($dec) ?>;
1444 var thousand = <?php echo json_encode($thousand) ?>;
1445
1446 var main_max_dec_shown = <?php echo (int) str_replace('.', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN')); ?>;
1447 var main_rounding_unit = <?php echo (int) getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); ?>;
1448 var main_rounding_tot = <?php echo (int) getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); ?>;
1449
1450 var amount = amount.toString();
1451
1452 // rounding for unit price
1453 var rounding = main_rounding_unit;
1454 var pos = amount.indexOf(dec);
1455 var decpart = '';
1456 if (pos >= 0) {
1457 decpart = amount.substring(pos + 1).replace('/0+$/i', ''); // Remove 0 for decimal part
1458 }
1459 var nbdec = decpart.length;
1460 if (nbdec > rounding) {
1461 rounding = nbdec;
1462 }
1463 // If rounding higher than max shown
1464 if (rounding > main_max_dec_shown) rounding = main_max_dec_shown;
1465 if (thousand != ',' && thousand != '.') amount = amount.replace(',', '.');
1466 amount = amount.replace(' ', ''); // To avoid spaces
1467 amount = amount.replace(thousand, ''); // Replace of thousand before replace of dec to avoid pb if thousand is .
1468 amount = amount.replace(dec, '.');
1469
1470 //console.log("amount before="+amount+" rounding="+rounding)
1471 var res = Math.round10(amount, - rounding);
1472 // Other solution is
1473 // var res = dolroundjs(amount, rounding)
1474
1475 console.log("price2numjs text="+amount+" return="+res);
1476
1477 return res;
1478}
1479
1480
1481<?php
1482if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
1483 ?>
1484// Defined properties for JNotify
1485$(document).ready(function() {
1486 if (typeof $.jnotify == 'function') {
1487 $.jnotify.setup({
1488 delay: 3000 // the default time to show each notification (in milliseconds)
1489 , sticky: false // determines if the message should be considered "sticky" (user must manually close notification)
1490 , closeLabel: "&times;" // the HTML to use for the "Close" link
1491 , showClose: true // determines if the "Close" link should be shown if notification is also sticky
1492 , fadeSpeed: 1000 // the speed to fade messages out (in milliseconds)
1493 , slideSpeed: 250 // the speed used to slide messages out (in milliseconds)
1494 , classContainer: "jnotify-container"
1495 , classNotification: "jnotify-notification"
1496 , classBackground: "jnotify-background"
1497 , classClose: "jnotify-close"
1498 , classMessage: "jnotify-message"
1499 , init: null // callback that occurs when the main jnotify container is created
1500 , create: null // callback that occurs when when the note is created (occurs just before appearing in DOM)
1501 , beforeRemove: null // callback that occurs when before the notification starts to fade away
1502 });
1503 }
1504});
1505 <?php
1506} ?>
1507
1508
1509
1510jQuery(document).ready(function() {
1511 // Force to hide menus when page is inside an iFrame so we can show any page into a dialog popup
1512 if (window.location && window.location.pathname.indexOf("core/frames.php") == -1 && window.location.pathname.indexOf("externalsite/frametop.php") == -1 && window.location !== window.parent.location ) {
1513 console.log("Page is detected to be into an iframe, we hide by CSS the menus");
1514 // The page is in an iframe
1515 jQuery(".side-nav-vert, .side-nav, .websitebar").hide();
1516 jQuery(".id-container").css('width', '100%');
1517
1518 }
1519
1520
1521 // Code to set tooltip on search field
1522 jQuery('table.liste tr.liste_titre_filter td.liste_titre input[name^="search"][type=text]:not(".maxwidthdate")').attr('title', '<?php echo dol_escape_js($langs->transnoentities("SearchSyntaxTooltipForStringOrNum")) ?>');
1523
1524
1525 // Code to toggle dropdown components
1526 jQuery(document).on("click", ".butAction.dropdown-toggle", function(event) {
1527 console.log("Click on .butAction.dropdown-toggle");
1528 let parentHolder = jQuery(event.target).parent();
1529 let dropDownContent = parentHolder.children(".dropdown-content");
1530 let offset = parentHolder.offset();
1531 let widthDocument = $(document).width();
1532 let heightDocument = $(document).height();
1533 let right = widthDocument - offset.left - parentHolder.width();
1534 let widthPopup = parentHolder.children(".dropdown-content").width();
1535 if (widthPopup + right >= widthDocument) {
1536 //right = 10;
1537 }
1538
1539 parentHolder.toggleClass("open"); /* If open, it closes, if closed, it opens */
1540
1541 // Check tooltip is in viewport
1542 let dropDownContentTop = dropDownContent.offset().top;
1543 let dropDownContentLeft = dropDownContent.offset().left;
1544 let dropDownContentHeight = dropDownContent.outerHeight();
1545 let dropDownContentBottom = dropDownContentTop + dropDownContentHeight;
1546 let viewportBottom = $(window).scrollTop() + $(window).height();
1547
1548 // Change dropdown Up/Down orientation if dropdown is close to bottom viewport
1549 if (parentHolder.hasClass('open')
1550 && dropDownContentBottom > viewportBottom // Check bottom of dropdown is behind viewport
1551 && dropDownContentTop - dropDownContentHeight > 0 // check if set dropdown to --up will not go over the top of document
1552 ) {
1553 parentHolder.addClass("--up");
1554 } else {
1555 parentHolder.removeClass("--up");
1556 }
1557
1558 // Change dropdown left/right offset if dropdown is close to left viewport
1559 if (parentHolder.hasClass('open') && dropDownContentLeft < 0) {
1560 parentHolder.addClass("--left");
1561 } else {
1562 parentHolder.removeClass("--left");
1563 }
1564 });
1565
1566
1567 // Close drop down
1568 jQuery(document).on("click", function(event) {
1569 // search if click was outside drop down
1570 if (!$(event.target).closest('.butAction.dropdown-toggle').length) {
1571 /* console.log("click close butAction - we click outside"); */
1572 let parentholder = jQuery(".butAction.dropdown-toggle").closest(".dropdown.open");
1573 if (parentholder){
1574 // Hide the menus.
1575 parentholder.removeClass("open --up --left");
1576 }
1577 }
1578 });
1579
1580
1581});
1582
1583
1584// Code to manage the js for combo list with dependencies (called by extrafields_view.tpl.php)
1585function showOptions(child_list, parent_list) {
1586 var parentInput = $("select[name="+parent_list+"]");
1587 if (parentInput.length === 0) { // when parent extra-field is in view mode and the child is edited directly on card (on line edit)
1588 parentInput = $("input[name="+parent_list+"]");
1589 }
1590 if (parentInput.length > 0) {
1591 var val = parentInput.val();
1592 var parentVal = parent_list + ":" + val;
1593 if (val > 0) {
1594 $("select[name=\""+child_list+"\"] option[parent]").prop("disabled", true).hide(); // hide not work with select2 element so disabled it
1595 $("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").prop('disabled', false).show(); // show not work with select2 element so enabled it
1596 } else {
1597 $("select[name=\""+child_list+"\"] option").prop("disabled", false).show(); // show not work with select2 element so enabled it
1598 }
1599 }
1600}
1601function setListDependencies() {
1602 console.log("setListDependencies");
1603 jQuery("select option[parent]").parent().each(function() {
1604 var child_list = $(this).attr("name");
1605 var parent = $(this).find("option[parent]:first").attr("parent");
1606 var infos = parent.split(":");
1607 var parent_list = infos[0];
1608 showOptions(child_list, parent_list);
1609
1610 /* Activate the handler to call showOptions on each future change */
1611 $("select[name=\""+parent_list+"\"]").change(function() {
1612 showOptions(child_list, parent_list);
1613 });
1614 });
1615}
1616
1617
1618<?php
1619if (!getDolGlobalString('MAIN_DISABLE_SELECT2_FOCUS_PROTECTION') && !defined('DISABLE_SELECT2_FOCUS_PROTECTION')) {
1620 ?>
1630$(document).on('select2:open', (e) => {
1631 console.log("Execute the focus (click on combo or use space when on component)");
1632 const target = $(e.target);
1633 if (target && target.length) {
1634 let id = target[0].id || target[0].name;
1635 if (id.substr(-2) == '[]') {
1636 id = id.substr(0,id.length-2);
1637 }
1638 document.querySelector('input[aria-controls*='+id+']').focus();
1639 }
1640});
1641 <?php
1642}
1643?>
1644
1645
1650$(document).ready(function() {
1651 if ($('.kanban .column').length > 0) {
1652 $('.kanban .column').sortable({
1653 items: '.kanban-draggable',
1654 connectWith: '.kanban .column',
1655 cursor: 'move',
1656 opacity: 0.8,
1657 tolerance: 'pointer',
1658 start: function(_, ui) {
1659 ui.item.data('original-column', ui.item.parent());
1660 ui.placeholder.height(ui.item.outerHeight());
1661 },
1662 receive: function(_, ui) {
1663 var originalColumn = ui.item.data('original-column');
1664 var newColumn = $(this);
1665
1666 if (!originalColumn.is(newColumn)) {
1667 onKanbanColumnChange(ui.item, newColumn);
1668 }
1669 }
1670 });
1671 }
1672});
1673
1679function onKanbanColumnChange(item, newColumn) {
1680 console.log("Call onKanbanColumnChange");
1681 jQuery.ajax({
1682 method: 'POST',
1683 url: '<?php echo DOL_URL_ROOT; ?>/core/ajax/saveinplace.php',
1684 data: {
1685 field: 'editval_'+newColumn.data('groupbyfield'),
1686 element: item.data('element'),
1687 table_element: item.data('tableelement'),
1688 fk_element: item.data('itemid'),
1689 value: newColumn.data('groupbyid'),
1690 token: '<?php echo currentToken() ?>'
1691 },
1692 context: document.body,
1693 success: function() {
1694 if (newColumn.hasClass('kanbancollapsed')) {
1695 item.hide();
1696 }
1697 }
1698 });
1699 item.data('original-column', newColumn);
1700}
1701
1702
1703// End of lib_head.js.php
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
ui state ui widget content ui state ui widget header ui state a ui button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
getOperatorsForFieldType(type, maybenull=0)
Get the list of possible operators for a given field type that we can use in the generic filter.
copyToClipboard(text, text2)
Function to output a dialog box for copy/paste.
document_preview(file, type, title)
Function to show a document preview popup.
pricejs(amount, mode='MT', currency_code='', force_locale='')
Function similar to PHP price()
generateFilterString(column, operator, context, fieldType)
Generate a filter string based on the given column, operator, context and field type.
newpopup(url, title)
Show a popup HTML page.
price2numjs(amount)
Function similar to PHP price2num()
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:158
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:161
if(empty( $takeposterminal)) fail($message)
Abort invoice creation with a given error message.
Definition invoice.php:123