PageSourceSearch

https://mypridenet.springfield.edu/js/scripts.js?v=20251007.1

js springfield.edu collected 2026-09-24 09:21:35 UTC 151,684 bytes, 4,270 lines download raw bytes

1/*!
2 * This file is used to encompass JS functions.
3 * Copyright 2011-2018 Novalsys, Inc - v.1.0.0 (01/2018)
4 * Use @FEATURE to find it. (@GENERAL for platform-wide functions)
5 */
6
7/*-----------------------------------------------------*\
8	@GENERAL
9\*-----------------------------------------------------*/
10	/* Function called to post video views.
11		* @param uid        : The video UID
12		* @param uid_s      : The encrypted video UID
13		* @param vTime      : The current video time
14		* @param bFinished  : Possible additional options, confirming that the video has been seen.
15	*/
16	function postVideoViews(uid, uid_s, vTime) {
17        var bFinished = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "";
18
19        $.ajax({
20            method: "POST",
21            url: "/video_views_endpoint",
22            data: { uid: uid, _s: uid_s, vtime: vTime, f: bFinished },
23            success: function () {
24                // Once the video is finished AND its view has been recorded, refresh the
25                // checklist so the just-completed item is re-evaluated server-side and the
26                // next item in a sequential checklist is un-greyed. Only fire on the
27                // finished call (not the 10s progress pings, which pass an empty flag).
28                if (bFinished === "true") {
29                    if (typeof refreshTrackChecklist !== "undefined") { refreshTrackChecklist(); }
30                    else if (typeof parent.refreshTrackChecklist !== "undefined") { parent.refreshTrackChecklist(); }
31                }
32            }
33        });
34    }
35
36	/* Function called to check if an element is in the viewport.
37		* @param elem  : The element
38	*/
39    function isInViewport(elem) {
40        var bounding = elem.getBoundingClientRect();
41        return (
42            bounding.top >= 0 &&
43            bounding.left >= 0 &&
44            bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
45            bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
46        );
47    };
48
49	/* Function called to delete a particular object.
50		* @param id         : The HTML object id
51		* @param dest_e     : The encrypted destination
52		* @param uid        : The uid to be updated
53		* @param _s         : The security token
54		* @param options    : Possible additional options, URL parameter style (ex: param=123&other=456). 
55							  Add it as last function parameter.
56	*/
57	function deleteContent(id, dest_e, uid, _s) {
58		var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : "";
59
60		var data = { dest_e: dest_e, uid: uid, _s: _s };
61		if ( options !== "" ) {
62			var arrOptions = options.split("&");
63			arrOptions.forEach(function (currentValue, index, arr) {
64				arr[index] = currentValue.split("=");
65			});
66			for (i = 0; i < arrOptions.length; i++) { 
67				data[arrOptions[i][0]] = arrOptions[i][1]
68			}
69		}
70		jQuery.ajax({
71			type: "POST",
72			cache: false,
73			url: "/delete.aspx",
74			data: data,
75			success: function (html) {
76				cmsg('Content deleted.', 1, 0);
77				$('#' + id + '').fadeOut();
78				setTimeout("document.getElementById('generic').style.display='none';", 2000);
79			}
80		});
81	}
82
83	/* Function called to delete a particular object.
84		* @param id                 : The HTML object id
85		* @param dest_e             : The encrypted destination
86		* @param uid                : The uid to be updated
87		* @param _s                 : The security token
88		* @param callbackFunction   : The callback function
89		* @param callbackParameters : The callback parameters
90		* @param options    		: Possible additional options, URL parameter style (ex: param=123&other=456). 
91						 			  Add it as last function parameter.
92	*/
93	function deleteContentWithCallback(id, dest_e, uid, _s, callbackFunction, callbackParameters) {
94		var options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : "";
95		
96		var data = { dest_e: dest_e, uid: uid, _s: _s };
97		if ( options !== "" ) {
98			var arrOptions = options.split("&");
99			arrOptions.forEach(function (currentValue, index, arr) {
100				arr[index] = currentValue.split("=");
101			});
102			for (i = 0; i < arrOptions.length; i++) { 
103				data[arrOptions[i][0]] = arrOptions[i][1]
104			}
105		}
106		jQuery.ajax({
107			type: "POST",
108			cache: false,
109			url: "/delete.aspx",
110			data: data,
111			success: function (html) {
112				cmsg('Content deleted.', 1, 0);
113				if(id) {
114					$('#' + id + '').fadeOut();
115				}
116				setTimeout("document.getElementById('generic').style.display='none';", 2000);
117
118				//Call the callbackFunction with the list of parameters
119				callbackFunction.apply(this, callbackParameters);
120			}
121		});
122	}
123
124	/* Function called to duplicate a particular object.
125		* @param dest_e             : The encrypted destination
126		* @param uid                : The uid to be updated
127		* @param _s                 : The security token
128		* @param callbackFunction   : Optional - The callback function
129		* @param callbackParameters : Optional - The callback parameters
130		* @param options            : Optional - Possible additional options, URL parameter style (ex: param=123&other=456). 
131									  Add it as last function parameter.
132	*/
133	function duplicateData(dest_e, uid, _s) {
134		var callbackFunction    = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () {};
135		var callbackParameters  = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
136		var options             = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "";
137
138		var data = { dest_e: dest_e, uid: uid, _s: _s };
139		if (options !== "") {
140			var arrOptions = options.split("&");
141			arrOptions.forEach(function (currentValue, index, arr) {
142				arr[index] = currentValue.split("=");
143			});
144			for (i = 0; i < arrOptions.length; i++) {
145				data[arrOptions[i][0]] = arrOptions[i][1];
146			}
147		}
148		jQuery.ajax({
149			type: "POST",
150			cache: false,
151			url: "/duplicate",
152			data: data,
153			dataType: "json",
154			success: function success(res) {
155				cmsg('Content duplicated.', 1, 0);
156				var responseObj = res;
157				if (responseObj.alertHTML != undefined) { displayAlertMessage(responseObj.alertHTML, 'success') }
158				if (responseObj.redirectURL != undefined) { window.location.href = responseObj.redirectURL; }
159
160				callbackFunction.apply(this, callbackParameters);
161			}
162		});
163	}
164
165	/* Function called to update a particular object with inline editing.
166		* @param message            : The CMSG input
167		* @param obj                : The HTML object used for the input action
168		* @param type               : The HTML object type (text, checkbox)
169		* @param dest               : The destination (TODO: encrypt)
170		* @param uid                : The uid to be updated
171		* @param _s                 : The security token
172		* @param callbackFunction   : Optional - The callback function
173		* @param callbackParameters : Optional - The callback parameters
174	*/
175	function inlineMod(message, obj, type, dest, uid, _s) {
176		var callbackFunction    = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : function () {};
177		var callbackParameters  = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : [];
178
179		if (editionEnCours) {
180			return false;
181		}
182		else {
183			editionEnCours = true;
184			sauve = false;
185		}
186
187		var input = null;
188
189		if (type == 'text' || type == 'textn') {    // Is not a checkbox: assignation de la valeur
190			if (obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
191			obj.innerHTML = '<input style="width:95%; background-color:#fff; padding-left:3px; border:0px;font-family:arial;font-size:1em;color:#333;" id="ajax_text" value="' + obj.innerHTML + '" />';
192			input = document.getElementById('ajax_text');
193
194			input.focus();
195			input.select();
196		}
197		else if (type == 'text_survey') {
198			if (obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
199			input = document.getElementById('ajax_text_' + uid);
200
201			input.focus();
202			input.select();
203			type = "text";
204		}
205		else if (type == 'textarea') {
206			obj.setAttribute('aria-expanded', 'true');
207			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
208			obj.innerHTML = '<textarea style="width:95%; background-color:#fff; border:0px;font-family:arial;font-size:1em;color:#333;" id="ajax_textarea" style="overflow: hidden" rows="10">' + obj.innerHTML + '</textarea>';
209			input = document.getElementById('ajax_textarea');
210
211			input.focus();
212		}
213		else if (type == 'textarea_feed') {
214			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
215			obj.innerHTML = '<textarea id="ajax_textarea" rows="5">' + obj.innerHTML.replace(/<br\s*\/?>/mg, "\n"); + '</textarea>';
216			input = document.getElementById('ajax_textarea');
217			input.focus();
218			type = "textarea_feed";
219
220		}
221		else if (type == 'textarea_slider_') {
222			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
223			obj.innerHTML = '<textarea style="width:95%; background-color:#fff; border:0px;font-family:arial;font-size:1em;color:#333;" id="ajax_textarea" style="overflow: hidden" rows="5">' + obj.innerHTML + '</textarea>';
224			input = document.getElementById('ajax_textarea');
225
226			input.focus();
227			type = "textarea";
228		}
229		else if (type == 'textarea_slider') {
230			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
231			//obj.innerHTML = '<textarea style="width:95%; background-color:#fff; border:0px;font-family:arial;font-size:1em;color:#333;" id="ajax_textarea" style="overflow: hidden" rows="5">' + obj.innerHTML + '</textarea>';
232			input = document.getElementById('ajax_textarea');
233			input.focus();
234			type = "textarea";
235		}
236		else if (type == 'text_slider') {  
237			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_f
237ield\">Empty</span>') { obj.innerHTML = '' }
238			input = document.getElementById('ajax_text');
239
240			input.focus();
241			input.select();
242			//type = "text";
243		}
244		else if (type == 'textarea_survey') {
245			if (Right(obj.innerHTML, 3) == '...' || obj.innerHTML == 'Empty' || obj.innerHTML == '<span class=\"empty_field\">Empty</span>') { obj.innerHTML = '' }
246			input = document.getElementById('ajax_textarea_' + uid);
247			input.focus();
248			type = "textarea";
249		}
250		else //checkbox
251		{
252			input = document.createElement("checkbox");
253			input.type = "checkbox";
254			input.setAttribute("checked", "checked");
255
256			obj.replaceChild(input, obj.firstChild);
257			input.focus();
258		}
259
260		input.onblur = function sortir() {
261			var strPassedDate;
262			strPassedDate = input.value;
263			if (strPassedDate == '') { strPassedDate = 'Empty'; }
264			if (type == 'textarea_feed') {
265				strPassedDate = strPassedDate.replace(/\n/g, "<br>");
266				type = "textarea";
267				location.reload();
268			}
269			if (type == 'text_slider') {
270				console.log('test');
271				document.getElementById("photoTextLegend").innerHTML = input.value;
272				$("#mainImageInfo").toggle();             
273				$("#frmEdit").toggle();
274				type = "text";
275			}
276			updateData(message, obj, type, dest, strPassedDate, uid, _s);
277
278			if (dest == 'file_uploads-student_comments') {
279				document.getElementById("descriptionText").innerHTML = input.value;
280				$("#frmEdit").hide();
281				$("#mainImageInfo").toggle();
282			}
283
284			if(obj) {
285				obj.setAttribute('aria-expanded', 'false');
286				let defaultAccLabel = obj.getAttribute('data-default-acc-label');
287				if (!isEmpty(defaultAccLabel)) {
288					let newAriaLabel = getSafeAriaLabelTextTrim(defaultAccLabel + ' ' + input.value);
289					obj.setAttribute('aria-label', newAriaLabel);
290				}
291			}
292
293			//delete input;
294
295			//Call the callbackFunction with the list of parameters
296			callbackFunction.apply(this, callbackParameters);
297		}
298
299		input.onkeydown = function keyDown(event) {
300			if (!event && window.event) {
301				event = window.event;
302			}
303		}
304	}
305
306	/* Function called to pin a particular object (mostly used in listings).
307		* @param object             : The HTML button object used for the click action
308		* @param strId              : The ID of the pinned element
309		* @param strType            : The type of the pinned element
310		* @param strTypeButton      : The class for the new pin button (after click)
311		* @param strStudentID       : The student ID
312		* @param strTypeButton      : The class for the new unpinned button (after click)
313	*/
314	function onPinned(object, strId, strType, strTypeButton, strStudentID, strUnpinnedClass) {
315		strUnpinnedClass = strUnpinnedClass ? strUnpinnedClass : 'btn btn-grey';
316		var isPinned = !$(object).hasClass(strUnpinnedClass);
317
318
319		if(isPinned) { //We unpin
320			jQuery.ajax({
321				type: "POST",
322				cache: false,
323				url: "/update_favorites.aspx",
324				data: { id: strId, student_id: strStudentID, type: strType, like: 0 },
325				success: function () {
326					cmsg('Element Unpinned', 2, 0);
327				}
328			});
329			$(object).removeClass(strTypeButton).addClass(strUnpinnedClass);
330			$(object).attr('aria-label', $(object).attr('data-aria-label-unpinned'));
331		}
332		else { //We pin
333			jQuery.ajax({
334				type: "POST",
335				cache: false,
336				url: "/update_favorites.aspx",
337				data: { id: strId, student_id: strStudentID, type: strType, like: 1 },
338				success: function () {
339					cmsg('Element Pinned', 2, 0);
340				}
341			});
342			$(object).removeClass(strUnpinnedClass).addClass(strTypeButton);
343			$(object).attr('aria-label', $(object).attr('data-aria-label-pinned'));
344		}
345	}
346
347	/* Function called to pin a particular object (mostly used in listings).
348		* @param object             : The HTML button object used for the click action
349		* @param strId              : The ID of the pinned element
350		* @param strType            : The type of the pinned element
351		* @param strTypeButton      : The class for the new pin button (after click)
352		* @param strStudentID       : The student ID
353		* @param strTypeButton      : The class for the new unpinned button (after click)
354	*/
355	function onFeedPinned(object, strId, strType, strTypeButton, strStudentID, strUnpinnedClass) {
356		strUnpinnedClass = strUnpinnedClass ? strUnpinnedClass : 'btn btn-grey';
357		var isPinned = !$(object).hasClass(strUnpinnedClass);
358
359		console.log(isPinned);
360
361		if(isPinned) { //We unpin
362			jQuery.ajax({
363				type: "POST",
364				cache: false,
365				url: "/mobile_ws/v18/mobile_pin_post.aspx",
366				data: { id: strId, student_id: strStudentID, type: strType, like: 0 },
367				success: function () {
368					cmsg('Element Unpinned', 2, 0);
369				}
370			});
371			$(object).removeClass(strTypeButton).addClass(strUnpinnedClass);
372		}
373		else { //We pin
374			jQuery.ajax({
375				type: "POST",
376				cache: false,
377				url: "/mobile_ws/v18/mobile_pin_post.aspx",
378				data: { id: strId, student_id: strStudentID, type: strType, like: 1 },
379				success: function () {
380					cmsg('Element Pinned', 2, 0);
381				}
382			});
383			$(object).removeClass(strUnpinnedClass).addClass(strTypeButton);
384		}
385	}
386
387	/* Function called to update a particular object.
388		* @param message    : The CMSG input
389		* @param obj        : The HTML object used for the input action
390		* @param type       : The HTML object type (text, checkbox)
391		* @param dest       : The destination (TODO: encrypt)
392		* @param data       : The data
393		* @param uid        : The uid to be updated
394		* @param _s         : The security token
395		* @param _csrf      : Optional The Cross-site request forgery token
396	*/
397	function updateData(message, obj, type, dest, data, uid, _s, csrf = "") {
398		return updateDataWithCallback(message, obj, type, dest, data, uid, _s, null, [], csrf);
399	}
400	
401
402	/* Function called to update a particular object, with a callback.
403		* @param message            : The CMSG input
404		* @param obj                : The HTML object used for the input action
405		* @param type               : The HTML object type (text, checkbox)
406		* @param dest               : The destination (TODO: encrypt)
407		* @param data               : The data
408		* @param uid                : The uid to be updated
409		* @param _s                 : The security token
410		* @param callbackFunction   : Optional callback function invoked after update (success or failure)
411		* @param callbackParameters : Optional callback parameters
412		* @param _csrf              : Optional The Cross-site request forgery token
413	*/
414	function updateDataWithCallback(message, obj, type, dest, data, uid, _s, callbackFunction = null, callbackParameters = [], csrf = "") {
415		if (type == 'checkbox') { if (data) { data = 1 } else { data = 0 } }
416		jQuery.ajax({
417			type: "POST",
418			cache: false,
419			url: "/update_data.aspx",
420			data: { dest: type + "-" + dest, data: data, uid: uid, _s: _s, _csrf: csrf },
421			success: function (html) {
422				if (html.replace(' ', '') != 'Failed') {
423					editionEnCours = false;
424					if (type != 'checkbox' && type != 'dropdown' && type != 'checkboxes' && type != 'number') {
425						if (!data) { data = '<span class=\"empty_field\">Empty</span>' }
426						if (obj && obj.firstChild) { obj.replaceChild(document.createTextNode(unescape(data)), obj.firstChild) };
427					}
428					//refresh the page when we activate or desactivate an officer
429					if (dest == "members-active_officer" || dest == "students-verified1" || dest == "rsvp-timeslot_ids") {
430						//location.reload();
431					}
432					if (dest == "file_uploads-checked_out_by" || dest == "events-refresh_qrcode") {
433						location.reload();
434					}
435					if (dest == "students-profile_photo_id" || dest == "students-cover_photo_id" || dest == "students-cover_photo_id" || dest == "clubs-logo_id" || dest == "clubs-cover_id" || dest == "events-flyer_id") {
436						if (document.getElementById('bigPhotoId')) {
437							window.parent.document.getElementById('TB_iframeContent').src = 'resize.aspx?request_type=iframe&file_id=' + document.getElementById('bigPhotoId').value;
438						}
439					}
440					if (obj && obj instanceof HTMLElement) {
441						let defaultAccLabel = obj.getAttribute('data-default-acc-label');
442						if (!isEmpty(defaultAccLabel)) {
443							let newAriaLabel = getSafeAriaLabelTextTrim(defaultAccLabel + ' ' + data);
444							obj.setAttribute('aria-label', newAriaLabel);
445						}
446					}
447				} else {
448					alert('Sorry, data update failed.<br/>Please log in again.');
449				}
450				if (typeof callbackFunction === 'function') { callbackFunction.apply(this, callbackParameters); }
451			}
452		});
453		if (message != 'nomsg') {
454			if (message != '') {
455				if (message.length > 10) {
456					cmsg(message, message.length / 15, 0)
457				}
458				else {
459					cmsg(message, 1, 0)
460				}
461			}
462			else {
463				cmsg('Data saved', 1, 0)
464			}
465		}
466		return true;
467	}
468
469	/* Function returning the HTML for the loader.
470		* @param message : The message to display under the loader.
471	*/
472	function writeLoading(message) {
473		if (message == "") { message = "Loading"};
474		return "<p class='loader'>" 
475					+ "<img alt='Loading' src='/images/loader-big.gif'>"
476					+ "<br> " + message
477				+ "</p>"
478	}
479
480
481	/* Function used to update joins table.
482		* @param dest_e 			: The encrypted destination
483		* @param f_field_e 			: The encrypted first key field name (of the join table)
484		* @param s_field_e 			: The encrypted second key field name (of the join table)
485		* @param f_table_e			: The encrypted first key table name 
486		* @param f_uid 				: The first key uid
487		* @param f_uid_s            : The first key security token
488		* @param s_table_e			: The encrypted second key table name
489		* @param s_uid 				: The second key uid
490		* @param s_uid_s            : The second key security token
491		* @param data 				: The data input (0 || 1)
492		* @param callbackFunction   : Optional - The callback function
493		* @param callbackParameters : Optional - The callback parameters
494	*/
495	function saveJoin(dest_e, f_field_e, s_field_e, f_table_e, f_uid, f_uid_s, s_table_e, s_uid, s_uid_s, data) {
496		var callbackFunction    = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : function () {};
497		var callbackParameters  = arguments.length > 11 && arguments[11] !== undefined ? arguments[11] : [];
498
499		var action = ""
500		if (data) { action = "add" } else { action = "del" }
501		jQuery.ajax({
502			type: "POST",
503			cache: false,
504			url: "/ajax_save_joins",
505			data: 
506				{ 	dest_e      : dest_e,
507					f_field_e	: f_field_e,
508					s_field_e   : s_field_e,
509					f_table_e   : f_table_e,
510					f_uid       : f_uid,
511					f_uid_s     : f_uid_s,
512					s_table_e   : s_table_e,
513					s_uid     	: s_uid,
514					s_uid_s     : s_uid_s,
515					action      : action 
516				},
517			success: function () {
518				cmsg("Data saved", 1, 0);
519				callbackFunction.apply(this, callbackParameters);
520			}
521		});
522	}
523
524
525	/* Function used to update ordering for drag & drop.
526		* @param dest_e 			: The encrypted destination
527		* @param field_name_e		: The encrypted ordering field name
528		* @param item_id 			: The moved item id
529		* @param item_position 		: The new position of the item
530		* @param security_field_e 	: Either 'school' or 'club' to identify the security param to check (encrypted)
531		* @param filter_name_e 		: The encrypted first filtering field name
532		* @param filter_name_opt_e 	: The encrypted second filtering field name (optional)
533	*/
534	function saveOrdering(dest_e, field_name_e, item_id, item_position, security_field_e, filter_name_e, filter_name_opt_e) {
535		jQuery.ajax({
536			type: "POST",
537			cache: false,
538			url: "/ajax_save_ordering",
539			data: 
540			{ 
541				dest_e 				: dest_e,
542				field_name_e 		: field_name_e,
543				item_id 			: item_id,
544				item_position 		: item_position,
545				security_field_e 	: security_field_e,
546				filter_name_e 		: filter_name_e,
547				filter_name_opt_e 	: filter_name_opt_e 
548			},
549			success: function () {
550				cmsg("Ordering saved", 1, 0);
551				if (a11yAnnounceToScreenReader) {
552					a11yAnnounceToScreenReader('Drag and drop. Ordering saved.');
553				}
554			},
555			error: function () {
556				if (a11yAnnounceToScreenReader) {
557					a11yAnnounceToScreenReader('Drag and drop. Failed to save ordering.');
558				}
559			}
560		});
561	}
562
563
564	/* Function used to update user settings (preferences & views).
565		* @param settingName 		: The setting name
566		* @param settingValue		: The setting value
567		* @param clubID				: A club ID (optional)
568
569	*/
570	function saveSettings(settingName, settingValue) {
571		var clubID = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "";
572		jQuery.ajax({
573			type: "POST",
574			cache: false,
575			url: "/settings_endpoint",
576			data: 
577			{ 
578				name  : settingName,
579				value : settingValue,
580				club  : clubID
581			},
582			success: function () {
583				//Nothing
584			}
585		});
586	}
587
588
589
590	/* Function called to make a button loading.
591		* @param button  : The button element.
592	*/
593	function loadButton(button) {
594		var buttonHtml = button.html();
595		var buttonWidth = button.css('width');
596		var backgroundColor = button.css('background-color');
597
598		button.attr("data-width", buttonWidth);
599		button.attr("data-html", buttonHtml);
600		button.attr("data-color", backgroundColor);
601
602		button.html('<span class="glyphicon glyphicon-refresh mdi-custom_spin"></span>');
603		button.css('width', buttonWidth);
604		button.prop('disabled', true);
605	}
606
607	/* Function called to restore a button as it was.
608		* @param button  : The button element.
609	*/
610	function restoreButton(button, timeout) {
611		setTimeout(function() {
612			button.html(button.attr("data-html"));
613			button.css('width', button.attr("data-width"));
614			button.css('background-color', button.attr("data-color"));
615			button.prop('disabled', false);
616		},  (timeout ? timeout : 0));
617	}
618
619	/* Function called to display the button with error and then restore it.
620		* @param button  : The button element.
621	*/
622	function errorButton(button, time) {
623		button.html('<span class="mdi mdi-alert-circle"></span>');
624		button.css('width', button.attr("data-width"));
625		button.css('background-color', 'red');
626		setTimeout(function() {
627			restoreButton(button);
628		}, (time ? time : 2000));
629	}
630
631	/* Function called to display only fex lines of a text (attribute data-max-lines to 
631set the value)
632		* @param className  : The class selector of the texts to clamp
633	*/
634	function clampText(className) {
635        $('.' + className).each(function(index, element) {
636			if ($(element).is(':visible')) {
637				if (!$(element).hasClass('clamped')) {
638					if ($(element).hasClass('more-button')) {
639						$(element).attr('full-text', $(element).html());
640					}
641
642					$clamp(element, {clamp: $(element).attr('data-max-lines'), useNativeClamp:false});
643					$(element).addClass('clamped');
644
645					if ($(element).hasClass('more-button') && $(element).html() != $(element).attr('full-text')) {
646						$(element).after('<button type="button" class="btn btn-link more-button-container" style="display: block;width: 100%;text-align:center;cursor:pointer;margin-top:5px" aria-expanded="false" aria-label="Show more" onclick="displayClampedText(this, \''
647						+ $(element).attr('id') + '\')"><span class="mdi mdi-chevron-down"></span></button>')
648					}
649				}
650			} else {
651				$(element).addClass('should-clamp');
652			}
653        });
654	}
655	
656	/* Function called to display only fex lines of a text (attribute data-max-lines to set the value)
657		* @param element  : the more element, will be hiden
658		* @param id  : The id of the text element
659	*/
660	function displayClampedText(element, id) {
661		var text = $('#' + id).attr('full-text');
662		var currentHeight = $('#' + id).height();
663		$('#' + id).html(text);
664		$('#' + id).css("max-height", currentHeight + 'px');
665		$('#' + id).animate({maxHeight: 1000},500);
666		$(element).hide();
667	}
668
669	/* Function called to show hidden items
670		* @param element  : the more element, will be rotated
671		* @param id  : The id of the hidden element
672	*/
673	function showMoreItems(moreElement, id) {
674		var element = $('#' + id);
675		if (element.is(':visible')) {
676			$(moreElement).css({'transform' : ''});
677			$(moreElement).attr('aria-expanded', 'false');
678		} else {
679			$(moreElement).css({'transform' : 'rotate(180deg)'});
680			$(moreElement).attr('aria-expanded', 'true');
681		}
682		element.slideToggle(200);
683		clampText('should-clamp');
684	}
685
686
687
688	/* Flash an array of elements for a given time
689		* @param elements  : The array of elements
690		* @param time  : The flashing time.
691	*/
692	function flashElements(elements, time) {
693		var i;
694		var element;
695		for (i = 0; i < elements.length; i++) {
696			element = elements[i];
697			element.css('border-color', '#66afe9');
698			element.css('box-shadow', 'inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6)');
699			element.css('border-width', '2px');
700		}
701
702		setTimeout(function() {
703			var i;
704			var element;
705			for (i = 0; i < elements.length; i++) {
706				element = elements[i];
707				element.css('border-color', '');
708				element.css('box-shadow', '');
709				element.css('border-width', '');
710			}
711		}, time);
712	}
713
714	/* Set a param in the current url and return the new url string
715		* @param param  : The param to set
716		* @param value  : The value of the param
717	*/
718	function setUrlParameter(param, value) {
719		var url = new URL(location.href);
720		url.searchParams.set(param, value);
721		return url.href;
722	}
723
724
725	/* Return a debounced function
726		* @param func  		  : the function to debounce
727		* @param wait         : the debounce time
728		* @param immediate    : if true, the function will be called immediatelly.
729	*/
730	function debounce(func, wait, immediate) {
731		var timeout;
732		return function() {
733			var context = this, args = arguments;
734			var later = function() {
735				timeout = null;
736				if (!immediate) func.apply(context, args);
737			};
738			var callNow = immediate && !timeout;
739			clearTimeout(timeout);
740			timeout = setTimeout(later, wait);
741			if (callNow) func.apply(context, args);
742		};
743	};
744
745	/* Return a throttled function
746		* @param func  		  : the function to debounce
747		* @param wait         : the debounce time
748		* @param options      : {leading, trailing}
749	*/
750	function throttle(func, wait, options) {
751		var now = _.now();
752		if (!previous && options.leading === false) previous = now;
753		var remaining = wait - (now - previous);
754		context = this;
755		args = arguments;
756		if (remaining <= 0 || remaining > wait) {
757		  if (timeout) {
758			clearTimeout(timeout);
759			timeout = null;
760		  }
761		  previous = now;
762		  result = func.apply(context, args);
763		  if (!timeout) context = args = null;
764		} else if (!timeout && options.trailing !== false) {
765		  timeout = setTimeout(later, remaining);
766		}
767		return result;
768	  };
769
770	/* Save a click in db
771		* @param type  		  : The type
772		* @param typeId       : The typeId
773	*/
774	function saveClick(type, typeId) {
775		getContent('xxx', 'save_click_endpoint?type=' + type + '&type_id=' + typeId);
776	}
777
778
779/*-----------------------------------------------------*\
780	@TOOLTIP
781\*-----------------------------------------------------*/
782	/* Display a help tooltip
783		* @param selector  : used to find the element on the page
784		* @param title     : The title of the tooltip
785		* @param message   : The message of the tooltip
786		* @param id  	   : The db id of the tooltip
787		* @param settings  : A setting JS object {color, new, testDisplay, icon, width}
788	*/
789	function displayTooltip(selector, title, message, id, settings) {
790		var pageElement = $(selector);
791		if (!pageElement.is(':visible')) return;
792
793		var offsetLeft = pageElement.offset().left;
794		var arrowLeftPercent = (offsetLeft + (pageElement.outerWidth() / 2) > window.innerWidth / 2) ? 80 : 20;
795
796		var color = (settings.color ? (settings.color) : "");
797		var testDipslay = settings.testDisplay;
798		var actionLink = settings.actionLink;
799		var jsLink = settings.jsLink.replace(/'/g, "\\'");
800
801		var offsetTop = pageElement.offset().top;
802		var verticalClass = (offsetTop + (pageElement.outerHeight() / 2) > window.innerHeight / 2) ? 'top' : 'bottom';
803		var elementHtml = $("<div id='displayed_tooltip_" + id + "' class='tooltip fade bubble " + verticalClass + " in' role='tooltip'>" +
804								"<div class='tooltip-arrow' style='left: " + arrowLeftPercent + "%;border-bottom-color: " + color + ";'></div>" + 
805								"<div class='tooltip-inner' style='width:" + (settings.width ? (settings.width + "px") : "") + ";background-color: " + color + ";'>" + 
806									"<div><h4>" + (settings.icon ? "<span class='" + settings.icon + "'></span> " : "") + title + (settings.new ? " <div class='bubble--new badge badge-danger'>New</div>" : "") + "</h4><p>" + message + "</p></div>" + 
807									"<div class='butons_container'><a href='javascript:' onclick='setNoMoreTooltips(" + id + ", 1, " + testDipslay + ")' style='color:  white;'><span class='mdi-eye-off mdi mdi mdi-font-13'></span></a>" + 
808									(settings.nameLink ? "<a class='action_link btn btn-grey' " + (actionLink ? "href='" + actionLink + "'" : "") + " onclick=\";closeTooltip(" + id + ", '" + jsLink + "', " + testDipslay + ");\" style='color:" + color + ";cursor:pointer'>" + settings.nameLink + "</a>" 
809									: "<a href='javascript:' style='color:white;margin-left:auto;' onclick='closeTooltip(" + id + ")'>Got it</a>") + "</div>" +
810								"</div>" + 
811							"</div>");
812		pageElement.after(elementHtml);
813
814		var left = (pageElement.outerWidth() / 2) - (elementHtml.outerWidth() * arrowLeftPercent / 100) + pageElement.position().left;
815		var top = verticalClass == 'top' ? - elementHtml.outerHeight() + pageElement.position().top : pageElement.outerHeight() + pageElement.position().top;
816		elementHtml.css('left', left + 'px');
817		elementHtml.css('top', top + 'px');
818		
819		elementHtml.show("slide", { direction: verticalClass == 'top' ? 'down' : 'up' }, 200);
820
821		if (!settings.testDisplay) {
822			getContent('update_settings','save_parameter?name=bubble_' + id + '&value=1');
823		}
824
825		$(document)[0].addEventListener("click", checkOverlayClick, true);
826
827		function checkOverlayClick(e) {
828			if ($('#displayed_tooltip_' + id).length == 0) {
829				$(document)[0].removeEventListener("click", checkOverlayClick, true);
830			} else if ($(e.target).not('#displayed_tooltip_' + id) && !$.contains($('#displayed_tooltip_' + id)[0], e.target)) {
831				closeTooltip(id);
832				$(document)[0].removeEventListener("click", checkOverlayClick, true);
833			}
834		}
835	}
836
837	/* Open a alert to stop definitely the tooltips for the user
838		* @param id  		  : id of the tooltip in db
839		* @param value        : 1 to stop
840		* @param testDisplay  : avoid the setting if true
841	*/
842	function setNoMoreTooltips(id, value, testDisplay) {
843		if (confirm("Tooltips are here to guide you and help you discover new features.\nAre you sure that you want to stop all tooltips from being displayed? (Note: some mandatory tooltips will still be displayed time to time.)")) {
844			if (!testDisplay) {
845				getContent('update_settings','save_parameter?name=bubbles_stop&value=' + value);
846			}
847			closeTooltip(id);
848		}
849	}
850
851	/* Display a help tooltip
852		* @param id  : id of the tooltip in db
853	*/
854	function closeTooltip(id, action, testDisplay) {
855		if (action) {
856			if (!testDisplay) {
857				saveClick('tooltip-action', id);
858			}
859			eval(action);
860		}
861		
862		$('#displayed_tooltip_' + id).hide(80, function(){
863			$('#displayed_tooltip_' + id).remove();
864		});
865	}
866
867
868/*-----------------------------------------------------*\
869	@MODAL
870\*-----------------------------------------------------*/
871
872	/* Function called to open a new modal.
873		* @param url  : The page URL to load.
874		* @param size : Optional - The modal size. Default 'lg'.
875	*/
876	function openModal(url) {
877		let size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "lg";
878		let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {backdrop: true, keyboard: true, show:true};
879		
880    	let pathToElementToFocusOnClose = null;
881    	if (window.event && window.event.target) {
882			let selector = getElementSelector(window.event.target);
883			if ($(selector).length > 0) {
884				pathToElementToFocusOnClose = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : selector;
885			}
886		}
887		
888		let modalId = !$("#primary-modal").is(':visible') ? "primary-modal" : "secondary-modal";
889
890		setModalSize(modalId, size);
891		$("#" + modalId).modal(options);
892		$("#" + modalId).modal('show');
893        initPopupAccessibility(modalId, pathToElementToFocusOnClose, 'modal');
894		$("#" + modalId + " .modal-content").load(url, function (result, textSatus, jqXHR) {
895			if (jqXHR.status == 403) {
896				location.reload();
897			}
898
899			const openModalEvent = new CustomEvent('openModal', {detail: {'modalId': modalId} });
900			document.dispatchEvent(openModalEvent);	
901			let modalHeading = $("#" + modalId + ' h1');
902			if (modalHeading.length < 1) {
903				modalHeading = $("#" + modalId + ' h2');
904			}
905			if (modalHeading.length > 0) {
906				let modalTitle = $(modalHeading[0]).text().trim();
907				if (modalTitle.length > 0) {
908					modalTitle += ', ';
909				}
910				a11yAnnounceToScreenReader(modalTitle + 'dialog');
911			}
912		});
913
914		if (options != undefined && options.backdrop != undefined){
915			$("#" + modalId).data('bs.modal').options.backdrop = options.backdrop;
916		}
917	}
918
919    /* Function called to open a new modal with an overwritten focus on a 
919specific element (not default one) when the modal closes
920        * @param url                      	 : The page URL to load
921        * @param pathToElementToFocusOnClose : The path to the element to focus on close
922    */
923	function openModalWithFocusOnClose(url, pathToElementToFocusOnClose) {
924		openModal(url, 'lg', null, pathToElementToFocusOnClose);
925	}
926
927	/* Function called to close a modal. Close the top modal when 2 are open. */
928	function closeModal() {
929		if( !$("#secondary-modal").is(':visible') ) {
930			$("#primary-modal").modal('hide');
931		}
932		else {
933			$("#secondary-modal").modal('hide');
934		}
935	}
936
937
938	/* Function called to resize a modal.
939		* @param id   : The modal identifier..
940		* @param size : The modal size.
941	*/
942	function setModalSize(id, size) { 
943		$("#" + id + " > div.modal-dialog").removeClass().addClass("modal-dialog modal-" + size);
944	}
945
946	/* Function called to load a new page URL without closing the modal.
947		* @param url  : The page URL to load.
948		* @param size : Optional - The modal size. Default 'lg'.
949	*/
950	function transitionModal(url) {
951		var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "lg";
952		let modalId = !$("#secondary-modal").is(':visible') ? "primary-modal" : "secondary-modal";
953
954		setModalSize(modalId, size);
955		$("#" + modalId + " .modal-content").html(writeLoading("Loading"));
956		$("#" + modalId + " .modal-content").load(url, function (result) {
957			const transitionModalEvent = new CustomEvent('transitionModal', {detail: {'modalId': modalId} });
958			document.dispatchEvent(transitionModalEvent);	
959		});
960
961	}
962
963	/* Function called to load a new content into the selected container without closing the modal & with an optional callback once loaded.
964		* @param url  		: The page URL to load.
965		* @param selector   : The container selector.
966		* @param size 		: Optional - The modal size. Default 'lg'.
967		* @param callback   : The callback function once loaded.
968		* @param callbackArgs : The callback arguments.
969	*/
970	function transitionModalContent(url, selector) {
971		var size = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "lg";
972		let modalId = !$("#secondary-modal").is(':visible') ? "primary-modal" : "secondary-modal";
973		var callback = arguments.length > 3 ? arguments[3] : null;
974		
975		setModalSize(modalId, size);
976		$("#" + modalId + " .modal-content " + selector).html(writeLoading("Loading"));
977		$("#" + modalId + " .modal-content " + selector).load(url, function (result) {
978			const transitionModalEvent = new CustomEvent('transitionModal', {detail: {'modalId': modalId} });
979			document.dispatchEvent(transitionModalEvent);
980
981			if (typeof callback === "function") {
982				callback();
983			}
984		});
985	}
986
987	/* Function called to load a new page URL without closing the modal & without displaying a loader.
988		It is advised not to change the size with this function.
989		* @param url  : The page URL to load.
990		* @param size : Optional - The modal size. Default 'lg'.
991	*/
992	function transitionModalWithoutLoading(url) {
993		var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "lg";
994		let modalId = !$("#secondary-modal").is(':visible') ? "primary-modal" : "secondary-modal";
995
996		setModalSize(modalId, size);
997		$("#" + modalId + " .modal-content").load(url, function (result) {/*Nothing*/});
998	}
999
1000
1001/*-----------------------------------------------------*\
1002	@DIALOG
1003\*-----------------------------------------------------*/
1004
1005    /* Function that toggles a dialog open/closed when the dialog toggle is clicked OR the dialog close/search (or equivalent) buttons are pressed
1006        * @param dialogId                        : String ID of the dialog container element
1007        * @param dialogOpenFocusElementId        : String ID of the element to focus on when the dialog opens   
1008    */
1009    function openDialog(dialogId, dialogOpenFocusElementId) {
1010    	let pathToElementToFocusOnClose = null;
1011    	if (window.event && window.event.target) {
1012			let selector = getElementSelector(window.event.target);
1013			if ($(selector).length > 0) {
1014				pathToElementToFocusOnClose = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : selector;
1015			}
1016		}
1017
1018        if (dialogId.startsWith('#')) { dialogId = dialogId.substring(1); }
1019        if (dialogOpenFocusElementId.startsWith('#')) { dialogOpenFocusElementId = dialogOpenFocusElementId.substring(1); }
1020        $('#' + dialogId).show(150);
1021
1022        let focusElement = $('#' + dialogId + ' #' + dialogOpenFocusElementId)[0];
1023        if (!isEmpty(focusElement)) { focusElement.focus(); }
1024
1025		const openDialogEvent = new CustomEvent('openDialog', {detail: {'dialogId': dialogId} });
1026		document.dispatchEvent(openDialogEvent);	
1027
1028        initPopupAccessibility(dialogId, pathToElementToFocusOnClose);
1029    }
1030
1031    function closeDialog(dialogId) {
1032    	if (dialogId.startsWith('#')) { dialogId = dialogId.substring(1); }
1033    	$('#' + dialogId).hide(150);
1034        terminatePopupAccessibility(dialogId);
1035    }
1036
1037/*-----------------------------------------------------*\
1038	@COMMENTS | @LIKES | @FEED
1039\*-----------------------------------------------------*/
1040	/* Function triggered by clicking on the Comment Icon Button.
1041		* @param strUid                 : The UID of the item the comments are linked to.
1042		* @param strContainerID         : The comments container ID.
1043	*/
1044	function onComment(strUid, strContainerID){
1045		$("#" + strContainerID).toggle();
1046		$("#comment__icon-" + strUid).removeAttr("style");
1047		$("#comments__post-textarea-" + strUid).focus();
1048
1049		let onCommentLink = $("#comment__on-comment-link-" + strUid);
1050		let onCommentLinkAriaLabel = $(onCommentLink).attr("aria-label");
1051		if ($("#" + strContainerID).css("display") === "none") {
1052			onCommentLinkAriaLabel = onCommentLinkAriaLabel.replace("Hide comments for", "Show comments for");
1053			$(onCommentLink).attr('aria-label', onCommentLinkAriaLabel);
1054			$(onCommentLink).attr('aria-expanded', 'false');
1055		} else {
1056			onCommentLinkAriaLabel = onCommentLinkAriaLabel.replace("Show comments for", "Hide comments for");
1057			$(onCommentLink).attr('aria-label', onCommentLinkAriaLabel);
1058			$(onCommentLink).attr('aria-expanded', 'true');
1059		}
1060	}
1061
1062	/* Function triggered by clicking the "reply" link to open the comments text area
1063		* @param strUid                 : The UID of the item the comments are linked to.
1064	*/
1065	function onReplyToPost(strUid) {
1066		$('#comments__post-' + strUid).toggle();
1067		let replyTextarea = $('#comments__post-textarea-' + strUid)[0];
1068		if (!isEmpty(replyTextarea)) {
1069			replyTextarea.focus();
1070			setTimeout(function () {
1071				// Wait for the screen reader to catch focus change before we remove the reply link (or we will not be able to type into the textarea)				
1072				$('#comments__reply-' + strUid).css('display', 'none');
1073			}, 33); // this will not work using setTimeout value of 0 but in testing using 33 works as expected (do not change this value)
1074		}
1075	}
1076
1077	/* Function triggered by clicking on the Post Comment Button.
1078		* @param strUid                 : The UID of the item the comments are linked to.
1079		* @param strType                : The type of the item the comments are linked to.
1080		* @param strStudentID           : The student ID.
1081		* @param strSubType           	: The subtype of the item the comments are linked to.
1082		* @param strSubTypeId           : The subtype id of the item the comments are linked to.
1083	*/
1084	function onCommentPost(strUid, strType, strStudentID, isPinnable, strSubType, strSubTypeId) {
1085		var strComment = $("#comments__post-textarea-" + strUid).val();
1086		var strAttachmentIds = $('#file_ids').val();
1087		
1088		if (strComment !== "") {
1089			$("#comments__post-textarea-" + strUid).parent().first().removeClass('has-error');
1090			cmsg("Posting...", 1, 0);
1091			$("#comments__post-textarea-" + strUid).attr('value', '');
1092			$('#attachment_ids-' + strUid).val('');
1093			$('#attachment_ids_container-' + strUid).hide(200);
1094			if ($('#feed_post').length > 0) {
1095				uploader.reset();
1096			}
1097			jQuery.ajax({
1098				type: "POST",
1099				cache: false,
1100				url: "/update_comments",
1101				data: { uid: strUid, type: strType, student_id: strStudentID, comment: strComment, attachment_ids: strAttachmentIds, sub_type: strSubType, sub_type_id: strSubTypeId },
1102				success: function () {
1103					jQuery.ajax({
1104						type: "GET",
1105						cache: false,
1106						url: "/ajax_comment_boot?ax=1",
1107						data: { uid: strUid, type: strType, pinnable: isPinnable },
1108						success: function (html) {
1109							$("#comments__post-" + strUid).before(html);
1110							if (a11yAnnounceToScreenReader) { a11yAnnounceToScreenReader('Comment posted.'); }
1111						}
1112					});
1113				}
1114			});
1115		} else {
1116			$("#comments__post-textarea-" + strUid).parent().first().addClass('has-error');
1117			if (a11yAnnounceToScreenReader) { a11yAnnounceToScreenReader('Please enter a comment before posting.'); }
1118		}
1119	}
1120
1121	/* Function triggered by clicking on the Like Button.
1122		* @param strUid                 : The UID of the item the likes are linked to.
1123		* @param strType                : The type of the item the likes are linked to.
1124		* @param strStudentID           : The student ID.
1125	*/
1126	function onLike(strUid, strType, strStudentID) {
1127		var likeIcon    = $("#like__icon-" + strUid);
1128		var likeCounter = $("#like__counter-" + strUid);
1129		var likeLink    = $(likeIcon).parent();
1130		//Heart Empty => It is a like
1131		if (likeIcon.hasClass("mdi-heart-outline")) {
1132			likeIcon.removeClass("mdi-heart-outline").addClass("mdi-heart");
1133			let oldLikes = '' + parseInt(likeCounter.html()) + ' likes.';
1134			var newCount = parseInt(likeCounter.html()) + 1;
1135			if (newCount == 1) {
1136				likeIcon.removeAttr("style");
1137				likeCounter.html(newCount).css("display", "inline-block");
1138			}
1139			else { 
1140				likeCounter.html(newCount).css("display", "inline-block");
1141			}
1142			onLikePost(strUid, strType, strStudentID, 1)
1143			let newLikes = '' + newCount + ' likes.';
1144
1145			let linkAriaLabel = $(likeLink).attr('aria-label');
1146			linkAriaLabel = linkAriaLabel.replace('Click to like.', 'You liked this, click again to unlike.');
1147			if (linkAriaLabel.includes(' likes.')) {
1148				linkAriaLabel = linkAriaLabel.replace(oldLikes, newLikes);
1149			} else {
1150				linkAriaLabel = newLikes + ' ' + linkAriaLabel;
1151			}
1152			$(likeLink).attr('aria-label', linkAriaLabel);
1153		}
1154		//Heart Full => It is a dislike
1155		else if (likeIcon.hasClass("mdi-heart")) {
1156			likeIcon.removeClass("mdi-heart").addClass("mdi-heart-outline");
1157			let oldLikes = '' + parseInt(likeCounter.html()) + ' likes.';
1158			var newCount = parseInt(likeCounter.html()) - 1;
1159			if (newCount == 0) {
1160				likeIcon.attr("style", 'color: #aaa !important');
1161				likeCounter.html(newCount).css("display", "none");
1162			}
1163			else {
1164				likeCounter.html(newCount);
1165			}
1166			onLikePost(strUid, strType, strStudentID, 0)
1167			let newLikes = '' + newCount + ' likes.';
1168
1169			let linkAriaLabel = $(likeLink).attr('aria-label');
1170			linkAriaLabel = linkAriaLabel.replace('You liked this, click again to unlike.', 'Click to like.');
1171			if (newCount == 0) {
1172				linkAriaLabel = linkAriaLabel.replace(oldLikes, '').trim();
1173			} else {
1174				linkAriaLabel = linkAriaLabel.replace(oldLikes, newLikes);
1175			}
1176			$(likeLink).attr('aria-label', linkAriaLabel);
1177		}
1178	}
1179
1180	/* Function triggered by clicking the "View N previous replies" for feed posts with a lot of comments
1181		* @param strUid                 : The UID of the item the previous replies are linked to.
1182		*
1183		* NOTE previously defined in the onClick for the "View N previous replies" link; moved here to
1184		*      improve focus on click for accessibility (we want to focus on the first comment inside
1185		*      the newly expanded replies).
1186	*/
1187	function viewNPreviousReplies(strUid) {
1188		$('#old_comments_' + strUid).css('display','block');
1189		$('#show_old_comments_' + strUid).css('display','none');
1190		$('#hide_old_comments_' + strUid).css('display','block');
1191		// (Accessibility) Move keyboard focus to the first comment inside the newly expanded block
1192		let firstPreviousReply = $('#feed__post-comments-' + strUid).find('.media-heading--feed')[0];
1193		if (!isEmpty(firstPreviousReply)) {
1194			firstPreviousReply.focus();
1195		}
1196	}
1197
1198	/* Function triggered by clicking on the Like Button.
1199		* @param strUid                 : The UID of the item the likes are linked to.
1200		* @param strType                : The type of the item the likes are linked to.
1201		* @param strStudentID           : The student ID.
1202		* @param isLike                 : 0 => Dislike / 1 => Like.
1203	*/
1204	function onLikePost(strUid, strType, strStudentID, isLike) {
1205		jQuery.ajax({
1206			type: "POST",
1207			cache: false,
1208			url: "/update_likes",
1209			data: { uid: strUid, type: strType, student_id: strStudentID, like: isLike },
1210			success: function () { /*Success*/ }
1211		});
1212	}
1213
1214	/* Function used to check if the privacy settings of a post are set. 
1215	Uses schoolFeedPrivacy variable which is set in SocialPostBoot.ascx */       
1216	function isSetPostPrivacy() {
1217		var boolPrivacySet = false;
1218
1219		if (schoolFeedPrivacy == 1) {
1220			if ($("#feed_type").val() == "feed" && schoolFeedAdmin == 1) {
1221				boolPrivacySet = true;
1222			}
1223			else if ($("#feed_type").val() == "discussion_posts") {
1224				boolPrivacySet = true;
1225			}
1226			else if ($("#feed_type").val() == "club") {
1227				boolPrivacySet = (($("#post_privacy_level").val() == 2 || $("#post_privacy_level").val() == 15) && $("#to_club_ids").val() != "");
1228			} else if ($("#feed_type").val() == "event") {
1229				boolPrivacySet = $("#post_privacy_level").val() == 1;
1230			}
1231		}
1232		else if ($("#feed_type").val() == "club") {
1233			boolPrivacySet = ( ($("#post_privacy_level").val() == 2 || $("#post_privacy_level").val() == 3 || $("#post_privacy_level").val() == 15) && $("#to_club_ids").val() != "");
1234		}
1235		else {
1236			boolPrivacySet = true;
1237		}
1238		return boolPrivacySet;
1239	}   
1240
1241	/* Function used to hide Post Area. */
1242	function hidePostArea(param) {
1243		$("#social-post").css("display", "none");
1244		if (param == "admin") {
1245			$("#social-post__admin").css("display", "block");
1246			$("#social-post__infos").css("display", "none");
1247		}
1248		else if (param == "infos") {
1249			$("#social-post__infos").css("display", "block");
1250			$("#social-post__admin").css("display", "none");
1251		}
1252	}
1253
1254	/* Function used to show Post Area. */
1255	function showPostArea() {
1256		$("#social-post__admin").css("display", "none");
1257		$("#social-post__infos").css("display", "none");
1258		$("#social-post").css("display", "block");
1259	}
1260
1261	/* Function used to hide Post Controls. */
1262	function hidePostAreaControls() {
1263		$("#feed_post").css("height", "20px");
1264		$("#social-post__actions").slideUp('fast', 'swing');
1265	}
1266
1267	/* Function used to show Post Controls. */
1268	function showPostAreaControls() {
1269		$("#feed_post").css("height", "auto");
1270		$("#social-post__actions").slideDown('fast', 'swing');
1271	}
1272
1273	/* Function triggered by clicking on the Schedule Feed Button.*/
1274	function toggleFeedScheduling() {
1275		var socialPostScheduleCont = $("#social-post_schedule")
1276		if(!$(socialPostScheduleCont).is(":visible")) {
1277			$(socialPostScheduleCont).slideDown('fast', 'swing');
1278		}
1279		else {
1280			$(socialPostScheduleCont).slideUp('fast', 'swing');
1281		}
1282	};
1283
1284	/* Function triggered by clicking on the Share With... Button.*/
1285	function toggleFeedShare() {
1286		openModal("/ajax_feed_privacy?ax=1&type=" + $("#feed_type").val() + "&type_id=" + $("#feed_type_id").val() + "&topic_id=" + $("#feed_topic_id").val()); 
1287		isPrivacyModalOpen = true;
1288	}
1289
1290	/* Function to reset the Feed Share inputs */
1291	function resetFeedShare() {
1292		$("#post_privacy_level").val("0");
1293		$("#to_type_ids").val("");
1294		$("#to_people_tags").val("");
1295		$("#to_yogs").val("");
1296		$("#to_club_ids").val("");
1297		$("#btn_ajax_feed_privacy").attr("class", "btn btn-grey").html("<span class='mdi mdi-lock' aria-hidden='true' role='presentation'></span> Share with...");
1298	}
1299
1300	/* Function triggered by switching between Post tabs (Status, Photos, Files, Link). Sets the value of input. */
1301	function setFeedSubType(type) {
1302		$("#post_element_sub_type").val(type);
1303	}
1304
1305	// Raised while a post submission is in flight, so extra clicks on the send button are ignored
1306	// instead of creating a duplicate post.
1307	var boolFeedPostInFlight = false;
1308
1309	/* Function used to lock / release the post controls around a submission.
1310		* @param boolLocked : True while the submission is in flight, false once it has settled.
1311	*/
1312	function setFeedPostLock(boolLocked) {
1313		boolFeedPostInFlight = boolLocked;
1314		// The send button is a real <button> on feeds_new.aspx, the other actions are Bootstrap
1315		// anchors: "disabled" only takes on the former, ".disabled" only takes on the latter.
1316		$("#feed_post__post-btn").prop("disabled", boolLocked).attr("aria-disabled", boolLocked);
1317		$("#social-post__actions a.btn").toggleClass("disabled", boolLocked).attr("aria-disabled", boolLocked);
1318	}
1319
1320	/* Function triggered by clicking on the Post Feed Button. */
1321	function onFeedPost() {
1322		if (boolFeedPostInFlight) { return; }
1323		if ($("#feed_post").val() != "") {
1324			if (isSetPostPrivacy()) {
1325				setFeedPostLock(true);
1326
1327				cmsg("Posting...", 1, 0);
1328				var postElementSubType = $("#post_element_sub_type").val();
1329				var attachmentsIDs;
1330				if (postElementSubType == "doc") {
1331					attachmentsIDs = $("#attachment_ids").val()
1332				}
1333				if (postElementSubType == "photo") {
1334				   attachmentsIDs = $("#photo_ids").val();
1335				}
1336				var postLink;
1337				if ($("#link_link").is(":visible")) {
1338					postLink = $("#link_link").val();
1339				}
1340				else {
1341					postLink = $("#video_link").val();
1342				}
1343
1344				jQuery.ajax({
1345					type: "POST",
1346					cache: false,
1347					url: "/ajax_feed_post",
1348					data: { post:                       1, 
1349							async:                      1,
1350							message:                    $("#feed_post").val(),
1351							post_link:                  postLink,
1352							feed_type:                  $("#feed_type").val(),
1353							feed_type_id:               $("#feed_type_id").val(),
1354							feed_topic_id:              $("#feed_topic_id").val(),
1355							post_element_sub_type:      $("#post_element_sub_type").val(),
1356							post_element_subtype_id:    $("#post_element_subtype_id").val(),
1357							post_privacy_level:         $("#post_privacy_level").val(),
1358							officers_only:              $("#officers_only").val(),
1359							to_type_ids:                $("#to_type_ids").val(),
1360							to_people_tags:             $("#to_people_tags").val(),
1361							to_yogs:                    $("#to_yogs").val(),
1362							to_club_ids:                $("#to_club_ids").val(),
1363							send_date:                  $("#send_date").val(),
1364							send_hour:                  $("#send_hour").val(),
1365							send_minute:                $("#send_minute").val(),
1366							send_ampm:                  $("#send_ampm").val(),
1367							attachment_ids:             attachmentsIDs,
1368							send_notifications:         $("#send_notifications").prop("checked"),
1369							_csrf:                      $("#_csrf").val()
1370						  },
1371					success: function () {
1372						$("#feed_post").val("");
1373						$("#link_link").val("");
1374						$("#video_link").val("");
1375						$("#post_element_sub_type").val("feed");
1376						$("#post_element_subtype_id").val("");
1377						$("#to_type_ids").val("");
1378						$("#to_people_tags").val("");
1379						$("#to_yogs").val("");
1380						$("#to_club_ids").val("");
1381						$("#send_date").val("");
1382						$("#send_hour").val("");
1383						$("#send_minute").val("");
1384						$("#send_ampm").val("");
1385						$("#attachment_ids").val("");
1386						$("#photo_ids").val("");
1387						$('.qq-upload-list').children().remove(); //Remove uploaded photos & files from upload area
1388						$('#fine-uploader__document .qq-gallery.qq-uploader:before').attr("content", "Drop documents here");
1389						$('#fine-uploader__photo .qq-gallery.qq-uploader:before').attr("content", "Drop photos here");
1390						jQuery.ajax({
1391							type: "GET",
1392							cache: false,
1393							url: "/ajax_feed_boot?ax=1&new_post=1&type=" + $("#feed_type").val() + "&type_id=" + $("#feed_type_id").val() + "&topic_id=" + $("#feed_topic_id").val(),
1394							data: { },
1395							success: function (html) {
1396								$(html).prependTo("#feed").hide().slideDown('slow', 'swing');
1397								$("#btn_ajax_feed_privacy").attr("class", "btn btn-grey").html("<span class='mdi mdi-lock' aria-hidden='true' role='presentation'></span> Share with...");
1398								hidePostAreaControls();
1399								$('#social-post .nav-tabs a:first').tab('show');
1400							}
1401						});
1402
1403						// Allow to post directly without selecting a particular privacy.
1404						if($("#feed_type").val() == "club" && $("#feed_type_id").val() !== "") {
1405							$("#post_privacy_level").val(2);
1406							$("#to_club_ids").val($("#feed_type_id").val());
1407						}
1408					},
1409					// Released here rather than in the feed refresh above so a failed post does not
1410					// leave the composer locked.
1411					complete: function () {
1412						setFeedPostLock(false);
1413					}
1414				});
1415			}
1416			else {
1417				alert('Please set the privacy of the post by clicking the "Share with..." button on the left of the "Post" button.');
1418			}
1419		} else {
1420			alert('Please enter something to share.');
1421		};
1422	};
1423
1424	function reloadPosts() { 
1425		window.location.reload();
1426	}
1427
1428	// General Variable indicating weither to reload the feed after a search.
1429	var hasFeedSearchBeenRan = false;
1430
1431	/* Function used to check if we can toggle the social-post when clicking out. */
1432	function isEmptySocialPost() {
1433		if ( $("#feed_post").val() !== "" || 
1434			 $("#link_link").val() !== "" || 
1435			 $("#video_link").val() !== "" || 
1436			 $("#attachment_ids").val() !== "" || 
1437			 $("#photo_ids").val() !== "" || 
1438			 $("#to_type_ids").val() !== "" || 
1439			 $("#to_yogs").val() !== "" || 
1440			 $("#to_people_tags").val() !== "" || 
1441			 $("#to_club_ids").val() !== "" || 
1442			 $("#modal__feed-privacy").is(":visible") ) {
1443			return false; 
1444		}
1445		else { 
1446			return true;
1447		}
1448	}
1449
1450	/* Function used to toggle the search bar for the feed. */
1451	function toggleFeedSearch() {
1452		if(!$("#feed-search__input").is(":visible")) {
1453			$("#feed-search__close-btn").animate({width:'toggle'},100, function() {
1454				$("#feed-search__input").animate({width:'toggle'},250).focus();
1455			});
1456			$("#feed-search__search-btn").removeClass("feed-search__round-btn").removeAttr("onclick").attr("onclick", "onFeedSearch()");
1457		}
1458		else {
1459			$("#feed-search__input").animate({width:'toggle'},250, function() {
1460				$("#feed-search__close-btn").animate({width:'toggle'},100);
1461				$("#feed-search__search-btn").addClass("feed-search__round-btn").removeAttr("onclick").attr("onclick", "toggleFeedSearch()");
1462			});
1463		}
1464	};
1465
1466	/* Function used to search & reload the feed. */
1467	function onFeedSearch() {
1468		resetFeedShare();
1469		cmsg("Searching...", 1000, 0);
1470		getContent("feed", "/ajax_feed_boot?ax=1&range=0&search=" + $("#feed-search__input").val() + "&type=" + $("#feed_type").val() + "&type_id=" + $("#feed_type_id").val() + "&topic_id=" + $("#feed_topic_id").val());
1471		hasFeedSearchBeenRan = true;
1472	}
1473
1474	/* Function used to clear the search & reload the feed. */
1475	function clearFeedSearch() {
1476		$("#feed-search__input").val("");
1477		toggleFeedSearch();
1478		if(hasFeedSearchBeenRan) { 
1479			getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=" + $("#feed_type").val() + "&type_id=" + $("#feed_type_id").val() + "&topic_id=" + $("#feed_topic_id").val());
1480		}
1481		hasFeedSearchBeenRan = false;
1482	}
1483
1484	/* Function triggered when a topic is selected.
1485		* @param topicID        : The topic ID, 0 being All Posts
1486		* @param topicName      : The topic name
1487		* @param topicIcon      : The topic icon
1488	*/
1489	function selectTopicFeed(topicID, topicName, topicIcon) {
1490		resetFeedShare();
1491
1492		// 0 is All Posts
1493		if (topicID === 0) { 
1494			if (schoolFeedPrivacy == 1 && schoolFeedAdmin == 0) {
1495				hidePostArea("infos");
1496			}
1497			else {
1498				showPostArea();
1499			}
1500			$('#feed_post').attr("placeholder", "Start a new conversation");
1501			$("#feed_type").val("feed");
1502			$("#feed_type_id").val("");
1503			$("#feed_topic_id").val("");
1504			$("#feed-header").html("<span class='mdi mdi-clipboard-text'></span> General");
1505			getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=feed");
1506		}
1507		else { 
1508			if (schoolFeedPrivacy == 1 && schoolFeedAdmin == 0) {
1509				hidePostArea("infos");
1510			}
1511			else {
1512				showPostArea();
1513			}
1514			$('#feed_post').attr("placeholder", "Start a new conversation under " + topicName + "...");
1515			$("#feed_type").val("feed");
1516			$("#feed_type_id").val("");
1517			$("#feed_topic_id").val(topicID);
1518			$("#feed-header").html("<span class='" + topicIcon + "'></span> " + topicName.charAt(0).toUpperCase() + topicName.substr(1));
1519			getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=feed&topic_id=" + topicID);
1520		}
1521
1522		// Remove new feeds counter.
1523		$("#topics__topic-" + topicID + " span.badge").remove();
1524	} 
1525
1526
1527	/* Function triggered when a topic from a group is selected.
1528		* @param topicID        : The topic ID, 0 being All Posts
1529		* @param topicName      : The topic name
1530		* @param topicIcon      : The topic icon
1531		* @param groupID        : The group ID
1532	*/
1533	function selectGroupTopicFeed(topicID, topicName, topicIcon, groupID) {
1534		resetFeedShare();
1535
1536		// 0 is All Posts
1537		if (topicID === 0) { 
1538			if (schoolFeedPrivacy == 1 && schoolFeedAdmin == 0) {
1539				hidePostArea("infos");
1540			}
1541			else {
1542				showPostArea();
1543			}
1544			$('#feed_post').attr("placeholder", "Start a new conversation");
1545			$("#feed_type").val("feed");
1546			$("#feed_type_id").val("");
1547			$("#feed_topic_id").val("");
1548			$("#feed-header").html("<span class='mdi mdi-clipboard-text'></span> General");
1549			getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=feed");
1550		}
1551		else { 
1552			if (schoolFeedPrivacy == 1 && schoolFeedAdmin == 0) {
1553				hidePostArea("infos");
1554			}
1555			else {
1556				showPostArea();
1557			}
1558			$('#feed_post').attr("placeholder", "Start a new conversation under " + topicName + "...");
1559			$("#feed_type").val("club");
1560			$("#feed_type_id").val(groupID);
1561			$("#feed_topic_id").val(topicID);
1562			$("#feed-header").html("<span class='" + topicIcon + "'></span> " + topicName.charAt(0).toUpperCase() + topicName.substr(1));
1563			getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=club&type_id=" + groupID + "&topic_id=" + topicID);
1564		}
1565
1566		// Remove new feeds counter.
1567		$("#topics__topic-" + topicID + " span.badge").remove();
1568	} 
1569
1570	/* Function triggered when a event is selected.
1571		* @param eventId        : The event ID
1572		* @param eventName      : The event name
1573		* @param eventIcon      : The event icon
1574	*/
1575	function selectEventFeed(eventId, eventName, eventIcon) {
1576		resetFeedShare();
1577
1578		showPostArea();
1579
1580		// Allow to post directly without selecting a particular privacy.
1581		$("#post_privacy_level").val(1);
1582		$('#feed_post').attr("placeholder", "Start a new conversation");
1583		$("#feed_type").val("event");
1584		$("#feed_type_id").val(eventId);
1585		$("#feed-header").html("<span class='" + eventIcon + "'></span> " + eventName.charAt(0).toUpperCase() + eventName.substr(1));
1586		getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=event&type_id=" + eventId);
1587
1588		// Remove new feeds counter.
1589		$("#topics__event-" + eventId + " span.badge").remove();
1590	}
1591
1592	/* Function triggered when a group is selected.
1593		* @param groupID        : The group ID
1594		* @param groupName      : The group name
1595		* @param groupIcon      : The group icon
1596	*/
1597	function selectGroupFeed(groupID, groupName, groupIcon) {
1598		resetFeedShare();
1599
1600		showPostArea();
1601
1602		// Allow to post directly without selecting a particular privacy.
1603		$("#post_privacy_level").val(2);
1604		$("#to_club_ids").val(groupID);
1605
1606		$('#feed_post').attr("placeholder", "Start a new conversation in " + groupName + "...");
1607		$("#feed_type").val("club");
1608		$("#feed_type_id").val(groupID);
1609		$("#feed-header").html("<span class='" + groupIcon + "'></span> " + groupName.charAt(0).toUpperCase() + groupName.substr(1));
1610		getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=club&type_id=" + groupID);
1611
1612		// Remove new feeds counter.
1613		$("#topics__group-" + groupID + " span.badge").remove();
1614	}
1615
1616	/* Function triggered when a forum post feed is selected.
1617		* @param discussionPostId        : The Forum Post ID
1618	*/
1619	function selectForumFeed(discussionPostId) {
1620		resetFeedShare();
1621		showPostArea();
1622
1623		// Allow to post directly without selecting a particular privacy.
1624		$("#post_privacy_level").val(0);
1625		$("#to_club_ids").val('');
1626
1627		$('#feed_post').attr("placeholder", "Comment on this forum post...");
1628		$("#feed_type").val("discussion_posts");
1629		$("#feed_type_id").val(discussionPostId);
1630		getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=discussion_posts&type_id=" + discussionPostId)
1631	}
1632
1633	function selectAllFeeds() {
1634		resetFeedShare();
1635
1636		//hidePostArea("admin");
1637		$("#post_privacy_level").val(0);
1638
1639		$('#feed_post').attr("placeholder", "Start a new conversation");
1640		$("#feed_type").val("feed");
1641		$("#feed_type_id").val("");
1642		$("#feed_topic_id").val("");
1643		$("#feed-header").html("<span class='mdi mdi-clipboard-text'></span> All Posts");
1644		getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=all");
1645	}
1646
1647	function selectCGFeeds() {
1648		resetFeedShare();
1649
1650		//hidePostArea("admin");
1651		$("#post_privacy_level").val(0);
1652
1653		$('#feed_post').attr("placeholder", "Start a new conversation");
1654		$("#feed_type").val("feed");
1655		$("#feed_type_id").val("");
1656		$("#feed-header").html("<span class='mdi mdi-clipboard-text'></span> All Posts");
1657		getContent("feed", "/ajax_feed_boot?ax=1&range=0&type=cg");
1658	}
1659
1660	function selectEvents() {
1661		resetFeedShare();
1662
1663		//hidePostArea("admin");
1664		$("#post_privacy_level").val(0);
1665
1666		getContent("right-container", "/events?ax=1");
1667	}
1668	
1669
1670/*-----------------------------------------------------*\
1671	@ALERT
1672\*-----------------------------------------------------*/
1673	/* Function called to display an alert-message in the container.
1674		* @param message  	: The message to display.
1675		* @param type (Opt) : The type of alert. eg: danger, warning. Default is info.
1676	*/
1677	function displayAlertMessage(message) {
1678		var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "info";
1679
1680		var container = $(".content").length ? $(".content") : $(".container");
1681
1682		if (container.length == 0) return;
1683
1684		var messageBoxHtml = "<div class='alert alert-" + type + " alert-dismissible alert--top' role='alert' style='display: none;'>";
1685			messageBoxHtml += "<span class='alert-message-span'>" + message + "</span>";
1686			messageBoxHtml += "<button type='button' class='close' data-dismiss='alert' aria-label='Close'>x</button>";
1687		messageBoxHtml += "</div>";
1688		
1689		var element = $(messageBoxHtml);
1690		container.prepend(element);
1691		element.slideDown('fast', 'swing');;
1692
1693		a11yAnnounceToScreenReader(element.find('.alert-message-span').text().trim());
1694
1695		return;
1696	}
1697
1698	/* Function called to display an alert-message in the container.
1699		* @param message  	 : The message to display.
1700		* @param containerEl : The HTML element to prepend the message to.
1701		* @param type (Opt)  : The type of alert. eg: danger, warning. Default is info.
1702	*/
1703	function displayAlertMessageInContainer(message, containerEl) {
1704		var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "info";
1705
1706		if (containerEl.length == 0) return;
1707
1708		var messageBoxHtml = "<div class='alert alert-" + type + " alert-dismissible alert--top' role='alert' style='display: none;'>";
1709			messageBoxHtml += "<span class='alert-message-span'>" + message + "</span>";
1710			messageBoxHtml += "<button type='button' class='close' data-dismiss='alert' aria-label='Close'>x</button>";
1711		messageBoxHtml += "</div>";
1712		
1713		var element = $(messageBoxHtml);
1714		containerEl.prepend(element);
1715		element.slideDown('fast', 'swing');;
1716
1717		a11yAnnounceToScreenReader(element.find('.alert-message-span').text().trim());
1718
1719		return;
1720	}
1721
1722
1723
1724
1725/*-----------------------------------------------------*\
1726	@ARIA
1727\*-----------------------------------------------------*/
1728	/* Function called to toggle the arai expanded attribute.
1729		* @param el  	: The element ID.
1730	*/
1731	function toggleAriaExpanded(el) {
1732		var element = document.getElementById(el);
1733		if (typeof element !== 'undefined' && element !== null) {
1734			var value = element.getAttribute("aria-expanded");
1735			if (value == "true") {
1736				element.setAttribute("aria-expanded", false);
1737			}
1738			else {
1739				element.setAttribute("aria-expanded", true);
1740			}
1741		}
1742	}
1743
1744
1745
1746
1747/*-----------------------------------------------------*\
1748	@OLD FUNCTIONS
1749\*-----------------------------------------------------*/
1750
1751var editionEnCours = false;
1752var roomRes;
1753var iMouseDown = 0;
1754var iCellRoom = 0;
1755var iCellTime = 0;
1756var iCellEndTime = 0;
1757var iCR = 0;
1758var iCT = 0;
1759var iCET = 0;
1760var getcontent;
1761var getcontentparent;
1762var strPreventNavAway = 0;
1763var xhr;
1764
1765function toggleSearchBox(openClose) {
1766	if (openClose == 1) {
1767		if ($('.searchbox > input').css('width') == '884px') {
1768			toggleSearchBox(0);
1769		} else {
1770			$('a.glass').fadeIn();
1771			$('.searchbox > input').animate({ width: "884px", height: "46px" }, 300, function () {
1772				$('.searchbox').addClass('active');
1773				$('.searchbox .close').animate({ width: "45px", height: "49px" }, 100);
1774				$('.searchbox > input').focus();
1775			});
1776		}
1777	} else {
1778		$('.searchbox > input').animate({ width: "0", height: "0" }, 300, function () {
1779			$('.searchbox').removeClass('active');
1780			$('.searchbox .close').animate({ width: "0", height: "0" }, 100);
1781			$('.searchbox > input').blur();
1782			$('.searchbox > input').val('');
1783			$('a.glass').fadeOut();
1784		});
1785	}
1786}
1787
1788// Groups
1789$(document).ready(function () {
1790	$(document).bind('click', function (e) {
1791		var $clicked = $(e.target);
1792		if (!$clicked.parents('li').hasClass("gr_dropdown") && !$clicked.parents().hasClass("fav")) {
1793			$(".gr_dropdown .gr_list").hide();
1794		}
1795		else {
1796			$('#searchlist_groups_input').focus();
1797		}
1798		if (!$clicked.parents().hasClass("dd_notif")) {
1799			$(".dd_notif .subMenu").hide();
1800			$(".notif_dropdown_click").removeClass('active');
1801		}
1802		if (!$clicked.parents().hasClass("appli_dropdown")) {
1803			$(".appli_dropdown .subMenu").hide();
1804		}
1805		if (!$clicked.parents().hasClass("dd_account")) {
1806			$(".dd_account .subMenu").hide();
1807			$(".account_dropdown_click").removeClass('active');
1808		}
1809		if (!$clicked.parents('li').hasClass("events_dropdown") && !$clicked.parents().hasClass("fav")) {
1810			$(".events_dropdown .gr_list").hide();
1811			$(".events_dropdown_click").removeClass('active');
1812		}
1813		var $clicked = $(e.target);
1814		if (!$clicked.parents().hasClass("dd_support")) {
1815			$(".dd_support .subMenu").hide();
1816			$(".dd_support_click").removeClass('active');
1817			$('#support_message_loading').slideUp();
1818			$('#support_message_confirm').slideUp();
1819			$('#support_message_form').slideDown();
1820			$('#support_message_text').val('');
1821		}
1822		var $clicked = $(e.target);
1823		if (!$clicked.parents().hasClass("mob_menu")) {
1824			$(".mob_menu > .nav").hide();
1825			$(".mob_menu_click").removeClass('active');
1826		}
1827	});
1828});
1829
1830// Notifications
1831$(document).ready(function () {
1832	$(".dd_notif > a").click(function () {
1833		$(".dd_notif .subMenu").toggle();
1834		$(".notif_dropdown_click").addClass('active');
1835	});
1836
1837	$(".dd_notif ul a").click(function () {
1838		var text = $(this).html();
1839		$(".dd_notif .subMenu").hide();
1840		$(".notif_dropdown_click").removeClass('active');
1841	});
1842});
1843
1844// Account
1845$(document).ready(function () {
1846	$(".dd_account > .account_dropdown_click").click(function () {
1847		$(".dd_account .subMenu").toggle();
1848		$(".account_dropdown_click").addClass('active');
1849	});
1850
1851	$(".dd_account ul a").click(function () {
1852		var text = $(this).html();
1853		$(".dd_account .subMenu").hide();
1854		$(".account_dropdown_click").removeClass('active');
1855	});
1856
1857});
1858
1859// Support
1860$(document).ready(function () {
1861	$(".dd_support > a").click(function () {
1862		$(".dd_support .subMenu").toggle();
1863		$(".dd_support_click").addClass('active');
1864	});
1865
1866	$(".dd_support ul a").click(function () {
1867		var text = $(this).html();
1868		$(".dd_support_click").removeClass('active');
1869	});
1870
1871
1872});
1873
1874// Mobile right menu
1875$(document).ready(function () {
1876	$(".mob_menu > a").click(function () {
1877		$(".mob_menu > .nav").toggle();
1878		$(".mob_menu_click").addClass('active');
1879	});
1880
1881	$(".appli_dropdown ul a").click(function () {
1882		var text = $(this).html();
1883		$(".mob_menu > .nav").hide();
1884		$(".mob_menu_click").removeClass('active');
1885	});
1886
1887
1888});
1889
1890
1891//===================================================================
1892
1893function isNumber(n) {
1894	return !isNaN(parseFloat(n)) && isFinite(n);
1895}
1896
1897//===================================================================
1898
1899function hideshow(strId) {
1900	if ($('#' + strId).is(":visible")) {
1901		$('#' + strId).fadeOut();
1902	}
1903	else {
1904		$('#' + strId).fadeIn();
1905	}
1906}
1907
1908//===================================================================
1909
1910function getContent(strId, url) {
1911	var milliseconds = new Date().getTime();
1912	if (IsIEBrowser()) {
1913		//adds a random variable to the URL so IE stops caching requests
1914		if ((url.indexOf("&") != -1) && (url.indexOf('lcd8') > 0)) { console.log("ie"); }
1915		//if ((url.indexOf("&") != -1) && (url.indexOf('lcd8') > 0)) {url = url + "&_" + milliseconds} else {url = url + "?_" + milliseconds}
1916	}
1917	xhr = jQuery.ajax({
1918		url: url,
1919		success: function (html) {
1920			if (strId == "ajax_event_list"){
1921				var getEventWidgets = document.getElementsByClassName('ajax_event_list');
1922				for (var i = 0; i < getEventWidgets.length; i++) {
1923					getEventWidgets[i].innerHTML = html;
1924				}
1925			}
1926			if (document.getElementById(strId)) {
1927				if (IsIE8Browser()) {
1928					document.getElementById(strId).innerHTML = html;
1929				} else {
1930					$('#' + strId).css({ 'opacity': 0 });
1931					$('#' + strId).html(html).animate({ 'opacity': 1 }, 200);
1932				}
1933			}
1934			if (document.getElementById('generic')) {
1935				document.getElementById('generic').style.display = 'none';
1936			}
1937
1938            const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore', {detail: { 'parentContainerId': strId }});
1939			document.dispatchEvent(ajaxLoadMoreEvent);
1940		},
1941		error: function (rep) {
1942			if (rep.status == 403) {
1943				//location.reload();
1944			}
1945		}
1946	});
1947}
1948
1949function getContentNew(selector, url, onSuccess, onFail){
1950	jQuery.ajax({
1951		method: "GET",
1952		url: url,
1953	})
1954	.done(function( data ) {
1955		if(selector !== undefined && selector !== ""){
1956			$(selector).css({ 'opacity': 0 });
1957			$(selector).html(data).animate({ 'opacity': 1 }, 200);
1958		}
1959		if (onSuccess !== undefined && typeof onSuccess == 'function'){
1960			onSuccess(data);
1961            
1962            // selector may include CSS - ajaxLoadMore requires a container ID (pass parentContainerId:null to scan entire DOM - this is OK)
1963            const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore', {detail: { 'parentContainerId': null }});
1964			document.dispatchEvent(ajaxLoadMoreEvent);
1965		}
1966	})
1967	.fail(function( jqXHR, textStatus ) {
1968		if (onFail !== undefined && typeof onFail == 'function'){
1969			onFail(jqXHR, textStatus);
1970		}
1971	});
1972}
1973
1974//===================================================================
1975
1976function getContentNotif(strId, url) {
1977	var milliseconds = new Date().getTime();
1978	if (IsIEBrowser()) {
1979		//adds a random variable to the URL so IE stops caching requests
1980		if ((url.indexOf("&") != -1) && (url.indexOf('lcd8') > 0)) { console.log("ie"); }
1981		//if ((url.indexOf("&") != -1) && (url.indexOf('lcd8') > 0)) {url = url + "&_" + milliseconds} else {url = url + "?_" + milliseconds}
1982	}
1983	xhr = jQuery.ajax({
1984		url: url,
1985		success: function (html) {
1986			if (document.getElementById(strId)) {
1987				if (IsIE8Browser()) {
1988					document.getElementById(strId).innerHTML = html;
1989				} else {
1990					// var test = $('#' + strId).html(html).val();
1991					$('#' + strId).html(html).animate({ 'opacity': 1 }, 200);
1992				}
1993
1994                const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore',{detail: { 'parentContainerId': strId }});
1995			    document.dispatchEvent(ajaxLoadMoreEvent);
1996			}
1997		}
1998	});
1999
2000}
2001
2002//===================================================================
2003
2004function appendContent(strId, url) {
2005	url = url.replace('.aspx', '');
2006	getcontentparent = jQuery.ajax({
2007		url: url,
2008		success: function (html) {
2009			$('#' + strId).append(html);
2010
2011            const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore', {detail: { 'parentContainerId': strId }});
2012			document.dispatchEvent(ajaxLoadMoreEvent);
2013		}
2014	});
2015}
2016
2017//===================================================================
2018
2019function appendContentWithCallback(strId, url, callbackFunction, callbackParameters) {
2020	url = url.replace('.aspx', '');
2021	getcontentparent = jQuery.ajax({
2022		url: url,
2023		success: function (html) {
2024			$('#' + strId).append(html);
2025
2026			//Call the callbackFunction with the list of parameters
2027			callbackFunction.apply(this, callbackParameters);
2028		}
2029	});
2030}
2031
2032//===================================================================
2033
2034function getContentOver(strId, url) {
2035
2036	if (xhr) { xhr.abort(); }
2037	url = url.replace('.aspx', '');
2038	document.getElementById('generic').style.display = 'inline-block';
2039	xhr = jQuery.ajax({
2040		url: url,
2041		success: function (html) {
2042			if (IsIE8Browser()) {
2043				document.getElementById(strId).innerHTML = html;
2044			} else {
2045				$('#' + strId).css({ 'opacity': 0 });
2046				$('#' + strId).html(html).animate({ 'opacity': 1 }, 200);
2047			}
2048			if (document.getElementById('generic')) {
2049				document.getElementById('generic').style.display = 'none';
2050			}
2051
2052            const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore', {detail: { 'parentContainerId': strId }});
2053			document.dispatchEvent(ajaxLoadMoreEvent);
2054		}
2055	});
2056}
2057
2058//===================================================================
2059
2060function getContentParent(strId, url) {
2061	url = url.replace('.aspx', '');
2062	getcontentparent = jQuery.ajax({
2063		url: url,
2064		success: function (html) {
2065			$('#' + strId, window.parent.document).empty();
2066			$('#' + strId, window.parent.document).append(html);
2067			//window.parent.document.getElementById('generic').style.display = 'none';
2068		}
2069	});
2070}
2071
2072// ====================================================================
2073
2074//New getContent function that allows the use of a callback. It will be trigger once the ajax event is completed
2075var getContentWithCallback = function (strId, url, callbackFunction, callbackParameters) {
2076	jQuery.ajax({
2077		url: url,
2078		success: function (html) {
2079			if (document.getElementById(strId)) {
2080				if (IsIE8Browser()) {
2081					document.getElementById(strId).innerHTML = html;
2082				} else {
2083					$('#' + strId).css({ 'opacity': 0 });
2084					$('#' + strId).html(html).animate({ 'opacity': 1 }, 200);
2085				}
2086			}
2087			//Call the callbackFunction with the list of parameters
2088			callbackFunction.apply(this, callbackParameters);
2089
2090            const ajaxLoadMoreEvent = new CustomEvent('ajaxLoadMore', {detail: { 'parentContainerId': strId }});
2091			document.dispatchEvent(ajaxLoadMoreEvent);
2092		}
2093	});
2094}
2095
2096// ====================================================================
2097function IsIEBrowser() {
2098	var undef, rv = -1; // Return value assumes failure.
2099	var ua = window.navigator.userAgent;
2100	var msie = ua.indexOf('MSIE ');
2101	var trident = ua.indexOf('Trident/');
2102
2103	if (msie > 0) {
2104		// IE 10 or older => return version number
2105		rv = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
2106	} else if (trident > 0) {
2107		// IE 11 (or newer) => return version number
2108		var rvNum = ua.indexOf('rv:');
2109		rv = parseInt(ua.substring(rvNum + 3, ua.indexOf('.', rvNum)), 10);
2110	}
2111
2112	return ((rv > -1) ? rv : undef);
2113}
2114
2115// ====================================================================
2116
2117function IsIE8Browser() {
2118	var rv = -1;
2119	var ua = navigator.userAgent;
2120	var re = new RegExp("Trident\/([0-9]{1,}[\.0-9]{0,})");
2121	if (re.exec(ua) != null) {
2122		rv = parseFloat(RegExp.$1);
2123	}
2124	return (rv == 4);
2125}
2126
2127
2128
2129//===================================================================
2130
2131function save_privacy(feed_id, type) {
2132	var officers_only = '';
2133	var to_type_ids = '';
2134	var to_yogs = '';
2135	var to_people_tags = '';
2136	var to_club_ids = '';
2137	var privacy_level = '';
2138	var id = "#privacy_lightbox_" + feed_id;
2139
2140
2141	if ($("#school_wide_radio_" + feed_id).is(':checked')) {
2142		$("#post_privacy_level").val("0");
2143		privacy_level = 0;
2144	}
2145
2146	if ($("#members_radio_" + feed_id).is(':checked')) {
2147		$("#post_privacy_level").val("2");
2148		privacy_level = 2;
2149	}
2150
2151	if ($("#student_type_radio_" + feed_id).is(':checked')) {
2152		$("#post_privacy_level").val("6");
2153		privacy_level = 6;
2154		$(id + " input:checkbox[name=to_type_ids]:checked").each(function () {
2155			to_type_ids += ',' + $(this).val();
2156		});
2157		$(id + " input:checkbox[name=to_yogs]:checked").each(function () {
2158			to_yogs += ',' + $(this).val();
2159		});
2160	}
2161	if ($("#people_tags_radio_" + feed_id).is(':checked')) {
2162		privacy_level = 9;
2163		$("#post_privacy_level").val("9");
2164		$(id + " input:checkbox[name=to_people_tags]:checked").each(function () {
2165			to_people_tags += ',' + $(this).val();
2166		});
2167	}
2168	if ($("#club_ids_radio_" + feed_id).is(':checked')) {
2169		privacy_level = 2;
2170		$("#post_privacy_level").val("2");
2171		$(id + " input:checkbox[name=to_club_ids]:checked").each(function () {
2172			if (to_club_ids == '') {
2173				to_club_ids = $(this).val();
2174			}
2175			else {
2176				to_club_ids += ',' + $(this).val();
2177			}
2178		});
2179	}
2180	if ($("#officers_radio_" + feed_id).is(':checked')) {
2181		privacy_level = 3;
2182		$("#post_privacy_level").val("3");
2183		officers_only = '1';
2184	}
2185
2186	if (type == 'socialpost') {
2187
2188		$("#to_type_ids").val(to_type_ids);
2189		$("#to_yogs").val(to_yogs);
2190		$("#to_people_tags").val(to_people_tags);
2191		$("#to_club_ids").val(to_club_ids);
2192		$("#officers_only").val(officers_only);
2193
2194		// Passing value for poll privacy . Satnam Singh Date 11-14-2013
2195		$("#hdnTo_type_ids").val(to_type_ids);
2196		$("#hdnTo_yogs").val(to_yogs);
2197		$("#hdnTo_people_tags").val(to_people_tags);
2198		$("#hdnTo_club_ids").val(to_club_ids);
2199		$("#hdnOfficers_only").val(officers_only);
2200		$("#hdnSave_Privacy").val("1");
2201
2202	}
2203	else if (type == 'feed') {
2204		jQuery.ajax({
2205			type: "POST",
2206			cache: false,
2207			url: "/update_feedprivacy.aspx",
2208			data: { feed_id: feed_id, officers_only: officers_only, to_type_ids: to_type_ids, to_people_tags: to_people_tags, to_yogs: to_yogs, to_club_ids: to_club_ids },
2209			success: function (data) {
2210
2211			}
2212		});
2213	}
2214	else if (type == 'accessUser') {
2215		jQuery.ajax({
2216			url: "/div_access.aspx",
2217			data: { privacy_level: privacy_level, feed_id: feed_id, officers_only: officers_only, to_type_ids: to_type_ids, to_people_tags: to_people_tags, to_yogs: to_yogs, to_club_ids: to_club_ids },
2218			success: function (data) {
2219				self.parent.tb_remove();
2220			}
2221		});
2222
2223	}
2224
2225}
2226
2227
2228//===================================================================
2229
2230function searchpeoplemark(type, itemId, student_id, student_name, del, DBmessage) {
2231	// DBmessage : success -> remove link else error message
2232	$("#" + type + "_result_list").show();
2233	if (del == 0) {
2234		var append;
2235		if (DBmessage == "success") {
2236			append = '<a class="btnDelete" href=\'#\' onClick="javascript:updatePeopleList(' + itemId + ', \'' + type + '\',' + student_id + ', \'\', 1);"></a>';
2237			if (type == 'officers') {
2238				id_array.push(student_id);
2239				tag_id_array.push(itemId);
2240				type_array.push('notification_new_officer')
2241			}
2242			else if (type == 'members') {
2243				id_array.push(student_id);
2244				tag_id_array.push(itemId);
2245				type_array.push('notification_new_member')
2246			}
2247			else if (type == 'tags') {
2248				id_array.push(student_id);
2249				tag_id_array.push(itemId);
2250			}
2251		}
2252		else if (DBmessage == "member_success") {
2253			append = '<a href=\'#\' class="btnDelete" onClick="javascript:updatePeopleList(' + itemId + ', \'' + type + '\',' + student_id + ', \'\', 2);"></a>';
2254			id_array.push(student_id);
2255			tag_id_array.push(itemId);
2256			type_array.push('notification_new_member_officer')
2257		}
2258		else {
2259			append = '<span class="searchpeople_message">' + DBmessage + '</span>';
2260		}
2261		$("#added_" + type).append("<li id='person_" + student_id + "'><span id='result_" + type + student_id + "'></span>" + append + "</li>");
2262		$("#result_" + type + student_id).html(student_name);
2263	}
2264	else {
2265		for (var i = 0; i < id_array.length; i++) {
2266			if (id_array[i] == student_id) {
2267				id_array.splice(i, 1);
2268				type_array.splice(i, 1);
2269				break;
2270			}
2271		}
2272		if ($("#person_" + student_id).length) {
2273			$("#person_" + student_id).hide();
2274		}
2275		$("#result_" + type + student_id).hide();
2276	}
2277}
2278
2279//===================================================================
2280
2281function cancel_searchpeople(type, id_array, tag_id_array) {
2282	for (var i = 0; i < id_array.length; i++) {
2283		jQuery.ajax({
2284			type: "POST",
2285			cache: false,
2286			url: "/update_peoplelist.aspx",
2287			dataType: 'json',
2288			data: { type: type, tag_id: tag_id_array[i], student_id: id_array[i], del: 1 },
2289			success: function (data) {
2290
2291			}
2292		});
2293	}
2294}
2295
2296//==================================================================
2297
2298function send_notifications(id_array, type_array) {
2299	for (var i = 0; i < id_array.length; i++) {
2300		jQuery.ajax({
2301			type: "POST",
2302			cache: false,
2303			url: "/update_peoplelist.aspx",
2304			dataType: 'json',
2305			data: { type: type_array[i], tag_id: 0, student_id: id_array[i], del: 0 },
2306			success: function (data) {
2307
2308			}
2309		});
2310	}
2311}
2312
2313
2314//===================================================================
2315
2316function updatePeopleList(tag_id, type, student_id, student_name, del) {
2317	jQuery.ajax({
2318		type: "POST",
2319		cache: false,
2320		url: "/update_peoplelist.aspx",
2321		dataType: 'json',
2322		data: { type: type, tag_id: tag_id, student_id: student_id, del: del },
2323		success: function (data) {
2324			var message = data.message;
2325			searchpeoplemark(type, tag_id, student_id, student_name, del, message);
2326		}
2327	});
2328
2329}
2330
2331//===================================================================
2332
2333function updateFavorite(message, student_id, id, type, like) {
2334	jQuery.ajax({
2335		type: "POST",
2336		cache: false,
2337		url: "/update_favorites.aspx",
2338		data: { id: id, student_id: student_id, type: type, like: like },
2339		success: function () {
2340			// Confirmation message
2341		}
2342	});
2343
2344}
2345
2346//===================================================================
2347
2348function favorite(object, itemClass, type, student_id) {
2349
2350	var auxUid = (object.className).replace("bookmark_", "bookmark_star_").replace('btn ', '');
2351	auxUid = '.' + auxUid;
2352	var auxUid2 = (object.className).replace("bookmark_", "bookmark_data_").replace('btn ', '');
2353	auxUid2 = '.' + auxUid2;
2354	var mark = $(auxUid2).html();
2355	if (mark == "Star") {
2356		updateFavorite('Star', student_id, itemClass, type, 1);
2357		$(auxUid2).html("Unstar");
2358		$(auxUid).attr("src", "/images/Unstar.png");
2359		$(auxUid).attr("alt", "Remove from favorites");
2360		let parentLinkAccLabel = $(auxUid).parent().attr('aria-label');
2361		if (!isEmpty(parentLinkAccLabel)) {
2362			$(auxUid).parent().attr('aria-label', parentLinkAccLabel.replace('Add to favorites', 'Remove from favorites'));
2363		}
2364	} else {
2365		updateFavorite('Unstar', student_id, itemClass, type, 0);
2366		$(auxUid2).html("Star");
2367		$(auxUid).attr("src", "/images/Star.png");
2368		$(auxUid).attr("alt", "Add to favorites");
2369		let parentLink
2369AccLabel = $(auxUid).parent().attr('aria-label');
2370		if (!isEmpty(parentLinkAccLabel)) {
2371			$(auxUid).parent().attr('aria-label', parentLinkAccLabel.replace('Remove from favorites', 'Add to favorites'));
2372		}
2373	}
2374
2375	favoriteCmsg(type, mark);
2376}
2377
2378
2379
2380//===================================================================
2381
2382function favoriteCmsg(type, mark) {
2383	if (type == 'event' && mark == 'Star') {
2384		cmsg('This event was added to your personal calendar feed.', 2, 0);
2385		if (window['refreshListEvents']) { refreshListEvents(); }
2386	}
2387	if (type == 'event' && mark == 'Unstar') {
2388		cmsg('This event was removed from your personal calendar feed.', 2, 0);
2389		if (window['refreshListEvents']) { refreshListEvents(); }
2390	}
2391	if (type == 'people' && mark == 'Star') {
2392		cmsg('This person was added to your short list.', 2, 0);
2393		if (window['refreshListPeople']) { refreshListPeople(); }
2394	}
2395	if (type == 'people' && mark == 'Unstar') {
2396		cmsg('This person was removed from your short list.', 2, 0);
2397		if (window['refreshListPeople']) { refreshListPeople(); }
2398	}
2399	if (type == 'group' && mark == 'Star') {
2400		cmsg('This group was added to your favorites.', 2, 0);
2401		//if (window['refreshListGroups']) { refreshListGroups(); }
2402	}
2403	if (type == 'group' && mark == 'Unstar') {
2404		cmsg('This group was removed from your favorites.', 2, 0);
2405		//if (window['refreshListGroups']) { refreshListGroups(); }
2406	}
2407	if (type == 'survey' && mark == 'Star') {
2408		cmsg('This survey will be placed on top of your surveys list next time you load the page.', 3, 0);
2409		//if (window['refreshListGroups']) { refreshListGroups(); }
2410	}
2411	if (type == 'survey' && mark == 'Unstar') {
2412		cmsg('This survey was removed from your favorites.', 2, 0);
2413		//if (window['refreshListGroups']) { refreshListGroups(); }
2414	}
2415	if (type == 'doc' && mark == 'Star') {
2416		cmsg('This document will be placed on top of your files list next time you load the page.', 3, 0);
2417		//if (window['refreshListGroups']) { refreshListGroups(); }
2418	}
2419	if (type == 'doc' && mark == 'Unstar') {
2420		cmsg('This document was removed from your favorites.', 2, 0);
2421		//if (window['refreshListGroups']) { refreshListGroups(); }
2422	}
2423}
2424
2425
2426//===================================================================
2427
2428function updateLikes(message, student_id, uid, type, like) {
2429	jQuery.ajax({
2430		type: "POST",
2431		cache: false,
2432		url: "/update_likes",
2433		data: { uid: uid, student_id: student_id, type: type, like: like },
2434		success: function () {
2435			// Confirmation message
2436		}
2437	});
2438}
2439
2440function likemark(object, itemUid, type, student_id) {
2441	var mark = object.innerHTML;
2442	var auxUid = (object.id).replace("like_", "likes_");
2443	var auxUid2 = (object.id).replace("like_", "blue_like_");
2444	var number = document.getElementById(auxUid).innerHTML
2445	var texto;
2446	if (mark == "Like") {
2447		updateLikes('Liked', student_id, itemUid, type, 1);
2448		object.innerHTML = "Unlike";
2449		if (number / 1 == 0) { texto = "1"; } else { texto = (number / 1 + 1) }
2450		document.getElementById(auxUid).innerHTML = texto;
2451	} else {
2452		updateLikes('Unliked', student_id, itemUid, type, 0);
2453		object.innerHTML = "Like";
2454		if (number / 1 == 2) { texto = "1"; } else { texto = (number / 1 - 1) }
2455		document.getElementById(auxUid).innerHTML = texto;
2456	}
2457	$('#' + auxUid2).show();
2458}
2459
2460
2461//===================================================================
2462
2463function updateComments(message, student_id, uid, type, comment) {
2464	jQuery.ajax({
2465		type: "POST",
2466		cache: false,
2467		url: "/update_comments",
2468		data: { uid: uid, student_id: student_id, type: type, comment: comment },
2469		success: function () {
2470			getContent('comments_' + type + '_' + uid, 'ajax_comments?to_uid=' + uid + '&type=' + type);
2471		}
2472	});
2473
2474}
2475
2476//===================================================================
2477
2478function commentmark(object, itemUid) {
2479	var number = $("#comment_number_" + itemUid).text();
2480	var texto;
2481	if (number / 1 == 0) { texto = "1"; } else { texto = (number / 1 + 1) }
2482	if (texto == "1") {
2483		$("#blue_number_" + itemUid).css('display', 'block');
2484	}
2485	$("#comment_number_" + itemUid).text(texto);
2486	/*
2487	var auxUid = (object.id).replace("comment_text_button_", "comment_number_");
2488	var auxUid2 = (object.id).replace("comment_text_button_", "blue_number_");
2489	var number = document.getElementById(auxUid).innerHTML
2490	var texto;
2491	if (number / 1 == 0) { texto = "1"; } else { texto = (number / 1 + 1) }
2492	document.getElementById(auxUid).innerHTML = texto;
2493	$('#' + auxUid2).show();
2494	*/
2495}
2496
2497
2498//===================================================================
2499
2500function display_lightbox(strUid, type) {
2501	$("#lightbox_" + strUid).show();
2502	if (type == 'comments') {
2503		$("#comment_lightbox_" + strUid).show();
2504		$("#comment_block_" + strUid).show();
2505		$("#comment_text_button_" + strUid).show();
2506	}
2507	else if (type == "privacy") {
2508		$("#privacy_lightbox_" + strUid).show();
2509	}
2510	else if (type == "online_now") {
2511		$("#online_now").show();
2512	}
2513	else if (type == "manage") {
2514		$("#popup_manage").show();
2515	}
2516	else if (type == "photo") {
2517
2518		$("#comment_" + "_lightbox_" + type + "_" + strUid).show();
2519	}
2520	else if (type != "") {
2521		$("#" + type + "_lightbox_" + strUid).show();
2522	}
2523}
2524
2525
2526
2527//===================================================================
2528
2529function hide_lightbox(strUid, type) {
2530	$("#lightbox_" + strUid).hide();
2531	if (type == 'comments') {
2532		$("#comment_lightbox_" + strUid).hide();
2533	}
2534	else if (type == 'privacy') {
2535		$("#privacy_lightbox_" + strUid).hide();
2536	}
2537	else if (type == "popup_online") {
2538		$("#popup_online").hide();
2539	}
2540	else if (type == "manage") {
2541		$("#popup_manage").hide();
2542	}
2543	//else if (type == "photo") {
2544	//    $("#comment_" + "_lightbox_" + type + "_" + strUid).hide();
2545	//}
2546	else if (type != '') {
2547		$("#" + type + "_lightbox_" + strUid).hide();
2548	}
2549}
2550
2551//===================================================================
2552
2553function postContent(obj, strId, url) {
2554	var parameters;
2555	parameters = '';
2556	for (var i = 0; i < obj.elements.length; ++i) {
2557		var cobj = obj.elements[i];
2558		if (cobj.type == 'radio' || cobj.type == 'checkbox') {
2559			if (cobj.checked) {
2560				parameters = parameters + cobj.name + '=' + encodeURIComponent(cobj.value) + '&';
2561			}
2562		}
2563		else {
2564			parameters = parameters + cobj.name + '=' + encodeURIComponent(cobj.value) + '&';
2565		}
2566		//alert(cobj.name+' = '+cobj.value);
2567	}
2568	parameters = parameters + '1=1';
2569	//confirmMessage(obj, 'Posting Data...');
2570	document.getElementById('generic').style.display = 'none';
2571	return jQuery.ajax({
2572		type: "POST",
2573		cache: false,
2574		url: url,
2575		data: parameters,
2576		success: function (html) {
2577			if (strId) {
2578				jQuery('#' + strId).empty();
2579				jQuery('#' + strId).append(html);
2580			}
2581		}
2582	});
2583}
2584
2585//===================================================================
2586
2587function submitTag(obj, strId, url) {
2588	var parameters;
2589	parameters = '';
2590	for (var i = 0; i < obj.childNodes.length; ++i) {
2591		var cobj = obj.childNodes[i];
2592		//console.log(cobj.tagName);
2593		if (cobj.type == 'radio' || cobj.type == 'checkbox') {
2594			if (cobj.checked) {
2595				parameters = parameters + cobj.name + '=' + encodeURIComponent(cobj.value) + '&';
2596			}
2597		}
2598		else if (cobj.type == 'select') {
2599			parameters = parameters + cobj.name + '=' + encodeURIComponent(cobj.options[cobj.selectedIndex].value) + '&';
2600		}
2601		else {
2602			parameters = parameters + cobj.name + '=' + encodeURIComponent(cobj.value) + '&';
2603		}
2604		//alert(cobj.name+' = '+cobj.value);
2605	}
2606	parameters = parameters + '1=1';
2607	//confirmMessage(obj, 'Posting Data...');
2608	//console.log('HERE: ' + url);
2609	jQuery.ajax({
2610		type: "POST",
2611		cache: false,
2612		url: url,
2613		data: parameters,
2614		success: function (html) {
2615			if (strId) {
2616				jQuery('#' + strId).empty();
2617			}
2618			jQuery('#' + strId).append(html);
2619		}
2620	});
2621	//document.getElementById('generic').style.display = 'none';
2622}
2623
2624
2625//=====================================================================
2626
2627function toggleRanking(e, id) {
2628	if (e.checked) {
2629		document.getElementById(id).value = e.value;
2630	}
2631	else {
2632		document.getElementById(id).value = '';
2633	}
2634}
2635
2636
2637//=====================================================================
2638
2639function checkAllCheckboxes(name) {
2640	aux = document.getElementById(name);
2641	nbs = aux.getElementsByTagName("input");
2642	for (x = 0; x < nbs.length; x++) {
2643		nbs[x].checked = true;
2644	}
2645	return false;
2646}
2647
2648function uncheckAllCheckboxes(name) {
2649	aux = document.getElementById(name);
2650	nbs = aux.getElementsByTagName("input");
2651	for (x = 0; x < nbs.length; x++) {
2652		nbs[x].checked = false;
2653	}
2654	return false;
2655}
2656
2657function uncheckReableAllCheckboxes(name) {
2658    aux = document.getElementById(name);
2659    nbs = aux.getElementsByTagName("input");
2660    for (x = 0; x < nbs.length; x++) {
2661        nbs[x].checked = false;
2662        nbs[x].disabled = false;
2663    }
2664    return false;
2665}
2666
2667function checkDisableAllCheckboxes(name) {
2668	aux = document.getElementById(name);
2669	nbs = aux.getElementsByTagName("input");
2670	for (x = 0; x < nbs.length; x++) {
2671		nbs[x].checked = true;
2672		nbs[x].disabled = true;
2673	}
2674	return false;
2675}
2676
2677function uncheckDisableAllCheckboxes(name) {
2678	aux = document.getElementById(name);
2679	nbs = aux.getElementsByTagName("input");
2680	for (x = 0; x < nbs.length; x++) {
2681		nbs[x].checked = false;
2682		nbs[x].disabled = false;
2683	}
2684	return false;
2685}
2686
2687function checkAllType(type) {
2688	nbs = document.getElementsByTagName("input");
2689	for (x = 0; x < nbs.length; x++) {
2690		if ((" " + nbs[x].className + " ").replace(/[\n\t]/g, " ").indexOf(" " + type + " ") > -1) {
2691			nbs[x].checked = true;
2692		}
2693	}
2694	return false;
2695}
2696
2697function uncheckAllType(type) {
2698	nbs = document.getElementsByTagName("input");
2699	for (x = 0; x < nbs.length; x++) {
2700		if ((" " + nbs[x].className + " ").replace(/[\n\t]/g, " ").indexOf(" " + type + " ") > -1) {
2701			nbs[x].checked = false;
2702		}
2703	}
2704	return false;
2705}
2706
2707//==============================================================
2708
2709function getPosition(element) {
2710	var left = 0;
2711	var top = 0;
2712	/*On r�cup�re l'�l�ment*/
2713	var e = document.getElementById(element);
2714	/*Tant que l'on a un �l�ment parent*/
2715	while (e.offsetParent != undefined && e.offsetParent != null) {
2716		/*On ajoute la position de l'�l�ment parent*/
2717		left += e.offsetLeft + (e.clientLeft != null ? e.clientLeft : 0);
2718		top += e.offsetTop + (e.clientTop != null ? e.clientTop : 0);
2719		e = e.offsetParent;
2720	}
2721	return new Array(left, top);
2722}
2723
2724//==============================================================================
2725//ts en ms
2726function timestamp() {
2727	var date = new Date();
2728	return date.getTime();
2729}
2730
2731//==============================================================================
2732
2733function setWhiteThickbox() {
2734	//$('#TB_overlay').css('background-color', '#fcfcfc');
2735	$('#TB_overlay').css('opacity', '0.4');
2736	$('#TB_window').css('border', '1px solid #535353');
2737	$('#TB_window').css('box-shadow', '0 4px 16px rgba(0,0,0,.2)');
2738	$('#TB_ajaxContent').css('width', 'auto');
2739	$('#TB_ajaxContent').css('height', 'auto');
2740	$('#TB_ajaxContent').css('max-height', '570px');
2741	$('#TB_ajaxContent').css('overflow', 'auto');
2742}
2743
2744function getRoomDetails() {
2745	if (document.getElementById("room_id")) {
2746		var e = document.getElementById("room_id");
2747		var strId = e.options[e.selectedIndex].value;
2748		getContent('room_details', '/ajax_room_details?room_id=' + strId);
2749	}
2750}
2751
2752//=================================================================================
2753var localObj;
2754
2755var xhrDataModel;
2756function setValue(objectName, id, updates, url, callback) {
2757
2758	localObj = JSON.parse(JSON.stringify(window[objectName]));
2759	updates = JSON.parse(updates.replace(/\r?\n/g, "\\n"));
2760	if (id != '-1') {
2761		var i = 0; var exit = 0;
2762		while (i < window['localObj'].items.length && exit == 0) {
2763			if (window['localObj'].items[i].id.val == id) {
2764				for (var fieldName in updates) {
2765					window['localObj'].items[i][fieldName].val = updates[fieldName];
2766					window['localObj'].items[i][fieldName].u = 1;
2767				}
2768				exit++;
2769			}
2770			i++;
2771		}
2772	} else {
2773		window['localObj'].items[0].id.val = '-1';
2774		for (var fieldName in updates) {
2775			window['localObj'].items[0][fieldName].val = updates[fieldName];
2776			window['localObj'].items[0][fieldName].u = 1;
2777		}
2778	}
2779	cleanItems(url, 'localObj', callback);
2780}
2781
2782function cleanItems(url, objectName, callback) {
2783	var i = 0; var updated; var status;
2784	while (i < window[objectName].items.length) {
2785		updated = 0;
2786		for (field in window[objectName].items[i]) {
2787			if (window[objectName].items[i][field].u == 1) {
2788				updated += 1;
2789			}
2790			else {
2791				if (field != 'id') {
2792					window[objectName].items[i][field].val = '';
2793				}
2794			}
2795		}
2796		if (updated == 0) { window[objectName].items.splice(i, 1); i -= 1; }
2797		i++;
2798	}
2799	sendObject(url, objectName, callback);
2800}
2801
2802function sendObject(url, objectName, callback) {
2803	$.post(url, { object: JSON.stringify(window[objectName]) }, function (data) {
2804		callback(data);
2805	});
2806}
2807
2808
2809function getDataJSON(url, objectName, subLevel, callback, noResultCallback) {
2810	if (url.indexOf('?') > 0) { url = url + '&' + timestamp(); } else { url = url + '?' + timestamp(); }
2811	$.getJSON(url, function (data) {
2812		if (data.length > 0) {
2813			if (subLevel != '') {
2814				window[objectName] = data[0][subLevel];
2815				if (data[0][subLevel].length == 0) {
2816					noResultCallback();
2817					return true;
2818				}
2819			}
2820			else {
2821				window[objectName] = data;
2822			}
2823			if (window[objectName].length == 0) {
2824				noResultCallback();
2825			}
2826			for (var i = 0; i < window[objectName].length; i++) {
2827				callback(window[objectName][i]);
2828			}
2829			tb_init('a.ltb');
2830			$('[data-toggle="tooltip"]').tooltip();
2831		}
2832		else {
2833			noResultCallback();
2834		}
2835	});
2836}
2837
2838var searchListTimeout;
2839function searchList(objectName, fieldName, q, callback, containerId, noResult, callUrl, subType) {
2840	var k = 0;
2841	var lExit = 0;
2842	var ids = '';
2843	q = q.toLowerCase();
2844	if (document.getElementById(containerId)) {
2845		if (IsIE8Browser()) {
2846			// innerText instead of innerHTML for IE8 <table> Bug - MM 8/13/2013
2847			document.getElementById(containerId).innerText = '';
2848		} else {
2849			document.getElementById(containerId).innerHTML = '';
2850		}
2851	}
2852	var fieldList = fieldName.split(',');
2853	clearTimeout(searchListTimeout);
2854	for (var i = 0; i < window[objectName].length; i++) {
2855		lExit = 0;
2856		for (var j = 0; j < fieldList.length; j++) {
2857			if (lExit == 0) {
2858				if (window[objectName][i][fieldList[j]].toLowerCase().indexOf(q.replace(' ', '&nbsp;')) != -1) {
2859					callback(window[objectName][i]);
2860					ids = ids + window[objectName][i].id + ','
2861					k += 1;
2862					lExit = 1;
2863				}
2864			}
2865		}
2866	}
2867	if (callUrl.length > 0 && q.length > 0) {
2868		if (q.length > 2 || k == 0) {
2869			callUrl = (k == 0) ? (callUrl + '&limit=50') : callUrl;
2870			ids = (k == 0) ? 0 : ids;
2871			searchListTimeout = setTimeout('searchListCallUrl("' + callUrl + '", "' + ids + '",' + k + ',' + callback + ',' + noResult + ',"' + subType + '")', 500);
2872		}
2873	}
2874	if (k == 0 && q.length == 0) {
2875		noResult();
2876	}
2877}
2878
2879function searchListCallUrl(callUrl, ids, k, callback, noResult, subType) {
2880	$.getJSON(callUrl + '&current_ids=' + ids, function (data) {
2881		for (var j = 0; j < data[0][subType].length; j++) {
2882			callback(data[0][subType][j]);
2883			k += 1;
2884		}
2885		if (k == 0) {
2886			noResult();
2887		}
2888	});
2889}
2890
2891function writeDate(updatedOn) {
2892	updatedOn = updatedOn.split(' ');
2893	return updatedOn(0);
2894}
2895
2896//Google methods experiment (using DOM core functions) MM 5/28
2897
2898//p: parent element
2899//h: innerHTML
2900//i: id
2901//c: className
2902function ul(p, h, i, c) {
2903	var t = document.createElement("ul");
2904	t.id = i;
2905	t.className = c;
2906	t.innerHTML = h;
2907	p.appendChild(t);
2908	return t;
2909}
2910
2911function li(p, h, i, c) {
2912	var t = document.createElement("li");
2913	t.id = i;
2914	t.className = c;
2915	t.innerHTML = h;
2916	p.appendChild(t);
2917	return t;
2918}
2919
2920function div(p, h, i, c) {
2921	var t = document.createElement("div");
2922	t.id = i;
2923	t.className = c;
2924	t.innerHTML = h;
2925	if (p) { p.appendChild(t); }
2926	return t;
2927}
2928
2929function td(p, h, i, c) {
2930	var t = document.createElement("td");
2931	t.id = i;
2932	t.className = c;
2933	t.innerHTML = h;
2934	p.appendChild(t);
2935	return t;
2936}
2937
2938function span(p, h, i, c) {
2939	var t = document.createElement("span");
2940	t.id = i;
2941	t.className = c;
2942	t.innerHTML = h;
2943	p.appendChild(t);
2944	return t;
2945}
2946// END Google methods experiment (using DOM core functions) MM 5/28
2947
2948
2949function tipInfo(e, id, topOffset) {
2950	$(e).mouseover(function () {
2951		$(id).css('left', $(this).offset().left + 'px');
2952		$(id).css('top', $(this).offset().top + topOffset + 'px');
2953		$(id).show();
2954	});
2955	$(e).mouseout(function () {
2956		$(id).css('display', 'none');
2957	});
2958}
2959
2960var cumulativeOffset = function (element) {
2961	var top = 0, left = 0;
2962	do {
2963		top += element.offsetTop || 0;
2964		left += element.offsetLeft || 0;
2965		element = element.offsetParent;
2966	} while (element);
2967
2968	return {
2969		top: top,
2970		left: left
2971	};
2972};
2973
2974//Time functions
2975function formatAMPM(date) {
2976	var hours = date.getHours();
2977	var minutes = date.getMinutes();
2978	var ampm = hours >= 12 ? 'pm' : 'am';
2979	hours = hours % 12;
2980	hours = hours ? hours : 12; // the hour '0' should be '12'
2981	minutes = minutes < 10 ? '0' + minutes : minutes;
2982	var strTime = hours + ':' + minutes + ' ' + ampm;
2983	return strTime;
2984}
2985
2986
2987//--------------------------- SEARCH PEOPLE ----------------------------
2988
2989var peopleSearch;
2990function searchPeople(responseId, q, url, callback, noResult) {
2991	if (!q.length > 0) { $('#' + responseId).html(''); }
2992	$.getJSON(url, { q: q }, function (data) {
2993		peopleSearch = data;
2994		searchList('peopleSearch', 'primary_field,secondary_field', q, callback, responseId, '', '', '');
2995	});
2996}
2997
2998//-----------------------------------------------------------------------
2999function budget_item_calculate(index) {
3000	var qty = document.getElementsByName('qty_' + index)[0].value;
3001	var unit = document.getElementsByName('unit_' + index)[0].value;
3002	qty = (qty) ? parseFloat(qty) : 0;
3003	unit = (unit) ? parseFloat(unit) : 0;
3004	if ((qty * unit).toString() != 'NaN') {
3005		document.getElementsByName('amount_' + index)[0].value = (qty * unit);
3006	} else {
3007		document.getElementsByName('amount_' + index)[0].value = 0;
3008	}
3009}
3010
3011//---------------------------------------------------------------------
3012
3013function truncateText(str, l) {
3014	var s;
3015	if (str.length > l) {
3016		s = str.substring(0, l - 3) + '...';
3017	} else {
3018		s = str;
3019	}
3020	return s;
3021}
3022
3023function tb_getPageSize() {
3024	var de = document.documentElement;
3025	var w = window.innerWidth || self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
3026	var h = window.innerHeight || self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
3027	arrayPageSize = [w, h];
3028	return arrayPageSize;
3029}
3030
3031function megaOverlay() {
3032	var WH = tb_getPageSize();
3033	var p = document.getElementsByTagName('body')[0];
3034	var e = div(p, '', 'megaOverlay', '');
3035	var e2 = div(p, '<img src="/images/loader-big.gif" alt=""/> Please wait...<br/>', 'megaOverlayInfo', '');
3036	div(e2, 'The image is resized to fit in the template.', '', 'moreInfo');
3037	if (!IsIE8Browser()) {
3038		div(e2, '<progress id="CKEProgress" value="0"></progress>', '', 'moreInfoProgress');
3039	}
3040	$(e).css('background-color', '#fff');
3041	$(e).css('opacity', '0');
3042	$(e).css('position', 'fixed');
3043	$(e).css('width', WH[0] + 'px');
3044	$(e).css('height', WH[1] + 'px');
3045	$(e).css('top', '0px');
3046	$(e).css('text-align', 'center');
3047	$(e).css('line-height', WH[1] + 'px');
3048	$(e).css('z-index', '100000');
3049	//$(e).html('<img src="/images/loader-big.gif" alt=""/>');
3050	$(e).animate({ 'opacity': '0.4' }, 300);
3051}
3052
3053function smallMegaOverlay() {
3054	var WH = tb_getPageSize();
3055	var p = document.getElementsByTagName('body')[0];
3056	var e = div(p, '', 'megaOverlay', '');
3057	var e2 = div(p, '<img src="/images/loader-big.gif" alt=""/> Please wait...<br/>', 'megaOverlayInfo', '');
3058	div(e2, 'The image is resized to fit in the template.', '', 'moreInfo');
3059	$(e).css('background-color', '#fff');
3060	$(e).css('opacity', '0');
3061	$(e).css('position', 'fixed');
3062	$(e).css('width', WH[0] + 'px');
3063	$(e).css('height', WH[1] + 'px');
3064	$(e).css('top', '0px');
3065	$(e).css('text-align', 'center');
3066	$(e).css('line-height', WH[1] + 'px');
3067	$(e).css('z-index', '100000');
3068	//$(e).html('<img src="/images/loader-big.gif" alt=""/>');
3069	$(e).animate({ 'opacity': '0.4' }, 300);
3070}
3071
3072
3073//-----------------------------------------------------
3074
3075function setAnswerOptionId(uid, val) {
3076	if (document.getElementById(uid + '_option_id')) {
3077		document.getElementById(uid + '_option_id').value = val;
3078	}
3079}
3080
3081//----------------------------------------------------
3082
3083function setAppliClass(name) {
3084	$('.appli_dropdown').removeClass('admin_dp');
3085	$('.appli_dropdown').removeClass('directory_dp');
3086	$('.appli_dropdown').removeClass('rooms_dp');
3087
3088	$('.appli_dropdown').addClass('active');
3089	$('.appli_dropdown').addClass(name);
3090}
3091
3092// ------------ OLD cg.js -----------------------------
3093
3094function w_tabChange(divName, linkObject) {
3095	var x;
3096	var tabToShow = 0;
3097	var arrayLinks = document.getElementById(divName).getElementsByTagName("a");
3098	// set aria-selected=true for active tab (linkObject) and false for all other tabs
3099	$(arrayLinks).attr('aria-selected', 'false');
3100	$(linkObject).attr('aria-selected', 'true');
3101	// put all the active links as inactive and hide their tab
3102	for (x = 0; x < arrayLinks.length; x++) {
3103		arrayLinks[x].className = (arrayLinks[x].className).replace(" active", "");
3104		if (arrayLinks[x] == linkObject) {
3105			// put the current link as active and show its tab
3106			linkObject.className = linkObject.className + " active";
3107		}
3108	}
3109}
3110
3111function w_showHideTabs(tab_id) {
3112	if (tab_id != active_tab) {
3113		$('#w_tab_' + active_tab).animate({ "height": "toggle", "opacity": "toggle" });
3114		$('#w_tab_' + tab_id).animate({ "height": "toggle", "opacity": "toggle" });
3115		active_tab = tab_id;
3116	}
3117}
3118
3119function trim(value) {
3120	//Deletes spaces and return carriage
3121	var temp = value;
3122	var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
3123	if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
3124	var obj = /  /g;
3125	while (temp.match(obj)) { temp = temp.replace(obj, " "); }
3126	return temp;
3127}
3128
3129function confirmMessage(obj, message) {
3130	document.getElementById('generic').innerHTML = '';
3131	document.getElementById('generic').style.display = 'inline-block';
3132	document.getElementById('generic').innerHTML = '<img style="width:40px;vertical-align:middle" src="/images/loader-big.gif"/> Loading...';
3133	if (message.length < 20) {
3134		//document.getElementById('generic').style.width = '100px';
3135	}
3136	if (message == 'Loading...') {
3137		setTimeout("document.getElementById(\'generic\').style.display=\'none\';", 15000);
3138	}
3139	else {
3140		if (message.length > 50) {
3141			setTimeout("document.getElementById(\'generic\').style.display=\'none\';", 10000);
3142		}
3143		else {
3144			setTimeout("document.getElementById(\'generic\').style.display=\'none\';", 10000);
3145		}
3146	}
3147}
3148
3149function cmsg(message, duration, loading_sign) {
3150	if (message == '') { message = 'Loading'; }
3151	if (document.getElementById('generic')) {
3152		document.getElementById('generic').innerHTML = '';
3153		if (loading_sign == 1) {
3154			document.getElementById('generic').innerHTML = '<img style="width:20px;margin-right:10px;vertical-align:middle" src="/images/loader-big.gif"/>' + message;
3155		} else {
3156			document.getElementById('generic').innerHTML = message;
3157		}
3158
3159		if ($('#generic').attr("waiting") == null) {
3160			$('#generic').attr("waiting", 0);
3161		}
3162
3163		document.getElementById('generic').style.display = 'inline-block';
3164		$('#generic').animate({ 'top': '0px' }, 400);
3165
3166		$('#generic').attr("waiting", parseInt($('#generic').attr("waiting"), 10) + 1);
3167		setTimeout(function() {
3168			//document.getElementById('generic').style.display = 'none';
3169			$('#generic').attr("waiting", parseInt($('#generic').attr("waiting"), 10) - 1);
3170			if ($('#generic').attr("waiting") == 0) {
3171				$('#generic').animate({ 'top': '-90px' }, 300);
3172			}
3173			document.getElementById('generic').innerHTML = '';
3174		}, duration * 1000);
3175	}
3176	a11yAnnounceToScreenReader(message);
3177}
3178
3179function error_cmsg(message, duration, loading_sign){
3180	const generic = document.getElementById('generic');
3181	if(generic){
3182		generic.style.backgroundColor = "#CD4A47";
3183		cmsg(message, duration, loading_sign);
3184		setTimeout(function() {
3185			generic.style.backgroundColor = null;
3186		}, duration * 1000 + 400);
3187	}
3188}
3189
3190function Left(str, n) {
3191	if (n <= 0)
3192		return "";
3193	else if (n > String(str).length)
3194		return str;
3195	else
3196		return String(str).substring(0, n);
3197}
3198function Right(str, n) {
3199	if (n <= 0)
3200		return "";
3201	else if (n > String(str).length)
3202		return str;
3203	else {
3204		var iLen = String(str).length;
3205		return String(str).substring(iLen, iLen - n);
3206	}
3207}
3208
3209function toggle_ecg(idDetail, idCaller, idFocus) {
3210	if (document.getElementById(idDetail)) {
3211		var style = document.getElementById(idDetail).style;
3212		if (style.display == "none") {
3213			style.display = "block";
3214			if (!isEmpty(idCaller)) {
3215				var elem = document.getElementById(idCaller);
3216				if (!isEmpty(elem)) {
3217					elem.setAttribute('aria-expanded', true);
3218				}
3219			}
3220			if (!isEmpty(idFocus)) {
3221				// console.log('> FIND idFocus=' + idFocus);
3222				var focusElem = document.getElementById(idFocus);
3223				if (!isEmpty(focusElem)) {
3224					// console.log('> SET focus');
3225					focusElem.focus();
3226				}
3227			}
3228			$('#' + idDetail + '_arrow').attr('aria-expanded', 'true');
3229			$('#' + idDetail + '_text').attr('aria-expanded', 'true');
3230		} else {
3231			jQuery('#' + idDetail + '').fadeOut();
3232			if (!isEmpty(idCaller)) {
3233				var elem = document.getElementById(idCaller);
3234				if (!isEmpty(elem)) {
3235					elem.setAttribute('aria-expanded', false);
3236				}
3237				if (!isEmpty(idFocus)) {
3238					// we've just toggled CLOSED so we'll re-focus back to the caller link
3239					var focusElem = document.getElementById(idCaller);
3240					if (!isEmpty(focusElem)) {
3241						focusElem.focus();
3242					}
3243				}
3244				$('#' + idDetail + '_arrow').attr('aria-expanded', 'false');
3245				$('#' + idDetail + '_text').attr('aria-expanded', 'false');
3246			}
3247		}
3248	}
3249}
3250
3251function toggle_ib_ecg(idDetail) {
3252	if (document.getElementById(idDetail)) {
3253		var style = document.getElementById(idDetail).style;
3254		if (style.display == "none") { style.display = "inline-block"; } else { jQuery('#' + idDetail + '').fadeOut(); }
3255	}
3256}
3257
3258function toggle_i_ecg(idDetail) {
3259    if (document.getElementById(idDetail)) {
3260        var style = document.getElementById(idDetail).style;
3261        if (style.display == "none") { style.display = "inline"; } else { jQuery('#' + idDetail + '').fadeOut(); }
3262    }
3263}
3264
3265function toggle_row(idDetail) {
3266	var style = document.getElementById(idDetail).style;
3267	style.display = (style.display == "none") ? "table-row" : "none";
3268}
3269
3270function numbersOnly(e) {
3271	var unicode = e.charCode ? e.charCode : e.keyCode
3272	if (unicode != 8) { //if the key isn't the backspace key (which we should allow)
3273		if (unicode < 48 || unicode > 57) //if not a number
3274			return false //disable key press
3275	}
3276}
3277
3278function changeClassBtn(intId, strTo) {
3279    var studentId = intId.replace("connect_btn_", "");
3280
3281	if (document.getElementById(intId)) {
3282		if (strTo == "connect") {
3283			cmsg('Connection request sent.', 2);
3284			document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','pending');getContent('connection_sent', 'ajax_connect?disconnect=1&student_id=" + studentId + "');\" class='btn btn-cg--user btn--full-width' style='border-radius: 0 0 4px 4px;'>Pending Connection <span class='mdi mdi-close-circle'></span></a>";
3285		}
3286		// Used for connections
3287		//else if (strTo == "pending") {
3288		//	cmsg('Connection request cancelled.', 2);
3289		//	$('#' + intId).removeClass('row');
3290		//	document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','connect');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "');\" class=\"btn btn-success btn--full-width\" style='border-radius: 0 0 4px 4px;'><span class='mdi mdi-plus-circle'></span> Connect</a>";
3291		//}
3292		else if (strTo == "connected") {
3293			cmsg('Connection accepted.', 2);
3294			$('#' + intId).removeClass('row');
3295			document.getElementById(intId).innerHTML = "<a class=\"btn btn-cg--group btn--full-width\" href=\"/user_chat?sid=" + studentId + "\" style=\"border-radius: 0 0 4px 4px;\"><span class=\"mdi mdi-comment-text\"></span> Message</a>";
3296		}
3297		else if (strTo == "follow") {
3298		    $('#' + intId).removeClass('row');
3299		    document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','following');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-grey btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi-check-circle'></span> Connected</a>";
3300		}
3301		else if (strTo == "following") {
3302		    $('#' + intId).removeClass('row');
3303		    document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','follow');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&follow=1');\" class=\"btn btn-default btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi mdi-check-circle-outline'></span> Connect</a>";
3304		}
3305		else if (strTo == "pending") {
3306		    $('#' + intId).removeClass('row');
3307		    document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','request');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-cg--user btn--full-width\" style='border-radius: 0 0 4px 0;'>Pending <span class='mdi mdi-close-circle'></span></a>";
3308		}
3309		else if (strTo == "request") {
3310		    $('#' + intId).removeClass('row');
3311		    document.getElementById(intId).innerHTML = "<a onclick=\"changeClassBtn('" + intId + "','pending');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&request=1');\" class=\"btn btn-default btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi mdi-check-circle-outline'></span> Connect</a>";
3312		}
3313	} if (document.getElementById("ajax_profile_" + intId)) {
3314		if (strTo == "follow_ajax_profile") {
3315		    $('#' + intId).removeClass('row');
3316		    document.getElementById("ajax_profile_" + intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','following_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-cg--user btn--rounded btn--margin-right\"><span class='mdi mdi-check-circle'></span> Connected</button>";
3317		    if (window.parent.document.getElementById(intId)) window.parent.document.getElementById(intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','following_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-grey btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi-check-circle'></span> Connected </button>";
3318		}
3319		else if (strTo == "following_ajax_profile") {
3320		    $('#' + intId).removeClass('row');
3321		    document.getElementById("ajax_profile_" + intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','follow_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&follow=1');\" class=\"btn btn-cg--user btn--rounded btn--margin-right\"><span class='mdi mdi mdi-check-circle-outline'></span> Connect</button>";
3322		    if (window.parent.document.getElementById(intId)) window.parent.document.getElementById(intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','follow_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&follow=1');\" class=\"btn btn-default btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi mdi-check-circle-outline'></span> Connect </button>";
3323		}
3324		else if (strTo == "pending_ajax_profile") {
3325		    $('#' + intId).removeClass('row');
3326		    document.getElementById("ajax_profile_" + intId).innerHTML = "<button type=\"button\" aria-label=\"cancel pending connection request\" onclick=\"changeClassBtn('" + intId + "','follow_request_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-cg--user btn--rounded btn--margin-right\"><span class='mdi mdi-close-circle'></span> Pending</button>";
3327		    if (window.parent.document.getElementById(intId)) window.parent.document.getElementById(intId).innerHTML = "<button type=\"button\" aria-label=\"cancel pending connection request\" onclick=\"changeClassBtn('" + intId + "','request');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&disconnect=1');\" class=\"btn btn-cg--user btn--full-width\" style='border-radius: 0 0 4px 0;'>Pending <span class='mdi mdi-close-circle'></span></button>";
3328		}
3329		else if (strTo == "follow_request_ajax_profile") {
3330		    $('#' + intId).removeClass('row');
3331		    document.getElementById("ajax_profile_" + intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','pending_ajax_profile');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&request=1');\" class=\"btn btn-success btn--rounded btn--margin-right\"><span class='mdi mdi mdi-check-circle-outline'></span> Connect</button>";
3332		    if (window.parent.document.getElementById(intId)) window.parent.document.getElementById(intId).innerHTML = "<button type=\"button\" onclick=\"changeClassBtn('" + intId + "','follow');getContent('connection_sent', 'ajax_connect?student_id=" + studentId + "&request=1');\" class=\"btn btn-default btn--full-width\" style='border-radius: 0 0 4px 0;'><span class='mdi mdi mdi-check-circle-outline'></span> Connect</button>";
3333		}
3334	}
3335}
3336
3337//show mouse over popup and send request to server to get detail for event
3338function ShowEventDetail(obj) {
3339
3340	var showOn_ = $(obj).attr('showOn');
3341	var flickrFeedsCache = {};
3342	$(obj).poshytip({
3343		className: 'tip-darkgray',
3344		//offsetX: -350,
3345		//offsetY: 0,
3346		//alignX: 'left',
3347		showOn: showOn_,
3348		//alignTo: 'target',
3349		//alignTo: 'cursor',
3350		//offsetX: -80,
3351		//alignY: 'bottom',
3352		content: function (updateCallback) {
3353			var rel = $(this).attr('rel');
3354			if (flickrFeedsCache[rel] && flickrFeedsCache[rel].container)
3355				return flickrFeedsCache[rel].container;
3356			if (!flickrFeedsCache[rel]) {
3357				flickrFeedsCache[rel] = { container: null };
3358				var eventId = rel;
3359				$.getJSON('/mobile_ws/v17/mobile_calendar?calendarView=detail&id=' + eventId,
3360					function (data) {
3361						var container = $('<div/>').addClass('flickr-thumbs');
3362						$('<div/>')
3363							.append(GetEventTipHTML(data))
3364							.appendTo(container)
3365						// store the content in the cache
3366						// and call updateCallback() to update the content in the main tooltip
3367						updateCallback(flickrFeedsCache[rel].container = container);
3368						var obj = $(".flickr-thumbs").parent().parent();
3369						$(obj).attr('style', 'background-color:white !important');
3370						tb_init('a.thickbox');
3371					}
3372				);
3373			}
3374			return 'Loading detail...';
3375		}
3376	});
3377	tb_init('a.thickbox');
3378}
3379
3380//get popup html
3381function GetEventTipHTML(data) {
3382	if (data.events.length > 0) {
3383		data = data.events[0];
3384		var title = data.title != null ? data.title : "";
3385		var eventAddress = data.event_address != null ? data.event_address : "";
3386		var eventZipCode = data.event_zipcode != null ? data.event_zipcode : "";
3387		var eventCity = data.event_city != null ? data.event_city : "";
3388		var eventState = data.event_state != null ? data.event_state : "";
3389		var eventLocation = data.event_location != null ? data.event_location : "";
3390		var eventAuthor = data.author_id != null ? data.author_id : "";
3391		var strEventDate = data.eventDate;
3392		var eventDate = Date.parse(data.eventDateStr);
3393		var eventEndDate = Date.parse(data.eventEndDateStr);
3394		var endTime = data.endTime != null ? data.endTime : "";
3395		var startTime = data.startTime != null ? data.startTime : "";
3396		var groupName = data.groupName != null ? data.groupName : "";
3397		var soldTickets = data.soldTickets != null ? data.soldTickets : 0;
3398		var description = data.eventDescription != null ? data.eventDescription : "";
3399		var eventFlyer = data.eventFlyer != null ? data.eventFlyer : "";
3400		var rsvp = data.rsvpLinkCalendar != null ? data.rsvpLinkCalendar : "";
3401		var groupURL = data.groupURL != null ? data.groupURL : "";
3402		var clubLogo = data.clubLogo != null ? data.clubLogo : "";
3403		var eventUID = data.eventUID != null ? data.eventUID : "";
3404		var canEdit = data.canEdit != null ? data.canEdit : false;
3405		var clubId = data.club_id != null ? data.club_id : "0";
3406		var favLink = data.favoriteLink != null ? data.favoriteLink : "";
3407		var registrationRequired = data.registrationRequired != null ? data.registrationRequired : "0";
3408		var strAddress = eventLocation;
3409		var stArray = [1, 21, 31];
3410		var ndArray = [2, 22];
3411		var rdArray = [3, 23];
3412		var thArray = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28, 29, 30];
3413		var eventDate_ = eventDate.getDate();
3414		var eventEndDate_ = eventEndDate.getDate();
3415		var eventDateSuffix;
3416		var eventEndDateSuffix;
3417
3418		if (stArray.indexOf(eventDate_) > -1) {
3419			eventDateSuffix = "st ";
3420		}
3421		else if (ndArray.indexOf(eventDate_) > -1) {
3422			eventDateSuffix = "nd ";
3423		}
3424		else if (rdArray.indexOf(eventDate_) > -1) {
3425			eventDateSuffix = "rd ";
3426		}
3427		else if (thArray.indexOf(eventDate_) > -1) {
3428			eventDateSuffix = "th ";
3429		}
3430
3431
3432		if (stArray.indexOf(eventEndDate_) > -1) {
3433			eventEndDateSuffix = "st ";
3434		}
3435		else if (ndArray.indexOf(eventEndDate_) > -1) {
3436			eventEndDateSuffix = "nd ";
3437		}
3438		else if (rdArray.indexOf(eventEndDate_) > -1) {
3439			eventEndDateSuffix = "rd ";
3440		}
3441		else if (thArray.indexOf(eventEndDate_) > -1) {
3442			eventEndDateSuffix = "th ";
3443		}
3444
3445		/*
3446		if (eventAddress != "" && eventAddress != null) {
3447			strAddress = strAddress + ',&nbsp;' + eventAddress + ',';
3448			if (eventAddress != "") {
3449				strAddress = strAddress + ',&nbsp;' + eventZipCode + '&nbsp;' + eventCity + ',&nbsp;' + eventState;
3450			}
3451		}
3452		*/
3453		var isMultipleDaysEvent = false;
3454		if (eventEndDate > eventDate) {
3455			isMultipleDaysEvent = true;
3456		}
3457
3458		//Parent div
3459		html = "<div class='tipContent'>";
3460		//Event Name
3461		html += "<div style='float:left;width:100%'>";
3462
3463		//fav button
3464		if (favLink != "") {
3465			html += "<div style='width: 6%;float: left;margin-top: 6px;'>" + favLink + "</div>";
3466		}
3467
3468		html += "<div class='tipEventTitle'";
3469
3470
3471		if (canEdit == false) {
3472			if (favLink != "") {
3473				html += "style='width:94%'";
3474			}
3475			else {
3476				html += "style='width:100%'";
3477			}
3478		}
3479		else {
3480			if (favLink != "") {
3481				html += "style='width:79.5%'";
3482			}
3483		}
3484
3485		html += ">";
3486	html += title;
3487		html += "</div>";
3488		//Edit Event
3489		var link = "javascript:void(0);";
3490		var editButton = "";
3491		if (eventUID != "") {
3492			link = "/officer_login_redirect?club_id=" + clubId + "&target=" + encodeURIComponent("/event_form2.aspx?uid=" + eventUID);
3493		}
3494		if (canEdit) {
3495			editButton = '<a style="margin-top:4px;margin-right:1px;float:right;
3495" href="' + link + '" title="Edit event" class="cgb b_edit"><span></span></a>';
3496
3497			html += "<div style='float:right;width:14%'>";
3498			html += editButton;
3499			html += "</div>";
3500		}
3501
3502		html += "</div>";
3503
3504		html += "<div class='line_bottom' style='float: left;width: 100%;'></div>";
3505		//Location
3506		html += "<div class='tipEventAddress'><div class='tipLocation'><img class='ico' src='/images/icon_location.png'></div>";
3507		html += "<div class='tipEAdress'><a class='thickbox' href='/ajax_map?location=" + strAddress.replace(/ /gi, "+") + "&embed=1&TB_iframe=true&ax=1&mode=set&modal=true&width=900&height=500' style='text-decoration:none;'>" + strAddress + "</a></div></div>";//width:85%;
3508		//Time
3509		html += "<div class='tipEventDateTime'><div style='float:left;width:4.5%;'><img src='/images/icon_date_time.png' class='ico floatLeft'/></div>";
3510		html += "<div class='tipEventDateTimeContent'>"
3511		if (isMultipleDaysEvent == false) {
3512			html += "<div class='tipEventDateTime_'>" + eventDate.clone().toString("dddd MMMM dd") + eventDateSuffix + eventDate.clone().toString("yyyy") + "<br />";
3513			html += startTime + " &#8211; " + endTime + "</div>";
3514		}
3515		else {
3516			html += "<div class='tipEventDateTime_'>" + eventDate.clone().toString("dddd MMMM dd") + eventDateSuffix + eventDate.clone().toString("yyyy") + " " + startTime + " &#8211; ";
3517			html += "<br />" + eventEndDate.clone().toString("dddd MMMM dd") + eventEndDateSuffix + eventEndDate.clone().toString("yyyy") + " " + endTime + "</div>";
3518		}
3519
3520		if (parseInt(registrationRequired) > 0) {
3521			html += "<div class='tipRSVPContent'>"
3522			html += "<a class='btns btnEvents RSVPTipLink' href='" + rsvp + "'>RSVP</a></div>";
3523		}
3524		/////
3525		html += "</div>";
3526		html += "</div>";
3527		//Attendings
3528		if (data.attendees.length > 0) {
3529			html += "<div class='tipEventAttending'>ATTENDING</div>";
3530			html += "<div class='tipPhotosContent'>";// background:1px Blue;
3531			//Images inside
3532			if (data.attendees.length > 0) {
3533				for (var i = 0; i < data.attendees.length; i++) {
3534					var photoSrc = data.attendees[i].attendeesPhotosSrc;
3535					var attendeeUid = data.attendees[i].attendee_uid;
3536					html += "<div class='tipPhotos'>";
3537					html += "<a style='text-decoration:none;' target='_blank' href='/student_profile?uid=" + attendeeUid + "'><img style='width:100%;' alt='Coming' src='" + photoSrc + "' /></a>";
3538					html += "</div>";
3539				}
3540			}
3541			html += "</div>";
3542		}
3543		html += "<div class='line_bottom' style='float: left;width: 100%;'></div>";
3544		html += "<div style='width:100%;float:left;'>";
3545		if (clubLogo != "") {
3546			html += "<div class='floatLeft'><img class='logoImg' src='" + clubLogo + "' alt='logo' /></div>";
3547		}
3548		html += "<span class='tipHostedBy'>&nbsp;Hosted by:</span>";
3549		html += "<span class='tipEventClubName'><a href='" + groupURL + "' class='tipClubLnk' target='_blank'>" + groupName;
3550		if (eventAuthor != "") {
3551			html += " - " + eventAuthor;
3552		}
3553		html += "</a></span>";
3554
3555		if (data.coHostClubList.length > 0) {
3556			html += "<span class='tipHostedBy'>&nbsp;Cohost by:</span>";
3557			html += "<span class='tipEventClubName'>";
3558			for (var i = 0; i < data.coHostClubList.length; i++) {
3559				html += "<a href='" + data.coHostClubList[i].url + "' class='tipClubLnk' target='_blank'>" + data.coHostClubList[i].clubName + "</a>, ";
3560			}
3561			html = html.substr(0, html.length - 2);
3562			html += "</span>";
3563		}
3564
3565		html += "</div>";
3566		html += "<div class='line_bottom' style='float: left;width: 100%;margin-bottom :10px;'></div>";
3567		if (eventFlyer != "") {
3568			html += "<div class='flyerContent'><img src='" + eventFlyer + "' alt='Flyer' /></div>";
3569		}
3570		//Close parent div
3571		html += "</div>";
3572	}
3573	else {
3574		 html="<div>This event information is private.</div>";
3575	}
3576	return html;
3577}
3578
3579/* Function used to add a subscription to a member.
3580* @param member_id : The student id
3581* @param subscription_id : The subscription id
3582* @param content_id : The id of the container for the getContent
3583*/
3584function updateMemberSubscription(member_id, subscription_id, content_id) {
3585	jQuery.ajax({
3586		type: "POST",
3587		cache: false,
3588		url: "/add_member_subscription",
3589		data: { member_id: member_id, subscription_id: subscription_id },
3590		success: function () {
3591			getContent(content_id, 'ajax_member_subscription?member_id=' + member_id + '&subscription_id=' + subscription_id);
3592		}
3593	});
3594}
3595
3596
3597
3598/* Function used to send JS notifications.
3599* @param notif_name : The name of the notification to send
3600* @param record_ids : The record ids
3601* @param sec_record_ids : The second record ids (optional)
3602* @param to_ids : Ids of user who will receive notification
3603* @param message : Message to send (optional)
3604* @param club_id : The club id (optional)
3605*/
3606function sendNotification(notif_name, record_ids, sec_record_ids, to_ids, message, club_id) {
3607	jQuery.ajax({
3608		type: "POST",
3609		cache: false,
3610		url: "/ajax_send_notification",
3611		data: { notif_name: notif_name, record_ids: record_ids, sec_record_ids: sec_record_ids, to_ids: to_ids, message: message, club_id: club_id},
3612		success: function () {
3613			cmsg("Notification sent", 1, 0);
3614		}
3615	});
3616
3617}
3618
3619function hex (c) {
3620	var s = "0123456789abcdef";
3621	var i = parseInt (c);
3622	if (i == 0 || isNaN (c))
3623	  return "00";
3624	i = Math.round (Math.min (Math.max (0, i), 255));
3625	return s.charAt ((i - i % 16) / 16) + s.charAt (i % 16);
3626  }
3627
3628  /* Convert an RGB triplet to a hex string */
3629  function convertToHex (rgb) {
3630	return hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
3631  }
3632
3633  /* Remove '#' in color hex string */
3634  function trimHex (s) { return (s.charAt(0) == '#') ? s.substring(1, 7) : s }
3635
3636  /* Convert a hex string to an RGB triplet */
3637  function convertToRGB (hex) {
3638	var color = [];
3639	color[0] = parseInt ((trimHex(hex)).substring (0, 2), 16);
3640	color[1] = parseInt ((trimHex(hex)).substring (2, 4), 16);
3641	color[2] = parseInt ((trimHex(hex)).substring (4, 6), 16);
3642	return color;
3643  }
3644
3645  function generateColor(colorStart,colorEnd,colorCount){
3646
3647	  // The beginning of your gradient
3648	  var start 
3649	  if (colorStart != '') {
3650		  start = convertToRGB (colorStart);    
3651	  }
3652	  else {
3653		  start = convertToRGB (colorEnd);
3654		  start[0] = start[0] * 3;
3655		  start[1] = start[1] * 3;
3656		  start[2] = start[2] * 3;
3657	  }
3658
3659	  // The end of your gradient
3660	  var end   = convertToRGB (colorEnd);    
3661
3662	  // The number of colors to compute
3663	  var len = colorCount;
3664
3665	  //Alpha blending amount
3666	  var alpha = 0.0;
3667
3668	  var colorArrayTemp = [];
3669	  
3670	  for (i = 0; i < len; i++) {
3671		  var c = [];
3672		  alpha += (1.0/len);
3673		  
3674		  c[0] = start[0] * alpha + (1 - alpha) * end[0];
3675		  c[1] = start[1] * alpha + (1 - alpha) * end[1];
3676		  c[2] = start[2] * alpha + (1 - alpha) * end[2];
3677
3678		  colorArrayTemp.push(convertToHex (c));
3679		  
3680	  }
3681	  return colorArrayTemp;
3682  }
3683
3684function getUserLocation(callback) {
3685	if (navigator.geolocation) {
3686		navigator.geolocation.getCurrentPosition(callback);
3687	} else { 
3688		alert("Geolocation is not supported by this browser.");
3689	}
3690}
3691
3692/* Function used to reset the Demo platfrom.
3693*/
3694function resetDemo () {
3695	jQuery.ajax({
3696		type: "POST",
3697		cache: false,
3698		url: "/reset_demo.aspx",
3699		data: {},
3700		success: function () {
3701			cmsg("Demo reset", 1, 0);
3702		}
3703	});
3704
3705}
3706
3707/*-----------------------------------------------------*\
3708	@UTILITIES
3709\*-----------------------------------------------------*/
3710 function windowLocationReload() {
3711    window.location.reload();
3712 }
3713
3714// --------------------------------------------------------------------------------
3715// Helper Functions
3716// --------------------------------------------------------------------------------
3717
3718var newlineChar = '\n';
3719
3720// --------------------------------------------------------------------------------
3721// async sleep - ex. await cgSleep(1000) to sleep for one second *** NO LONGER USED
3722// --------------------------------------------------------------------------------
3723function cgSleep(millis) {
3724	console.error('> SLEEP cgSleep(' + millis + ') no longer supported.');
3725	// return new Promise(resolve => setTimeout(resolve, millis));
3726}
3727
3728// --------------------------------------------------------------------------------
3729// data validation and type checking
3730// --------------------------------------------------------------------------------
3731function isBigInt(x) {
3732	return typeof x === 'bigint';
3733}
3734
3735function isBoolean(x) {
3736	return typeof x === 'boolean';
3737}
3738
3739function isDate(x) {
3740	if (isEmpty(x)) {
3741		return false;
3742	} else if (!isObject(x)) {
3743		return false;
3744	} else if (isEmpty(x.now)) {
3745		return false;
3746	} else if (isEmpty(x.getTime)) {
3747		return false;
3748	} else if (isEmpty(x.getYear)) {
3749		return false;
3750	} else if (isEmpty(x.getMonth)) {
3751		return false;
3752	} else if (isEmpty(x.UTC)) {
3753		return false;
3754	}
3755	return true;
3756}
3757
3758function isEmpty(x) {
3759
3760	if (typeof x === 'undefined') {
3761		return true;
3762	} else if (x === null) {
3763		return true;
3764	} else if (typeof x === 'string' && x.trim().length < 1) {
3765		return true;
3766	}
3767	return false;
3768}
3769
3770function isFunction(x) {
3771	return typeof x === 'function';
3772}
3773
3774// console.error('> LOAD-TRACE scripts.js is loading...');
3775
3776var ulcHexCharacters = '0123456789abcdefABCDEF';
3777function isHexCharacter(c) {
3778	if (isEmpty(c)) {
3779		return false;
3780	} else if (!isString(c)) {
3781		return false;
3782	}
3783
3784	if (!ulcHexCharacters.includes(c)) {
3785		return false;
3786	}
3787	return true;
3788}
3789function isHexString(s) {
3790	if (isEmpty(s)) {
3791		return false;
3792	} else if (!isString(s)) {
3793		return false;
3794	}
3795
3796	for (var i = 0; i < s.length; i++) {
3797		if (!isHexCharacter(s.charAt(i))) {
3798			return false;
3799		}
3800	}
3801
3802	return true;
3803}
3804
3805function isNull(x) {
3806	if (isUndefined(x)) {
3807		console.error('x is undefined');
3808		return;
3809	}
3810	return x === null;
3811}
3812
3813function isNumber(x) {
3814	return typeof x === 'number' && !isNaN(x);
3815}
3816
3817function isNumberBoolean(x) {
3818	// return true if x is a number and is either zero or one
3819	if (!isNumber(x)) {
3820		return false;
3821	}
3822	return x === 0 || x === 1;
3823}
3824
3825function isObject(x) {
3826	return typeof x === 'object';
3827}
3828
3829function isPrimitive(x) {
3830	//
3831	// return true if x is a JavaScript primitive data type
3832	//
3833	if (isBigInt(x)) {
3834		return true;
3835	} else if (isBoolean(x)) {
3836		return true;
3837	} else if (isFunction(x)) {
3838		// functions are treated as primitives in this implementation
3839		return true;
3840	} else if (isNumber(x)) {
3841		return true;
3842	} else if (isString(x)) {
3843		return true;
3844	} else if (isSymbol(x)) {
3845		return true;
3846	} else if (isUndefined(x)) {
3847		return true;
3848	}
3849
3850	return false;
3851}
3852
3853function isString(x) {
3854	return typeof x === 'string';
3855}
3856
3857function isStringBoolean(x) {
3858	// return true if x is a string with value 'true' or 'false' (after trim() and toLowerCase() applied)
3859	if (!isString(x)) {
3860		return false;
3861	}
3862	x = x.trim().toLowerCase();
3863	return x === 'true' || x === 'false';
3864}
3865
3866function isSymbol(x) {
3867	return typeof x === 'symbol';
3868}
3869
3870function isUndefined(x) {
3871	return typeof x === 'undefined';
3872}
3873
3874function isUUID(uuidString) {
3875	// a hack implementation but usable
3876	if (isEmpty(uuidString)) {
3877		return false;
3878	} else if (!isString(uuidString)) {
3879		return false;
3880	} else if (uuidString.length !== 36) {
3881		return false;
3882	} else if (uuidString.charAt(8) !== '-') {
3883		return false;
3884	} else if (uuidString.charAt(13) !== '-') {
3885		return false;
3886	} else if (uuidString.charAt(18) !== '-') {
3887		return false;
3888	} else if (uuidString.charAt(23) !== '-') {
3889		return false;
3890	} else {
3891		uuidString = uuidString.trim().toLowerCase().replaceAll('-', '');
3892		if (uuidString.length != 32) {
3893			return false;
3894		}
3895		if (!isHexString(uuidString)) {
3896			return false;
3897		}
3898	}
3899	return true;
3900}
3901
3902// --------------------------------------------------------------------------------
3903// quickly toggle a boolean value represented in various ways (safe toggle to preserve type)
3904// --------------------------------------------------------------------------------
3905function toggleBoolean(x) {
3906	if (isEmpty(x)) {
3907		console.error('x is empty');
3908		return;
3909	} else if (isBoolean(x)) {
3910		return !x;
3911	} else if (isStringBoolean(x)) {
3912		return (x === 'false') ? 'true' : 'false';
3913	} else if (isNumberBoolean(x)) {
3914		return (x === 0) ? 1 : 0;
3915	} else {
3916		console.error('typeof x is unsupported');
3917	}
3918}
3919
3920// -------------------------------------------------------------------------------------------
3921// GET safe string description of any variable (used for debug console output)
3922// -------------------------------------------------------------------------------------------
3923function getIndent(indentCount) {
3924	var indent = '';
3925	for (var count = 0; count < indentCount; count++) {
3926		indent += ' ';
3927	}
3928	return indent;
3929}
3930
3931function getDescription(x, indentCount) {
3932	var indentCountMax = 8;
3933
3934	if (isEmpty(indentCount)) {
3935		indentCount = 0;
3936	}
3937
3938	if (isUndefined(x)) {
3939		return '{undefined}';
3940	} else if (isNull(x)) {
3941		return '{null}';
3942	} else if (isUUID(x)) {
3943		return '{UUID}' + x.toString();
3944	} else if (typeof x === 'string') {
3945		return '{string}' + x.toString();
3946	} else if (typeof x === 'number') {
3947		return '{number}' + x.toString();
3948	} else if (typeof x === 'boolean') {
3949		return '{boolean}' + x.toString();
3950	} else if (typeof x === 'symbol') {
3951		return '{symbol}' + x.toString();
3952	} else if (typeof x === 'function') {
3953		return '{function}' + x.toString();
3954	} else if (isDate(x)) {
3955		return '{date}' + x.toString();
3956	} else if (typeof x === 'object') {
3957		if (!isEmpty(x.length)) {
3958			var description = '{object,length=' + x.length + '}' + newlineChar;
3959			indentCount += 2;
3960			if (indentCount > indentCountMax) {
3961				description += getIndent(indentCount) + 'index[' + index + ']={more...;indentCount>indentCountMax}' + newlineChar;
3962			} else {
3963				for (var index = 0; index < x.length; index++) {
3964					description += getIndent(indentCount) + 'index[' + index + ']=' + getDescription(x[index], indentCount) + newlineChar;
3965				}
3966				return description.trim();
3967			}
3968		} else if (getPropertyCount(x) > 0) {
3969			var description = '{object,propertyCount=' + getPropertyCount(x) + '}' + newlineChar;
3970			indentCount += 2;
3971			if (indentCount > indentCountMax) {
3972				description += getIndent(indentCount) + 'index[' + index + ']={more...;indentCount>indentCountMax}' + newlineChar;
3973			} else {
3974				for (var property in x) {
3975					description += getIndent(indentCount) + 'property[' + property + ']=' + getDescription(x[property], indentCount) + newlineChar;
3976				}
3977				return description.trim();
3978			}
3979		}
3980		return '{object}' + x.toString();
3981	} else {
3982		var unknownType = typeof x;
3983		return '{unknown-type[' + unknownType + ']}' + x.toString();
3984	}
3985}
3986
3987// -------------------------------------------------------------------------------------------
3988// test if an element is a jQuery element
3989// -------------------------------------------------------------------------------------------
3990function isJQueryElement(x) {
3991	if (isEmpty(x)) {
3992		return false;
3993	} else if (!isEmpty(x.jquery)) {
3994		return true;
3995	}
3996	return false;
3997}
3998
3999// -------------------------------------------------------------------------------------------
4000// count the number of properties in an object
4001// -------------------------------------------------------------------------------------------
4002function getPropertyCount(x) {
4003	if (isEmpty(x)) {
4004		return 0;
4005	} else if (isPrimitive(x)) {
4006		return 0;
4007	}
4008
4009	var count = 0;
4010	for (k in x) {
4011		count++;
4012	}
4013
4014	return count;
4015}
4016
4017// --------------------------------------------------------------------------------
4018// skips to next section of the profile when triggering profile filling mode for matching
4019//
4020function goToProfileStep(dest, mode, studentUid, signedStudentUid, stepDirection) { 
4021	var destPath;
4022
4023	switch(dest) {
4024		case "matching_home":
4025		case "matching_home_from_photo":
4026			destPath = 'profile2?stay=1';
4027			if (dest == "matching_home_from_photo") {
4028				destPath += '&profile_photo=1';
4029			}
4030			break;
4031		case "profile_photo":
4032			destPath = 'ajax_profile?uid='+studentUid+'&embed=1&profile_photo=1';
4033			break;
4034		case "interests":
4035		case "interests_from_photo":
4036			destPath = 'student_interests_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4037			if (dest == "interests_from_photo") {
4038				destPath += '&profile_photo=1';
4039			}
4040			break;
4041		case "looking_for":
4042			destPath = 'student_looking_for_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4043			break;
4044		case "bio":
4045			destPath = 'student_bio_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4046			break;
4047		case "sports":
4048			destPath = 'student_sports_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4049			break;
4050		case "career_interests":
4051			destPath = 'student_industry_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4052			break;
4053		case "resume_education":
4054			destPath = 'student_academic_entry?ax=1&async=1&ajax_profile=1&visible=1';
4055			break;
4056		case "resume_work":
4057			destPath = 'student_work_entry?ax=1&async=1&ajax_profile=1&visible=1';
4058			break;
4059		case "languages":
4060			destPath = 'student_languages_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4061			break;
4062		case "social_links":
4063			destPath = 'student_social_links_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4064			break;
4065		case "twitter":
4066			destPath = 'student_twitter_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4067			break;
4068		case "linkedin":
4069			destPath = 'student_linkedin_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4070			break;
4071		case "facebook":
4072			destPath = 'student_facebook_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4073			break;
4074		case "instagram":
4075			destPath = 'student_instagram_entry?ax=1&async=1&uid='+studentUid+'&_s='+signedStudentUid;
4076			break;
4077		case "profile":
4078			destPath = 'profile?stay=1';
4079			break;
4080	}
4081
4082	if (mode == "transitionModal") {
4083		destPath = destPath + '&fill_mode=popup';
4084		transitionModal(destPath, 'lg');
4085	}
4086	else if (mode == "openModal") {
4087		destPath = destPath + '&fill_mode=popup';
4088		openModal(destPath, 'lg');
4089	}
4090	else if (mode == "getContent") {
4091		if (dest != "profile") {
4092			destPath = destPath + '&fill_mode=fullscreen';
4093			window.scrollTo({ top: 0, behavior: 'smooth' });
4094			if (dest != "profile_photo") {
4095				$("#profile_header_photo").hide();
4096			}
4097			getContentWithCallback('profile_container', destPath, goToProfileStepCallBack(dest), []);
4098			
4099			if (stepDirection == "next") {
4100				$(".progress_step_active").first().next().addClass("progress_step_next_active");
4101				$(".progress_step_active").first().removeClass("progress_step_active");
4102				$(".progress_step_next_active").first().removeClass("progress_step_next_active").addClass("progress_step_active");
4103			}
4104			else if (stepDirection == "back") {
4105				$(".progress_step_active").first().prev().addClass("progress_step_next_active");
4106				$(".progress_step_active").first().removeClass("progress_step_active");
4107				$(".progress_step_next_active").first().removeClass("progress_step_next_active").addClass("progress_step_active");
4108				if (dest == "matching_home") {
4109					$("#progress_steps").hide();
4110				}
4111			}
4112		}
4113		else {
4114			window.location = destPath;
4115		}
4116	}
4117}
4118
4119function goToProfileStepCallBack(dest) {
4120	if (dest == "profile_photo") {
4121		$("#profile_header_photo").show();
4122	}
4123} 
4124
4125// --------------------------------------------------------------------------------
4126// skips to next onboarding step
4127//
4128
4129
4130function goToOnboardingStep(stepId, strCurrentStepId, strOnboardingId, studentUid, signedStudentUid, redirectURl) { 
4131	var destPath;
4132
4133	if (strCurrentStepId != "" && stepId != "") {
4134		$("#slides").addClass("loading_step");
4135		$("#transition_loader").show();
4136	}
4137
4138	if (stepId != "") {
4139		destPath = 'onboarding?stay=1&ax=1&async=1&step_id='+stepId+'&uid='+studentUid+'&_s='+signedStudentUid;
4140		if (strCurrentStepId != "") {
4141			saveSettings('onboarding_' + strOnboardingId, strCurrentStepId);
4142		}
4143
4144		window.scrollTo({ top: 0, behavior: 'smooth' });
4145
4146		$.ajax({
4147			url: destPath,
4148			success: function (html) {
4149				$('#onboarding_wrapper').hide().html(html).fadeIn('slow');
4150				$("#slides").removeClass("loading_step");
4151				let state = { };
4152				let title = '';
4153				let myurl = '/' + destPath;
4154				//history.pushState(state, title, myurl);
4155			}
4156		});
4157	}
4158	else {
4159		$.ajax({
4160			url: "/settings_endpoint",
4161			data: 
4162			{ 
4163				name  : 'onboarding_' + strOnboardingId,
4164				value : 'complete',
4165				club  : ""
4166			},
4167			success: function () {
4168				window.location = redirectURl;
4169			}
4170		});
4171	}	
4172}
4173
4174// --------------------------------------------------------------------------------
4175// selectize options and renderer
4176//
4177
4178function getSelectizeRenderHTML(item, escape, wrapperId){
4179	var listItemAriaLabel = getAriaLabelForSelectizeStudents(wrapperId, escape(item.full_name), item.email ? escape(item.email) : '', escape(item.student_type), item.student_yog);
4180	var imageAltText = escape(item.full_name) + ' Profile Image';
4181	return '<div data-z="s04" aria-label="' + getSafeAriaLabelTextTrim(listItemAriaLabel) + '">' +
4182				'<img src="' + item.photo_url + '" alt="' + getSafeAriaLabelText(imageAltText) + '" aria-label="' + getSafeAriaLabelText(imageAltText) + '" />' +
4183				'<div style="display: inline-block; vertical-align: top; margin-left: 3px;">' +
4184					'<span style="font-weight: bold;">' + escape(item.full_name) + '</span>' +
4185					(item.email ? '<span> - ' + escape(item.email) + '</span>' : '') +
4186					'<br />' +
4187					'<span>' + item.student_type + item.student_yog + '</span>' +
4188				'</div>' +
4189			'</div>';
4190}
4191
4192function initSelectize(repoId, wrapperId, inputId, isSelectizeInputRequired, customUpdate){
4193
4194	$("#" + repoId).selectize({
4195		valueField: 'id',
4196		labelField: 'full_name',
4197		searchField: 'search_field',
4198		create: false,
4199		plugins: ['remove_button'],
4200		render: {
4201			option: function(item, escape){
4202				return getSelectizeRenderHTML(item, escape, wrapperId)
4203			}
4204		},
4205		load: function (query, callback) {
4206			if (!query.length) return callback();
4207			$.ajax({
4208				dataType: "json",
4209				url: '/mobile_ws/v18/mobile_student_select?search=' + encodeURIComponent(query) ,
4210				type: 'GET',
4211				error: function () {
4212					callback();
4213				},
4214				success: function (res) {
4215					var inputLabelText = 'Lookup users by name or email';
4216					var resultList = res.students.slice(0, 10);
4217					clearSelectizeResults(wrapperId);
4218					setSelectizeResultLength(wrapperId, resultList.length)
4219					callback(resultList);
4220					setAriaLabelForSelectizeDelayed(100, wrapperId, inputLabelText, true);
4221				}
4222			});
4223		},
4224		onDelete: function (values) {
4225			return true;
4226		}
4227	});
4228
4229	var selectizeInputAriaLabel = 'Lookup users by name or email. Select users.';
4230	var new_student_id = ""
4231	var $wrapper = $("#" + wrapperId);
4232	$('select.selectized,input.selectized', $wrapper).each(function () {
4233		var $input = $(this);
4234		var update = function (e) {
4235			let selectedUsers = '';
4236			let updatedAriaLabel = selectizeInputAriaLabel + " ";
4237
4238			if (isSelectizeInputRequired ? true : false) {
4239            	updatedAriaLabel += ' Required.';
4240        	}
4241
4242			selectedUsers = $('#' + wrapperId + ' div.items div.item').map(function () {
4243				return $(this).text().trim();
4244			}).get().join(',');
4245			if (!selectedUsers) {
4246				selectedUsers = 'none';
4247			}
4248
4249			updatedAriaLabel += ' ' + selectedUsers + ' selected. Start typing and wait for suggestions.';
4250
4251			var selectizeInputId = '#' + wrapperId + ' input:not([type="radio"])';
4252			var selectizeInput = $(selectizeInputId);
4253			selectizeInput.attr('aria-label', getSafeAriaLabelTextTrim(updatedAriaLabel));
4254
4255			if ($input.val() !== null) {
4256				new_student_id = $input.val().toString();
4257				if (customUpdate != undefined) customUpdate(new_student_id);
4258				else $('#' + inputId).val(new_student_id).trigger('change');
4259			}
4260		}
4261		$(this).on('change', update);
4262		update();
4263	});
4264	
4265	setupSelectizeInput(wrapperId, selectizeInputAriaLabel, isSelectizeInputRequired ? true : false);
4266
4267	var $select = $(document.getElementById(repoId)).selectize();
4268	var selectize = $select[0].selectize;
4269	return selectize;
4270}

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.