PageSourceSearch

https://www.cse-cst.gc.ca/invitation-manager/InvitationManager.js

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

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.