PageSourceSearch

https://www.heilbronn.de/_assets/99af69b389b627b502895f80363129a3/js/main.js?1790168110

js heilbronn.de collected 2026-09-24 19:46:09 UTC 40,963 bytes, 1,568 lines download raw bytes

1String.prototype.hashCode = function(){
2	var hash = 0;
3	if (this.length == 0) return hash;
4	for (i = 0; i < this.length; i++) {
5		char = this.charCodeAt(i);
6		hash = ((hash<<5)-hash)+char;
7		hash = hash & hash; // Convert to 32bit integer
8	}
9	return hash;
10}
11
12// Opera 8.0+
13var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
14
15// Firefox 1.0+
16var isFirefox = typeof InstallTrigger !== 'undefined';
17
18// Safari 3.0+ "[object HTMLElementConstructor]"
19var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
20
21// Internet Explorer 6-11
22var isIE = /*@cc_on!@*/false || !!document.documentMode;
23
24// Edge 20+
25var isEdge = !isIE && !!window.StyleMedia;
26
27// Chrome 1+
28var isChrome = !!window.chrome && !!window.chrome.webstore;
29
30// Blink engine detection
31var isBlink = (isChrome || isOpera) && !!window.CSS;
32
33// True when the viewport is a desktop.
34var globalIsViewportDesktop = false;
35
36var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
37
38//Timer for resize event.
39var globalResizeTimer = null;
40
41//True when on start page.
42var globalIsStartPage = false;
43
44//Variable for storing the scroll height when opening the menu on smartphones.
45var scrollHeight;
46
47//Object for determining the last viewport size.
48var globalLastViewportSize = {
49
50	xxs: window.matchMedia("(max-width: 575px)"),
51
52	xs: window.matchMedia("(min-width: 576px)"),
53
54	sm: window.matchMedia("(min-width: 768px)"),
55
56	md: window.matchMedia("(min-width: 992px)"),
57
58	lg: window.matchMedia("(min-width: 1200px)"),
59
60	last: '',
61};
62
63var root = document.documentElement;
64
65//Calls the init function when the document is ready.
66jQuery(document).ready(function ()
67{
68	document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
69	init();
70	initAccessiblity();
71});
72
73/*
74	Does all the initialisation stuff
75 */
76function init()
77{
78
79	if(iOS) {
80		var styleTag = document.createElement("style");
81		document.body.appendChild(styleTag);
82		styleTag.innerText = "body {height: auto !important}"
83	}
84
85	/*
86	Gets called, whenever the window is resized.
87	 */
88	jQuery(window).resize(function ()
89	{
90		if (globalResizeTimer != null) window.clearTimeout(globalResizeTimer);
91		globalResizeTimer = window.setTimeout(function ()
92		{
93			resizeHandler();
94		}, 200);
95	});
96
97
98	jQuery(window).scroll(function ()
99	{
100		scrollControll();
101	});
102
103	globalIsViewportDesktop = window.matchMedia("(min-width: 992px)").matches;
104
105	//Sets the height of the gray menu overlay, if the body is higher than 3000px
106	if(jQuery('body').height() > 3000) {
107		setTimeout(function ()
108		{
109			jQuery('.facelift-menu-overlay').height(jQuery('body').height())
110		}, 400)
111	}
112
113	/*
114	Divides the submenu elements into two separate lists, so that the submenu entries can be
115	displayed in two columns next to each other.
116	 */
117
118	let menuItems = jQuery('.sub1 ul.row');
119	menuItems.each(function ()
120	{
121		var counter = 0;
122		var right = jQuery(this).next('.right-col')
123		let children = jQuery(this).children();
124		for(let i = 0; i < children.length; i++) {
125			counter += jQuery(children[i]).children('.sub2').children('ul').children('li').length + 1
126		}
127
128		let dividerCounter = 0;
129
130		for(let i = 0; i < children.length; i++) {
131			if(dividerCounter > (counter / 2)) {
132				jQuery(children[i]).detach().appendTo(right);
133			}
134
135			dividerCounter += jQuery(children[i]).children('.sub2').children('ul').children('li').length + 1.1;
136		}
137	});
138
139	/*
140	Determines if the user is on the start page
141	 */
142	if (jQuery('.facelift-2018').hasClass('startPage'))
143	{
144		globalIsStartPage = true;
145	}
146
147	/*
148	When the user clicks on the search icon in the meta menu, this function opens the
149	offcanvas menu with a true parameter which tells the menu that it should focus on the
150	searchbar.
151	 */
152	jQuery('#header #search .tx-kesearch-pi1 .submit').click(function (e)
153	{
154		e.preventDefault();
155		toggleOffCanvas(true);
156	});
157
158	if(jQuery('.start-slider .frame-type-image .columns-1.single-gallery').length > 0) {
159		headerImageSliderSetup();
160	}
161
162	if(jQuery('.imagemap-wrap').length > 0) {
163		initImageMapSidebarHighlight();
164	}
165
166	if(jQuery('.tt-address-slider').length > 0) {
167		ttAddressSliderSetup();
168	}
169
170	/*
171	Some needed actions for the filter list
172	 */
173	var $filterList = jQuery('#main-content .searchform .kesearch_filter-list'),
174		$filters = jQuery('#main-content .searchform .kesearch_filter-list .optionCheckBox input');
175	if($filterList.length > 0) {
176		var kesearchFilterHeight = jQuery('#main-content .searchform .kesearch_filter-list').get(0).offsetHeight;
177		if(kesearchFilterHeight > 0) {
178			jQuery('#main-content .kesearch_result-list').css({'min-height': kesearchFilterHeight + 'px'});
179		}
180	}
181
182	if($filters.length > 0) {
183		$filters.on('change', function() {
184			jQuery('#main-content .searchform').submit();
185		});
186	}
187
188	jQuery("a[href='#top']").click(function() {
189		jQuery("html, body").animate({ scrollTop: 0 }, "slow");
190		return false;
191	});
192
193	/*
194	This toggles the normal menu opening action when the user clicks on the burger menu
195	 */
196	jQuery('.facelift-2018 #header .toggleOffCanvas').click(function ()
197	{
198		toggleOffCanvas(false);
199	});
200
201	/*
202	Adds functionality to the top-level-menu-points to toggle the corresponding submenu on the desktop viewport.
203	 */
204	jQuery('.facelift-menu-main > ul > li > a').each(function ()
205	{
206		jQuery(this).click(function (e)
207		{
208			toggleSubmenuDesktop(jQuery(this), e)
209		})
210	});
211
212	/*
213	Adds functionality to the plus (+) symbols visible on viewports smaller than 992px width.
214	When the user clicks on the plus symbol next to the top-level-menu-point it opens the corresponding submenu.
215	 */
216	jQuery('.toggleSubMenu1').each(function ()
217	{
218		jQuery(this).click(function (e)
219		{
220			if (window.matchMedia("(max-width: 992px)").matches)
221			{
222				e.preventDefault();
223				toggleSubmenuMobile(jQuery(this), 1)
224			}
225		})
226	});
227
228	/*
229	 Adds functionality to the plus (+) symbols visible on viewports smaller than 992px width.
230	 When the user clicks on the plus symbol next to the top-level-menu-point it opens the corresponding submenu.
231	 */
232	jQuery('.toggleSubMenu2').each(function ()
233	{
234		jQuery(this).click(function (e)
235		{
236			if (window.matchMedia("(max-width: 992px)").matches)
237			{
238				e.preventDefault();
239				toggleSubmenuMobile(jQuery(this), 2)
240			}
241		})
242
243		if (window.matchMedia("(min-width: 992px)").matches) {
244			jQuery(this).attr('aria-expanded','true');
245		}
246	});
247
248	/*
249	 Adds functionality to the plus (+) symbols visible on viewports smaller than 992px width.
250	 When the user clicks on the plus symbol next to the top-level-menu-point it opens the corresponding submenu.
251	 */
252	jQuery('.toggleSubMenu3').each(function ()
253	{
254		jQuery(this).click(function (e)
255		{
256			e.preventDefault();
257			toggleSubmenuMobile(jQuery(this), 3)
258		})
259	});
260
261	/*
262	 Adds functionality to the plus (+) symbols visible on viewports smaller than 992px width.
263	 When the user clicks on the plus symbol next to the top-level-menu-point it opens the corresponding submenu.
264	 */
265	jQuery('.toggleSubMenu4').each(function ()
266	{
267		jQuery(this).click(function (e)
268		{
269			e.preventDefault();
270			toggleSubmenuMobile(jQuery(this), 4)
271		})
272	});
273
274	/*
275	 Adds functionality to the plus (+) symbols visible on viewports smaller than 992px width.
276	 When the user clicks on the plus symbol next to the top-level-menu-point it opens the corresponding submenu.
277	 */
278	jQuery('.toggleSubMenu5').each(function ()
279	{
280		jQuery(this).click(function (e)
281		{
282			e.preventDefault();
283			toggleSubmenuMobile(jQuery(this), 5)
284		})
285	});
286
287	//object-fit polyfill
288	if(isIE || isEdge) {
289		jQuery('.multi-gallery .image a').each(function () {
290			var $container = jQuery(this),
291				imgUrl = $container.find('img').prop('src');
292			if (imgUrl) {
293				$container
294					.css('backgroundImage', 'url(' + imgUrl + ')')
295					.addClass('compat-object-fit');
296			}
297		});
298		if(jQuery(".ce-left .single-gallery").length > 0) {
299			jQuery(".ce-left .single-gallery").css({float: "none"});
300			var width = jQuery(".ce-left .single-gallery img").width();
301			jQuery(".ce-left .single-gallery").css({float: "left", width: width});
302		}
303	}
304
305	jQuery('.last-image-overlay').click(function ()
306	{
307		jQuery(this).remove();
308		jQuery('.hidden-row').css({display: "flex"});
309	})
310
311	jQuery('.replace-https').html(function(){
312		var $inner = jQuery(this).html();
313		$inner = $inner.replace("https://", "");
314		return $inner;
315	})
316
317	jQuery('.citizen-serivce-content .anchor-button').attr('href', "#shopping-content");
318
319	jQuery('.citizen-serivce-content .anchor-button').on('click', function (event) {
320		event.preventDefault();
321
322		jQuery('html, body').animate({
323			scrollTop: jQuery($.attr(this, 'href')).offset().top - 200
324		}, 500);
325	});
326
327	compensateScrollBar("@media (min-width: 768px) {body.menu-open #header {width: calc(100% - #scrollbar#) !important;}");
328	compensateScrollBar("@media (min-width: 768px) {body.menu-open {padding-right: #scrollbar#}}");
329	renderPopUp();
330	generateContactPopUp();
331	imgToSvg();
332
333	accordionSetup();
334
335	if (globalIsStartPage)
336	{
337		sliderSetup();
338	}
339
340	if(jQuery().slick) {
341		jQuery('.slider-gallery').slick({
342			dots: true,
343			infinite: true,
344			speed: 500,
345			arrows: false,
346			autoplay: true,
347			autoplaySpeed: 6000,
348			slidesToShow: window.sliderGallerySlides ?? 2,
349			responsive: [
350				{
351					breakpoint: 1024,
352					settings: {
353						slidesToShow: 1,
354					}
355				},
356			]
357		});
358
359		if(jQuery('.perspective-images--slider').length) {
360			jQuery('.perspective-images--slider').slick({
361				dots: true,
362				infinite: true,
363				speed: 500,
364				arrows: false,
365				autoplay: true,
366				autoplaySpeed: 6000,
367				slidesToShow: 1,
368			});
369		}
370	}
371	if (window.matchMedia("(max-width: 577px)").matches) {
372		if(jQuery('.topics-container--mobile-slider .topic').length) {
373			jQuery('.container > .topics-container--mobile-slider .topic').slick({
374				dots: true,
375				infinite: true,
376				speed: 500,
377				arrows: false,
378				autoplay: true,
379				autoplaySpeed: 6000,
380				centerMode: false,
381				slidesToShow: 1,
382				mobileFirst: true,
383				responsive: [
384					{
385						breakpoint: 576,
386						settings: "unslick"
387					},
388				]
389			});
390		}
391	}
392
393	setLastViewportSize();
394
395	resizeHandler();
396
397	if(window.location.hash.length > 2) {
398		try	{
399			if(jQuery(window.location.hash).hasClass('gridlayout-facelift-accordion-tab')){
400				jQuery(window.location.hash +' header').click();
401			}
402		} catch(e) {
403
404		}
405	}
406
407	/* responsive tables */
408	// element that will be wrapped
409	var tables = document.querySelectorAll('.contenttable, .ce-table');
410
411	if (tables.length > 0) {
412		tables.forEach(function(el){
413			if(el.parentNode.className.indexOf('table-scrollable') == -1) {
414				// create wrapper container
415				var wrapper = document.createElement('div');
416					wrapper.classList.add('table-scrollable');
417
418				// insert wrapper before el in the DOM tree
419				el.parentNode.insertBefore(wrapper, el);
420				// move el into wrapper
421				wrapper.appendChild(el);
422			}
423		});
424	}
425
426	googleOpeningHours();
427
428	initNewsHubGallery();
429	initNewsHubFilterMenu();
430
431	jQuery('a[data-mailto-vector="1"] span').each(function(index, element) {
432		if(element.innerText === '@') {
433			while (element.parentNode && element.parentNode.nodeName !== 'A') {
434				element = element.parentNode;
435			}
436			element.parentNode.classList.add('cnw_skip_translation');
437		}
438	});
439
440	jQuery('#side-icons-expand').on('click', function() {
441		if(jQuery('#side-icons').hasClass('active')) {
442			jQuery('.side-icon-hidden').each(function() {
443				jQuery(this).removeClass('active')
444				jQuery(this).find('a').attr('aria-hidden','true')
445				jQuery(this).find('a').attr('tabindex','-1')
446			})
447
448			setTimeout(() => {
449				jQuery('.side-icon-hidden').each(function() {
450					jQuery('.cnw_translationbox').removeClass('active')
451					jQuery('#side-icons').removeClass('active')
452				})
453			}, 300)
454		} else {
455			jQuery('.cnw_translationbox').addClass('active')
456			jQuery('#side-icons').addClass('active')
457
458			setTimeout(() => {
459				jQuery('.side-icon-hidden').each(function() {
460					jQuery(this).addClass('active')
461					jQuery(this).find('a').removeAttr('aria-hidden')
462					jQuery(this).find('a').removeAttr('tabindex')
463				})
464			}, 100)
465		}
466	})
467
468	jQuery('#toggle-chatbot').on('click', function() {
469		if(jQuery('#chatbot-iframe').hasClass('open')) {
470			jQuery('#chatbot-iframe').removeClass('open')
471			closeChatbot(document.getElementById('chatbot-iframe'))
472		} else {
473			jQuery('#chatbot-iframe').addClass('open')
474			openChatbot(document.getElementById('chatbot-iframe'))
475		}
476	})
477
478	var chatbotOpen = false;
479	var container = document.getElementById("chatbot-wrapper");
480	var iframe = container?.querySelector("iframe");
481	function messageHandler(e) {
482		console.log("Event: " + e.data.type)
483		if(e.data.type === 'chatbotIsOpen') {
484			chatbotOpen = (e.data.message === "true");
485			adjustChatbotSize();
486		}
487	}
488
489	window.addEventListener("message", messageHandler);
490
491	function adjustChatbotSize() {
492		if(chatbotOpen) {
493			container.style.opacity = 1;
494			container.style.width = "min(500px, 100%)";
495			container.style.height = "min(800px, 80%)";
496			iframe.style.width = "min(500px, 100%)";
497			iframe.style.height = "min(800px, 100%)";
498		} else {
499			container.style.opacity = 0;
500			container.style.width = "60px";
501			container.style.height = "60px";
502			iframe.style.width = "60px";
503			iframe.style.height = "60px";
504		}
505	}
506
507	iframe?.addEventListener('load', function() {
508		setTimeout(() => {
509			adjustChatbotSize();
510		}, 1000);
511	});
512
513	initStolpersteineSearch();
514}
515
516function initStolpersteineSearch() {
517	var searches = document.querySelectorAll('[data-stolpersteine-search]');
518	if (!searches.length) {
519		return;
520	}
521
522	searches.forEach(function (searchEl) {
523		var list = searchEl.parentNode.querySelector('[data-stolpersteine-list]');
524		if (!list) {
525			return;
526		}
527
528		var input = searchEl.querySelector('.stolpersteine-search__input');
529		var button = searchEl.querySelector('.stolpersteine-search__button');
530		var resetBtn = searchEl.querySelector('.stolpersteine-search__reset');
531		var emptyMsg = searchEl.querySelector('.stolpersteine-search__empty');
532		var items = list.querySelectorAll('.list-stolperstein');
533
534		var haystacks = [];
535		items.forEach(function (item) {
536			haystacks.push((item.textContent || '').toLowerCase().replace(/\s+/g, ' ').trim());
537		});
538
539		function applyFilter() {
540			var query = (input.value || '').toLowerCase().replace(/\s+/g, ' ').trim();
541			var visible = 0;
542
543			items.forEach(function (item, i) {
544				var match = query === '' || haystacks[i].indexOf(query) !== -1;
545				item.style.display = match ? '' : 'none';
546				if (match) {
547					visible++;
548				}
549			});
550
551			if (emptyMsg) {
552				emptyMsg.hidden = visible !== 0;
553			}
554			if (resetBtn) {
555				resetBtn.hidden = query === '';
556			}
557		}
558
559		function resetFilter() {
560			input.value = '';
561			applyFilter();
562			input.focus();
563		}
564
565		button.addEventListener('click', applyFilter);
566		input.addEventListener('keydown', function (e) {
567			if (e.key === 'Enter') {
568				e.preventDefault();
569				applyFilter();
570			}
571		});
572		input.addEventListener('input', function () {
573			if (input.value === '') {
574				applyFilter();
575			}
576		});
577		if (resetBtn) {
578			resetBtn.addEventListener('click', resetFilter);
579		}
580	});
581}
582
583function openChatbot(iframe) {
584	if (iframe && iframe.contentWindow) {
585		iframe.contentWindow.postMessage(
586			{type: 'toggleBotOpenState', message: "true"},
587			'*'
588		);
589	}
590}
591
592function closeChatbot(iframe) {
593	if (iframe && iframe.contentWindow) {
594		iframe.contentWindow.postMessage(
595			{type: 'toggleBotOpenState', message: "false"},
596			'*'
597		);
598	}
599}
600
601/*
602gets called when a resize happens and moves the meta menu around when the user goes from a smaller to a larger viewport and
603vice versa.
604Also sets the states for the variable to determine weather the user is on a desktop viewport
605 */
606function resizeHandler()
607{
608
609	if(globalLastViewportSize.md.matches) {
610		moveMetaMenu(jQuery('#meta-menu-outer'), 'prepend');
611		globalIsViewportDesktop = true;
612	} else {
613		moveMetaMenu(jQuery('.menu-search-wrapper'), 'append');
614		jQuery('#meta-menu-outer').text("");
615		globalIsViewportDesktop = false;
616	}
617	topicboxHeaderEqualHeight();
618	setLastViewportSize();
619
620	root.style.setProperty('--header-banner-margin', (document.querySelector('.header-banner-important-links')?.offsetHeight / 2 + 20) + "px");
621}
622
623
624/*
625sets the last viewport size after a resize
626 */
627function setLastViewportSize()
628{
629	if (globalLastViewportSize.lg.matches)
630	{
631		globalLastViewportSize.last = 'lg'
632	}
633	else if (globalLastViewportSize.md.matches)
634	{
635		globalLastViewportSize.last = 'md'
636	}
637	else if (globalLastViewportSize.sm.matches)
638	{
639		globalLastViewportSize.last = 'sm'
640	}
641	else if (globalLastViewportSize.xs.matches)
642	{
643		globalLastViewportSize.last = 'xs'
644	}
645	else if (globalLastViewportSize.xxs.matches)
646	{
647		globalLastViewportSize.last = 'xxs'
648	}
649}
650
651/*
652Toggles submenu underneath the given menu-point
653 */
654function toggleSubmenuMobile($this, level)
655{
656	if (!$this.hasClass('open'))
657	{
658		jQuery('.toggleSubMenu' + level).removeClass('open');
659		jQuery('.toggleSubMenu' + level).html('+');
660		jQuery('.sub' + level).slideUp().removeClass('open');
661		$this.html('−');
662		$this.addClass('open');
663		$this.siblings('.sub' + level).addClass('open').slideDown({start: function () {
664				jQuery(this).css({
665					display: "grid"
666				})
667			}});
668		ariaExpandedTrue(jQuery('.toggleSubMenu' + level));
669		jQuery('.toggleSubMenu' + level).attr('aria-label', 'Untermenü schließen');
670	}
671	else
672	{
673		jQuery('.toggleSubMenu' + level).removeClass('open');
674		jQuery('.toggleSubMenu' + level).html('+');
675		jQuery('.sub' + level).slideUp().removeClass('open');
676		ariaExpandedFalse(jQuery('.toggleSubMenu' + level));
677		jQuery('.toggleSubMenu' + level).attr('aria-label', 'Untermenü Öffnen');
678	}
679}
680
681
682/*
683	Toggles submenu when in the menu and on a desktop viewport
684 */
685function toggleSubmenuDesktop($this, e)
686{
687	if (window.matchMedia("(min-width: 992px)").matches)
688	{
689		if (!$this.hasClass('open'))
690		{
691			jQuery('.facelift-menu-main ul li a').removeClass('open');
692			jQuery('.sub1').removeClass('open');
693			e.preventDefault();
694			$this.siblings('.sub1').addClass('open');
695			jQuery('.facelift-menu-overlay').addClass('open');
696			jQuery('.menu-search-wrapper').addClass('left');
697			jQuery('.menu-topic-box ').addClass('right');
698			$this.addClass('open');
699			ariaExpandedTrue($this);
700		}
701		else
702		{
703			jQuery('.sub1').removeClass('open');
704			$this.removeClass('open');
705			jQuery('.facelift-menu-overlay').removeClass('open');
706			jQuery('.menu-search-wrapper').removeClass('left');
707			jQuery('.menu-topic-box ').removeClass('right');
708			e.preventDefault();
709			ariaExpandedFalse($this);
710		}
711	}
712
713	if($this.attr('href').includes('#') && !$this.hasClass('toggleSubMenu')) {
714		toggleOffCanvas();
715	}
716}
717
718
719/*
720Function for opening and closing the offcanvas menu
721 */
722function toggleOffCanvas(focusSearch)
723{
724	if (!jQuery('#facelift-menu').hasClass('open'))
725	{
726		jQuery('body').addClass('menu-open');
727		if (!globalIsViewportDesktop)
728		{
729
730			jQuery('#facelift-menu').addClass('open-mobile');
731		}
732		else
733		{
734			jQuery('#facelift-menu').addClass('open-desktop');
735			if(typeof selectors !== 'undefined' && !focusSearch) {
736				jQuery('.facelift-menu-overlay').addClass('open');
737				jQuery('.menu-search-wrapper').addClass('left');
738				jQuery('.menu-topic-box ').addClass('right');
739				jQuery('.facelift-menu-main > ul > li.active div.sub1, .facelift-menu-main > ul > li.active > a').addClass('open')
740			}
741		}
742		jQuery('#facelift-menu').addClass('open');
743		jQuery('.hamburger').addClass('is-active');
744		if(window.matchMedia("(max-width: 767px)").matches) {
745			scrollOnMenu(true);
746		}
747
748		if(focusSearch) {
749			jQuery('#offCanvasSearch .searchInput').focus();
750		}
751
752	}
753	else
754	{
755		jQuery('#facelift-menu').removeClass('open-desktop');
756		jQuery('#facelift-menu').removeClass('open-mobile');
757		jQuery('#facelift-menu').removeClass('open');
758		jQuery('.facelift-menu-main > ul > li > a').removeClass('open');
759		jQuery('.menu-topic-box ').removeClass('right');
760		jQuery('.sub1').removeClass('open');
761		jQuery('.facelift-menu-overlay').removeClass('open');
762		jQuery('.menu-search-wrapper').removeClass('left');
763		jQuery('.toggleSubMenu').removeClass('open');
764		jQuery('.toggleSubMenu').html('+');
765		jQuery('.hamburger').removeClass('is-active');
766		jQuery('.sub3').slideUp();
767		if(window.matchMedia("(max-width: 767px)").matches) {
768			scrollOnMenu(false);
769		}
770		jQuery('body').removeClass('menu-open');
771
772	}
773}
774
775/*
776Moves meta menu into the offacanvasmenu on smaller viewports and back, when a bigger viewport is reached
777 */
778function moveMetaMenu($destination, direction)
779{
780	switch (direction)
781	{
782		case 'prepend':
783			jQuery('#meta-menu-header').detach().prependTo($destination);
784			jQuery('#meta-menu-header').addClass('col d-flex justify-content-end');
785			break;
786		case 'append':
787			jQuery('#meta-menu-header').detach().appendTo($destination);
788			jQuery('#meta-menu-header').removeClass();
789			break;
790	}
791}
792
793/*
794Setup for the accordion element
795 */
796function accordionSetup()
797{
798	jQuery('.accordion h2').each(function ()
799	{
800		if(!jQuery(this).hasClass('open')) {
801			jQuery(this).addClass('default-hidden');
802			jQuery(this).next('.tab-element').hide();
803		}
804
805		jQuery(this).click(function ()
806		{
807			jQuery(this).toggleClass('open');
808			if ( jQuery(this).hasClass('open') ) {
809				jQuery(this).find('button').attr('aria-expanded','true');
810			} else {
811				jQuery(this).find('button').attr('aria-expanded','false');
812			}
813
814			jQuery(this).next('.tab-element')
815			            .toggleClass('open')
816			            .slideToggle();
817
818			/*	Solution load galleries in accordion correctly */
819			jQuery(this).next('.tab-element').find('.slider-gallery').slick('unslick');
820			jQuery(this).next('.tab-element').find('.slider-gallery').slick({
821				dots: true,
822				infinite: true,
823				speed: 500,
824				arrows: false,
825				autoplay: true,
826				autoplaySpeed: 6000,
827				slidesToShow: 2,
828				responsive: [
829					{
830						breakpoint: 1024,
831						settings: {
832							slidesToShow: 1,
833						}
834					},
835				]
836			}).delay(100);
837
838			if(jQuery(this).hasClass('default-hidden')) {
839				topicboxHeaderEqualHeight();
840				jQuery(this).removeClass('default-hidden')
841			}
842
843		})
844	})
845}
846
847/*
848Setup for the slick slider on the start page
849 */
850function sliderSetup()
851{
852	if(jQuery('.start-slider .header-slide').length > 0) {
853		jQuery('.start-slider').slick({
854			dots: true,
855			infinite: true,
856			speed: 500,
857			fade: true,
858			cssEase: 'linear',
859			arrows: false,
860			autoplay: true,
861			autoplaySpeed: 6000,
862		});
863	} else if (jQuery('.start-slider .header-banner-images picture').length > 1) {
864		jQuery('.start-slider .header-banner-images').slick({
865			dots: true,
866			infinite: true,
867			speed: 500,
868			fade: true,
869			cssEase: 'linear',
870			arrows: true,
871			autoplay: true,
872			autoplaySpeed: 6000,
873			pauseOnHover: true,
874			pauseOnFocus: true,
875			accessibility: true,
876		});
877
878		const slider = jQuery('.start-slider .header-banner-images');
879		const pauseIcon = '<i class="fas fa-pause" aria-hidden="true"></i>';
880		const playIcon  = '<i class="fas fa-play" aria-hidden="true"></i>';
881		const pauseBtn = jQuery('<button type="button" class="slider-play-pause-btn" aria-label="Slideshow pausieren">' + pauseIcon + '</button>');
882
883
884		jQuery('.header-banner-content').append(pauseBtn);
885
886		let isPaused = false;
887
888		pauseBtn.on('click', function () {
889			isPaused = !isPaused;
890			if (isPaused) {
891				slider.slick('slickPause');
892				jQuery(this).attr('aria-label', 'Slideshow abspielen').html(playIcon);
893			} else {
894				slider.slick('slickPlay');
895				jQuery(this).attr('aria-label', 'Slideshow pausieren').html(pauseIcon);
896			}
897		});
898	}
899}
900
901/*
902 Setup for the slick slider on the start page
903 */
904function generalSliderSetup()
905{
906	jQuery('.general-slider').slick({
907		dots: true,
908		infinite: true,
909		speed: 500,
910		arrows: false,
911		autoplay: true,
912		autoplaySpeed: 6000,
913	});
914}
915
916function maskSliderSetup()
917{
918	jQuery('.mask-slick-slider').slick({
919		dots: false,
920		infinite: false,
921		speed: 500,
922		arrows: true,
923		autoplay: false,
924		slidesToShow: 3,
925		prevArrow: '<button type="button" class="slick-prev" aria-label="Vorheriger Slide"><i class="fas fa-chevron-left"></i></button>',
926		nextArrow: '<button type="button" class="slick-next" aria-label="Nächster Slide"><i class="fas fa-chevron-right"></i></button>',
927		responsive: [
928			{
929				breakpoint: 1024,
930				settings: {
931					slidesToShow: 2,
932				}
933			},
934			{
935				breakpoint: 768,
936				settings: {
937					slidesToShow: 1,
938				}
939			}
940		]
941	});
942}
943
944function headerImageSliderSetup()
945{
946	jQuery('.start-slider .frame-type-image .columns-1.single-gallery').slick({
947		dots: true,
948		infinite: true,
949		speed: 500,
950		arrows: false,
951		autoplay: true,
952		autoplaySpeed: 6000,
953	});
954}
955
956
957function ttAddressSliderSetup() {
958	const url = new URL(window.location.href);
959	const address = url.searchParams.get('address');
960
961	jQuery('.tt-address-slider').each(function() {
962		jQuery(this).not('.slick-initialized').slick({
963			dots: false,
964			infinite: true,
965			speed: 500,
966			arrows: true,
967			autoplay: false,
968			slidesToShow: 3,
969			responsive: [
970				{
971					breakpoint: 1024,
972					settings: {
973						slidesToShow: 2,
974					}
975				},
976				{
977					breakpoint: 768,
978					settings: {
979						slidesToShow: 1,
980						adaptiveHeight: true
981					}
982				},
983			]
984		});
985		if(address !== null) {
986			if(jQuery('#address-' + address, this).length > 0) {
987				jQuery(this).slick('slickGoTo', jQuery('#address-' + address, this).data('slick-index'));
988			}
989		}
990	});
991}
992
993/*
994Converts img tags with svg-sources into svgs directly embedded into the html
995 */
996function imgToSvg()
997{
998	jQuery('img.svg').each(function ()
999	{
1000		var $img = jQuery(this);
1001		var imgID = $img.attr('id');
1002		var imgClass = $img.attr('class');
1003		var imgURL = $img.attr('src');
1004
1005		jQuery.get(imgURL, function (data)
1006		{
1007			// Get the SVG tag, ignore the rest
1008			var $svg = jQuery(data).find('svg');
1009
1010			// Add replaced image's ID to the new SVG
1011			if (typeof imgID !== 'undefined')
1012			{
1013				$svg = $svg.attr('id', imgID);
1014			}
1015			// Add replaced image's classes to the new SVG
1016			if (typeof imgClass !== 'undefined')
1017			{
1018				$svg = $svg.attr('class', imgClass + ' replaced-svg');
1019			}
1020
1021			// Remove any invalid XML tags as per http://validator.w3.org
1022			$svg = $svg.removeAttr('xmlns:a');
1023
1024			// Check if the viewport is set, else we gonna set it if we can.
1025			if (!$svg.attr('viewBox') && $svg.attr('height') && $svg.attr('width'))
1026			{
1027				$svg.attr('viewBox', '0 0 ' + $svg.attr('height') + ' ' + $svg.attr('width'))
1028			}
1029
1030			// Replace image with new SVG
1031			$img.replaceWith($svg);
1032
1033		}, 'xml');
1034
1035	});
1036}
1037
1038/*
1039When opening the submenu on mobile viewports this function remembers the current scrollHeight, scrolls to the top of the page.
1040When closing the menu it scrolls back to where the user left off.
1041 */
1042function scrollOnMenu(open)
1043{
1044		/*if(open) {
1045			jQuery('#header').addClass('relative')
1046			scrollHeight = jQuery(window).scrollTop()
1047			jQuery(window).scrollTop(0)
1048		} else {
1049			jQuery('#header').removeClass('relative')
1050			jQuery(window).scrollTop(scrollHeight)
1051		}*/
1052}
1053
1054/*
1055Accessibility
1056*/
1057function initAccessiblity() {
1058	jQuery('#header .toggleOffCanvas').on('keydown', function(e){
1059		var keyCode = e.keyCode || e.which;
1060		if ( keyCode == 9 && jQuery(this).hasClass('is-active')) {
1061			e.preventDefault();
1062			if ( jQuery('#offCanvasSearch').parent().hasClass('left') ) {
1063				jQuery('.facelift-menu-main > ul > li > a').first().focus();
1064			} else {
1065				jQuery('#offCanvasSearch .searchInput').focus();
1066			}
1067		}
1068	});
1069
1070	jQuery('#jumpToMenu').on('click', function(e){
1071		e.preventDefault();
1072		toggleOffCanvas(false);
1073
1074		if ( jQuery('#offCanvasSearch').parent().hasClass('left') ) {
1075			jQuery('.facelift-menu-main > ul > li > a').first().focus();
1076		} else {
1077			jQuery('#offCanvasSearch .searchInput').focus();
1078		}
1079	});
1080
1081	jQuery('#main-content').attr('tabindex','0');
1082	jQuery('#jumpToContent').on('click', function(e){
1083		e.preventDefault();
1084		jQuery('#main-content').focus();
1085	});
1086
1087
1088	jQuery('#offCanvasSearch .searchInput').on('keydown', function(e){
1089		var keyCode = e.keyCode || e.which;
1090		if ( keyCode == 9 && e.shiftKey ) {
1091			e.preventDefault();
1092			jQuery('#header .toggleOffCanvas').focus();
1093		}
1094	});
1095
1096	jQuery('.facelift-menu-main > ul > li > a').first().on('keydown', function(e){
1097		var keyCode = e.keyCode || e.which;
1098		if ( keyCode == 9 && e.shiftKey && jQuery('#offCanvasSearch').parent().hasClass('left') ) {
1099			e.preventDefault();
1100			jQuery('#header .toggleOffCanvas').focus();
1101		}
1102	});
1103
1104	jQuery('.sr-hint').on('focusin', function(event){
1105		jQuery(this).addClass('js-focus');
1106	});
1107
1108	jQuery('.sr-hint').on('focusout', function(event){
1109		jQuery(this).removeClass('js-focus');
1110	});
1111
1112	if(jQuery('.kesearch_filter-list').length > 0) {
1113		jQuery('.kesearch_filter-list input[type=checkbox]').on('focus',function(event){
1114			jQuery(this).addClass('js-focus');
1115		});
1116		jQuery('.kesearch_filter-list input[type=checkbox]').on('blur',function(event){
1117			jQuery(this).removeClass('js-focus');
1118		});
1119	}
1120
1121}
1122
1123function ariaExpandedFalse($this){
1124		$this.parent().find('> a').attr('aria-expanded','false');
1125	}
1126	function ariaExpandedTrue($this){
1127		$this.parent().find('> a').attr('aria-expanded','true');
1128	}
1129
1130var scrollBarController = {
1131	styles: [],
1132	calculatedWidth: null,
1133	width: function(){
1134		if(this.calculatedWidth === null) {
1135			this.calculatedWidth = this.calculate();
1136		}
1137		return this.calculatedWidth;
1138	},
1139	calculate: function(){
1140		var outer = document.createElement("div");
1141		outer.style.visibility = "hidden";
1142		outer.style.width = "100px";
1143		outer.style.msOverflowStyle = "scrollbar";
1144
1145		document.body.appendChild(outer);
1146
1147		var widthNoScroll = outer.offsetWidth;
1148		// force scrollbars
1149		outer.style.overflow = "scroll";
1150
1151		// add innerdiv
1152		var inner = document.createElement("div");
1153		inner.style.width = "100%";
1154		outer.appendChild(inner);
1155
1156		var widthWithScroll = inner.offsetWidth;
1157
1158		// remove divs
1159		outer.parentNode.removeChild(outer);
1160		return  widthNoScroll - widthWithScroll;
1161	},
1162	onResize: function(){
1163		this.calculatedWidth = this.calculate();
1164		for( var i in this.styles) {
1165			this.styles[i].tag.innerText = this.styles[i].style.replace(/#scrollbar#/g, this.width() + "px");
1166		}
1167	}
1168};
1169function compensateScrollBar(style){
1170	var styleTag = document.createElement("style");
1171	document.body.appendChild(styleTag);
1172	styleTag.innerText = style.replace(/#scrollbar#/g, scrollBarController.width() + "px");
1173	scrollBarController.styles.push({
1174		tag: styleTag,
1175		style: style
1176	});
1177}
1178
1179jQuery(document).ready(function(){
1180	jQuery(window).resize(function(){
1181	    scrollBarController.onResize();
1182	});
1183});
1184
1185if(isSafari) {
1186	window.addEventListener('DOMContentLoaded', (event) => {
1187		const { href } = window.location;
1188		if (href.lastIndexOf('#')) {
1189			const id = href.substring(href.lastIndexOf('#'), href.length);
1190			scrollToElement(id);
1191		}
1192	});
1193}
1194
1195async function scrollToElement(id) {
1196	const delay = ms => new Promise(res => setTimeout(res, ms));
1197	await delay(50);
1198
1199	const el = document.querySelector(id);
1200	if(el) {
1201		el.scrollIntoView();
1202	}
1203}
1204
1205
1206function scrollControll()
1207{
1208	var scrollTop;
1209
1210	if(!isSafari) {
1211		scrollTop = jQuery('html').scrollTop();
1212	} else {
1213		scrollTop = jQuery(window).scrollTop();
1214	}
1215
1216
1217	if(scrollTop > 0) {
1218		if(!jQuery('#header').hasClass('scroll-down')) {
1219			jQuery('#header, body').addClass('scroll-down');
1220		}
1221	} else {
1222		if(jQuery('#header').hasClass('scroll-down')) {
1223			jQuery('#header, body').removeClass('scroll-down');
1224		}
1225	}
1226}
1227//Equal Height
1228function topicboxHeaderEqualHeight(callback)
1229{
1230		jQuery('.topics-container .traveloffer .header').height('auto');
1231
1232		// Select and loop the container element of the elements to equalise
1233		jQuery('.topics-container').each(function(){
1234			// Cache the highest
1235			var highestBox = 0;
1236
1237			// Select and loop the elements to equalise
1238			jQuery('.traveloffer .header', this).each(function(){
1239				// If this box is higher than the cached highest then store it
1240				if(jQuery(this).height() > highestBox) {
1241					highestBox = jQuery(this).height();
1242				}
1243			});
1244			// Set the height of all those children to whichever was highest
1245			jQuery('.traveloffer .header',this).height(highestBox);
1246		});
1247
1248		if(typeof(callback) == 'function') {
1249			callback()
1250		}
1251
1252}
1253
1254function setCookie(cname, cvalue, exdays) {
1255	var d = new Date();
1256	d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
1257	var expires = "expires="+d.toUTCString();
1258	document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
1259}
1260
1261function getCookie(cname) {
1262	var name = cname + "=";
1263	var ca = document.cookie.split(';');
1264	for(var i = 0; i < ca.length; i++) {
1265		var c = ca[i];
1266		while (c.charAt(0) == ' ') {
1267			c = c.substring(1);
1268		}
1269		if (c.indexOf(name) == 0) {
1270			return c.substring(name.length, c.length);
1271		}
1272	}
1273	return "";
1274}
1275
1276function contrastCookie()
1277{
1278	var cookie = getCookie('contrast');
1279
1280	if(cookie == "") {
1281			setCookie("contrast", "contrast", 90);
1282	} else {
1283		if(cookie == "contrast") {
1284			setCookie("contrast", "", 90)
1285		} else {
1286			setCookie("contrast", "contrast", 90)
1287		}
1288	}
1289	location.reload();
1290
1291}
1292
1293function renderPopUp() {
1294	const popUpContent = document.getElementById('popup-content');
1295	const $popUpContent = jQuery('#popup-content');
1296
1297	if(!jQuery('body').hasClass('start')) {
1298		$popUpContent.remove();
1299	} else {
1300		if(popUpContent !== null) {
1301			let hasSeenPopUpCookie = getCookie('hasSeenPopUpContent');
1302
1303			if(hasSeenPopUpCookie === "" || $popUpContent.text().trim().hashCode() != hasSeenPopUpCookie) {
1304				popUpContent.classList.add('active');
1305			}
1306			const closeButton = popUpContent.querySelector('.info-banner__teaser');
1307			closeButton.addEventListener('click', togglePopUp);
1308
1309			jQuery(".side-icons-group-wrapper").append("<div style='order: 6' class=\"side-icon popup-toggle\">" +
1310				"<button onclick='togglePopUp()' aria-hidden=\"true\" tabindex=\"-1\" title=\"Popup\" class=\"side-icon-red last\">" +
1311				"<img src=\"/_assets/99af69b389b627b502895f80363129a3/Images/Ausrufezeichen.svg\" height=\"26\" alt=\"\">" +
1312				"</button>" +
1313				"</div>")
1314		}
1315	}
1316}
1317
1318function togglePopUp() {
1319	var $popUpContent = jQuery('#popup-content');
1320
1321	setCookie('hasSeenPopUpContent', $popUpContent.text().trim().hashCode());
1322	const popUpContent = document.getElementById('popup-content');
1323	popUpContent.classList.toggle('active');
1324}
1325
1326function generateContactPopUp() {
1327	const markup = "<div id=\"contact-popup-content\"><div class=\"info-banner__wrapper\">\n"
1328	               + "<div class=\"info-banner\">\n"
1329	               + "<div></div><div class=\"info-banner__text\">\n"
1330	               + "</div>\n"
1331	               + "<div class=\"info-banner__teaser\">Kontakt\n"
1332	               + "<button class=\"info-banner__toggle\">\n"
1333	               + "<i class=\"fa fa-solid fa-plus\"></i>\n"
1334	               + "</button>\n"
1335	               + "</div>\n"
1336	               + "</div>\n"
1337	               + "</div></div>";
1338
1339	let popUp = createElementFromHTML(markup);
1340
1341
1342	let popUpContent = aggregatePopUpContent();
1343
1344	let contentAdded = false;
1345
1346	popUpContent.forEach(nodes => {
1347		nodes.forEach(node => {
1348			contentAdded = true;
1349			popUp.querySelector('.info-banner__text').innerHTML += node.innerHTML;
1350			if(node.id !== 'subpage-popup-content') {
1351				node.classList.add('d-none');
1352				node.classList.add('d-md-block');
1353			} else {
1354				node.remove();
1355			}
1356		})
1357	})
1358
1359	if(contentAdded) {
1360		const closeButton = popUp.querySelector('.info-banner__teaser');
1361		closeButton.addEventListener('click', () => popUp.classList.toggle('active'));
1362
1363		document.querySelector('.facelift-2018').append(popUp);
1364	}
1365}
1366function aggregatePopUpContent() {
1367	let cizizenServiceContent = document.querySelectorAll('.citizen-container .sidebar');
1368	let subpageContent = document.querySelectorAll('#subpage-popup-content');
1369
1370	return [cizizenServiceContent, subpageContent];
1371}
1372
1373function createElementFromHTML(htmlString) {
1374	var div = document.createElement('div');
1375	div.innerHTML = htmlString.trim();
1376
1377	return div.firstChild;
1378}
1379
1380function handleOpeningHours(member) {
1381	if(!member.classList.contains('opening-hours-loaded')) {
1382
1383		member.querySelector('.lds-dual-ring').classList.remove('d-none');
1384		member.querySelector('img').classList.add('d-none');
1385
1386		jQuery.ajax({
1387			url: openingHoursAjaxUrl, data: {
1388				tx_heilbronn_shopping: {
1389					googleid: member.dataset.google
1390				}
1391			},
1392			success: function(data) {
1393				var openingHoursData = null;
1394				if (data && data !== 'null') {
1395					if(typeof data == 'object') {
1396						openingHoursData = data;
1397					} else {
1398						try {
1399							openingHoursData = JSON.parse(data);
1400						} catch(err) {
1401							openingHoursData = null;
1402						}
1403					}
1404				}
1405
1406				member.querySelector('.d-none').classList.remove('d-none');
1407				member.querySelector('.lds-dual-ring').remove();
1408				member.classList.add('opening-hours-loaded', 'popup-active');
1409
1410				if(openingHoursData) {
1411					member.querySelector('.opening-hours').innerHTML = openingHoursData.openingStatus;
1412					member.querySelector('.full-opening-hours').innerHTML = openingHoursData.officeHours;
1413				} else {
1414					member.querySelector('.opening-hours').innerHTML = "Status unbekannt";
1415					member.querySelector('.full-opening-hours').innerHTML = "Leider konnten die Öffnungszeiten nicht ermittelt werden.";
1416				}
1417			}
1418		});
1419	} else {
1420		member.classList.toggle('popup-active');
1421	}
1422}
1423
1424function googleOpeningHours() {
1425	var openingHoursNodes = document.querySelectorAll('[data-google]');
1426
1427	openingHoursNodes.forEach(member => {
1428		member.addEventListener('click', () => handleOpeningHours(member));
1429		member.addEventListener('mouseover', () => handleOpeningHours(member));
1430	});
1431
1432	document.addEventListener('click', e => {
1433		document.querySelectorAll('[data-google].popup-active').forEach(member => {
1434			if (!member.contains(e.target)) {
1435				member.classList.remove('popup-active');
1436			}
1437		});
1438	});
1439}
1440
1441function initNewsHubGallery() {
1442	if(jQuery('.news-hub-gallery').length && jQuery('.news-hub-gallery').children().length > 1) {
1443		jQuery('.news-hub-gallery').slick({
1444			infinite: true,
1445			speed: 500,
1446			arrows: true,
1447			autoplay: false,
1448			slidesToShow: 1,
1449			dots: true,
1450			dotsClass: 'slider-paging-number',
1451			customPaging: function (slider, index) {
1452				return (index + 1) + ' ' + '<span>/' + ' ' + '0' + slider.slideCount + '</span>';
1453			},
1454		});
1455	}
1456}
1457
1458function initNewsHubFilterMenu() {
1459	const flyOutMenu = document.getElementById('news-hub__fly-out-menu');
1460	const flyOutMenuBackground = document.getElementById('fly-out-menu__background');
1461	if(flyOutMenu !== null) {
1462		flyOutMenu.querySelector('.menu__close button').addEventListener('click', function() {
1463			flyOutMenu.querySelector('.fly-out-menu__menu').classList.remove('active');
1464			flyOutToggle.setAttribute('aria-expanded', false);
1465			setTimeout(function(){
1466				flyOutMenu.classList.remove('active');
1467			}, 400);
1468		});
1469
1470		flyOutMenuBackground.addEventListener('click', function() {
1471			flyOutMenu.querySelector('.fly-out-menu__menu').classList.remove('active');
1472			flyOutToggle.setAttribute('aria-expanded', false);
1473			setTimeout(function(){
1474				flyOutMenu.classList.remove('active');
1475			}, 400);
1476		});
1477
1478		const flyOutToggle = document.getElementById('fly-out-menu-toggle');
1479
1480		if(flyOutToggle !== null) {
1481			flyOutToggle.addEventListener('click', function() {
1482				flyOutMenu.classList.add('active');
1483
1484				flyOutToggle.setAttribute('aria-expanded', true);
1485
1486
1487				setTimeout(function(){
1488					flyOutMenu.querySelector('.fly-out-menu__menu').classList.add('active');
1489				}, 1);
1490			})
1491		}
1492	}
1493}
1494
1495function resetNewsHubFilter(button) {
1496	const form = document.getElementById('newshub__filter-form');
1497
1498	form.querySelectorAll('select').forEach(select => {
1499		for(let option of select.options) {
1500			option.removeAttribute('selected')
1501			if(option.value == '') {
1502				option.removeAttribute('disabled')
1503				option.setAttribute('selected', '')
1504				option.setAttribute('disabled', '')
1505				option.value = "";
1506			}
1507		}
1508		select.selectedIndex = -1
1509	} )
1510
1511	form.submit()
1512}
1513function resetShopAndGastroFinder(button) {
1514	const categoryFilters = button.closest('.category-filters')
1515	const form = button.closest('form')
1516
1517	categoryFilters.querySelectorAll('select').forEach(select => {
1518		select.selectedIndex = 0
1519	})
1520
1521	categoryFilters.querySelectorAll('input[type=checkbox]').forEach(checkbox => {
1522		checkbox.checked = false
1523	})
1524
1525	form.querySelectorAll('input[type=hidden][name=category]').forEach(input => {
1526		input.remove()
1527	})
1528
1529	form.submit()
1530}
1531
1532
1533function initImageMapSidebarHighlight()
1534{
1535	var $sidebarLinks = jQuery('.imagemap-sidebar a[href]');
1536
1537	if ($sidebarLinks.length === 0) {
1538		return;
1539	}
1540
1541	jQuery('.imagemap-wrap map area[href]').each(function ()
1542	{
1543		var area = this;
1544		var $menuItem = null;
1545
1546		$sidebarLinks.each(function ()
1547		{
1548			if (this.href === area.href) {
1549				$menuItem = jQuery(this).closest('li');
1550				return false;
1551			}
1552		});
1553
1554		if ($menuItem === null || $menuItem.length === 0) {
1555			return;
1556		}
1557
1558		jQuery(area).on('mouseenter focus', function ()
1559		{
1560			$menuItem.addClass('hover');
1561		});
1562
1563		jQuery(area).on('mouseleave blur', function ()
1564		{
1565			$menuItem.removeClass('hover');
1566		});
1567	});
1568}

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.