1 2/* global udem_www, localforage, AOS, _ */ 3 4// Global namespace, for utilies and shared state. 5if (typeof window.udem_www === 'undefined') { 6 window.udem_www = {}; 7} 8 9// TODO harden or remove underscore/lowdash dependency 10if (typeof _ === 'undefined') { 11 console.log('ERROR! Missing "Underscore/lowdash" library.'); 12} 13udem_www.debounce = _.debounce; 14udem_www.groupBy = _.groupBy; 15udem_www.find = _.find; 16 17// Fixes scrolling position when navigating to content anchor '#c[0-9]+'. Default delay is 160ms. 18udem_www.scrollToAnchoredContent = function (delay) { 19 'use strict'; 20 var $ = jQuery, 21 hash = window.location.href.match(/(#c[0-9]+)|(#[-a-z0-9]+)$/), 22 $content; 23 24 delay = typeof delay === 'number' ? delay : 160; 25 26 if (hash && hash.length) { 27 hash = hash[0]; 28 $content = $(hash).first(); 29 30 // Simple anchors don't report a precise position (in webkit at least) 31 if (hash[2] && $content.is('a[id]')) { 32 // $content = $content.parents('[id^="c"]'); 33 $content = $content.parents(':visible'); 34 } 35 36 // console.log('scrollToAnchoredContent hash / $content: ', hash, $content); 37 if ($content.length) { 38 setTimeout(function () { udem_www.scrollToElement($content, $('#udemwww-main-toolbar').height()); }, delay); 39 } 40 } 41}; 42 43// Scrolls the viewport to an element top position, minus the given offset. 44udem_www.scrollToElement = function (element, offset) { 45 'use strict'; 46 var $ = jQuery, 47 $element = $(element), 48 element_offset; 49 50 if ($element.length) { 51 element_offset = $element.offset(); 52 if (typeof offset === 'undefined') { 53 offset = 0; 54 } 55 // console.log('element_offset.top, offset: ', element_offset.top, offset); 56 if (element_offset.top > offset) { 57 $('html').scrollTop(element_offset.top - offset); // IE 58 $('body').scrollTop(element_offset.top - offset); // Gecko/Webkit/... 59 } 60 } 61}; 62 63/** 64 * Return if the search window is shown.
65 * @returns {Boolean} 66 */ 67udem_www.search_is_shown = function () { 68 return $('body').hasClass('udem-show-search-modal'); 69}; 70 71/** 72 * Check if the browser use an inner scrollbar (Ex Edge and mobile devices, not Chrome or Firefox). 73 * @returns {Boolean} 74 */ 75udem_www.use_inner_scrollbar = (function () { 76 let use_inner_scrollbar_cached; 77 return function () { 78 if (typeof use_inner_scrollbar_cached === 'undefined') { 79 var $body = $('body'), 80 originalStyle = $body.attr('style'), 81 scrollWidthWithScroll, 82 scrollWidthWithoutScroll; 83 $body.css('overflow', 'scroll'); 84 scrollWidthWithScroll = window.innerWidth > document.documentElement.clientWidth; 85 $body.css('overflow', 'hidden'); 86 scrollWidthWithoutScroll = window.innerWidth > document.documentElement.clientWidth; 87 if (typeof originalStyle !== 'undefined') { 88 $body.attr('style', originalStyle); 89 } else { 90 $body.removeAttr('style'); 91 } 92 use_inner_scrollbar_cached = scrollWidthWithScroll === scrollWidthWithoutScroll; 93 } 94 return use_inner_scrollbar_cached; 95 }; 96}()); 97 98/** 99 * Get the scrollbar width 100 * @returns {Number} 101 */ 102udem_www.get_scrollbar_width = (function () { 103 let get_scrollbar_width_cached; 104 return function () { 105 if (typeof get_scrollbar_width_cached === 'undefined') { 106 var width = 100, 107 $outer = $('<div>').css({ visibility: 'hidden', width: width, overflow: 'scroll' }).appendTo('body'), 108 widthWithScroll = $('<div>').css({ width: '100%' }).appendTo($outer).outerWidth(); 109 $outer.remove(); 110 get_scrollbar_width_cached = width - widthWithScroll; 111 } 112 return get_scrollbar_width_cached; 113 }; 114}()); 115 116/** 117 * Position the main toolbar based on scrollbar. 118 * Used to keep in place the main toolbar's buttons, even if there a scrollbar or not on the body. 119 */ 120udem_www.position_main_toolbar = function () { 121 if (!udem_www.use_inner_scrollbar()) { 122 var hasScrollbar = window.innerWidth > document.documentElement.clientWidth, 123 width = udem_www.get_scrollbar_width(), 124 $connectionMenu = $('#udem-menu-connexion'); 125 $('#udemwww-main-toolbar-container').css('right', hasScrollbar ? 0 : width); 126 $('#udemwww-main-toolbar > div > h1 > a').css('transform', hasScrollbar ? 'none' : 'translateX(-' + (width / 2) + 'px)'); 127 if ($connectionMenu.css('position') === 'absolute') { 128 $connectionMenu.css('transform', hasScrollbar ? 'none' : 'translateX(-' + (width / 2) + 'px)'); 129 } 130 } 131}; 132 133/* 134 * The body will have the class "udem-urgence-active" if the urgence plugin triggered the 135 * onUdemUrgence custom event 136 */ 137window.addEventListener('onUdemUrgence', function (e) { 138 var bannerSizeClass = 'udem-urgence-active-small'; 139 if ($('#udem-urgence-bcrp').hasClass('large')) { 140 bannerSizeClass = 'udem-urgence-active-large'; 141 } 142 $('body').addClass('udem-urgence-active ' + bannerSizeClass); 143}, false); 144 145// The body will have the class "udemwww-scrolled" if the viewport is vertically scrolled by more than 16px from the top. 146// Depending on the browser, the value may be updated during the scroll, or at the end of the scroll. 147// The Urgence banner will also be reduced 148udem_www.update_body_scrolled = function () { 149 'use strict'; 150 var scrollY = (typeof window.pageYOffset === 'undefined') 151 ? (document.documentElement || document.body.parentNode || document.body).scrollTop 152 : window.pageYOffset; 153 jQuery('body').toggleClass('udemwww-scrolled', scrollY >= 16); 154 155 if ($('body').hasClass('udem-urgence-active') || $('body').hasClass('udem-urgence-muted')) { 156 if (scrollY >= 1) { 157 $('body').removeClass('udem-urgence-active'); 158 $('#udem-urgence-bcrp').hide(); 159 $('body').addClass('udem-urgence-muted'); 160 } else { 161 $('body').removeClass('udem-urgence-muted'); 162 $('#udem-urgence-bcrp').show(); 163 $('body').addClass('udem-urgence-active'); 164 } 165 } 166}; 167 168jQuery(function ($) { 169 'use strict'; 170 171 // Init update_body_scrolled 172 $(window).on('scroll', udem_www.debounce(udem_www.update_body_scrolled, 16)); 173 setTimeout(udem_www.update_body_scrolled, 16); 174 175 // Init scrollToAnchoredContent. 176 $(window).on('hashchange', udem_www.scrollToAnchoredContent); 177 178 // Wait for images be loaded, otherwize offset would be not accurate. 179 if (document.readyState === 'complete') { 180 udem_www.scrollToAnchoredContent(); 181 } else { 182 $(window).one('load', udem_www.scrollToAnchoredContent); 183 } 184}); 185 186// Main menu 187jQuery(function ($) { 188 'use strict'; 189 var $mainmenu_menu = $('#udemwww-mainmenu'), 190 $mainmenu_trigger = $('#udemwww-mainmenu-trigger'),
191 $mainmenu_overlay = $('#udemwww-mainmenu-overlay'), 192 mainmenu_cls = 'udemwww-show-mainmenu'; 193 194 // Creates (once) the menus overlay 195 function mainmenu_init_overlay() { 196 if ($mainmenu_overlay.length === 0) { 197 $mainmenu_overlay = $('<div id="udemwww-mainmenu-overlay"></div>').appendTo(document.body); 198 } 199 } 200 201 // TODO May be more robust to have persitent "click" handler on the body (to avoid the "re-init")... 202 function mainmenu_init_close() { 203 $('body').one('click', function (event) { 204 if (mainmenu_is_shown()) { 205 if ($.contains($mainmenu_menu.get(0), event.target)) { 206 // Clicked a link in menu, which may be an anchor in the same page? 207 var clicked_link_href = $(event.target).closest('a[href]').prop('href'); 208 if (clicked_link_href && clicked_link_href.replace(/#.*$/, '#') === window.location.href.replace(/#.*$/, '') + '#') { 209 // hide but do not prevent default! 210 mainmenu_hide(); 211 udem_www.scrollToAnchoredContent(); 212 } else { 213 // re-init! 214 mainmenu_init_close(); 215 } 216 } else { 217 event.preventDefault(); 218 event.stopPropagation(); 219 mainmenu_hide(); 220 } 221 udem_www.position_main_toolbar(); 222 } 223 }); 224 } 225 226 function mainmenu_is_shown() { 227 return $('body').hasClass(mainmenu_cls); 228 } 229 230 function mainmenu_hide() { 231 $('body').removeClass(mainmenu_cls); 232 $mainmenu_menu.removeClass('udemwww-mainmenu-opened'); 233 } 234 235 function mainmenu_show() { 236 if (!mainmenu_is_shown()) { 237 mainmenu_init_overlay(); 238 $('body').addClass(mainmenu_cls); 239 mainmenu_init_close(); 240 241 $mainmenu_menu.one('transitionend', function (event) { 242 $mainmenu_menu.addClass('udemwww-mainmenu-opened'); 243 }); 244 245 // Also close the search modal! 246 // TODO but the search-modal state should be also fixed! 247 $('body').removeClass('udem-show-search-modal'); 248 udem_www.position_main_toolbar(); 249 } 250 } 251 252 $mainmenu_trigger.on('click', function (event) { 253 if (!mainmenu_is_shown()) { 254 event.preventDefault(); 255 event.stopPropagation(); 256 mainmenu_show(); 257 udem_www.position_main_toolbar(); 258 } 259 }); 260}); 261 262udem_www.resize_custom_menu_container = function () { 263 $('.udemwww-level2-custom-container').each(function() { 264 var $this = $(this), 265 $mainmenu_menu = $('#udemwww-mainmenu'), 266 $container = $this.parent(), 267 $height = ''; 268 $this.css('height', $height); 269 if ($mainmenu_menu.css('position') === 'static' && $container.parent().hasClass('expanded')) { 270 $height = $container.innerHeight() - 271 parseInt($this.css('marginTop')) - 272 parseInt($this.css('marginBottom')) + 'px'; 273 } 274 $this.css('height', $height); 275 }); 276}; 277 278udem_www.fix_primarynav_expanded_position = function () { 279 'use strict'; 280 var $item = $('#udemwww-primarynav > ul > li.expandable.expanded > a'), 281 $div = $item.length ? $item.next() : [], 282 $li, $ul, ul_offset, ul_width, li_offset, 283 li_width, li_relative_offset, fix_offset, 284 div_width; 285 286 if ($div.length && $div.css('position') === 'absolute') { 287 // We want the largest div_width possible, with "natural" layout... 288 $li = $item.parent(); 289 $ul = $li.parent(); 290 ul_offset = $ul.offset().left; 291 ul_width = $ul.width(); 292 li_offset = $li.offset().left; 293 li_width = $li.width(); 294 li_relative_offset = li_offset - ul_offset; 295 fix_offset = 0; 296 div_width = 0; 297 298 $div.css({ left: 0, maxWidth: ul_width + 'px' }); 299 div_width = $div.width(); 300 $div.css({ left: 'auto' }); 301 302 fix_offset = ((div_width - li_width) / -2) - 1; 303
304 if (li_relative_offset + div_width + fix_offset > ul_width) { 305 fix_offset = ul_width - li_relative_offset - div_width - 3; 306 } 307 308 if (li_relative_offset < -fix_offset) { 309 fix_offset = -li_relative_offset - 1; 310 } 311 312 $div.css({ 313 marginLeft: Math.round(fix_offset) + 'px' 314 }); 315 } else { 316 $('#udemwww-primarynav > ul > li.expandable > div').css({ 317 marginLeft: '0', 318 maxWidth: 'none' 319 }); 320 } 321}; 322 323udem_www.getHeight = function (selector) { 324 'use strict'; 325 var $elements = jQuery(selector), 326 height = 0; 327 328 if ($elements.length === 0) { 329 return 0; 330 } 331 332 height = Math.max($elements.first().height(), typeof $elements[0].clientHeight !== 'undefined' ? $elements[0].clientHeight : 0); 333 return height; 334}; 335 336udem_www.set_columns_for_menus = function() { 337 var max_items_per_columns = $('#udemwww-primarynav').data('max-items-per-column'); 338 if (!max_items_per_columns) { 339 return; 340 } 341 $('#udemwww-primarynav > ul > li.expandable > div> ul').each(function() { 342 var $this = $(this), 343 nb_items = $this.find('> li').length, 344 nb_columns = Math.ceil(nb_items / max_items_per_columns); 345 if (nb_columns > 1) { 346 $this.addClass('udem-menu-' + nb_columns + '-columns'); 347 } 348 }); 349} 350 351jQuery(function ($) { 352 'use strict'; 353 $(window).on('resize scroll scrollstop', udem_www.debounce(udem_www.fix_primarynav_expanded_position, 16)); 354 355 // Fix the mega menu custom container height to fit its parent (only for large display and non scrollable). 356 $(window).on('resize scroll scrollstop', udem_www.debounce(udem_www.resize_custom_menu_container, 16)); 357 358 // Fix the number of columns for each list if the number of item exceed the maximun allowed. 359 udem_www.set_columns_for_menus(); 360}); 361 362// Menu with expandable submenus 363jQuery(function ($) { 364 'use strict'; 365 var $body = $('body'), 366 $mainmenu_menu = $('#udemwww-mainmenu'), 367 $nav_with_expandables = $('#udemwww-primarynav, #udem-menu-connexion, #udem-menu-liens-rapides, #udem-menu-clienteles'); 368 369 $nav_with_expandables.on('click', '> ul > li.expandable > a', function (event) { 370 var $item = $(this), 371 $li = $item.parent(), 372 $ul = $li.parent(), 373 $nav = $ul.parent(), 374 expand = !$li.hasClass('expanded'); 375 376 event.preventDefault(); 377 378 if ($nav.is('#udem-menu-connexion')) { 379 $('#udem-menu-connexion-trigger').toggleClass('active', expand); 380 } 381 382 // Close all expanded 383 $nav_with_expandables.find('> ul > li.expandable.expanded').removeClass('expanded'); 384 385 if (expand) { 386 if ($li.css('position') !== 'static') { 387 $mainmenu_menu.animate({scrollTop: $li.offset().top - $mainmenu_menu.offset().top + $mainmenu_menu.scrollTop() -1}, 300); 388 } 389 $li.addClass('expanded'); 390 } 391 392 udem_www.fix_primarynav_expanded_position(); 393 udem_www.resize_custom_menu_container(); 394 }); 395 396 // Create a return bouton for each expandable content (mobile and scrollable only). 397 $nav_with_expandables.filter(":not(#udem-menu-clienteles)").each(function() { 398 $(this).find('ul > li.expandable').each(function() { 399 var $this = $(this), 400 $title = $this.children(":first"), 401 $container = $this.children("div"), 402 $close_menu = $title.clone().prependTo($container); 403 $close_menu.removeClass().addClass('udem-close-menu'); 404 $close_menu.on('click', function(e) { 405 e.preventDefault(); 406 $this.removeClass('expanded'); 407 $('#udem-menu-connexion-trigger').removeClass('active'); 408 }); 409 }); 410 411 }); 412 413 // Special case: #udem-menu-connexion on non-scrolled-desktop is a dropdown. 414 $('#udem-menu-connexion-trigger').on('click', function (event) { 415 var $trigger = $(this), 416 $nav = $('#udem-menu-connexion'), 417 $li = $nav.find('>ul>li'), 418 expand = !$li.hasClass('expanded'); 419 420 event.preventDefault(); 421 422 if ($nav.css('position') === 'absolute') { 423 event.stopPropagation(); 424 } 425 426 // Close all expanded 427 $nav_with_expandables.find('> ul > li.expandable.expanded').removeClass('expanded'); 428 429 if ($('#udem-menu-tiroir').hasClass('udem-menu-tiroir-ouvert')) { 430 udem_www.menu_tiroir.close(); 431 } 432 433 $trigger.toggleClass('active', expand); 434 if (expand) { 435 $li.addClass('expanded'); 436 } 437 438 udem_www.fix_primarynav_expanded_position(); 439 }); 440 441 // Special case: #udem-menu-connexion on desktop is a dropdown. So we may want to close it at some point! 442 $body.on('click', function (event) { 443 // Trigger click if its the lang menu or udem-je-donne. 444 if (event.target.id === 'udem-toolbar-menu-langues' || $(event.target).hasClass('udem-je-donne')) { 445 $(event.target).trigger('click'); 446 } 447 448 var $expanded = $('#udem-menu-connexion .expandable.expanded'); 449 if ($expanded.length > 0 && $('#udem-menu-connexion').css('position') === 'absolute') { 450 $expanded.removeClass('expanded'); 451 $('#udem-menu-connexion-trigger').removeClass('active'); 452 } 453 }); 454 455 // Prevent the propagation on the lang menu and udem-je-donne. 456 $('#udem-toolbar-menu-langues, .udem-je-donne').on('click', function (event) { 457 event.stopPropagation(); 458 }); 459 460 if ($('#udemwww-main-toolbar .udem-je-donne').length) { 461 $body.addClass('with-udem-je-donne'); 462 } 463}); 464 465// Menu tiroir 466udem_www.menu_tiroir = { 467 styleElement: null 468}; 469 470// Updates the dedicated style element 471udem_www.menu_tiroir.updateStyleSheet = function (css) { 472 var styleElement = udem_www.menu_tiroir.styleElement; 473 try { 474 if (!styleElement) { 475 styleElement = document.createElement('style'); 476 styleElement.setAttribute('type', 'text/css'); 477 styleElement.setAttribute('id', 'udemwww-menu-tiroir-style'); 478 (document.head || document.getElementsByTagName('head')[0]).appendChild(styleElement); 479 480 udem_www.menu_tiroir.styleElement = styleElement; 481 } 482 483 if (styleElement.styleSheet) { 484 styleElement.styleSheet.cssText = css; // Old IE 485 } else { 486 for (let i = 0; i < styleElement.childNodes.length; i++) { 487 styleElement.removeChild(styleElement.childNodes[i]); 488 } 489 styleElement.appendChild(document.createTextNode(css)); 490 } 491 } catch (e) { 492 console.log('Could not update styleSheet: ', e, this, arguments); 493 } 494}; 495 496udem_www.menu_tiroir.open = function () { 497 'use strict'; 498 var $ = jQuery, 499 $body = $('body'), 500 $trigger = $('#udem-menu-tiroir-trigger'), 501 $tiroir = $('#udem-menu-tiroir'), 502 $expandable = $tiroir.find('.expandable').first(), 503 tiroir_height, 504 toolbar_top, 505 // FIXME use the same value than @udem-main-toolbar-extralarge-bordertop-size less variable. 506 border_top = 4; 507 508 // Close all expanded expandables 509 // FIXME selectors should not be hardcoded here! 510 $('#udemwww-primarynav, #udem-menu-connexion, #udem-menu-liens-rapides, #udem-menu-clienteles').find('> ul > li.expandable.expanded').removeClass('expanded'); 511 $('#udem-menu-connexion-trigger').removeClass('active'); 512 513 $trigger.addClass('active'); 514 $tiroir.addClass('udem-menu-tiroir-ouvert'); 515 $tiroir.addClass('udem-menu-tiroir-entrouvert'); 516 $body.addClass('udem-menu-tiroir-animated'); 517 $expandable.addClass('expanded'); 518 $body.addClass('udem-show-menu-tiroir'); 519 520 tiroir_height = udem_www.getHeight('#udem-menu-tiroir-inner'); 521 toolbar_top = $('#udemwww-main-toolbar').offset().top; 522 523 udem_www.menu_tiroir.updateStyleSheet([ 524 'body:not(.udemwww-scrolled).udem-show-menu-tiroir #udemwww-main-toolbar { margin-top: ' + 525 (tiroir_height + toolbar_top - border_top) + 'px; }' 526 ].join('\n')); 527}; 528 529/** 530 * Remove classes at the end of the tiroir closing. 531 * @param {jQuery} $body 532 * @param {jQuery} $expandable 533 * @param {jQuery} $tiroir 534 */ 535udem_www.menu_tiroir.close_end = function ($body, $expandable, $tiroir) { 536 $body.removeClass('udem-show-menu-tiroir'); 537 $expandable.removeClass('expanded'); 538 $tiroir.removeClass('udem-menu-tiroir-ouvert'); 539 $body.removeClass('udem-menu-tiroir-animated'); 540}; 541 542/** 543 * 544 * @param {Boolean} animate 545 * @returns {undefined} 546 */ 547udem_www.menu_tiroir.close = function (animate) { 548 'use strict'; 549 var $ = jQuery, 550 $body = $('body'), 551 $trigger = $('#udem-menu-tiroir-trigger'), 552 $tiroir = $('#udem-menu-tiroir'), 553 $expandable = $tiroir.find('.expandable').first(); 554 555 animate = typeof animate !== 'undefined' ? animate : false;
556 557 if (animate) { 558 // Remove classes at the end of the css animation when animated. 559 $tiroir.one('transitionend', function (event) { 560 udem_www.menu_tiroir.close_end($body, $expandable, $tiroir); 561 }); 562 } else { 563 udem_www.menu_tiroir.close_end($body, $expandable, $tiroir); 564 } 565 566 $trigger.removeClass('active'); 567 $body.toggleClass('udem-menu-tiroir-animated', animate); 568 $tiroir.removeClass('udem-menu-tiroir-entrouvert'); 569 570 udem_www.menu_tiroir.updateStyleSheet(''); 571}; 572 573// Search modal and menu tiroir 574jQuery(function ($) { 575 'use strict'; 576 var lang = $('html').attr('lang') === 'en' ? 'en' : 'fr', 577 $body = $('body'), 578 $search_trigger = $('#udem-search-trigger'), 579 $search_modal = $('<div id="udemwww-search-modal"></div>').appendTo(document.body), 580 581 search_content_loaded = false, 582 search_personne_form_initialized = false, 583 584 gcse_initialized = false, 585 gcse_poll_interval = 300, // milliseconds 586 gcse_poll_max_calls = 20, // max. 20 tries 587 gcse_poll_counter = 0, 588 589 max_reached_msg = 'Résultats : Le maximum de ${n} personnes affichées est atteint. Nous vous recommandons dâaffiner vos critères de recherche.'; 590 591 function ll(msg, vars) { 592 /* eslint-disable no-template-curly-in-string */ 593 if (lang === 'en') { 594 switch (msg) { 595 case 'Recherche en cours': 596 msg = 'Searching...'; 597 break; 598 case 'Aucun résultat': 599 msg = 'No results'; 600 break; 601 case 'Résultats : 1 personne trouvée': 602 msg = 'Result: 1 person found'; 603 break; 604 case max_reached_msg: 605 msg = 'Results: The maximum of ${n} people displayed has been reached. We recommend that you refine your search criteria.'; 606 break; 607 case 'Résultats : ${n} personnes trouvées': 608 msg = 'Results: ${n} persons found'; 609 break; 610 case 'Une erreur est survenue durant la recherche': 611 msg = 'An error has occurred during the search'; 612 break; 613 } 614 } 615 /* eslint-enable no-template-curly-in-string */ 616 617 // Poor Man templates... 618 if (typeof vars === 'object') { 619 $.each(vars, function (k, v) { 620 msg = msg.replace('${' + k + '}', v); 621 }); 622 } 623 624 return msg; 625 } 626 627 // Called when gcse is ready (input is available). 628 function gcse_onready() { 629 // $('#udemwww-search-modal form.gsc-search-box input.gsc-input').trigger('focus'); 630 } 631 632 // Polls for gcse readyness (input is available), then call gcse_onready(). 633 function gcse_poll_ready() { 634 if ($search_modal.find('form.gsc-search-box').length > 0) { 635 gcse_poll_counter = 0; 636 gcse_onready(); 637 } else if (gcse_poll_counter < gcse_poll_max_calls) { 638 gcse_poll_counter++; 639 setTimeout(gcse_poll_ready, gcse_poll_interval); 640 } else { 641 console.log('gcse_poll limit reached: ', gcse_poll_counter, gcse_poll_max_calls); 642 // TODO display error message to user? 643 } 644 } 645 646 // Initializes gcse. 647 function gcse_init() { 648 setTimeout(gcse_poll_ready, gcse_poll_interval); 649 650 if (gcse_initialized) { 651 return; 652 } 653 654 // TODO put cx in an html data attribute! 655 var cx = '011034801931486537785:va21ny36mjw', 656 gcse = document.createElement('script'), 657 s = document.getElementsByTagName('script')[0]; 658 gcse.type = 'text/javascript'; 659 gcse.async = true; 660 gcse.src = 'https://cse.google.com/cse.js?cx=' + cx; 661 s.parentNode.insertBefore(gcse, s); 662 } 663 664 function search_init_tabs() { 665 var $search_tabbar_items = $search_modal.find('.search-tabbar a'), 666 $search_tabs_items = $search_modal.find('.search-tabs > *'); 667 668 $search_tabbar_items.on('click', function (event) { 669 var href = $(this).attr('href'), 670 $tab = []; 671 672 if (href.match(/^#[-_.a-zA-Z0-9]+$/)) { 673 $tab = $search_tabs_items.filter(href); 674 } 675 676 if ($tab.length > 0) { 677 event.preventDefault(); 678 679 $search_tabbar_items.removeClass('active'); 680 $search_tabs_items.hide(); 681 682 $(this).addClass('active'); 683 $tab.show(); 684 685 // Update the location hash 686 window.location.hash = href; 687 688 $tab.find('form input[type=search], form input[type=text]').first().trigger('focus'); 689 } 690 }); 691 } 692 693 function search_load_content(callback) { 694 if (search_content_loaded) { 695 if (typeof callback === 'function') { 696 callback(); 697 } 698 return; 699 } 700 701 var url = $search_trigger.attr('data-search-modal-url'); 702 if (url) { 703 $search_modal.load(url, function () { 704 if (typeof callback === 'function') { 705 search_content_loaded = true; 706 callback(); 707 } 708 }); 709 } else { 710 console.log('COULD NOT FIND data-search-modal-url!'); 711 } 712 } 713 714 function search_hide() { 715 $body.removeClass('udem-show-search-modal'); 716 udem_www.position_main_toolbar(); 717 718 // Remove the #udemwww-search-* location hash 719 if (typeof window.location.hash.replace === 'function') { 720 window.location.hash = window.location.hash.replace( 721 /^(#udemwww-search-sites|#udemwww-search-programme|#udemwww-search-personne|#udemwww-search-sofia)(&.*)?$/, 722 '' 723 ); 724 } 725 } 726 727 // TODO May be more robust to have persistent "click" handler on the body... 728 function search_init_close() { 729 $body.one('click', function (event) { 730 if (!jQuery.contains($search_modal.get(0), event.target) && event.target !== $search_modal.get(0)) { 731 event.preventDefault(); 732 search_hide(); 733 } else { 734 // reinit! 735 search_init_close(); 736 } 737 }); 738 } 739 740 function search_personne_form_init() { 741 if (search_personne_form_initialized) { 742 return; 743 } 744 search_personne_form_initialized = true; 745 746 $search_modal.find('#udemwww-search-personne form').on('submit', function (event) { 747 var $form = $(this), 748 form_action = $form.prop('action'), 749 form_data = $form.serializeArray(), 750 $results = $search_modal.find('#udemwww-search-personne-results'); 751 752 event.preventDefault(); 753 pushSearchInputValueToDatalayer(this); 754 755 $results.html('<div>' + ll('Recherche en cours') + '</div>'); 756 757 $.ajax({ url: form_action, data: form_data }).done(function (content, status, jqXhr) { 758 if (status === 'success' && typeof content === 'object' && content.data && typeof content.data.length !== 'undefined') { 759 /* eslint-disable no-template-curly-in-string */ 760 if (content.data.length === 0) { 761 // TODO localize 762 $results.html('<div>' + ll('Aucun résultat') + '</div>'); 763 } else if (content.data.length === 1) { 764 $results.html('<div>' + ll('Résultats : 1 personne trouvée') + '</div>'); 765 } else if (content.data.length === 100 || content.data.length === 200) { 766 $results.html('<div>' + ll(max_reached_msg, { n: content.data.length }) + '</div>'); 767 } else { 768 $results.html('<div>' + ll('Résultats : ${n} personnes trouvées', { n: content.data.length }) + '</div>'); 769 } 770 /* eslint-enable no-template-curly-in-string */ 771 772 $(content.data).each(function (i, personne) { 773 var $row = $('<div class="udemwww-search-personne-result"></div>'), 774 $subrows; 775 776 $('<span class="nom"></span>').text(personne.nomComplet || '').appendTo($row); 777 778 if (personne.courriel) { 779 $('<a class="email"></a>').text(personne.courriel).attr('href', 'mailto:' + personne.courriel).appendTo($row); 780 } 781 782 if (personne.fonction) { 783 $('<span class="fonction"></span>').text(personne.fonction).appendTo($row); 784 } 785 786 if (personne.unite) { 787 $('<span class="unite"></span>').text(personne.unite).appendTo($row); 788 } 789 790 if (personne.telephone) { 791 $('<a class="tel"></a>').text(personne.telephone).attr('href', 'tel:' + personne.telephone.replace(/[^0-9#,]/g, '')).appendTo($row); 792 } 793 794 if (personne.pavillon || personne.local) { 795 $('<span class="local"></span>').text( 796 personne.pavillon 797 ? personne.pavillon + (personne.local ? ', local ' + personne.local : '') 798 : personne.local 799 ).appendTo($row); 800 } 801 802 if (personne.fonctions && personne.fonctions.length) { 803 $subrows = $('<div class="fonctions"></div>').appendTo($row); 804 805 $(personne.fonctions).each(function (j, fonc) { 806 var $row = $('<div class="fonc"></div>').appendTo($subrows); 807 808 if (fonc.fonction) { 809 $('<span class="fonction"></span>').text(fonc.fonction).appendTo($row); 810 } 811 812 if (fonc.courriel) { 813 $('<a class="email"></a>').text(fonc.courriel).attr('href', 'mailto:' + fonc.courriel).appendTo($row); 814 } 815 816 if (fonc.unite) { 817 $('<span class="unite"></span>').text(fonc.unite).appendTo($row); 818 } 819
820 if (fonc.telephone) { 821 $('<a class="tel"></a>').text(fonc.telephone).attr('href', 'tel:' + fonc.telephone.replace(/[^0-9#,]/g, '')).appendTo($row); 822 } 823 824 if (fonc.pavillon || fonc.local) { 825 $('<span class="local"></span>').text( 826 fonc.pavillon 827 ? fonc.pavillon + (fonc.local ? ', local ' + fonc.local : '') 828 : fonc.local 829 ).appendTo($row); 830 } 831 }); 832 } 833 834 $results.append($row); 835 }); 836 } else { 837 $results.html('<div>' + ll('Une erreur est survenue durant la recherche') + '</div>'); 838 } 839 }).fail(function (jqXhr, status, errorThrown) { 840 // The service does not provide a way to disciminate "not found" and error! 841 $results.html('<div>' + ll('Aucun résultat') + '</div>'); 842 }); 843 }); 844 } 845 846 $(window).on('resize', udem_www.position_main_toolbar); 847 udem_www.position_main_toolbar(); 848 849 // Send searched terms to GA 850 function pushSearchInputValueToDatalayer(formElement) { 851 // Get only input elements with type="search" within div with class "search-tabs" 852 const searchInputs = formElement.querySelectorAll('.search-tabs input[type="search"]'); 853 854 // Add values to dataLayer 855 searchInputs.forEach(input => { 856 window.dataLayer.push({ 'event': 'recherche', 'query': input.value }); 857 }); 858 } 859 860 function addSearchListener() { 861 862 const searchTabsIds = ['udemwww-search-programme', 'udemwww-search-sofia']; 863 searchTabsIds.forEach((tabId) => { 864 // Attach event listener to submit event of forms within div with class "search-tabs" 865 const searchTabsForms = document.querySelectorAll('#' + tabId + ' form'); 866 searchTabsForms.forEach(form => { 867 if (form.getAttribute("listener") !== "true") { 868 form.setAttribute("listener", "true"); 869 form.addEventListener('submit', function (event) { 870 event.preventDefault(); 871 pushSearchInputValueToDatalayer(this); 872 this.submit() 873 }); 874 } 875 }); 876 }); 877 } 878 879 function search_show(callback) { 880 if ($('#udem-menu-tiroir').hasClass('udem-menu-tiroir-ouvert')) { 881 udem_www.menu_tiroir.close(); 882 } 883 884 search_load_content(function () { 885 search_init_tabs(); 886 gcse_init(); 887 888 search_personne_form_init(); 889 890 if (typeof callback !== 'undefined') { 891 callback(); 892 } 893 894 addSearchListener(); 895 896 // Update the location hash 897 window.location.hash = $search_modal.find('.search-tabbar a.active').attr('href'); 898 }); 899 900 setTimeout(search_init_close, 500); // must be defered, otherwize will be trigered immediatly (click is not finished yet?!) 901 902 $body.addClass('udem-show-search-modal'); 903 udem_www.position_main_toolbar(); 904 // console.log('TODO when search modal opened (but content not loaded yet!): ', this, arguments); 905 } 906 907 $search_trigger.on('click', function (event) { 908 event.preventDefault(); 909 if (udem_www.search_is_shown()) { 910 search_hide(); 911 } else { 912 search_show(); 913 } 914 udem_www.position_main_toolbar(); 915 }); 916 917 function search_handle_hashchange() { 918 var regex = /(#udemwww-search-sites|#udemwww-search-programme|#udemwww-search-personne|#udemwww-search-sofia)(&.*)?$/, 919 hash = window.location.href.match(regex); 920 if (hash && hash[1]) { 921 if (!udem_www.search_is_shown()) { 922 search_show(function () { 923 var $search_tabbar_items = $search_modal.find('.search-tabbar a'), 924 $search_tabs_items = $search_modal.find('.search-tabs > *'), 925 $tab; 926 927 $search_tabbar_items.removeClass('active'); 928 $search_tabs_items.hide(); 929
930 $tab = $search_tabs_items.filter(hash[1]); 931 if ($tab.length) { 932 $search_tabbar_items.filter('[href="' + hash[1] + '"]').addClass('active'); 933 $tab.show(); 934 $tab.find('form input[type=search], form input[type=text]').first().trigger('focus'); 935 } 936 }); 937 } 938 } 939 } 940 941 // on doc ready 942 setTimeout(search_handle_hashchange, 300); 943 944 // on navigation 945 $(window).on('hashchange', search_handle_hashchange); 946 947 // on anchor link click 948 $([ 949 'a[href="#udemwww-search-sites"]', 950 'a[href="#udemwww-search-programme"]', 951 'a[href="#udemwww-search-personne"]', 952 'a[href="#udemwww-search-sofia"]' 953 ].join(',')).on('click', function (event) { 954 search_handle_hashchange(); 955 }); 956 957 // Needed to initialize the dedicated style element 958 udem_www.menu_tiroir.close(); 959 960 $('#udem-menu-tiroir-trigger').on('click', function (event) { 961 var expand = !$(this).hasClass('active'); 962 963 event.preventDefault(); 964 965 if (expand) { 966 if (udem_www.search_is_shown()) { 967 search_hide(); 968 } 969 udem_www.menu_tiroir.open(); 970 } else { 971 udem_www.menu_tiroir.close(true); 972 } 973 }); 974 975 $(window).on('resize scroll scrollstop', udem_www.debounce(function () { 976 var a = $('body').hasClass('udemwww-scrolled') && $('#udem-menu-tiroir').hasClass('udem-menu-tiroir-ouvert'), 977 b = $(window).width() < 1200; 978 if (a || b) { 979 udem_www.menu_tiroir.close(); 980 } 981 }, 17)); 982}); 983 984// Equalize some block heights 985jQuery(function ($) { 986 'use strict'; 987 988 var equalize_heights_selector = [ 989 '.CType-textpic.lyt-3 .csc-header', 990 '.carousel-outer[data-carousel-slides-to-show="3"] .slick-slide > div > div > .content-container:not(.CType-textpic.lyt-3, .CType-image)', 991 '.carousel-outer[data-carousel-slides-to-show="2"] .slick-slide > div > div > .content-container:not(.CType-textpic.lyt-3, .CType-image)', 992 '.gbl-udembootstrap_2cols > .row > * > *:not(.CType-textpic.lyt-3, .CType-image)', 993 '.gbl-udembootstrap_3cols > .row > * > *:not(.CType-textpic.lyt-3, .CType-image)', 994 '.gbl-udembootstrap_4cols > .row > * > *:not(.CType-textpic.lyt-3, .CType-image)', 995 '.bloc-synthese-trigger' 996 ].join(','), 997 998 // Float-precision pixel units can mess-up some layouts: use ceiled dimensions! 999 // jQuery outerHeight method round the height in IE! 1000 getCeiledHeight = function (element) { 1001 if (typeof element.getBoundingClientRect === 'function') { 1002 try { 1003 var rect = element.getBoundingClientRect(); 1004 return Math.ceil(rect.bottom - rect.top); 1005 } catch (e) { 1006 // In IE<=11, calling getBoundingClientRect on an element outside of the DOM throws an unspecified error instead of returning a 0x0 DOMRect 1007 // pass 1008 } 1009 } 1010 if (typeof window.getComputedStyle === 'function') { 1011 return Math.ceil(parseFloat(window.getComputedStyle(element, null).height)); 1012 } 1013 return Math.ceil($(element).outerHeight()); 1014 }, 1015 1016 getFlooredOffsetTop = function (element) { 1017 return Math.floor($(element).offset().top); 1018 }; 1019 1020 // Returns the next group of blocs to be processed, or undefined. 1021 // A group is a subset of the blocs: 1022 // having the same offset top, 1023 // having more than one bloc, 1024 // having more than 1 distinct height. 1025 function next_group(blocs) { 1026 var groups = udem_www.groupBy(blocs, function (bloc) { 1027 return getFlooredOffsetTop(bloc); 1028 }); 1029 return udem_www.find(groups, function (group) { 1030 var heights, min_height, max_height, 1031 headers, headers_heights, 1032 headers_min_height = 0, 1033 headers_max_height = 0; 1034 1035 if (group.length < 2) { 1036 return undefined; 1037 } 1038 if (group.length === 2 && group[0] === group[1].parentNode) { 1039 return undefined; 1040 } 1041 1042 heights = $.map(group, getCeiledHeight); 1043 min_height = Math.min.apply(null, heights); 1044 max_height = Math.max.apply(null, heights); 1045 1046 headers = $(group).children('.csc-header'); 1047 if (headers.length > 1) { 1048 headers_heights = $.map(headers, getCeiledHeight); 1049 headers_min_height = Math.min.apply(null, headers_heights); 1050 headers_max_height = Math.max.apply(null, headers_heights); 1051 } 1052 1053 if (min_height === max_height && headers_min_height === headers_max_height) { 1054 return undefined; 1055 } 1056 1057 return true; 1058 }); 1059 } 1060 1061 function equalize_heights(group) { 1062 var height, headers, headers_height; 1063 1064 height = Math.max.apply(null, $.map(group, getCeiledHeight)); 1065 $(group).css({ height: height + 'px' }); 1066 1067 headers = $(group).children('.csc-header'); 1068 headers_height = Math.max.apply(null, $.map(headers, getCeiledHeight)); 1069 $(headers).css({ height: headers_height + 'px' }); 1070 } 1071 1072 function save_or_restore_original_height(blocs) { 1073 var i, origin; 1074 for (i = 0; i < blocs.length; i++) { 1075 if (blocs[i].hasAttribute('data-original-height')) { 1076 origin = blocs[i].getAttribute('data-original-height'); 1077 $(blocs[i]).css('height', origin); 1078 } else { 1079 origin = blocs[i].style.height; 1080 if (origin === '') { 1081 origin = 'auto'; 1082 } 1083 blocs[i].setAttribute('data-original-height', origin); 1084 } 1085 } 1086 } 1087 1088 function reflow() { 1089 var $blocs = $(equalize_heights_selector), 1090 blocs, group; 1091 1092 if ($blocs.length === 0) { 1093 return; 1094 } 1095 1096 blocs = $blocs.get(); 1097 1098 save_or_restore_original_height(blocs); 1099 save_or_restore_original_height($blocs.children('.csc-header').get()); 1100 1101 for (let i = 0; i < 50; i++) { // limit to 50 passes, just in case... 1102 group = next_group(blocs); 1103 if (group) { 1104 equalize_heights(group); 1105 } else { 1106 break; 1107 } 1108 } 1109 } 1110 1111 $(window).on('resize', udem_www.debounce(reflow, 300)); 1112 setTimeout(reflow, 50); 1113}); 1114 1115// Accordeons udemwww_accordion. 1116jQuery(function ($) { 1117 'use strict'; 1118 1119 var udemwww_accordion_TRANSITION_DURATION = 350, 1120 udemwww_accordion_trigger_selector = '.csc-header', 1121 udem_accordion_body_selector = '.udemwww_accordion-body'; 1122 1123 // Get hash escaping the special char |. 1124 // Should use escapeSelector in Jquery 3 instead when used. 1125 function udemwww_get_escaped_hash() { 1126 return window.location.hash.replace(/\|/g, '\\|'); 1127 } 1128 1129 function udemwww_accordion_openhash() { 1130 var hash = udemwww_get_escaped_hash().replace(/^#/, '').replace(/&.*$/, ''), 1131 accortion_selector = '.udemwww_accordion-item', 1132 $item, 1133 $identifier, 1134 is_accordion; 1135 1136 if (!hash) { 1137 return; 1138 } 1139 1140 $item = $('#' + hash); 1141 $identifier = $item; 1142 is_accordion = $identifier.is(accortion_selector); 1143 1144 // Check if the hash passed is the named identifier instead of the auto generated one. 1145 if (!is_accordion) { 1146 $identifier = $item.parent(); 1147 is_accordion = $identifier.is(accortion_selector); 1148 } 1149 1150 if (is_accordion) { 1151 udemwww_accordion_show($identifier); 1152 } 1153 } 1154 1155 function udemwww_accordion_show($item) { 1156 var $trigger = $item.children(udemwww_accordion_trigger_selector), 1157 $element = $item.children(udem_accordion_body_selector), 1158 $parent = $item.parent(), 1159 hash; 1160 1161 // Activate/show ancestor tab-items/accordion-items 1162 $parent.parents().each(function () { 1163 var $ancestor = $(this); 1164 1165 if ($ancestor.is(udem_accordion_body_selector)) { 1166 udemwww_accordion_show($ancestor.parent()); 1167 } 1168 }); 1169 1170 // Put last opened accordion item in location hash. 1171 hash = $item.attr('id'); 1172 1173 // Check the accordion item have a named identifier and use this one instead of the auto generated one. 1174 if ($item.children().first().is('a')) { 1175 hash = $item.children().first().attr('id'); 1176 } 1177 if (hash) { 1178 if (typeof window.history.pushState === 'function') { 1179 window.history.pushState('', '', '#' + hash); 1180 } else { 1181 window.location.hash = '#' + hash; 1182 } 1183 } 1184 1185 $element.slideDown(udemwww_accordion_TRANSITION_DURATION, function () { 1186 $element.addClass('collapse in'); 1187 $trigger.removeClass('collapsed'); 1188 }); 1189 $trigger.removeClass('collapsed'); 1190 } 1191 1192 function udemwww_accordion_hide($item) { 1193 var $trigger = $item.children(udemwww_accordion_trigger_selector), 1194 $element = $item.children(udem_accordion_body_selector); 1195 1196 $element.slideUp(udemwww_accordion_TRANSITION_DURATION, function () { 1197 $element.removeClass('collapse in'); 1198 $trigger.addClass('collapsed'); 1199 }); 1200 } 1201 1202 function udemwww_accordion_toggle($item) { 1203 var $element = $item.children(udem_accordion_body_selector); 1204 1205 if ($element.hasClass('in')) { 1206 udemwww_accordion_hide($item); 1207 } else { 1208 udemwww_accordion_show($item); 1209 } 1210 } 1211 1212 $(window).on('hashchange', udemwww_accordion_openhash); 1213 setTimeout(udemwww_accordion_openhash, 160); 1214 1215 $('.udemwww_accordion-container').each(function () { 1216 var $parent = $(this); 1217 1218 $parent.children().each(function () { 1219 var $item = $(this), 1220 $trigger = $item.children(udemwww_accordion_trigger_selector), 1221 $element = $item.children(udem_accordion_body_selector); 1222 1223 if (!$element.hasClass('in')) { 1224 $trigger.addClass('collapsed'); 1225 } 1226 1227 $trigger.on('click', function (event) { 1228 var $item = $(this).parent(); 1229 event.preventDefault(); 1230 udemwww_accordion_toggle($item); 1231 }); 1232 }); 1233 }); 1234}); 1235 1236// Onglets udembootstrap_tabs 1237jQuery(function ($) { 1238 'use strict'; 1239 1240 $('.gbl-udembootstrap_tabs').each(function () { 1241 var $container = $(this), 1242 $tabs = $container.children(), 1243 $tabbar = $('<ul class="tabs-tabbar"></ul>'); 1244 1245 $container.addClass('tabs-container'); 1246 1247 $tabs.each(function (i) { 1248 var $tab = $(this), 1249 id = $tab.attr('id'), 1250 $header = $tab.find('.csc-header').first(), 1251 label = $header.text(), 1252 $tabbar_item, 1253 $accordion_trigger, 1254 activator; 1255 1256 $tab.addClass('tabs-panel'); 1257 $header.addClass('tabs-original-header'); 1258 1259 $tabbar_item = $('<li><a href="#' + id + '">' + label + '</a></li>');
1260 $accordion_trigger = $('<div class="tabs-accordion-trigger"><a href="#' + id + '">' + label + '</a></div>'); 1261 1262 $tabbar.append($tabbar_item); 1263 $tab.before($accordion_trigger); 1264 1265 activator = (function () { 1266 return function (event) { 1267 event.preventDefault(); 1268 var tabsactiveclass = 'tabs-active', 1269 expandedclass = 'expanded'; 1270 $tabbar.children().not($tabbar_item).removeClass(tabsactiveclass); 1271 $container.children('.tabs-accordion-trigger').not($accordion_trigger).removeClass(tabsactiveclass); 1272 $tabs.not($tab).removeClass(expandedclass); 1273 1274 if ($accordion_trigger.hasClass(tabsactiveclass)) { 1275 $accordion_trigger.removeClass(tabsactiveclass); 1276 $tabbar_item.removeClass(tabsactiveclass); 1277 $tab.removeClass(expandedclass); 1278 } else { 1279 $accordion_trigger.addClass(tabsactiveclass); 1280 $tabbar_item.addClass(tabsactiveclass); 1281 $tab.addClass(expandedclass); 1282 // If mobile, scroll to accordion, else, scroll to tab. 1283 if ($(this).is($accordion_trigger)) { 1284 udem_www.scrollToElement($accordion_trigger, $('#udemwww-main-toolbar').height()); 1285 } else { 1286 udem_www.scrollToElement($tab, $('#udemwww-main-toolbar').height()); 1287 } 1288 } 1289 }; 1290 }()); 1291 1292 $accordion_trigger.on('click', activator); 1293 $tabbar_item.on('click', activator); 1294 }); 1295 1296 $container.prepend($tabbar); 1297 }); 1298}); 1299 1300// "udemInViewport" jQuery special event. 1301// Adapted from https://github.com/protonet/jquery.inview/blob/master/jquery.inview.js (published under "DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE") 1302(function ($) { 1303 'use strict'; 1304 1305 var inviewObjects = [], 1306 viewportSize = null, // cache of viewport size 1307 viewportOffset = null, // cache of viewport scroll amount 1308 d = document, 1309 w = window, 1310 docElement = d.documentElement, 1311 timer, 1312 getViewportOffset, 1313 checkinview_latency = 32; // latency in milliseconds. The lowest, the fastest, but the more stressfull for the layiout engine. Tests show that "32" adds 2% JS performance penalty compared to "120". 1314 1315 // Returns cross-browser equivalent of { top: window.scrollY, left: window.scrollX } 1316 getViewportOffset = (function () { 1317 if (typeof window.pageXOffset === 'undefined') { 1318 return function () { 1319 return { 1320 top: document.documentElement.scrollTop, 1321 left: document.documentElement.scrollLeft 1322 }; 1323 }; 1324 } else { 1325 return function () { 1326 return { 1327 top: window.pageYOffset, 1328 left: window.pageXOffset 1329 }; 1330 }; 1331 } 1332 }()); 1333 1334 // TODO Profiling shows that this function really need more optimizations! 1335 function checkInView() { 1336 var i, 1337 $elements, 1338 $element, 1339 elementHeight, 1340 elementWidth, 1341 elementOffset, 1342 inView, 1343 viewportHeight, 1344 viewportWidth, 1345 viewportOffsetTop, 1346 viewportOffsetLeft; 1347 1348 if (!inviewObjects.length) { 1349 return; 1350 } 1351 1352 // Use or refresh viewportSize cache 1353 if (viewportSize === null) { 1354 viewportSize = { 1355 height: window.document.documentElement.clientHeight, 1356 width: window.document.documentElement.clientWidth 1357 }; 1358 } 1359 // Create copies of values asap, to try to avoid race conditions with the events that invalidate viewportSize cache (TODO should have some kind of lock?) 1360 viewportHeight = viewportSize.height; 1361 viewportWidth = viewportSize.width; 1362 1363 // Use or refresh viewportOffset cache 1364 if (viewportOffset === null) { 1365 viewportOffset = getViewportOffset(); 1366 } 1367 // Create copies of values asap, to try to avoid race conditions with the events that invalidate viewportOffset cache (TODO should have some kind of lock?) 1368 viewportOffsetTop = viewportOffset.top; 1369 viewportOffsetLeft = viewportOffset.left; 1370 1371 $elements = $.map(inviewObjects, function (inviewObject) { 1372 var selector = inviewObject.data.selector, 1373 $element = inviewObject.$element; 1374 return selector ? $element.find(selector) : $element; 1375 }); 1376 1377 for (i = 0; i < $elements.length; i++) { 1378 // Ignore elements that are not in the DOM tree -- TODO optimize! 1379 if (!$.contains(docElement, $elements[i][0])) { 1380 continue; 1381 } 1382 1383 $element = $($elements[i]); 1384 elementHeight = $element[0].offsetHeight; 1385 elementWidth = $element[0].offsetWidth; 1386 elementOffset = $element.offset(); 1387 inView = $element.data('udemInViewport_isInView'); 1388 1389 if (elementOffset.top + elementHeight > viewportOffsetTop && 1390 elementOffset.top < viewportOffsetTop + viewportHe
1390ight && 1391 elementOffset.left + elementWidth > viewportOffsetLeft && 1392 elementOffset.left < viewportOffsetLeft + viewportWidth) { 1393 if (!inView) { 1394 $element.data('udemInViewport_isInView', true).trigger('udemInViewport', [true, $element.data('udemInViewport_wasInView')]); 1395 $element.data('udemInViewport_wasInView', true); 1396 } 1397 } else if (inView) { 1398 $element.data('udemInViewport_isInView', false).trigger('udemInViewport', [false]); 1399 } 1400 } 1401 } 1402 1403 $.event.special.udemInViewport = { 1404 // @see http://learn.jquery.com/events/event-extensions/#add-function-handleobj 1405 add: function (handleObj) { 1406 inviewObjects.push({ data: handleObj, $element: $(this), element: this }); 1407 if (!timer && inviewObjects.length) { 1408 // TODO this timer consumes a lot of cycles! Any way to avoid that? 1409 timer = setInterval(checkInView, checkinview_latency); 1410 } 1411 }, 1412 1413 // @see http://learn.jquery.com/events/event-extensions/#remove-function-handleobj 1414 remove: function (handleObj) { 1415 for (let i = 0; i < inviewObjects.length; i++) { 1416 let inviewObject = inviewObjects[i]; 1417 if (inviewObject.element === this && inviewObject.data.guid === handleObj.guid) { 1418 inviewObjects.splice(i, 1); 1419 break; 1420 } 1421 } 1422 1423 // Clear interval when we no longer have any elements listening 1424 if (!inviewObjects.length) { 1425 clearInterval(timer); 1426 timer = null; 1427 } 1428 } 1429 }; 1430 1431 $(function () { 1432 // on document ready 1433 viewportSize = { 1434 height: window.document.documentElement.clientHeight, 1435 width: window.document.documentElement.clientWidth 1436 }; 1437 1438 viewportOffset = getViewportOffset(); 1439 }); 1440 1441 // Reset viewportSize and viewportOffset when they may have changed 1442 $(w).on('scroll resize scrollstop', udem_www.debounce(function () { 1443 // TODO May be better to warmup with actual values (less job in checkInView function)... 1444 viewportSize = viewportOffset = null; 1445 }, 32)); 1446 1447 // IE < 9 scrolls to focused elements without firing the "scroll" event 1448 if (!docElement.addEventListener && docElement.attachEvent) { 1449 docElement.attachEvent('onfocusin', function () { 1450 viewportOffset = null; 1451 }); 1452 } 1453}(jQuery)); 1454 1455jQuery(function ($) { 1456 'use strict'; 1457 1458 // Loads Alertes with XHR into #udemwww-alertes-placeholder 1459 var $alertes_placeholder = $('#udemwww-alertes-placeholder').first(), 1460 alertes_url = $alertes_placeholder.attr('data-alertes-url'), 1461 l10n_precedent = $('html').attr('lang') === 'en' ? 'Previous alert' : 'Alerte précédente', 1462 l10n_suivant = $('html').attr('lang') === 'en' ? 'Next alert' : 'Alerte suivante'; 1463 1464 function escapeHtml(string) { 1465 return String(string).replace(/[&<>"]/g, function (s) { 1466 return { '&': '&', '<': '<', '>': '>', '"': '"' }[s]; 1467 }); 1468 } 1469 1470 function arrow(btnClass, iconClass, label) { 1471 return '<button type="button" class="' + escapeHtml(btnClass) + 1472 '" tabindex="0" data-role="none" role="button" aria-label="' + 1473 escapeHtml(label) + '"><span class="' + escapeHtml(iconClass) + 1474 '"></span><span class="sr-only">' + escapeHtml(label) + 1475 '</span></button>'; 1476 } 1477 1478 if ($alertes_placeholder.length < 1) { 1479 return; 1480 } 1481 1482 /* 1483 // Mobile (default): #udemwww-alertes-placeholder is nextsibling of #udemwww-main-header 1484 // Desktop (>=1200): #udemwww-alertes-placeholder is previoussibling of #udemwww-main-header 1485 function init_fix_alertes_placeholder() { 1486 function fix_alertes_placeholder() { 1487 var body_width = $('body').width(), 1488 next_id = $alertes_placeholder.next().attr('id'), 1489 prev_id = $alertes_placeholder.prev().attr('id'), 1490 $main_header = $('#udemwww-main-header'); 1491 1492 if ($main_header.length !== 1) { 1493 return; 1494 } 1495 if (body_width >= 1200 && next_id !== 'udemwww-main-header') { 1496 console.log(' DESKTOP!'); 1497 $alertes_placeholder.insertBefore($main_header); 1498 } else if (body_width < 1200 && prev_id !== 'udemwww-main-header') { 1499 console.log(' MOBILE!'); 1500 $alertes_placeholder.insertAfter($main_header); 1501 } 1502 } 1503 1504 fix_alertes_placeholder(); 1505 $(window).on('resize', fix_alertes_placeholder); 1506 } 1507 */ 1508 1509 $.get(alertes_url, function (content, status, xhr) { 1510 var $inner1, $inner2; 1511 if (status === 'success' && content) { 1512 $inner1 = $('<div id="udemwww-alertes-placeholder-inner1"></div>').appendTo($alertes_placeholder);
1513 $inner2 = $('<div id="udemwww-alertes-placeholder-inner2"></div>').appendTo($inner1); 1514 $inner2.html(content); 1515 1516 $inner2.css({ margin: '0 64px' }); 1517 1518 $inner2.slick({ 1519 adaptiveHeight: true, 1520 prevArrow: arrow('slick-prev', 'udem-icon-ico-chevron-left', l10n_precedent), 1521 nextArrow: arrow('slick-next', 'udem-icon-ico-chevron-right', l10n_suivant), 1522 respondTo: 'slider' 1523 }); 1524 1525 $('body').addClass('udem-has-alertes'); 1526 1527 // init_fix_alertes_placeholder(); 1528 } 1529 }); 1530}); 1531 1532// Calendrier 1533jQuery(function ($) { 1534 'use strict'; 1535 1536 function escapeHtml(string) { 1537 return String(string).replace(/[&<>"]/g, function (s) { 1538 return { '&': '&', '<': '<', '>': '>', '"': '"' }[s]; 1539 }); 1540 } 1541 1542 function arrow(btnClass, iconClass, label) { 1543 return '<button type="button" class="' + escapeHtml(btnClass) + 1544 '" tabindex="0" data-role="none" role="button" aria-label="' + 1545 escapeHtml(label) + '"><span class="' + escapeHtml(iconClass) + 1546 '"></span><span class="sr-only">' + escapeHtml(label) + 1547 '</span></button>'; 1548 } 1549 1550 $('.udemwww-calendrier-list-view').each(function (i, calendrier) { 1551 var $calendrier = $(calendrier), 1552 l10n_precedent = 'Activités précédentes', // TODO localize 1553 l10n_suivant = 'Activités suivantes'; // TODO localize 1554 1555 // Remember that 'init' event MUST be registered before calling 'slick()'! 1556 // Same thing for the 'setPosition' if you want those event for first layout ! 1557 1558 // Equalize heights! (works only if adaptiveHeight option is false, because of animation...) 1559 $calendrier.on('setPosition', function (event, slick) { 1560 try { 1561 var height = slick.$list.height(); 1562 slick.$slides.height(height); 1563 } catch (e) { 1564 // pass 1565 // console.log('CAUGHT Exception: ', e); 1566 } 1567 }); 1568 1569 $calendrier.slick({ 1570 autoplay: false, 1571 adaptiveHeight: false, 1572 1573 arrows: true, 1574 prevArrow: arrow('slick-prev', 'udem-icon-ico-chevron-left', l10n_precedent), 1575 nextArrow: arrow('slick-next', 'udem-icon-ico-chevron-right', l10n_suivant), 1576 1577 dots: false, 1578 infinite: false, 1579 speed: 300, 1580 // centerMode : true, 1581 // centerPadding : '80%', 1582 1583 slidesToShow: 1, 1584 slidesToScroll: 1, 1585 1586 mobileFirst: true, 1587 respondTo: 'window', 1588 responsive: [ 1589 { 1590 breakpoint: 767, 1591 settings: { 1592 slidesToShow: 2, 1593 slidesToScroll: 1 1594 } 1595 }, 1596 { 1597 breakpoint: 1199, 1598 settings: { 1599 slidesToShow: 3, 1600 slidesToScroll: 1 1601 } 1602 } 1603 ] 1604 }); 1605 }); 1606}); 1607 1608// Carousel 1609/* 1610 .gbl-udembootstrap_carousel $carousel 1611 > a (?) 1612 > div.carousel-outer[data-carousel-...="..."] $wrapper 1613 > .carousel-inner $container 1614 > .udembootstrap_carousel_item (*) 1615*/ 1616jQuery(function ($) { 1617 'use strict'; 1618 1619 var $carousels = $('.gbl-udembootstrap_carousel'); 1620 1621 function escapeHtml(string) { 1622 return String(string).replace(/[&<>"]/g, function (s) { 1623 return { '&': '&', '<': '<', '>': '>', '"': '"' }[s]; 1624 }); 1625 } 1626 1627 function dataAttrBool($element, name, default_value) { 1628 var value = $element.attr('data-carousel-' + name); 1629 if (typeof value === 'undefined') { 1630 return !!default_value; 1631 } 1632 if (value === 'false' || value === '0' || value === '' || !!value === false) { 1633 return false; 1634 } 1635 return true; 1636 } 1637 1638 function dataAttrInt($element, name, default_value) { 1639 var value = $element.attr('data-carousel-' + name); 1640 if (typeof value !== 'undefined' && value.match(/^-?[0-9]+$/)) { 1641 return parseInt(value, 10); 1642 } 1643 return default_value; 1644 } 1645 1646 function arrow(btnClass, iconClass, label) { 1647 return '<button type="button" class="' + escapeHtml(btnClass) + 1648 '" tabindex="0" data-role="none" role="button" aria-label="' + 1649 escapeHtml(label) + '"><span class="' + escapeHtml(iconClass) + 1650 '"></span><span class="sr-only">' + escapeHtml(label) + 1651 '</span></button>'; 1652 } 1653 1654 function makeCarouselConfig($wrapper) { 1655 var config, 1656 l10n_precedent = $('html').attr('lang') === 'en' ? 'Previous' : 'Précédent', 1657 l10n_suivant = $('html').attr('lang') === 'en' ? 'Next' : 'Suivant', 1658 1659 slidesToShow = dataAttrInt($wrapper, 'slides-to-show', 1); 1660 // slidesToScroll = dataAttrInt($wrapper, 'slides-to-scroll', 1), 1661 // arrows = dataAttrBool($wrapper, 'arrows', false) 1662 ; 1663 1664 config = { 1665 autoplay: dataAttrBool($wrapper, 'autoplay', false), 1666 autoplaySpeed: dataAttrInt($wrapper, 'autoplay-speed', 3000), 1667 adaptiveHeight: dataAttrBool($wrapper, 'adaptive-height', false), 1668 arrows: dataAttrBool($wrapper, 'arrows-mobile', false), 1669 dots: dataAttrBool($wrapper, 'dots', false), 1670 infinite: dataAttrBool($wrapper, 'infinite', false), 1671 speed: dataAttrInt($wrapper, 'speed', 300), 1672 prevArrow: arrow('slick-prev', 'udem-icon-btn-precedent', l10n_precedent), 1673 nextArrow: arrow('slick-next', 'udem-icon-btn-suivant', l10n_suivant), 1674 slidesToShow: 1, 1675 slidesToScroll: 1, 1676 mobileFirst: true, 1677 respondTo: 'window', 1678 responsive: [ 1679 { 1680 breakpoint: 767, 1681 settings: { 1682 centerMode: slidesToShow > 1, 1683 centerPadding: '10%', 1684 variableWidth: true,
1685 arrows: false 1686 } 1687 }, 1688 { 1689 breakpoint: 768, 1690 settings: { 1691 centerMode: false, 1692 variableWidth: false, 1693 arrows: dataAttrBool($wrapper, 'arrows', false), 1694 slidesToScroll: dataAttrInt($wrapper, 'slides-to-scroll', 1) 1695 } 1696 }, 1697 { 1698 breakpoint: 991, 1699 settings: { 1700 centerMode: false, 1701 variableWidth: false, 1702 slidesToShow: slidesToShow, 1703 arrows: dataAttrBool($wrapper, 'arrows', false), 1704 slidesToScroll: dataAttrInt($wrapper, 'slides-to-scroll', 1) 1705 } 1706 } 1707 ] 1708 }; 1709 1710 return config; 1711 } 1712 1713 $carousels.each(function (index, element) { 1714 var $carousel = $(element), 1715 $wrapper = $carousel.children('.carousel-outer').first(), 1716 $container = $wrapper.children('.carousel-inner'), 1717 config = makeCarouselConfig($wrapper), 1718 initial_slide = dataAttrInt($wrapper, 'initial-slide', 1) - 1, 1719 remember_and_increment = dataAttrBool($wrapper, 'remember-and-increment', false); 1720 1721 // Do not initialize the carousel if it has less than 2 items! 1722 if ($container.children().length < 2) { 1723 return; 1724 } 1725 1726 // Remember that 'init' event MUST be registered before calling 'slick()'! 1727 $container.on('init', function (ev, slick) { 1728 slick.$slides.children().children().css({ 'display': 'block' }); // CTTYPO3-1624 1729 }); 1730 1731 $container.slick(config); 1732 1733 $container.slick('slickGoTo', initial_slide, true); 1734 1735 // Remember last current slide, and start at the next one on next visit 1736 if (remember_and_increment && typeof localforage !== 'undefined') { 1737 let remember_ns = 'udembootstrap_carousel_remember_and_increment', 1738 remember_store = localforage.createInstance({ name: remember_ns }), 1739 remember_key = $carousel.attr('id') || window.location.href + '#' + index, 1740 remember_getter = (function (store, key) { 1741 return function (callback) { 1742 store.getItem(key, callback); 1743 }; 1744 }(remember_store, remember_key)), 1745 remember_setter = (function (store, key) { 1746 return function (value, callback) { 1747 store.setItem(key, value, callback); 1748 }; 1749 }(remember_store, remember_key)); 1750 1751 $container.on('afterChange', function (event, slick, currentSlideIndex) { 1752 // Stores current slide index 1753 remember_setter(currentSlideIndex); 1754 }); 1755 1756 // On after slide change, put the dots inside the galerie item if in a square (imageorient-205). 1757 $container.on('afterChange', function(event, slick, currentSlide) { 1758 var $currentSlide = $container.find("[data-slick-index='"+ currentSlide + "']"), 1759 $dots = $container.find('.slick-dots'), 1760 $contentContainer = $currentSlide.find('.udembootstrap_carousel_item > .content-container'); 1761 1762 if ($dots.length) { 1763 if ($contentContainer.hasClass('imageorient-205')) { 1764 $contentContainer.find('.csc-textpic-text-outer').append($dots); 1765 } else if ($dots.parent() !== $container) { 1766 1767 $container.append($dots); 1768 } 1769 } 1770 }); 1771 1772 remember_getter(function (err, remembered_slide_index) { 1773 if (typeof remembered_slide_index === 'number') { 1774 // Calculating "next index" is quite difficult (because of all pagination options)! 1775 // So we go to the stored slide index, and then call slickNext()... 1776 $container.slick('slickGoTo', remembered_slide_index, true); 1777 $container.slick('changeSlide', { data: { message: 'next' } }, true); // don't animate! 1778 } else { 1779 remember_setter(initial_slide); 1780 } 1781 }); 1782 } 1783 }); 1784}); 1785 1786// Titre avec lien => Tout le contenu est actif ! 1787// Mais il ne faut pas empêcher les autres liens de fonctionner... 1788jQuery(function ($) { 1789 'use strict'; 1790 1791 $('.content-container .csc-header a').each(function (i, header_link) { 1792 if (header_link.href) { 1793 var $content = $content = $(header_link).parents('.content-container').first(), 1794 makeClickHandler = function (header_link) { 1795 return function (event) { 1796 // Notice: we do not prevent default! 1797 if (!$(event.target).is('a[href],button,input')) { 1798 // Too bad we can not safely make a synthetic click event on the header_link... 1799 if (header_link.target) { 1800 window.open(header_link.href, header_link.target); 1801 } else { 1802 window.location.href = header_link.href; 1803 } 1804 } 1805 }; 1806 }; 1807 1808 if ($content.length === 1) { 1809 $content.css({ cursor: 'pointer' }); 1810 $content.on('click', makeClickHandler(header_link)); 1811 } 1812 } 1813 }); 1814}); 1815 1816// Typographic sugars... 1817jQuery(function ($) { 1818 'use strict'; 1819 $('h1 *,h2 *,h3 *,h4 *,h5 *, h6 *,h1,h2,h3,h4,h5,h6').each(function (i, h) { 1820 var htm = $(h).html(), 1821 rtest = /(^|\s| )([0-9]+)(st|nd|rd|th|er|re|de|d|e)(\s| |$)/, 1822 rrepl = /^([0-9]+)(st|nd|rd|th|er|re|de|d|e)$/g, 1823 chunks, n; 1824 1825 if (htm.match(rtest)) { 1826 chunks = htm.split(/(\s| )/g); 1827 for (n = 0; n < chunks.length; n++) { 1828 chunks[n] = chunks[n].replace(rrepl, '$1<sup>$2</sup>'); 1829 } 1830 $(h).html(chunks.join('')); 1831 } 1832 }); 1833}); 1834 1835// Répertoire : expand/collapse group items 1836jQuery(function ($) { 1837 'use strict'; 1838 $('.udemwww-repertoire-group').each(function () { 1839 var $group = $(this), 1840 $expand_btns = $group.find('.udemwww-repertoire-more-btn'); 1841 if ($expand_btns.length < 1) { 1842 // No "more" button? Do not hide items!... 1843 return; 1844 } 1845 $group.addClass('udemwww-repertoire-hide-items'); 1846 $expand_btns.on('click', function (event) { 1847 event.preventDefault(); 1848 $group.toggleClass('udemwww-repertoire-hide-items'); 1849 }); 1850 }); 1851}); 1852 1853// TODO This looks like dead/unfinished code! 1854jQuery(function ($) { 1855 'use strict'; 1856 var selector = [
1857 '.col-md-8:nth-child(odd) > .CType-textpic.lyt-3:nth-child(odd) .csc-header', 1858 '.col-md-6:nth-child(odd) > .CType-textpic.lyt-3:nth-child(odd) .csc-header', 1859 '.col-md-4:nth-child(odd) > .CType-textpic.lyt-3:nth-child(odd) .csc-header', 1860 '.col-md-8:nth-child(even) > .CType-textpic.lyt-3:nth-child(even) .csc-header', 1861 '.col-md-6:nth-child(even) > .CType-textpic.lyt-3:nth-child(even) .csc-header', 1862 '.col-md-4:nth-child(even) > .CType-textpic.lyt-3:nth-child(even) .csc-header' 1863 ].join(','); 1864 1865 $(selector).each(function () { 1866 var $block_portrait_header = $(this), 1867 $container = $block_portrait_header.parents('.content-container[data-udemwww-bgcoloropacity]').first(); 1868 1869 if ($container.attr('data-udemwww-bgcoloropacity')) { 1870 // $block_portrait_header.css({ }); 1871 1872 // var bgc = $block_portrait_header.css('backgroundColor'); 1873 // TODO 1874 // bgc = bgc.replace(/^rgb\(/, 'rgba(').replace(/\)/, ', ' + ($container.attr('data-udemwww-bgcoloropacity') / 100) + ')') 1875 // $block_portrait_header.css({'backgroundColor': bgc}); 1876 1877 // TODO if bgc luminance < 40%, color: '#000' 1878 } 1879 }); 1880}); 1881 1882jQuery(function ($) { 1883 'use strict'; 1884 1885 $('.content-container[data-udemwww-bgcoloropacity]').each(function () { 1886 var $content = $(this), 1887 bgcoloropacity = $content.attr('data-udemwww-bgcoloropacity'), 1888 bgcolor = $content.css('backgroundColor'), 1889 m; 1890 1891 bgcoloropacity = parseInt(bgcoloropacity, 10) / 100; 1892 1893 if (bgcolor === 'transparent') { 1894 bgcolor = 'rgba(0,0,0,0)'; 1895 } 1896 1897 m = bgcolor.match(/^(rgb|hsl)\s*\(\s*([0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*)\)/); 1898 if (m) { 1899 $content.attr('data-udemwww-original-bgcolor', bgcolor); 1900 $content.css({ 'backgroundColor': m[1] + 'a(' + m[2] + ', ' + bgcoloropacity + ')' }); 1901 return; 1902 } 1903 1904 m = bgcolor.match(/^(rgb|hsl)a\s*\(\s*([0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*),\s*[0-9]+\s*\)/); 1905 if (m) { 1906 $content.attr('data-udemwww-original-bgcolor', bgcolor); 1907 $content.css({ 'backgroundColor': m[1] + 'a(' + m[2] + ', ' + bgcoloropacity + ')' }); 1908 return; 1909 } 1910 1911 m = bgcolor.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/); 1912 if (m) { 1913 // console.log('MATCH #hex: ', m); 1914 // console.log('TODO!'); 1915 } 1916 }); 1917}); 1918 1919// Bloc synthèse 1920jQuery(function ($) { 1921 'use strict'; 1922 1923 var $blocks = $('.CType-text.lyt-8,.CType-textpic.lyt-8'); 1924 1925 function fix_bloc_position($bloc) { 1926 var $content = $bloc.children('.bloc-synthese-content'), 1927 $col = $bloc.parent(), 1928 $row = $col.parent(); 1929 1930 if ($row.hasClass('row') && $col.css('position') === 'relative') { 1931 let fix_margin_left = $col.offset().left - $row.offset().left, 1932 fix_content_width = $row.outerWidth(true); 1933 1934 if ($content.css('position') === 'absolute') { 1935 $content.css({ 1936 marginLeft: -fix_margin_left + 'px', 1937 width: fix_content_width + 'px' 1938 }); 1939 } else { 1940 $content.css({ 1941 marginLeft: 0, 1942 width: '100%' 1943 }); 1944 } 1945 } else { 1946 // Not in ".row > .col-*" 1947 if ($content.css('position') === 'absolute') { 1948 $content.css({ width: $col.width() }); 1949 } 1950 } 1951 } 1952 1953 $(window).on('resize', udem_www.debounce(function () { 1954 $blocks.filter('.udem-bloc-synthese-expanded').each(function () { 1955 fix_bloc_position($(this)); 1956 }); 1957 }, 300)); 1958 1959 $blocks.each(function () { 1960 var $bloc = $(this), 1961 $header = $bloc.find('> .csc-header').first(), 1962 $trigger = $('<div class="bloc-synthese-trigger"></div>'), 1963 $content = $('<div class="udemtheme-blanc bloc-synthese-content"></div>'), 1964 $close = $('<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>'); 1965 1966 $trigger.append($header.find('h1,h2,h3,h4,h5,h6').first().clone()); 1967 if ($trigger.find('[class^="udem-icon-"]').length === 0) { 1968 $trigger.children('h1,h2,h3,h4,h5,h6').first().prepend('<span class="udem-icon-ico-plus hidden-md hidden-lg smaller-icon"></span>'); 1969 } 1970 1971 $content.append($close); 1972 $content.append($bloc.children()); 1973
1974 $content.prependTo($bloc); 1975 $trigger.prependTo($bloc); 1976 1977 $close.on('click', function (event) { 1978 $bloc.removeClass('udem-bloc-synthese-expanded'); 1979 }); 1980 1981 $trigger.css({ cursor: 'pointer' }); 1982 $trigger.on('click', function (event) { 1983 var win_scrollY; 1984 event.preventDefault(); 1985 $blocks.not($bloc).removeClass('udem-bloc-synthese-expanded'); 1986 $bloc.toggleClass('udem-bloc-synthese-expanded'); 1987 if ($bloc.hasClass('udem-bloc-synthese-expanded')) { 1988 fix_bloc_position($bloc); 1989 1990 // Scroll to content if not two third of it at least is not visible, or if top of the element is above viewport 1991 win_scrollY = typeof window.pageXOffset === 'undefined' ? document.documentElement.scrollTop : window.pageYOffset; 1992 if ( 1993 ($content.offset().top - win_scrollY - $(window).height()) > $content.height() * -0.66 || 1994 $content.offset().top < win_scrollY 1995 ) { 1996 udem_www.scrollToElement($trigger.get(0), $('#udemwww-main-toolbar').height()); 1997 } 1998 } 1999 }); 2000 }); 2001}); 2002 2003jQuery(function ($) { 2004 'use strict'; 2005 2006 // Returns the element background image size (async, returns 0 if o image or on error). 2007 // WARNING: Cached (in data-udemwww-bgimg-height attribute) for performance reasons 2008 function getBackgroundImageHeight($element, callback) { 2009 var cached, url, img; 2010 2011 cached = $element.attr('data-udemwww-bgimg-height'); 2012 if (typeof cached !== 'undefined') { 2013 callback(parseFloat(cached)); 2014 return; 2015 } 2016 2017 url = $element.css('background-image'); 2018 if (!url) { 2019 callback(0); 2020 return; 2021 } 2022 2023 url = url.match(/^url\("?(.+?)"?\)$/); 2024 if (!url || url.length !== 2 || !url[1]) { 2025 callback(0); 2026 return; 2027 } 2028 url = url[1]; 2029 2030 img = new Image(); 2031 img.onload = function () { 2032 if (typeof this.height !== 'undefined') { 2033 $element.attr('data-udemwww-bgimg-height', this.height); 2034 callback(this.height); 2035 } else { 2036 callback(0); 2037 } 2038 }; 2039 img.onerror = function () { 2040 callback(0); 2041 }; 2042 img.src = url; 2043 } 2044 2045 function parallax_update() { 2046 $('.parallax').each(function () { 2047 var $element = $(this), 2048 parallax_scale = $element.attr('data-udemwww-parallax-scale'), 2049 viewport_height = $(window).height(), 2050 element_height = $element.height(), 2051 element_offset_top = $element.offset().top, 2052 viewport_scrollY = typeof window.pageYOffset === 'undefined' 2053 ? (document.documentElement || document.body.parentNode || document.body).scrollTop 2054 : window.pageYOffset, 2055 etv = element_offset_top - viewport_scrollY; 2056 2057 // Case where we can abort early: element is not in viewport! 2058 if (etv < -element_height || etv > viewport_height) { 2059 // console.log('PARALLAX ABORT: OUT OF VIEWPORT!', $element, etv, element_height, viewport_height); 2060 return; 2061 } 2062 2063 if (parallax_scale) { 2064 parallax_scale = parseFloat(parallax_scale); 2065 parallax_scale = isNaN(parallax_scale) ? 1.0 : parallax_scale / 100; 2066 } 2067 if (!parallax_scale) { 2068 parallax_scale = 1.0; 2069 } 2070 2071 getBackgroundImageHeight($element, function (bgimg_height) { 2072 var parallax_ratio; 2073 2074 if (!bgimg_height) { 2075 // console.log(' PARALLAX ABORT: BGIMG_HEIGHT IS FALSY: ', $element, bgimg_height); 2076 return; 2077 } 2078 2079 parallax_ratio = (etv + element_height) / (viewport_height + element_height); 2080 2081 if (parallax_scale < 0) { 2082 parallax_ratio = (1 - parallax_ratio) / -parallax_scale; 2083 } else if (parallax_scale > 0) { 2084 parallax_ratio = parallax_ratio / parallax_scale; 2085 } 2086 2087 $element.css({ 2088 'background-position': '50% ' + (parallax_ratio * 100) + '%' 2089 }); 2090 }); 2091 }); 2092 } 2093 2094 $(window).on('scroll resize load', udem_www.debounce(parallax_update, 16)); 2095}); 2096 2097jQuery(function ($) { 2098 'use strict'; 2099 2100 $('.udemwww-portraits').each(function () { 2101 var $container = $(this), 2102 initial_count = parseInt($container.attr('data-udemwww-portraits-initial-count'), 10) || 10, 2103 $columns = $container.children(), 2104 lang = $('html').attr('lang') === 'en' ? 'en' : 'fr', 2105 $bottom, 2106 $trigger; 2107 2108 if ($columns.children().length <= initial_count) { 2109 return; 2110 } 2111 2112 $trigger = $('<button></button>').append($('<span></span>').text(lang === 'en' ? 'Show more portraits' : 'Voir plus de portraits'));
2113 $bottom = $('<div></div>').addClass('udemwww-portraits__bottom').append($trigger).insertAfter($container); 2114 2115 function show_next_page() { 2116 var $not_visibles = $columns.children().not(':visible'), 2117 n_cols = $columns.length; 2118 2119 if ($not_visibles.length <= initial_count) { 2120 $not_visibles.fadeIn(); 2121 $bottom.fadeOut(); 2122 return; 2123 } 2124 2125 $columns.each(function (c, column) { 2126 $(column).children().each(function (p, elm) { 2127 $(elm).data('order', p + (c / n_cols)); 2128 }); 2129 }); 2130 2131 $not_visibles.sort(function (a, b) { 2132 var aw = $(a).data('order'), 2133 bw = $(b).data('order'); 2134 return aw - bw; 2135 }); 2136 2137 $not_visibles = $not_visibles.slice(0, initial_count); 2138 2139 $not_visibles.fadeIn(); 2140 2141 if ($columns.children().not(':visible').length === 0) { 2142 $bottom.fadeOut(); 2143 } 2144 } 2145 2146 $columns.children().hide(); 2147 show_next_page(); 2148 2149 $trigger.on('click', show_next_page); 2150 }); 2151}); 2152 2153jQuery(function ($) { 2154 'use strict'; 2155 2156 // Transforms links with the "embed-as-audio" class. (CTTYPO3-1467) 2157 $('a.embed-as-audio').each(function () { 2158 let $a = $(this), 2159 src = this.href, 2160 desc = $a.text(), 2161 $div = $('<div class="embed-as-audio" controls></div>'), 2162 $iframe, 2163 $audio; 2164 2165 if (src.match(/^https:\/\/soundcloud.com\//)) { 2166 $iframe = $('<iframe width="100%" height="166" scrolling="no" frameborder="no"></iframe>'); 2167 $iframe.attr('src', 'https://w.soundcloud.com/player/?url=' + window.encodeURIComponent(src)); 2168 $iframe.appendTo($div); 2169 } else { 2170 $audio = $('<audio controls></audio>').attr('src', src); 2171 $audio.appendTo($div); 2172 } 2173 2174 $a.before($div); 2175 2176 if (desc) { 2177 $('<div class="embeded-audio-description"></div>').text(desc).insertAfter($div); 2178 } 2179 2180 $a.remove(); 2181 }); 2182}); 2183 2184jQuery(function ($) { 2185 'use strict'; 2186 2187 // Transforms links with the "embed-as-video" class. (CTTYPO3-1462) 2188 // Some browsers (guess which ones!) do no support RegExp named groups... 2189 // Too bad. It could have been implemented in a more generic fashion. 2190 $('a.embed-as-video').each(function () { 2191 let a = $(this), 2192 src = this.href, 2193 desc = a.text(), 2194 div = $('<div class="embed-as-video embed-responsive embed-responsive-16by9"></div>'), 2195 /* eslint-disable no-template-curly-in-string */ 2196 sites = [ 2197 { 2198 regexp: /^(?:https?:\/\/|\/\/)?(?:www\.|m\.)?(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\/watch\?.+&v=))([\w-]+)/, 2199 src: 'https://www.youtube.com/embed/${0}?rel=0&showinfo=0' 2200 }, 2201 { 2202 regexp: /^(?:https?:\/\/)?(?:www\.)?(?:vimeo\.com)\/([-a-zA-Z0-9_]+)/, 2203 src: 'https://player.vimeo.com/video/${0}?portrait=0&byline=0' 2204 }, 2205 { 2206 regexp: /^(?:https?:\/\/)?(?:(?:www\.)?dailymotion\.(?:com|alice\.it)\/(?:(?:[^"]*?)?video|swf)|dai\.ly)\/([a-z0-9]+)/, 2207 src: 'https://www.dailymotion.com/embed/video/${0}' 2208 }, 2209 { 2210 regexp: /^(?:https?:\/\/)?(?:www\.)?(?:archive\.org)\/(?:details|embed)\/([-a-zA-Z0-9_]+)/, 2211 src: 'https://archive.org/embed/${0}' 2212 } 2213 ]; 2214 /* eslint-enable no-template-curly-in-string */ 2215 2216 sites.every(function (element) { 2217 let match = src.match(element.regexp); 2218 if (match) { 2219 /* eslint-disable-next-line no-template-curly-in-string */ 2220 src = element.src.replace('${0}', match[1]); 2221 } 2222 }); 2223 2224 div.append($('<iframe class="embed-responsive-item" frameborder="0" allowfullscreen="" width="640" height="360"></iframe>').attr('src', src)); 2225 a.before(div); 2226 2227 if (desc) { 2228 $('<div class="embeded-video-description"></div>').text(desc).insertAfter(div); 2229 } 2230 2231 a.remove(); 2232 }); 2233}); 2234 2235jQuery(function ($) { 2236 'use strict'; 2237 // Adds the "apply-overflow-x-hidden" class to ".gbl-udemwww_strate" elements 2238 // that have at least one ".csc-text-text > blockquote" element. 2239 // @see comment around CSS class definition (in "udem_www_main.less") 2240 $('.csc-text-text > blockquote').each(function () { 2241 $(this).parents('.gbl-udemwww_strate').addClass('apply-overflow-x-hidden'); 2242 }); 2243}); 2244 2245// Text & Image with mid overflow above, center image orientation. 2246jQuery(function ($) { 2247 'use strict'; 2248 2249 function set_margintop() { 2250 var $blocks = $('.CType-textpic.imageorient-214'); 2251 $blocks.each(function () { 2252 var $imgcontainer = $(this).find('.csc-textpic-midoverflowabove .csc-textpic-imagewrap'), 2253 $outerwrap = $(this).find('.midoverflowabove-outerwrap'), 2254 overflow = ($imgcontainer.outerHeight() / 2) + 2255 parseFloat($outerwrap.css('padding-top')) + 2256 parseFloat($outerwrap.css('border-top-width')); 2257 $(this).css('padding-top', overflow + 'px'); 2258 $imgcontainer.css('margin-top', -overflow + 'px'); 2259 }); 2260 } 2261 2262 $(window).on('resize scroll scrollstop', set_margintop); 2263 set_margintop(); 2264}); 2265 2266// Handles click on the "Share by email" in News plugins 2267jQuery(function ($) { 2268 'use strict'; 2269 $('.ltp-news_pi1 a.share_email').on('click', function (event) { 2270 var $this = $(this), 2271 subject = $this.attr('data-subject'), 2272 body = $this.attr('data-body'); 2273 console.log('subject, body: ', subject, body); 2274 event.preventDefault(); 2275 window.location.href = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); 2276 }); 2277}); 2278 2279// Animate on scroll (AOS) 2280jQuery(function ($) { 2281 'use strict'; 2282 2283 if (typeof AOS !== 'object') { 2284 return; 2285 } 2286 2287 // TODO use udemInViewport instead of AOS observer 2288 AOS.init(); 2289}); 2290 2291// La fonte "Lora" a une grande queue sous les Q 2292jQuery(function ($) { 2293 'use strict'; 2294 2295 $('span.lettrine').each((i, elm) => { 2296 if (elm.textContent === 'Q') { 2297 elm.style.paddingBottom = '.166666em'; 2298 } 2299 }); 2300});
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.