1// source --> /wp-content/themes/sae-2021/resources/js/toast.script.js 2(function(){ 3 "use strict"; 4 $.Toast = function(title, message, type, options){ 5 var defaultOptions = { 6 appendTo: "body", 7 stack: false, 8 position_class: "toast-bottom-right", 9 fullscreen:false, 10 width: 250, 11 spacing:20, 12 timeout: 4000, 13 has_close_btn:true, 14 has_icon:true, 15 sticky:false, 16 border_radius:6, 17 has_progress:false, 18 rtl:false 19 } 20 21 var $element = null; 22 23 var $options = $.extend(true, {}, defaultOptions, options); 24 25 var spacing = $options.spacing; 26 27 var css = { 28 "position":($options.appendTo == "body") ? "fixed" : "absolute", 29 "min-width":$options.width, 30 "display":"none", 31 "border-radius":$options.border_radius, 32 "z-index":99999 33 } 34 35 $element = $('<div class="toast-item-wrapper ' + type + ' ' + $options.position_class + '"></div>'); 36 $('<p class="toast-title">' + title + '</p>').appendTo($element); 37 $('<p class="toast-message">' + message + '</p>').appendTo($element); 38 39 if($options.fullscreen){ 40 $element.addClass( "fullscreen" ); 41 } 42 43 if($options.rtl){ 44 $element.addClass( "rtl" ); 45 } 46 47 if($options.has_close_btn){ 48 $('<span class="toast-close">×</span>').appendTo($element); 49 if( $options.rtl){ 50 css["padding-left"] = 20; 51 } else { 52 css["padding-right"] = 20; 53 } 54 } 55 56 if($options.has_icon){ 57 $('<i class="toast-icon toast-icon-' + type + '"></i>').appendTo($element); 58 if( $options.rtl){ 59 css["padding-right"] = 50; 60 } else { 61 css["padding-left"] = 50; 62 } 63 } 64 65 if($options.has_progress && $options.timeout > 0){ 66 $('<div class="toast-progress"></div>').appendTo($element); 67 } 68 69 if($options.sticky){ 70 $options.spacing = 0; 71 spacing = 0; 72 73 switch($options.position_class){ 74 case "toast-top-left" : { 75 css["top"] = 0; 76 css["left"] = 0; 77 break; 78 } 79 case "toast-top-right" : { 80 css["top"] = 0; 81 css["left"] = 0; 82 break; 83 } 84 case "toast-top-center" : { 85 css["top"] = 0; 86 css["left"] = css["right"] = 0; 87 css["width"] = "100%"; 88 break; 89 } 90 case "toast-bottom-left" : { 91 css["bottom"] = 0; 92 css["left"] = 0; 93 break; 94 } 95 case "toast-bottom-right" : { 96 css["bottom"] = 0; 97 css["right"] = 0; 98 break; 99 } 100 case "toast-bottom-center" : { 101 css["bottom"] = 0; 102 css["left"] = css["right"] = 0; 103 css["width"] = "100%"; 104 break; 105 } 106 default : { 107 break; 108 } 109 } 110 } 111 112 if($options.stack){ 113 if($options.position_class.indexOf("toast-top") !== -1 ){ 114 $($options.appendTo).find('.toast-item-wrapper').each(function(){ 115 css["top"] = parseInt($(this).css("top")) + this.offsetHeight + spacing; 116 }); 117 } else if($options.position_class.indexOf("toast-bottom") !== -1 ){ 118 $($options.appendTo).find('.toast-item-wrapper').each(function(){ 119 css["bottom"] = parseInt($(this).css("bottom")) + this.offsetHeight + spacing; 120 }); 121 } 122 } 123 124 $element.css(css); 125 126 $element.appendTo($options.appendTo); 127 128 if($element.fadeIn) { 129 $element.fadeIn(); 130 }else { 131 $alert.css({display: 'block', opacity: 1}); 132 } 133 134 function removeToast(){ 135 $.Toast.remove( $element ); 136 } 137 138 if($options.timeout > 0){ 139 setTimeout(removeToast, $options.timeout); 140 if($options.has_progress){ 141 $(".toast-progress", $element).animate({"width":"100%"}, $options.timeout); 142 } 143 } 144 145 $(".toast-close", $element).click(removeToast) 146 147 return $element; 148 } 149 150 $.Toast.remove = function( $element ){ 151 "use strict"; 152 if($element.fadeOut) 153 { 154 $element.fadeOut(function(){ 155 return $element.remove(); 156 }); 157 } 158 else{ 159 $element.remove(); 160 } 161 } 162})(); 163// source --> /wp-content/themes/sae-2021/resources/js/core.js?v=188 164/** 165 * Extract the value of an url parameter 166 * @param {string} parameterName parameter to be matched 167 * @param {string} url (optional) url to scan for get parameter. Defaults to 'location.search' if not provided 168 * @returns {string} parameterValue if parameter is set (ex. ?foo=bar) OR 169 * @returns {string} ''(empty string) if parameterValue is empty (ex. ?foo=) OR 170 * @returns {object} null if parameterValue is not set (ex. ?foo) OR 171 * @returns {bool} false if parameter is not set (ex. ?) 172 */ 173function getValueOfUrlParameter(parameterName, url = location.search){ 174 parameterName = parameterName.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]")
175 let regexSearch = "[\\?&]" + parameterName + "=([^&#]*)" 176 let regex = new RegExp(regexSearch) 177 if(parameter = regex.exec(url)){ 178 return decodeURIComponent(parameter[1]) 179 } 180 regexSearch = "[\\?&]" + parameterName + "([^&#]*)" 181 regex = new RegExp(regexSearch) 182 parameter = regex.exec(url) 183 return parameter && parameterName === parameter[0].slice(1) ? null : false 184} 185function getCookie(cname) { 186 let name = cname + "="; 187 let ca = document.cookie.split(';'); 188 for(let i = 0; i < ca.length; i++) { 189 let c = ca[i]; 190 while (c.charAt(0) == ' ') { 191 c = c.substring(1); 192 } 193 if (c.indexOf(name) == 0) { 194 return c.substring(name.length, c.length); 195 } 196 } 197 return ""; 198} 199/** 200 * 201 * @param {Element} element â The DOM node to scroll to 202 * @param {Number} offsetTopY â Optional offset between 203 * 204 */ 205function scrollAnimatedToElement(element, offsetTopY){ 206 offsetTopY = (typeof offsetTopY !== 'undefined') ? offsetTopY : 50; 207 var headerHeight = document.querySelector('.header-container').offsetHeight; 208 $('html,body').stop().animate({scrollTop: $(element).offset().top - headerHeight - offsetTopY}); 209} 210 211function addParameterStringToUrlSave(targetUrl, stringToAdd){ 212 if(['', '?', '&'].indexOf(stringToAdd) === -1){ 213 if(['?', '&'].indexOf(stringToAdd[0]) !== -1){ 214 stringToAdd = stringToAdd.slice(1); 215 } 216 return (targetUrl.indexOf('?') === -1) ? targetUrl + '?' + stringToAdd : targetUrl + '&' + stringToAdd; 217 } 218 return targetUrl; 219} 220 221// get it from the DEV Menu in the gate mapping 222// https://docs.google.com/spreadsheets/d/1uCscd6X7Gwq9cgCQ51iA3p2SD7U0DD9v1GHL1gwfXe4/edit 223// v2.1. 224const gateMap = { 225 "Audio & Music": ["Audio Engineering", "Audio Engineering Diploma", "Audio Engineering Advanced Diploma", "BA/BSc (Hons) Audio Production", "Electronic Music Production Certificate", "Hip Hop & RnB Producer Certificate", "Home Studio Recording Certificate", "Songwriting Certificate", "Music Business", "Music Business Diploma", "Music Business Advanced Diploma", "BA/BSc (Hons) Music Business", "Creative Industries", "Event Engineering Diploma", "Event Engineering", "Event Engineering Advanced Diploma", "BA/BSc (Hons) Event Engineering", "SQQ7 Certificate", "Voice Acting", "Voice Acting Diploma", "Voice", "Music Production", "Sound Selling Certificate", "DJ Kurs", "Music Production & Sound Design", "Artist & Media Management", "Songwriting", "Popular Music Performance"], 226 "Audio Engineering": ["Audio Engineering Diploma", "Audio Engineering Advanced Diploma", "BA/BSc (Hons) Audio Production", "Electronic Music Production Certificate", "Hip Hop & RnB Producer Certificate", "Home Studio Recording Certificate", "Songwriting Certificate", "Music Production", "DJ Kurs", "Music Production & Sound Design", "Songwriting", "Popular Music Performance"], 227 "Media Business & Marketing": ["Audio Engineering", "Songwriting Certificate", "Music Business", "Music Business Diploma", "Music Business Advanced Diploma", "BA/BSc (Hons) Music Business", "Webdesign & Development", "Webdesign & Development Diploma", "Webdesign & Development Advanced Diploma", "BA/BSc (Hons) Web Development", "Content Creation & Online Marketing", "Designsysteme in Tailwind CSS Certificate", "Content Creation & Online Marketing Diploma", "Content Creation & Online Marketing Advanced Diploma", "BA/BSc (Hons) Content Creation and Online Marketing", "Voice Acting", "Voice Acting Diploma", "Voice", "Graphic Design", "Graphic Design Diploma", "Design", "Graphic Design Advanced Diploma", "BA/BSc (Hons) Graphic Design", "Design Essentials Certificate", "Fotografie Kurzkurs", "Grafik Design", "Social Media Manager", "Sound Selling Certificate", "Creative Photography & AI Technologies", "Film Production", "2D Animation & Storytelling", "Artist & Media Management"], 228 "Music Business": ["Music Business Diploma", "Music Business Advanced Diploma", "BA/BSc (Hons) Music Business", "Sound Selling Certificate"], 229 "Film & Visual Arts": ["Creative Industries", "Event Engineering Diploma", "Event Engineering", "Event Engineering Advanced Diploma", "BA/BSc (Hons) Event Engineering", "SQQ7 Certificate", "Game Art & 3D Animation", "Game Art & 3D Animation Diploma", "Game Art & 3D Animation Advanced Diploma", "BA/BSc (Hons) Game Art Animation", "Game Design Certificate", "Game Creature Creation Certificate", "3D Printing Certificate", "Content Creation & Online Marketing", "Content Creation & Online Marketing Diploma", "Content Creation & Online Marketing Advanced Diploma", "BA/BSc (Hons) Content Creation and Online Marketing", "Graphic Design", "Graphic Design Diploma", "Design", "Graphic Design Advanced Diploma", "BA/BSc (Hons) Graphic Design", "Design Essentials Certificate", "Film Production", "Film Production Diploma", "Film Production Advanced Diploma", "BA/BSc (Hons) Film Production", "DaVinci Resolve Kompaktkurs Certificate", "DaVinci Resolve End User Certificate", "Certified Drone Pilot Certificate", "Music Video Production Certificate", "Video Production Certificate", "Animation & Visual Effects", "Visual FX & 3D Animation Diploma", "Animation & Visual Effects Diploma", "Visual FX & 3D Animation Advanced Diploma", "Animation & Visual Effects Advanced Diploma", "BA/BSc (Hons) Visual Effects Animation", "3D Animation Certificate", "Visual Motion Design", "Fotografie Kurzkurs", "Grafik Design", "Digital Designer", "Game Artist", "Indie Game Developer", "3D Production", "Blender Kurzkurs", "KI-Integriertes DrehbuchÂschreiben Certificate", "Creative Photography & AI Technologies", "2D Animation & Storytelling"], 230 "Creative Industries": ["Event Engineering Diploma", "Event Engineering Advanced Diploma", "BA/BSc (Hons) Event Engineering", "SQQ7 Certificate"], 231 "Event Engineering": ["Event Engineering Diploma", "Event Engineering Advanced Diploma", "BA/BSc (Hons) Event Engineering", "SQQ7 Certificate"], 232 "Coding & Technology": ["Creative Technologies", "Software Engineering Diploma", "Software Engineering", "Software Engineering Advanced Diploma", "BA/BSc (Hons) Software Engineering", "Webdesign & Development", "Webdesign & Development Diploma", "Webdesign & Development Advanced Diploma", "BA/BSc (Hons) Web Development", "Content Creation & Online Marketing", "Designsysteme in Tailwind CSS Certificate", "Games Programming", "Games Programming Diploma", "Game Development", "Game Development Diploma", "Games Programming Advanced Diploma", "Game Development Advanced Diploma", "BSc (Hons) Games Programming", "Web Design Grundlagen", "Games Programming Zertifikat", "Digital Designer"], 233 "Creative Technologies": ["Software Engineering Diploma", "Software Engineering Advanced Diploma", "BA/BSc (Hons) Software Engineering"], 234 "Software Engineering": ["Software Engineering Diploma", "Software Engineering Advanced Diploma", "BA/BSc (Hons) Software Engineering"], 235 "Webdesign & Development": ["Webdesign & Development Diploma", "Webdesign & Development Advanced Diploma", "BA/BSc (Hons) Web Development", "Web Design Grundlagen"], 236 "Content Creation & Online Marketing": ["Designsysteme in Tailwind CSS Certificate", "Content Creation & Online Marketing Diploma", "Content Creation & Online Marketing Advanced Diploma", "BA/BSc (Hons) Content Creation and Online Marketing", "Design Essentials Certificate", "Fotografie Kurzkurs", "Grafik Design", "Digital Designer", "Social Media Manager", "Creative Photography & AI Technologies"], 237 "Games & Animation": ["Game Art & 3D Animation", "Game Art & 3D Animation Diploma", "Game Art & 3D Animation Advanced Diploma", "BA/BSc (Hons) Game Art Animation", "Zeichnen Certificate", "Game Design Certificate", "Game Creature Creation Certificate", "3D Printing Certificate", "Games Programming", "Games Programming Diploma", "Game Development", "Game Development Diploma", "Games Programming Advanced Diploma", "Game Development Advanced Diploma", "BSc (Hons) Games Programming", "Animation & Visual Effects", "Visual FX & 3D Animation Diploma", "Animation & Visual Effects Diploma", "Visual FX & 3D Animation Advanced Diploma", "Animation & Visual Effects Advanced Diploma", "BA/BSc (Hons) Visual Effects Animation", "3D Animation Certificate", "Visual Motion Design", "Games Programming Zertifikat", "Game Artist", "Indie Game Developer", "3D Production", "Blender Kurzkurs"], 238 "Game Art & 3D Animation": ["Game Art & 3D Animation Diploma", "Game Art & 3D Animation Advanced Diploma", "BA/BSc (Hons) Game Art Animation", "Zeichnen Certificate", "Game Design Certificate", "Game Creature Creation Certificate", "3D Printing Certificate", "Game Artist", "Indie Game Developer"], 239 "Games Programming": ["Games Programming Diploma", "BSc (Hons) Games Programming", "Games Programming Zertifikat"], 240 "Game Development": ["Game Development Diploma", "Games Programming Advanced Diploma", "Game Development Advanced Diploma", "BSc (Hons) Games Programming"], 241 "Voice Acting": ["Voice Acting Diploma"], 242 "Voice": ["Voice Acting Diploma"], 243 "Graphic Design": ["Graphic Design Diploma", "Graphic Design Advanced Diploma", "BA/BSc (Hons) Graphic Design"], 244 "Design": ["Graphic Design Diploma", "Graphic Design Advanced Diploma", "BA/BSc (Hons) Graphic Design"], 245 "Film Production": ["Film Production Diploma", "Film Production Advanced Diploma", "BA/BSc (Hons) Film Production", "DaVinci Resolve Kompaktkurs Certificate", "DaVinci Resolve End User Certificate", "Certified Drone Pilot Certificate", "Music Video Production Certificate", "Video Production Certificate", "KI-Integriertes DrehbuchÂschreiben Certificate", "2D Animation & Storytelling"], 246 "Animation & Visual Effects": ["Visual FX & 3D Animation Diploma", "Animation & Visual Effects Diploma", "Visual FX & 3D Animation Advanced Diploma", "Animation & Visual Effects Advanced Diploma", "BA/BSc (Hons) Visual Effects Animation", "3D Animation Certificate", "Visual Motion Design", "3D Production", "Blender Kurzkurs"], 247 "Medienvorkurs": ["Medienvorkurs"], 248 "Music Production & Sound Design": ["Music Production & Sound Design"], 249 "Artist & Media Management": ["Artist & Media Management"] 250} 251 252
253function getLocaleStringBasedOnCountryData( blogId, language ){ 254 language = ( language !== undefined ) ? language : '' 255 const countryData = { 256 '2' : 'DE', 257 '9' : 'IT', 258 '21' : 'NL', 259 '23' : 'AT', 260 '26' : 'CH', 261 } 262 return ( typeof countryData[ blogId ] !== 'undefined' && language !== '' ) ? language + '_' + countryData[ blogId ] : false 263} 264 265/* SAE-5580 */ 266 267function expandTextIntroPreview( triggerElement ) { 268 let rootContainer = triggerElement.closest( '.text_intro-container' ) 269 270 if( triggerElement.type === 'button' ) { 271 triggerElement.parentElement.removeChild( triggerElement ) 272 let textIntro = rootContainer.querySelector( '.text_intro-text' ) 273 textIntro.classList.remove( 'preview' ) 274 textIntro.removeAttribute( 'title' ) 275 textIntro.removeAttribute( 'data-click-purpose' ) 276 } 277 else { 278 triggerElement.classList.remove( 'preview' ) 279 triggerElement.removeAttribute( 'title' ) 280 triggerElement.removeAttribute( 'data-click-purpose' ) 281 let expandButton = rootContainer.querySelector( '.h-button--s_icon' ) 282 expandButton.parentElement.removeChild( expandButton ) 283 } 284} 285 286/* / SAE-5580 */ 287 288$(function(){ 289 const url = window.location.href; 290 const paths = window.location.pathname.split('/'); 291 const countryPath = '/' + paths[1]; 292 const languagePath = paths[2].length === 2 ? '/' + paths[2] : ''; 293 const userFirstname = $('body').data('userfirstname'); 294 295 console.log('MODE:', countryPath, languagePath) 296 297 if(parseInt(localStorage.getItem("displayWelcomeToast")) === 1){ 298 var welcomeBackTitle = __ple("Howdy") + ' ' + userFirstname; 299 var welcomeBackMessage = __ple("Welcome back at SAE,") + " " + userFirstname + "!"; 300 301 $.Toast(welcomeBackTitle, welcomeBackMessage, "success", { 302 has_icon: false, 303 has_close_btn: true, 304 stack: true, 305 fullscreen: false, 306 timeout: 18000, 307 sticky: false, 308 has_progress: true, 309 rtl: false, 310 position_class: 'toast-top-right' 311 }); 312 localStorage.setItem("displayWelcomeToast", "0"); 313 } 314 315 if(url.indexOf('permalogin') > 0){ 316 localStorage.setItem("displayWelcomeToast", "1"); 317 window.location.href = window.location.href.split('?')[0]; 318 } 319 320 if(countryPath === '/deu' || countryPath === '/aut' || countryPath === '/che' || countryPath === '/ch-ge'){ 321 if(countryPath !== '/deu'){ 322 $('.um-field-gsacampus').hide(); 323 } 324 325 $('.get_new_login_token_form .h-button').on('click', function(){ 326 $.ajax({ 327 type: 'GET', 328 dataType: 'json', 329 cache: false, 330 async: false, 331 url: 'https://www.sae.edu' + countryPath + languagePath + "?act=resendpermanlink&email=" + $('.email_reset_email').val().replace('+', '%2b'), 332 complete: function(datum){ 333 if(countryPath.toLowerCase() === '/deu'){ 334 window.location.href = 'https://www.sae.edu' + countryPath + "/login-started/"; 335 }else{ 336 alert(__ple('Thank you, if we were able to assign your e-mail address, you will receive your custom login link by e-mail in a few seconds.')); 337 window.location.href = 'https://www.sae.edu' + countryPath + languagePath; 338 } 339 } 340 }); 341 }); 342 343 $('body').attr('data-gsacampusid', countryPath.slice(1)); 344 345 var userId = parseInt($('body').data('userid')); 346 if(userId > 0){ 347 if(countryPath !== '/aut'){ 348 // no enquire modal 349 $('.desktop_navigation_menu-enquire-btn').find('button').remove(); 350 $('.mobile_navigation-actions').find('button').eq(0).remove(); 351 if(countryPath !== '/ch-ge' && languagePath === ''){ 352 const buttonCtaGsa = '<a id="header--book-a-tour-button" class="h-button h-button--v_contained h-button--light" data-url="' + countryPath + '/termin-vereinbaren" href="' + countryPath + '/termin-vereinbaren"><span class="h-button-content">' + __ple("Book appointment") + '</span></a>'; 353 $('.desktop_navigation_menu-enquire-btn').append(buttonCtaGsa); 354 $('.mobile_navigation-actions').prepend(buttonCtaGsa); 355 } 356 } 357 } 358 359 // SAE GSA specific - add url into url-field of registration form 360 $('.um-field.um-field-url.um-field-gsauser_url.um-field-url.um-field-type_url').find('input').val(window.location.href); 361 // SAE GSA specific - add consent into textarea of registration form 362 $consentTextBlock = $('.um-field.um-field-textarea.um-field-gsaconsent_text.um-field-textarea.um-field-type_textarea'); 363 if($consentTextBlock.prev().length){ 364 $consentTextBlock.find('textarea').val($consentTextBlock.prev().text()); 365 } 366 367 // SAE GSA specific - forward tracking cookies into the hidden gsauser_cookies field of the registration form 368 function fillGsauserCookiesField(){ 369 var adStorage = null; 370 if(typeof dataLayer !== 'undefined' && Array.isArray(dataLayer)){ 371 for(var i = dataLayer.length - 1; i >= 0; i--){ 372 var entry = dataLayer[i]; 373 if(entry && entry[0] === 'consent' && entry[2] && typeof entry[2] === 'object' && 'ad_storage' in entry[2]){ 374 adStorage = entry[2].ad_storage; 375 break; 376 } 377 } 378 } 379 380 var cookies = {'_sc_ad_storage': adStorage}; 381 if(adStorage === 'granted'){ 382 var cookieNames = ['_fbc', '_scid_gcl', '_scid_gbr', '_scid_wbr', 'ttclid']; 383 document.cookie.split(';').forEach(function(part){ 384 var eq = part.indexOf('='); 385 if(eq === -1){ 386 return; 387 } 388 var name = part.slice(0, eq).trim(); 389 if(cookieNames.indexOf(name) === -1){ 390 return; 391 } 392 cookies[name] = decodeURIComponent(part.slice(eq + 1)); 393 }); 394 } 395 396 var $cookieField = $('.um-field-gsauser_cookies input'); 397 if($cookieField.length){ 398 $cookieField.val(JSON.stringify(cookies)); 399 } 400 } 401 const buggySubmitInput = document.querySelector('input[type="submit"]'); 402 if(buggySubmitInput){ 403 buggySubmitInput.addEventListener('click', fillGsauserCookiesField); 404 } 405 406 if($('.um-3820').length || $('.um-3821').length || $('.um-12068').length){ // /GSA/de || /GSA/en || /deu/dev 407 // SAE GSA pre-select course on course-pages by discipline 408 if($('.gate_introduction').length || $('.gate_form_introduction').length){ 409 let course = $('.course_hero-svg-icon h4').text().trim(); 410 course = course === 'Audio Engineering / Music Production' ? 'Audio Engineering' : course; 411 if(course && $("#gsacourse option[value='" + course + "']").length > 0){ 412 $('#gsacourse').val(course).trigger('change'); 413 $('.um-field-gsacourse').hide(); 414 } 415 } 416 // SAE Gate pre-select campus 417 let campus = getValueOfUrlParameter('saecampus') || document.referrer.match(/campuses\/(\w*)/) 418 campus = campus && typeof campus == 'object' ? campus[1] : campus 419 campus = campus ? campus[0].toUpperCase() + campus.slice(1).replace('ue', 'ü').replace('oe', 'ö') : false
420 if(campus && $("#gsacampus option[value='" + campus + "']").length > 0){ 421 $('#gsacampus').val(campus).trigger('change'); 422 } 423 424 } 425 // SAE Gate pre-select cluster / discipline / course 426 if($('#gate_controller').length){ 427 let parentValue = false 428 for(key of ['clt', 'dcp', 'crs']){ 429 let val = getValueOfUrlParameter(key) 430 val = val === null ? '' : val 431 val = !val && typeof sae !== 'undefined' && typeof sae.gate !== 'undefined' && typeof sae.gate[key] !== 'undefined' ? sae.gate[key] : val 432 if(val !== false){ 433 val = val.replace(/\s?\+\s?/, ' & ') 434 if($('#gate_controller option[value="' + key + '"]').length > 0){ 435 $('#gate_controller').val(key).trigger('change'); 436 } 437 if($('#' + key + ' option[value="' + val + '"]').length > 0){ 438 $('#' + key).val(val).trigger('change'); 439 if(!parentValue && key == 'crs'){ 440 //$('#crs').prop('disabled', true) /* SAE2030 */ 441 } 442 } 443 if(parentValue){ 444 for(let option of $('#' + key).children()){ 445 if(option.value !== '' && !gateMap[parentValue].includes(option.value)){ 446 option.remove() 447 } 448 } 449 } 450 parentValue = val; 451 } 452 } 453 } 454 455 if(null !== document.getElementById('further-education-detail-page-primary-card-cta')){ 456 (function(furtherEducationDetailPagePrimaryCardCta){ 457 if(null !== document.querySelector('.eventbrite_tiles-section')){ 458 var eventbriteTilesSection = document.querySelector('.eventbrite_tiles-section'); 459 furtherEducationDetailPagePrimaryCardCta.querySelector('span').textContent = 'Kurs buchen'; 460 furtherEducationDetailPagePrimaryCardCta.href = '#'; 461 ['touched', 'click'].forEach(event => { 462 furtherEducationDetailPagePrimaryCardCta.addEventListener(event, function(){ 463 scrollAnimatedToElement(eventbriteTilesSection); 464 event.preventDefault(); 465 }); 466 }); 467 } 468 }(document.getElementById('further-education-detail-page-primary-card-cta'))); 469 } 470 471 /* 472 // deprecated: SAE-2118 473 $('#um-submit-btn').on('click', function(){ 474 formID = $(this).parents('form').children('[name=form_id]').val(); 475 switch(formID){ 476 case '3149': // Registration DE 477 case '3353': // Registration EN 478 window.dataLayer.push({event: 'CourseCTA registration'}); 479 break; 480 case '3820': // Registration on enquire now DE 481 case '3821': // Registration on enquire now EN 482 window.dataLayer.push({event: 'HeaderCTA registration'}); 483 break; 484 } 485 }); 486 */ 487 488 } 489 490 /** 491 * Scroll to a standard form component container if the get parameter 'stf' is not null ( e.g.: ?stf=1 ) 492 */ 493 if(getValueOfUrlParameter('stf') !== null && getValueOfUrlParameter('stf') !== false && $('[data-scroll-form-anchor]').length > 0){ 494 setTimeout(function(){ 495 scrollAnimatedToElement('[data-scroll-form-anchor]', 0); 496 }, 1500); 497 } 498 499 /** 500 * SAE-1981 501 */ 502 let userAccountMenus = Array.from(document.getElementsByClassName('user-account-menu')); 503 if(userAccountMenus.length > 0){ 504 let desktopNavigationMenuUserAccountBackdrop = document.getElementById('desktop_navigation_menu-user-account-backdrop'); 505 let userAccountNavigationTriggers = { 506 'Activation': [ 507 document.getElementById('desktop_navigation_menu-user-account-btn'), 508 document.getElementById('user-logged-in-as-hint'), 509 document.getElementById('mobile_navigation_menu-user-account-btn') 510 ], 511 'Deactivation': [ 512 document.getElementById('desktop_navigation_menu-user-account-backdrop'), 513 document.querySelector('.mobile_navigation_menu-user-account-container [data-trigger=mobile_navigation_menu-user-account-close-btn]') 514 ] 515 } 516 Object.keys(userAccountNavigationTriggers).forEach(function(key){ 517 userAccountNavigationTriggers[key].forEach(function(triggerItem){ 518 triggerItem.addEventListener('click', function(){ 519 userAccountMenus.forEach(function(userAccountMenu){ 520 if(key === 'Activation'){ 521 userAccountMenu.classList.add('user-account-menu-active'); 522 }else if(key === 'Deactivation'){ 523 userAccountMenu.classList.remove('user-account-menu-active'); 524 } 525 }); 526 if(key === 'Activation'){ 527 desktopNavigationMenuUserAccountBackdrop.classList.add('user-account-menu-active'); 528 document.body.classList.add('overflow-hidden'); 529 }else if(key === 'Deactivation'){ 530 desktopNavigationMenuUserAccountBackdrop.classList.remove('user-account-menu-active'); 531 document.body.classList.remove('overflow-hidden'); 532 } 533 }); 534 }); 535 }); 536 } 537 538 /* SAE-2363 */ 539 function updateProgressUi(progressUi){ 540 let body = document.body; 541 let scrollProgress = (window.pageYOffset / (body.scrollHeight - window.innerHeight - document.querySelector('footer.footer-container').offsetHeight)) * 100; 542 if(scrollProgress > 100){ 543 scrollProgress = 100; 544 } 545 progressUi.style = 'width:' + scrollProgress + '%'; 546 } 547 548 function generateProgressUi(config){ 549 let body = document.body; 550 if(config[body.dataset['blogId']]){ 551 let post_id = parseInt(body.dataset['postId']); 552 if(config[body.dataset['blogId']].indexOf(post_id) === -1){ 553 let progress_ui = document.createElement('div'); 554 progress_ui.classList.add('progress-ui'); 555 progress_ui.id = 'progress-ui'; 556 progress_ui = body.appendChild(progress_ui); 557 558 updateProgressUi(progress_ui); 559 window.addEventListener('scroll', function(){ 560 updateProgressUi(progress_ui); 561 }); 562 window.addEventListener('resize', function(){ 563 updateProgressUi(progress_ui); 564 }); 565 } 566 } 567 } 568 569 // sae.progress_ui_data = { 26 : [ 18495 ] } => { {{blog-id}}: [ comma separated list of all post-ids that shall be excluded ] } 570 // add data at Theme Settings > Tracking Scripts > Footer Tracking Scripts 571 // TODO: create backend interface to make sae.progress_ui_data editable for authors 572 if(sae.progress_ui_data){ 573 generateProgressUi(sae.progress_ui_data); 574 } 575 /* /SAE-2363 */ 576 577 578 /* SAE-3122 */ 579 if(countryPath === '/deu' && sae && sae.userData && sae.userData.hash){ 580 const drecksDownloadButton = $('button[data-download-course-structure-url]') 581 drecksDownloadButton.parent().html(drecksDownloadButton.parent().html()).children().on('click', () => { // course page 582 $.ajax({ 583 'url': '/api/wp-wp.php', 584 'data': {user: sae.userData.id, hash: sae.userData.hash, task: 'download', data: 'coursePDF'} 585 }).always(() => { 586 location.href = drecksDownloadButton.data('downloadCourseStructureUrl') 587 }) 588 }) 589 $('.sidepanel_content-fees_download a').on('click', () => { // course page 590 $.ajax({ 591 'url': '/api/wp-wp.php', 592 'data': {user: sae.userData.id, hash: sae.userData.hash, task: 'download', data: 'coursePaymentPlanPDF'} 593 }) 594 }) 595 $('a.paymentPlanPDF').on('click', (event) => { // (gated) payment plan page 596 event.preventDefault() 597 $.ajax({ 598 'url': '/api/wp-wp.php', 599 'data': {user: sae.userData.id, hash: sae.userData.hash, task: 'download', data: 'paymentPlanPDF'} 600 }).always(() => { 601 location.href = event.currentTarget.href 602 }) 603 }) 604 } 605 606 /* SAE-4462 */ 607 (function( blogId, languageCode ) {
608 let correctLocaleString = getLocaleStringBasedOnCountryData( blogId, languageCode ) 609 if( correctLocaleString !== false ) { 610 let htmlNode = document.querySelector( 'html' ) 611 if( htmlNode.getAttribute( 'lang' ) !== correctLocaleString ) { 612 htmlNode.setAttribute( 'lang', correctLocaleString ) 613 } 614 } 615 })( sae.siteData.blogId, sae.siteData.polylangLanguageCode ) 616 /* SAE-4462 */ 617 618 619 /* SAE-5580 */ 620 if( document.querySelectorAll( '.text_intro-section [data-click-purpose=expand-preview]' ).length > 0 ) { 621 let textIntroPreviewTriggers = Array.from( document.querySelectorAll( '.text_intro-section [data-click-purpose=expand-preview]' ) ) 622 textIntroPreviewTriggers.forEach( function( trigger ) { 623 trigger.addEventListener( 'click', function( event ) { 624 expandTextIntroPreview( event.target ) 625 }) 626 }) 627 } 628 /* / SAE-5580 */ 629 630}); 631 632window.onload = function(e){ 633 // set this class for tracking clicks on WA messenger people button 634 setTimeout(function(){ 635 $('.MuiButtonBase-root').find('path').attr('class', 'messengerpeople'); 636 }, 3000); 637 638}; 639// source --> /wp-content/themes/sae-2021/resources/js/urijs.js 640/*! 641 * URI.js - Mutating URLs 642 * 643 * Version: 1.19.10 644 * 645 * Author: Rodney Rehm 646 * Web: http://medialize.github.io/URI.js/ 647 * 648 * Licensed under 649 * MIT License http://www.opensource.org/licenses/mit-license 650 * 651 */ 652(function (root, factory) { 653 'use strict'; 654 // https://github.com/umdjs/umd/blob/master/returnExports.js 655 if (typeof module === 'object' && module.exports) { 656 // Node 657 module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains')); 658 } else if (typeof define === 'function' && define.amd) { 659 // AMD. Register as an anonymous module. 660 define(['./punycode', './IPv6', './SecondLevelDomains'], factory); 661 } else { 662 // Browser globals (root is window) 663 root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root); 664 } 665}(this, function (punycode, IPv6, SLD, root) { 666 'use strict'; 667 /*global location, escape, unescape */ 668 // FIXME: v2.0.0 renamce non-camelCase properties to uppercase 669 /*jshint camelcase: false */ 670 671 // save current URI variable, if any 672 var _URI = root && root.URI; 673 674 function URI(url, base) { 675 var _urlSupplied = arguments.length >= 1; 676 var _baseSupplied = arguments.length >= 2; 677 678 // Allow instantiation without the 'new' keyword 679 if (!(this instanceof URI)) { 680 if (_urlSupplied) { 681 if (_baseSupplied) { 682 return new URI(url, base); 683 } 684 685 return new URI(url); 686 } 687 688 return new URI(); 689 } 690 691 if (url === undefined) { 692 if (_urlSupplied) { 693 throw new TypeError('undefined is not a valid argument for URI'); 694 } 695 696 if (typeof location !== 'undefined') { 697 url = location.href + ''; 698 } else { 699 url = ''; 700 } 701 } 702 703 if (url === null) { 704 if (_urlSupplied) { 705 throw new TypeError('null is not a valid argument for URI'); 706 } 707 } 708 709 this.href(url); 710 711 // resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor 712 if (base !== undefined) { 713 return this.absoluteTo(base); 714 } 715 716 return this; 717 } 718 719 function isInteger(value) { 720 return /^[0-9]+$/.test(value); 721 } 722 723 URI.version = '1.19.10'; 724 725 var p = URI.prototype; 726 var hasOwn = Object.prototype.hasOwnProperty; 727 728 function escapeRegEx(string) { 729 // https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963 730 return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 731 } 732 733 function getType(value) { 734 // IE8 doesn't return [Object Undefined] but [Object Object] for undefined value 735 if (value === undefined) { 736 return 'Undefined'; 737 } 738 739 return String(Object.prototype.toString.call(value)).slice(8, -1); 740 } 741 742 function isArray(obj) { 743 return getType(obj) === 'Array'; 744 } 745 746 function filterArrayValues(data, value) { 747 var lookup = {}; 748 var i, length; 749 750 if (getType(value) === 'RegExp') { 751 lookup = null; 752 } else if (isArray(value)) { 753 for (i = 0, length = value.length; i < length; i++) { 754 lookup[value[i]] = true; 755 } 756 } else { 757 lookup[value] = true; 758 } 759 760 for (i = 0, length = data.length; i < length; i++) { 761 /*jshint laxbreak: true */ 762 var _match = lookup && lookup[data[i]] !== undefined 763 || !lookup && value.test(data[i]); 764 /*jshint laxbreak: false */ 765 if (_match) { 766 data.splice(i, 1); 767 length--; 768 i--; 769 } 770 } 771 772 return data; 773 } 774 775 function arrayContains(list, value) { 776 var i, length; 777 778 // value may be string, number, array, regexp 779 if (isArray(value)) { 780 // Note: this can be optimized to O(n) (instead of current O(m * n)) 781 for (i = 0, length = value.length; i < length; i++) { 782 if (!arrayContains(list, value[i])) { 783 return false;
784 } 785 } 786 787 return true; 788 } 789 790 var _type = getType(value); 791 for (i = 0, length = list.length; i < length; i++) { 792 if (_type === 'RegExp') { 793 if (typeof list[i] === 'string' && list[i].match(value)) { 794 return true; 795 } 796 } else if (list[i] === value) { 797 return true; 798 } 799 } 800 801 return false; 802 } 803 804 function arraysEqual(one, two) { 805 if (!isArray(one) || !isArray(two)) { 806 return false; 807 } 808 809 // arrays can't be equal if they have different amount of content 810 if (one.length !== two.length) { 811 return false; 812 } 813 814 one.sort(); 815 two.sort(); 816 817 for (var i = 0, l = one.length; i < l; i++) { 818 if (one[i] !== two[i]) { 819 return false; 820 } 821 } 822 823 return true; 824 } 825 826 function trimSlashes(text) { 827 var trim_expression = /^\/+|\/+$/g; 828 return text.replace(trim_expression, ''); 829 } 830 831 URI._parts = function() { 832 return { 833 protocol: null, 834 username: null, 835 password: null, 836 hostname: null, 837 urn: null, 838 port: null, 839 path: null, 840 query: null, 841 fragment: null, 842 // state 843 preventInvalidHostname: URI.preventInvalidHostname, 844 duplicateQueryParameters: URI.duplicateQueryParameters, 845 escapeQuerySpace: URI.escapeQuerySpace 846 }; 847 }; 848 // state: throw on invalid hostname 849 // see https://github.com/medialize/URI.js/pull/345 850 // and https://github.com/medialize/URI.js/issues/354 851 URI.preventInvalidHostname = false; 852 // state: allow duplicate query parameters (a=1&a=1) 853 URI.duplicateQueryParameters = false; 854 // state: replaces + with %20 (space in query strings) 855 URI.escapeQuerySpace = true; 856 // static properties 857 URI.protocol_expression = /^[a-z][a-z0-9.+-]*$/i; 858 URI.idn_expression = /[^a-z0-9\._-]/i; 859 URI.punycode_expression = /(xn--)/i; 860 // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care? 861 URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; 862 // credits to Rich Brown 863 // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096 864 // specification: http://www.ietf.org/rfc/rfc4291.txt 865 URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; 866 // expression used is "gruber revised" (@gruber v2) determined to be the 867 // best solution in a regex-golf we did a couple of ages ago at 868 // * http://mathiasbynens.be/demo/url-regex 869 // * http://rodneyrehm.de/t/url-regex.html 870 URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»ââââ]))/ig; 871 URI.findUri = { 872 // valid "scheme://" or "www." 873 start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi, 874 // everything up to the next whitespace 875 end: /[\s\r\n]|$/, 876 // trim trailing punctuation captured by end RegExp 877 trim: /[`!()\[\]{};:'".,<>?«»âââââ]+$/, 878 // balanced parens inclusion (), [], {}, <> 879 parens: /(\([^\)]*\)|\[[^\]]*\]|\{[^}]*\}|<[^>]*>)/g, 880 }; 881 URI.leading_whitespace_expression = /^[\x00-\x20\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/ 882 // http://www.iana.org/assignments/uri-schemes.html 883 // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports 884 URI.defaultPorts = { 885 http: '80', 886 https: '443', 887 ftp: '21', 888 gopher: '70', 889 ws: '80', 890 wss: '443' 891 }; 892 // list of protocols which always require a hostname 893 URI.hostProtocols = [ 894 'http', 895 'https' 896 ]; 897 898 // allowed hostname characters according to RFC 3986 899 // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded 900 // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . - _ 901 URI.invalid_hostname_characters = /[^a-zA-Z0-9\.\-:_]/; 902 // map DOM Elements to their URI attribute 903 URI.domAttributes = { 904 'a': 'href', 905 'blockquote': 'cite', 906 'link': 'href', 907 'base': 'href', 908 'script': 'src', 909 'form': 'action', 910 'img': 'src', 911 'area': 'href', 912 'iframe': 'src', 913 'embed': 'src', 914 'source': 'src', 915 'track': 'src', 916 'input': 'src', // but only if type="image" 917 'audio': 'src', 918 'video': 'src' 919 }; 920 URI.getDomAttribute = function(node) { 921 if (!node || !node.nodeName) { 922 return undefined; 923 } 924 925 var nodeName = node.nodeName.toLowerCase(); 926 // <input> should only expose src for type="image"
927 if (nodeName === 'input' && node.type !== 'image') { 928 return undefined; 929 } 930 931 return URI.domAttributes[nodeName]; 932 }; 933 934 function escapeForDumbFirefox36(value) { 935 // https://github.com/medialize/URI.js/issues/91 936 return escape(value); 937 } 938 939 // encoding / decoding according to RFC3986 940 function strictEncodeURIComponent(string) { 941 // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent 942 return encodeURIComponent(string) 943 .replace(/[!'()*]/g, escapeForDumbFirefox36) 944 .replace(/\*/g, '%2A'); 945 } 946 URI.encode = strictEncodeURIComponent; 947 URI.decode = decodeURIComponent; 948 URI.iso8859 = function() { 949 URI.encode = escape; 950 URI.decode = unescape; 951 }; 952 URI.unicode = function() { 953 URI.encode = strictEncodeURIComponent; 954 URI.decode = decodeURIComponent; 955 }; 956 URI.characters = { 957 pathname: { 958 encode: { 959 // RFC3986 2.1: For consistency, URI producers and normalizers should 960 // use uppercase hexadecimal digits for all percent-encodings. 961 expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig, 962 map: { 963 // -._~!'()* 964 '%24': '$', 965 '%26': '&', 966 '%2B': '+', 967 '%2C': ',', 968 '%3B': ';', 969 '%3D': '=', 970 '%3A': ':', 971 '%40': '@' 972 } 973 }, 974 decode: { 975 expression: /[\/\?#]/g, 976 map: { 977 '/': '%2F', 978 '?': '%3F', 979 '#': '%23' 980 } 981 } 982 }, 983 reserved: { 984 encode: { 985 // RFC3986 2.1: For consistency, URI producers and normalizers should 986 // use uppercase hexadecimal digits for all percent-encodings. 987 expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig, 988 map: { 989 // gen-delims 990 '%3A': ':', 991 '%2F': '/', 992 '%3F': '?', 993 '%23': '#', 994 '%5B': '[', 995 '%5D': ']', 996 '%40': '@', 997 // sub-delims 998 '%21': '!', 999 '%24': '$', 1000 '%26': '&', 1001 '%27': '\'', 1002 '%28': '(', 1003 '%29': ')', 1004 '%2A': '*', 1005 '%2B': '+', 1006 '%2C': ',', 1007 '%3B': ';', 1008 '%3D': '=' 1009 } 1010 } 1011 }, 1012 urnpath: { 1013 // The characters under `encode` are the characters called out by RFC 2141 as being acceptable 1014 // for usage in a URN. RFC2141 also calls out "-", ".", and "_" as acceptable characters, but 1015 // these aren't encoded by encodeURIComponent, so we don't have to call them out here. Also 1016 // note that the colon character is not featured in the encoding map; this is because URI.js 1017 // gives the colons in URNs semantic meaning as the delimiters of path segements, and so it 1018 // should not appear unencoded in a segment itself. 1019 // See also the note above about RFC3986 and capitalalized hex digits. 1020 encode: { 1021 expression: /%(21|24|27|28|29|2A|2B|2C|3B|3D|40)/ig, 1022 map: { 1023 '%21': '!', 1024 '%24': '$', 1025 '%27': '\'', 1026 '%28': '(', 1027 '%29': ')', 1028 '%2A': '*', 1029 '%2B': '+', 1030 '%2C': ',', 1031 '%3B': ';', 1032 '%3D': '=', 1033 '%40': '@' 1034 } 1035 }, 1036 // These characters are the characters called out by RFC2141 as "reserved" characters that 1037 // should never appear in a URN, plus the colon character (see note above). 1038 decode: { 1039 expression: /[\/\?#:]/g, 1040 map: { 1041 '/': '%2F', 1042 '?': '%3F', 1043 '#': '%23', 1044 ':': '%3A' 1045 } 1046 } 1047 } 1048 }; 1049 URI.encodeQuery = function(string, escapeQuerySpace) { 1050 var escaped = URI.encode(string + ''); 1051 if (escapeQuerySpace === undefined) { 1052 escapeQuerySpace = URI.escapeQuerySpace; 1053 } 1054 1055 return escapeQuerySpace ? escaped.replace(/%20/g, ' ') : escaped; 1056 }; 1057 URI.decodeQuery = function(string, escapeQuerySpace) { 1058 string += ''; 1059 if (escapeQuerySpace === undefined) { 1060 escapeQuerySpace = URI.escapeQuerySpace; 1061 } 1062 1063 try { 1064 return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string); 1065 } catch(e) { 1066 // we're not going to mess with weird encodings, 1067 // give up and return the undecoded original string 1068 // see https://github.com/medialize/URI.js/issues/87 1069 // see https://github.com/medialize/URI.js/issues/92 1070 return string; 1071 } 1072 }; 1073 // generate encode/decode path functions 1074 var _parts = {'encode':'encode', 'decode':'decode'}; 1075 var _part; 1076 var generateAccessor = function(_group, _part) { 1077 return function(string) {
1078 try { 1079 return URI[_part](string + '').replace(URI.characters[_group][_part].expression, function(c) { 1080 return URI.characters[_group][_part].map[c]; 1081 }); 1082 } catch (e) { 1083 // we're not going to mess with weird encodings, 1084 // give up and return the undecoded original string 1085 // see https://github.com/medialize/URI.js/issues/87 1086 // see https://github.com/medialize/URI.js/issues/92 1087 return string; 1088 } 1089 }; 1090 }; 1091 1092 for (_part in _parts) { 1093 URI[_part + 'PathSegment'] = generateAccessor('pathname', _parts[_part]); 1094 URI[_part + 'UrnPathSegment'] = generateAccessor('urnpath', _parts[_part]); 1095 } 1096 1097 var generateSegmentedPathFunction = function(_sep, _codingFuncName, _innerCodingFuncName) { 1098 return function(string) { 1099 // Why pass in names of functions, rather than the function objects themselves? The 1100 // definitions of some functions (but in particular, URI.decode) will occasionally change due 1101 // to URI.js having ISO8859 and Unicode modes. Passing in the name and getting it will ensure 1102 // that the functions we use here are "fresh". 1103 var actualCodingFunc; 1104 if (!_innerCodingFuncName) { 1105 actualCodingFunc = URI[_codingFuncName]; 1106 } else { 1107 actualCodingFunc = function(string) { 1108 return URI[_codingFuncName](URI[_innerCodingFuncName](string)); 1109 }; 1110 } 1111 1112 var segments = (string + '').split(_sep); 1113 1114 for (var i = 0, length = segments.length; i < length; i++) { 1115 segments[i] = actualCodingFunc(segments[i]); 1116 } 1117 1118 return segments.join(_sep); 1119 }; 1120 }; 1121 1122 // This takes place outside the above loop because we don't want, e.g., encodeUrnPath functions. 1123 URI.decodePath = generateSegmentedPathFunction('/', 'decodePathSegment'); 1124 URI.decodeUrnPath = generateSegmentedPathFunction(':', 'decodeUrnPathSegment'); 1125 URI.recodePath = generateSegmentedPathFunction('/', 'encodePathSegment', 'decode'); 1126 URI.recodeUrnPath = generateSegmentedPathFunction(':', 'encodeUrnPathSegment', 'decode'); 1127 1128 URI.encodeReserved = generateAccessor('reserved', 'encode'); 1129 1130 URI.parse = function(string, parts) { 1131 var pos; 1132 if (!parts) { 1133 parts = { 1134 preventInvalidHostname: URI.preventInvalidHostname 1135 }; 1136 } 1137 1138 string = string.replace(URI.leading_whitespace_expression, '') 1139 1140 // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment] 1141 1142 // extract fragment 1143 pos = string.indexOf('#'); 1144 if (pos > -1) { 1145 // escaping? 1146 parts.fragment = string.substring(pos + 1) || null; 1147 string = string.substring(0, pos); 1148 } 1149 1150 // extract query 1151 pos = string.indexOf('?'); 1152 if (pos > -1) { 1153 // escaping? 1154 parts.query = string.substring(pos + 1) || null; 1155 string = string.substring(0, pos); 1156 } 1157 1158 // slashes and backslashes have lost all meaning for the web protocols (https, http, wss, ws) 1159 string = string.replace(/^(https?|ftp|wss?)?:+[/\\]*/i, '$1://'); 1160 1161 // extract protocol 1162 if (string.substring(0, 2) === '//') { 1163 // relative-scheme 1164 parts.protocol = null; 1165 string = string.substring(2); 1166 // extract "user:pass@host:port" 1167 string = URI.parseAuthority(string, parts); 1168 } else { 1169 pos = string.indexOf(':'); 1170 if (pos > -1) { 1171 parts.protocol = string.substring(0, pos) || null;
1172 if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) { 1173 // : may be within the path 1174 parts.protocol = undefined; 1175 } else if (string.substring(pos + 1, pos + 3).replace(/\\/g, '/') === '//') { 1176 string = string.substring(pos + 3); 1177 1178 // extract "user:pass@host:port" 1179 string = URI.parseAuthority(string, parts); 1180 } else { 1181 string = string.substring(pos + 1); 1182 parts.urn = true; 1183 } 1184 } 1185 } 1186 1187 // what's left must be the path 1188 parts.path = string; 1189 1190 // and we're done 1191 return parts; 1192 }; 1193 URI.parseHost = function(string, parts) { 1194 if (!string) { 1195 string = ''; 1196 } 1197 1198 // Copy chrome, IE, opera backslash-handling behavior. 1199 // Back slashes before the query string get converted to forward slashes 1200 // See: https://github.com/joyent/node/blob/386fd24f49b0e9d1a8a076592a404168faeecc34/lib/url.js#L115-L124 1201 // See: https://code.google.com/p/chromium/issues/detail?id=25916 1202 // https://github.com/medialize/URI.js/pull/233 1203 string = string.replace(/\\/g, '/'); 1204 1205 // extract host:port 1206 var pos = string.indexOf('/'); 1207 var bracketPos; 1208 var t; 1209 1210 if (pos === -1) { 1211 pos = string.length; 1212 } 1213 1214 if (string.charAt(0) === '[') { 1215 // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6 1216 // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts 1217 // IPv6+port in the format [2001:db8::1]:80 (for the time being) 1218 bracketPos = string.indexOf(']'); 1219 parts.hostname = string.substring(1, bracketPos) || null; 1220 parts.port = string.substring(bracketPos + 2, pos) || null; 1221 if (parts.port === '/') { 1222 parts.port = null; 1223 } 1224 } else { 1225 var firstColon = string.indexOf(':'); 1226 var firstSlash = string.indexOf('/'); 1227 var nextColon = string.indexOf(':', firstColon + 1); 1228 if (nextColon !== -1 && (firstSlash === -1 || nextColon < firstSlash)) { 1229 // IPv6 host contains multiple colons - but no port 1230 // this notation is actually not allowed by RFC 3986, but we're a liberal parser 1231 parts.hostname = string.substring(0, pos) || null; 1232 parts.port = null; 1233 } else { 1234 t = string.substring(0, pos).split(':'); 1235 parts.hostname = t[0] || null; 1236 parts.port = t[1] || null; 1237 } 1238 } 1239 1240 if (parts.hostname && string.substring(pos).charAt(0) !== '/') { 1241 pos++; 1242 string = '/' + string; 1243 } 1244 1245 if (parts.preventInvalidHostname) { 1246 URI.ensureValidHostname(parts.hostname, parts.protocol); 1247 } 1248 1249 if (parts.port) { 1250 URI.ensureValidPort(parts.port); 1251 } 1252 1253 return string.substring(pos) || '/'; 1254 }; 1255 URI.parseAuthority = function(string, parts) { 1256 string = URI.parseUserinfo(string, parts); 1257 return URI.parseHost(string, parts); 1258 }; 1259 URI.parseUserinfo = function(string, parts) { 1260 // extract username:password 1261 var _string = string 1262 var firstBackSlash = string.indexOf('\\'); 1263 if (firstBackSlash !== -1) { 1264 string = string.replace(/\\/g, '/') 1265 } 1266 var firstSlash = string.indexOf('/'); 1267 var pos = string.lastIndexOf('@', firstSlash > -1 ? firstSlash : string.length - 1); 1268 var t; 1269 1270 // authority@ must come before /path or \path 1271 if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) { 1272 t = string.substring(0, pos).split(':'); 1273 parts.username = t[0] ? URI.decode(t[0]) : null; 1274 t.shift(); 1275 parts.password = t[0] ? URI.decode(t.join(':')) : null; 1276 string = _string.substring(pos + 1); 1277 } else { 1278 parts.username = null; 1279 parts.password = null; 1280 } 1281 1282 return string; 1283 }; 1284 URI.parseQuery = function(string, escapeQuerySpace) { 1285 if (!string) { 1286 return {}; 1287 } 1288 1289 // throw out the funky business - "?"[name"="value"&"]+ 1290 string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, ''); 1291 1292 if (!string) { 1293 return {}; 1294 } 1295 1296 var items = {}; 1297 var splits = string.split('&'); 1298 var length = splits.length; 1299 var v, name, value; 1300 1301 for (var i = 0; i < length; i++) { 1302 v = splits[i].split('='); 1303 name = URI.decodeQuery(v.shift(), escapeQuerySpace); 1304 // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters 1305 value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null; 1306 1307 if (name === '__proto__') { 1308 // ignore attempt at exploiting JavaScript internals 1309 continue; 1310 } else if (hasOwn.call(items, name)) { 1311 if (typeof items[name] === 'string' || items[name] === null) { 1312 items[name] = [items[name]]; 1313 } 1314 1315 items[name].push(value); 1316 } else { 1317 items[name] = value; 1318 } 1319 } 1320 1321 return items; 1322 }; 1323 1324 URI.build = function(parts) { 1325 var t = ''; 1326 var requireAbsolutePath = false 1327 1328 if (parts.protocol) { 1329 t += parts.protocol + ':'; 1330 } 1331 1332 if (!parts.urn && (t || parts.hostname)) { 1333 t += '//'; 1334 requireAbsolutePath = true 1335 } 1336 1337 t += (URI.buildAuthority(parts) || ''); 1338 1339 if (typeof parts.path === 'string') {
1340 if (parts.path.charAt(0) !== '/' && requireAbsolutePath) { 1341 t += '/'; 1342 } 1343 1344 t += parts.path; 1345 } 1346 1347 if (typeof parts.query === 'string' && parts.query) { 1348 t += '?' + parts.query; 1349 } 1350 1351 if (typeof parts.fragment === 'string' && parts.fragment) { 1352 t += '#' + parts.fragment; 1353 } 1354 return t; 1355 }; 1356 URI.buildHost = function(parts) { 1357 var t = ''; 1358 1359 if (!parts.hostname) { 1360 return ''; 1361 } else if (URI.ip6_expression.test(parts.hostname)) { 1362 t += '[' + parts.hostname + ']'; 1363 } else { 1364 t += parts.hostname; 1365 } 1366 1367 if (parts.port) { 1368 t += ':' + parts.port; 1369 } 1370 1371 return t; 1372 }; 1373 URI.buildAuthority = function(parts) { 1374 return URI.buildUserinfo(parts) + URI.buildHost(parts); 1375 }; 1376 URI.buildUserinfo = function(parts) { 1377 var t = ''; 1378 1379 if (parts.username) { 1380 t += URI.encode(parts.username); 1381 } 1382 1383 if (parts.password) { 1384 t += ':' + URI.encode(parts.password); 1385 } 1386 1387 if (t) { 1388 t += '@'; 1389 } 1390 1391 return t; 1392 }; 1393 URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) { 1394 // according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html 1395 // being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed 1396 // the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax! 1397 // URI.js treats the query string as being application/x-www-form-urlencoded 1398 // see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type 1399 1400 var t = ''; 1401 var unique, key, i, length; 1402 for (key in data) { 1403 if (key === '__proto__') { 1404 // ignore attempt at exploiting JavaScript internals 1405 continue; 1406 } else if (hasOwn.call(data, key)) { 1407 if (isArray(data[key])) { 1408 unique = {}; 1409 for (i = 0, length = data[key].length; i < length; i++) { 1410 if (data[key][i] !== undefined && unique[data[key][i] + ''] === undefined) { 1411 t += '&' + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace); 1412 if (duplicateQueryParameters !== true) { 1413 unique[data[key][i] + ''] = true; 1414 } 1415 } 1416 } 1417 } else if (data[key] !== undefined) { 1418 t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace); 1419 } 1420 } 1421 } 1422 1423 return t.substring(1); 1424 }; 1425 URI.buildQueryParameter = function(name, value, escapeQuerySpace) { 1426 // http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded 1427 // don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization 1428 return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? '=' + URI.encodeQuery(value, escapeQuerySpace) : ''); 1429 }; 1430 1431 URI.addQuery = function(data, name, value) { 1432 if (typeof name === 'object') { 1433 for (var key in name) { 1434 if (hasOwn.call(name, key)) { 1435 URI.addQuery(data, key, name[key]); 1436 } 1437 } 1438 } else if (typeof name === 'string') { 1439 if (data[name] === undefined) { 1440 data[name] = value; 1441 return; 1442 } else if (typeof data[name] === 'string') { 1443 data[name] = [data[name]]; 1444 } 1445 1446 if (!isArray(value)) { 1447 value = [value]; 1448 } 1449 1450 data[name] = (data[name] || []).concat(value); 1451 } else { 1452 throw new TypeError('URI.addQuery() accepts an object, string as the name parameter'); 1453 } 1454 }; 1455 1456 URI.setQuery = function(data, name, value) { 1457 if (typeof name === 'object') { 1458 for (var key in name) { 1459 if (hasOwn.call(name, key)) { 1460 URI.setQuery(data, key, name[key]); 1461 } 1462 } 1463 } else if (typeof name === 'string') { 1464 data[name] = value === undefined ? null : value; 1465 } else { 1466 throw new TypeError('URI.setQuery() accepts an object, string as the name parameter'); 1467 } 1468 }; 1469 1470 URI.removeQuery = function(data, name, value) { 1471 var i, length, key; 1472 1473 if (isArray(name)) { 1474 for (i = 0, length = name.length; i < length; i++) { 1475 data[name[i]] = undefined; 1476 } 1477 } else if (getType(name) === 'RegExp') { 1478 for (key in data) { 1479 if (name.test(key)) { 1480 data[key] = undefined; 1481 } 1482 } 1483 } else if (typeof name === 'object') { 1484 for (key in name) { 1485 if (hasOwn.call(name, key)) { 1486 URI.removeQuery(data, key, name[key]); 1487 } 1488 } 1489 } else if (typeof name === 'string') { 1490 if (value !== undefined) { 1491 if (getType(value) === 'RegExp') { 1492 if (!isArray(data[name]) && value.test(data[name])) { 1493 data[name] = undefined; 1494 } else { 1495 data[name] = filterArrayValues(data[name], value); 1496 } 1497 } else if (data[name] === String(value) && (!isArray(value) || value.length === 1)) { 1498 data[name] = undefined; 1499 } else if (isArray(data[name])) { 1500 data[name] = filterArrayValues(data[name], value); 1501 } 1502 } else { 1503 data[name] = undefined; 1504 } 1505 } else { 1506 throw new TypeError('URI.removeQuery() accepts an object, string, RegExp as the first parameter'); 1507 } 1508 }; 1509 URI.hasQuery = function(data, name, value, withinArray) { 1510 switch (getType(name)) { 1511 case 'String': 1512 // Nothing to do here 1513 break; 1514 1515 case 'RegExp': 1516 for (var key in data) {
1517 if (hasOwn.call(data, key)) { 1518 if (name.test(key) && (value === undefined || URI.hasQuery(data, key, value))) { 1519 return true; 1520 } 1521 } 1522 } 1523 1524 return false; 1525 1526 case 'Object': 1527 for (var _key in name) { 1528 if (hasOwn.call(name, _key)) { 1529 if (!URI.hasQuery(data, _key, name[_key])) { 1530 return false; 1531 } 1532 } 1533 } 1534 1535 return true; 1536 1537 default: 1538 throw new TypeError('URI.hasQuery() accepts a string, regular expression or object as the name parameter'); 1539 } 1540 1541 switch (getType(value)) { 1542 case 'Undefined': 1543 // true if exists (but may be empty) 1544 return name in data; // data[name] !== undefined; 1545 1546 case 'Boolean': 1547 // true if exists and non-empty 1548 var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]); 1549 return value === _booly; 1550 1551 case 'Function': 1552 // allow complex comparison 1553 return !!value(data[name], name, data); 1554 1555 case 'Array': 1556 if (!isArray(data[name])) { 1557 return false; 1558 } 1559 1560 var op = withinArray ? arrayContains : arraysEqual; 1561 return op(data[name], value); 1562 1563 case 'RegExp': 1564 if (!isArray(data[name])) { 1565 return Boolean(data[name] && data[name].match(value)); 1566 } 1567 1568 if (!withinArray) { 1569 return false; 1570 } 1571 1572 return arrayContains(data[name], value); 1573 1574 case 'Number': 1575 value = String(value); 1576 /* falls through */ 1577 case 'String': 1578 if (!isArray(data[name])) { 1579 return data[name] === value; 1580 } 1581 1582 if (!withinArray) { 1583 return false; 1584 } 1585 1586 return arrayContains(data[name], value); 1587 1588 default: 1589 throw new TypeError('URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter'); 1590 } 1591 }; 1592 1593 1594 URI.joinPaths = function() { 1595 var input = []; 1596 var segments = []; 1597 var nonEmptySegments = 0; 1598 1599 for (var i = 0; i < arguments.length; i++) { 1600 var url = new URI(arguments[i]); 1601 input.push(url); 1602 var _segments = url.segment(); 1603 for (var s = 0; s < _segments.length; s++) { 1604 if (typeof _segments[s] === 'string') { 1605 segments.push(_segments[s]); 1606 } 1607 1608 if (_segments[s]) { 1609 nonEmptySegments++; 1610 } 1611 } 1612 } 1613 1614 if (!segments.length || !nonEmptySegments) { 1615 return new URI(''); 1616 } 1617 1618 var uri = new URI('').segment(segments); 1619 1620 if (input[0].path() === '' || input[0].path().slice(0, 1) === '/') { 1621 uri.path('/' + uri.path()); 1622 } 1623 1624 return uri.normalize(); 1625 }; 1626 1627 URI.commonPath = function(one, two) { 1628 var length = Math.min(one.length, two.length); 1629 var pos; 1630 1631 // find first non-matching character 1632 for (pos = 0; pos < length; pos++) { 1633 if (one.charAt(pos) !== two.charAt(pos)) { 1634 pos--; 1635 break; 1636 } 1637 } 1638 1639 if (pos < 1) { 1640 return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : ''; 1641 } 1642 1643 // revert to last / 1644 if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') { 1645 pos = one.substring(0, pos).lastIndexOf('/'); 1646 } 1647 1648 return one.substring(0, pos + 1); 1649 }; 1650 1651 URI.withinString = function(string, callback, options) { 1652 options || (options = {}); 1653 var _start = options.start || URI.findUri.start; 1654 var _end = options.end || URI.findUri.end; 1655 var _trim = options.trim || URI.findUri.trim; 1656 var _parens = options.parens || URI.findUri.parens; 1657 var _attributeOpen = /[a-z0-9-]=["']?$/i; 1658 1659 _start.lastIndex = 0; 1660 while (true) { 1661 var match = _start.exec(string); 1662 if (!match) { 1663 break; 1664 } 1665 1666 var start = match.index; 1667 if (options.ignoreHtml) { 1668 // attribut(e=["']?$) 1669 var attributeOpen = string.slice(Math.max(start - 3, 0), start); 1670 if (attributeOpen && _attributeOpen.test(attributeOpen)) { 1671 continue; 1672 } 1673 } 1674 1675 var end = start + string.slice(start).search(_end); 1676 var slice = string.slice(start, end); 1677 // make sure we include well balanced parens 1678 var parensEnd = -1; 1679 while (true) { 1680 var parensMatch = _parens.exec(slice); 1681 if (!parensMatch) { 1682 break; 1683 } 1684 1685 var parensMatchEnd = parensMatch.index + parensMatch[0].length; 1686 parensEnd = Math.max(parensEnd, parensMatchEnd); 1687 } 1688 1689 if (parensEnd > -1) { 1690 slice = slice.slice(0, parensEnd) + slice.slice(parensEnd).replace(_trim, ''); 1691 } else { 1692 slice = slice.replace(_trim, ''); 1693 } 1694 1695 if (slice.length <= match[0].length) { 1696 // the extract only contains the starting marker of a URI, 1697 // e.g. "www" or "http://" 1698 continue; 1699 } 1700 1701 if (options.ignore && options.ignore.test(slice)) { 1702 continue; 1703 } 1704 1705 end = start + slice.length; 1706 var result = callback(slice, start, end, string); 1707 if (result === undefined) { 1708 _start.lastIndex = end; 1709 continue; 1710 } 1711 1712 result = String(result);
1713 string = string.slice(0, start) + result + string.slice(end); 1714 _start.lastIndex = start + result.length; 1715 } 1716 1717 _start.lastIndex = 0; 1718 return string; 1719 }; 1720 1721 URI.ensureValidHostname = function(v, protocol) { 1722 // Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986) 1723 // they are not part of DNS and therefore ignored by URI.js 1724 1725 var hasHostname = !!v; // not null and not an empty string 1726 var hasProtocol = !!protocol; 1727 var rejectEmptyHostname = false; 1728 1729 if (hasProtocol) { 1730 rejectEmptyHostname = arrayContains(URI.hostProtocols, protocol); 1731 } 1732 1733 if (rejectEmptyHostname && !hasHostname) { 1734 throw new TypeError('Hostname cannot be empty, if protocol is ' + protocol); 1735 } else if (v && v.match(URI.invalid_hostname_characters)) { 1736 // test punycode 1737 if (!punycode) { 1738 throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-:_] and Punycode.js is not available'); 1739 } 1740 if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) { 1741 throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-:_]'); 1742 } 1743 } 1744 }; 1745 1746 URI.ensureValidPort = function (v) { 1747 if (!v) { 1748 return; 1749 } 1750 1751 var port = Number(v); 1752 if (isInteger(port) && (port > 0) && (port < 65536)) { 1753 return; 1754 } 1755 1756 throw new TypeError('Port "' + v + '" is not a valid port'); 1757 }; 1758 1759 // noConflict 1760 URI.noConflict = function(removeAll) { 1761 if (removeAll) { 1762 var unconflicted = { 1763 URI: this.noConflict() 1764 }; 1765 1766 if (root.URITemplate && typeof root.URITemplate.noConflict === 'function') { 1767 unconflicted.URITemplate = root.URITemplate.noConflict(); 1768 } 1769 1770 if (root.IPv6 && typeof root.IPv6.noConflict === 'function') { 1771 unconflicted.IPv6 = root.IPv6.noConflict(); 1772 } 1773 1774 if (root.SecondLevelDomains && typeof root.SecondLevelDomains.noConflict === 'function') { 1775 unconflicted.SecondLevelDomains = root.SecondLevelDomains.noConflict(); 1776 } 1777 1778 return unconflicted; 1779 } else if (root.URI === this) { 1780 root.URI = _URI; 1781 } 1782 1783 return this; 1784 }; 1785 1786 p.build = function(deferBuild) { 1787 if (deferBuild === true) { 1788 this._deferred_build = true; 1789 } else if (deferBuild === undefined || this._deferred_build) { 1790 this._string = URI.build(this._parts); 1791 this._deferred_build = false; 1792 } 1793 1794 return this; 1795 }; 1796 1797 p.clone = function() { 1798 return new URI(this); 1799 }; 1800 1801 p.valueOf = p.toString = function() { 1802 return this.build(false)._string; 1803 }; 1804 1805 1806 function generateSimpleAccessor(_part){ 1807 return function(v, build) { 1808 if (v === undefined) { 1809 return this._parts[_part] || ''; 1810 } else { 1811 this._parts[_part] = v || null; 1812 this.build(!build); 1813 return this; 1814 } 1815 }; 1816 } 1817 1818 function generatePrefixAccessor(_part, _key){ 1819 return function(v, build) { 1820 if (v === undefined) { 1821 return this._parts[_part] || ''; 1822 } else { 1823 if (v !== null) { 1824 v = v + ''; 1825 if (v.charAt(0) === _key) { 1826 v = v.substring(1); 1827 } 1828 } 1829 1830 this._parts[_part] = v; 1831 this.build(!build); 1832 return this; 1833 } 1834 }; 1835 } 1836 1837 p.protocol = generateSimpleAccessor('protocol'); 1838 p.username = generateSimpleAccessor('username'); 1839 p.password = generateSimpleAccessor('password'); 1840 p.hostname = generateSimpleAccessor('hostname'); 1841 p.port = generateSimpleAccessor('port'); 1842 p.query = generatePrefixAccessor('query', '?'); 1843 p.fragment = generatePrefixAccessor('fragment', '#'); 1844 1845 p.search = function(v, build) { 1846 var t = this.query(v, build); 1847 return typeof t === 'string' && t.length ? ('?' + t) : t; 1848 }; 1849 p.hash = function(v, build) { 1850 var t = this.fragment(v, build); 1851 return typeof t === 'string' && t.length ? ('#' + t) : t; 1852 }; 1853 1854 p.pathname = function(v, build) { 1855 if (v === undefined || v === true) { 1856 var res = this._parts.path || (this._parts.hostname ? '/' : ''); 1857 return v ? (this._parts.urn ? URI.decodeUrnPath : URI.decodePath)(res) : res; 1858 } else { 1859 if (this._parts.urn) { 1860 this._parts.path = v ? URI.recodeUrnPath(v) : ''; 1861 } else { 1862 this._parts.path = v ? URI.recodePath(v) : '/'; 1863 } 1864 this.build(!build); 1865 return this; 1866 } 1867 }; 1868 p.path = p.pathname; 1869 p.href = function(href, build) { 1870 var key; 1871 1872 if (href === undefined) { 1873 return this.toString(); 1874 } 1875 1876 this._string = ''; 1877 this._parts = URI._parts(); 1878 1879 var _URI = href instanceof URI; 1880 var _object = typeof href === 'object' && (href.hostname || href.path || href.pathname); 1881 if (href.nodeName) { 1882 var attribute = URI.getDomAttribute(href); 1883 href = href[attribute] || ''; 1884 _object = false;
1885 } 1886 1887 // window.location is reported to be an object, but it's not the sort 1888 // of object we're looking for: 1889 // * location.protocol ends with a colon 1890 // * location.query != object.search 1891 // * location.hash != object.fragment 1892 // simply serializing the unknown object should do the trick 1893 // (for location, not for everything...) 1894 if (!_URI && _object && href.pathname !== undefined) { 1895 href = href.toString(); 1896 } 1897 1898 if (typeof href === 'string' || href instanceof String) { 1899 this._parts = URI.parse(String(href), this._parts); 1900 } else if (_URI || _object) { 1901 var src = _URI ? href._parts : href; 1902 for (key in src) { 1903 if (key === 'query') { continue; } 1904 if (hasOwn.call(this._parts, key)) { 1905 this._parts[key] = src[key]; 1906 } 1907 } 1908 if (src.query) { 1909 this.query(src.query, false); 1910 } 1911 } else { 1912 throw new TypeError('invalid input'); 1913 } 1914 1915 this.build(!build); 1916 return this; 1917 }; 1918 1919 // identification accessors 1920 p.is = function(what) { 1921 var ip = false; 1922 var ip4 = false; 1923 var ip6 = false; 1924 var name = false; 1925 var sld = false; 1926 var idn = false; 1927 var punycode = false; 1928 var relative = !this._parts.urn; 1929 1930 if (this._parts.hostname) { 1931 relative = false; 1932 ip4 = URI.ip4_expression.test(this._parts.hostname); 1933 ip6 = URI.ip6_expression.test(this._parts.hostname); 1934 ip = ip4 || ip6; 1935 name = !ip; 1936 sld = name && SLD && SLD.has(this._parts.hostname); 1937 idn = name && URI.idn_expression.test(this._parts.hostname); 1938 punycode = name && URI.punycode_expression.test(this._parts.hostname); 1939 } 1940 1941 switch (what.toLowerCase()) { 1942 case 'relative': 1943 return relative; 1944 1945 case 'absolute': 1946 return !relative; 1947 1948 // hostname identification 1949 case 'domain': 1950 case 'name': 1951 return name; 1952 1953 case 'sld': 1954 return sld; 1955 1956 case 'ip': 1957 return ip; 1958 1959 case 'ip4': 1960 case 'ipv4': 1961 case 'inet4': 1962 return ip4; 1963 1964 case 'ip6': 1965 case 'ipv6': 1966 case 'inet6': 1967 return ip6; 1968 1969 case 'idn': 1970 return idn; 1971 1972 case 'url': 1973 return !this._parts.urn; 1974 1975 case 'urn': 1976 return !!this._parts.urn; 1977 1978 case 'punycode': 1979 return punycode; 1980 } 1981 1982 return null; 1983 }; 1984 1985 // component specific input validation 1986 var _protocol = p.protocol; 1987 var _port = p.port; 1988 var _hostname = p.hostname; 1989 1990 p.protocol = function(v, build) { 1991 if (v) { 1992 // accept trailing :// 1993 v = v.replace(/:(\/\/)?$/, ''); 1994 1995 if (!v.match(URI.protocol_expression)) { 1996 throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]'); 1997 } 1998 } 1999 2000 return _protocol.call(this, v, build); 2001 }; 2002 p.scheme = p.protocol; 2003 p.port = function(v, build) { 2004 if (this._parts.urn) { 2005 return v === undefined ? '' : this; 2006 } 2007 2008 if (v !== undefined) { 2009 if (v === 0) { 2010 v = null; 2011 } 2012 2013 if (v) { 2014 v += ''; 2015 if (v.charAt(0) === ':') { 2016 v = v.substring(1); 2017 } 2018 2019 URI.ensureValidPort(v); 2020 } 2021 } 2022 return _port.call(this, v, build); 2023 }; 2024 p.hostname = function(v, build) { 2025 if (this._parts.urn) { 2026 return v === undefined ? '' : this; 2027 } 2028 2029 if (v !== undefined) { 2030 var x = { preventInvalidHostname: this._parts.preventInvalidHostname }; 2031 var res = URI.parseHost(v, x); 2032 if (res !== '/') { 2033 throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]'); 2034 } 2035 2036 v = x.hostname; 2037 if (this._parts.preventInvalidHostname) { 2038 URI.ensureValidHostname(v, this._parts.protocol); 2039 } 2040 } 2041 2042 return _hostname.call(this, v, build); 2043 }; 2044 2045 // compound accessors 2046 p.origin = function(v, build) { 2047 if (this._parts.urn) { 2048 return v === undefined ? '' : this; 2049 } 2050 2051 if (v === undefined) { 2052 var protocol = this.protocol(); 2053 var authority = this.authority(); 2054 if (!authority) { 2055 return ''; 2056 } 2057 2058 return (protocol ? protocol + '://' : '') + this.authority(); 2059 } else { 2060 var origin = URI(v); 2061 this 2062 .protocol(origin.protocol()) 2063 .authority(origin.authority()) 2064 .build(!build); 2065 return this; 2066 } 2067 }; 2068 p.host = function(v, build) { 2069 if (this._parts.urn) { 2070 return v === undefined ? '' : this; 2071 } 2072 2073 if (v === undefined) { 2074 return this._parts.hostname ? URI.buildHost(this._parts) : ''; 2075 } else { 2076 var res = URI.parseHost(v, this._parts); 2077 if (res !== '/') { 2078 throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]'); 2079 } 2080 2081 this.build(!build); 2082 return this; 2083 } 2084 }; 2085 p.authority = function(v, build) { 2086 if (this._parts.urn) { 2087 return v === undefined ? '' : this; 2088 } 2089 2090 if (v === undefined) { 2091 return this._parts.hostname ? URI.buildAuthority(this._parts) : ''; 2092 } else { 2093 var res = URI.parseAuthority(v, this._parts); 2094 if (res !== '/') { 2095 throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]'); 2096 } 2097 2098 this.build(!build); 2099 return this; 2100 } 2101 }; 2102 p.userinfo = function(v, build) { 2103 if (this._parts.urn) { 2104 return v === undefined ? '' : this; 2105 } 2106 2107 if (v === undefined) { 2108 var t = URI.buildUserinfo(this._parts); 2109 return t ? t.substring(0, t.length -1) : t; 2110 } else { 2111 if (v[v.length-1] !== '@') { 2112 v += '@'; 2113 } 2114 2115 URI.parseUserinfo(v, this._parts); 2116 this.build(!build); 2117 return this; 2118 } 2119 }; 2120 p.resource = function(v, build) { 2121 var parts; 2122 2123 if (v === undefined) { 2124 return this.path() + this.search() + this.hash(); 2125 } 2126 2127 parts = URI.parse(v); 2128 this._parts.path = parts.path; 2129 this._parts.query = parts.query; 2130 this._parts.fragment = parts.fragment; 2131 this.build(!build); 2132 return this; 2133 }; 2134 2135 // fraction accessors 2136 p.subdomain = function(v, build) { 2137 if (this._parts.urn) { 2138 return v === undefined ? '' : this; 2139 } 2140 2141 // convenience, return "www" from "www.example.org" 2142 if (v === undefined) { 2143 if (!this._parts.hostname || this.is('IP')) { 2144 return ''; 2145 } 2146 2147 // grab domain and add another segment 2148 var end = this._parts.hostname.length - this.domain().length - 1; 2149 return this._parts.hostname.substring(0, end) || ''; 2150 } else { 2151 var e = this._parts.hostname.length - this.domain().length; 2152 var sub = this._parts.hostname.substring(0, e); 2153 var replace = new RegExp('^' + escapeRegEx(sub)); 2154 2155 if (v && v.charAt(v.length - 1) !== '.') { 2156 v += '.'; 2157 } 2158 2159 if (v.indexOf(':') !== -1) { 2160 throw new TypeError('Domains cannot contain colons'); 2161 } 2162 2163 if (v) { 2164 URI.ensureValidHostname(v, this._parts.protocol); 2165 } 2166 2167 this._parts.hostname = this._parts.hostname.replace(replace, v); 2168 this.build(!build); 2169 return this; 2170 } 2171 }; 2172 p.domain = function(v, build) { 2173 if (this._parts.urn) { 2174 return v === undefined ? '' : this; 2175 } 2176 2177 if (typeof v === 'boolean') { 2178 build = v; 2179 v = undefined; 2180 } 2181 2182 // convenience, return "example.org" from "www.example.org" 2183 if (v === undefined) { 2184 if (!this._parts.hostname || this.is('IP')) { 2185 return ''; 2186 } 2187 2188 // if hostname consists of 1 or 2 segments, it must be the domain 2189 var t = this._parts.hostname.match(/\./g); 2190 if (t && t.length < 2) { 2191 return this._parts.hostname; 2192 } 2193 2194 // grab tld and add another segment 2195 var end = this._parts.hostname.length - this.tld(build).length - 1; 2196 end = this._parts.hostname.lastIndexOf('.', end -1) + 1; 2197 return this._parts.hostname.substring(end) || ''; 2198 } else { 2199 if (!v) { 2200 throw new TypeError('cannot set domain empty'); 2201 } 2202 2203 if (v.indexOf(':') !== -1) { 2204 throw new TypeError('Domains cannot contain colons'); 2205 } 2206 2207 URI.ensureValidHostname(v, this._parts.protocol); 2208 2209 if (!this._parts.hostname || this.is('IP')) { 2210 this._parts.hostname = v; 2211 } else { 2212 var replace = new RegExp(escapeRegEx(this.domain()) + '$'); 2213 this._parts.hostname = this._parts.hostname.replace(replace, v); 2214 } 2215 2216 this.build(!build); 2217 return this; 2218 } 2219 }; 2220 p.tld = function(v, build) { 2221 if (this._parts.urn) { 2222 return v === undefined ? '' : this; 2223 } 2224 2225 if (typeof v === 'boolean') { 2226 build = v; 2227 v = undefined; 2228 } 2229 2230 // return "org" from "www.example.org" 2231 if (v === undefined) { 2232 if (!this._parts.hostname || this.is('IP')) { 2233 return ''; 2234 } 2235 2236 var pos = this._parts.hostname.lastIndexOf('.'); 2237 var tld = this._parts.hostname.substring(pos + 1); 2238 2239 if (build !== true && SLD && SLD.list[tld.toLowerCase()]) { 2240 return SLD.get(this._parts.hostname) || tld; 2241 } 2242 2243 return tld; 2244 } else { 2245 var replace; 2246 2247 if (!v) { 2248 throw new TypeError('cannot set TLD empty'); 2249 } else if (v.match(/[^a-zA-Z0-9-]/)) { 2250 if (SLD && SLD.is(v)) { 2251 replace = new RegExp(escapeRegEx(this.tld()) + '$'); 2252 this._parts.hostname = this._parts.hostname.replace(replace, v); 2253 } else { 2254 throw new TypeError('TLD "' + v + '" contains characters other than [A-Z0-9]'); 2255 } 2256 } else if (!this._parts.hostname || this.is('IP')) { 2257 throw new ReferenceError('cannot set TLD on non-domain host'); 2258 } else { 2259 replace = new RegExp(escapeRegEx(this.tld()) + '$'); 2260 this._parts.hostname = this._parts.hostname.replace(replace, v); 2261 } 2262 2263 this.build(!build); 2264 return this; 2265 } 2266 }; 2267 p.directory = function(v, build) { 2268 if (this._parts.urn) { 2269 return v === undefined ? '' : this; 2270 } 2271 2272 if (v === undefined || v === true) { 2273 if (!this._parts.path && !this._parts.hostname) { 2274 return ''; 2275 } 2276 2277 if (this._parts.path === '/') { 2278 return '/'; 2279 } 2280 2281 var end = this._parts.path.length - this.filename().length - 1; 2282 var res = this._parts.path.substring(0, end) || (this._parts.hostname ? '/' : ''); 2283 2284 return v ? URI.decodePath(res) : res; 2285 2286 } else { 2287 var e = this._parts.path.length - this.filename().length; 2288 var directory = this._parts.path.substring(0, e); 2289 var replace = new RegExp('^' + escapeRegEx(directory)); 2290 2291 // fully qualifier directories begin with a slash 2292 if (!this.is('relative')) { 2293 if (!v) { 2294 v = '/'; 2295 } 2296 2297 if (v.charAt(0) !== '/') { 2298 v = '/' + v; 2299 } 2300 } 2301 2302 // directories always end with a slash 2303 if (v && v.charAt(v.length - 1) !== '/') { 2304 v += '/'; 2305 } 2306 2307 v = URI.recodePath(v); 2308 this._parts.path = this._parts.path.replace(replace, v); 2309 this.build(!build); 2310 return this; 2311 } 2312 }; 2313 p.filename = function(v, build) { 2314 if (this._parts.urn) { 2315 return v === undefined ? '' : this; 2316 } 2317 2318 if (typeof v !== 'string') { 2319 if (!this._parts.path || this._parts.path === '/') { 2320 return ''; 2321 } 2322 2323 var pos = this._parts.path.lastIndexOf('/'); 2324 var res = this._parts.path.substring(pos+1); 2325 2326 return v ? URI.decodePathSegment(res) : res; 2327 } else { 2328 var mutatedDirectory = false;
2329 2330 if (v.charAt(0) === '/') { 2331 v = v.substring(1); 2332 } 2333 2334 if (v.match(/\.?\//)) { 2335 mutatedDirectory = true; 2336 } 2337 2338 var replace = new RegExp(escapeRegEx(this.filename()) + '$'); 2339 v = URI.recodePath(v); 2340 this._parts.path = this._parts.path.replace(replace, v); 2341 2342 if (mutatedDirectory) { 2343 this.normalizePath(build); 2344 } else { 2345 this.build(!build); 2346 } 2347 2348 return this; 2349 } 2350 }; 2351 p.suffix = function(v, build) { 2352 if (this._parts.urn) { 2353 return v === undefined ? '' : this; 2354 } 2355 2356 if (v === undefined || v === true) { 2357 if (!this._parts.path || this._parts.path === '/') { 2358 return ''; 2359 } 2360 2361 var filename = this.filename(); 2362 var pos = filename.lastIndexOf('.'); 2363 var s, res; 2364 2365 if (pos === -1) { 2366 return ''; 2367 } 2368 2369 // suffix may only contain alnum characters (yup, I made this up.) 2370 s = filename.substring(pos+1); 2371 res = (/^[a-z0-9%]+$/i).test(s) ? s : ''; 2372 return v ? URI.decodePathSegment(res) : res; 2373 } else { 2374 if (v.charAt(0) === '.') { 2375 v = v.substring(1); 2376 } 2377 2378 var suffix = this.suffix(); 2379 var replace; 2380 2381 if (!suffix) { 2382 if (!v) { 2383 return this; 2384 } 2385 2386 this._parts.path += '.' + URI.recodePath(v); 2387 } else if (!v) { 2388 replace = new RegExp(escapeRegEx('.' + suffix) + '$'); 2389 } else { 2390 replace = new RegExp(escapeRegEx(suffix) + '$'); 2391 } 2392 2393 if (replace) { 2394 v = URI.recodePath(v); 2395 this._parts.path = this._parts.path.replace(replace, v); 2396 } 2397 2398 this.build(!build); 2399 return this; 2400 } 2401 }; 2402 p.segment = function(segment, v, build) { 2403 var separator = this._parts.urn ? ':' : '/'; 2404 var path = this.path(); 2405 var absolute = path.substring(0, 1) === '/'; 2406 var segments = path.split(separator); 2407 2408 if (segment !== undefined && typeof segment !== 'number') { 2409 build = v; 2410 v = segment; 2411 segment = undefined; 2412 } 2413 2414 if (segment !== undefined && typeof segment !== 'number') { 2415 throw new Error('Bad segment "' + segment + '", must be 0-based integer'); 2416 } 2417 2418 if (absolute) { 2419 segments.shift(); 2420 } 2421 2422 if (segment < 0) { 2423 // allow negative indexes to address from the end 2424 segment = Math.max(segments.length + segment, 0); 2425 } 2426 2427 if (v === undefined) { 2428 /*jshint laxbreak: true */ 2429 return segment === undefined 2430 ? segments 2431 : segments[segment]; 2432 /*jshint laxbreak: false */ 2433 } else if (segment === null || segments[segment] === undefined) { 2434 if (isArray(v)) { 2435 segments = []; 2436 // collapse empty elements within array 2437 for (var i=0, l=v.length; i < l; i++) { 2438 if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) { 2439 continue; 2440 } 2441 2442 if (segments.length && !segments[segments.length -1].length) { 2443 segments.pop(); 2444 } 2445 2446 segments.push(trimSlashes(v[i])); 2447 } 2448 } else if (v || typeof v === 'string') { 2449 v = trimSlashes(v); 2450 if (segments[segments.length -1] === '') { 2451 // empty trailing elements have to be overwritten 2452 // to prevent results such as /foo//bar 2453 segments[segments.length -1] = v; 2454 } else { 2455 segments.push(v); 2456 } 2457 } 2458 } else { 2459 if (v) { 2460 segments[segment] = trimSlashes(v); 2461 } else { 2462 segments.splice(segment, 1); 2463 } 2464 } 2465 2466 if (absolute) { 2467 segments.unshift(''); 2468 } 2469 2470 return this.path(segments.join(separator), build); 2471 }; 2472 p.segmentCoded = function(segment, v, build) { 2473 var segments, i, l; 2474 2475 if (typeof segment !== 'number') { 2476 build = v; 2477 v = segment; 2478 segment = undefined; 2479 } 2480 2481 if (v === undefined) { 2482 segments = this.segment(segment, v, build); 2483 if (!isArray(segments)) { 2484 segments = segments !== undefined ? URI.decode(segments) : undefined; 2485 } else { 2486 for (i = 0, l = segments.length; i < l; i++) { 2487 segments[i] = URI.decode(segments[i]); 2488 } 2489 } 2490 2491 return segments; 2492 } 2493 2494 if (!isArray(v)) { 2495 v = (typeof v === 'string' || v instanceof String) ? URI.encode(v) : v; 2496 } else { 2497 for (i = 0, l = v.length; i < l; i++) { 2498 v[i] = URI.encode(v[i]); 2499 } 2500 } 2501 2502 return this.segment(segment, v, build); 2503 }; 2504 2505 // mutating query string 2506 var q = p.query; 2507 p.query = function(v, build) { 2508 if (v === true) { 2509 return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2510 } else if (typeof v === 'function') { 2511 var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2512 var result = v.call(this, data); 2513 this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); 2514 this.build(!build); 2515 return this; 2516 } else if (v !== undefined && typeof v !== 'string') { 2517 this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); 2518 this.build(!build); 2519 return this; 2520 } else { 2521 return q.call(this, v, build); 2522 } 2523 }; 2524 p.setQuery = function(name, value, build) { 2525 var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2526 2527 if (typeof name === 'string' || name instanceof String) { 2528 data[name] = value !== undefined ? value : null; 2529 } else if (typeof name === 'object') { 2530 for (var key in name) { 2531 if (hasOwn.call(name, key)) { 2532 data[key] = name[key]; 2533 } 2534 } 2535 } else { 2536 throw new TypeError('URI.addQuery() accepts an object, string as the name parameter'); 2537 } 2538 2539 this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); 2540 if (typeof name !== 'string') { 2541 build = value; 2542 } 2543 2544 this.build(!build); 2545 return this; 2546 }; 2547 p.addQuery = function(name, value, build) { 2548 var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2549 URI.addQuery(data, name, value === undefined ? null : value); 2550 this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); 2551 if (typeof name !== 'string') { 2552 build = value; 2553 } 2554 2555 this.build(!build); 2556 return this; 2557 }; 2558 p.removeQuery = function(name, value, build) { 2559 var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2560 URI.removeQuery(data, name, value); 2561 this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace); 2562 if (typeof name !== 'string') { 2563 build = value; 2564 } 2565 2566 this.build(!build); 2567 return this; 2568 }; 2569 p.hasQuery = function(name, value, withinArray) { 2570 var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace); 2571 return URI.hasQuery(data, name, value, withinArray); 2572 }; 2573 p.setSearch = p.setQuery; 2574 p.addSearch = p.addQuery; 2575 p.removeSearch = p.removeQuery; 2576 p.hasSearch = p.hasQuery; 2577 2578 // sanitizing URLs 2579 p.normalize = function() { 2580 if (this._parts.urn) { 2581 return this 2582 .normalizeProtocol(false) 2583 .normalizePath(false) 2584 .normalizeQuery(false) 2585 .normalizeFragment(false) 2586 .build(); 2587 } 2588 2589 return this 2590 .normalizeProtocol(false) 2591 .normalizeHostname(false) 2592 .normalizePort(false) 2593 .normalizePath(false) 2594 .normalizeQuery(false) 2595 .normalizeFragment(false) 2596 .build(); 2597 }; 2598 p.normalizeProtocol = function(build) { 2599 if (typeof this._parts.protocol === 'string') { 2600 this._parts.protocol = this._parts.protocol.toLowerCase(); 2601 this.build(!build); 2602 } 2603 2604 return this; 2605 }; 2606 p.normalizeHostname = function(build) { 2607 if (this._parts.hostname) { 2608 if (this.is('IDN') && punycode) { 2609 this._parts.hostname = punycode.toASCII(this._parts.hostname); 2610 } else if (this.is('IPv6') && IPv6) { 2611 this._parts.hostname = IPv6.best(this._parts.hostname); 2612 } 2613 2614 this._parts.hostname = this._parts.hostname.toLowerCase(); 2615 this.build(!build); 2616 } 2617 2618 return this; 2619 }; 2620 p.normalizePort = function(build) { 2621 // remove port of it's the protocol's default 2622 if (typeof this._parts.protocol === 'string' && this._parts.port === URI.defaultPorts[this._parts.protocol]) { 2623 this._parts.port = null; 2624 this.build(!build); 2625 } 2626 2627 return this; 2628 };
2629 p.normalizePath = function(build) { 2630 var _path = this._parts.path; 2631 if (!_path) { 2632 return this; 2633 } 2634 2635 if (this._parts.urn) { 2636 this._parts.path = URI.recodeUrnPath(this._parts.path); 2637 this.build(!build); 2638 return this; 2639 } 2640 2641 if (this._parts.path === '/') { 2642 return this; 2643 } 2644 2645 _path = URI.recodePath(_path); 2646 2647 var _was_relative; 2648 var _leadingParents = ''; 2649 var _parent, _pos; 2650 2651 // handle relative paths 2652 if (_path.charAt(0) !== '/') { 2653 _was_relative = true; 2654 _path = '/' + _path; 2655 } 2656 2657 // handle relative files (as opposed to directories) 2658 if (_path.slice(-3) === '/..' || _path.slice(-2) === '/.') { 2659 _path += '/'; 2660 } 2661 2662 // resolve simples 2663 _path = _path 2664 .replace(/(\/(\.\/)+)|(\/\.$)/g, '/') 2665 .replace(/\/{2,}/g, '/'); 2666 2667 // remember leading parents 2668 if (_was_relative) { 2669 _leadingParents = _path.substring(1).match(/^(\.\.\/)+/) || ''; 2670 if (_leadingParents) { 2671 _leadingParents = _leadingParents[0]; 2672 } 2673 } 2674 2675 // resolve parents 2676 while (true) { 2677 _parent = _path.search(/\/\.\.(\/|$)/); 2678 if (_parent === -1) { 2679 // no more ../ to resolve 2680 break; 2681 } else if (_parent === 0) { 2682 // top level cannot be relative, skip it 2683 _path = _path.substring(3); 2684 continue; 2685 } 2686 2687 _pos = _path.substring(0, _parent).lastIndexOf('/'); 2688 if (_pos === -1) { 2689 _pos = _parent; 2690 } 2691 _path = _path.substring(0, _pos) + _path.substring(_parent + 3); 2692 } 2693 2694 // revert to relative 2695 if (_was_relative && this.is('relative')) { 2696 _path = _leadingParents + _path.substring(1); 2697 } 2698 2699 this._parts.path = _path; 2700 this.build(!build); 2701 return this; 2702 }; 2703 p.normalizePathname = p.normalizePath; 2704 p.normalizeQuery = function(build) { 2705 if (typeof this._parts.query === 'string') { 2706 if (!this._parts.query.length) { 2707 this._parts.query = null; 2708 } else { 2709 this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace)); 2710 } 2711 2712 this.build(!build); 2713 } 2714 2715 return this; 2716 }; 2717 p.normalizeFragment = function(build) { 2718 if (!this._parts.fragment) { 2719 this._parts.fragment = null; 2720 this.build(!build); 2721 } 2722 2723 return this; 2724 }; 2725 p.normalizeSearch = p.normalizeQuery; 2726 p.normalizeHash = p.normalizeFragment; 2727 2728 p.iso8859 = function() { 2729 // expect unicode input, iso8859 output 2730 var e = URI.encode; 2731 var d = URI.decode; 2732 2733 URI.encode = escape; 2734 URI.decode = decodeURIComponent; 2735 try { 2736 this.normalize(); 2737 } finally { 2738 URI.encode = e; 2739 URI.decode = d; 2740 } 2741 return this; 2742 }; 2743 2744 p.unicode = function() { 2745 // expect iso8859 input, unicode output 2746 var e = URI.encode; 2747 var d = URI.decode; 2748 2749 URI.encode = strictEncodeURIComponent; 2750 URI.decode = unescape; 2751 try { 2752 this.normalize(); 2753 } finally { 2754 URI.encode = e; 2755 URI.decode = d; 2756 } 2757 return this; 2758 }; 2759 2760 p.readable = function() { 2761 var uri = this.clone(); 2762 // removing username, password, because they shouldn't be displayed according to RFC 3986 2763 uri.username('').password('').normalize(); 2764 var t = ''; 2765 if (uri._parts.protocol) { 2766 t += uri._parts.protocol + '://'; 2767 } 2768 2769 if (uri._parts.hostname) { 2770 if (uri.is('punycode') && punycode) { 2771 t += punycode.toUnicode(uri._parts.hostname); 2772 if (uri._parts.port) { 2773 t += ':' + uri._parts.port; 2774 } 2775 } else { 2776 t += uri.host(); 2777 } 2778 } 2779 2780 if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') { 2781 t += '/'; 2782 } 2783 2784 t += uri.path(true); 2785 if (uri._parts.query) { 2786 var q = ''; 2787 for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) { 2788 var kv = (qp[i] || '').split('='); 2789 q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace) 2790 .replace(/&/g, '%26'); 2791 2792 if (kv[1] !== undefined) { 2793 q += '=' + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace) 2794 .replace(/&/g, '%26'); 2795 } 2796 } 2797 t += '?' + q.substring(1); 2798 } 2799 2800 t += URI.decodeQuery(uri.hash(), true); 2801 return t; 2802 }; 2803 2804 // resolving relative and absolute URLs 2805 p.absoluteTo = function(base) { 2806 var resolved = this.clone(); 2807 var properties = ['protocol', 'username', 'password', 'hostname', 'port']; 2808 var basedir, i, p; 2809 2810 if (this._parts.urn) { 2811 throw new Error('URNs do not have any generally defined hierarchical components'); 2812 } 2813 2814 if (!(base instanceof URI)) { 2815 base = new URI(base); 2816 } 2817 2818 if (resolved._parts.protocol) { 2819 // Directly returns even if this._parts.hostname is empty. 2820 return resolved; 2821 } else { 2822 resolved._parts.protocol = base._parts.protocol; 2823 } 2824 2825 if (this._parts.hostname) { 2826 return resolved; 2827 } 2828 2829 for (i = 0; (p = properties[i]); i++) { 2830 resolved._parts[p] = base._parts[p]; 2831 } 2832 2833 if (!resolved._parts.path) { 2834 resolved._parts.path = base._parts.path;
2835 if (!resolved._parts.query) { 2836 resolved._parts.query = base._parts.query; 2837 } 2838 } else { 2839 if (resolved._parts.path.substring(-2) === '..') { 2840 resolved._parts.path += '/'; 2841 } 2842 2843 if (resolved.path().charAt(0) !== '/') { 2844 basedir = base.directory(); 2845 basedir = basedir ? basedir : base.path().indexOf('/') === 0 ? '/' : ''; 2846 resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path; 2847 resolved.normalizePath(); 2848 } 2849 } 2850 2851 resolved.build(); 2852 return resolved; 2853 }; 2854 p.relativeTo = function(base) { 2855 var relative = this.clone().normalize(); 2856 var relativeParts, baseParts, common, relativePath, basePath; 2857 2858 if (relative._parts.urn) { 2859 throw new Error('URNs do not have any generally defined hierarchical components'); 2860 } 2861 2862 base = new URI(base).normalize(); 2863 relativeParts = relative._parts; 2864 baseParts = base._parts; 2865 relativePath = relative.path(); 2866 basePath = base.path(); 2867 2868 if (relativePath.charAt(0) !== '/') { 2869 throw new Error('URI is already relative'); 2870 } 2871 2872 if (basePath.charAt(0) !== '/') { 2873 throw new Error('Cannot calculate a URI relative to another relative URI'); 2874 } 2875 2876 if (relativeParts.protocol === baseParts.protocol) { 2877 relativeParts.protocol = null; 2878 } 2879 2880 if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) { 2881 return relative.build(); 2882 } 2883 2884 if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) { 2885 return relative.build(); 2886 } 2887 2888 if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) { 2889 relativeParts.hostname = null; 2890 relativeParts.port = null; 2891 } else { 2892 return relative.build(); 2893 } 2894 2895 if (relativePath === basePath) { 2896 relativeParts.path = ''; 2897 return relative.build(); 2898 } 2899 2900 // determine common sub path 2901 common = URI.commonPath(relativePath, basePath); 2902 2903 // If the paths have nothing in common, return a relative URL with the absolute path. 2904 if (!common) { 2905 return relative.build(); 2906 } 2907 2908 var parents = baseParts.path 2909 .substring(common.length) 2910 .replace(/[^\/]*$/, '') 2911 .replace(/.*?\//g, '../'); 2912 2913 relativeParts.path = (parents + relativeParts.path.substring(common.length)) || './'; 2914 2915 return relative.build(); 2916 }; 2917 2918 // comparing URIs 2919 p.equals = function(uri) { 2920 var one = this.clone(); 2921 var two = new URI(uri); 2922 var one_map = {}; 2923 var two_map = {}; 2924 var checked = {}; 2925 var one_query, two_query, key; 2926 2927 one.normalize(); 2928 two.normalize(); 2929 2930 // exact match 2931 if (one.toString() === two.toString()) { 2932 return true; 2933 } 2934 2935 // extract query string 2936 one_query = one.query(); 2937 two_query = two.query(); 2938 one.query(''); 2939 two.query(''); 2940 2941 // definitely not equal if not even non-query parts match 2942 if (one.toString() !== two.toString()) { 2943 return false; 2944 } 2945 2946 // query parameters have the same length, even if they're permuted 2947 if (one_query.length !== two_query.length) { 2948 return false; 2949 } 2950 2951 one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace); 2952 two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace); 2953 2954 for (key in one_map) { 2955 if (hasOwn.call(one_map, key)) { 2956 if (!isArray(one_map[key])) { 2957 if (one_map[key] !== two_map[key]) { 2958 return false; 2959 } 2960 } else if (!arraysEqual(one_map[key], two_map[key])) { 2961 return false; 2962 } 2963 2964 checked[key] = true; 2965 } 2966 } 2967 2968 for (key in two_map) { 2969 if (hasOwn.call(two_map, key)) { 2970 if (!checked[key]) { 2971 // two contains a parameter not present in one 2972 return false; 2973 } 2974 } 2975 } 2976 2977 return true; 2978 }; 2979 2980 // state 2981 p.preventInvalidHostname = function(v) { 2982 this._parts.preventInvalidHostname = !!v; 2983 return this; 2984 }; 2985 2986 p.duplicateQueryParameters = function(v) { 2987 this._parts.duplicateQueryParameters = !!v; 2988 return this; 2989 }; 2990 2991 p.escapeQuerySpace = function(v) { 2992 this._parts.escapeQuerySpace = !!v; 2993 return this; 2994 }; 2995 2996 return URI; 2997})); 2998// source --> /wp-content/themes/sae-2021/resources/js/global.js?v=188 2999 (function($){ 3000 3001 $(function(){ 3002 3003 var lastUpdate = $("meta[property='article:modified_time']").attr("content"); 3004console.log('LASTUPDATE:'+lastUpdate); 3005 $('.footer_copyright-content').append('<span id="site-last-updated">Last update: '+lastUpdate+'</span>') 3006 3007 // autoSuggest -> mock the "delete value" function for input fields 3008 $('.wfAutosuggest').on('click', function(){ 3009 $(this).parent().siblings('.fa.fa-times-circle.tt-clear').click(); 3010 }); 3011 3012 // reformat dates with spaces that salesforce accept them as a valid date 3013 $('.validate-datecal').on('focusout', function(){ 3014 $(this).val($(this).val().trim().replaceAll(' ', '/')); 3015 }); 3016 3017 /** 3018 * Add get-parameters to a.href's. 3019 */ 3020 if(typeof URI === 'function'){ 3021 /** 3022 * Save get-parameters, to use in replaceAHrefs(). 3023 * But don't save array-get-parameters, these are only used on the current page. 3024 * Don't save Search Filter params (fs_*) â they must stay on the target link, not accumulate. 3025 * And don't save "p", "page" and "page_id" becaus they try to
3025show another page and thats results in a 404. 3026 * Hint: pages with reloading content elemens are removing get-parameters they dont use, but they do this later ââ©â®(ï½¥â¡ï½¥)ââ©â® 3027 */ 3028 let uriQueryData = new URI(location.href).query(true); 3029 for(let d in uriQueryData){ 3030 if(d.slice(-2) === '[]' || d.slice(0, 3) === 'fs_' || d === 'p' || d === 'page' || d === 'page_id' || d === 'post_type'){ 3031 delete (uriQueryData[d]); 3032 } 3033 } 3034 3035 /** 3036 * Monitor if there are elements that change their content on user action. 3037 * And replaceAHrefs() when new content is loaded. 3038 */ 3039 var elementsWithReloadingContent = [ 3040 events = $('.events_filter-initial_results'), 3041 courses = $('.courses_filter-results'), 3042 search = $('#search_modal_results') 3043 ]; 3044 3045 // STEP 1 - save old events container data 3046 var oldDataAlgoliaData = []; 3047 for (let element of elementsWithReloadingContent) { 3048 if (element.length) { 3049 oldDataAlgoliaData[element.attr('class')] = element.html(); 3050 } 3051 } 3052 3053 // STEP 2 3054 // Now we watch for new events container elements coming from Algolia 3055 const observer = new MutationObserver(() => { 3056 for (let element of elementsWithReloadingContent) { 3057 if (element.length) { 3058 let oldHtml = oldDataAlgoliaData[element.attr('class')] !== undefined ? oldDataAlgoliaData[element.attr('class')] : element.html(); 3059 if (oldHtml != element.html()) { 3060 oldHtml = element.html(); 3061 replaceAHrefs(element); 3062 // write new data back in data array 3063 oldDataAlgoliaData[element.attr('class')] = element.html(); 3064 } 3065 } 3066 } 3067 }); 3068 3069 // STEP 3 - Init Observer 3070 // Param 1 of observe() must be a Node - document is a valid Node and is looking for changes on the "page" 3071 observer.observe(document.getElementById('page'), { 3072 childList: true, 3073 subtree: true 3074 }); 3075 3076 /*for(let element of elementsWithReloadingContent){ 3077 if(element.length){ 3078 let oldHtml = element.html(); 3079 element.on('DOMNodeInserted', function(){ 3080 if(oldHtml != element.html()){ 3081 oldHtml = element.html(); 3082 replaceAHrefs(element); 3083 } 3084 }); 3085 } 3086 }*/ 3087 3088 /** 3089 * Add at all locally a.href's the saved get-parameters 3090 * 3091 * @param element jQuery Element whose content should be changed 3092 */ 3093 function replaceAHrefs(element){ 3094 element.find('a').each(function(){ 3095 let $a = $(this); 3096 let aHref = $a.attr('href'); 3097 if(aHref){ 3098 let href = new URI(aHref); 3099 let hostname = href.hostname(); 3100 if(!hostname || hostname === location.hostname){ 3101 href = href.addQuery(uriQueryData); 3102 href = aHref.slice(-1) === '#' ? href + '#' : href; 3103 $a.attr('href', href); 3104 } 3105 } 3106 }); 3107 } 3108 3109 // do it on load 3110 replaceAHrefs($('body')); 3111 } 3112 3113 3114 // Cookie pref center triggers 3115 (function(){ 3116 const countriesNotUsingCookieWidgetClickPrevention = [ 'ita' ]; // SAE-3070 3117 countriesNotUsingCookieWidgetClickPrevention.forEach( function( country) { 3118 if( window.location.href.indexOf( '/' + country + '/' ) === -1 ) { 3119 $('a[href="#cookiepolicy"], a[href$="/cookie-policy/"], a[href$="/cookie-policy"]').on('click', function (e) { 3120 e.preventDefault(); 3121 if (window.location.href.indexOf('/gbr/') !== -1) { 3122 document.querySelector('button.CookiebotWidget-logo').click(); // SAE-2375 3123 } else { 3124 document.querySelector('#BorlabsCookieWidget .brlbs-cmpnt-widget').click(); 3125 } 3126 return false; 3127 }); 3128 } 3129 }); 3130 }()); 3131 3132 3133 // SAE-1910 3134 if(location.href.indexOf('/nld/') > -1){ 3135 // home page 3136 $audioIcon = $('.discipline_tiles-container--desktop .discipline-theme--audio + img + div > svg').clone() 3137 $('.discipline_tiles-container--desktop .discipline-theme--emc + img + div').prepend($audioIcon) 3138 $audioIcon = $audioIcon.clone() 3139 $('.discipline_tiles-container--mobile .discipline-theme--emc + img + div').prepend($audioIcon) 3140console.debug($audioIcon) 3141 $filmIcon = $('.discipline_tiles-container--desktop .discipline-theme--film + img + div > svg').clone() 3142 $('.discipline_tiles-container--desktop .discipline-theme--film-2 + img + div').prepend($filmIcon) 3143 $filmIcon = $filmIcon.clone() 3144 $('.discipline_tiles-container--mobile .discipline-theme--film-2 + img + div').prepend($filmIcon) 3145console.debug($filmIcon) 3146 // courses page
3147 $audioIcon = $('.courses_filter-disciplines_tiles--desktop .discipline-theme--audio + img + div > svg').clone() 3148 $('.courses_filter-disciplines_tiles--desktop .discipline-theme--emc + img + div').prepend($audioIcon) 3149 $audioIcon = $audioIcon.clone() 3150 $('.courses_filter-disciplines_tiles--mobile .discipline-theme--emc + img + div').prepend($audioIcon) 3151console.debug($audioIcon) 3152 $filmIcon = $('.courses_filter-disciplines_tiles--desktop .discipline-theme--film + img + div > svg').clone() 3153 $('.courses_filter-disciplines_tiles--desktop .discipline-theme--film-2 + img + div').prepend($filmIcon) 3154 $filmIcon = $filmIcon.clone() 3155 $('.courses_filter-disciplines_tiles--mobile .discipline-theme--film-2 + img + div').prepend($filmIcon) 3156console.debug($filmIcon) 3157 } 3158 3159 // send user, after opening a student gallery page as link back to previous page "on close" 3160 if ($('.single-student_work-data').length){ 3161 $('.modal-close').off('click').on('click', function(event){ 3162 event.preventDefault(); 3163 history.back(1); 3164 return false; 3165 }); 3166 } 3167 3168 // SAE-3357 SPECIAL HANDLING for FRA Chat System which is not working, if a modal window is active 3169 if (location.href.indexOf('/fra/') > -1) { 3170 $('body').on('focus', '.crisp-client textarea', function () { 3171 if ($('.modal-close_wrapper').is(':visible')) { 3172 $('.modal-close_wrapper').hide(); 3173 } 3174 }); 3175 $('body').on('blur', '.crisp-client textarea', function () { 3176 $('.modal-close_wrapper').show(); 3177 }); 3178 } 3179 3180 }); 3181 3182})(jQuery);
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.