/*!
 * JS for the ImmobilienScout24 Core Framework Documentation, v3.3.0
 * http://www.immobilienscout24.de/
 */
var IS24 = IS24 || {}; // eslint-disable-line @typescript-eslint/no-use-before-define

IS24.core = IS24.core || {};
IS24.core.documentation = IS24.core.documentation || {};

/**
 * Turns demo HTML snippets into escaped markup examples.
 *
 * Run this before the pretty-printer and before synchronization of the demo boxes.
 *
 * On the code example (`<pre>`), apply the `data-target-id` attribute with the HTML snippet container's ID as a value.
 *
 * <pre data-target-id="arbitrary-id">
 * <div id="arbitrary-id">
 *     <span>Stuff to be turned into a code example</span>
 * </div>
 *
 * Turn all affected elements on the page into code examples with `IS24.core.documentation.codeExamples.init()`.
 *
 * Restrictions: The origin snippet needs to be consistently indented, so that every line starts with the same sequence of whitespace characters.
 */
IS24.core.documentation.codeExamples = (function () {
	"use strict";

	var MARKUP_EXAMPLE_CONFIG_ATTRIBUTE = "data-target-id";
	var MARKUP_EXAMPLE_SELECTOR = "[" + MARKUP_EXAMPLE_CONFIG_ATTRIBUTE + "]";

	function hasRequiredDOMAPIs() {
		return (
			"querySelectorAll" in document &&
			"forEach" in Array.prototype &&
			"filter" in Array.prototype &&
			"trim" in String.prototype
		);
	}

	function isFirstLineEmpty(lines) {
		return (lines.length > 0 && lines[0].trim().length === 0);
	}

	function isLastLineEmpty(lines) {
		return (lines.length > 0 && lines[lines.length - 1].trim().length === 0);
	}

	function trimLeadingAndTrailingEmptyLines(markupString) {
		var markupLines = markupString.split("\n");

		while (isFirstLineEmpty(markupLines)) {
			markupLines.shift();
		}
		while (isLastLineEmpty(markupLines)) {
			markupLines.pop();
		}

		return markupLines.join("\n");
	}

	function extractFirstLeadingWhitespace(markupString) {
		var markupLines = markupString.split("\n");
		var firstLine = markupLines[0];
		var leadingWhiteSpace = firstLine.match(/^\s+/);

		return (leadingWhiteSpace) ? leadingWhiteSpace[0] : undefined;
	}

	function stripLeadingWhitespace(markupString, whiteSpaceToStrip) {
		var leadingWhiteSpace = new RegExp("^" + whiteSpaceToStrip);
		var markupLines = markupString.split("\n");

		return markupLines
			.map(function (line) {
				return line.replace(leadingWhiteSpace, "");
			})
			.join("\n");
	}

	function escapeMarkup(unescapedMarkup) {
		return unescapedMarkup
			.replace((/</g), "&lt;")
			.replace((/>/g), "&gt;")
			.replace((/"/g), "&quot;")
			.replace((/'/g), "&#39;")
			.replace((/\t/g), "  ");
	}

	function writeMarkupToCodeSnippet(htmlSnippetNode, markupExampleNode) {
		var trimmedOriginalMarkup = trimLeadingAndTrailingEmptyLines(htmlSnippetNode.innerHTML);

		if (trimmedOriginalMarkup.length > 0) {
			const leadingWhiteSpace = extractFirstLeadingWhitespace(trimmedOriginalMarkup);
			const unindentedMarkup = stripLeadingWhitespace(trimmedOriginalMarkup, leadingWhiteSpace);
			const escapedMarkup = escapeMarkup(unindentedMarkup);

			markupExampleNode.innerHTML = escapedMarkup;
		}
	}

	function init() {
		var markupExamplesNodeList, markupExamples;

		if (!hasRequiredDOMAPIs()) { return; }

		markupExamplesNodeList = document.querySelectorAll(MARKUP_EXAMPLE_SELECTOR);
		markupExamples = Array.prototype.slice.call(markupExamplesNodeList);

		markupExamples
			.map(function (markupExampleNode) {
				var targetedHTMLSnippetContainerID = markupExampleNode.getAttribute(MARKUP_EXAMPLE_CONFIG_ATTRIBUTE);
				var targetedHTMLSnippetContainer = document.getElementById(targetedHTMLSnippetContainerID);

				return {
					markupExampleContainer: markupExampleNode,
					htmlSnippetContainer: targetedHTMLSnippetContainer
				};
			})
			.forEach(function (markupExampleAndSnippetMapping) {
				writeMarkupToCodeSnippet(markupExampleAndSnippetMapping.htmlSnippetContainer, markupExampleAndSnippetMapping.markupExampleContainer);
			});
	}

	return {
		init: init
	};
}());

var IS24 = IS24 || {}; // eslint-disable-line @typescript-eslint/no-use-before-define

IS24.core = IS24.core || {};
IS24.core.documentation = IS24.core.documentation || {};

/**
 * Script for interactive demo boxes. Allows adjustment of demo snippets by toggling through a predefined list of classes.
 *
 * <div class="js-code-example-demo-box">
 *     <button class="js-code-example-demo-box-toggle" data-class-list="[&quot;class1&quot;, &quot;class2&quot;, &quot;class3&quot;]">
 *     <figure>
 *         <pre>
 *             &lt;div class="some-class <span class="code-example-demo-box-toggled-string js-code-example-demo-box-toggled-string atv"></span>"&gt;&lt;/div&gt;
 *         </pre>
 *     </figure>
 *     <div class="some-class js-code-example-demo-box-toggled-element"></div>
 * </div>
 */
IS24.core.documentation.demoBoxes = (function () {
	"use strict";
	
	var demoBoxClassName               = "js-code-example-demo-box",
		demoBoxToggleClassName         = "js-code-example-demo-box-toggle",
		demoBoxToggledElementClassName = "js-code-example-demo-box-toggled-element",
		demoBoxToggledStringClassName  = "js-code-example-demo-box-toggled-string";
	
	var demoBoxes;
	
	function getNextClass(classList, currentlyActiveClass) {
		var nextClassIndex = classList.indexOf(currentlyActiveClass) + 1,
			nextClass = (nextClassIndex < classList.length && nextClassIndex > 0) ? classList[nextClassIndex] : classList[0];
		
		return nextClass;
	}
	
	function getClassList(toggle) {
		var configuredClassList = JSON.parse(toggle.getAttribute("data-class-list"));
		
		return (configuredClassList) ? configuredClassList : [];
	}
	
	function switchContent(previousClassName, nextClassName, toggledElements, toggledStrings) {
		var i, l;
		
		for (i = 0, l = toggledElements.length; i < l; i++) {
			if (previousClassName) {
				toggledElements[i].classList.remove(previousClassName);
			}
			if (nextClassName) {
				toggledElements[i].classList.add(nextClassName);
			}
		}
		
		for (i = 0, l = toggledStrings.length; i < l; i++) {
			toggledStrings[i].setAttribute("data-content", nextClassName);
		}
	}
	
	function initDemoBox(demoBox) {
		var toggle          = demoBox.getElementsByClassName(demoBoxToggleClassName)[0],
			toggledElements = demoBox.getElementsByClassName(demoBoxToggledElementClassName),
			toggledStrings  = demoBox.getElementsByClassName(demoBoxToggledStringClassName);
		
		var classList;
		
		var currentlyActiveClass,
			nextClass;
		
		if (!(toggle && toggledElements && toggledStrings)) { return; }
		
		classList = getClassList(toggle);
		
		// Set initial state
		nextClass = classList[0];
		switchContent(null, nextClass, toggledElements, toggledStrings);
		currentlyActiveClass = classList[0];
		
		// Toggle on each button click
		toggle.addEventListener("click", function () {
			nextClass = getNextClass(classList, currentlyActiveClass);
			switchContent(currentlyActiveClass, nextClass, toggledElements, toggledStrings);
			currentlyActiveClass = getNextClass(classList, currentlyActiveClass);
		});
	}
	
	function init() {
		var i, l;
		
		if (!("getElementsByClassName" in document && "classList" in document.documentElement)) { return; }
		
		demoBoxes = document.getElementsByClassName(demoBoxClassName);
		
		for (i = 0, l = demoBoxes.length; i < l; i++) {
			initDemoBox(demoBoxes[i]);
		}
	}
	
	return {
		init: init
	};
}());

var IS24 = IS24 || {}; // eslint-disable-line @typescript-eslint/no-use-before-define

IS24.core = IS24.core || {};
IS24.core.documentation = IS24.core.documentation || {};
IS24.core.documentation.interactiveCodeSnippets = IS24.core.documentation.interactiveCodeSnippets || {};

/**
 * Interactive Code Examples
 *
 * Initiate with IS24.core.documentation.interactiveCodeSnippets.initializeSnippets.init();
 *
 * Runs on all elements with the "interactive-code-snippet" class.
 *
 * On change, transfers code snippet to the target element designated through the "data-target-id" attribute. Omitting this attribute will simply make the snippet interactive and allow for easier copy & paste.
 *
 * Simplified Markup:
 * <div>
 *     <pre class="prettyprint interactive-code-snippet" data-target-id="[arbitrary-target-id]">
 * </div>
 * <div id="[arbitrary-target-id]"></div>
 */
(function () {
	"use strict";
	
	var helpers = {
			hasNecessaryDOMAPIs: function (apiNames) {
				var hasNecessaryAPIs = true,
					i;
				
				for (i = 0; i < apiNames.length; i++) {
					if (!(apiNames[i] in document.body)) {
						hasNecessaryAPIs = false;
						break;
					}
				}
				
				return hasNecessaryAPIs;
			}
		},
		hideClassName = "hide";
	
	IS24.core.documentation.interactiveCodeSnippets.transferSnippetContent = (function () {
		var throttle,
			throttleDelay = 200;
		
		function extractSnippetText(snippet) {
			return snippet.innerHTML;
		}
		
		function decodeSnippetText(snippetText) {
			return snippetText.replace(/<br>/g, "") // filter out unwanted line breaks introduced through formatting
			                  .replace(/\t/g, "    ")
			                  .replace(/&lt;/g, "<")
			                  .replace(/&gt;/g, ">")
			                  .replace(/&amp;/g, "&");
		}
		
		function transferTextToTarget(target, snippetText) {
			if (target) {
				target.innerHTML = snippetText;
			}
		}
		
		function applyPrettyPrint(encodedTarget) {
			encodedTarget.classList.remove("prettyprinted");
			if (window.prettyPrint) {
				window.prettyPrint();
			}
		}
		
		function changeListener(snippet, encodedTarget, decodedTarget) {
			var snippetText = extractSnippetText(snippet),
				decodedSnippetText = decodeSnippetText(snippetText);
			
			transferTextToTarget(encodedTarget, snippetText);
			transferTextToTarget(decodedTarget, decodedSnippetText);
			
			applyPrettyPrint(encodedTarget);
		}
		
		function throttledChangeListener(snippet, encodedTarget, decodedTarget) {
			if (throttle) {
				window.clearTimeout(throttle);
			}
			throttle = window.setTimeout(function () {
				changeListener(snippet, encodedTarget, decodedTarget);
			}, throttleDelay);
		}
		
		function addSnippetChangeListener(snippet, encodedTarget, decodedTarget) {
			snippet.addEventListener("input", function () {
				throttledChangeListener(snippet, encodedTarget, decodedTarget);
			});
			// IE:
			snippet.addEventListener("textinput", function () {
				throttledChangeListener(snippet, encodedTarget, decodedTarget);
			});
		}
		
		function init(snippet, encodedTarget, decodedTarget) {
			if (!helpers.hasNecessaryDOMAPIs(["addEventListener"])) { return; }
			
			addSnippetChangeListener(snippet, encodedTarget, decodedTarget);
		}
		
		return {
			init: init
		};
	}());
	
	IS24.core.documentation.interactiveCodeSnippets.swapSnippets = (function () {
		function setFocusOnSnippet(snippet) {
			snippet.focus();
		}
		
		function setScrollPosition(referenceSnippet, otherSnippet) {
			otherSnippet.scrollTop = referenceSnippet.scrollTop;
			otherSnippet.scrollLeft = referenceSnippet.scrollLeft;
		}
		
		function toggle(snippet, prettyprintSnippet) {
			snippet.classList.remove(hideClassName);
			setScrollPosition(prettyprintSnippet, snippet); // must be called in between because scrollTop/scrollLeft do not apply to hidden containers
			prettyprintSnippet.classList.add(hideClassName);
		}
		
		function untoggle(snippet, prettyprintSnippet) {
			prettyprintSnippet.classList.remove(hideClassName);
			setScrollPosition(snippet, prettyprintSnippet); // must be called in between because scrollTop/scrollLeft do not apply to hidden containers
			snippet.classList.add(hideClassName);
		}
		
		function attachToggleListener(snippet, prettyprintSnippet) {
			prettyprintSnippet.addEventListener("click", function () {
				toggle(snippet, prettyprintSnippet);
				setFocusOnSnippet(snippet);
			});
		}
		
		function attachUntoggleListener(snippet, prettyprintSnippet) {
			snippet.addEventListener("blur", function () {
				untoggle(snippet, prettyprintSnippet);
			});
		}
		
		function init(snippet, prettyprintSnippet) {
			if (!helpers.hasNecessaryDOMAPIs(["addEventListener", "classList", "querySelectorAll"])) { return; }
			
			attachToggleListener(snippet, prettyprintSnippet);
			attachUntoggleListener(snippet, prettyprintSnippet);
			
			untoggle(snippet, prettyprintSnippet); // initial
		}
		
		return {
			init: init
		};
	}());
	
	IS24.core.documentation.interactiveCodeSnippets.initializeSnippets = (function () {
		var snippetClassName = "interactive-code-snippet",
			editableSnippetClassName = "interactive-code-snippet--editable",
			dataAttributeTarget = "data-target-id";
		
		function insertEditableSnippet(snippet) {
			var editableSnippet = document.createElement("pre");
			
			editableSnippet.contentEditable = true;
			editableSnippet.className = editableSnippetClassName + " " + hideClassName;
			editableSnippet.setAttribute("style", snippet.getAttribute("style"));
			editableSnippet.innerHTML = snippet.innerHTML;
			
			return snippet.parentNode.insertBefore(editableSnippet, snippet.nextSibling);
		}
		
		function initiateSnippetContentTransfer(snippets) {
			var editableSnippet,
				target,
				i;
			
			for (i = 0; i < snippets.length; i++) {
				editableSnippet = insertEditableSnippet(snippets[i]);
				target = document.getElementById(snippets[i].getAttribute(dataAttributeTarget));
				IS24.core.documentation.interactiveCodeSnippets.transferSnippetContent.init(editableSnippet, snippets[i], target);
				IS24.core.documentation.interactiveCodeSnippets.swapSnippets.init(editableSnippet, snippets[i]);
			}
		}
		
		function init() {
			var snippets;
			
			if (!helpers.hasNecessaryDOMAPIs(["addEventListener", "getElementsByClassName"])) { return; }
			
			snippets = document.getElementsByClassName(snippetClassName);
			
			initiateSnippetContentTransfer(snippets);
		}
		
		return {
			init: init
		};
	}());
}());
