PageSourceSearch

https://travel.gc.ca/vt/custom/js/invitationmanager.js

js travel.gc.ca collected 2026-09-24 08:28:14 UTC 26,143 bytes, 830 lines download raw bytes

1/*
2 * Inviation Manager Independent Version
3 */
4
5
6window.addEventListener( "load", imSetup);
7
8
9/*
10 * First function called  
11 */
12function imSetup() {
13	"use strict";
14
15	// Document all main variables
16
17	var debugMode, 		// If true, log console info
18		surveyDB, 		// JSON var of data stored in im.json
19		surveyScope, 	// Equals 1 if survey page and 2 is survey site
20		dbURL, 			// location of im.xml, example "/content/dam/canada/json/im.json"
21		Adobe,			// Equals to yes if Adobe analytics is set
22
23		// The following variables are used for debugging with query string
24		overrideScope = false, 	// Query parameter sets this to a text value to force the scope (page or site)
25		overrideStart = false, 	// Query parameter sets this to true to ignore the start date of surveys
26		overrideID, 			// Query parameter sets this to an Id value for a specific survey	
27
28
29		/*
30		* Name of session storage values
31		*/
32		storageNames = {
33			
34			session: "im-settings"
35		};
36
37		//var dbURL = dbUrlFromJson();
38
39	try { 
40		
41		// download the dbURL path
42		if (!dbURL)
43		{
44			dbUrlFromJson();
45
46			// asynchronous call
47		}
48		else {
49			
50			mainPart1();
51		}		
52
53	} catch (e) {
54		return false;
55	}
56		
57		
58	/*
59	 * main function Part One - setup the survey data
60	 */
61	function mainPart1 () {
62		if (!dbURL) {
63			return;
64		}
65		// Enable console logging if debug mode is turned on
66		debugMode = checkEnableDebugMode();
67		consoleLog("Start");
68
69		// Check if any special parameters are used for testing reasons
70		checkTestParams();		
71		consoleLog("Check Test Params");	
72
73		/* In order to handle the proper functioning of IM for websites
74		 * that have IM installed on different subdomains from different servers,
75		 * survey serving is now conditioned by the presence of the cookie 'lastDateIMShown' and not
76		 * by the localstorage item 'lastDateIMShown'
77		 * If there is no cookie "lastDateIMShown", the user has not seen a survey
78		 */
79		if ( !getCookie('lastDateIMShown')) {
80			consoleLog("lastDateIMShown === null");
81
82			// Check if DB is already in session storage
83			surveyDB = JSON.parse(sessionStorage.getItem(storageNames.session));
84
85			// Download the DB if it is not already stored 
86			if (!surveyDB) {
87				consoleLog("downloadSurveyDB");
88				downloadSurveyDB();
89				// Asynchronous call, do not execute code here
90			}
91			else {
92				/* To take into consideration the surveys that were visible prior the 'cookie handling' implementation
93				 * the script has to check the localstorage for 'lastDateIMShown' and setup the cookie with that date.
94				 * The expiration of the cookie should be the sum of lastDateIMShown and the survey setting duration-delay value
95				 */
96				if(localStorage.getItem('lastDateIMShown')){
97					var cookieName = 'lastDateIMShown',
98					maxNbDaysIMPersist = new Date(localStorage.getItem('lastDateIMShown'));
99					maxNbDaysIMPersist.setDate(maxNbDaysIMPersist.getDate() + surveyDB.settings["duration-delay"]);
100					setCookie(cookieName,localStorage.getItem('lastDateIMShown'),maxNbDaysIMPersist, removeSubdomain(window.location.href.toLowerCase()));
101				}else{
102					mainPart2();
103				}
104			}
105		}
106		// Case when the user has already seen an invitation
107		else {
108			consoleLog("User has seen an invitation already");
109			downloadSurveyDB();
110			// Asynchronous call may have been made, do not execute code here
111			/* If shown survey was served from another server, make sure the localstorage item 'lastDateIMShown' is set
112			 * with the value from 'lastDateIMShown' cookie.
113			 */			
114			localStorage.setItem('lastDateIMShown', new Date(getCookie('lastDateIMShown')));
115			
116		}
117	}
118
119
120	/*
121	 * main function Part two - select the survey to display
122	 */
123	function mainPart2() {
124		// Make sure the surveyDB object is set, it could not have been defined if 
125		// localStorage was not defined and the json is unfound on the server
126		if (!surveyDB || !surveyDB.settings)
127			return;
128		
129		if (getCookie('lastDateIMShown')) {
130			var	lastDateIMShown = new Date(getCookie('lastDateIMShown'));
131			// If storage delay is expired, remove 'lastDateIMShown' from cookie and localstorage
132			if (isStorageExpired(lastDateIMShown)) {
133				document.cookie = "lastDateIMShown=;expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure;domain=" + removeSubdomain(window.location.href.toLowerCase()) + ";path=/";
134				localStorage.removeItem( "lastDateIMShown" );
135			}
136			else{
137				return;
138			}
139		}
140		
141
142		// At this point, we don't know if it the was just downloaded or if it was 
143		// retrieved from session storage
144		
145		// if the DB doesn't have a scopeHat, then we need to decide on which scope hat to give
146		if (!surveyDB.settings.scopeHat) {
147			firstTimeSetup();
148		}
149
150		// Check if we need to override the scope hat
151		// This is needed since firstTimeSetup won't run if this is not the first page of the visit
152		if (overrideScope) {
153			surveyDB.settings.scopeHat = overrideScope;
154		} 
155
156		surveyScope = surveyDB.settings.scopeHat;
157		consoleLog("Scope hat = " + surveyScope + ", " + surveyDB.surveys.length + " potential surveys, removing surveys outside of date range");
158		
159		// Delete all surveys that are outside of working dates
160		var list = surveyDB.surveys;
161		for (var i = 0; i < list.length; i++) {
162			var n = new Date().getTime();
163			var end_time = new Date(list[i]["end-on"]).getTime();
164			if (n < new Date(list[i]["start-on"]).getTime() || 
165				n >= end_time) {
166				
167				// If overriding start date and still before end date then don't delete this survey
168				if (overrideStart && n < end_time) {
169					continue;
170				}
171				
172				// Splice removes the element from the array
173				list.splice(i,1);
174
175				//decrement i since the next record has moved to replace the current record
176				i--;
177			}
178		}
179
180		consoleLog(list.length + " potential surveys, removing surveys based on targeting");	
181
182		surveyDB.surveys = list;
183
184		// save the DB to session storage
185		sessionStorage.setItem(storageNames.session, JSON.stringify(surveyDB));
186
187		consoleLog("Checking whitelist");
188
189		// Check if url on whitelist
190		var list = getObjArr(surveyDB.settings.whitelist),
191			a = document.location, 
192			//h = a.hostname + ":8080" + a.pathname, 
193			h = a.host + a.pathname,
194			didMatch = false;
195
196		if ( !list ) {
197			return false;
198		}
199
200		for (var i = 0; i < list.length; i++) {
201			var b = new RegExp(list[i].url, "i");
202			if (b.test(h))
203				didMatch = true;
204		}
205
206		if ( !didMatch ) {
207			return false;
208		}
209		
210		consoleLog("Checking blacklist");
211
212		// Check if url is on blacklist
213		var list = getObjArr(surveyDB.settings.blacklist), 
214			a = document.location, 
215			//h = a.hostname + a.pathname;
216			h = a.host + a.pathname;
217
218		for (var i = 0; i < list.length; i++) {
219			var l = list[i].url;
220			if (l !== undefined && l.length !== undefined) {
221				if (l.toLowerCase() == h.toLowerCase()) {
222					return false;
223				}
224			}
225		}
226		
227		consoleLog("Removing surveys based on page attributes");
228
229		// Make a deep copy of the survey DB before we remove the applicable surveys 
230		// This is needed because getPageAttributeMatches will delete any survey that matches from the surveyDB list
231		var cpySurveys = JSON.parse(JSON.stringify(surveyDB.surveys));
232		
233		// b will be the index of a survey in cpySurveys that was chosen otherwise 
234		// it will be undefined if no survey was selected
235		var b = getWeightedRandom(getPageAttributeMatches());
236
237		if ( b ) {
238
239			for (var j = 0; j < cpySurveys.length; j++) {
240
241				if(cpySurveys[j].id === b){
242                			var selectedSurveyIndex = j;
243                			break;
244
245				}
246			}
247			consoleLog("Select survey " + cpySurveys[selectedSurveyIndex].name);
248			// Popup the survey
249			invite(cpySurveys[selectedSurveyIndex]);
250
251			//set lastDateIMShown in localStorage and cookie with the date the visitor was invited
252			if(!getCookie('lastDateIMShown')  && !(/[?&]im_nocookiecheck=1/i.test(document.location.search))){
253				//set a cookie with the domain name and the date visitor was invited 
254				var cookieName = 'lastDateIMShown';				
255				var maxNbDaysIMPersist = new Date(),
256				lastDateIMShown = new Date();
257				maxNbDaysIMPersist.setDate(maxNbDaysIMPersist.getDate() + surveyDB.settings["duration-delay"]);
258				//set the date visitor was invited in localstorage
259				localStorage.setItem('lastDateIMShown', lastDateIMShown);	
260				
261				setCookie(cookieName,lastDateIMShown,maxNbDaysIMPersist, removeSubdomain(window.location.href.toLowerCase()))
262			}
263		}
264		else {
265			consoleLog("No survey selected, " + surveyDB.surveys.length + " surveys remain in session storage");
266			//save the updated DB to session storage
267			//the DB must be saved here since we need to remove surveys that were applicable on this page so we don't test for them again in the visit
268			sessionStorage.setItem(storageNames.session, JSON.stringify(surveyDB));
269		}
270		
271	}
272
273	/*
274	 * Check if debug mode is enabled
275	 */
276	function checkEnableDebugMode() {
277		
278			//Set the local storage token to remember to log console info
279		if (/[?&]logim=(true|1)/i.test(document.location.search))
280			localStorage.setItem('imlog', 1);
281
282		if (/[?&]logim=(false|0)/i.test(document.location.search))
283			localStorage.removeItem('imlog');
284		
285		return (localStorage.getItem('imlog') == 1);
286	}
287	
288
289	/*
290	 * Log console data if debug mode is enabled
291	 */
292	function consoleLog (val) {
293		if (debugMode) {
294			console.log("Invitation Mgr: " + val);
295		}
296	}
297
298
299	/*
300	 * Check if any special parameters are used for testing reasons
301	 */
302	function checkTestParams() {
303		// Process query parameters
304		
305		// Case of cookies
306		if (/[?&]im_nocookiecheck=1/i.test(document.location.search)) {
307			consoleLog("Treat visitor as new visitor (deleting locally stored data)");
308			
309			// Delete this cookie
310			localStorage.removeItem( "lastDateIMShown" );
311			
312		}
313
314		// Case of scope (page or site)
315		if ( (/[?&]im_scope=page/i.test(document.location.search)))
316			overrideScope = "Page";
317		else if ( (/[?&]im_scope=site/i.test(document.location.search)))
318			overrideScope = "Entire site";
319		
320		// Case of date
321		if ( (/[?&]im_nodatecheck=1/i.test(document.location.search)))
322			overrideStart = true;
323		
324		// Case of Survey Id
325		var b = /[?&]im_surveyid=([^?&]+)/.exec(document.location.search);
326		if (b !== null )
327			overrideID = b[1];
328
329	}
330
331
332	/*
333	* Load the survey from json file to storage
334	*/
335	function downloadSurveyDB() {
336
337		$.getJSON(
338		dbURL,
339		function() {
340			consoleLog("Get Json File is Successful");
341		} )
342		.done( function(result) {
343			surveyDB = JSON.parse(JSON.stringify(result));
344			mainPart2();
345		} )
346		.fail( function() {
347			consoleLog( "Fail to get JSON File" );
348		} )
349		.always( function() {
350			consoleLog( "JSON file Complete" );
351		} );
352
353	}
354
355
356	/*
357	 * Take a date object as parameter and test if the storage date is expired
358	 */
359	function isStorageExpired(storageDate) {
360		var now = new Date();
361		var maxNbDaysIMPersist = surveyDB.settings["duration-delay"];
362		if ( ( ( now - storageDate ) <= ( maxNbDaysIMPersist * 86400000 ) ) || !storageDate ) {
363			return false;
364			
365		}
366		return true;
367	}
368
369
370	/*
371	 * First time invitation setup. We need to allocate scope hat and remove
372	 * surveys that don't match the scope hat
373	 */
374	function firstTimeSetup() {
375
376		// tmpSurveyDB is where we will copy all the wanted data from surveyDB into
377		// this removes non applicable scope surveys
378		var tmpSurveyDB = {settings : {}, surveys : []};
379
380		consoleLog("Rolling probability for scope hat");
381
382		if (overrideScope)
383		{
384			surveyScope = overrideScope; // Page or Site
385			consoleLog("surveyScope = overrideScope = " + overrideScope);
386		}
387		else
388		{
389			
390			surveyScope = getWeightedRandom({"Page":surveyDB.settings["page-probability"], "Entire site":surveyDB.settings["site-probability"]});
391			
392			consoleLog("surveyScope = getWeightedRandom = " + surveyScope);
393		}
394		
395		// get an object array of all surveys in the DB
396		var list = getObjArr (surveyDB.surveys);
397
398		consoleLog(list.length + " potential surveys, removing surveys that don't match scope hat");
399
400		for (var i = 0; i < list.length; i++) {
401			
402			// Get only surveys that apply to this scope
403			if (list[i].type == surveyScope) {
404				
405				// Copy good surveys to tmpSurveyDB
406				tmpSurveyDB.surveys[tmpSurveyDB.surveys.length] = list[i];
407
408			}
409		}
410
411		// Put all the needed settings into tmpSurveyDB
412		tmpSurveyDB.settings["page-probability"] = surveyDB.settings["page-probability"];
413		tmpSurveyDB.settings["site-probability"] = surveyDB.settings["site-probability"];
414		tmpSurveyDB.settings.scopeHat = surveyScope;
415		tmpSurveyDB.settings.whitelist = surveyDB.settings.whitelist;
416		tmpSurveyDB.settings.blacklist = surveyDB.settings.blacklist;
417		tmpSurveyDB.settings["duration-delay"] = surveyDB.settings["duration-delay"];
418		
419		//set surveyDB to tmpSurveyDB so we can be sure that surveyDB is the good variable
420		surveyDB = tmpSurveyDB;
421		
422	}
423
424
425	/*
426	 * Given a JSON array of values and probabilities, output the weighted random selection.
427	 * For example {coffee: 0.20, tea: 0.80}
428	 * Output nothing if no selection is made (I.E. undefined)
429	 */
430	function getWeightedRandom(spec) {
431		
432		var i, sum =0, r = Math.random();
433		for (i in spec) {
434			
435			// be sure to never select something with 0% probability,
436			// since Math.random() can be 0
437			if (spec[i] === 0){
438				continue;
439			}
440
441			sum += parseFloat(spec[i]); //Number(spec[i]);
442			if (r <= sum)
443				return i;
444		}
445
446	} 
447
448	/*
449	 * return an object array containing the given parameter.
450	 * This allows us to loop through the object array whether there are 0, 1 or more objects in the array
451	 */
452	function getObjArr(o){
453		
454		if (!o) {
455			return [];
456		}
457		else if (Array.isArray(o)) {
458			return o;
459		}
460		else {
461			return [o];
462		}
463		
464	}
465
466
467	/*
468	 * Return a list of valid surveys based on page or site
469	 */
470	function getPageAttributeMatches() {
471
472		// Return list of valid surveys based on page / site and check if override survey id needed
473
474		// Remove surveys that are valid from the surveyDB array so that when we save it, 
475		// those ones are removed and not tested on future pages in the visit
476		
477    	var surveySubList = {};
478		//var url = document.location.hostname + document.location.pathname;
479		var url = document.location.host + document.location.pathname;
480		var count = 0;
481		
482		for (var i = 0; i < surveyDB.surveys.length; i++) {
483			
484			var survey_i = surveyDB.surveys[i];
485			
486			if (overrideID) {
487				
488				// Skip this loop's logic if it's not the desired survey, 
489				// therefore there's no chance it will be selected
490				if (survey_i.id !== overrideID){
491				
492					continue;
493				}  
494
495				// If it is the desired survey, set it's probability to 100%. 
496				// Note that the desired survey must still be applicable on this page 
497				// so the logic in the swtich statement still must execute meet the conditions
498				survey_i.probability = 100;
499			}
500			
501			switch(survey_i.type) {
502
503				case "Entire site":
504				
505					// Get an object array of the sites to run the survey on
506					var sublist = getObjArr(survey_i["survey-urls"]);
507					
508					for (var c = 0; c < sublist.length; c++) {
509						
510						//convert the value for the site into RegExp non-case sensitive
511						var b = new RegExp(sublist[c].site, "i");
512						
513						//if the url matches the regEx
514						if (b.test(url)) {
515							
516							// Store this survey's id and probability in the new object 
517							// (this object is later fed to getWeightedRandom)
518
519							// Convert site survey probability into rate since it is 
520							// between 0 and Visitor Allocation for site wide type
521						
522							surveySubList[survey_i.id] = 
523								survey_i.probability * surveyDB.settings["site-probability"];
524
525							// Keep track of the number of surveys that apply so we can 
526							// console log it later
527							count++;
528
529							// Remove this survey from the DB, since we won't test for it on future pages
530							surveyDB.surveys.splice(i,1);
531							
532							// Decrement i since the next record has moved to replace the current record
533							i--;
534						}
535					}
536					
537					break;
538				
539					
540				case "Page":
541
542					var sublist = getObjArr(survey_i["survey-urls"]);
543					
544					var wLocation = window.location.href.toLowerCase();
545
546					for (var c = 0; c < sublist.length; c++) {
547
548						if (wLocation.indexOf(sublist[c].url.toLowerCase()) !== -1 && i>=0) {
549							surveySubList[survey_i.id] = survey_i.probability * surveyDB.settings["page-probability"];
550							count++;
551							
552							// Remove the eligible survey from the DB, since we won't test for it on future pages
553							surveyDB.surveys.splice(i,1);
554
555							// Decrement i since the next record has moved to replace the current record
556							i--;
557						}
558					}
559					
560					break;
561			}		
562		}
563		
564		consoleLog(count + " potential surveys, selecting a survey");
565	
566		return surveySubList;
567	}
568
569
570	/*
571	 * Display the popup given the survey parameters
572	 */
573	function invite(survey) {
574		var referrerPath = encodeURIComponent(window.location.pathname),
575			surveyLink = survey["link-" + wb_im.lang],
576			delimiter = "?",
577			popupInput;
578
579		if ( surveyLink.includes( "?" ) ) {
580			delimiter = "&";
581		}
582
583		if (Adobe.toLowerCase() === "yes")
584		{
585			popupInput = "<input type='hidden' name='popupName' value='" + survey["name"] + "'>";
586		} 
587		else
588		{
589			popupInput = "";
590		}
591	
592		var html =  
593		"<aside id='gc-im-popup' class='asideBody gc-im-wb-overlay gc-im-modal-content gc-im-overlay-def gc-im-wb-popup-mid shadow' tabindex='0' >" +
594			"<header class='gc-im-modal-header'>" +
595				"<div class='gc-im-modal-title'>" + 
596				"<div class='gc-im-modal-title-fix1'>" + survey["title-" + wb_im.lang] + "</div>" +
597					"<div class='gc-im-modal-title-fix2'>" + "<button id='close-im' type='button' class='gc-im-overlaydef closeIcon zoomX' aria-label='Close' >&times;</button>" +
598					"</div>" +
599					"<div class='clearfix'></div>" +
600				"</div>" + // for the close icon
601			"</header>" +
602			"<div class='gc-im-modal-body'>" +
603				survey["body-" + wb_im.lang] +
604				"<ul class='list-inline mrgn-sm'>" +
605			"<li class='mrgn-sm marginBottom-yes'><a id='survey-yes' class='gc-im-btn gc-im-btn-primary' href='" + survey["link-" + wb_im.lang] + delimiter + "ref=" + referrerPath + "' target='_blank' referrer-policy='unsafe-url'>" + survey["yes-" + wb_im.lang] + "</a></li> " + 
606					"<li class='mrgn-sm marginBottom-no'><button id='survey-no' class='gc-im-btn gc-im-btn-secondary survey-close'>" + survey["no-" + wb_im.lang] + "</button></li>" +
607				"</ul>" +
608				popupInput +
609			"</div>" +
610			"<div class='gc-im-modal-footer  hidden'>" +
611		
612			"</div>" +
613		"</aside>",
614		
615		$html = $( html ),
616		$userFocus = false,
617		overlayIsClosing,
618		focusFlag;
619
620		// Close the overlay if any of its links/buttons get clicked or if the escape key gets pressed.
621		$html.bind( "click vclick mouseup keydown", function( e ) {	
622	  	// Proceed if any of the overlay's links or buttons get clicked (including middle mouse clicks) 
623		// or if the escape key gets pressed within the overlay. 
624	  	if (
625		  ( ( e.type === "click" || e.type === "vclick" ) && e.which === 1 && $( e.target ).closest( "a, button", this ).length ) // Clicked/Tapped a link/button.
626		  ||
627		  ( e.type === "mouseup" && e.which === 2 && $( e.target ).closest( "a", this ).length ) // Middle-clicked a link.
628		  ||
629		  ( ( e.type === "keydown" ) && ( e.which === 27 ) ) // Pressed escape key.
630		) {
631		
632		
633			// add to remove added classes to overlay when closing
634			$html.removeClass( "open" );
635			$("#first-focus").remove();
636			$( "#wb-info" ).removeClass( "im-mrgn" );		
637					
638		  	// Set a flag to indicate the overlay is closing.
639		  	// Needed to prevent IE11 (possibly also IE8-10/Edge) from failing to return 
640			// user focus when closing the overlay (due to a separate focusin event triggering 
641			// too quickly and clearing the user focus variable before it's needed).
642		  	overlayIsClosing = 1;
643		  
644		  	// Hide the overlay immediately.			
645		  	$( this ).hide();
646		  
647		  	// Remove the overlay shortly afterwards.
648		  	// This is being done to prevent problems when the yes link is middle-clicked. If the overlay were to be immediately removed, middle-clicking the yes link would remove the overlay without opening the link in a new tab/window. To avoid that issue, the overlay is now getting immediately hidden, then removed a short time later.
649		  	setTimeout( function() { $html.empty() }, 1000 );
650			
651			// Remove this event handler.
652			$( this ).off();
653	  	}
654		
655		// if Tab in the NO button then focus will be triggerred outside of the popup
656		if ( ( e.type === "keydown" ) && ( e.which === 9 ) ) // Pressed the tab key.
657		{
658			if (e.target.id === "survey-no")
659			{	
660				setTimeout( function() {
661					$("#wb-tphp").trigger("focus");
662				},1000);	
663			}	
664		}
665		
666		// if shift-Tab in the close button then focus will be triggerred outside of the popup
667		if ( ( e.type === "keydown" ) && ( e.which === 9 ) && (e.which === 16)) // Pressed the tab key and shift key
668		{
669			if (e.target.id === "close-im")
670			{	
671				setTimeout( function() {
672					$("#wb-tphp").trigger("focus");
673				},1000);	
674			}	
675		}
676		
677		} );
678	
679
680		// Insert the overlay directly before the <main> element.
681		$( "main" ).before( $html );
682
683		// Since jQuery fix will sanitize the target attribute, here is a quick solution to add it back right after the IM html popup is inserted
684		$("#survey-yes").attr("target", "_blank");
685		
686		// Inset the "Skip to Invitation Manager Popup" link before the <main> element.
687		if (wb_im.lang === "fr")
688		{
689			$( "#wb-tphp" ).prepend("<li id='first-focus' class='wb-slc'>" +
690			"<a  class='wb-sl' href='#gc-im-popup'>Passer au Gestionnaire des Invitations</a></li> ");
691		}
692		else {
693			$( "#wb-tphp" ).prepend("<li id='first-focus' class='wb-slc'>" +
694			"<a   class='wb-sl' href='#gc-im-popup'>Skip to Invitation Manager Popup</a></li> ");
695		}
696		
697		// trigger the init and open event of the overlay
698		$( "#gc-im-popup" ).trigger( "wb-init.gc-im-wb-overlay" );
699		$( "#gc-im-popup" ).trigger( "open.gc-im-wb-overlay" );
700		
701		// add for Adobe tracking
702		if (!$html.is(":hidden") && Adobe.toLowerCase() === "yes")
703		{
704			var popUpName = $("input[name='popupName']").val();
705                   
706			_satellite.setVar('jsPopupName', popUpName);
707			//direct call rule
708			_satellite.track('popUpImpression');
709			
710		}
711		
712		// Correct popup positionning on load, on resize an on Y scroll if necessary
713		$( window ).on( "resize scroll", function() {
714			
715			
716			// Equals to popup default bottom value in CSS
717			var bottomY = 25;
718			var $footer = $( "#wb-info" );
719			
720			if (!$html.is(":hidden"))
721			{
722				// Add some white space over the footer for the popup to sit
723				$footer.addClass( "im-mrgn" );
724			}
725			else{
726				$footer.removeClass( "im-mrgn" );
727			}
728			if ( $( window ).scrollTop() >= $( document ).outerHeight() - $( window ).outerHeight() - $footer.outerHeight() ) {
729					$html.css( {
730						bottom: ( $footer.outerHeight() - ( $( document ).outerHeight() - $( window ).outerHeight() - $( window ).scrollTop() ) + bottomY )
731					} );
732				} else {
733					$html.css( {
734						bottom: bottomY
735					} );
736				}
737		} );
738
739		
740  	}
741
742	/*
743	* get the dbURL path from the config.json file
744	*/
745	function dbUrlFromJson() {
746		// find the path of the current javascript file "InvitationManager.js"		
747		var scripts = document.getElementsByTagName("script"),
748		i = 0,
749		path = "";
750		while (i < scripts.length)
751		{
752			var src = scripts[i].src;
753			if (src.indexOf("InvitationManager.js") > -1) 
754			{
755				path = src.substring(0,src.lastIndexOf("/")+1);
756				break;
757			}
758			i++;
759		}
760		
761		/* window.imConfigPath stores the path of the config.JSON file.
762		 * if not defined, the script will check for config.JSON in the same directory where InvitationManger.js is located
763		 */
764		if (window.imConfigPath) 
765		{
766			path = window.imConfigPath;
767		}
768		
769		// get the config.json file
770		$.getJSON(
771			path + "config.JSON",
772			//"/invitation-manager/config.JSON",
773			function() {
774				consoleLog("Get Config File Path is Successful");
775			})
776			.done( function (result) {
777				var myConfig = JSON.parse( JSON.stringify( result ) );;
778        		dbURL = myConfig.dbURL;
779				Adobe = myConfig.Adobe;
780				if (dbURL) {
781					mainPart1();
782				}
783				else {
784					return;
785				}
786			})
787			.fail( function() {
788				consoleLog("Get Config File Path Failed");
789			})
790			.always( function() {
791				consoleLog("Get Config File Path is Completed");
792			});
793	}
794
795	/**
796	 * Return the domain name without the subdomain.
797	 * @param {string} url The domain url to be cleaned
798	 * @return {string} url without the subdomain;
799	 */
800	function removeSubdomain (url) {
801		const urlSegments = new URL(url).hostname.split('.')
802		// consider domains names having .gc or .qc before the domain name suffix
803		var arrIndex = ['gc', 'qc'].indexOf(urlSegments.slice(-2)[0]) >= 0 ?3:2;
804		return urlSegments.slice(0).slice(-(arrIndex)).join('.')
805	}
806
807	/**
808	 * Return the cookie value for a specific cookie.
809	 * @param {string} name cookie name
810	 * @return {string} cookie value
811	 */
812	function getCookie(name){
813		if (document.cookie.split(';').some((item) => item.trim().startsWith(name+'='))) {
814			return document.cookie.split('; ').find(row => row.startsWith(name.trim())).split('=')[1]
815		}
816		return false ;
817	}
818
819	/**
820	 * Set a cookie.
821	 * @param {string} cName cookie name
822	 * @param {Date} cValue cookie value
823	 * @param {Date} cExpDate cookie expiration date
824	 * @param {string} cDomain cookie domain
825	 */
826	function setCookie(cName,cValue,cExpDate,cDomain){
827		document.cookie = cName +"=" + cValue + ";expires=" + cExpDate 
828				+ ";domain=" + cDomain + ";Secure;path=/";
829	}
830}

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.