PageSourceSearch

https://www.roundhillfishkill.com/js/ng-controllers/main.js?v=2.1.1.30179

js roundhillfishkill.com collected 2026-09-26 05:00:32 UTC 44,941 bytes, 1,632 lines download raw bytes

1var myApp = angular.module('BungeeApp', ['ngRoute', 'ngSanitize', 'ui.validate', 'ngAnimate', 'cgNotify', 'blockUI', 'zxcvbn', 'datatables', 'ui.bootstrap',
2	'lr.upload', 'dndLists', 'ui.mask', 'ngTagsInput']);
3
4myApp
5	.controller('MainController', function ($scope, $http, $location, $route, $rootScope, $window, $filter, $timeout, $uibModal, notify, AppConstants, $sce) {
6		$rootScope.pageData = {
7			title: '',
8			menu: null,
9			showMobileMenu: false,
10			header: '',
11		};
12
13		$rootScope.AppConstants = AppConstants;
14		$rootScope.billingChoices = { planOption: null, residenceCount: null, termLength: null };
15
16        $scope.bodyclass = '';
17
18		$rootScope.setPage = function (t, m, h) {
19			$rootScope.pageData.title = t;
20			$rootScope.pageData.header = h || t;
21			if (m) {
22				$rootScope.pageData.menu = m;
23            }
24		};
25
26		$rootScope.hasBillingChoices = false;
27
28		$rootScope.enableTooltips = function () {
29			jQuery('[data-toggle="popover"]').each(function () {
30				if (jQuery(this).attr('title').length) {
31					jQuery(this).popover({
32						placement: 'top',
33						html: true,
34						trigger: 'hover'
35					});
36				}
37			});
38			/*
39			jQuery("[data-toggle='tooltip']").each(function () {
40				if (jQuery(this).attr('title').length) {
41					jQuery(this).tooltip({
42						trigger: 'hover',
43						html: true,
44
45					});
46				}
47			});*/
48
49			jQuery("[rel='tooltip']").each(function () {
50				if (jQuery(this).attr('title').length) {
51					jQuery(this).tooltip({
52						trigger: 'hover',
53						html: true,
54
55					});
56				}
57			});
58
59			jQuery('.freshdeskTooltip').each(function () {
60				if (jQuery(this).attr('title') != null) {
61					if (jQuery(this).attr('title').length) {
62						jQuery(this).popover({
63							placement: 'top',
64							html: true,
65						});
66					}
67                }
68			});
69		}
70
71		$rootScope.barsToCommas = function (input) {
72			return input.replace('|', ',');
73		}
74
75		$rootScope.getBillingChoices = function () {
76			$rootScope.ajaxCall('/Customer/GetBillingChoices', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
77				if (data != null) {
78					$rootScope.billingChoices = data;
79					$rootScope.hasBillingChoices = true;
80				}
81			});
82		}
83
84		$rootScope.enableTooltipClose = function () {
85			jQuery(document).on("click", ".popover .tooltipClose", function () {
86				jQuery(this).parents(".popover").popover('hide');
87			});
88
89			jQuery(".popover").click(function (e) {
90				e.stopPropagation();
91			});
92			jQuery(".freshdeskTooltip").click(function (e) {
93				e.stopPropagation();
94			});
95
96			jQuery('body').unbind('click').click(function () {
97				jQuery(".popover").each(function () {
98					jQuery(this).popover('hide');
99				});
100			});
101		}
102
103		$rootScope.addCommas = function (x) {
104			if (!x) {
105				return x;
106			}
107
108			return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
109		}
110
111		$rootScope.replaceSpaces = function (x) {
112			if (!x) {
113				return x;
114			}
115
116			return x.toString().replace(/ /g, "-");
117		}
118
119		$rootScope.displayCurrentDate = function () {
120			var today = new Date();
121			var dd = String(today.getDate()).padStart(2, '0');
122			var mm = String(today.getMonth() + 1).padStart(2, '0');
123			var yyyy = today.getFullYear();
124
125			return mm + '/' + dd + '/' + yyyy;
126        }
127
128		$rootScope.stateList = [
129			{ abbr: 'AL', name: 'Alabama' },
130			{ abbr: 'AK', name: 'Alaska' },
131			{ abbr: 'AZ', name: 'Arizona' },
132			{ abbr: 'AR', name: 'Arkansas' },
133			{ abbr: 'CA', name: 'California' },
134			{ abbr: 'CO', name: 'Colorado' },
135			{ abbr: 'CT', name: 'Connecticut' },
136			{ abbr: 'DE', name: 'Delaware' },
137			{ abbr: 'DC', name: 'District Of Columbia' },
138			{ abbr: 'FL', name: 'Florida' },
139			{ abbr: 'GA', name: 'Georgia' },
140			{ abbr: 'HI', name: 'Hawaii' },
141			{ abbr: 'ID', name: 'Idaho' },
142			{ abbr: 'IL', name: 'Illinois' },
143			{ abbr: 'IN', name: 'Indiana' },
144			{ abbr: 'IA', name: 'Iowa' },
145			{ abbr: 'KS', name: 'Kansas' },
146			{ abbr: 'KY', name: 'Kentucky' },
147			{ abbr: 'LA', name: 'Louisiana' },
148			{ abbr: 'ME', name: 'Maine' },
149			{ abbr: 'MD', name: 'Maryland' },
150			{ abbr: 'MA', name: 'Massachusetts' },
151			{ abbr: 'MI', name: 'Michigan' },
152			{ abbr: 'MN', name: 'Minnesota' },
153			{ abbr: 'MS', name: 'Mississippi' },
154			{ abbr: 'MO', name: 'Missouri' },
155			{ abbr: 'MT', name: 'Montana' },
156			{ abbr: 'NE', name: 'Nebraska' },
157			{ abbr: 'NV', name: 'Nevada' },
158			{ abbr: 'NH', name: 'New Hampshire' },
159			{ abbr: 'NJ', name: 'New Jersey' },
160			{ abbr: 'NM', name: 'New Mexico' },
161			{ abbr: 'NY', name: 'New York' },
162			{ abbr: 'NC', name: 'North Carolina' },
163			{ abbr: 'ND', name: 'North Dakota' },
164			{ abbr: 'OH', name: 'Ohio' },
165			{ abbr: 'OK', name: 'Oklahoma' },
166			{ abbr: 'OR', name: 'Oregon' },
167			{ abbr: 'PA', name: 'Pennsylvania' },
168			{ abbr: 'RI', name: 'Rhode Island' },
169			{ abbr: 'SC', name: 'South Carolina' },
170			{ abbr: 'SD', name: 'South Dakota' },
171			{ abbr: 'TN', name: 'Tennessee' },
172			{ abbr: 'TX', name: 'Texas' },
173			{ abbr: 'UT', name: 'Utah' },
174			{ abbr: 'VT', name: 'Vermont' },
175			{ abbr: 'VA', name: 'Virginia' },
176			{ abbr: 'WA', name: 'Washington' },
177			{ abbr: 'WV', name: 'West Virginia' },
178			{ abbr: 'WI', name: 'Wisconsin' },
179			{ abbr: 'WY', name: 'Wyoming' },
180		];
181
182		$rootScope.countryCache = null;
183		$rootScope.getCountries = function () {
184			if ($rootScope.countries == null) {
185				$scope.ajaxCall('/Country/CountryList', {}, function (data) {
186					$rootScope.countryCache = data;
187					return data;
188				});
189			} else {
190				return $rootScope.countryCache;
191			}
192		}
193
194		$rootScope.preventPaste = function ($event) {
195			$event.preventDefault();
196			$event.stopPropagation();
197			return false;
198        }
199
200		$rootScope.ajaxCall = function (url, inputData, success, error, doLoading) {
201			doLoading = ((typeof doLoading !== 'undefined') ? doLoading : true);
202
203			url = '/data' + url;
204
205			return $http.post(url, JSON.stringify(inputData), { headers: { 'X-Do-Loading': doLoading ? 'true' : 'false' } }).then(function (response) {
206				if (status == 403 || status == 401) {
207					$window.location.href = '/Login?ReturnUrl=' + $window.location.pathname;
208					return;
209				}
210				if (response.data != null) {
211					if (response.data.draw != null) {
212						// assume data tables call
213						if (success != null) {
214							success(response.data);
215						}
216						return response.data;
217					} else if (response.data.success) {
218						if (success != null) {
219							success(response.data.results);
220						}
221						return response.data.results;
222					} else {
223						if (doLoading) {
224							$rootScope.errorMessage(response.data.message);
225						}
226
227						if (error != null) {
228							error(response.data);
229						}
230					}
231                } else if (error != null) {
232
233                    if (error == 'file') {
234                        $rootScope.errorMessage("The maximum file upload limit is 5MB");
235                    } else {
236                        error(response.data);
237                    }
238				}
239			}, function (response) {
240				if (response.status == 403 || response.status == 401) {
241					$window.location.href = '/Login?ReturnUrl=' + $window.location.pathname;
242					return;
243				}
244				if (error != null) {
245					error(response.data);
246				}
247			});
248		};
249
250
251		$rootScope.ajaxApiCall = function (url, inputData, success, error, doLoading) {
252			doLoading = ((typeof doLoading !== 'undefined') ? doLoading : true);
253
254			if (!url.includes('https://')) {
255				url = '/data' + url;
256            }
257
258			console.log("API Call");
259			return $http.post(url, JSON.stringify(inputData), { headers: { } }).then(function (response) {
260				if (status == 403 || status == 401) {
261					$window.location.href = '/Login?ReturnUrl=' + $window.location.pathname;
262					return;
263				}
264				if (response.data != null) {
265					if (response.data.message == 'Success') {
266						success(response.data);
267					} else {
268						if (doLoading) {
269							$rootScope.errorMessage(response.data.message);
270						}
271
272						if (error != null) {
273							error(response.data);
274						}
275					}
276				} else if (error != null) {
277					console.log("API Call 2");
278					if (error == 'file') {
279						$rootScope.errorMessage("The maximum file upload limit is 5MB");
280					} else {
281						error(response.data);
282					}
283				}
284			}, function (response) {
285				if (response.status == 403 || response.status == 401) {
286					$window.location.href = '/Login?ReturnUrl=' + $window.location.pathname;
287					return;
288				}
289				if (error != null) {
290					error(response.data);
291				}
292			});
293		};
294
295		$rootScope.ajaxCallNoLoading = function (url, inputData, success, error) {
296			return $rootScope.ajaxCall(url, inputData, success, error, false);
297		};
298
299		$rootScope.questionsHasAdminOnly = function (questions) {
300			for (var i = 0; i < questions.length; i++) {
301				if (questions[i].adminOnly) {
302					return true;
303                }
304			}
305			return false;
306        }
307
308		$rootScope.errorMessage = function (msg) {
309			notify({
310				message: msg,
311				classes: 'alert-danger',
312				duration: 10000,
313			});
314		};
315
316		$rootScope.successMessage = function (msg) {
317			notify({
318				message: msg,
319				classes: 'alert-success',
320				duration: 10000,
321			});
322		};
323
324		$rootScope.setUrl = function (url) {
325			window.location.href = url;
326		}
327
328		$rootScope.formatDateToString = function (date) {
329			return $filter('date')(date, 'MM/dd/yyyy');
330		}
331
332		$rootScope.formatDateTimeToString = function (date) {
333			return $filter('date')(date, 'MM/dd/yyyy h:mm:ss a');
334		}
335
336		$rootScope.formatTag = function (tag) {
337			if (!tag || !tag.length) {
338				return '';
339			}
340
341			return tag.replace(/[^a-zA-Z0-9\- ]+/g, '').toLowerCase();
342        }
343
344		$rootScope.confirm = function (msg, ok, cancel, yesText, noText) {
345			$uibModal.open({
346				templateUrl: 'modal-confirm-prompt',
347				controller: function ($scope, pInfo) {
348					$scope.pInfo = pInfo;
349
350					$scope.clickYes = function () {
351						if ($scope.pInfo.yesAction != null) {
352							$scope.pInfo.yesAction();
353						}
354						$scope.$close();
355					};
356
357					$scope.clickNo = function () {
358						if ($scope.pInfo.noAction != null) {
359							$scope.pInfo.noAction();
360						}
361						$scope.$close();
362					};
363				},
364				backdrop: 'static',
365				resolve: {
366					pInfo: function () {
367						return {
368							prompt: msg,
369							yesText: yesText == null ? 'Yes' : yesText,
370							noText: noText == null ? 'No' : noText,
371							yesAction: ok,
372							noAction: cancel
373						};
374					}
375				}
376			});
377		};
378
379		$scope.preLoad = true;
380		$scope.load = function () {
381			$timeout(function () {
382				$scope.preLoad = false;
383			}, 1000);
384		}
385
386        $rootScope.goback = function () {
387            history.back();
388        }
389
390		$scope.priceformatter = new Intl.NumberFormat('en-US', {
391			style: 'currency',
392			currency: 'USD',
393			minimumFractionDigits: 2
394		});
395
396		$rootScope.initFormDatePickerField = function () {
397			(function ($) {
398				console.log("Initializing the form date picker field");
399
400				var start = '.formDatePickerField';
401				$rootScope.initMobiscrollDatePicker('.formDatePickerField');
402			})(jQuery);
403		}
404
405		$rootScope.showPassword = false;
406		$rootScope.tempShowPassword = function () {
407			$rootScope.showPassword = !$rootScope.showPassword;
408			$timeout(function () {
409				$rootScope.showPassword = false;
410			}, 30000);
411		}
412
413		$rootScope.currentCustomerSite = null;
414
415		$rootScope.initMobiscrollTimePicker = function (element) {
416			mobiscroll.setOptions({
417				theme: 'ios',
418				themeVariant: 'light',
419			});
420
421			$timeout(function () {
422				$(element).mobiscroll().datepicker({
423					controls: ['time'],
424					select: 'date',
425					stepMinute: 5,
426					timeFormat: 'h:mmA',
427					touchUi: true,
428					anchor: $(element)[0],
429					display: 'bubble',
430				});
431			}, 10);
432		}
433
434		$rootScope.initMobiscrollTimePickerRange = function (start, end) {
435			mobiscroll.setOptions({
436				theme: 'ios',
437				themeVariant: 'light',
438			});
439
440			$timeout(function () {
441				var defaultStartTime = $(start).val();
442				var defaultEndTime = $(end).val();
443
444				console.log(defaultStartTime);
445				console.log(defaultEndTime);
446				var picker = $(start).mobiscroll().datepicker({
447					controls: ['time'],
448					select: 'range',
449					startInput: start,
450					endInput: end,
451					stepMinute: 5,
452					timeFormat: 'h:mmA',
453					touchUi: true,
454					anchor: $(start)[0],
455					display: 'bubble',
456				}).mobiscroll('getInst');
457
458				$timeout(function () {
459					picker.setVal([defaultStartTime, defaultEndTime]);
460				}, 50);
461			}, 500);
462		}
463
464		$rootScope.initMobiscrollDatePicker = function (start) {
465			console.log("Shouldn't be a range");
466			mobiscroll.setOptions({
467				theme: 'ios',
468				themeVariant: 'light',
469			});
470
471			$timeout(function () {
472				var defaultStartDate = $(start).val();
473
474				var picker = $(start).mobiscroll().datepicker({
475					controls: ['calendar'],
476					touchUi: true,
477					anchor: $(start)[0],
478					display: 'bubble',
479					select: 'date',
480				}).mobiscroll('getInst');
481
482				$timeout(function () {
483					picker.setVal([defaultStartDate]);
484				}, 50);
485			}, 100);
486		}
487		$rootScope.initMobiscrollDateTimePicker = function (start) {
488			mobiscroll.setOptions({
489				theme: 'ios',
490				themeVariant: 'light',
491			});
492
493			$timeout(function () {
494				var defaultStartDate = $(start).val();
495
496				var picker = $(start).mobiscroll().datepicker({
497					controls: ['calendar', 'time'],
498					touchUi: true,
499					anchor: $(start)[0],
500					display: 'bubble',
501					select: 'date',
502				}).mobiscroll('getInst');
503
504				$timeout(function () {
505					picker.setVal([defaultStartDate]);
506				}, 50);
507			}, 100);
508		}
509
510		$rootScope.initMobiscrollDatePickerRange = function (start, end) {
511			mobiscroll.setOptions({
512				theme: 'ios',
513				themeVariant: 'light',
514			});
515
516			$timeout(function () {
517				var defaultStartDate = $(start).val();
518				var defaultEndDate = $(end).val();
519
520				var picker = $(start).mobiscroll().datepicker({
521					anchor: $(start)[0],
522					display: 'bubble',
523					controls: ['calendar'],
524					select: 'range',
525					startInput: start,
526					endInput: end,
527					touchUi: true,
528				}).mobiscroll('getInst');
529
530				$timeout(function () {
531					picker.setVal([defaultStartDate, defaultEndDate]);
532				}, 50);
533			}, 500);
534		}
535
536		$rootScope.initMobiscrollDatePickerRangeOneInput = function (start) {
537			mobiscroll.setOptions({
538				theme: 'ios',
539				themeVariant: 'light',
540			});
541
542			$timeout(function () {
543				var defaultStartDate = $(start).val();
544
545				var picker = $(start).mobiscroll().datepicker({
546					anchor: $(start)[0],
547					display: 'bubble',
548					controls: ['calendar'],
549					select: 'range',
550					touchUi: true,
551				}).mobiscroll('getInst');
552
553				$timeout(function () {
554					picker.setVal([defaultStartDate]);
555				}, 50);
556			}, 500);
557		}
558
559		$rootScope.removeSite = function () {
560			sessionStorage.removeItem("bungee-sitecookie-site");
561		}
562		$rootScope.setSite = function (site) {
563            sessionStorage.setItem("bungee-sitecookie-site", site);
564		}
565
566		$rootScope.checkUrl = function(string) {
567			let givenURL;
568			try {
569				givenURL = new URL(string);
570			} catch (error) {
571				console.log("error is", error);
572				return false;
573			}
574			return true;
575		}
576
577
578		$rootScope.isAdmin = false;
579		$rootScope.isCommitteeLeader = false;
580
581		$rootScope.checkCommitteeLeader = function () {
582			if (!$rootScope.currentCustomerSite) {
583				return;
584			}
585
586			if ($rootScope.isAdmin) {
587				$rootScope.isCommitteeLeader = true;
588			}
589
590			$scope.ajaxCall('/Committee/CheckIsCommitteeLeader', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
591				$rootScope.isCommitteeLeader = data;
592
593			}, function () {
594
595			}, false);
596		}
597
598		$rootScope.getSite = function () {
599			var site = sessionStorage.getItem("bungee-sitecookie-site");
600			angular.forEach(AppConstants.CurrentUser.sites, function (lsite) {
601				if (lsite.customerSlug == site) {
602					$rootScope.isAdmin = lsite.isAdmin;
603					$rootScope.currentCustomerSite = lsite;
604                }
605			});
606
607			return site;
608		}
609
610		$rootScope.replies = null;
611		$rootScope.getPanelStepData = function () {
612			return new Promise(resolve => setTimeout(resolve, 500)).then(() => $rootScope.getPanelStepDataAfterWait());
613		}
614
615		$rootScope.getPanelStepDataAfterWait = function () {
616
617			var s = $rootScope.currentCustomerSite.customerId;
618			$rootScope.ajaxCall('/SupportStep/GetCustomerCurrentStep', { customerId: s }, function (data) {
619				$rootScope.replies = data.replies;
620			});
621		}
622
623		$rootScope.getTrialEndDate = function () {
624			var c = $rootScope.currentCustomerSite.customerId;
625			$rootScope.ajaxCall('/SupportStep/GetTrialEndDate', { customerId: c }, function (data) {
626				$rootScope.trialEndDate = data;
627			});
628        }
629
630		$rootScope.checkPlan = function () {
631			if (!$rootScope.currentCustomerSite) {
632				return false;
633			}
634
635			if (!$rootScope.currentCustomerSite.hasTrialOrPlan) {
636				return false;
637			}
638
639			if ($rootScope.currentCustomerSite.hasTrialOrPlan == true) {
640				return true;
641			}
642		}
643
644		$rootScope.checkSitePermission = function (permission) {
645			if (!$rootScope.currentCustomerSite) {
646				//console.log("No current customer site");
647				return false;
648			}
649
650			if (!$rootScope.currentCustomerSite.features) {
651				//console.log("No current customer site features");
652				return false;
653            }
654
655			if ($rootScope.currentCustomerSite.features.includes(permission)) {
656				return true;
657            }
658		}
659
660		$rootScope.checkSubAdminPermission = function (permission) {
661			if (!$rootScope.currentCustomerSite) {
662				return false;
663			}
664
665			if ($rootScope.isAdmin) {
666				return true;
667			}
668
669			if (!$rootScope.currentCustomerSite.features) {
670				return false;
671			}
672
673			if (!$rootScope.currentCustomerSite.subAdminFeatures) {
674				return false;
675            }
676			if ($rootScope.currentCustomerSite.subAdminFeatures.includes(permission)) {
677				return true;
678			}
679		}
680
681		$rootScope.setCurrentCustomerSite = function (site) {
682			$rootScope.currentCustomerSite = site;
683        }
684
685		$scope.triggerUpgradeAccountModal = function () {
686			$uibModal.open({
687				templateUrl: 'upgrade-account-modal',
688				controller: function ($scope, $timeout) {
689					$scope.upgradeAccount = function () {
690						$scope.gotoPath($scope.getSite() + "/organization");
691					}
692				},
693				backdrop: 'static',
694				resolve: {
695
696				}
697			}).result.then(function () {
698
699			}, function () {
700
701			});
702		};
703
704		$rootScope.triggerSubAdminAccessModal = function (type, id) {
705			$uibModal.open({
706				templateUrl: 'sub-admin-access-modal',
707				size: 'lg',
708				controller: function ($scope, $timeout, type, id, customerId) {
709					$scope.matrixData = null;
710
711					$scope.label = "";
712					$scope.typeLabel = "";
713
714					switch (type) {
715						case 1:
716							$scope.label = "form";
717							$scope.typeLabel = "Custom Forms";
718							break;
719						case 2:
720							$scope.label = "amenity";
721							$scope.typeLabel = "Amenities";
722							break;
723						case 3:
724							$scope.label = "event";
725							$scope.typeLabel = "Events";
726							break;
727						case 4:
728							$scope.label = "calendar";
729							$scope.typeLabel = "Calendars";
730							break;
731						case 5:
732							$scope.label = "folder";
733							$scope.typeLabel = "Document Repository";
734							break;
735						case 6:
736							$scope.label = "form";
737							$scope.typeLabel = "Surveys";
738							break;
739						case 7:
740							$scope.label = "form";
741							$scope.typeLabel = "Ticketing System";
742							break;
743						case 8:
744							$scope.label = "form";
745							$scope.typeLabel = "Violations";
746							break;
747						case 9:
748							$scope.label = "committees";
749							$scope.typeLabel = "Committees";
750							break;
751						case 10:
752							$scope.label = "boardroom";
753							$scope.typeLabel = "Boardroom";
754							break;
755                    }
756
757					$scope.ajaxCall('/SubAdmin/GetAssignedSubAdminUsers', { customerId: customerId, itemId: id, itemType: type }, function (data) {
758						$scope.matrixData = data;
759					});
760
761					$scope.submitAttempt = false;
762					$scope.noneSelected = false;
763					$scope.save = function (form) {
764						$scope.submitAttempt = true;
765
766						if (form.$invalid) {
767							return;
768						}
769
770						$scope.ajaxCall('/SubAdmin/SaveAssignedSubAdminUsers', { customerId: customerId, itemId: id, itemType: type, data: $scope.matrixData }, function (data) {
771							$scope.$close();
772						});
773					}
774				},
775				backdrop: 'static',
776				resolve: {
777					type: function () {
778						return type;
779					},
780					id: function () {
781						return id;
782					},
783					customerId: function () {
784						return $rootScope.currentCustomerSite.customerId;
785					}
786				}
787			}).result.then(function () {
788
789			}, function () {
790
791			});
792		};
793
794		$rootScope.getSiteBySlug = function (site) {
795
796			angular.forEach(AppConstants.CurrentUser.sites, function (lsite) {
797				if (lsite.customerSlug == site) {
798					$rootScope.isAdmin = lsite.isAdmin;
799					$rootScope.currentCustomerSite = lsite;
800				}
801			});
802
803			return site;
804		}
805
806		$rootScope.currentSites = AppConstants.CurrentUser.sites;
807
808		$rootScope.hasPublicSite = false;
809		$rootScope.checkPublicSite = function (slug) {
810			$scope.ajaxCall('/Customer/CheckPublicSite', { customerSlug: slug }, function (data) {
811				if (data == false) {
812					$rootScope.hasPublicSite = false;
813				} else {
814					$rootScope.hasPublicSite = true;
815
816				}
817
818			}, function () {
819
820			}, false);
821		}
822
823		$rootScope.hasProptia = false;
824		$rootScope.checkProptia = function () {
825			$scope.ajaxCall('/Customer/CheckProptiaEnabled', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
826				if (data == false) {
827					$rootScope.hasProptia = false;
828				} else {
829					$rootScope.hasProptia = true;
830				}
831			}, function () {
832
833			}, false);
834		}
835		
836		$rootScope.hasHelpSteps = false;
837		$rootScope.checkHelpSteps = function () {
838			$scope.ajaxCall('/Customer/CheckHelpStepsEnabled', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
839				if (data == false) {
840					$rootScope.hasHelpSteps = false;
841				} else {
842					$rootScope.hasHelpSteps = true;
843				}
844			}, function () {
845
846			}, false);
847		}
848		
849		$rootScope.siteSuspended = false;
850		$rootScope.checkSiteSuspended = function () {
851			$scope.ajaxCall('/Customer/CheckSiteSuspended', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
852				if (data == false) {
853					$rootScope.siteSuspended = false;
854				} else {
855					$rootScope.siteSuspended = true;
856				}
857			}, function () {
858
859			}, false);
860		}
861
862
863		$rootScope.preventDefault = function ($event) {
864			$event.preventDefault();
865		}
866
867
868		$rootScope.hasTutorialWizard = false;
869		$rootScope.checkTutorialWizard = function () {
870			if ($rootScope.currentCustomerSite) {
871				$scope.ajaxCall('/Customer/GetSetupWizard', { customerId: $rootScope.currentCustomerSite.customerId }, function (data) {
872					$scope.steps = 0;
873					$scope.setupwizard = data;
874					if ($scope.setupwizard.step1 == true) {
875						$scope.steps++;
876					}
877					if ($scope.setupwizard.step2 == true) {
878						$scope.steps++;
879					}
880					if ($scope.setupwizard.step3 == true) {
881						$scope.steps++;
882					}
883					if ($scope.setupwizard.step4 == true) {
884						$scope.steps++;
885					}
886					if ($scope.setupwizard.step5 == true) {
887						$scope.steps++;
888					}
889					if ($scope.setupwizard.step6 == true) {
890						$scope.steps++;
891					}
892
893					if ($scope.steps == 6) {
894						$rootScope.hasTutorialWizard = true;
895					} else {
896						$rootScope.hasTutorialWizard = false;
897					}
898				}, function () {
899
900				}, false);
901			}
902		}
903
904		$rootScope.transferToInternalSite = function (domain, customerId) {
905			$scope.ajaxCall('/AuthTransfer/RequestInternalMagicToken', { customerId: customerId }, function (data) {
906				window.location = domain + "/TransferInternal/" + data;
907			});
908		}
909
910		$rootScope.transferToMemberPortal = function (domain, customerId) {
911			$scope.ajaxCall('/AuthTransfer/RequestInternalMagicToken', { customerId: $scope.currentCustomerSite.customerId }, function (data) {
912				window.location = "https://hoastartmobileportal.azurewebsites.net/home?magicToken=" + data;
913			});
914		}
915
916		$rootScope.transferToMemberPortalRedirect = function (customerId) {
917			$scope.ajaxCall('/AuthTransfer/RequestInternalMagicToken', { customerId: customerId }, function (data) {
918				window.location = "https://hoastartmobileportal.azurewebsites.net/home?magicToken=" + data;
919			});
920		}
921
922		$rootScope.transferToMemberDashboardPortal = function (domain, customerId) {
923			$scope.ajaxCall('/AuthTransfer/RequestInternalMagicToken', { customerId: $scope.currentCustomerSite.customerId }, function (data) {
924				window.location = "https://hoastartmobileportal.azurewebsites.net/dashboard?magicToken=" + data;
925			});
926		}
927
928		$rootScope.transferToProptia = function () {
929			if (AppConstants.CurrentUser.proptiaUserId == null || true) {
930
931				$scope.ajaxCall('/User/GetCurrent', { userId: AppConstants.CurrentUser.userId }, function (data) {
932					$scope.currentUser = data;
933
934					if ($scope.currentUser.proptiaUserId != null) {
935						console.log("Transferring to Proptia");
936						var userSyncData = {
937							PassKey: 'UGcxdU5QSjUzNXFQMjNqSWdyN25UdU1hZllyQm8ybGxERll1MHNmeWhabnpsei9GTGpvOFI0a0FTL0JMcHdYQmZTYjFsWTRnaXNlNUYvV',
938							SourceIdentifier: $scope.currentUser.userId,
939							TargetIdentifier: $scope.currentUser.proptiaUserId,
940						};
941						$scope.ajaxApiCall('https://portal.proptia.com/api/auth/signonauth', userSyncData, function (data) {
942							window.location = data.url;
943						}, function () {
944
945						}, false);
946					} else {
947						console.log("User not synced with Proptia, redirect to proptia marketing site");
948						window.location = "https://portal.proptia.com/";
949					}
950				});
951
952			}
953		}
954
955		$scope.adjustHeight = function () {
956			if (jQuery('#admin-footer').length) {
957				$timeout(function () {
958					jQuery(document).ready(function () {
959						var footerHeight = jQuery('#admin-footer').height();
960						if (jQuery(window).height() > (jQuery('.page-container').height() + footerHeight + 90)) {
961							jQuery('#admin-footer').addClass('customStickyFooter').removeClass('noStickyFooter');
962							jQuery('#admin-footer').addClass('customStickyFooter').removeClass('noStickyFooter');
963						} else {
964							jQuery('#admin-footer').removeClass('customStickyFooter').addClass("noStickyFooter");
965
966						}
967						$timeout($scope.adjustHeight(), 200);
968					});
969				}, 10);
970			}
971		}
972
973		$scope.adjustHeight();
974
975		if (AppConstants.CurrentUser.isAdmin) {
976			$rootScope.loadAdminDashboard = true;
977		} else {
978			$rootScope.loadAdminDashboard = false;
979		}
980
981		$rootScope.timeouts = [];
982
983
984		$rootScope.checkSiteAccess = async function (slug, checkAdmin) {
985			$rootScope.siteSuspended = false;
986
987			window.onbeforeunload = function (e) {
988
989			}
990
991			if ($rootScope.timeouts.length) {
992				angular.forEach($rootScope.timeouts, function (timeout) {
993					$timeout.cancel(timeout);
994				});
995
996				$rootScope.timeouts = [];
997            }
998			var id = window.setTimeout(function () { }, 0);
999
1000			while (id--) {
1001				window.clearTimeout(id); // will do nothing if no timeout with id is present
1002			}
1003
1004			if (!AppConstants.CurrentUser.sites || !AppConstants.CurrentUser.sites.length) {
1005				var mypromise = await new Promise(resolve => {
1006					$scope.ajaxCall('/User/GetCurrentUserSites', { }, function (data) {
1007						AppConstants.CurrentUser.sites = data;
1008						$rootScope.currentSites = data;
1009						resolve("\t\t Got the data");
1010					});
1011				});
1012			} 
1013
1014			if (jQuery('.modal-backdrop').length) {
1015				jQuery('.modal-backdrop').remove();
1016            }
1017
1018			if (jQuery('.modal.fade').length) {
1019				jQuery('.modal.fade').remove();
1020			}
1021
1022			jQuery('body').removeClass('modal-open');
1023
1024			var adminfound = false;
1025
1026			var customDomainObject = false;
1027
1028			angular.forEach(AppConstants.CurrentUser.sites, function (loopsite) {
1029				if (loopsite.isAdmin) {
1030					adminfound = true;
1031				}
1032
1033				if (loopsite.useDomain == true && loopsite.isAdmin) {
1034					if (window.location.href.includes(loopsite.customDomain)) {
1035						customDomainObject = loopsite;
1036						$rootScope.setSite(loopsite.customerSlug);
1037
1038						if (loopsite.customerSlug != slug) {
1039							window.location.href = '/admin-dashboard/#!/' + loopsite.customerSlug + '/dashboard';
1040							return;
1041						}
1042					}
1043				}
1044			});
1045
1046			if (!adminfound && checkAdmin) {
1047				if (AppConstants.CurrentUser.sites.length && AppConstants.CurrentUser.sites.length > 0) {
1048					var tempsite = AppConstants.CurrentUser.sites[0];
1049					if (tempsite.customDomain) {
1050						window.location.href = tempsite.customDomain;
1051					} else {
1052						window.location.href = '/' + tempsite.customerSlug;
1053					}
1054				}
1055			} else {
1056				if (!$rootScope.currentCustomerSite || slug != $rootScope.currentCustomerSite.customerSlug) {
1057					$rootScope.getSiteBySlug(slug);
1058					if (!$rootScope.currentCustomerSite || slug != $rootScope.currentCustomerSite.customerSlug) {
1059						$rootScope.removeSite();
1060						window.location.href = '/';
1061						return;
1062					}
1063				}
1064
1065				if (!$rootScope.currentCustomerSite) {
1066					$rootScope.removeSite();
1067					window.location.href = '/';
1068					return;
1069				}
1070
1071				if (!$rootScope.isAdmin && checkAdmin) {
1072					$rootScope.removeSite();
1073					window.location.href = '/';
1074					return;
1075				}
1076
1077				if (!$rootScope.isAdmin && !checkAdmin && (!$rootScope.currentCustomerSite.subAdminFeatures || !$rootScope.currentCustomerSite.subAdminFeatures.length)) {
1078					angular.forEach(AppConstants.CurrentUser.sites, function (loopsite) {
1079						if (loopsite.customerSlug == slug) {
1080							if (loopsite.useDomain == true) {
1081								if (window.location.href.includes(loopsite.customDomain)) {
1082									customDomainObject = loopsite;
1083									$rootScope.setSite(loopsite.customerSlug);
1084
1085									if (loopsite.customerSlug != slug) {
1086										window.location.href = loopsite.customDomain;
1087										return;
1088									}
1089								} else {
1090									window.location.href = loopsite.customDomain;
1091									return;
1092								}
1093							} else {
1094								window.location.href = '/' + loopsite.customerSlug;
1095								return;
1096                            }
1097                        }
1098					});
1099					return;
1100				}
1101
1102				$rootScope.loadAdminDashboard = true;
1103
1104				$scope.adjustHeight();
1105
1106				$rootScope.checkPublicSite(slug);
1107				$rootScope.checkProptia();
1108				$rootScope.checkHelpSteps();
1109				$rootScope.checkSiteSuspended();
1110				$rootScope.getTrialEndDate();
1111				$rootScope.getBillingChoices();
1112				$rootScope.setSite(slug);
1113			}
1114		}
1115
1116		$rootScope.checkPublicSites = async function () {
1117			if (AppConstants != null) {
1118				if (AppConstants.CurrentUser != null) {
1119					if (AppConstants.CurrentUser.userId != null) {
1120						if (!AppConstants.CurrentUser.sites || !AppConstants.CurrentUser.sites.length) {
1121							var mypromise = await new Promise(resolve => {
1122								$scope.ajaxCall('/User/GetCurrentUserSites', {}, function (data) {
1123									AppConstants.CurrentUser.sites = data;
1124									$rootScope.currentSites = data;
1125									resolve("\t\t Got the data");
1126
1127								}, function (data) {
1128									console.log("Failed to retrieve data");
1129								});
1130
1131							});
1132						}
1133					}
1134				}
1135			}
1136		}
1137
1138		$rootScope.formatDecimalInput = function (number) {
1139			var pow = Math.pow(10 || 10, 2);
1140			number = Math.floor(number * pow) / pow;
1141
1142			if (number < 0) {
1143				number = 0;
1144            }
1145			return number;
1146		}
1147
1148		$rootScope.formatPaymentInput = function (input) {
1149			var data = parseFloat(input); 
1150
1151			if (!data && data != 0) {
1152				return "";
1153			}
1154
1155			return data.toFixed(2);
1156        }
1157
1158		$rootScope.zeroNaN = function (amount) {
1159			if (isNaN(amount)) {
1160				return 0;
1161			}
1162			return amount;
1163        }
1164
1165		$rootScope.formatMoney = function (amount) {
1166			if (isNaN(amount)) {
1167				amount = 0;
1168			}
1169
1170			amount = parseFloat(amount);
1171			return amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
1172		}
1173
1174		$rootScope.formatMoneyWithDollars = function (amount) {
1175			let USDollar = new Intl.NumberFormat('en-US', {
1176				style: 'currency',
1177				currency: 'USD',
1178			});
1179
1180
1181			if (isNaN(amount)) {
1182				amount = 0;
1183			}
1184
1185			amount = parseFloat(amount);
1186
1187			return USDollar.format(amount);
1188
1189			//return amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
1190		}
1191
1192		$rootScope.calculateConvenienceFee = function (value, percentage, dollarAmount, lateFee) {
1193			if (isNaN(percentage) || percentage == null) {
1194				percentage = 0;
1195			}
1196
1197			if (isNaN(dollarAmount) || dollarAmount == null) {
1198				dollarAmount = 0;
1199			}
1200
1201			if (isNaN(lateFee) || lateFee == null) {
1202				lateFee = 0;
1203			}
1204
1205			if (isNaN(value) || value == null) {
1206				value = 0;
1207			}
1208
1209			var charge = (parseFloat(value) + lateFee); 
1210			return ((charge + parseFloat(dollarAmount)) / (1 - (parseFloat(percentage) / 100)) - charge);
1211		}
1212
1213		$rootScope.isGlobalDirty = false;
1214		$rootScope.setGlobalDirty = function (isDirty) {
1215			$rootScope.isGlobalDirty = isDirty;
1216		}
1217
1218		$rootScope.checkDirtyGoToPath = function (url, m) {
1219			console.log("Checking global dirty");
1220			console.log($rootScope.isGlobalDirty);
1221			if ($rootScope.isGlobalDirty) {
1222				if (!confirm('You have unsaved changes. Are you sure you want to leave this page?')) {
1223					event.preventDefault();
1224					return;
1225				} else {
1226					$rootScope.gotoPath(url, m);
1227				}
1228			} else {
1229				$rootScope.gotoPath(url, m);
1230			}
1231
1232		}
1233
1234		$rootScope.gotoPath = function (url, m) {
1235
1236			jQuery('.alert-success').each(function () {
1237				jQuery(this).remove();
1238			});
1239
1240			$location.path(url);
1241			if (m) {
1242				$rootScope.pageData.menu = m;
1243			}
1244			$rootScope.pageData.showMobileMenu = false;
1245		};
1246
1247		$rootScope.trustedHtml = function (plainText) {
1248			return $sce.trustAsHtml(plainText);
1249			//return plainText;
1250		}
1251
1252		$rootScope.compareObjects = function (object1, object2) {
1253
1254			const keys1 = Object.keys(object1);
1255			const keys2 = Object.keys(object2);
1256
1257			for (const key of keys1) {
1258				if (key != "$$hashKey") {
1259					const val1 = object1[key];
1260					const val2 = object2[key];
1261					const areObjects = $rootScope.isObject(val1) && $rootScope.isObject(val2);
1262					if (
1263						areObjects && !$rootScope.compareObjects(val1, val2) ||
1264						!areObjects && val1 !== val2
1265					) {
1266						return false;
1267					}
1268                }
1269			}
1270			for (const key of keys2) {
1271				if (key != "$$hashKey") {
1272					const val1 = object1[key];
1273					const val2 = object2[key];
1274					const areObjects = $rootScope.isObject(val1) && $rootScope.isObject(val2);
1275					if (
1276						areObjects && !$rootScope.compareObjects(val1, val2) ||
1277						!areObjects && val1 !== val2
1278					) {
1279						return false;
1280					}
1281				}
1282			}
1283
1284			return true;
1285		}
1286
1287		$rootScope.loadScript = function (url, type, charset) {
1288			if (type === undefined) type = 'text/javascript';
1289			if (url) {
1290				var script = document.querySelector("script[src*='" + url + "']");
1291				if (!script) {
1292					var heads = document.getElementsByTagName("head");
1293					if (heads && heads.length) {
1294						var head = heads[0];
1295						if (head) {
1296							script = document.createElement('script');
1297							script.setAttribute('src', url);
1298							script.setAttribute('type', type);
1299							if (charset) script.setAttribute('charset', charset);
1300							head.appendChild(script);
1301						}
1302					}
1303				}
1304				return script;
1305			}
1306		};
1307
1308
1309		$rootScope.isObject = function(object) {
1310			return object != null && typeof object === 'object';
1311		}
1312
1313		$rootScope.isGoodDate = function (dt) {
1314			if (dt == null || dt == '') {
1315				return true; // blank is fine
1316			}
1317			/*
1318			if ((dt.length != 8) && (dt.length != 10)) {
1319				return false;
1320			}
1321
1322			var month = parseInt(dt.substring(0, 2));
1323			var day = parseInt(dt.substring((dt.length == 8) ? 2 : 3, (dt.length == 8) ? 4 : 5));
1324			var year = parseInt(dt.substring((dt.length == 8) ? 4 : 6, (dt.length == 8) ? 8 : 10));
1325			*/
1326
1327			var re = RegExp(/^(\d?\d)\/(\d?\d)\/((\d\d)?\d\d)$/g);
1328			var found = re.exec(dt);
1329			if (found == null) {
1330				return false;
1331			}
1332
1333			var month = parseInt(found[1]);
1334			var day = parseInt(found[2]);
1335			var year = parseInt(found[3]);
1336			if (year < 100) {
1337				year += 2000;
1338			}
1339
1340			if (month < 1 || month > 12) { // check month range
1341				return false;
1342			}
1343
1344			if (day < 1 || day > 31) {
1345				return false;
1346			}
1347
1348			if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) {
1349				return false;
1350			}
1351
1352			if (month == 2) { // check for february 29th
1353				var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
1354				if (day > 29 || (day == 29 && !isleap)) {
1355					return false;
1356				}
1357			}
1358
1359			return true; // date is valid
1360		};
1361
1362		$rootScope.clearSingleFormField = function (targetElementClass) {
1363			$(targetElementClass).val("").trigger('change');
1364		}
1365	})
1366
1367	.config(function (blockUIConfig) {
1368		blockUIConfig.template = '<div class=\"block-ui-overlay\"></div><div class=\"block-ui-message-container\" aria-live=\"assertive\" aria-atomic=\"true\"><i class="fa fa-spin fa-spinner fa-3x"></i></div>';
1369        //blockUIConfig.template = '<div class=\"block-ui-overlay\"></div><div class=\"block-ui-message-container\" aria-live=\"assertive\" aria-atomic=\"true\"></div>';
1370		blockUIConfig.delay = 100;
1371		blockUIConfig.requestFilter = function (config) {
1372			return (config.method == 'POST') && ((config.headers['X-Do-Loading'] || 'false') == 'true');
1373		};
1374	})
1375
1376	.directive('icheck', function ($timeout, $parse) {
1377		return {
1378			require: 'ngModel',
1379			scope: {},
1380			link: function (scope, element, $attrs, ngModel) {
1381				return $timeout(function () {
1382					var value;
1383					value = $attrs['value'];
1384
1385					if ($attrs['disabled'])
1386						$(element).iCheck($attrs['disabled'] ? 'disable' : 'enable');
1387
1388					scope.$watch(function () { return ngModel.$modelValue; }, function (newValue) {
1389						if (newValue != undefined) {
1390							$(element).iCheck('update');
1391						}
1392					});
1393
1394					scope.$watch($attrs['ngDisabled'], function (newValue) {
1395						if (newValue != undefined) {
1396							$(element).iCheck(newValue ? 'disable' : 'enable');
1397							$(element).iCheck('update');
1398						}
1399					});
1400
1401					return $(element).iCheck({
1402						checkboxClass: $attrs['checkboxClass'] ? $attrs['checkboxClass'] : 'icheckbox_square-blue',
1403						radioClass: $attrs['radioClass'] ? $attrs['radioClass'] : 'iradio_square-blue',
1404						increaseArea: '20%' // optional
1405					}).on('ifChanged', function (event) {
1406						if ($(element).attr('type') === 'checkbox') {
1407							ngModel.$setViewValue(event.target.checked);
1408						}
1409					}).on('ifClicked', function (event) {
1410						if ($(element).attr('type') === 'radio') {
1411							//set up for radio buttons to be de-selectable
1412							if (ngModel.$viewValue != value) {
1413								$(element).iCheck('check');
1414								ngModel.$setViewValue(value);
1415							}
1416							else {
1417								$(element).iCheck('uncheck');
1418								ngModel.$setViewValue(undefined);
1419							}
1420							ngModel.$render();
1421						}
1422					});
1423				});
1424			}
1425		};
1426	})
1427
1428	.filter('localDateTime', function ($filter) {
1429		return function (input) {
1430			if (input == null) {
1431				return null;
1432			}
1433			return $filter('date')(new Date(parseInt(input)), 'short');
1434		};
1435	})
1436
1437	.filter('localDate', function ($filter) {
1438		return function (input) {
1439			if (input == null) {
1440				return null;
1441			}
1442			return $filter('date')(new Date(parseInt(input)), 'M/d/yyyy');
1443		};
1444	})
1445
1446	.filter('localLongDate', function ($filter) {
1447		return function (input) {
1448			return $filter('date')(new Date(parseInt(input)), 'longDate');
1449		};
1450	})
1451
1452	.filter('localShortDate', function ($filter) {
1453		return function (input) {
1454			return $filter('date')(new Date(parseInt(input)), 'shortDate');
1455		};
1456	})
1457
1458	.filter('localPrettyDate', function ($filter) {
1459		return function (input) {
1460			return $filter('date')(new Date(parseInt(input)), 'MMM d, yyyy');
1461		};
1462	})
1463
1464	.filter('maskedDate', function ($filter) {
1465		return function (dt) {
1466			if (dt == null || dt == '') {
1467				return dt;
1468			}
1469
1470			if (dt.length == 10) {
1471				return dt;
1472			}
1473
1474			if (dt.length != 8) {
1475				return '';
1476			}
1477
1478			return dt.substring(0, 2) + '/' + dt.substring(2, 4) + '/' + dt.substring(4, 8);
1479		};
1480	})
1481
1482	.filter('range', function () {
1483		return function (input, min, max) {
1484			min = parseInt(min); //Make string input int
1485			max = parseInt(max);
1486			for (var i = min; i <= max; i++)
1487				input.push(i);
1488			return input;
1489		};
1490	})
1491
1492	.filter('moment', function () {
1493		return function (input, momentFn /*, param1, param2, ...param n */) {
1494			var args = Array.prototype.slice.call(arguments, 2),
1495				momentObj = moment(input);
1496			return momentObj[momentFn].apply(momentObj, args);
1497		};
1498	})
1499
1500	.directive('dirtyTracking', ['$window', function ($window) {
1501		return {
1502			restrict: 'E',
1503			scope: {
1504				forms: '=',
1505				formProps: '=',
1506				checkDirty: '=check'
1507			},
1508			link: function ($scope, $element, $attrs) {
1509				function isDirty() {
1510					if (!$scope.checkDirty) {
1511						return false;
1512					}
1513					var dirty = false;
1514					for (var jdx = 0; jdx < $scope.formProps.length; jdx++) {
1515						var formVars = $scope.formProps[jdx].split('.');
1516						var formObj = $scope.forms[formVars[0]];
1517						for (var idx = 1; idx < formVars.length; idx++) {
1518							formObj = formObj[formVars[idx]];
1519						}
1520						if (formObj) {
1521							if (formObj.$$element.is(':visible')) {
1522								dirty |= (formObj.$pristine === false);
1523							}
1524						}
1525					}
1526					return dirty;
1527				}
1528
1529				function areYouSurePrompt() {
1530					if (isDirty()) {
1531						return 'You have unsaved changes. Are you sure you want to leave this page?';
1532					}
1533				}
1534
1535				//$window.addEventListener('beforeunload', areYouSurePrompt);
1536				$window.onbeforeunload = areYouSurePrompt;
1537
1538				$element.bind("$destroy", function () {
1539					$window.onbeforeunload = null;
1540				});
1541
1542				$scope.$on('$locationChangeStart', function (event) {
1543					var prompt = areYouSurePrompt();
1544					if (!event.defaultPrevented && prompt && !confirm(prompt)) {
1545						event.preventDefault();
1546					}
1547				});
1548
1549				$scope.$on('modal.closing', function (event) {
1550					var prompt = areYouSurePrompt();
1551					if (!event.defaultPrevented && prompt && !confirm(prompt)) {
1552						event.preventDefault();
1553					}
1554				});
1555			}
1556		};
1557	}])
1558
1559	.directive('timePicker', function () {
1560		return {
1561			restrict: 'E',
1562			replace: true,
1563			template: '<select ng-options="x.id as x.text for x in times"><option value="" disabled>Select...</option></select>',
1564			scope: {
1565				jump: '='
1566			},
1567			link: function ($scope, $element, $attrs) {
1568				$scope.times = [];
1569				for (var x = 0; x < 24 * (60 / $scope.jump); x++) {
1570					var hours = Math.floor((x * $scope.jump) / 60);
1571					var minutes = (x * $scope.jump) % 60;
1572					var ampm = (hours >= 12 ? 'pm' : 'am');
1573					hours = hours % 12;
1574					hours = hours ? hours : 12; // the hour '0' should be '12'
1575
1576					$scope.times.push({ id: (x * $scope.jump), text: hours + ':' + (minutes < 10 ? '0' + minutes : minutes) + ' ' + ampm });
1577				}
1578			}
1579		};
1580	})
1581
1582	.directive('lazyLoad', function () {
1583		return {
1584			restrict: 'A',
1585			scope: {
1586				loadMore: '&',
1587			},
1588			link: function ($scope, $element, $attrs) {
1589				const observer = new IntersectionObserver(loadImg)
1590				const e = angular.element($element)[0];
1591				observer.observe(e)
1592
1593				function loadImg(changes) {
1594					changes.forEach(function (change) {
1595						if (change.intersectionRatio > 0) {
1596							$scope.loadMore();
1597						}
1598					})
1599				}
1600			}
1601		};
1602	})
1603
1604	.directive('brokenParagraphs', function () {
1605		return {
1606			restrict: 'E',
1607			scope: true,
1608			template: '<p ng-repeat="line in paragraphs track by $index" ng-bind-html="line | linky : \'_blank\'" style="margin-bottom: 0px;"></p>',
1609			link: function (scope, element, attributes) {
1610				scope.$watch(attributes.text, function (text) {
1611					scope.paragraphs = [];
1612					if (text) {
1613						//remove weird newlines
1614						text.replace(/\r\n|\r/g, '\n');
1615
1616						// split lines and paragraphs
1617						scope.paragraphs = text.split('\n');
1618					}
1619				});
1620			}
1621		};
1622	})
1623
1624	.directive('backImg', function () {
1625		return function (scope, element, attrs) {
1626			var url = attrs.backImg;
1627			element.css({
1628				'background-image': 'url(' + url + ')',
1629				'background-size': 'cover'
1630			});
1631		};
1632	});

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.