PageSourceSearch

https://nantucket-ma.gov/Assets/Scripts/Modal.js

js nantucket-ma.gov collected 2026-09-24 09:02:04 UTC 29,598 bytes, 806 lines download raw bytes

1/*
2JQuery plug-in to handle modals on MVC modules.
3It assumes existing markup as defined on MvcAdmin.Master or MvcFront.Master (It could be used custom in own view if needed nested modals).
4*/
5
6$(function () {
7	(function ($) {
8		$.fn.cpModal = function (opt) {
9			//Here extends more options when needed modal with new behaviors LC.
10			var defaults = {
11				title: 'CP modal', 	// [html content] modal title's
12				url: '', 			// [string] URL to be loaded on modal
13				type: 'GET', 		// [string] type used for request (only in Ajax call. 'GET' or 'POST').
14				contentType: 'application/x-www-form-urlencoded', // [string] contentType used for request (only in Ajax call).
15				data: null, 		// [Object/string] data used for request (only in Ajax call).
16				cache: false, 		// [bool] cache used for request (only in Ajax call).
17				htmlContent: '', 	// [html content] When url is empty, uses this as modal's content
18				className: '', 		// [string] CSS class for modal container
19				isFrontEnd: true, 	// [bool] Whether modal's border color will be displayed as front-end or not
20				parentWindowID: '', // [string] modal's parent window identifier (used for nested modals)
21				isNestedModal: false, // [bool] is this a nested modal
22				onClose: null, 		// [function] code to execute when close button is clicked
23				autoLoad: true, 	// [bool] load content when invoking plug-in
24				autoAdjust: false, // [bool] whether or not the modal will be auto positioned when scrolling
25				useIframe: false, 	// [bool] whether or not to use an iFrame to load content
26				iframeHeight: '', // [string] if using iframe, set specifc height for it (ex: '200px').
27				elementID: null, //If a modal needs to be created with a custom ID, not using  mvcModal as ID.
28				draggable: true, //Whether or not the modal is draggable.
29				insideEditor: false, //Whether or not the modal is opening inside editor, in case of slideshow within editor, for example.
30				showLoading: false, //Whether or not to show loading graphic while loading the modal.
31				async: false, //Whether or not to load asynchronously the html content (only if options.url is defined).
32				onLoadComplete: null,  //[function] code to execute when the modal has loaded successfully from AJAX
33				scrolling: false, // [bool] whether iframe is scrollable.
34				verticalCenter: false
35			};
36
37			var options = $.extend(defaults, opt);
38			// cache the base element
39			var base = this;
40			base.options = options;
41			// initialize (and localize) all variables
42			var $modal = '';
43			var $parentWindow = (options.parentWindowID && options.parentWindowID != '') ? $('#' + options.parentWindowID) : $(window);
44			var triggeringElement = null;
45
46			// PUBLIC FUNCTIONS
47			this.init = function () {
48				$modal = $(this);
49				$modal.attr("role", "dialog");
50				$modal.attr("aria-modal", "true");
51				$modal.attr("tabindex", "-1");
52				var titleId = (Math.random().toString(36)).slice(2);
53				$modal.attr("aria-labelledby", titleId);
54
55				// If this modal is appearing inside an iframe remove admin wrap which can cause some issues 
56				if (window.self !== window.top) {
57					$modal.attr('style', function (i, s) { return s + 'height: auto !important;' });
58				}
59
60				// Saves current jquery object reference for future references to call public methods if needed
61				if (!$modal.data('cpModal'))
62					$modal.data('cpModal', base);
63
64				// Sets modal title
65				var $modalTitle = $modal.find('.modalTitle').filter(":first");
66				$modalTitle.html(options.title);
67				$modalTitle.attr("id", titleId);
68				// Sets modal close button event.
69				if (options.onClose != null) {
70					$modal.find('.modalClose').filter(":first").attr('onclick', '').unbind('click');
71					$modal.find('.modalClose').filter(":first").bind("click", function (e) {
72						e.preventDefault();
73						options.onClose(e, base);
74						var element = document.getElementsByClassName('modalTitle')[0]
75						var events = $._data(element, "events");
76						if(events != undefined)
77							events.mouseout = undefined;
78					});
79				} else {
80					$modal.find('.modalClose').filter(":first").attr('onclick', '').unbind('click');
81					$modal.find('.modalClose').filter(":first").bind("click", function (e) {
82						e.preventDefault();
83						base.hide();
84						var element = document.getElementsByClassName('modalTitle')[0]
85						var events = $._data(element, "events");
86						if(events != undefined)
87							events.mouseout = undefined;
88						return false;
89					});
90				}
91
92				$(".modalTitle").mouseout(function () {
93					$("body").trigger($.Event("mouseup", { which: 1 }));
94				});
95
96				if ($.fn.draggable) {
97					if (!options.draggable)
98						$modal.css({ 'top': '', 'left': '' }); //reset to default position
99					if ($modal.data('draggable') && !options.draggable) {
100						$modal.draggable('destroy');
101						$modalTitle.css('cursor', 'auto');
102					}
103					if (!$modal.data('draggable') && options.draggable) {
104						$modal.draggable({
105							handle: '.modalTitleLeft',
106							iframeFix: self !== top,
107							start: function (event, ui) {
108							}
109						});
110						$modalTitle.css('cursor', 'move');
111					}
112				}
113
114				// if needed, loads modal after initialization
115				if (options.autoLoad)
116					base.load();
117			};
118
119			this.load = function () {
120				if (options.useIframe) {
121					//create iframe on the fly
122					var iframeUrl = document.createElement('a');
123					iframeUrl.href = options.url;
124					if (iframeUrl.protocol !== window.location.protocol || iframeUrl.host !== window.location.host) {
125						alert('Modal was incorrectly configured to display content.');
126						return;
127					}
128					var $iframe = $('<iframe/>', {
129						'data-src': iframeUrl.pathname + iframeUrl.search + iframeUrl.hash,
130						id: 'iframeModal',
131						name: 'iframeModal',
132						frameborder: 0
133					}).css('border', 'none');
134					if (options.iframeHeight)
135						$iframe.css('height', options.iframeHeight);
136					if (options.scrolling)
137						$iframe.css('overflow-y', 'auto');
138					else
139						$iframe.attr('scrolling', 'no');
140					$modal.find('.modalContent').filter(":first").html($iframe);
141					base.show();
142
143					if (options.onLoadComplete != null) {
144						options.onLoadComplete(base);
145					}
146				} else if (options.url && options.url != '') {
147					var ignoreEditor = options.insideEditor;
148					//load from URL
149					$.ajax({
150						url: options.url,
151						type: options.type,
152						contentType: options.contentType,
153						data: options.data,
154						async: options.async,
155						ignoreEditor: ignoreEditor,
156						cache: options.cache,
157						success: function (result) {
158							var content = parseContent(result);
159							$modal.find('.modalContent').filter(":first").html(content.body);
160							$modal.find('.modalBottom').filter(":first").html(content.footer);
161							base.show();
162
163							if (options.onLoadComplete != null) {
164								options.onLoadComplete(base);
165							}
166						},
167						error: function (xhr, textStatus, exception) {
168							alert('Error encountered while loading modal.' + "\nStatus: " + xhr.status + "\n" + "Exception: " + exception);
169							$('#ajaxErrorLog').text(xhr.responseText);
170						},
171						beforeSend: function () {
172							if (options.showLoading)
173								ajaxPostBackStart('Loading');
174						},
175						complete: function () {
176							if (options.showLoading)
177								ajaxPostBackEnd();
178						}
179					});
180				} else if (options.htmlContent && options.htmlContent != '') {
181					//Load specific content
182					var content = parseContent(options.htmlContent);
183					$modal.find('.modalContent').filter(":first").html(content.body);
184					$modal.find('.modalBottom').filter(":first").html(content.footer);
185					base.show();
186
187					if (options.onLoadComplete != null) {
188						options.onLoadComplete(base);
189					}
190				} else {
191					alert('Modal was incorrectly configured to display content.');
192				}
193			};
194
195			this.show = function () {
196				if (options.useIframe) {
197					if (isie) {
198						window.frames['iframeModal'].document.location.href = $modal.find('iframe').attr('data-src'); // This method was required tp address issue in IE 9 with moving IFrames arund and javascript errors					
199					}
200				}
201
202				if ($modal.hasClass('modalContainerCP') && options.isFrontEnd) {
203					$modal.removeClass('modalContainerCP');
204					$modal.find('.modalContent').filter(":first").removeClass('adminWrap');
205				} else if (!$modal.hasClass('modalContainerCP') && !options.isFrontEnd) {
206					$modal.addClass('modalContainerCP');
207					$modal.find('.modalContent').filter(":first").addClass('adminWrap');
208				}
209
210				if (options.useIframe && $modal.hasClass('noIframe')) {
211					$modal.removeClass('noIframe');
212				} else if (!options.useIframe && !$modal.hasClass('noIframe')) {
213					$modal.addClass('noIframe');
214				}
215
216				if (options.className && options.className != '')
217					$modal.addClass(options.className);
218
219				if (options.useIframe && window.self !== window.top) { //IFrame loaded inside IFrame
220					$modal.addClass('nestedIframe');
221				}
222
223				if (options.autoAdjust) {
224					$(window).bind("scroll." + $modal.attr('id'), function () {
225						adjustPosition();
226					});
227					adjustPosition();
228				}
229
230				if ($('.modalBackground').length == 0) {
231					//creates a modal background for the page on the fly
232					$('<div id="mvcModal_backgroundElement" class="modalBackground"></div>').prependTo('body');
233				}
234
235				$(document).on("keydown.cpModal." + $modal.attr('id'), function (e) {
236					if (e.key === "Escape") {
237						base.hide();
238						return;
239					}
240
241					if (e.key !== "Tab") {
242						return;
243					}
244
245					var $focusableElements = $modal.find('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])').filter(':visible');
246					if ($focusableElements.length === 0) {
247						e.preventDefault();
248						$modal.focus();
249						return;
250					}
251
252					var first = $focusableElements.get(0);
253					var last = $focusableElements.get($focusableElements.length - 1);
254
255					if (e.shiftKey && document.activeElement === first) {
256						e.preventDefault();
257						last.focus();
258					} else if (!e.shiftKey && document.activeElement === last) {
259						e.preventDefault();
260						first.focus();
261					}
262				});
263
264				$modal.show();
265				triggeringElement = document.activeElement;
266
267				var $initialFocusableElement = $modal.find('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')
268					.filter(':visible')
269					.first();
270
271				if ($initialFocusableElement.length > 0) {
272					$initialFocusableElement.get(0).focus({ preventScroll: true });
273				} else {
274					$modal.get(0).focus({ preventScroll: true });
275				}
276				
277				if (!isie) {
278					$modal.find('iframe').attr('src', $modal.find('iframe').attr('data-src'));
279				}
280				if (!options.autoAdjust || !options.verticalCenter)
281					window.CommonPopoverInstance.resizeToWindow($modal);
282
283				$(window).unbind('resize orientationChanged', setModalHeightWidthToFull).bind('resize orientationChanged', setModalHeightWidthToFull);
284
285				setModalHeightWidthToFull($modal);
286
287				$('.modalBackground').show(); //TODO: How to handle this better for nested modals
288
289				options.verticalCenter && verticalCenter($modal);
290			};
291
292			function verticalCenter($modal) {
293				parent = window;
294				$modal.css({
295					"position": "absolute",
296					"top": ((($(parent).height() - $modal.outerHeight()) / 2) + $(parent).scrollTop() + "px"),
297					"left": ((($(parent).width() - $modal.outerWidth()) / 2) + $(parent).scrollLeft() + "px")
298				});
299			}
300
301			this.hide = function () {
302				$modal.hide();
303				$modal.removeClass(options.className);
304				$(window).unbind("scroll." + $modal.attr('id'));
305				$(window).unbind('resize orientationChanged', setModalHeightWidthToFull);
306				var $visibleModals = $('.modalContainer:visible, .modalContainerCP:visible');
307				var hasOpenNestedModals = $visibleModals.length > 0;
308				if (!options.isNestedModal || !hasOpenNestedModals)
309					$('.modalBackground').hide(); //TODO: How to handle this better for nested modals
310				$(document).off("keydown.cpModal." + $modal.attr('id'));
311				triggeringElement?.focus();
312			};
313
314			this.destroy = function () {
315				//TODO: This needs more work
316				$modal.data('cpModal').remove();
317				$(document).off("keydown.cpModal." + $modal.attr('id'));
318			};
319
320			// PRIVATE FUNCTIONS
321
322			function adjustPosition() {
323				var top = (($parentWindow.height() - $modal.outerHeight(false)) / 2) + $parentWindow.scrollTop();
324				if (top < 0) // do not tolerate negative on top position.
325					top = 0;
326				$modal.css({ "top": top + "px" });
327	            /* Not needed since left position is managed with styles and it works fine for nested modals too.
328				if (options.isNestedModal)
329				$modal.css({ "left": (($parentWindow.width() - $modal.outerWidth()) / 2) + $parentWindow.scrollLeft() + "px" });*/
330			}
331
332			/*This is used to adjust the modal when the users screen view is smaller than the modal. -Zach Terwort*/
333			var lastScroll = 0;
334			$(window).scroll(function () {
335				var top = $parentWindow.height() - $modal.outerHeight(false);
336				var scrollDirection = $parentWindow.scrollTop();
337				if (top < 0 && scrollDirection > lastScroll) {
338					top = $parentWindow.height() - $modal.height() - 20;
339					$modal.css({ "top": top + "px" });
340				}
341				else if (top < 0) {
342					top = 20;
343					$modal.css({ "top": top + "px" });
344				}
345				lastScroll = scrollDirection;
346			});
347
348			/*This is used to make sure the modal isn't off the screen, and inaccessible after the user drops the modal. -Zach Terwort */
349			$(document).mouseup(function (event) {
350				var top = $modal.position().top;
351				if (top < 0) {
352					top = 20;
353					$modal.css({ "top": top + "px" });
354				}
355			});
356
357			function parseContent(htmlContent) {
358				htmlContent = htmlContent || '';
359				var content = { body: htmlContent, footer: '' };
360				var indexFooter = htmlContent.indexOf('<modalfooter>');
361				if (indexFooter != -1) {
362					content.body = htmlContent.substr(0, indexFooter);
363					content.footer = htmlContent.substr(indexFooter).replace('<modalfooter>', '').replace('</modalfooter>', '');
364				}
365				return content;
366			}
367
368			function setModalHeightWidthToFull() {
369				var viewportHeight = $(window).height();
370				var maxHeight = viewportHeight - 140;
371				if (maxHeight <= $modal.height() && $(document.body).hasClass('wide')) {
372					$('.modalContentRight > .modalContent', $modal).css('max-height', maxHeight + 'px');
373					$modal.addClass('fullHeight');
374				}
375				else {
376					$('.modalContentRight > .modalContent', $modal).css('max-height', '');
377					$modal.removeClass('fullHeight');
378				}
379
380				if ($(document.body).hasClass('wide') && $modal.hasClass('fullWidth')) {
381					$modal.removeClass('fullWidth');
382					$modal.css('margin-top', '');
383				}
384			}
385
386			base.init();
387			return this;
388		};
389	})(jQuery);
390});
391
392//Standard function to open modals in most of the scenarios
393function openCpModal(options) {
394	//override modal options for mobile view
395	if ($('#IsMobile').val() == 'True') {
396		var mobileOptions = {
397			autoAdjust: false //do not autoadjust when is mobile view. Modal nneds to be accessed from small browser in one spot.
398		};
399		options = $.extend(mobileOptions, options);
400		if (options.className == null || options.className.indexOf('mobile') < 0)
401			options.className = options.className + ' mobile'; //if needed add mobile class to modal.
402	}
403
404	var $modal = getCommonCpModal(options.elementID);
405	if ($modal.parent() != $('body'))
406		$modal.appendTo('body'); //if needed move at the end of the body
407
408	if ($.fn.cpModal === undefined) { //Multiple versions of Jquery causing fn not to be registered.
409		$.getScript('/Assets/Scripts/Modal.js', function () { return $modal.cpModal(options); });
410	} else {
411		return $modal.cpModal(options);
412	}
413}
414
415//Standard function to close modals in most of the sceneries
416function closeCpModal() {
417	if (getCommonCpModal().data('cpModal')) {
418		return getCommonCpModal().data('cpModal').hide();
419	}
420}
421
422//Get markup modal or create one on the fly if needed
423function getCommonCpModal(elementID) {
424	//If elementID is not provided, use the default 'mvcModal' id.
425	if (elementID == null)
426		elementID = 'mvcModal';
427	//If the element already exists on the page, simply return the element to be reused.
428	if ($('#' + elementID).length > 0) {
429		return $('#' + elementID);
430	}
431	else {
432		//Create on the fly a common mvc modal markup with custom ID.
433		var $modalContainer = $('<div id="' + elementID + '" class="modalContainer modalContainerCP MVC noIframe" style="display: none;"></div>');
434		var $modalTitleLeft = $('<div class="modalTitleLeft"><h3 class="modalTitle"></h3><a class="modalClose" href="#" aria-label="Close this modal" title="Close this window">' +
435								'<svg viewBox="0 0 25 25" id="modalIconClose"><polygon style="fill:currentcolor" points="18.806,6.98 18.075,6.248 12.532,11.792 6.989,6.248 6.257,6.98 11.786,12.508 6.212,18.084 6.943,18.815 12.532,13.224 18.12,18.815 18.851,18.084 13.277,12.508"></polygon>' +
436								'</svg></a></div>');
437		var $modalContentLeft = $('<div class="modalContentLeft"><div class="modalContentRight"><div class="newCP adminWrap modalContent"></div></div></div>');
438		var $modalBottomLeft = $('<div class="modalBottomLeft"><div class="modalBottomRight"><div class="modalBottom"></div></div></div>');
439		$modalContainer.append($modalTitleLeft).append($modalContentLeft).append($modalBottomLeft);
440		$modalContainer.appendTo('body');
441		return $modalContainer;
442	}
443}
444
445function PositionVerticle($modal) {
446	$modal.css({ "top": (($(window).height() - $modal.outerHeight(false)) / 2) + $(window).scrollTop() + "px" });
447	$modal.css({ "top": ($parentWindow.height() - $modal.outerHeight(false)) / 2 + $parentWindow.scrollTop() + "px" });
448}
449
450function showModal(className) {
451	var $modal = $('.modalContainer.' + className);
452	window.onscroll = function () {
453		PositionVerticle($modal);
454	};
455	PositionVerticle($modal);
456	$modal.show();
457	$(window).unbind('resize orientationChanged', setModalHeightWidthToFull).bind('resize orientationChanged', setModalHeightWidthToFull);
458	setModalHeightWidthToFull($modal);
459	$('.modalBackground').show();
460}
461
462function hideModal(className) {
463	$('.modalContainer.' + className).hide();
464	$('.modalBackground').hide();
465}
466
467function hideModalAndReloadPage(className) {
468	hideModal(className);
469	location.reload(true);
470}
471
472/*
473JQuery plug-in to handle popovers.
474
475*/
476function CommonPopoverClass() {
477	this.popovers = [];
478	this.initialized = false;
479	this.$commonPopup = null;
480
481	this.PopoverWrap = function ($popover, options) {
482		this.ID = $popover.attr('id');
483		this.$popoverContent = $popover.children();
484		this.isVisible = false;
485		this.options = options;
486		this.onClose = null;
487	};
488}
489
490CommonPopoverClass.prototype.hideAll = function (animate) {
491	var self = this;
492	if (self.$commonPopup != null) {
493		self.$commonPopup.fadeOut(animate ? 300 : 0, function () {
494			for (var i = 0; i < self.popovers.length; i++) {
495				if (self.popovers[i].options.className && self.popovers[i].options.className != '')
496					self.$commonPopup.removeClass(self.popovers[i].options.className);
497				self.popovers[i].isVisible = false;
498			}
499		});
500	}
501};
502
503CommonPopoverClass.prototype.show = function (popoverWrap) {
504	var self = this;
505	var alreadyOpened = false; //To prevent creating animation if popover is already opened
506	for (var i = 0; i < self.popovers.length; i++) {
507		if (self.popovers[i].options.className)
508			self.$commonPopup.removeClass(self.popovers[i].options.className);
509		if (self.popovers[i].isVisible)
510			alreadyOpened = true;
511		self.popovers[i].isVisible = false;
512	}
513	self.$commonPopup.children().detach();
514	self.$commonPopup.append(popoverWrap.$popoverContent);
515
516	if (popoverWrap.options.className) {
517		self.$commonPopup.addClass(popoverWrap.options.className);
518		window.setTimeout(function () {
519			self.$commonPopup.addClass(popoverWrap.options.className);
520		}, 0);
521	}
522
523	popoverWrap.isVisible = true;
524	self.$commonPopup.fadeIn(alreadyOpened ? 0 : 300, function () {
525		//If needed call back onOpen event
526		if (popoverWrap.options.onOpen != null && typeof (popoverWrap.options.onOpen) == "function")
527			popoverWrap.options.onOpen(popoverWrap);
528	});
529};
530
531CommonPopoverClass.prototype.hide = function (popoverWrap) {
532	var self = this;
533	if (popoverWrap && popoverWrap.options) {
534		self.$commonPopup.fadeOut(300, function () {
535			if (popoverWrap.options.className)
536				self.$commonPopup.removeClass(popoverWrap.options.className);
537			popoverWrap.isVisible = false;
538		});
539	}
540};
541
542CommonPopoverClass.prototype.RegisterPopover = function ($popover, options) {
543	var self = this;
544	if ($popover.attr('id') == null || $popover.attr('id') == "") {
545		$.ajaxSetup({ async: false });
546		AJAX("/Utility/NewGuid", "post", null, function (reponse) {
547			$popover.attr('id', reponse.Guid);
548		}, false);
549		$.ajaxSetup({ async: true });
550	}
551
552	//Creates popover wrapper
553	var newPopoverWrap = new self.PopoverWrap($popover, options);
554	//remove popover if nedded
555	self.RemovePopover(newPopoverWrap);
556	//add popover to global array
557	self.popovers.push(newPopoverWrap);
558
559	//common popover initialization if needed
560	self.init();
561
562	//pop over specific initialization
563
564	// Sets popover close button event.
565	if (options.onClose != null && typeof (options.onClose) == "function") {
566		$popover.find('.cpClosePopOver').filter(":first").attr('onclick', '').unbind('click');
567		$popover.find('.cpClosePopOver').filter(":first").bind("click", function (e) {
568			options.onClose(e, newPopoverWrap);
569		});
570	} else {
571		$popover.find('.cpClosePopOver').filter(":first").attr('onclick', '').unbind('click');
572		$popover.find('.cpClosePopOver').filter(":first").bind("click", function (e) {
573			e.preventDefault();
574			self.hide(newPopoverWrap);
575		});
576	}
577
578	return newPopoverWrap;
579};
580
581CommonPopoverClass.prototype.RemovePopover = function (popoverWrap) {
582	var self = this;
583	for (var i = 0; i < self.popovers.length; i++) {
584		if (self.popovers[i].ID == popoverWrap.ID)
585			self.popovers.splice(i);
586	}
587};
588
589CommonPopoverClass.prototype.init = function () {
590	var self = this;
591	if (!self.initialized) {
592		self.$commonPopup = self.getCommonPopover();
593
594		self.$commonPopup.click(function (e) {
595			e.stopPropagation();
596		}).draggable({
597			handle: '.cpPopOverHeader'
598		});
599
600		$('html').click(function () {
601			self.hideAll(true);
602		});
603
604		self.initialized = true;
605	}
606};
607
608CommonPopoverClass.prototype.getCommonPopover = function (elementID) {
609	var commonElementID = "cpCommonPopover";
610	if (elementID == null) {
611		if ($('#cpCommonPopover').length == 1)
612			return $('#cpCommonPopover');
613		if ($('.cpPopOver.common').length == 1)
614			return $('.cpPopOver.common');
615	}
616	else
617		commonElementID = elementID;
618	//create on the fly a common popover markup.
619	//Sets initial top position based on toolbar if exists
620	var topPosition = "";
621	if ($("#divToolbars").length > 0) {
622		topPosition = " top: " + ($("#divToolbars").height() + $('div.cp-Toolbars').height() + 30) + "px;";
623	}
624
625	var $commonPopover = $("<div id=\"" + commonElementID + "\" class=\"cpPopOver common\" style=\"display: none; z-index:100; position:fixed;" + topPosition + "\"></div>");
626	return $commonPopover.appendTo('body');
627};
628
629(function ($) {
630	$.fn.cpPopover = function (opt) {
631		//Here extends more options when needed popover with new behaviors LC.
632		var defaults = {
633			url: '', 			// [string] URL to be loaded on popover
634			type: 'GET', 		// [string] type used for request (only in Ajax call. 'GET' or 'POST').
635			contentType: 'application/x-www-form-urlencoded', // [string] contentType used for request (only in Ajax call).
636			data: null, 		// [Object/string] data used for request (only in Ajax call).
637			cache: false, 		// [bool] cache used for request (only in Ajax call).
638			htmlContent: '', 	// [html content] When url is empty, uses this as popover's content
639			onClose: null, 		// [function] code to execute when close button is clicked
640			onOpen: null, 		// [function] code to execute when popover is opened
641			onInit: null, 		// [function] code to execute when popover is initialized (after loading)
642			className: '', 		// [string] CSS class for popover container
643			makeAjaxCallEverytime: false //Whether or not an ajax call is always made whenever the control is clicked. If false, uses the cached(already rendered) version.
644		};
645
646		var options = $.extend(defaults, opt);
647
648		// cache the base element
649		var base = this;
650		// initialize (and localize) all variables
651		var $handler = null;
652		var $popover = null;
653		var popoverWrapper = null;
654
655		// PUBLIC FUNCTIONS
656		this.init = function () {
657			$handler = $(this);
658			$popover = $handler.nextAll("div.cpPopOver");
659
660			// Saves current jquery object reference for future references to call public methods if needed
661			if (!$handler.data('cpPopover'))
662				$handler.data('cpPopover', base);
663
664			$handler.unbind("click.cpPopover").bind("click.cpPopover", function (e) {
665				e.preventDefault();
666				e.stopPropagation();
667				if ($popover.length == 0 || options.makeAjaxCallEverytime) {
668					//if popup has not being rendered yet, loaded it only first time
669					base.load();
670				} else {
671					//toggle display
672					base.toggle();
673				}
674			});
675
676			if ($popover.length != 0) {
677				base.initPopOver();
678			}
679		};
680
681		this.initPopOver = function () {
682			$popover = $handler.nextAll("div.cpPopOver");
683
684			//Register popover and store wrap reference
685			popoverWrapper = window.CommonPopoverInstance.RegisterPopover($popover, options);
686
687			//If needed call back onInit event
688			if (options.onInit != null && typeof (options.onInit) == "function")
689				options.onInit(popoverWrapper);
690		};
691
692		this.load = function () {
693			if (options.url && options.url != '') {
694				//load from URL
695				$.ajax({
696					url: options.url,
697					type: options.type,
698					contentType: options.contentType,
699					data: options.data,
700					async: false,
701					cache: options.cache,
702					success: function (response) {
703						if (response.ErrorMessage) {
704							alert(response.ErrorMessage);
705						}
706						else {
707							$handler.after(response);
708							base.initPopOver();
709							base.show();
710						}
711					},
712					error: function (xhr, textStatus, exception) {
713						alert('Error encountered while loading popover.' + "\nStatus: " + xhr.status + "\n" + "Exception: " + exception);
714						$('#ajaxErrorLog').text(xhr.responseText);
715					}
716				});
717			} else if (options.htmlContent && options.htmlContent != '') {
718				//Load specific content //TOOD: ????
719				$handler.after(options.htmlContent);
720				base.initPopOver();
721				base.show();
722			} else {
723				alert('Popover was incorrectly configured to display content.');
724			}
725		};
726
727		this.show = function () {
728			window.CommonPopoverInstance.show(popoverWrapper);
729		};
730
731		this.hide = function () {
732			window.CommonPopoverInstance.hide(popoverWrapper);
733		};
734
735		this.toggle = function () {
736			if (popoverWrapper.isVisible)
737				base.hide();
738			else
739				base.show();
740		};
741
742		this.destroy = function () {
743			if ($popover.length != 0) {
744				$popover.find('.cpClosePopOver').filter(":first").unbind('click');
745				$popover.unbind("click");
746				if($popover.data('draggable'))
747					$popover.draggable('destroy');
748				window.CommonPopoverInstance.RemovePopover(popoverWrapper);
749				$popover.remove();
750			}
751			$handler.unbind("click.cpPopover");
752			$handler.removeData('cpPopover');
753		};
754
755		// PRIVATE FUNCTIONS
756
757		base.init();
758		return this;
759	};
760})(jQuery);
761
762$(function () {
763	if (!window.CommonPopoverInstance) {
764		window.CommonPopoverInstance = new CommonPopoverClass();
765	}
766});
767
768CommonPopoverClass.prototype.resizeToWindow = function ($popup) {
769	var viewportHeight = $(window).height();
770	var toolbarHeight = ($('#divToolbars').height() + $('div.cp-Toolbars').height());
771	var maxHeight = viewportHeight - toolbarHeight - 205;
772	var popupHeight = $popup.height();
773	var popupOffset = $popup.offset();
774	var scrollTop = $popup.scrollTop();
775	var pageScroll = getPageScroll();
776	$popup.find('.cpTabPanel').css('max-height', maxHeight + 'px');
777	$popup.css('top', 20 + 'px'); //This always needs to have a top. So we set it here.
778};
779
780function getPageScroll() {
781	var xScroll, yScroll;
782	if (self.pageYOffset) {
783		yScroll = self.pageYOffset;
784		xScroll = self.pageXOffset;
785	} else if (document.documentElement && document.documentElement.scrollTop) {
786		yScroll = document.documentElement.scrollTop;
787		xScroll = document.documentElement.scrollLeft;
788	} else if (document.body) {// all other Explorers
789		yScroll = document.body.scrollTop;
790		xScroll = document.body.scrollLeft;
791	}
792	return new Array(xScroll, yScroll);
793}
794
795// Adapted from getPageSize() by quirksmode.com
796function getPageHeight() {
797	var windowHeight;
798	if (self.innerHeight) { // all except Explorer
799		windowHeight = self.innerHeight;
800	} else if (document.documentElement && document.documentElement.clientHeight) {
801		windowHeight = document.documentElement.clientHeight;
802	} else if (document.body) { // other Explorers
803		windowHeight = document.body.clientHeight;
804	}
805	return windowHeight;
806}

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.