1/* Mini frameWork Js pour Digifactory 2* quelques raccourcis pratique et fonctions basiques. 3*/ 4 5 6var _ = { 7 cfg: { 8 currency:'€', 9 currencyCode:'EUR', 10 workspace:'backoffice', 11 allowFullPageUpload: true, 12 }, 13 tmpDatas:{}, 14 cache:{ 15 lists: { 16 loaders: {}, 17 values: {} 18 }, 19 indexed: {}, 20 uids: {}, 21 }, 22 flags:{}, 23 Datas:{glob:{}}, // Tableau global 24 Tpls:{}, 25 quickHelps:{main:[]}, 26 dataTpls:{glob:{}}, 27 linkTpls:{glob:{}}, 28 Users: {0:{nr:0, pic:'/custom/img/user/user_male.png', n:'Non-assigné'}}, 29 pageX:0, 30 pageY:0, 31 windowsFocus:true, 32 urlParams: new URLSearchParams(window.location.search), 33 loadedScript: [], 34 charterColors: null, 35 styleSets: null, 36 styleSetsDetailed: null, 37 entities: null, 38 listIndex: [], 39 rootUrl: location.href, 40 dragging:false, 41 dragToMove:false, 42 dragService:null, 43 pageModules:{}, 44 delayHandlers:{}, 45 momentFormats: ["DD-MM-YYYY HH:mm", "YYYY-MM-DD HH:mm", "DD-MM-YYYY", "YYYY-MM-DD"], 46 47 start: function(config, workspace, toload) { 48 if (typeof digiSuiteVars != "undefined") 49 { 50 _.cfg = $.extend(_.cfg, digiSuiteVars); 51 } 52 53 if (workspace) _.cfg.workspace = workspace; 54 if (_.cfg.ext) _.cfg.workspace = 'extranet'; 55 56 if (!toload) toload = ''; 57 switch (_.cfg.workspace) 58 { 59 case 'frontoffice': toload+= ''; break; 60 case 'extranet': toload+= ',notif,websocket,upload,chat'; break; 61 case 'backoffice': 62 default: toload+= 'kb,cp,tpl,pm,review,help,notif,websocket,upload,chat,alert,header,itemSelector'; break; 63 } 64 65 _.newSession = config.isNew; 66 67 _.digi = new digiRequest({urladmin:_.cfg.urladmin}); 68 _.error = new ErrorService({urladmin:_.cfg.urladmin}); 69 70 _.startListening(); 71 72 var services = toload.split(','); 73 if (_.cfg.workspace!='frontoffice') 74 { 75 services.push('components'); 76 } 77 _.loadServices(services, config); 78 79 _.checkUrlApp(); 80 81 _.bringSectionToLife(); 82 83 if (_.cfg.workspace=='backoffice') _.startBackOffice(); 84 }, 85 86 startBackOffice: function() { 87 _.$appBody = $("body"); 88 _.$appHead = $("body > header"); 89 var lastScrollY = 0; 90 _.MenuOn = true; 91 $('[name="t"]', _.$appHead).change(()=>$('[name="s"]', _.$appHead).focus()); 92 $('[name="s"]', _.$appHead).on('blur', ()=>_.keysCtrlF=false); 93 if (_.intval(_.get('nbntor'))) 94 { 95 $('.main-ariane > .home', _.$appHead).append('<span class="toRead small top-corner" title="'+_.get('nbntor')+' news à lire"></span>'); 96 } 97 98 99 _.kb.addRules(document, {keys: { 100 'alt+a': function(ev) { ev.preventDefault(); $(".digiAlertBell").click(); }, 101 'alt+t': function(ev) { $(".timeInput", _.$appHead).click(); }, 102 'alt+f': function(ev) { ev.preventDefault(); $("nav.leftMenu").click(); }, 103 'ctrl+f': function(ev) { 104 if (!_.keysCtrlF && !_.keysCtrlFother) 105 { 106 ev.stopPropagation(); 107 ev.preventDefault(); 108 109 $(".df-search input[name='s']", _.$appHead).focus(); 110 _.$appHead.addClass('on forced-on'); 111 _.$appHead.css('top', 0); 112 _.MenuOn = true; 113 _.keysCtrlF = true; 114 } 115 else _.keysCtrlF = false; 116 }, 117 }}); 118 119 120 setTimeout(function(){ 121 _.uptQuickHelp('main', [ 122 {kb:'Ctrl + F', name:'Recherche', title:'principale ou TimeLine', detail:'un 2ème Ctrl+F ouvre la recherche du navigateur'}, 123 {kb:'Alt + F', name:'Menu', title:'ouvre le menu latéral de gauche', detail:'permet également de refermer le menu'}, 124 {kb:'Alt + A', name:'Alertes', title:'afficher le panel de droite', detail:'avec les notifications'}, 125 {kb:'Alt + T', name:'Temps', title:'ouvre la saisie rapide de temps'}, 126 ]); 127 }, 100); 128 129 _.$appHead.on('click', (ev)=>{ 130 if (!_.MenuOn) 131 { 132 _.$appHead.addClass('on forced-on').css('top', 0); 133 _.MenuOn = true; 134 } 135 else 136 { 137 if ($(ev.target).hasClass('df-search')) 138 { 139 $(".df-search input[name='s']", _.$appHead).focus(); 140 } 141 } 142 }); 143 144 145 $(window).scroll(function() { 146 if (!_.MenuLocked) 147 { 148 if (!$('[name="s"]', _.$appHead).is(':focus')) 149 { 150 var scroll = $(document).scrollTop(); 151 if (scroll<lastScrollY && scroll<500 && _.$appHead.hasClass('forced-on')) 152 { 153 _.$appHead.css('top', 0); 154 _.MenuOn = true; 155 } 156 else 157 { 158 _.$appHead.removeClass('forced-on'); 159 _.$appHead.css('top', Math.max(-scroll, -45)); 160 if (scroll>=50) 161 { 162 if (_.MenuOn) 163 { 164 _.MenuOn = false;
165 _.$appBody.addClass('scrolling'); 166 _.$appHead.removeClass('on'); 167 } 168 } 169 else if (scroll<50) 170 { 171 if (!_.MenuOn) 172 { 173 _.MenuOn = true; 174 _.$appBody.removeClass('scrolling'); 175 _.$appHead.addClass('on'); 176 } 177 } 178 } 179 lastScrollY = scroll; 180 } 181 } 182 }); 183 184 $('.SidePanel .panel-toggle').click(function() { 185 _.panel.toggle(); 186 }); 187 }, 188 189 190 // A Faire: analyser chaque élément et voir si peut être amélioré - déplacé - transformé en module - ou autre modif intéressante 191 startListening: function() { 192 193 if ($.fn.jAction) 194 { 195 $(document).on("click", ".jaction,[jsinfos]", function(ev){ 196 if (!this.inProcess) 197 { 198 ev.stopPropagation(); 199 $('.date-Picker').each(function() {if (this.close) this.close();}); 200 $(this).jAction({fromClick:true, event:ev}); 201 $(this).filter(':button').blur(); 202 } 203 }); 204 205 $(document).on("dblclick", ".jactiondbl", function(ev){ 206 if (!this.inProcess) 207 { 208 ev.stopPropagation(); 209 $(this).jAction({fromClick:true, event:ev}); 210 $(this).filter(':button').blur(); 211 } 212 }); 213 214 $(document).on("change", ".jchange,[jschange]", function(ev){ 215 if (!this.inProcess) 216 { 217 ev.stopPropagation(); 218 $(this).jAction({fromChange:true, event:ev}); 219 } 220 }); 221 222 $(document).on("submit", "form:not([action])", function(ev) { 223 ev.preventDefault(); 224 }); 225 226 $(document).on('keypress', ".enter :input", function(ev) { 227 var tn = ev.target.tagName.toLowerCase(); 228 if (ev.keyCode == 13 && (tn!="textarea" || (tn=="textarea" && ev.ctrlKey)) ) // Enter 229 { 230 var panel = $(this).closest('.enter'); 231 var btn = $(".btn.jaction.valid, .btn.valid[jsinfos]", panel); 232 if (btn.length>0) 233 { 234 $(btn[0]).jAction(); 235 } 236 } 237 }); 238 239 $(".jActionInit").each(function(){ 240 $(this).removeClass('jActionInit'); // pour ne pas répéter l'initialisation en cas d'un second appel 241 $(this).jAction(); 242 }); 243 244 $(window).on("popstate", function(ev) { 245 var state = ev.originalEvent.state; 246 if (state) 247 { 248 if (state.page || state.lSort || state.filter || state.nbbypage) 249 { 250 if (_.listIndex[+state.list]) 251 { 252 _.listIndex[+state.list].popState(state); 253 } 254 } 255 else if (state.url) 256 { 257 var actions = [{url:state.url, state:true}]; 258 if (state.select) actions[0].select = state.select; 259 $(this).jAction({actions:actions}); 260 } 261 } 262 }); 263 } 264 265 $(document).on('change', ':input.calcul', (ev)=>{ 266 _.inputCalcul(ev.currentTarget); 267 }); 268 269 270 if (_.cfg.workspace=='backoffice') 271 { 272 var prevZone = _.ce('i', 'i-caret-up', {title:'monter'}); 273 var nextZone = _.ce('i', 'i-caret-down', {title:'descendre'}); 274 $('.sideIco.nav').append(prevZone, nextZone); 275 276 function getOrderedTarget(reverse=false) { 277 var $targets = $('body, nav.tabs, .basket-compose'); 278 var orderedTargets = []; 279 $targets.each((i, target)=>{ 280 orderedTargets.push({top:$(target).offset().top, target:target}); 281 }); 282 var way = reverse? -1 : 1; 283 orderedTargets.sort((a, b)=>{ 284 if (a.top==b.top) return 0; 285 return a.top<b.top? -1*way : 1*way; 286 }); 287 return orderedTargets; 288 } 289 290 291 $(prevZone).on('click', ()=>{ 292 var wscroll = $(window).scrollTop(), 293 orderedTargets = getOrderedTarget(true); 294 for (let item of orderedTargets) 295 { 296 if (item.top < wscroll - 40) 297 { 298 $('body,html').animate({scrollTop:item.top}, 300); break; 299 } 300 } 301 }); 302 303 $(nextZone).on('click', ()=>{ 304 var wscroll = $(window).scrollTop(), 305 orderedTargets = getOrderedTarget(); 306 for (let item of orderedTargets) 307 { 308 if (item.top>wscroll + 40) 309 { 310 $('body,html').animate({scrollTop:item.top}, 300); break; 311 } 312 } 313 }); 314 315 316 var $mainNav = $("nav.leftMenu"); 317 318 $(".addFavorite").click(function() { 319 var url = document.location.href; 320 if (url.indexOf('?')==-1) url+= "?"; 321 url+="&addBkmk=1"; 322 document.location.href = url; 323 }); 324 325 $(".bookmark select").change(function() { 326 document.location.href = $(this).val(); 327 }); 328 329 $("header .burger").click(function() { 330 _.isMobileSize = true; 331 if ($mainNav.hasClass('mobile')) 332 { 333 $mainNav.hide(); 334 $mainNav.removeClass('mobile'); 335 $mainNav[0].close(); 336 } 337 else 338 { 339 $mainNav.show(); 340 $mainNav.addClass('mobile'); 341 $mainNav.click(); 342 } 343 }); 344 $(".df-search input[name='s']").keypress(function(ev) { 345 if (ev.keyCode == 27) // Esc 346 { 347 ev.preventDefault(); 348 $(this).val(""); 349 } 350 if (ev.keyCode == 13) // enter 351 { 352 ev.preventDefault(); 353 $(this).jAction({actions:[{},{route:'app/search//s'}]}); 354 } 355 }); 356 357 $(".df-search .i-search", _.$appHead).click(function(){ 358 $(this).jAction({actions:[{},{route:'app/search//s'}]}); 359 }); 360 361 $(document).on('click', ".collapse .collapse-trigger", function() { 362 var $section = $(this).closest('.collapse'); 363 if ($section.hasClass('closed')) 364 { 365 $section.removeClass('closed'); 366 $section.find(">div,>article").slideDown(200); 367 } 368 else 369 { 370 $section.addClass('closed'); 371 $section.find(">div,>article").slideUp(200); 372 } 373 }); 374 375 $(".timeInput", _.$appHead).dblclick(function() { 376 document.location.href = digiSuiteVars.urladmin+'/pjt/task/times'; 377 }).click(function() { 378 if ($(".timeInputTool").hasClass('open')) 379 { 380 $(".timeInputTool").removeClass('open'); 381 } 382 else 383 { 384 _.actions({ 385 route: 'pjt/task//get-last-time', 386 then: function(data) { 387 $(".timeInputTool [name='startT']").val(data.t); 388 if (data.prev) 389 { 390 $(".timeInputTool [name='startT']").on('keyup', function(ev) { 391 if (ev.which == 37) 392 { 393 if (!$(this).val()) 394 { 395 $(this).val(data.prev); 396 } 397 } 398 }); 399 } 400 } 401 }); 402 403 $(".timeInputTool [name='endT']").val(moment().format('HH:mm')); 404 $(".timeInputTool [name='comment']").val(""); 405 $(".timeInputTool").show().addClass('open'); 406 $(".timeInputTool input.search").focus(); 407 } 408 }); 409 410 $(".taskBug").on('click', function() { 411 if ($(this).hasClass('inv')) 412 { 413 $(this).removeClass('inv'); 414 $(this).siblings('input[name="bug"]').val(0); 415 } 416 else 417 { 418 $(this).addClass('inv'); 419 $(this).siblings('input[name="bug"]').val(1); 420 } 421 }); 422 $(document).on({ 423 mouseenter: function (ev) { 424 if (_.Help) 425 { 426 if ($(ev.target).closest('.suggDropList').length==0) 427 { 428 var code = $(this).attr('help'); 429 clearTimeout(_.helpOnTimer); 430 _.helpTargetCode = code; 431 _.helpTimer = setTimeout(function() { 432 if (_.helpTargetCode && _.helpTargetCode==code) 433 { 434 _.Help.init(code); 435 } 436 }, 2000); 437 } 438 } 439 }, 440 mouseleave: function (ev) { 441 if (_.Help) 442 { 443 var code = $(this).attr('help'); 444 _.helpTargetCode = false;
445 clearTimeout(_.helpTimer); 446 _.helpOnTimer = setTimeout(function() { 447 if (_.helpOnTimer && _.Help.code && _.Help.code==code) 448 { 449 _.Help.close(); 450 } 451 }, 2500); 452 } 453 } 454 }, '[help]'); 455 $(document).on('mousemove', (ev)=>{ 456 if ($(ev.target).closest('.suggDropList').length>0) 457 { 458 if (_.helpOnTimer) 459 { 460 _.Help.close(); 461 } 462 if (_.tooltipTarget && _.tooltipTarget.closeToolTip) _.tooltipTarget.closeToolTip(); 463 } 464 }); 465 // $(document).on('mouseenter', (ev)=>{ 466 // if (_.Help && _.helpTargetCode ) 467 // { 468 // if (_.helpTimer) clearTimeout(_.helpTimer); 469 // _.Help.close(); 470 // } 471 // }, '.suggDropList'); 472 $(document).on('scroll', function() { 473 $('.date-Picker').each(function() {if (this.close) this.close();}); 474 }); 475 476 $(window).focus(function() { 477 _.windowsFocus = true; 478 }); 479 $(window).blur(function() { 480 _.windowsFocus = false; 481 }); 482 483 $(document).on('click', '.toggle-next', (ev)=>{ 484 var $next = $(ev.currentTarget).next('section.group'); 485 if ($(ev.currentTarget).hasClass('closed')) 486 { 487 $next.slideDown('fast', ()=>{ 488 $next.css('overflow', 'visible'); 489 }); 490 $(ev.currentTarget).removeClass('closed'); 491 $('i[class^="i-caret"]', ev.currentTarget).removeClass('i-caret-right').addClass('i-caret-down'); 492 } 493 else 494 { 495 $next.slideUp('fast'); 496 $(ev.currentTarget).addClass('closed'); 497 $('i[class^="i-caret"]', ev.currentTarget).removeClass('i-caret-down').addClass('i-caret-right'); 498 } 499 500 }); 501 502 if (("standalone" in window.navigator) && window.navigator.standalone) 503 { 504 // For iOS Apps 505 $(document).on('click', 'a', function(e) { 506 507 e.preventDefault(); 508 509 var url = $(this).attr('href'); 510 if (url != undefined && url.substr(0, 1) != '#' && url.substr(0, 4) != 'http' && $(this).attr('data-method') == undefined) 511 { 512 window.location = url; 513 } 514 }); 515 } 516 } 517 }, 518 519 uid: function () { 520 let uid = null; 521 522 while (uid === null || uid in this.cache.uids) { 523 uid = (new Date().getTime())+'.'+Math.random().toString(16).slice(2); 524 } 525 526 this.cache.uids[uid] = true; 527 528 return uid; 529 }, 530 531 loadServices: function(services, config) { 532 if (typeof services == 'string') services = [services]; 533 534 $.each(services, function(i, v) {services[v] = true;}); 535 536 if (services.header && !_.PageHeader) _.PageHeader = new PageHeader(); 537 if (services.kb && !_.kb) _.kb = new KBoard(); 538 if (services.cp && !_.cp) _.cp = new CopyPaste(); 539 if (services.tpl && !_.tpl) _.tpl = new TemplateService(); 540 if (services.review && !_.Reviews) _.Reviews = jReviews(config.reviews, config.isNew? true: false); 541 if (services.help && !_.Help) _.Help = jHelp({}); 542 if (services.websocket && !_.websocket) _.websocket = new WebSocketService(_.cfg.websocketAuthToken, _.cfg.websocketReconnectionInterval); 543 if (services.components && !_.components) _.components = new ComponentService(); 544 if (services.notif && !_.Notifs) _.Notifs = new jNotifs(config.nbNotifs); 545 if (services.upload && !_.Upload) _.Upload = new digiUpload(); 546 if (services.chat && !_.chat) _.chat = new ChatService(); 547 if (services.alert && !_.Alerts) _.Alerts = new digiAlert({list:config.alerts}); 548 if (services.pm && !_.pm) 549 { 550 import('./mod/ui/ui.js').then((list)=>{ 551 _.pm = new list['PanelManager'](); 552 }); 553 } 554 if (services.itemSelector && !_.itemSelector) _.itemSelector = new itemSelector(); 555 }, 556 557 bringSectionToLife: function(section, isBloc) { 558 if (!section) section = document; 559 560 if (typeof _ == 'object' && 'components' in _) _.components.check(section); 561 562 if ($.fn.module) 563 { 564 if (isBloc) $("[module]", section).removeClass('blocTpl').module(); 565 else $("[module]", section).not('.blocTpl').module(); 566 } 567 568 $('.to-hide').hide().removeClass('to-hide'); 569 570 $('i.log-infos').not('.done').addClass('done').click(function(ev) { 571 ev.stopPropagation(); 572 var ct = $(this).next('span.JSinfos').html(); 573 $(this).jToolTip({cls:'log-infos', title:ct}); 574 }); 575 576 577 if (isBloc) $(".checkbox", section).removeClass('blocTpl').checkboxField(); 578 else $(".checkbox", section).not(".blocTpl").checkboxField(); 579 580 $("panel", section).Panel(); 581 $(".radio-grp", section).radioGrp(); 582 $(".radio-grp-bis", section).radioGrpBis(); 583 $(".tags-input", section).tagsInput(); 584 $(".multi-select", section).multiSelectField(); 585 $("select.dropdown", section).dropDownSelect(); 586 $(".multiValueSelect", section).multiValSelect(); 587 $(".jUsers", section).userSelector(); 588 if (typeof $.fn.uploadHtml5 !== "undefined") $(".appUpload", section).uploadHtml5(); 589 if (typeof $.fn.jGED!=='undefined') $(".GedModule", section).jGED(); 590 if (typeof $.fn.jGallery!=='undefined') $(".jGallery", section).jGallery(); 591 if (typeof $.fn.foldBloc!=='undefined') $(".jFoldBloc", section).foldBloc(); 592 if (typeof $.fn.dragNDrop!=='undefined') $(".dragging", section).dragNDrop(); 593 if (typeof $.fn.appList!=='undefined') $(".appList", section).appList(); 594 if (typeof $.fn.jMoreLess!=='undefined') $(".more-less", section).jMoreLess(); 595 if (typeof $.fn.datePicker!=='undefined') 596 { 597 $(".datepicker", section).datePicker(); 598 $("div.datepicker > input", section).each(function() {$(this).datePicker();}); 599 } 600 if (typeof $.fn.jColorPicker!=='undefined') $(".jColorPicker", section).jColorPicker(); 601 $(".quantityModifier", section).qttyModifier(); 602 if (typeof $.fn.stateFlow!=='undefined') $(".stateFlow", section).stateFlow(); 603 if (typeof $.fn.projectFlow!=='undefined') $(".projectFlow", section).projectFlow(); 604 if (typeof $.fn.jProgressBar!=='undefined') $(".jProgressBar", section).jProgressBar(); 605 if (typeof $.fn.jEmailSplit!=='undefined') $(".jEmailSplit", section).jEmailSplit(); 606 if (typeof $.fn.jTxtLength!=='undefined') $(".jTxtLength", section).jTxtLength(); 607 if (typeof $.fn.jToolBar!=='undefined') $(".jToolBar", section).jToolBar(); 608 609 610 $('.jsonEditor', section).each(function(i, editorBloc) { 611 $(editorBloc).removeClass('jsonEditor').addClass('json-editor'); 612 var editorArea = $('textarea', editorBloc)[0]; 613 var container = _.ce('div'); 614 $(editorArea).hide().addClass('json-editor-area').after(container); 615 616 var job = new SyncJob(function () { 617 // create the editor
618 var code = editorArea.jsonCode? editorArea.jsonCode : $(editorArea).val(); 619 620 const options = { 621 mode: 'code', 622 mainMenuBar:false, 623 navigationBar:false, 624 statusBar:false, 625 }; 626 editorArea.editor = new JSONEditor(container, options); 627 if (typeof code=="object" || Array.isArray(code)) 628 { 629 editorArea.editor.set(code); 630 } 631 else editorArea.editor.setText(code); 632 }); 633 job.addRequiredJob(new ImportScriptJob('/admin/inc/js/jsoneditor/jsoneditor.min.js')); 634 job.addRequiredJob(new ImportStylesheetJob('/admin/inc/js/jsoneditor/jsoneditor.min.css')); 635 job.run(); 636 }); 637 638 $(".jFilterList", section).jFilterList(); 639 if (typeof $.fn.contentPackValues!=='undefined') 640 { 641 $(".contentView article", section).has('pack').contentPackValues(); 642 } 643 644 _.bringJActionInitToLife(section); 645 646 $("fieldset [autofocus]", section).focus(); 647 648 _.appModules().then((appModules) => appModules.autoload(section)); 649 }, 650 651 bringJActionInitToLife: function(section) { 652 if (!section) section = document; 653 $(".jActionInit", section).each(function(){ 654 $(this).removeClass('jActionInit'); // pour ne pas répéter l'initialisation en cas d'un second appel 655 656 $(this).jAction(); 657 }); 658 }, 659 660 doubleSessionCatch: function() { 661 $.modalPanel({ 662 code:"doubleSession", 663 content:"<nav><h4>Double session</h4></nav><div class='close jaction'><span class='JSinfos'>{act:'close'}</span></div><section><article>Une deuxième session avec cet utilisateur est active. <br>Veuillez fermer la session en trop. <nav class='actions'><br><br><a class='btn danger' href='"+digiSuiteVars.urladmin+"/?quit=1'>Quittez et reconnectez vous</a></nav></article></section>", 664 cssClass:"modal full", 665 callback:function() { 666 setTimeout(function() {$("#doubleSession").hide();}, 4000); 667 } 668 }); 669 }, 670 671 addQuickHelp: function(type, helps) { 672 this.quickHelps[type] = (this.quickHelps[type] || []).concat(helps); 673 this.displayQuickHelp(); 674 }, 675 uptQuickHelp: function(type, helps) { 676 this.quickHelps[type] = helps; 677 this.displayQuickHelp(); 678 }, 679 680 displayQuickHelp: function() { 681 var $QuickHelps = $('.main-quick-helps'); 682 if (!$QuickHelps.length) 683 { 684 $QuickHelps = $('<ul class="main-quick-helps"/>'); 685 if ($('.status-bar').length) $('.status-bar > .quick').append($QuickHelps); 686 else $('body > .main-container > section:not(.PanelSection):not(.modal), body > .main-container > section > .CentralPanel').first().append($QuickHelps); 687 } 688 $('>*', $QuickHelps).remove(); 689 $.each(_.quickHelps, function(type, helps) { 690 $.each(helps, function(i, help) { 691 var li = _.ce('li', 'short-help'); 692 $(li).html('<span class="short"></span><span class="name">'+help.name+'<span>'); 693 if (help.ico) $('.short', li).append('<i class="i-'+help.ico+'"></i>'); 694 if (help.ico && help.kb) $('.short', li).append(' + '); 695 if (help.kb) $('.short', li).append(help.kb); 696 if (help.title) $(li).attr('title', help.title); 697 if (help.detail) $(li).attr('detail', help.detail); 698 $QuickHelps.append(li); 699 }); 700 701 }); 702 }, 703 hideQuickHelp: function() { 704 705 }, 706 delay: function (id, callback, timeout) { 707 if (id in _.delayHandlers) { 708 clearTimeout(_.delayHandlers[id]); 709 delete _.delayHandlers[id]; 710 } 711 _.delayHandlers[id] = setTimeout(callback, timeout || 500); 712 }, 713 714 test: function(cond, data) { 715 if (Array.isArray(cond)) 716 { 717 for (let subCond of cond) 718 { 719 if (subCond.cond) 720 { 721 if (_.test(subCond.cond, data)) 722 { 723 return subCond; 724 } 725 } 726 else return subCond; 727 } 728 } 729 730 if (typeof cond == "undefined" || cond==="" || cond===null) return true; 731 else if (cond===false) return false; 732 733 var _if = analyserCondition(cond); 734 if (_if===false) return false; 735 return calculCondition(_if, data); 736 }, 737 738 makeData: function(code, data) { 739 var parts = code.split('.'); 740 var res = {}; 741 for (var i=parts.length-1; i>=0; --i) 742 { 743 if (i==parts.length-1) res = {[parts[i]]:data}; 744 else res = {[parts[i]]: res}; 745 } 746 return res; 747 }, 748 749 setData: function(code, value, data) { 750 var parts = code.split('.'); 751 var target = data; 752 for (var i=0; i < parts.length-1; i++) 753 { 754 if (target[parts[i]] == null) target[parts[i]] = {}; 755 target = target[parts[i]]; 756 } 757 target[parts[parts.length-1]] = value; 758 }, 759 760 readData: function(code, data) { 761 if (code.indexOf('.')>0) 762 { 763 let originCode = code; 764 var [first, ...code] = code.split('.'); 765 code = code.join('.'); 766 if (!data[first]) 767 { 768 if (originCode in data) return data[originCode]; 769 return null; 770 } 771 return _.readData(code, data[first]); 772 } 773 else 774 {
775 return data[code]; 776 } 777 }, 778 779 deleteData: function(code, data) { 780 var parts = code.split('.'); 781 var target = data; 782 for (var i = 0; i < parts.length - 1; i++) 783 { 784 if (!target[parts[i]]) return; 785 target = target[parts[i]]; 786 } 787 delete target[parts[parts.length - 1]]; 788 }, 789 790 extendModule: function(module, config, datas, tree) { 791 module.initStarted = true; 792 if (!datas) datas = {}; 793 if (!config) config = {}; 794 795 module.initModule = function() { 796 module.tags = {}; 797 module.config = config; 798 module.datas = datas; 799 }; 800 801 module.initModuleTree = function() { 802 803 // référentiel global (par type de module) 804 if (!module.initDone && module.moduleCode) 805 { 806 if (!_.pageModules[module.moduleCode]) 807 { 808 _.pageModules[module.moduleCode] = []; 809 } 810 _.pageModules[module.moduleCode].push(module); 811 } 812 if (!tree) tree = {parent:false, children:[], siblings:[]}; 813 module.moduleTree = tree; 814 if (tree.parent) 815 { 816 if (!tree.parent.moduleTree.children) tree.parent.moduleTree.children = []; 817 if (tree.parent.moduleTree.children.length>0) 818 { 819 if (!tree.siblings) tree.siblings = []; 820 $.each(tree.parent.moduleTree.children, function (i, child) { 821 var found = false; 822 $.each(tree.siblings, function(j, sibly) { 823 if (child==sibly) 824 { 825 found = true; 826 return false; 827 } 828 }); 829 if (!found) tree.siblings.push(child); 830 }); 831 } 832 tree.parent.moduleTree.children.push(module); 833 } 834 }; 835 836 module.makeChildModuleTree = function() { 837 var tree = { 838 parent: module, 839 siblings: module.moduleTree.children, 840 children: [], 841 }; 842 return tree; 843 }; 844 845 module.addModuleChild = function(mod) { 846 $.each(module.moduleTree.children, function(j, child) { 847 if (child==mod) 848 { 849 found = true; 850 return false; 851 } 852 }); 853 if (!found) 854 { 855 module.moduleTree.children.push(mod); 856 } 857 }; 858 859 module.removeModuleChild = function(mod) { 860 $.each(module.moduleTree.children, function(j, child) { 861 if (child==mod) 862 { 863 module.moduleTree.children.splice(j, 1); 864 return false; 865 } 866 }); 867 }; 868 869 module.readExternalParameters = function() { 870 871 // read jsinfos 872 var jsinfos = $(">.JSinfos", module).html(); 873 if (jsinfos) 874 { 875 var config = eval("("+jsinfos.trim()+")"); 876 $(">.JSinfos", module).remove(); 877 $.extend(true, module.config, config); 878 } 879 var $nextInfos = $(module).next('.JSinfos'); 880 if ($nextInfos.length>0) 881 { 882 var jsinfos = $nextInfos.html(); 883 if (jsinfos) 884 { 885 var config = eval("("+jsinfos.trim()+")"); 886 $.extend(true, module.config, config); 887 } 888 $nextInfos.remove(); 889 } 890 891 // read tag parameters 892 for (var i=0; i<module.attributes.length; i++) 893 { 894 var attr = module.attributes[i], 895 attrName = attr.name; 896 if (attrName.match(/^data-/i)) 897 { 898 attrName = attrName.replace(/^data-/i, ''); 899 } 900 else attrName = attr.name; 901 if (['class','id','style','align','title','detail','help'].indexOf(attrName.toLowerCase())==-1) 902 { 903 module.config[attr.name] = attr.specified? true : attr.value; 904 } 905 } 906 }; 907 908 909 910 module.initModule(); 911 module.initModuleTree(); 912 module.readExternalParameters(); 913 module.initDone = true; 914 915 }, 916 917 addContextObj: function(domElement, Object) { 918 if (!domElement.contextObjects) domElement.contextObjects = []; 919 var found = false; 920 $.each(domElement.contextObjects, function(i, obj) { 921 if (obj==Object) 922 { 923 found = true; 924 return false; 925 } 926 }); 927 if (!found) 928 { 929 domElement.contextObjects.push(Object); 930 } 931 }, 932 933 checkUrlApp: function() { 934 if (_.urlParams.get('daR') && _.urlParams.get('daD')) 935 { 936 // accès direct 937 var route = _.urlParams.get('daR').split('/'); 938 var data = JSON.parse(_.urlParams.get('daD')); 939 _.actions({sct:route[0], obj:route[1], xAct:route[2], data:data}); 940 } 941 942 }, 943 944 ce: function(e, c, attr, html) { 945 e = document.createElement(e); 946 if (e=='button' && (!attr || !attr.type)) 947 { 948 if (!attr) attr = {}; 949 attr.type = 'button'; 950 } 951 if (c) $(e).addClass(c); 952 if (attr) $(e).attr(attr); 953 if (html) $(e).html(html); 954 return e; 955 }, 956 ceNS: function(e,c, attr) { 957 e = document.createElementNS('http://www.w3.org/2000/svg', e); 958 if (c) $(e).attr({class:c}); 959 if (attr) 960 { 961 $.each(attr, function(a, v) { 962 e.setAttribute(a, v); 963 }); 964 } 965 if (attr && attr['xlink:href']) e.setAttributeNS('http://www.w3.org/1999/xlink','href',attr['xlink:href']); 966 return e; 967 }, 968 969 ceField: function(def, dest) { 970 var art = _.ce('article', def.fldClass? def.fldClass : '', {}, '<label>'+def.label+'</label><div></div>'); 971 var $fld; 972 switch (def.format) 973 { 974 case 'boolean': 975 var fldHtml = '<input type="checkbox" class="checkbox" name="'+def.name+'" '+(_.intval(def.defaultValue)? "checked" : "")+' value="1">'; 976 if (def.switch) fldHtml+= '<span>'+def.switch.on+'|'+def.switch.off+'</span>'; 977 $fld = $(fldHtml); 978 break; 979 case 'color': 980 $('>div', art).addClass('colors'); 981 982 if (def.info && def.info.mode=='picker') 983 { 984 $fld = $('<input type="text" class="jColorPicker" name="'+def.name+'" value="'+(def.defaultValue? def.defaultValue : '')+'" '+(def.info.jsinfos? 'jsinfos="'+def.info.jsinfos+'"':'')+'>'); 985 break; 986 } 987 else 988 { 989 def.values = []; 990 if (def.colors) 991 { 992 $.each(def.colors, function(i, color) { 993 def.values.push({value:color, background:color, text:''}); 994 }); 995 } 996 } 997 case 'radiogrp': 998 $('>div', art).addClass('radio-grp wrap'); 999 if (def.values) 1000 { 1001 $.each(def.values, function(i, value) { 1002 var r = _.ce('input', '', {name:def.name, value:value.value, type:'radio'}); 1003 var l = _.ce('label', '', {}, value.text); 1004 if (value.selected || def.selected==value.value) $(r).prop('checked', true); 1005 if (value.background) 1006 { 1007 $(l).append('<div></div>'); 1008 $('>div', l).css({background:value.background}); 1009 } 1010 $('>div', art).append(r,l); 1011 }); 1012 1013 } 1014 break; 1015 case 'date': $fld = $('<input type="text" class="datepicker" name="'+def.name+'" step="1" value="'+(def.defaultValue? def.defaultValue : '')+'">'); break; 1016 case 'datetime': $fld = $('<input type="text" class="datepicker hour" name="'+def.name+'" step="1" value="'+(def.defaultValue? def.defaultValue : '')+'">'); break; 1017 case 'nb': $fld = $('<input type="text" name="'+def.name+'" value="'+(def.defaultValue? def.defaultValue : '0')+'">'); break; 1018 case 'select': 1019 $fld = $('<select name="'+def.name+'"></select>'); 1020 _.populate($fld, def.values); 1021 break; 1022 case 'slider': 1023 $fld = $('<input type="text" class="new-slider" app-module="SliderField" min="'+(def.min? def.min : 0)+'" max="'+(def.max? def.max : 100)+'" step="'+(def.step? def.step : 1)+'" name="'+def.name+'" step="1" value="'+(def.value? def.value : (def.defaultValue? def.defaultValue : '0'))+'">'); 1024 if (def.info) $fld.attr('jsinfos', def.info); 1025 break; 1026 case 'sugg-select': 1027 case 'sugg': 1028 if (def.format=='sugg-select') 1029 { 1030 var fld = _.ce('select', '', {name:def.name, placeholder:def.placeHolder}); 1031 _.populate(fld, def.values); 1032 } 1033 else var fld = _.ce('input', '', {type:'text', name:def.name, placeholder:def.placeHolder}); 1034 def.info.type = 'sugg'; 1035 if (!def.info.format) def.info.format = 'short'; 1036 $fld = $('<div/>'); 1037 $fld.append(fld); 1038 var cmp = _.components.add(def.info, fld); 1039 break; 1040 case 'static': $fld = $('<div>'+def.value+'</div>'); $(art).addClass('static'); break; 1041 case 'img': $fld = $('<div><img src="'+def.src+'"></div>'); $(art).addClass('static img'); break; 1042 case 'time': $fld = $('<input type="text" class="new-slider" app-module="SliderField" min="'+(def.min? def.min : 0)+'" max="'+(def.max? def.max : 3600)+'" name="'+def.name+'" step="1" value="'+(def.defaultValue? def.defaultValue : '0')+'">'); break; 1043 case 'text': $fld = $('<input type="text" name="'+def.name+'" value="'+(def.defaultValue? def.defaultValue : '')+'">'); break; 1044 case 'textarea': $fld = $('<textarea name="'+def.name+'"></textarea>'); break; 1045 case 'wy': 1046 $fld = $('<textarea name="'+def.name+'" class="jWysiwyg '+def.id+'" id="'+def.id+'"></textarea>'); 1047 break; 1048 case 'json': $fld = $('<textarea name="'+def.name+'"></textarea>'); $(art).addClass('jsonEditor'); break; 1049 case 'module': 1050 var fld = _.ce(def.fld, 'ce-module', def.attrs? def.attrs : {}); 1051 $fld = $('<div/>'); 1052 $fld.append(fld); 1053 break; 1054 } 1055 1056 if (def.placeHolder) $fld.attr('placeholder', def.placeHolder); 1057 if (def.divClass) $('>div', art).addClass(def.divClass); 1058 if (def.cls) $fld.addClass(def.cls); 1059 if (def.css) $fld.css(def.css); 1060 if (def.title) $fld.attr('title', def.title); 1061 if (def.detail) $fld.attr('detail', def.detail); 1062 1063 if ($fld) $('>div', art).append($fld); 1064 if (def.suffix) $('>div', art).append(def.suffix); 1065 1066 if (dest) 1067 { 1068 $(dest).append(art); 1069 switch (def.format) 1070 { 1071 case 'boolean': break; 1072 case 'time': break; 1073 case 'slider': break; 1074 case 'wy': 1075 setTimeout(function() { 1076 _.wy({ 1077 select: "."+def.id, 1078 mode:'light', 1079 height:def.height? def.height : 200, 1080 options: $.extend({ 1081 min_height: def.height? def.height : 200,
1082 max_height: def.height? 2*def.height : 200, 1083 setup: function(editor) { 1084 editor.on('init', function(e) { 1085 _.actions({trigger:'mysiwyg-loaded'}); 1086 }); 1087 } 1088 }, def.options || {}), 1089 }); 1090 }, 1000); 1091 break; 1092 1093 case 'module': 1094 _.loadModule(fld, def.info, {}, def.moduleId); 1095 break; 1096 } 1097 } 1098 1099 return {article:art, field:$fld? $fld[0] : false}; 1100 }, 1101 1102 ceStatic: function(def, dest) { 1103 var art = _.ce(def.tag? def.tag : 'article', 'static '+(def.code? def.code : ''), {}, ''); 1104 var $content = false; 1105 switch (def.format) 1106 { 1107 case 'text': $content = $('<div>'+def.text+'</div>'); break; 1108 case 'img': $content = $('<img src="'+def.src+'">'); break; 1109 } 1110 1111 if (def.cls) $content.addClass(def.cls); 1112 if (def.css) $content.css(def.css); 1113 1114 if ($content) $(art).append($content); 1115 1116 if (dest) $(dest).append(art); 1117 1118 return {article:art, content:$content? $content[0] : false}; 1119 }, 1120 1121 ceFldGroup: function(group, dest) { 1122 if (group.definition) 1123 { 1124 var div = _.ce('div'), 1125 art = _.ce('article', group.fldClass? group.fldClass : '', {}, '<label>'+group.label+'</label>'); 1126 $(art).append(div); 1127 if (group.divClass) $(div).addClass(group.divClass); 1128 1129 $.each(group.definition, function(i, def) { 1130 def.fieldOnly = true; 1131 var res = _.ceField(def); 1132 if (res.article) $(div).append($('>div', res.article)); 1133 }); 1134 $(dest).append(art); 1135 } 1136 }, 1137 1138 ceGroup: function(group, dest) { 1139 if (group.definition) 1140 { 1141 var title = _.ce('h5', 'title'); 1142 if (group.open) $(title).addClass('opened'); 1143 $(title).html(group.name); 1144 $(dest).append(title); 1145 1146 var section = _.ce('section', 'formGroup'); 1147 _.ceFormDef(group.definition, section); 1148 $(dest).append(section); 1149 $(title).on('click', ()=>{ 1150 if ($(title).hasClass('closed')) 1151 { 1152 $(section).slideDown('fast', ()=>{ 1153 $(section).css('overflow', 'visible'); 1154 }); 1155 $(title).removeClass('closed'); 1156 } 1157 else 1158 { 1159 $(section).slideUp('fast'); 1160 $(title).addClass('closed'); 1161 } 1162 }); 1163 } 1164 }, 1165 1166 ceFormDef: function(definition, dest) { 1167 $.each(definition, function(i, def) { 1168 if (def.type=='group') 1169 { 1170 _.ceGroup(def, dest); 1171 } 1172 else if (def.type=='field-grp') 1173 { 1174 _.ceFldGroup(def, dest); 1175 } 1176 else if (def.type=='field') 1177 { 1178 _.ceField(def, dest); 1179 } 1180 else if (def.type=='static') 1181 { 1182 _.ceStatic(def, dest); 1183 } 1184 else if (def.type=='hidden') 1185 { 1186 var hidden = _.ce('input', def.cls? def.cls : '', {type:'hidden', name:def.name, value:def.value? def.value : ''}); 1187 $(dest).append(hidden); 1188 } 1189 }); 1190 _.bringSectionToLife(dest); 1191 }, 1192 1193 inputCalcul: function(input) { 1194 return new Promise((resolve)=>{ 1195 var val = input instanceof HTMLElement? $(input).val() : input; 1196 if (val && val.match(/[*/+()]|\d\s*[-]/)) 1197 { 1198 _.calculator().then(calculator => { 1199 val = _.preCleanNum(val); 1200 try { 1201 var result = calculator.calculate(val); 1202 } 1203 catch(e) { 1204 var result = _.doubleval(val); 1205 } 1206 if (Number.isFinite(result)) 1207 { 1208 if (input instanceof HTMLElement) 1209 { 1210 $(input).val(result); 1211 } 1212 } 1213 resolve(result); 1214 }); 1215 } 1216 else resolve(val); 1217 }); 1218 }, 1219 1220 clipping: function(clippingNr, fusionPreviewData) { 1221 return new Promise((resolve, reject) => { 1222 _.actions({route:'cfg/clip//load', data:{nr: clippingNr}, onSuccess: (res) => { 1223 let clipping = res.clipping; 1224 clipping.content = clipping.description; 1225 delete clipping.description; 1226 if (clipping.content.match(/#/) || (clipping.subject && clipping.subject.match(/#/))) { 1227 let ajaxData = $.extend({contents: {subject: clipping.subject || '', content: clipping.content}}, fusionPreviewData || {}); 1228 _.actions({route: 'tl/fusion//preview', data: ajaxData, onSuccess: (data) => { 1229 clipping.content = data.content; 1230 clipping.subject = data.subject; 1231 resolve(clipping); 1232 }}); 1233 } 1234 else { 1235 resolve(clipping); 1236 } 1237 }}); 1238 }); 1239 }, 1240 1241 set: function(key, val, session) { 1242 if (typeof val == "undefined") val = ""; 1243 if (session) res = window.sessionStorage.setItem(key, JSON.stringify(val)); 1244 else res = window.localStorage.setItem(key, JSON.stringify(val)); 1245 return res; 1246 }, 1247 get: function(key, session) { 1248 if (session) var val = window.sessionStorage.getItem(key); 1249 else var val = window.localStorage.getItem(key); 1250 if (typeof val !="undefined") return JSON.parse(val); 1251 return false;
1252 }, 1253 round: function(num, nb) { 1254 if (!nb) nb = 0; 1255 num = parseFloat(num); 1256 var pow = Math.pow(10,nb) 1257 return Math.round(num*pow)/pow; 1258 // num = num*pow; // je ne sais plus pourquoi c'était aussi alambiqué ... probablement le contournement d'un glitch 1259 // if (nb<6) num = Math.round(num*100000)/100000; 1260 // return Math.round(num)/pow; 1261 }, 1262 roundExpr: function(num, nb=3, half=true) { 1263 if (!num) return 0; 1264 var d = Math.ceil(Math.log10(num < 0? -num : +num)), 1265 power = nb - d, 1266 magn = Math.pow(10, power); 1267 if (half) magn*= 2; 1268 var shifted = Math.round(num*magn); 1269 return shifted/magn; 1270 }, 1271 preCleanNum: function(num) { 1272 if (typeof num =="string") 1273 { 1274 num = num.replace(",", ".").replace(" ", "").replace(" ", ""); 1275 } 1276 return num; 1277 }, 1278 cleanNum: function(num) { 1279 if (!num) num = 0; 1280 if (typeof num =="string") 1281 { 1282 num = _.preCleanNum(num); 1283 num = num.replace('O', '0'); 1284 num = num.replace(/[^0-9,.-]+/, ''); 1285 if (!num) num = 0; 1286 num = parseFloat(num); 1287 } 1288 return num; 1289 }, 1290 formatBigNb: function(n, format, full) { 1291 if (full) 1292 { 1293 switch (format) 1294 { 1295 case 'price': return Intl.NumberFormat('fr-FR', { style: 'currency', currency: _.cfg.currencyCode, maximumSignificantDigits: 10 }).format(Math.round(n)); break; 1296 case 'int': return Intl.NumberFormat('fr-FR', { style: 'decimal', maximumSignificantDigits: 10 }).format(Math.round(n)); break; 1297 case 'percent': return Intl.NumberFormat('fr-FR', { style: 'decimal' }).format(Math.round(n))+' %'; break; 1298 case 'float': return Intl.NumberFormat('fr-FR', { style: 'decimal' }).format(Math.round(n)); break; 1299 } 1300 } 1301 else 1302 { 1303 n = parseFloat(n); 1304 var v=0, l=''; 1305 if (n>20000000) { n = (n/1000000).toFixed(0); l='M';} 1306 else if (n>2000000) { n = (n/1000000).toFixed(1); l='M';} 1307 else if (n>800000) { n = (n/1000000).toFixed(2); l='M';} 1308 else if (n>20000) { n = (n/1000).toFixed(0); l='K';} 1309 else if (n>2000) { n = (n/1000).toFixed(1); l='K';} 1310 else if (n>20) { n = n.toFixed(0); l='';} 1311 switch (format) 1312 { 1313 case 'price': 1314 if (n>20) v = number_format(Math.round(n), 0, ",", " ")+l+" <span class='cur'>"+_.cfg.currency+"</span>"; 1315 else v = parseFloat(n).toFixed(2)+l+" <span class='cur'>"+_.cfg.currency+"</span>"; 1316 return v; 1317 break; 1318 case 'percent': 1319 return parseFloat(n)+l+" <span class='cur'>%</span>"; 1320 break; 1321 case 'float': 1322 if (n>20) v = Math.round(n); 1323 else if (n>10) v = n.toFixed(1); 1324 else if (n>1) v = n.toFixed(2); 1325 else if (n>0.1) v = n.toFixed(3); 1326 break; 1327 case 'int': 1328 default : v = Math.round(n); break; 1329 } 1330 return v+''+l; 1331 } 1332 }, 1333 formatCode: function(code) { 1334 var text = []; 1335 if (typeof code == "string") 1336 { 1337 var parts = [...code.matchAll(/[A-Z][a-z-]+|[A-Z]+|[a-z-]+|[0-9.]/g)]; 1338 $.each(Array.from(parts), function(i, part) { 1339 text.push(part[0].ucwords()); 1340 }); 1341 } 1342 1343 return text.join(" "); 1344 }, 1345 nephew: function(n, t) {return $(t).siblings().find(n);}, 1346 1347 lr:function(fr, en, ru) { 1348 if (digiSuiteVars.locale=='en') return en; 1349 else if (digiSuiteVars.locale=='ru') return ru; 1350 else return fr; 1351 }, 1352 isMobile: function() { 1353 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); 1354 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); 1355 1356 return mobile; 1357 }, 1358 removeAccents: function(str) { 1359 if (!str) str = ''; 1360 return str.normalize('NFD').replace(/[\u0300-\u036f]/g, ""); 1361 }, 1362 cleanEmoji: function(str) { 1363 var reg = /\ud83c[\udf00-\udfff]|\ud83d[\udc00-\ude4f]|\ud83d[\ude80-\udeff]/g; 1364 str = str.replace(reg, ' '); 1365 return str; 1366 }, 1367 cleanRegEx: function(str) { 1368 var reg = /[*+?\[\]\(\)]/g; 1369 str = str.replace(reg, ' '); 1370 str = str.replace(/ +/, ' '); 1371 return str; 1372 }, 1373 closeToolTips: function() { 1374 if (_.tooltipTarget && _.tooltipTarget.closeToolTip) _.tooltipTarget.closeToolTip(); 1375 $('.toolTip').hide(); 1376 }, 1377 populate: function(s, vals, test, valFunc, selected, clear) { 1378 if (clear) $('>*', s).remove(); 1379 var v = $(s).val(); 1380 if (v) selected = v; 1381 if (vals) 1382 { 1383 $.each(vals, function(k, v) { 1384 var icon = false;
1385 var color = false; 1386 if (Array.isArray(v)) 1387 { 1388 k = v[0]; v = v[1]; 1389 } 1390 if (typeof v == "object") 1391 { 1392 if (typeof v.key!='undefined') 1393 { 1394 if (v.icon) icon = v.icon; 1395 if (v.color) color = v.color; 1396 k = v.key; v = v.val || v.value; 1397 } 1398 else if (typeof v.text!='undefined') 1399 { 1400 if (v.icon) icon = v.icon; 1401 if (v.color) color = v.color; 1402 k = v.value; v = v.text; 1403 } 1404 } 1405 if (!test || test(k, v)) 1406 { 1407 var opt = _.ce('option'); 1408 $(opt).attr('value',k).html(valFunc? valFunc(k, v) : v); 1409 if (selected!==false && ((Array.isArray(selected) && selected.indexOf(k)!=-1) || selected==k)) 1410 { 1411 $(opt).prop('selected', true); 1412 } 1413 $(s).append(opt); 1414 } 1415 if (icon) $(opt).attr('icon', icon); 1416 if (color) $(opt).attr('color', color); 1417 }); 1418 } 1419 }, 1420 goUrl: function(url, ev) { 1421 var a = _.ce("a"), 1422 pos = ev? _.eventXY(ev) : {x:0,y:0}, 1423 evParams = { 1424 button:0, 1425 clientX:ev? pos.x : _.pageX, 1426 clientY:ev? pos.y : _.pageY, 1427 bubbles: true, 1428 cancelable: true, 1429 view: window, 1430 }, 1431 evt = new MouseEvent("click", evParams); 1432 $(a).attr('href',url).attr('target','_blank'); 1433 1434 a.dispatchEvent(evt); 1435 1436 }, 1437 1438 getHtmlBloc: function(url, callback) { 1439 $.ajax({ 1440 type: 'GET', 1441 url: url, 1442 data: {}, 1443 dataType: 'html', 1444 cache:false, 1445 timeout:10000, 1446 success: function(res) { 1447 callback(res); 1448 } 1449 }); 1450 }, 1451 1452 getContentBloc: function(data) { 1453 1454 return new Promise(function(resolve, reject) { 1455 1456 var content = ""; 1457 if (data.template) content = $(data.template).html(); 1458 if (!content && data.content) content = data.content; 1459 if (content) 1460 { 1461 resolve(content); 1462 } 1463 else if (data.code || data.urlsite || data.url) 1464 { 1465 var params = data.params? data.params : "", 1466 url = ''; 1467 if (!params && data.data) params = data.transData? data.transData : data.data; 1468 if (data.url) 1469 { 1470 url = _.cfg.urladmin+"/"+data.url.replace(/&/g, '&'); 1471 url+= (url.indexOf('?')!=-1? '&' : '?')+'spaceSelect=1'; 1472 } 1473 else if (data.urlsite) url = _.cfg.urlsite+"/"+data.urlsite; 1474 else url = _.cfg.urladmin+"/cfg/modal/ajax/"+data.code; 1475 1476 _.digi.request(url, params, {dataType:'html'}).then((res)=>{ 1477 if (res) 1478 { 1479 resolve(res); 1480 } 1481 else 1482 { 1483 reject(new Error('The URL or Code ('+url+') hasn\'t return any content')); 1484 } 1485 }).catch((error) => { 1486 reject(error); 1487 }); 1488 } 1489 else 1490 { 1491 reject(new Error('No content specified to get (or url or code)')); 1492 } 1493 }); 1494 }, 1495 1496 copyToClipboard: function(text, select) { 1497 if (select) text = $(select).html(); 1498 1499 function listener(e) { 1500 var plainText = $('<div>'+text+'</div>').text().trim(); 1501 1502 e.clipboardData.setData("text/plain", plainText); 1503 e.clipboardData.setData("text/html", text); 1504 1505 e.preventDefault(); 1506 } 1507 1508 document.addEventListener("copy", listener); 1509 document.execCommand("copy"); 1510 document.removeEventListener("copy", listener); 1511 }, 1512 1513 wyChangeHeight: function(id, h) { 1514 if (typeof tinymce != "undefined") 1515 { 1516 var wy = tinymce.get(id); 1517 if (wy) $(wy.getContainer()).css('height', h); 1518 } 1519 }, 1520 wy5: function(config, cross) { 1521 if (typeof tinymce == "undefined" && !cross) 1522 { 1523 window.dependencyLoader.loadScripts([ 1524 _.cfg.urlrealadmin+'/inc/tinymce5/tinymce.min.js' 1525 ]).then(() => { 1526 _.wy5(config, true); 1527 }); 1528 1529 return; 1530 } 1531 1532 _.clearDetachedWYEditors(); 1533 1534 var config = $.extend({ 1535 height: 250, 1536 menu: false, 1537 status: false, 1538 }, config || {}); 1539 1540 let defaultOptions = {}; 1541 1542 switch (config.mode) 1543 { 1544 case 'full': 1545 defaultOptions.fontsize_formats = "8px 9px 10px 11px 12px 14px 16px 18px 22px 26px 32px 36px 48px 60px 76px"; 1546 1547 defaultOptions.plugins = [ 1548 'print preview paste searchreplace autolink directionality code visualblocks ', // importcss 1549 'visualchars fullscreen image link media template codesample table charmap hr pagebreak nonbreaking ', 1550 'anchor toc insertdatetime advlist lists wordcount imagetools textpattern noneditable help charmap quickbars' // emoticons 1551 ]; 1552 if (config.menu===true) config.menu = 'file edit view insert format tools table help'; 1553 defaultOptions.toolbar = "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | al
1553ignleft aligncenter alignright alignjustify | " 1554 +"outdent indent | numlist bullist | forecolor backcolor removeformat | link image | fullscreen preview | " 1555 +"anchor media insertfile template codesample | pagebreak | charmap"; // emoticons 1556 defaultOptions.quickbars_selection_toolbar = 'bold italic | h1 h2 h3 blockquote | removeformat'; 1557 1558 if (config.fullpage) 1559 { 1560 defaultOptions.plugins.push('fullpage'); 1561 defaultOptions.fullpage_default_encoding = 'UTF-8'; 1562 } 1563 1564 defaultOptions.file_picker_callback = function(callback, value, meta) { // file_browser_callback 1565 var inputFile = _.ce('input', '', {type:'file', accept:'image/*'}); 1566 inputFile.onchange = function () { 1567 var file = this.files[0]; 1568 var reader = new FileReader(); 1569 reader.onload = function () { 1570 var id = file.name; 1571 var blobCache = tinymce.activeEditor.editorUpload.blobCache; 1572 var base64 = reader.result.split(',')[1]; 1573 var blobInfo = blobCache.create(id, file, base64); 1574 blobCache.add(blobInfo); 1575 1576 callback(blobInfo.blobUri(), { title: file.name }); 1577 }; 1578 reader.readAsDataURL(file); 1579 }; 1580 1581 inputFile.click(); 1582 1583 }; 1584 if (config.uploadType) 1585 { 1586 defaultOptions.images_upload_url = _.cfg.urladmin+"/ajax/ups/ajax/n?tiny=1&type="+config.uploadType+"&nr="+config.uploadNr; 1587 defaultOptions.images_upload_credentials = true; 1588 // defaultOptions.images_reuse_filename = true; 1589 defaultOptions.paste_data_images = true; 1590 defaultOptions.file_picker_types = 'image media'; 1591 } 1592 defaultOptions.convert_urls = true; 1593 defaultOptions.image_caption = true; 1594 1595 defaultOptions.block_formats = 'Bloc=div; Paragraphe=p; Titre 1=h1; Titre 2=h2; Titre 3=h3; Titre 4=h4; Titre 5=h5; Titre 6=h6; Preformaté=pre'; 1596 defaultOptions.content_style = 'body { font-family:Arial,Helvetica,sans-serif; font-size:14px }'; 1597 break; 1598 1599 case 'light': 1600 case 'lighter': 1601 defaultOptions.toolbar = config.mode=='lighter'? "bold italic | bullist | link | customAddFile customSendButton" 1602 :"bold italic | bullist numlist | link blockquote | codesample | customAddFile customSendButton"; 1603 defaultOptions.plugins = [ 1604 'paste autolink code image link codesample lists imagetools' 1605 ]; 1606 if (!config.options || config.options.autoresize!==false) defaultOptions.plugins[0]+= ' autoresize'; 1607 defaultOptions.toolbar_location = 'bottom'; 1608 defaultOptions.force_p_newlines = false; 1609 defaultOptions.force_br_newlines = true; 1610 defaultOptions.convert_newlines_to_brs = true; 1611 defaultOptions.forced_root_block = ''; 1612 defaultOptions.images_file_types = ''; 1613// defaultOptions.height = 80; 1614 defaultOptions.autoresize_bottom_margin = 0; 1615 if (!defaultOptions.min_height) defaultOptions.min_height = 80; 1616 if (!defaultOptions.max_height) defaultOptions.max_height = 250; 1617 1618 defaultOptions.content_style = 'body { font-family:Arial,Helvetica,sans-serif; font-size:12px; margin:0.2rem 0.5rem; }'; 1619 defaultOptions.valid_elements = '@[id|class|style|title],-a[href|target=_blank],pre,b/strong,-div[align],blockquote,br,-i/em,-s/strike,-u,hr,br,-p,-ul[type|compact],-ol[type|compact],-li,-span,-h1,-h2,-h3,-h4,-h5,-h6'; 1620 defaultOptions.quickbars_selection_toolbar = false; 1621 break; 1622 1623 1624 case 'message': 1625 defaultOptions.fontsize_formats = "8px 9px 10px 11px 12px 14px 16px 18px 22px 26px 32px 36px 48px 60px 76px"; 1626// defaultOptions.icons = 'small'; 1627 defaultOptions.toolbar = "undo | fontsizeselect | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | link image | forecolor backcolor"; 1628 defaultOptions.plugins = [ 1629 'paste autolink code visualblocks ', 1630 'visualchars image link media template codesample table charmap hr pagebreak nonbreaking ', 1631 'anchor insertdatetime advlist lists imagetools textpattern noneditable charmap quickbars' 1632 ]; 1633 defaultOptions.content_style = 'body { font-family:Arial,Helvetica,sans-serif; font-size:14px }'; 1634 defaultOptions.valid_elements = '@[id|class|style|title],-a[href|target=_blank],b/strong,-div[align],br,-i/em,-s/strike,-u,hr,br,-p,-ul[type|compact],-ol[type|compact],-li,-span,-h1,-h2,-h3,-h4,-h5,-h6'; 1635 defaultOptions.quickbars_selection_toolbar = 'bold italic underline strikethrough | h1 h2 h3 blockquote'; 1636 1637 break; 1638 1639 case 'simple': 1640 defaultOptions.toolbar = "bold italic | alignleft aligncenter alignright | bullist numlist outde
1640nt indent | link | forecolor"; 1641 defaultOptions.plugins = [ 1642 'paste autolink code visualblocks ', 1643 'visualchars image link media template codesample table charmap hr pagebreak nonbreaking ', 1644 'anchor insertdatetime advlist lists imagetools textpattern noneditable charmap quickbars' 1645 ]; 1646 defaultOptions.content_style = 'body { font-family:Arial,Helvetica,sans-serif; font-size:14px }'; 1647 break; 1648 } 1649 1650 config.options = $.extend(defaultOptions, config.options || {}); 1651 if (!config.autosave_disable) config.options.plugins.push('autosave'); 1652 if (config.options.toolbarPrepend) config.options.toolbar = config.options.toolbarPrepend + config.options.toolbar; 1653 if (config.options.addPlugins) config.options.plugins+= ' '+config.options.addPlugins; 1654 1655 var init = { 1656 selector: config.select, 1657 base_url: _.cfg.urlrealadmin+'/inc/tinymce5', 1658 skin: 'oxide', 1659 relative_urls: false, 1660 convert_urls: false, 1661// remove_script_host : false, 1662 toolbar_sticky: true, 1663 1664 autosave_ask_before_unload: false, 1665 autosave_interval: '10s', 1666 autosave_prefix: 'df3-{path}{query}-{id}-', 1667 autosave_restore_when_empty: true, 1668 autosave_retention: '12m', 1669 1670 imagetools_cors_hosts: ['picsum.photos'], 1671 language : _.cfg.locale=='fr'? "fr_FR" : "en_GB", 1672 width: "100%", 1673 height: config.height, 1674 menubar: config.menu, 1675 statusbar: config.status, 1676 toolbar_mode: 'sliding', 1677 1678 help_tabs: [ 'shortcuts', 'keyboardnav'], 1679 quickbars_insert_toolbar: false, 1680 contextmenu: 'link image imagetools table', 1681 textpattern_patterns: [ 1682 {start: '1. ', cmd: 'InsertOrderedList'}, 1683 {start: '* ', cmd: 'InsertUnorderedList'}, 1684 {start: '- ', cmd: 'InsertUnorderedList' } 1685 ], 1686// paste_data_images: true, 1687 paste_preprocess: function(plugin, args) { 1688 1689 }, 1690 paste_postprocess: function(plugins, args) { 1691 } 1692 }; 1693 if (config.options) 1694 { 1695 init = $.extend(init, config.options); 1696 } 1697 tinymce.suffix = '.min'; 1698 tinymce.uploadType = config.type; 1699 tinymce.uploadNr = config.nr; 1700 tinymce.init(init); 1701 1702 _.wyAutoCleanDraft(); 1703 }, 1704 1705 wyAutoCleanDraft: function() { 1706 var keys = Object.keys(window.localStorage); 1707 $.each(keys, function(i, key) { 1708 var match = key.match(/df3-(.*)-time/); 1709 if (match) 1710 { 1711 if (parseInt(window.localStorage.getItem(key)) < (Date.now()-12*60*1000)) 1712 { 1713 window.localStorage.removeItem(key); 1714 window.localStorage.removeItem('df3-'+match[1]+'-draft'); 1715 } 1716 } 1717 }); 1718 }, 1719 1720 1721 1722 wy: function(config) { // type, [nr], select, [height], [menu] 1723 return _.wy5(config); 1724 }, 1725 1726 // @deprecated : use _.clearDetachedWYEditors() 1727 wyRemoveAll: function(wId) { 1728 _.clearDetachedWYEditors(); 1729 }, 1730 1731 1732 simplifyHtml: function(html) { // , keep 1733 //var toKeep = ['a', 'i', 'em', 'b', 'strong', 'br', 'p', 'div', 'span', 'ul', 'li', 'ol', 'u', 's']; 1734 html = html.replace(/<script[^>]*>/ig, ""); 1735 if (html.match(/<a([^>]+?)href\s*=\s*["']([^"']+)["'][^>]*>/ig)) 1736 { 1737 html = html.replace(/<a[^>]+?href\s*=\s*["']([^"']+)["'][^>]*>/ig, "<a href=\"$1\">"); 1738 } 1739 else html = html.replace(/<([a-z0-9]+)[^>]*>/ig, "<$1>"); 1740 html = html.replace(/<\/([a-z0-9]+)[^>]*>/ig, "</$1>"); 1741 1742 return html; 1743 }, 1744 1745 changeStrPattern: function(str, patterns) { 1746 1747 // Liens 1748 var linkReg = /(^|[\(:]\s*|<[a-z]+[^>]*>|\s+|;)(((https|http|ftp):\/\/|www\.)?(([a-z0-9][a-z0-9-]*)(\.[a-z0-9][a-z0-9-]*)*\.[a-z]+)(\/[^<\"' \t\n\r]*)?)(\s*<\/[a-z]+>|[<\"' \t\n\r;,!*?.&()]|\s|$)/ig; 1749 var donePatterns = []; 1750 while ((match = linkReg.exec(str))!==null) 1751 { 1752 if (match[0].toLowerCase().indexOf('</a>')==-1) 1753 { 1754 var link = match[2]; 1755 if (!link.match(/(https|http|ftp):\/\//i)) link = "https://"+link; 1756 str = str.strReplace(match[0], match[1]+'<a href="'+link+'" target="_blank">'+(match[2].length>60? match[2].substring(0,57)+"...": match[2])+'</a>'+match[9]); 1757 } 1758 else 1759 { 1760 // trouver le <a> et rajouter target="_blank" 1761 } 1762 } 1763 1764 // Emails 1765 var matches = str.matchAll(/(^|\s+|[\(:])([_.0-9a-z]([-+=_.0-9a-z])*@[0-9a-z]([-.]?[0-9a-z]+)*\.[a-z]{2,13})([<\t\n\r,;:!?()|=&]|$)/ig); 1766 var donePatterns = []; 1767 $.each(matches, function(i, match) { 1768 var link = match[2]; 1769 str = str.strReplace(match[2], '<a href="mailto:'+match[2]+'" target="_blank">'+(match[2].length>60? match[2].substring(0,57)+"...": match[2])+'</a>'); 1770 }); 1771 1772 // patterns pré-détectés 1773 if (patterns) 1774 { 1775 $.each(patterns, function(i, pattern) { 1776 if (donePatterns.indexOf(pattern.ref)==-1) 1777 { 1778 donePatterns.push(pattern.ref); 1779 var rep = pattern.name? pattern.name : pattern.ref; 1780 var pre="", title="", url=''; 1781 switch (pattern.type) 1782 { 1783 case 'contact': pre = '<i class="i-user"></i> '; title="Contact"; url = 'crm/ct/view?nr='+pattern.nr; break; 1784 case 'tk': pre = '<i class="i-tag"></i> '; title="Ticket"; url = 'crm/ticket/view?nr='+pattern.nr; break; 1785 case 'fa': pre = '<i class="i-shopping-cart"></i> '; title="Facture"; url = 'com/cto/viewPayment?nr='+pattern.nr; break; 1786 case 'bl': pre = '<i class="i-shopping-cart"></i> '; title="Bon de Livraison"; url = 'com/cto/viewLogistic?nr='+pattern.nr; break; 1787 case 'bc': pre = '<i class="i-shopping-cart"></i> '; title="Vente / Bon de commande"; url = 'com/cto/view?nr='+pattern.nr; break; 1788 case 'cpy': pre = '<i class="i-building"></i> '; title="Société"; url = 'crm/cpy/view?nr='+pattern.nr; break; 1789 case 'qt': pre = '<i class="i-file-spreadsheet far"></i> '; title="Devis"; url = 'crm/oppt/qtView?qnr='+pattern.nr; break; 1790 case 'user': pre = '@'; title=""; break; 1791 } 1792 if (pattern.name) title+= " - "+pattern.ref; 1793 if (url) 1794 { 1795 rep = '<span title="'+title+'" class="jaction ref known" jsinfos="{url: \''+url+'\', target:\'_blank\'}">'+pre+rep+'</span>'; 1796 } 1797 else 1798 { 1799 rep = '<b class="known">'+pre+rep+'</b>'; 1800 } 1801 if (pattern.str && pattern.str.length > 0) 1802 { 1803 str = str.strReplace(pattern.str, rep); 1804 } 1805 } 1806 }); 1807 } 1808 1809 // autre pattern identifiable 1810 // prix 1811 str = str.replace(/(^|\s|>)(-\s?[0-9][0-9 .,]*\s*(â¬|$|£|€|euros?))/ig, '$1<span class="price neg">$2</span>'); 1812 str = str.replace(/(^|\s|>)([0-9][0-9 .,]*\s*(â¬|$|£|€|euros?))/ig, '$1<span class="price">$2</span>'); 1813 1814 // références-like 1815 var matches = str.matchAll(/(^|\s+|<[a-z0-9]+\s+[^>]*|=\s*"||=\s*'|[\(:>
1815"'])([a-z]+[0-9_-]+[0-9a-z_-]*[0-9a-z]|[0-9]+[a-z_-]+[0-9a-z_-]*[0-9a-z]|[0-9a-z]+[_-][0-9a-z_-]*[0-9a-z](\s+[0-9]{2}:[0-9]{2}:[0-9]{2})?)($|\s+|[\)<"'])/ig); 1816 $.each(matches, function(i, match) { 1817 if (donePatterns.indexOf(match[2])==-1 && !match[1].match(/^<[a-z0-9]/i) && !match[1].match(/^=\s*['"]/i) && match[2].match(/[0-9]/) 1818 && (match[2].length>5 || match[2].match(/^[A-Z0-9_-]+$/))) 1819 { 1820 donePatterns.push(match[2]); 1821 str = str.strReplace(match[2], '<span class="ref">'+match[2]+'</span>'); 1822 } 1823 }); 1824 1825 return str; 1826 }, 1827 1828 showContentInfo: function(data, bloc, subSelect) { 1829 if ($(subSelect, bloc).length>0) 1830 { 1831 setTimeout(function() { 1832 $(subSelect+' img', bloc).attr('cmp', 'img'); 1833 $(subSelect+' img', bloc).cmpImg(); 1834 }, 500); 1835 } 1836 if (data.tags && data.tags.length>0) 1837 { 1838 $(bloc).append('<ul class="tags"></ul>'); 1839 $.each(data.tags, function(i, tag) { 1840 var li = _.ce('li', 'tag', {}, "#"+tag.name); // A faire : Ajouter un jsinfos pour aller sur le channel de ce tag 1841 $('ul.tags', bloc).append(li); 1842 }); 1843 } 1844 if (data.links && data.links.length>0) 1845 { 1846 var links = _.ce('ul', 'links'); 1847 var imgLinks = []; 1848 $(bloc).append(links); 1849 1850 $.each(data.links, function(i, link) { 1851 if (link.type=="image") 1852 { 1853 imgLinks.push({isPic:true, thumb:link.urlimg, src:link.urlimg, mime:'jpg'}); 1854 } 1855 else if (link.title || link.description || link.urlimg) 1856 { 1857 var linkLi = _.ce('li', 'link'), 1858 lkTitle = _.ce('h6', '', {}, link.title), 1859 lkDesc = _.ce('p', '', {}, link.description.length>140? link.description.substring(0,137)+'...' : link.description); 1860 if (link.urlimg) 1861 { 1862 $(linkLi).append('<figure/>'); 1863 $('figure', linkLi).html('<img src="'+link.urlimg+'">'); 1864 } 1865 $(linkLi).append('<div/>'); 1866 $('>div', linkLi).append(lkTitle, lkDesc); 1867 $(links).append(linkLi); 1868 $(linkLi).click(function(ev) { 1869 _.actions({act:'href', href: link.link, target:'_blank'}, false, ev); 1870 }); 1871 } 1872 }); 1873 } 1874 if (data.files && data.files.length>0) 1875 { 1876 var listCmp = _.components.add({type:'filelist', format:'mix', data:{list:data.files}}); 1877 $(bloc).append(listCmp); 1878 } 1879 if (imgLinks && imgLinks.length>0) 1880 { 1881 var listCmp = _.components.add({type:'filelist', format:'mix', data:{list:imgLinks}}); 1882 $(bloc).append(listCmp); 1883 } 1884 }, 1885 1886 1887 filterItems: function(search, items, flds, max, wordSearch) { 1888 if (!search) 1889 { 1890 return items.slice(); 1891 } 1892 else 1893 { 1894 if (!Array.isArray(flds)) flds = [flds]; 1895 var words = _.cleanRegEx(_.removeAccents(search)).split(" "); 1896 var filterItems = []; 1897 for (var j=0; j<items.length; j++) 1898 { 1899 // split de la value en mots 1900 var dataVal = ""; 1901 for (var f=0; f<flds.length; f++) 1902 { 1903 var _str = items[j][flds[f]]; 1904 if (typeof _str == "object" && Array.isArray(_str)) 1905 { 1906 _str = _str.join(" "); 1907 } 1908 dataVal+= " "+_.removeAccents(_str); 1909 } 1910 var dataOk = true; 1911 1912 // recherche des mots dans la valeur à recherche (à concaténer) 1913 for (var k=0; k<words.length; k++) 1914 { 1915 var regex = new RegExp((wordSearch? "\\b" : "")+words[k], "gi"); 1916 if (!dataVal.match(regex)) 1917 { 1918 dataOk = false; 1919 } 1920 } 1921 if (dataOk) 1922 { 1923 filterItems.push(items[j]); 1924 } 1925 if (max && filterItems.length>max) return filterItems; 1926 } 1927 return filterItems; 1928 } 1929 }, 1930 1931 boldSearch: function(str, search) { 1932 // bug probable : si le contenu retourné contient des tags html ET que le début de la 1933 // recherche correspond à un nom de tag ou un attribut de tag, par exemple src = 1934 if (!search) return str; 1935 if (!str) str = ""; 1936 var s = search.split(" "), 1937 words = str.split(" "), 1938 nbs = s.length, 1939 nb = words.length; 1940 1941 for (var k=0; k<nb; k++) 1942 { 1943 if (!words[k].match(/[<>="]/i)) // pour ne pas traiter des mots qui seraient des morceaux de tag ou attributs 1944 { 1945 for (var j=0; j<nbs; j++) 1946 { 1947 w = _.removeAccents(words[k]).toLowerCase(); 1948 1949 var sj = _.removeAccents(s[j]).toLowerCase(); 1950 sj = sj.replace('\^', ''); 1951 pos = w.indexOf(sj); 1952 1953 if (pos>=0 && words[k].indexOf("<b>")==-1) // pour éviter d'éventuels ajouts de <b> 1954 { 1955 slen = sj.length; 1956 wlen = words[k].length; 1957 words[k] = words[k].substr(0, pos)+"<b>"+words[k].substr(pos, slen)+"</b>"+words[k].substr(slen+pos); 1958 } 1959 } 1960 } 1961 } 1962 return words.join(" "); 1963 }, 1964 1965 1966 isObject: function(o) { 1967 return (typeof o==="object" && !Array.isArray(o)); 1968 }, 1969 isEmpty: function(o) { 1970 if (o==null) return true; 1971 if (Array.isArray(o) || typeof o == 'string') return o.length==0; 1972 for (var k in o) if (o.hasOwnProperty(k)) return false;
1973 return true; 1974 }, 1975 objLength: function(o) { 1976 var n = 0; 1977 for (var k in o) if (o.hasOwnProperty(k)) n++; 1978 return n; 1979 }, 1980 splitKeys(keys) { 1981 if (typeof keys == 'string') keys = keys.split(','); 1982 return keys; 1983 }, 1984 reduceKeys: function (obj, keys) { 1985 keys = this.splitKeys(keys); 1986 1987 return Object.entries(obj).filter((entry) => keys.includes(entry[0])).reduce((accumulator, entry) => { accumulator[entry[0]] = entry[1]; return accumulator; }, {}); 1988 }, 1989 isHTML: function(str) { 1990 return str.match(/<(br|p|div|ul|li|ol|pre)[^>]*>/i); 1991 }, 1992 1993 intersect: function(a, b) { 1994 var ai=0, bi=0, r = []; 1995 1996 while (ai<a.length && bi<b.length) 1997 { 1998 if (a[ai] < b[bi] ) ai++; 1999 else if (a[ai] > b[bi] ) bi++; 2000 else 2001 { 2002 r.push(a[ai]); 2003 ai++; bi++; 2004 } 2005 } 2006 return r; 2007 }, 2008 2009 eventXY: function (e) { 2010 var out = {x:0, y:0}; 2011 if (e.type.indexOf('touch')!=-1) 2012 { 2013 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 2014 out.x = touch.pageX; out.y = touch.pageY; 2015 out.target = document.elementFromPoint(out.x - window.pageXOffset, out.y - window.pageYOffset); 2016 } 2017 else if (e.type.indexOf('mouse')!=-1 || e.type=='contextmenu' || e.type=='click') 2018 { 2019 out.x = e.pageX; out.y = e.pageY; 2020 out.target = e.target; 2021 } 2022 else if ((e.type=='drop' || e.type=='dragover' || e.type=='dragenter' || e.type == 'dragleave') && e.originalEvent) 2023 { 2024 out.x = e.originalEvent.pageX; out.y = e.originalEvent.pageY; 2025 out.target = e.originalEvent.target; 2026 } 2027 else if (e.page && e.nativeEvent) // PIXI 2028 { 2029 out.x = e.page.x; 2030 out.y = e.page.y; 2031 } 2032 if ((typeof out.target=='undefined' && typeof _.pageX!='undefined') || e.type=="paste") 2033 { 2034 out.x = _.pageX; 2035 out.y = _.pageY; 2036 } 2037 else if (typeof out.x!='undefined' && typeof out.y!='undefined') 2038 { 2039 _.pageX = out.x; 2040 _.pageY = out.y; 2041 } 2042 2043 let originalEvent = 'originalEvent' in e ? e.originalEvent : e; 2044 if (originalEvent && originalEvent.view) 2045 { 2046 let eventDocument = originalEvent.view.document; 2047 if (eventDocument !== document) { 2048 let iframe = originalEvent.view.frameElement; 2049 let iframePos = $(iframe).offset(); 2050 let yScroll = eventDocument.documentElement.scrollTop; 2051 let xScroll = eventDocument.documentElement.scrollLeft; 2052 out.iframeRelativePos = { 2053 x: out.x - xScroll, 2054 y: out.y - yScroll, 2055 }; 2056 out.x += iframePos.left - xScroll; 2057 out.y += iframePos.top - yScroll; 2058 } 2059 } 2060 2061 return out; 2062 }, 2063 2064 followTheMouse: function() { 2065 if (!_.metaKey) 2066 { 2067 _.pageX = 0; 2068 _.pageY = 0; 2069 _.metaKey = {}; 2070 _.pageLastTarget = false;
2071 $(document).ready(function() { 2072 $(document).on('mousemove', (e)=>{ // dragover drag 2073 var pos = _.eventXY(e); 2074 _.pageX = pos.x; 2075 _.pageY = pos.y; 2076 _.pageLastTarget = e.target; 2077 _.metaKey = { 2078 ctrl:e.ctrlKey, 2079 shift:e.shiftKey, 2080 alt:e.altKey, 2081 key:e.keyCode 2082 }; 2083 }); 2084 }); 2085 } 2086 }, 2087 2088 autoHscroll: function(section) { 2089 2090 _.followTheMouse(); 2091 2092 var moveTimer = setInterval(function() { 2093 if (_.pageLastTarget && $(_.pageLastTarget).closest('.latPanel').length>0) 2094 { 2095 } 2096 else 2097 { 2098 var activate = false; 2099 if (!section) activate = true; 2100 else 2101 { 2102 var o = $(section).offset(); 2103 if (_.pageY>=o.top && _.pageY<=o.top+$(section).outerHeight()) 2104 { 2105 activate = true; 2106 } 2107 } 2108 var $excludedZones = $('.chat-room,nav.leftMenu'); 2109 if ($excludedZones.length>0) 2110 { 2111 $excludedZones.each(function(i, toExclude) { 2112 var exO = $(toExclude).offset(); 2113 if (_.pageX>=exO.left && _.pageX<=exO.left+$(toExclude).width() && 2114 _.pageY>=exO.top && _.pageY<=exO.top+$(toExclude).height() ) 2115 { 2116 activate = false; 2117 } 2118 }); 2119 } 2120 if (activate) 2121 { 2122 var left = $(document).scrollLeft(); 2123 var lw = _.pageX-left; 2124 if (lw>30) 2125 { 2126 if (lw<120 && left>20) 2127 { 2128 if (lw>100) { left-=20; _.pageX-= 20; } 2129 else if (lw>70) { left-=50; _.pageX-= 50; } 2130 else { left-=80; _.pageX-= 80; } 2131 2132 if (left<0) left = 0; 2133 $(document).scrollLeft(left); 2134 } 2135 else 2136 { 2137 var w = $(window).width(), 2138 totw = $('.scrollZone').width(); 2139 var rw = w - (_.pageX - left); 2140 2141 if (rw<120 && left<totw-20) 2142 { 2143 if (rw>100) { left+=20; _.pageX+= 20; } 2144 else if (rw>70) { left+=50; _.pageX+= 50; } 2145 else { left+=80; _.pageX+= 80; } 2146 2147 $(document).scrollLeft(left); 2148 } 2149 } 2150 } 2151 } 2152 } 2153 2154 }, 100); 2155 2156 }, 2157 2158 autoDragScroll: function() { 2159 2160 _.draggingTimer = setInterval(function() { 2161 2162 // ScrollScreen while drag 2163 var scrTop = $('html').scrollTop(); 2164 if (scrTop+50 > _.pageY && _.pageY>0) 2165 { 2166 var diff = 4; 2167 if (scrTop+30 > _.pageY) diff = 8; 2168 if (scrTop+10 > _.pageY) diff = 16; 2169 if (scrTop-5 > _.pageY) diff = 30; 2170 $('html').scrollTop(scrTop-diff); 2171 } 2172 2173 var scrBottom = scrTop + $(window).height(); 2174 2175 if (scrBottom-50 < _.pageY && scrBottom<$(document).height()-$(_.dragToMove).height()-20) 2176 { 2177 var diff = 4; 2178 if (scrBottom-30 < _.pageY) diff = 8; 2179 if (scrBottom-10 < _.pageY) diff = 16; 2180 if (scrBottom+5 < _.pageY) diff = 30; 2181 $('html').scrollTop(scrTop+diff); 2182 } 2183 }, 200); 2184 2185 2186 }, 2187 autoDragScrollStop: function() { 2188 clearInterval(_.draggingTimer); 2189 _.draggingTimer = false; 2190 _.draggingLastY = false; 2191 }, 2192 2193 findClosestMarkedScrollableElement: function (element) { 2194 let closestMarkedScrollableElement = $(element).closest('.df3-scrollable').get(0); 2195 return closestMarkedScrollableElement || null; 2196 }, 2197 2198 markScrollableElements: function (direction) { 2199 if (typeof direction == 'undefined') direction = 'vertical'; 2200 let elements = []; 2201 if (direction == 'vertical' || direction == 'both') elements = elements.concat(this.getScrollableElements('vertical')); 2202 if (direction == 'horizontal' || direction == 'both') elements = elements.concat(this.getScrollableElements('horizontal')); 2203 2204 _.unmarkScrollableElements(); 2205 $(elements).addClass('df3-scrollable'); 2206 }, 2207 2208 unmarkScrollableElements: function () { 2209 $('.df3-scrollable').removeClass('df3-scrollable'); 2210 }, 2211 2212 getScrollableElements: function (direction) { 2213 if (typeof direction == 'undefined') direction = 'vertical'; 2214 return [...document.querySelectorAll('*')].filter(x => _.isElementScrollable(x, direction)); 2215 }, 2216 2217 isElementScrollable: function(element, direction) { 2218 let scrollProperty = (direction !== 'horizontal') ? 2219 'scrollTop' : 'scrollLeft'; 2220 let overflowProperty = (direction !== 'horizontal') ? 2221 'overflow-y' : 'overflow-x'; 2222 2223 if (getComputedStyle(element).getPropertyValue(overflowProperty) == 'hidden') { 2224 return false;
2225 } 2226 2227 var res = !! element[scrollProperty]; 2228 2229 if (!res) { 2230 element[scrollProperty] = 1; 2231 res = !!element[scrollProperty]; 2232 element[scrollProperty] = 0; 2233 } 2234 2235 return res; 2236 }, 2237 2238 isElementPartiallyVisible: function (element, container, scrollValue) { 2239 if (typeof container == 'undefined' || !container) container = document.body; 2240 2241 const elementClientRect = element.getBoundingClientRect(); 2242 const elementTop = elementClientRect.top; 2243 const elementBottom = elementClientRect.bottom; 2244 2245 let containerTop = null; 2246 let containerBottom = null; 2247 if (container.window === container) { 2248 containerTop = scrollValue || container.document.body.scrollTop; 2249 containerBottom = containerTop + container.innerHeight; 2250 } else { 2251 containerTop = scrollValue || container.scrollTop; 2252 containerBottom = container.getBoundingClientRect().bottom; 2253 } 2254 2255 // Some part of the element is visible in the container 2256 return (elementTop >= containerTop && (elementTop + 20) < containerBottom) 2257 || (elementBottom <= containerBottom && (elementBottom + 20) > containerTop) 2258 || (elementTop < containerTop && elementBottom > containerBottom); 2259 }, 2260 2261 getCoord: function(item) { 2262 var itemCoord = false; 2263 if (typeof item == 'string') item = $(item); 2264 if (typeof item == 'object' && !Array.isArray(item)) 2265 { 2266 if (item instanceof jQuery) item = item[0]; 2267 if (item instanceof HTMLElement) 2268 { 2269 itemCoord = [$(item).offset()]; 2270 var w = $(item).outerWidth(), 2271 h = $(item).outerHeight(); 2272 } 2273 else 2274 { 2275 itemCoord = [{top:_.pageY, left:_.pageX}]; 2276 var w = 0, 2277 h = 0; 2278 } 2279 itemCoord[1] = { 2280 left: itemCoord[0].left + w, 2281 top: itemCoord[0].top + h 2282 }; 2283 } 2284 else if (Array.isArray(item)) 2285 { 2286 itemCoord = item; 2287 } 2288 return itemCoord; 2289 }, 2290 2291 getPosition: function(item, outsideOf, insideOf, options) { 2292 var itemCoord = _.getCoord(item), // item à placer 2293 outCoord = _.getCoord(outsideOf), // autour duquel ? 2294 inCoord = _.getCoord(insideOf), 2295 win = { 2296 h: $(window).height(), 2297 w: $(window).width(), 2298 top: options.window? 0 : $(window).scrollTop(), 2299 left: options.window? 0 : $(window).scrollLeft(), 2300 }; 2301 2302 if (itemCoord) 2303 { 2304 if (!options) options = {}; 2305 else options = $.extend(true, {}, options); 2306 if (!options.prio) options.prio = 'bottom'; // right, top 2307 if (!options.pos) options.pos = 'half'; // // top, bottom 2308 if (!options.padding) options.padding = 0; 2309 2310 if (!Array.isArray(options.prio)) 2311 { 2312 if (options.prio=='bottom') options.prio = ['bottom', 'top', 'left', 'right']; 2313 else if (options.prio=='right') options.prio = ['right', 'left', 'top', 'bottom']; 2314 else if (options.prio=='left') options.prio = ['left', 'right', 'bottom', 'top']; 2315 else if (options.prio=='top') options.prio = ['top', 'bottom', 'left', 'right']; 2316 options.prio.push('bottomRight'); 2317 options.prio.push('bottomLeft'); 2318 options.prio.push('topLeft'); 2319 } 2320 else options.prioOnly = true; 2321 if (options.over) options.prio.push('over'); 2322 2323 // prio bas, puis droite, puis gauche, puis haut 2324 var propItem = (options.pos=='half')? 0.5 : 0, 2325 w = $(item).outerWidth(), 2326 h = $(item).outerHeight(); 2327 2328 // ajuster dans quel cadre on peut placer l'élément 2329 if (inCoord) 2330 { 2331 if (inCoord[0].left<win.left) inCoord[0].left = win.left; 2332 if (inCoord[0].top<win.top) inCoord[0].top = win.top; 2333 if (inCoord[1].left>win.left+win.w) inCoord[1].left = win.left+win.w; 2334 if (inCoord[1].top>win.top+win.h) inCoord[1].top = win.top+win.h; 2335 } 2336 else 2337 { 2338 inCoord = [ win, {left:win.w+win.left, top:win.h+win.top} ]; 2339 } 2340 if (!outCoord) 2341 { 2342 outCoord = [ inCoord[1], inCoord[0], ]; 2343 } 2344 var outW = outCoord[1].left - outCoord[0].left, 2345 outH = outCoord[1].top - outCoord[0].top; 2346 2347 var tests = { 2348 left: {length:w, compare:outCoord[0].left - inCoord[0].left, pos:{left:outCoord[0].left - w - options.padding, top:outCoord[0].top + outH*propItem - h*propItem}}, 2349 right: {length:w, compare:inCoord[1].left - outCoord[1].left, pos:{left:outCoord[1].left + options.padding, top:outCoord[0].top + outH*propItem - h*propItem}},
2350 top: {length:h, compare:outCoord[0].top - inCoord[0].top, pos:{left:outCoord[0].left + outW*propItem - w*propItem, top:outCoord[0].top - h - options.padding}}, 2351 topLeft:{length:h, compare:outCoord[0].top - inCoord[0].top, pos:{left:outCoord[0].left - w + outW, top:outCoord[0].top - h - options.padding}}, 2352 bottom: {length:h, compare:inCoord[1].top - outCoord[1].top, pos:{left:outCoord[0].left + outW*propItem - w*propItem, top:outCoord[1].top + options.padding}}, 2353 bottomRight:{length:w, compare:inCoord[1].left - outCoord[1].left, pos:{left:outCoord[1].left + options.padding, top:outCoord[1].top - h}}, 2354 bottomLeft: {length:w, compare:outCoord[0].left - inCoord[0].left, pos:{left:outCoord[0].left - w - options.padding, top:outCoord[1].top - h}}, 2355 }; 2356 2357 if (options.over) 2358 { 2359 tests.over = {length:0, compare:outCoord[1].top - outCoord[0].top, pos:{ 2360 left:inCoord[0].left + (inCoord[1].left - inCoord[0].left - w)/2 + options.padding, 2361 top: inCoord[0].top + (inCoord[1].top - inCoord[0].top - h)/2 2362 }}; 2363 } 2364 2365 var enoughtSpace = []; 2366 2367 for (var i=0; i<options.prio.length; i++) 2368 { 2369 var test = tests[options.prio[i]]; 2370 if (test.compare>0 && test.length<=test.compare) 2371 { 2372 enoughtSpace.push({direction: options.prio[i], pos:test.pos}); 2373 // check inside 2374 if ((inCoord[0].left<= test.pos.left && (inCoord[1].left>= test.pos.left+w || (options.prio[i]=="topLeft")) && 2375 inCoord[0].top<= test.pos.top && inCoord[1].top>= test.pos.top+h) || !test.pos) 2376 { 2377 return {direction: options.prio[i], pos:test.pos}; 2378 } 2379 } 2380 } 2381 if (!options.prioOnly) 2382 { 2383 if (options.pos!='half') 2384 { 2385 options.pos = 'half'; 2386 return _.getPosition(item, outsideOf, insideOf, options); 2387 } 2388 else if (!options.over) 2389 { 2390 options.over = true; 2391 return _.getPosition(item, outsideOf, insideOf, options); 2392 } 2393 } 2394 // rien ne rentre, on applique la position prioritaire, tant pis 2395 var test = tests[options.prio[0]];; 2396 return {direction: options.prio[0], pos:test.pos}; 2397 } 2398 2399 2400 }, 2401 2402 unique: function(a) { return a.filter(function(e, i, ar) { return i==ar.indexOf(e); }); }, 2403 2404 2405 readLocation: function() { 2406 return { 2407 params: $.deparam(window.location.search), 2408 path: window.location.pathname, 2409 }; 2410 }, 2411 2412 pushState: function(state) { 2413 var url = _.readLocation(); 2414 window.history.pushState(state,"", url.path+"?"+$.param($.extend(url.params, state))); 2415 }, 2416 removeUrlParam: function(toremove) { 2417 var url = _.readLocation(); 2418 for (let rmv of toremove) 2419 { 2420 delete url.params[rmv]; 2421 } 2422 window.history.replaceState({}, "", url.path+"?"+$.param(url.params)); 2423 }, 2424 2425 url:function(q) { 2426 if (typeof q!=='string') { return {}; } 2427 q = q.trim().replace(/^(\?|#)/, ''); 2428 if (!q) { return {}; } 2429 return q.trim().split('&').reduce(function (k, v) { 2430 var parts = v.replace(/\+/g, ' ').split('='); 2431 var key = parts[0]; 2432 var val = parts[1]; 2433 2434 key = decodeURIComponent(key); 2435 2436 val = val === undefined? null : decodeURIComponent(val); 2437 if (!k.hasOwnProperty(key)) k[key] = val; 2438 else if (Array.isArray(k[key])) k[key].push(val); 2439 else k[key] = [k[key], val]; 2440 return k; 2441 }, {}); 2442 }, 2443 2444 isEqual: function (a, b, ignoreKeys) { 2445 var kA = Object.keys(a), 2446 kB = Object.keys(b); 2447 if (kA.length!=kB.length) return false; 2448 2449 var res = true; 2450 $.each(kA, function(ikey, key) { 2451 if (!ignoreKeys || ignoreKeys.indexOf(key)==-1) 2452 { 2453 var vA = a[key], 2454 vB = b[key], 2455 isObj = vA!=null && typeof vA==="object" && vB!=null && typeof vB==="object"? true : false; 2456 2457 if ((isObj && !_.isEqual(vA, vB)) || (!isObj && vA!==vB)) 2458 { 2459 res = false; 2460 return false; 2461 } 2462 } 2463 }); 2464 2465 return res; 2466 }, 2467 intval: function(n) { 2468 if (!n) return 0; 2469 if (n===true) return 1; 2470 if (Array.isArray(n)) return n.map(function(value) { return _.intval(value); }); 2471 if (typeof n !='string' && isNaN(n)) return 0; 2472 return parseInt(n); 2473 }, 2474 doubleval: function(n) { 2475 if (!n) return 0; 2476 if (typeof n !='string' && isNaN(n)) return 0; 2477 if (typeof n === 'number') return n; 2478 n = n.replace(/ /, '').replace(/,/, '.'); 2479 return parseFloat(n); 2480 }, 2481 clamp: function(val, min, max) { 2482 return Math.min(Math.max(val, min), max); 2483 }, 2484 mod:(n, m) => ((n % m) + m) % m, 2485 2486 funcList : { 2487 round:function(param){return parseFloat(param[0]).toFixed(param[1]);}, 2488 num:function(param) {return parseFloat(param[0]);}, 2489 number:function(param, dec) {if (typeof dec == "undefined") dec=2; return number_format(parseFloat(param[0]), dec, ",", " ");}, 2490 price:function(param){return number_format(parseFloat(param[0]), 2, ",", " ")+" "+(_.cfg.currency || '€');}, //.toFixed(2).replace(".", ",").replace(/\B(?=(\d{3})+(?!\d))/g, " ") 2491 date:function(param){return moment(param[0]).format(param[1]);}, 2492 lower:function(param){return param[0]? param[0].toLowerCase() : '';}, 2493 upper:function(param){return param[0]? param[0].toUpperCase() : '';}, 2494 ucfirst:function(param){if (param[0]) param[0].ucfirst(); return param[0]? param[0] : '';}, 2495 ucwords:function(param){if (param[0]) param[0].ucwords(); return param[0]? param[0] : '';}, 2496 repeat:function(param){ 2497 var str = ''; 2498 for(var ir=0; ir<param[1]; ir++) str+= param[0]; 2499 return str; 2500 }, 2501 substr:function(param){return param[0]? param[0].substring(param[1], param[2]) : '';}, 2502 calc:function(param){return eval(param[0]);}, 2503 'if':function(param, Datas){ 2504 if (param[0]) return param[1]; 2505 else return param[2]; 2506 }, 2507 }, 2508 price: function(p) {return this.funcList.price([p]); }, 2509 number: function(p, dec) {return this.funcList.number([p], dec); }, 2510 formatDate: function(value, format) { 2511 if (value) 2512 { 2513 if (!format) format = 'short'; 2514 var m = moment(value); 2515 if (format=='full') 2516 { 2517 day = 'dddd D MMMM'; 2518 } 2519 else day = "DD MMM"; 2520 if (m.format("YY")==moment().format("YY")) value = m.format(day); 2521 else value = m.format(day+" YYYY"); 2522 } 2523 return value; 2524 }, 2525 formatDateTime: function(value, format) { 2526 if (value) 2527 { 2528 if (!format) format = 'short'; 2529 var m = moment(value); 2530 if (format=='full') 2531 { 2532 day = 'dddd D MMMM'; 2533 } 2534 else 2535 { 2536 day = "D MMM"; 2537 } 2538 var m = moment(value, ["DD-MM-YYYY", "YYYY-MM-DD", "DD-MM-YYYY HH:mm", "YYYY-MM-DD HH:mm"]); 2539 if (m.format("YY")==moment().format("YY")) value = m.format(day); 2540 else value = m.format(day+" YYYY"); 2541 if (m.format("HH:mm")!="00:00") 2542 { 2543 value += " - "+m.format("H")+"h"; 2544 if (m.format("mm")!="00") value+= m.format("mm"); 2545 } 2546 } 2547 return value; 2548 }, 2549 formatTime: function(timestamp, format = "", hourPerDay = 24, style = "") { 2550 let seconds = timestamp % 60; 2551 timestamp = Math.floor(timestamp / 60); 2552 let minutes = timestamp % 60; 2553 if (seconds > 10) minutes++; 2554 let totalHours = Math.floor(timestamp / 60); 2555 let hours = format==="d|Hhm?|m:s" ? totalHours % hourPerDay : totalHours; 2556 let days = format==="d|Hhm?|m:s" ? Math.floor(totalHours / hourPerDay) : 0; 2557 2558 let pad = n => String(n).padStart(2, "0"); 2559 let plural = (n, w) => n + " " + w + (n > 1 ? "s" : ""); 2560 2561 switch (format) { 2562 case "Hhm?": return minutes ? `${hours}h${pad(minutes)}` : `${hours}h`; 2563 case "Hh": return `${hours}h`; 2564 case "h:m?": return minutes ? `${hours}:${pad(minutes)}` : `${hours}`; 2565 case "d|Hhm?|m:s": 2566 if (days && hours) return `${plural(days, "jour")} ${hours}h`; 2567 if (days) return plural(days, "jour"); 2568 if (hours) return minutes ? `${hours}h${pad(minutes)}` : `${hours}h`; 2569 return style === "full" 2570 ? `${plural(minutes, "minute")}` + (seconds ? pad(seconds) : "") 2571 : `${minutes}:${pad(seconds)}`; 2572 default: return `${hours}h${pad(minutes)}`; 2573 } 2574 }, 2575 formatFileSize: function(size, considerZeroAsMissing) { 2576 if(typeof considerZeroAsMissing === "undefined") considerZeroAsMissing = true; 2577 2578 if (size>900000000) size = Math.round(size/107374182)/10+" Go"; 2579 else if (size>900000) size = Math.round(size/104857)/10+" Mo"; 2580 else if (size>900) size = Math.round(size/102.4)/10+" Ko"; 2581 else if (size>0) size = size+" o"; 2582 else if (considerZeroAsMissing && (!size || size==-1)) size = "<notag class='error'>File is missing</notag>"; 2583 2584 return size; 2585 }, 2586 2587 checkHour: function(h) { 2588
2589 if (h.length==5) var h = h.substring(0,2)+":"+h.substring(3, 5); // hh:mm 2590 else if (h.length==4) 2591 { 2592 if (parseInt(h.substring(0,2)) && !parseInt(h[2])) h = h.substring(0,2)+":0"+parseInt(h[3]); // hh:m 2593 else h = h.substring(0,2)+":"+h.substring(2,4); // hhmm 2594 } 2595 else if (h.length<4 && h) 2596 { 2597 //if (h.length==2 && h.match(/^[0-9]{2}/)) h = parseInt(h); // hh: 2598 if (h.length==3 && !parseInt(h[1])) 2599 { 2600 h = "0"+parseInt(h[0])+":0"+parseInt(h[2]); // h:m 2601 } 2602 else if (h.length==3 && ((parseInt(h.substring(1))+"").length==2 || ((parseInt(h.substring(1))+"").length==1 && h[1]=="0" && parseInt(h[2])))) 2603 { // hmm 2604 h = "0"+parseInt(h.substring(0,1))+":"+parseInt(h.substring(1)); 2605 } 2606 else 2607 { // hh ou h 2608 h = ('00' + parseInt(h.substring(0,2))).slice(-2)+":00"; 2609 } 2610 } 2611 return h; 2612 }, 2613 2614 getHolidays: function(year) { 2615 if (!year) year = moment().format('YYYY'); 2616 2617 // jours fixes 2618 _.holidays = {}; 2619 _.holidays[year] = {}; 2620 _.holidays[year][year+'-01-01'] = true; // 1er janvier 2621 _.holidays[year][year+'-05-01'] = true; // Fête du travail 2622 _.holidays[year][year+'-05-08'] = true; // Victoire des alliés 2623 _.holidays[year][year+'-07-14'] = true; // Fête nationale 2624 _.holidays[year][year+'-08-15'] = true; // Assomption 2625 _.holidays[year][year+'-11-01'] = true; // Toussaint 2626 _.holidays[year][year+'-11-11'] = true; // Armistice 2627 _.holidays[year][year+'-12-25'] = true; // Noël 2628 2629 var easterDates = { 2630 2014:'04-20', 2015:'04-05', 2016:'03-27', 2017:'04-16', 2018:'04-01', 2019:'04-21', 2020:'04-12', 2021:'04-04', 2022:'04-17', 2023:'04-09', 2024:'03-31', 2025:'04-20', 2631 2026:'04-05', 2027:'03-28', 2028:'04-16', 2029:'04-01', 2030:'04-21', 2031:'04-13', 2032:'03-28', 2033:'04-17' 2632 }; 2633 var easter = moment(year+"-"+easterDates[year]); 2634 2635 _.holidays[year][moment(easter).add(1, 'day').format("YYYY-MM-DD")] = true; // pâques lui-même. 2636 _.holidays[year][moment(easter).add(39, 'day').format("YYYY-MM-DD")] = true; // Ascension 2637 _.holidays[year][moment(easter).add(50, 'day').format("YYYY-MM-DD")] = true; // lundi de pentecôte 2638 2639 }, 2640 2641 isHoliday: function(date) { 2642 var d = moment(date); 2643 var year = d.format('YYYY'); 2644 if (!_.holidays || !_.holidays[year]) _.getHolidays(year); 2645 return _.holidays[year][d.format('YYYY-MM-DD')]? true : false; 2646 }, 2647 2648 organizeGrid: function(items) { 2649 var itemCols = [[]], 2650 nbCols = 1, 2651 colW = 100, 2652 ic=0, 2653 ii=0, 2654 nbitem=0, 2655 freespace = true, 2656 sum=0; 2657 2658 $.each(items, function(it, item) { 2659 item.endMinutes = item.startMinutes+item.duration; 2660 2661 var len = item.duration; 2662 2663 sum+= len; 2664 2665 for(ic=0; ic<nbCols; ic++) 2666 { 2667 freespace = true; 2668 nbitem = itemCols[ic].length; 2669 for(ii=0; ii<nbitem; ii++) 2670 { 2671 item2 = itemCols[ic][ii]; 2672 if ((item2.startMinutes <= item.startMinutes && item2.endMinutes > item.startMinutes) || (item2.startMinutes >= item.startMinutes && item2.startMinutes < item.endMinutes)) 2673 { 2674 freespace = false; 2675 // Intersection, on ajoute une colonne ? ou on passe à la suivante. 2676 if (ic>=nbCols-1) 2677 { 2678 colW = Math.floor(100/(nbCols+1)); 2679 for (var icr=0; icr<nbCols; icr++) 2680 { 2681 var nbitemR = itemCols[icr].length; 2682 for (var iir=0; iir<nbitemR; iir++) 2683 { 2684 itemCols[icr][iir].width = colW-2; 2685 itemCols[icr][iir].left = icr*colW; 2686 } 2687 } 2688 itemCols[nbCols] = []; 2689 nbCols++; 2690 } 2691 break; 2692 } 2693 } 2694 if (freespace) break; 2695 } 2696 itemCols[ic][ii] = item; 2697 2698 item.width = colW-2; 2699 item.left = ic*colW; 2700 2701 }); 2702 2703 for (var ic=0; ic<nbCols; ic++) 2704 { 2705 nbitem = itemCols[ic].length; 2706 for(var ii=0; ii<nbitem; ii++) 2707 { 2708 var colSpan = 1; 2709 var item = itemCols[ic][ii]; 2710 for (var ic2=ic+1; ic2<nbCols; ic2++) 2711 { 2712 var nbitem2 = itemCols[ic2].length; 2713 freespace = true; 2714 for (var ii2=0; ii2<nbitem2; ii2++) 2715 { 2716 item2 = itemCols[ic2][ii2]; 2717 if ((item2.startMinutes <= item.startMinutes && item2.endMinutes > item.startMinutes) 2718 || (item2.startMinutes >= item.startMinutes && item2.startMinutes < item.endMinutes)) 2719 { 2720 freespace = false;
2721 break; 2722 } 2723 } 2724 if (freespace) colSpan++; 2725 else break; 2726 } 2727 if (colSpan>1) 2728 { 2729 item.width = colW*colSpan - 2 2730 item.left = ic*colW; 2731 } 2732 } 2733 } 2734 2735 return itemCols; 2736 }, 2737 2738 picAsCanvas: function(pic) { 2739 var canvas = _.ce('canvas'); 2740 canvas.width = pic.width; 2741 canvas.height = pic.height; 2742 canvas.getContext('2d').drawImage(pic, 0, 0, pic.width, pic.height); 2743 return canvas; 2744 }, 2745 2746 getPicPix: function(pic, x, y) { 2747 if (!x) x = 0; 2748 if (!y) y = 0; 2749 var canvas = _.picAsCanvas(pic); 2750 var pix = canvas.getContext('2d').getImageData(x, y, 1, 1).data; 2751 return pix; 2752 }, 2753 2754 addCacheDate: function(d, type) { 2755 if (d) 2756 { 2757 if (!_.cache.dates) _.cache.dates = []; 2758 var m = moment(d, ["DD-MM-YYYY", "YYYY-MM-DD", "DD-MM-YYYY HH:mm", "YYYY-MM-DD HH:mm"]); 2759 _.cache.dates.push({date:m.format('YYYY-MM-DD HH:mm'), type:type? parseInt(type) : 0}); 2760 } 2761 }, 2762 2763 getCacheDates: function(max, wHour) { 2764 var dates = []; 2765 if (!max) max = 8; 2766 if (_.cache.dates) 2767 { 2768 _.cache.dates.sort(function(a, b) { 2769 if (a.type==b.type) 2770 { 2771 if (a.date==b.date) return 0; 2772 return a.date<b.date? -1 : 1; 2773 } 2774 else return a.type<b.type? 1 : -1; 2775 }); 2776 var nbByDate = {}; 2777 $.each(_.cache.dates, function(i, d) { 2778 var date = moment(d.date).format(wHour? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD'); 2779 if (!nbByDate[date]) nbByDate[date] = {date:date, type:d.type? d.type : 0, nb:0}; 2780 nbByDate[date].nb++; 2781 }); 2782 $.each(nbByDate, function(date, d) { 2783 dates.push(d); 2784 }); 2785 dates.sort(function(a,b) { 2786 if (a.type==b.type) 2787 { 2788 if (a.type>0) 2789 { 2790 if (a.date==b.date) return 0; 2791 return a.date<b.date? -1 : 1; 2792 } 2793 else 2794 { 2795 if (a.nb==b.nb) return 0; 2796 return a.nb<b.nb? -1 : 1; 2797 } 2798 } 2799 else return a.type<b.type? 1 : -1; 2800 }); 2801 2802 dates = dates.slice(0, max); 2803 2804 dates.sort(function(a,b) { 2805 if (a.type==b.type) 2806 { 2807 if (a.date==b.date) return 0; 2808 return a.date<b.date? -1 : 1; 2809 } 2810 else return a.type<b.type? 1 : -1; 2811 }); 2812 2813 return dates; 2814 } 2815 return false; 2816 }, 2817 2818 2819 genId: function(nb) { 2820 var r='', pool='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', pnb = pool.length; 2821 for (var i=0; i<nb; i++) r+= pool.charAt(Math.floor(Math.random()*pnb)); 2822 return r; 2823 }, 2824 2825 actions: function(actions, datas, ev, context) { 2826 if (!Array.isArray(actions)) actions = [actions]; 2827 var acts = {actions:actions}; 2828 if (context) acts.actions.splice(0, 0, {context:context}); 2829 if (datas) acts.datas = datas; 2830 if (ev) acts.event = ev; 2831 $('<div/>').jAction(acts); 2832 }, 2833 2834 base64Encode: function (str) { 2835 return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, 2836 function toSolidBytes(match, p1) { 2837 return String.fromCharCode('0x' + p1); 2838 })); 2839 }, 2840 base64Decode: function (str) { 2841 return decodeURIComponent(atob(str).split('').map(function(c) { 2842 return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); 2843 }).join('')); 2844 }, 2845 2846 isElementFocusable(element) { 2847 if (element instanceof jQuery) { 2848 element = element.get(0); 2849 } 2850 2851 if(!element) return false; 2852 2853 if ($(element).is(":hidden") || $(element).is(":disabled") ) { 2854 return false; 2855 } 2856 2857 let tabIndex = parseInt($(element).attr("tabindex")); 2858 tabIndex = isNaN(tabIndex) ? -1 : tabIndex; 2859 return $(element).is( ":input, a[href], area[href], iframe" ) || tabIndex > -1; 2860 }, 2861 2862 getEntities: function () { 2863 var jobLoadEntities = Job.getUnique('loadEntities', function () { 2864 return new Job(function (job) { 2865 if (_.entities) { 2866 job.success(_.entities); 2867 } else { 2868 _.actions({ 2869 route: 'cfg/charter//get-entities', 2870 data: {}, 2871 then: function(data) { 2872 _.entities = data.entities; 2873 job.success(_.entities); 2874 } 2875 }); 2876 } 2877 }); 2878 }); 2879 2880 return jobLoadEntities.getPromise(); 2881 }, 2882 2883 getCachedList: function (code) { 2884 if (!code.includes('::')) code = 'std::'+code; 2885 if (!(code in this.cache.lists.values)) return undefined; 2886 return this.cache.lists.values[code]; 2887 }, 2888 2889 getCachedStdList: function (code) { 2890 return this.getCachedList('std::'+code); 2891 }, 2892 2893 getCachedSpecDataList: function (code) { 2894 return this.getCachedList('spec::'+code); 2895 }, 2896 2897 getList: function (query, defaultType, forceReload) { 2898 if (typeof defaultType == 'undefined') defaultType = 'std'; 2899 2900 if (!Array.isArray(query)) query = query.split(','); 2901 2902 if (query.length == 0) return Promise.resolve([]); 2903 2904 query = query.map((item) => { 2905 let outputKey = null; 2906 2907 if (typeof item == 'string') { 2908 let type = defaultType; 2909 let code = item; 2910 2911 outputKey = code; 2912 2913 if (code.includes('::')) { 2914 let parts = item.split('::', 2); 2915 type = parts[0]; 2916 code = parts[1]; 2917 } 2918 item = { 2919 code: code, 2920 type: type, 2921 }; 2922 } else { 2923 if (!('type' in item)) item.type = defaultType; 2924 outputKey = item.outputKey || item.code; 2925 } 2926 2927 if (!('outputKey' in item)) { 2928 item.outputKey = outputKey; 2929 } 2930 2931 if (!('cacheKey' in item)) { 2932 if ('params' in item) { 2933 item.cacheKey = this.uid(); 2934 item.noCache = true; 2935 } else { 2936 item.cacheKey = item.type+'::'+item.code; 2937 } 2938 } 2939 2940 return item; 2941 }); 2942 2943 let types = {};
2944 for (let item of query) { 2945 types[item.type] = true; 2946 } 2947 types = Object.keys(types); 2948 2949 let toLoadQuery = []; 2950 if (forceReload) { 2951 toLoadQuery = query; 2952 } else { 2953 for (let item of query) { 2954 if (item.noCache || !(item.cacheKey in this.cache.lists.loaders)) toLoadQuery.push(item); 2955 } 2956 } 2957 2958 if (toLoadQuery.length > 0) { 2959 let ajaxData = { 2960 query: toLoadQuery, 2961 }; 2962 let loader = new Promise((resolve, reject) => { 2963 _.actions({route: 'ajax/list//multi', dataFormat: 'json', data:ajaxData, then: (data) => { 2964 for (let item of Object.values(data)) { 2965 this.cache.lists.values[item.query.cacheKey] = item.result || undefined; 2966 } 2967 resolve(data); 2968 }}); 2969 }); 2970 for (let item of toLoadQuery) { 2971 this.cache.lists.loaders[item.cacheKey] = loader; 2972 } 2973 } 2974 2975 let promises = []; 2976 for (let item of query) { 2977 promises.push(this.cache.lists.loaders[item.cacheKey]); 2978 } 2979 return new Promise((resolve, reject) => { 2980 Promise.all(promises).then((outputs) => { 2981 let result = {}; 2982 2983 for (let output of outputs) { 2984 for (let item of Object.values(output)) { 2985 result[item.query.outputKey] = item.result; 2986 } 2987 } 2988 2989 if (query.length == 1) resolve(Object.values(result)[0]); 2990 else resolve(result); 2991 }); 2992 }); 2993 }, 2994 2995 getStdList: function (codes, forceReload) { 2996 return _.getList(codes, forceReload); 2997 }, 2998 2999 getSpecDataList: function (codes, forceReload) { 3000 if (!Array.isArray(codes)) codes = codes.split(','); 3001 codes = codes.map((code) => { 3002 if (!code.includes('::')) code = 'spec::'+code; 3003 return code; 3004 }); 3005 return _.getList(codes, forceReload); 3006 }, 3007 3008 getStyleSets: function () { 3009 var uniqueJob = Job.getUnique('loadStyleSets', function () { 3010 return new Job(function (job) { 3011 if (_.styleSets) { 3012 job.success(_.styleSets); 3013 } else { 3014 _.actions({
3015 route: 'cfg/charter//get-styleSets', 3016 data: {}, 3017 then: function(data) { 3018 _.styleSets = data.styleSets; 3019 job.success(_.styleSets); 3020 } 3021 }); 3022 } 3023 }); 3024 }); 3025 3026 return uniqueJob.getPromise(); 3027 }, 3028 3029 3030 getDetailedStyleSets: function (forceReload) { 3031 if (forceReload) { 3032 _.styleSetsDetailed = null; 3033 Job.clearUnique('loadDetailedStyleSets'); 3034 } 3035 3036 var uniqueJob = Job.getUnique('loadDetailedStyleSets', function () { 3037 return new Job(function (job) { 3038 if (_.styleSetsDetailed) { 3039 job.success(_.styleSetsDetailed); 3040 } else { 3041 _.actions({ 3042 route: 'cfg/style//getList', 3043 data: {}, 3044 then: function(data) { 3045 _.styleSetsDetailed = data.styleSets; 3046 job.success(_.styleSetsDetailed); 3047 } 3048 }); 3049 } 3050 }); 3051 }); 3052 3053 return uniqueJob.getPromise(); 3054 }, 3055 3056 resolveStyleSetValue: function (value, filters) { 3057 if (_.isStyleSetReferenceValue(value)) { 3058 return this.getStyleSetValue(value.__styleSetKey, filters); 3059 } 3060 3061 return Promise.resolve(value); 3062 }, 3063 3064 isStyleSetReferenceValue: function (value) { 3065 return null !== value && typeof value == 'object' && '__styleSetKey' in value; 3066 }, 3067 3068 getStyleSetReferenceValue: function (key) { 3069 return {__styleSetKey: key}; 3070 }, 3071 3072 getStyleSetValue: function (key, filters) { 3073 if (!filters) filters = {}; 3074 3075 return new Promise((resolve, reject) => { 3076 this.getDetailedStyleSets().then((styleSets) => { 3077 let sources = []; 3078 if (filters.styleSetNr) { 3079 sources.push(filters.styleSetNr); 3080 } else if (filters.entityNr) { 3081 for (let styleSetNr of (styleSets.entityDefault[filters.entityNr] || [])) { 3082 sources.push(styleSetNr); 3083 } 3084 } else { 3085 for (let styleSetNr of (styleSets.globalDefault || [])) { 3086 sources.push(styleSetNr); 3087 } 3088 } 3089 3090 for (let styleSetNr of sources) { 3091 let styleSetData = styleSets.data[styleSetNr]; 3092 if (key in styleSetData) { 3093 resolve($.extend({key: key}, styleSetData[key])); 3094 } 3095 } 3096 3097 resolve(null); 3098 }); 3099 }); 3100 }, 3101 3102 getCharterColors: function () { 3103 var jobLoadCharterColors = Job.getUnique('loadCharterColors', function () { 3104 return new Job(function (job) { 3105 if (_.charterColors) { 3106 job.success(_.charterColors); 3107 } else { 3108 _.actions({ 3109 route: 'cfg/charter//get-colors', 3110 data: {}, 3111 then: function(data) { 3112 _.charterColors = data.colors; 3113 job.success(_.charterColors); 3114 } 3115 }); 3116 } 3117 }); 3118 }); 3119 3120 return jobLoadCharterColors.getPromise(); 3121 }, 3122 3123 toHSL(rgb) { // copied from js/inc/color.js (for quick use without ressource) 3124 let r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; 3125 3126 let cmin = Math.min(r,g,b), cmax = Math.max(r,g,b), 3127 delta = cmax - cmin, 3128 h = 0, s = 0, l = 0; 3129 3130 if (delta == 0) h = 0; 3131 else if (cmax == r) h = ((g - b) / delta) % 6; 3132 else if (cmax == g) h = (b - r) / delta + 2; 3133 else h = (r - g) / delta + 4; 3134 3135 h = Math.round(h * 60); 3136 if (h < 0) h += 360; 3137 l = (cmax + cmin) / 2; 3138 s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); 3139 s = +Math.round(s * 100); 3140 l = +Math.round(l * 100); 3141 3142 return [h, s, l]; 3143 }, 3144 3145 hexToRGB(hex) { 3146 let result = /^#?(?:([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})|([a-f\d])([a-f\d])([a-f\d]))/i.exec(hex); 3147 return result ? { 3148 r: parseInt(result[1] || result[4].repeat(2), 16), 3149 g: parseInt(result[2] || result[5].repeat(2), 16), 3150 b: parseInt(result[3] || result[6].repeat(2), 16) 3151 } : {r: 255, g: 255, b: 255}; 3152 }, 3153 3154 getScript: function(url, callback) { 3155 window.dependencyLoader.loadScripts([url]).then(()=>{ 3156 if (callback) callback(); 3157 }); 3158 }, 3159 3160 addContacts: function (contacts){ 3161 if (contacts) 3162 { 3163 if (!_.cache.Contacts) _.cache.Contacts = {}; 3164 $.each (contacts, (nr, c)=>{ 3165 if (!_.cache.Contacts[c.nr]) _.cache.Contacts[c.nr] = c; 3166 }); 3167 } 3168 }, 3169 addUsers: function(users){ 3170 if (users) 3171 { 3172 $.each (users, (nr, u)=>{ 3173 if (!+u.nr && nr) u.nr = +nr; 3174 if (!_.Users[+u.nr]) 3175 { 3176 if (u.firstname) u.fn = u.firstname; 3177 if (u.lastname) u.ln = u.lastname; 3178 _.Users[+u.nr] = { 3179 nr: +u.nr, 3180 firstname:u.fn, 3181 lastname:u.ln, 3182 str: u.fn+' '+u.ln, 3183 name:u.fn+' '+u.ln, 3184 pic:u.pic, 3185 func:'', 3186 cpy:'', 3187 active:u.active, 3188 assignment:u.assignment || '' 3189 }; 3190 } 3191 }); 3192 } 3193 }, 3194 3195 getUsers: function(teamNr, callBack) { 3196 if ((!teamNr && !_.flags.usersAllLoaded) || (teamNr && (!_.cache.teams || !_.cache.teams[teamNr]))) 3197 { 3198 _.actions({route:'user/usr//get-list', data:{teamnr:teamNr}, then:function(data) { 3199 _.addUsers(data.users); 3200 if (!teamNr) _.flags.usersAllLoaded = true; 3201
3202 // A faire : compléter le système pour les teams 3203 3204 if (callBack) callBack(); 3205 }}); 3206 } 3207 else 3208 { 3209 if (callBack) callBack(); 3210 } 3211 3212 }, 3213 isElementInside: function (element, containers) { 3214 if (typeof containers != 'object' || !('length' in containers)) { 3215 containers = [containers]; 3216 } 3217 3218 if (element instanceof jQuery) element = element.get(0); 3219 if (!element || !(element instanceof HTMLElement)) return false; 3220 3221 for (let container of containers) { 3222 if (container instanceof jQuery) container = container.get(0); 3223 if (!container || !(container instanceof HTMLElement)) continue; 3224 3225 if (element == container || -1 != $.inArray(container, $(element).parents())) { 3226 return true; 3227 } 3228 } 3229 3230 return false; 3231 }, 3232 logEventAction: function (event, actionCode, data) { 3233 if ('originalEvent' in event) event = event.originalEvent; 3234 3235 if (!('__actions__' in event)) { 3236 event.__actions__ = []; 3237 } 3238 event.__actions__.push({code: actionCode || 'default', data: data || {}}); 3239 }, 3240 hasEventAction: function (event, actionCode) { 3241 if ('originalEvent' in event) event = event.originalEvent; 3242 3243 if (!('__actions__' in event)) return false; 3244 3245 if (typeof actionCode == 'undefined') return event.__actions__.length > 0; 3246 3247 return event.__actions__.findIndex((e) => e.code == actionCode) >= 0; 3248 }, 3249 getEventActions: function (event, actionCode) { 3250 if ('originalEvent' in event) event = event.originalEvent; 3251 3252 if (!('__actions__' in event)) return []; 3253 3254 if (typeof actionCode == 'undefined') return event.__actions__; 3255 3256 return event.__actions__.filter((e) => e.code == actionCode); 3257 }, 3258 clearDetachedWYEditors: function () { 3259 if (typeof tinymce != 'object' || !('editors' in tinymce)) return; 3260 3261 for (let editor of Object.values(tinymce.editors)) { 3262 if (!editor.editorContainer || $(editor.editorContainer).closest('body').length > 0) continue; 3263 3264 tinymce.execCommand('mceRemoveEditor', true, editor.id); 3265 } 3266 }, 3267 debounce: function (fn, delay) { 3268 let timeoutId; 3269 return function (...args) { 3270 clearTimeout(timeoutId); 3271 timeoutId = setTimeout(() => { 3272 fn(...args); 3273 }, delay); 3274 }; 3275 }, 3276 getDragService: function () { 3277 if (null === _.dragService) { 3278 _.dragService = new Promise((resolve, reject) => { 3279 window.dependencyLoader.loadModularScripts([{load: '/admin/inc/js/mod/drag/DragService.js'}]).then((modules) => { 3280 resolve(new modules.DragService()); 3281 }); 3282 }); 3283 } 3284 3285 return _.dragService; 3286 }, 3287 3288 // petite light factorisation, en attendant d'avoir un proper service dédié aux panels en général 3289 panel: { 3290 toggle: () =>{ 3291 var $panel = $('.SidePanel'); 3292 if ($panel.attr('state')=='close') _.panel.open(); 3293 else _.panel.close(); 3294 }, 3295 open: () => { 3296 var $panel = $('.SidePanel'); 3297 $panel.attr('state', 'open'); 3298 3299 }, 3300 3301 close: () => { 3302 var $panel = $('.SidePanel'); 3303 $panel.attr('state', 'close'); 3304 } 3305 }, 3306 3307 closeToolPanels: function() { 3308 setTimeout(function() { 3309 $('.contentPanel.tool-panel').each(function() {this.close();}); 3310 }, 100); 3311 }, 3312 3313 3314 appModules: function () { 3315 if (!('loadAppModulesPromise' in _)) { 3316 _.loadAppModulesPromise = new Promise((resolve, reject) => { 3317 if (window.appModules) resolve(window.appModules); 3318 else { 3319 import('/admin/inc/js/mod/app/AppModuleService.js').then((importedObject) => { 3320 if (!'AppModuleService' in importedObject) { 3321 throw new Error('AppModuleService was not found after import'); 3322 } else { 3323 window.appModules = new importedObject['AppModuleService'](); 3324 resolve(window.appModules); 3325 } 3326 }); 3327 } 3328 }); 3329 } 3330 return _.loadAppModulesPromise; 3331 }, 3332 loadModule: function (element, options, data, moduleId, forceReload) { 3333 if (typeof element=="string") element = $(element)[0]; 3334 return new Promise((resolve, reject) => {
3335 _.appModules().then((appModules) => { 3336 appModules.load(element, options, data, moduleId, forceReload) 3337 .then((module) => resolve(module)) 3338 .catch((error) => reject(error)); 3339 }); 3340 }); 3341 3342 }, 3343 calculator: function () { 3344 return new Promise((resolve, reject) => { 3345 window.dependencyLoader.loadScripts(['/admin/inc/js/math.js']).then(() => { 3346 let calculator = { 3347 stdFunctions: { 3348 price: function (result) { 3349 return _.price(result); 3350 }, 3351 percent: function (result) { 3352 return result+' %'; 3353 }, 3354 formatTime: function (result) { 3355 return _.formatTime(result); 3356 }, 3357 }, 3358 customFunctions: {}, 3359 math: math, 3360 }; 3361 calculator.addFunction = (name, callback) => { 3362 if (typeof callback != 'function') throw new Error("callback must be a function"); 3363 calculator.customFunctions[name] = callback; 3364 }; 3365 calculator.sanitizeExpression = (expression) => { 3366 let sanitizedExpression = expression.toString().replaceAll(/(?:<>|&&|\|\|)/g, (match) => { 3367 switch (match) { 3368 case '<>': return '!='; 3369 case '&&': return 'and'; 3370 case '||': return 'or'; 3371 } 3372 return match; 3373 }); 3374 sanitizedExpression = sanitizedExpression.replaceAll(/((?:[a-z_][a-z0-9_]*)(?:(?:\.|->)(?:[a-z_][a-z0-9_]*))*)/g, (match) => { 3375 return calculator.sanitizeParameterName(match); 3376 }); 3377 return sanitizedExpression; 3378 }; 3379 calculator.sanitizeParameters = (parameters) => { 3380 let sanitizedParameters = {}; 3381 for (let [key, value] of Object.entries(parameters)) { 3382 sanitizedParameters[calculator.sanitizeParameterName(key)] = value; 3383 } 3384 return sanitizedParameters; 3385 }; 3386 calculator.unsanitizeParameters = (parameters) => { 3387 let unsanitizedParameters = {}; 3388 for (let [key, value] of Object.entries(parameters)) { 3389 unsanitizedParameters[calculator.unsanitizeParameterName(key)] = value; 3390 } 3391 return unsanitizedParameters; 3392 }; 3393 calculator.sanitizeParameterName = (name) => { 3394 return name.replaceAll(/\./g, '___DOT___').replaceAll(/->/g, '___ARW___'); 3395 }; 3396 calculator.unsanitizeParameterName = (name) => { 3397 return name.replaceAll(/___DOT___/g, '.').replaceAll(/___ARW___/g, '->'); 3398 }; 3399 calculator.calculate = (expression, parameters, expose) => { 3400 if (typeof parameters == 'undefined') parameters = {}; 3401 3402 if (typeof expression != 'object' || !('length' in expression)) { 3403 expression = [expression]; 3404 } 3405 3406 let outputData = {}; 3407 let previousOutput = '__previous__' in parameters ? parameters.__previous__ : undefined; 3408 let extendedParameters = $.extend({}, parameters, outputData, {__previous__: previousOutput || 0}); 3409 extendedParameters = calculator.sanitizeParameters(extendedParameters); 3410 for (let expr of Object.values(expression)) { 3411 let parserExpression = typeof expr == 'string' ? expr : expr.expr; 3412 3413 let conditionResult = true; 3414 if (typeof expr == 'object' && 'cond' in expr) { 3415 conditionResult = !!calculator.calculate(expr.cond, calculator.unsanitizeParameters(extendedParameters)); 3416 if (!conditionResult && 'else' in expr) { 3417 parserExpression = expr.else; 3418 conditionResult = true; 3419 } 3420 } 3421 3422 if (conditionResult) { 3423 let allFunctions = $.extend({}, calculator.stdFunctions, calculator.customFunctions); 3424 let allSymbols = $.extend({}, extendedParameters, allFunctions); 3425 3426 let whileResult = true; 3427 if (typeof expr == 'object' && 'while' in expr) { 3428 whileResult = !!calculator.calculate(expr.while, calculator.unsanitizeParameters(extendedParameters)); 3429 } 3430 3431 const toEvaluateExpression = calculator.sanitizeExpression(parserExpression); 3432 const node = math.parse(toEvaluateExpression); 3433 let args = {}; 3434 node.traverse((leaf, path, parent) => { 3435 if (leaf.type == 'SymbolNode') { 3436 if (!parent || parent.type != 'FunctionNode' || parent.name != leaf.name) { 3437 args[leaf.name] = true; 3438 } 3439 } 3440 }); 3441 let hasMissingArguments = false; 3442 for (let arg of Object.keys(args)) { 3443 if (!(arg in allSymbols) || typeof allSymbols[arg] == 'undefined') { 3444 hasMissingArguments = true; 3445 break; 3446 } 3447 } 3448 3449 let iterations = 0; 3450 while (whileResult && iterations < 100) { 3451 iterations++; 3452 3453 const parser = math.parser(); 3454 for (let [k, v] of Object.entries(extendedParameters)) { 3455 parser.set(k, v); 3456 } 3457 for (let [name, callback] of Object.entries(allFunctions)) { 3458 parser.set(name, callback); 3459 } 3460 3461 try { 3462 let result = hasMissingArguments ? undefined : parser.evaluate(toEvaluateExpression); 3463 if (typeof result == 'string' && parseFloat(result).toString() == result) result = parseFloat(result); 3464 3465 previousOutput = result; 3466 if (typeof expr == 'object' && 'name' in expr) { 3467 outputData[calculator.sanitizeParameterName(expr.name)] = result; 3468 }
3469 if (typeof expr == 'object' && 'push' in expr) { 3470 if (!(expr.push in outputData)) { 3471 outputData[calculator.sanitizeParameterName(expr.push)] = []; 3472 } 3473 3474 outputData[calculator.sanitizeParameterName(expr.push)].push(result); 3475 } 3476 } catch (e) { 3477 console.debug('Calculation failed', { 3478 expression: parserExpression, 3479 data: calculator.unsanitizeParameters(extendedParameters), 3480 }); 3481 throw e; 3482 } 3483 3484 extendedParameters = $.extend({}, parameters, outputData, {__previous__: previousOutput}); 3485 if (typeof expr == 'object' && 'while' in expr) { 3486 whileResult = !!calculator.calculate(expr.while, calculator.unsanitizeParameters(extendedParameters)); 3487 } else { 3488 whileResult = false; 3489 } 3490 } 3491 } 3492 } 3493 3494 if (typeof expose == 'undefined') expose = true; 3495 3496 if (!expose || Object.keys(outputData).length == 0) return previousOutput; 3497 3498 if (typeof expose == 'object') { 3499 let exposedData = {}; 3500 for (let key of Object.values(expose)) { 3501 if (!(key in outputData)) continue; 3502 3503 exposedData[key] = outputData[key]; 3504 } 3505 outputData = exposedData; 3506 } 3507 3508 outputData['__output__'] = previousOutput; 3509 return calculator.unsanitizeParameters(outputData); 3510 } 3511 resolve(calculator); 3512 }); 3513 }); 3514 }, 3515 calculate: function (expression, parameters, expose) { 3516 return new Promise((resolve, reject) => { 3517 _.calculator().then((calculator) => { 3518 resolve(calculator.calculate(expression, parameters, expose)); 3519 }); 3520 }); 3521 }, 3522}; 3523 3524 3525// plugin jquery pour prendre une query string et en faire un objet. 3526 3527 3528(function ($) { 3529 var re = /([^&=]+)=?([^&]*)/g; 3530 var decode = function (str) { 3531 return decodeURIComponent(str.replace(/\+/g, ' ')); 3532 }; 3533 3534 $.deparam = function( params, coerce ) { 3535 var obj = {}, 3536 coerce_types = { 'true': !0, 'false': !1, 'null': null }; 3537 3538 params = params.trim().replace(/^(\?|#)/, ''); 3539 3540 // Iterate over all name=value pairs. 3541 $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){ 3542 var param = v.split( '=' ), 3543 key = decode( param[0] ), 3544 val, 3545 cur = obj, 3546 i = 0, 3547 3548 // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it 3549 // into its component parts. 3550 keys = key.split( '][' ), 3551 keys_last = keys.length - 1; 3552 3553 // If the first keys part contains [ and the last ends with ], then [] 3554 // are correctly balanced. 3555 if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) { 3556 // Remove the trailing ] from the last keys part. 3557 keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' ); 3558 3559 // Split first keys part into two parts on the [ and add them back onto 3560 // the beginning of the keys array. 3561 keys = keys.shift().split('[').concat( keys ); 3562 3563 keys_last = keys.length - 1; 3564 } else { 3565 // Basic 'foo' style key. 3566 keys_last = 0; 3567 } 3568 3569 // Are we dealing with a name=value pair, or just a name? 3570 if ( param.length === 2 ) { 3571 val = decode( param[1] ); 3572 3573 // Coerce values. 3574 if ( coerce ) { 3575 val = val && !isNaN(val) ? +val // number 3576 : val === 'undefined' ? undefined // undefined 3577 : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null 3578 : val; // string 3579 } 3580 3581 if ( keys_last ) { 3582 // Complex key, build deep object structure based on a few rules: 3583 // * The 'cur' pointer starts at the object top-level. 3584 // * [] = array push (n is set to array length), [n] = array if n is 3585 // numeric, otherwise object. 3586 // * If at the last keys part, set the value. 3587 // * For each keys part, if the current level is undefined create an 3588 // object or array based on the type of the next keys part. 3589 // * Move the 'cur' pointer to the next level. 3590 // * Rinse & repeat. 3591 for ( ; i <= keys_last; i++ ) { 3592 key = keys[i] === '' ? cur.length : keys[i]; 3593 cur = cur[key] = i < keys_last 3594 ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] ) 3595 : val; 3596 } 3597 3598 } else { 3599 // Simple key, even simpler rules, since only scalars and shallow 3600 // arrays are allowed. 3601 3602 if ( Array.isArray( obj[key] ) ) { 3603 // val is already an array, so push on the next value. 3604 obj[key].push( val ); 3605 3606 } else if ( obj[key] !== undefined ) { 3607 // val isn't an array, but since a second value has been specified, 3608 // convert val into an array.
3609 obj[key] = [ obj[key], val ]; 3610 3611 } else { 3612 // val is a scalar. 3613 obj[key] = val; 3614 } 3615 } 3616 3617 } else if ( key ) { 3618 // No value was defined, so set something meaningful. 3619 obj[key] = coerce 3620 ? undefined 3621 : ''; 3622 } 3623 }); 3624 3625 return obj; 3626 }; 3627 3628 $.deserialize = function (query) { 3629 function subDeserialize(params, key, value) { 3630 key = key + ''; 3631 if (key.indexOf('.') !== -1) 3632 { 3633 var list = key.split('.'); 3634 var new_key = key.split(/\.(.+)?/)[1]; 3635 if (!params[list[0]]) params[list[0]] = {}; 3636 if (new_key !== '') subDeserialize(params[list[0]], new_key, value); 3637 } 3638 else if (key.indexOf('[') !== -1) 3639 { 3640 var list = key.split('['); 3641 key = list[0]; 3642 var list = list[1].split(']'); 3643 var index = list[0] 3644 if (index == '') 3645 { 3646 if (!params) params = {}; 3647 if (!params[key] || !Array.isArray(params[key])) params[key] = []; 3648 params[key].push(value); 3649 } 3650 else 3651 { 3652 if (!params) params = {}; 3653 if (!params[key]) params[key] = {}; 3654 params[key][index] = value; 3655 } 3656 } 3657 else 3658 { 3659 if (!params) params = {}; 3660 params[key] = value; 3661 } 3662 } 3663 query = query + ''; 3664 if (query === '') query = window.location + ''; 3665 var params = {}, e; 3666 if (query) { 3667 if (query.indexOf('#') !== -1) { 3668 query = query.substr(0, query.indexOf('#')); 3669 } 3670 3671 if (query.indexOf('?') !== -1) { 3672 query = query.substr(query.indexOf('?') + 1, query.length); 3673 } else return {}; 3674 if (query == '') return {}; 3675 while (e = re.exec(query)) { 3676 var key = decode(e[1]); 3677 var value = decode(e[2]); 3678 subDeserialize(params, key, value); 3679 } 3680 } 3681 return params; 3682 }; 3683})(jQuery); 3684 3685 3686 3687// Permet un $(target).on('destroyed', handler); 3688// le "remove" du special event est le fait qu'un event soit retiré de l'élément... ce qui est 3689// aussi appelé à la suppression de l'élément qui nettoie tous les events de l'élément supprimé. 3690$.event.special.destroyed = { 3691 remove: function(o) { 3692 if (o.handler) o.handler() 3693 } 3694}; 3695 3696 3697$.fn.getInfos = function () { 3698 this.each(function() { 3699 var Obj = this; 3700 var infos = null; 3701 var infosTag = $('>.JSinfos', Obj); 3702 if (infosTag.length == 0 || !infosTag.html()) { 3703 infosTag = $(Obj).next('.JSinfos'); 3704 } 3705 if (infosTag.length == 0 || !infosTag.html()) { 3706 infos = $(Obj).attr('jsinfos'); 3707 if (infos) $(Obj).removeAttr('jsinfos'); 3708 } else { 3709 infos = infosTag.html(); 3710 if (infosTag.hasClass('base64')) { 3711 infos = _.base64Decode(infos); 3712 } 3713 infosTag.remove(); 3714 } 3715 if (infos) infos = eval("("+infos+")"); 3716 else infos = {}; 3717 Obj.infos = infos; 3718 }); 3719}; 3720 3721$.fn.initComponent = function (options) { 3722 this.each(function() { 3723 var Obj = this; 3724 $(Obj).addClass('cmpDone'); 3725 $(Obj).getInfos(); 3726 Obj.infos = $.extend(true, options, Obj.infos); 3727 if (!Obj.infos.data) Obj.infos.data = {}; 3728 if (!Obj.infos.config) Obj.infos.config = {}; 3729 Obj.infos.cmp = $(Obj).attr('cmp'); 3730 }); 3731}; 3732 3733$.fn.caret = function (begin, end) { 3734 if (this.length == 0) return; 3735 var target = this[0]; 3736 var isContentEditable = target.contentEditable === 'true'; 3737 if (typeof begin == 'number') 3738 { 3739 end = (typeof end == 'number') ? end : begin; 3740 return this.each(function () { 3741 if (this.setSelectionRange) this.setSelectionRange(begin, end); 3742 else if (this.createTextRange) 3743 { 3744 var range = this.createTextRange(); 3745 range.collapse(true); 3746 range.moveEnd('character', end); 3747 range.moveStart('character', begin); 3748 try { range.select(); } catch (ex) { } 3749 } 3750 }); 3751 } 3752 else 3753 { 3754 if (target.setSelectionRange) 3755 { 3756 begin = target.selectionStart; 3757 end = target.selectionEnd; 3758 } 3759 else if (target.ownerDocument.selection && target.ownerDocument.selection.createRange) 3760 { 3761 var range = target.ownerDocument.selection.createRange(); 3762 begin = 0 - range.duplicate().moveStart('character', -100000); 3763 end = begin + range.text.length; 3764 } 3765 return { begin: begin, end: end }; 3766 } 3767}; 3768 3769 3770$.fn.imagesLoaded = function(callback){ 3771 var elems = this.filter('img'), 3772 len = elems.length, 3773 blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; 3774 3775 elems.bind('load.imgloaded',function(){ 3776 if (--len <= 0 && this.src !== blank){ 3777 elems.unbind('load.imgloaded'); 3778 callback.call(elems,this); 3779 } 3780 }).each(function(){ 3781 // cached images don't fire load sometimes, so we reset src. 3782 if (this.complete || this.complete === undefined){ 3783 var src = this.src; 3784 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 3785 // data uri bypasses webkit log warning (thx doug jones) 3786 this.src = blank; 3787 this.src = src; 3788 } 3789 }); 3790 3791 return this; 3792}; 3793 3794 3795 3796function openSidePanel(panel) 3797{ 3798 var $aside = $("aside."+panel); 3799 $aside.addClass('on floating'); 3800 if (!$aside[0].close) $aside[0].close = function() {$aside.removeClass('on floating');}; 3801 return $aside; 3802} 3803 3804 3805function number_format(number, decimals, dec_point, thousands_sep) { 3806 var n = !isFinite(+number) ? 0 : +number, 3807 prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), 3808 sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, 3809 dec = (typeof dec_point === 'undefined') ? '.' : dec_point, 3810 toFixedFix = function (n, prec) { 3811 var k = Math.pow(10, prec); 3812 return Math.round(n * k) / k; 3813 }, 3814 s = (prec ? toFixedFix(n, prec) : Math.round(n)).toString().split('.'); 3815if (s[0].length >
3815 3) s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); 3816if ((s[1] || '').length < prec) 3817 { 3818 s[1] = s[1] || ''; 3819 s[1] += new Array(prec - s[1].length + 1).join('0'); 3820 } 3821 return s.join(dec); 3822} 3823 3824function sortObject(obj, num) { // numéric or string 3825 var arr = []; 3826 for (var prop in obj) { 3827 if (obj.hasOwnProperty(prop)) { 3828 arr.push({k: prop, v: obj[prop]}); 3829 } 3830 } 3831 if (num) arr.sort(function(a, b) { return a.v - b.v; }); 3832 else arr.sort(function(a, b) { 3833 if (typeof a.v!="object") { var sa = a.v, sb=b.v;} 3834 else { var sa = a.v[0], sb=b.v[0];} 3835 return sa.toLowerCase().localeCompare(sb.toLowerCase()); 3836 }); 3837 return arr; 3838} 3839 3840String.prototype.replaceAt = function(i, c) { 3841 return this.substr(0, i) + c + this.substr(i + c.length); 3842} 3843String.prototype.ucfirst = function() { 3844 return this.charAt(0).toUpperCase() + this.slice(1); 3845} 3846String.prototype.ucwords = function() { 3847 var w = this.split(" "); 3848 for(var i=0; i<w.length; i++) 3849 { 3850 w[i] = w[i].toLowerCase(); 3851 w[i] = w[i].ucfirst(); 3852 } 3853 return w.join(" "); 3854} 3855String.prototype.plurial = function(nb) { 3856 if (nb<=1) return this; 3857 var W = this.split(" "); 3858 for(var i=0; i<W.length; i++) 3859 { 3860 var w = W[i].toLowerCase(); 3861 if (w.length>=3) 3862 { 3863 if (!['s', 'x', 'z'].includes(w.slice(-1))) 3864 { 3865 if (['bal', 'cal', 'carnaval', 'caracal', 'choral', 'étal', 'récital ', 'val', 'chacal', 'festival', 'régal', 'pal', 'pneu', 'bleu', 'émeu'].includes(w)) W[i]+= 's'; 3866 else if (["bijou", 'caillou', 'joujou', 'hibou'].includes(w) || ['au', 'eu'].includes(w.slice(-2))) W[i]+= "x"; 3867 else if (w.slice(-2)=="al") W[i] = W.slice(0, -2)+"aux"; 3868 else if (['bail', 'corail', 'émail', 'soupirail', 'travail', 'vantail', 'vitrail'].includes(w)) W[i] = W.slice(0, -2)+"ux"; 3869 else W[i]+= "s"; 3870 } 3871 } 3872 } 3873 return W.join(" "); 3874} 3875String.prototype.strReplace = function(needle, replace) { 3876 needle = needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); 3877 var r = new RegExp(needle, 'g'); 3878 return this.replaceAll(r, replace); 3879} 3880 3881function timeDiff(datas, jAct) 3882{ 3883 var $f = $(jAct).closest('fieldset, form'), 3884 s = $("[name='startT']", $f).val(), 3885 e = $("[name='endT']", $f).val(); 3886 3887 s = _.checkHour(s); 3888 e = _.checkHour(e); 3889 3890 var d = moment("2000-01-01 "+e).diff("2000-01-01 "+s, 'minutes'); 3891 $('.time-diff', $f).removeClass('neg over'); 3892 if (d<0) $('.time-diff', $f).addClass('neg'); 3893 if (Math.abs(d)<60) 3894 { 3895 $('.time-diff', $f).html(d+"' "); 3896 } 3897 else 3898 { 3899 var m = d%60, 3900 h = Math.floor(d/60); 3901 $('.time-diff', $f).html(h+"h"+m+" "); 3902 if (h>4) $('.time-diff', $f).addClass('over'); 3903 } 3904} 3905 3906function extractFormData(elements) { 3907 if(!(elements instanceof jQuery)) { 3908 elements = $(elements); 3909 } 3910 3911 let data = {}; 3912 elements.each((index, element) => { 3913 let form = $(element).closest("form"); 3914 if(form.length==0) { 3915 let clone = $("<div>").append($(element).clone()); 3916 clone.find('input, select, textarea').each(function(i) { 3917 let sourceElement = $(element).find('input, select, textarea').eq(i); 3918 $(this).val(sourceElement.val()); 3919 if(sourceElement.is(':checked')) { 3920 $(this).prop('checked', true); 3921 } 3922 }); 3923 form = $("<form>").append(clone); 3924 } 3925 3926 let rawDatas = form.serializeArray(); 3927 for(let rawData of rawDatas) { 3928 let key = rawData["name"]; 3929 let value = rawData["value"]; 3930 if(data.hasOwnProperty(key) && !Array.isArray(data[key])) { 3931 data[key] = [data[key]]; 3932 } 3933 if(Array.isArray(data[key])) { 3934 data[key].push(value); 3935 } else { 3936 data[key] = value; 3937 } 3938 } 3939 }); 3940 return data; 3941}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.