/*!
 * ImmobilienScout24 Core JS, v3.0.0-12
 * http://www.immobilienscout24.de/
 */
var IS24 = IS24 || {};
IS24.core = IS24.core || {};

(function (window, document) {
"use strict";

/* --- Helper Functions for Core Framework Scripts --- */

var helpers = {
	documentReady: function(callback) {
		if (document.addEventListener) {
			document.addEventListener('DOMContentLoaded', callback);
		} else if (document.attachEvent) {
			document.attachEvent('onreadystatechange', function() {
				if (document.readyState === 'complete') {
					callback();
				}
			});
		}
	},
	
	addEventListener: function (targetElement, eventType, handler, useCapture) {
		if (targetElement.addEventListener) {
			targetElement.addEventListener(eventType, handler, useCapture);
			return true;
		} else if (targetElement.attachEvent) {
			return targetElement.attachEvent("on" + eventType, handler);
		}
	},
	
	/* 
	 * @returns obj { top: <int TopOffset>, left: <int LeftOffset>}
	 */
	getPageScroll: function () {
		var scroll = { top: 0, left: 0 };
		
		if ("scrollTop" in document.documentElement && "scrollLeft" in document.documentElement) {
			// get sum of document and body offset so it doesn't matter which of the two the browser scrolls - either one should be zero
			scroll.top  = document.documentElement.scrollTop  + document.body.scrollTop;
			scroll.left = document.documentElement.scrollLeft + document.body.scrollLeft;
		} else if ("pageYOffset" in document.body && "pageXOffset" in document.body) {
			scroll.top  = document.body.pageYOffset;
			scroll.left = document.body.pageXOffset;
		}
		
		return scroll;
	},
	
	/* 
	 * Determines coordinates of an element within the document.
	 * @returns obj { x: <int horizontalCoordinate>, y: <int verticalCoordinate>}
	 */
	getCoordinates: function (element) {
		var boundingRect = (element.getBoundingClientRect() ? element.getBoundingClientRect() : { top: 0, left: 0 }),
			coordinates = {};
		
		coordinates.x = parseInt(boundingRect.left + this.getPageScroll().left, 10);
		coordinates.y = parseInt(boundingRect.top  + this.getPageScroll().top, 10);
		
		return coordinates;
	},
	
	hasClass: function (element, wantedClass) {
		var classString,
			classMatch;
		
		if (!element || !wantedClass) { return; }
		
		classString = (" " + element.className + " ").replace(/\s+/g, " "); // makes sure all class names are padded by single white spaces
		classMatch = new RegExp("\\s" + wantedClass + "\\s", "g");
		
		return !!classString.match(classMatch);
	},
	
	addClass: function (element, addedClass) {
		var elementHasClass = this.hasClass(element, addedClass);
		
		if (!elementHasClass) {
			element.className += " " + addedClass;
		}
	},
	
	removeClass: function (element, removedClass) {
		var elementHasClass = this.hasClass(element, removedClass);
		
		if (elementHasClass) {
			element.className = (" " + element.className + " ").replace(" " + removedClass + " ", " ").replace(/^\s+|\s+$/g, ''); // remove class, trim
		}
	},
	
	toggleClass: function (element, toggledClass) {
		var elementHasClass = this.hasClass(element, toggledClass);
		
		if (elementHasClass) {
			this.removeClass(element, toggledClass);
		} else {
			this.addClass(element, toggledClass);
		}
	},
	
	nearestAncestor: function (element, ancestorSelector) {
		var currentNode = element,
			matchedAncestor,
			isClassSelector = (ancestorSelector.indexOf(".") === 0);
		
		ancestorSelector = (isClassSelector) ? ancestorSelector.substring(1) : ancestorSelector;
		
		if (isClassSelector) {
			while (currentNode.parentNode) {
				currentNode = currentNode.parentNode;
				if (this.hasClass(currentNode, ancestorSelector)) {
					matchedAncestor = currentNode;
					break;
				}
			}
		} else {
			while (currentNode.parentNode) {
				currentNode = currentNode.parentNode;
				if (currentNode.nodeName.toLowerCase() === ancestorSelector.toLowerCase()) {
					matchedAncestor = currentNode;
					break;
				}
			}
		}
		
		return matchedAncestor;
	},
	
	/*
	 * @returns obj { hostAndPath: string, search: string, hash: string }
	 * query and fragment are called "search" and "hash" for compatibility with window.location
	 */
	resolveURLString: function(URLString) {
		var hasSearch = (URLString.indexOf("?") !== -1),
			hasHash = (URLString.indexOf("#") !== -1);
		
		return {
			hostAndPath: (hasSearch) ? URLString.split("?")[0] : URLString.split("#")[0],
			search: (hasSearch) ? URLString.substring(URLString.indexOf("?")).split("#")[0] : "",
			hash: (hasHash) ? URLString.substring(URLString.indexOf("#")) : ""
		};
	},
	
	/* 
	 * Attaches an arbitrary number of URL parameters to the href attribute of an element, overwrites previous occurrences
	 * @argument DOMElement element
	 * @argument obj addedParameters { parameterName: parameterValue }
	 */
	attachLinkParameters: function(element, addedParameters) {
		var sanitizeRegExp = /[^\w\-\s.,+%@&="\[\]]/g,
			sanitizedAddedParameters = {},
			sanitizedName,
			sanitizedValue,
			resolvedHref,
			currentParameters,
			currentParameterName,
			parameterString,
			i, key;
		
		if (!(element && element.href) || !addedParameters || typeof addedParameters !== "object") { return; }
		
		// sanitize input
		for (key in addedParameters) {
			if (addedParameters.hasOwnProperty(key)) {
				sanitizedName = key.replace(sanitizeRegExp, "");
				sanitizedValue = addedParameters[key].replace(sanitizeRegExp, "");
				sanitizedAddedParameters[sanitizedName] = sanitizedValue;
			}
		}
		
		// resolve href, separate parameters
		resolvedHref = this.resolveURLString(element.href);
		currentParameters = (resolvedHref.search) ? resolvedHref.search.substring(1).split("&") : [];
		
		// remove duplicates from current parameters
		for (i=0; i<currentParameters.length; i++) {
			currentParameterName = currentParameters[i].split("=")[0];
			if (sanitizedAddedParameters[currentParameterName]) {
				currentParameters.splice(i, 1);
			}
		}
		
		// add new parameters, stringify
		for (key in sanitizedAddedParameters) {
			if (sanitizedAddedParameters.hasOwnProperty(key)) {
				currentParameters.push(key + "=" + sanitizedAddedParameters[key]);
			}
		}
		parameterString = "?" + currentParameters.join("&");
		
		// insert
		element.href = resolvedHref.hostAndPath + parameterString + resolvedHref.hash;
	}
};
/* --- Public Helpers --- */
// (mainly for mocking in unit tests)

IS24.core.helpers = {
	loadScript: function (url) {
		var scriptElement = document.createElement('script');
		scriptElement.type = "text/javascript";
		scriptElement.src = url;
		document.getElementsByTagName('head')[0].appendChild(scriptElement);
	},
	
	getLocation: function () {
		return window.location;
	}
};
/* --- Fixed Elements --- */
/*
 *  Will make an element stick to the top of the window when the browser scrolls past it.
 *  Initiated through "IS24.core.fixElementToViewportTop.init()".
 *  Required Markup:
 *  <div class="fix-to-top-wrapper">
 *      <div class="fix-to-top"></div>
 *  </div>
 */

IS24.core.fixElementToViewportTop = (function () {
	var fixedElementWrapper,
		fixedElement,
		isFixedClass,
		fixedPoint,
		currentOffset,
		isFixed = false,
		throttle,
		throttleTimeout = 1000;
	
	function updateCurrentOffset() {
		currentOffset = helpers.getPageScroll().top;
	}
	function updateFixedPoint() {
		fixedPoint = helpers.getCoordinates(fixedElementWrapper).y;
	}
	
	function toggleFixed() {
		if (currentOffset > fixedPoint && !isFixed) {
			helpers.addClass(fixedElement, isFixedClass);
			isFixed = true;
		} else if (currentOffset < fixedPoint && isFixed) {
			helpers.removeClass(fixedElement, isFixedClass);
			isFixed = false;
		}
	}
	
	// selective scroll handler to reduce computing time if scroll events are fired in rapid succession
	function throttledHandler() {
		if (!throttle) {
			throttle = window.setTimeout(function () {
				throttle = null;
			}, throttleTimeout);
			
			// put stuff here that should only run every so often
			updateFixedPoint();
		}
		// put stuff here that needs to run every time for responsiveness
		updateCurrentOffset();
		toggleFixed();
	}
	
	function init() {
		fixedElementWrapper = document.querySelector(".fix-to-top-wrapper");
		fixedElement = document.querySelector(".fix-to-top");
		isFixedClass = "fix-to-top--fixed";
		
		if (!fixedElementWrapper || !fixedElement) { return; }
		
		helpers.addEventListener(window, "resize", throttledHandler);
		helpers.addEventListener(window, "scroll", throttledHandler);
		
		// initial execution
		updateFixedPoint();
		updateCurrentOffset();
		toggleFixed();
	}
	
	return { init: init };
}());

/* --- SSO Login Status --- */

IS24.core.sso = (function () {
	function getFirstAndSecondLevelDomain() {
		var host = IS24.core.helpers.getLocation().hostname,
			hostWithoutFirstLevel,
			secondLevelDotIndex;
		
		// trim leading and trailing dots
		while (host.slice(0,1) === ".") { host = host.slice(1); }
		while (host.slice( -1) === ".") { host = host.slice(0, host.length - 1); }
		
		if (host.indexOf(".") !== -1) {
			hostWithoutFirstLevel = host.substring(0, host.lastIndexOf(".") - 1);
			secondLevelDotIndex = hostWithoutFirstLevel.lastIndexOf(".");
			if (secondLevelDotIndex !== -1) {
				host = host.substring(secondLevelDotIndex + 1);
			}
		}
		
		return host;
	}
	
	function getSSOApiDomain() {
		return "sso." + getFirstAndSecondLevelDomain();
	}
	
	function appendSSOLinkParameters(options) {
		var loginLink = document.getElementById("link_loginLinkInternal"),
			logoutLink = document.getElementById("link_logoutLinkInternal"),
			registerLink = document.getElementById("link_registerLinkInternal"),
			returnLocation = encodeURIComponent(options.ssoReturnUrl),
			ssoAppName = encodeURIComponent(options.ssoAppName);
		
		if (loginLink) {
			helpers.attachLinkParameters(loginLink, {
				"returnUrl": returnLocation,
				"appName": ssoAppName
			});
		}
		
		if (logoutLink) {
			helpers.attachLinkParameters(logoutLink, {
				"openid.ssoff.rp_post_logout_url": returnLocation
			});
		}
		
		if (registerLink) {
			helpers.attachLinkParameters(registerLink, {
				"sso_return": returnLocation,
				"appName": ssoAppName
			});
		}
	}
	
	function getJSONPUrl(callbackName) {
		var apiHost = getSSOApiDomain(),
			timestamp = Math.round((new Date()).getTime() / 1000); // cache killer
		
		return "https://" + apiHost + "/sso/getLoginInfo?callback=" + callbackName + "&ts=" + timestamp;
	}
	
	function updateLoginStatus() {
		var options = {
			ssoReturnUrl: (IS24.ssoReturnUrl || IS24.core.helpers.getLocation().href),
			ssoAppName: (IS24.ssoAppName || "is24main")
		};
		
		appendSSOLinkParameters(options);
		IS24.core.helpers.loadScript(getJSONPUrl("IS24.core.sso.jsonpCallback"));
	}
	
	function jsonpCallback(data) {
		var loginWrapper = document.querySelector(".sso-login"),
			userNameContainer = document.querySelector(".sso-login__user-name"),
			loggedInClass = "sso-login--logged-in";
		
		if (!(data && data.username) || !loginWrapper || !userNameContainer) { return; }
		
		userNameContainer.innerHTML = data.username;
		helpers.addClass(loginWrapper, loggedInClass);
	}
	
	return {
		updateLoginStatus: updateLoginStatus,
		jsonpCallback: jsonpCallback
	};
}());

/* --- Top Navigation --- */

IS24.core.topNavigation = {
	slideIn: (function () {
		var leftTrigger,
			rightTrigger,
			slideInContainer,
			slideInContainerIsOpenedClassFragment,
			initialized = false;
		
		function clickHandler(event, direction) {
			event.preventDefault();
			helpers.toggleClass(slideInContainer, slideInContainerIsOpenedClassFragment + direction);
		}
		
		function documentHandler(event) {
			var target = event.target || event.srcElement;
			if (helpers.nearestAncestor(target, ".top-navigation__slide-in--left") ||
				helpers.nearestAncestor(target, ".top-navigation__slide-in--right")) {
				event.stopPropagation();
			} else if (helpers.hasClass(slideInContainer, slideInContainerIsOpenedClassFragment + "left")) {
				event.preventDefault();
				helpers.removeClass(slideInContainer, slideInContainerIsOpenedClassFragment + "left");
			} else if (helpers.hasClass(slideInContainer, slideInContainerIsOpenedClassFragment + "right")) {
				event.preventDefault();
				helpers.removeClass(slideInContainer, slideInContainerIsOpenedClassFragment + "right");
			}
		}
		
		function init() {
			leftTrigger = document.querySelector(".top-navigation__slide-in-trigger--left");
			rightTrigger = document.querySelector(".top-navigation__slide-in-trigger--right");
			slideInContainer = document.querySelector(".top-navigation-slide-in-container");
			slideInContainerIsOpenedClassFragment = "top-navigation-slide-in-container--open-";
			
			if (!slideInContainer || initialized) { return; }
			
			if (leftTrigger) {
				helpers.addEventListener(leftTrigger, "click", function (event) {
					clickHandler(event, "left");
				});
			}
			if (rightTrigger) {
				helpers.addEventListener(rightTrigger, "click", function (event) {
					clickHandler(event, "right");
				});
			}
			if (leftTrigger || rightTrigger) {
				helpers.addEventListener(document, "touchend", function (event) {
					documentHandler(event);
				});
			}
			
			initialized = true; // prevents duplicate click handlers (and the resulting simultaneous open/close in the same event)
		}
		
		return { init: init };
	}()),
	
	touchHover: (function () {
		var hoverTabs,
			hoverTabLabels,
			pseudoHoverClass,
			lastTouchedItem,
			initialized = false;
		
		// check if the slide-in navigation is active by testing if one of the slide-in triggers is being displayed
		function hasSlideInNavigation() {
			var slideInTriggers = (document.querySelectorAll) ? document.querySelectorAll('[class*="top-navigation__slide-in-trigger--"]') : [],
				hasSlideIn = false,
				i;
			
			for (i=0; i<slideInTriggers.length; i++) {
				if (slideInTriggers[i].offsetWidth !== 0) {
					hasSlideIn = true;
				}
			}
			
			return hasSlideIn;
		}
		
		// prevent first click and simulate hover instead
		function tabHandler(event) {
			var ancestorLIElement,
				i;
			
			if (hasSlideInNavigation()) { return; } // skip touch handling when slide-in navigation is displayed instead of the tab navigation
			
			if (lastTouchedItem !== event.target) {
				lastTouchedItem = event.target;
				event.preventDefault();
				event.stopPropagation();
				for (i=0; i<hoverTabs.length; i++) {
					helpers.removeClass(hoverTabs[i], pseudoHoverClass);
				}
				ancestorLIElement = helpers.nearestAncestor(event.target, "li");
				helpers.addClass(ancestorLIElement, pseudoHoverClass);
			}
		}
		
		// remove hover effect on touch events outside of layer
		function documentHandler() {
			var i;
			
			if (!lastTouchedItem) { return; }
			
			lastTouchedItem = undefined;
			for (i=0; i<hoverTabs.length; i++) {
				helpers.removeClass(hoverTabs[i], pseudoHoverClass);
			}
		}
		
		function attachEventListeners() {
			var i;
			for (i=0; i<hoverTabLabels.length; i++) {
				helpers.addEventListener(hoverTabLabels[i], "touchstart", tabHandler);
			}
			helpers.addEventListener(document, "touchstart", documentHandler);
		}
		
		function init() {
			hoverTabs = document.querySelectorAll(".top-navigation__hover-tabs > li");
			hoverTabLabels = document.querySelectorAll(".top-navigation__hover-tabs__label");
			pseudoHoverClass = "hover";
			
			if (hoverTabLabels.length === 0 || hoverTabs.length === 0 || initialized) { return; }
			
			attachEventListeners();
			
			initialized = true;
		}
		
		return { init: init };
	}()),
	
	init: function () {
		this.slideIn.init();
		this.touchHover.init();
	}
};

}(window, document));
// Check if JQuery is available
if (typeof jQuery === 'function') {


/*! ========================================================================
 * Bootstrap: tooltip.js v3.3.2
 * http://getbootstrap.com/javascript/#tooltip
 * Inspired by the original jQuery.tipsy by Jason Frame
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */


+function($) {
    "use strict";
    var Tooltip = function(element, options) {
        this.type = this.options = this.enabled = this.timeout = this.hoverState = this.$element = null;
        this.init("tooltip", element, options);
    };
    Tooltip.VERSION = "3.3.2";
    Tooltip.TRANSITION_DURATION = 150;
    Tooltip.DEFAULTS = {
        animation: true,
        placement: "top",
        selector: false,
        template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
        trigger: "hover focus",
        title: "",
        delay: 0,
        html: false,
        container: false,
        viewport: {
            selector: "body",
            padding: 0
        }
    };
    Tooltip.prototype.init = function(type, element, options) {
        this.enabled = true;
        this.type = type;
        this.$element = $(element);
        this.options = this.getOptions(options);
        this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport);
        var triggers = this.options.trigger.split(" ");
        for (var i = triggers.length; i--; ) {
            var trigger = triggers[i];
            if (trigger == "click") {
                this.$element.on("click." + this.type, this.options.selector, $.proxy(this.toggle, this));
            } else if (trigger != "manual") {
                var eventIn = trigger == "hover" ? "mouseenter" : "focusin";
                var eventOut = trigger == "hover" ? "mouseleave" : "focusout";
                this.$element.on(eventIn + "." + this.type, this.options.selector, $.proxy(this.enter, this));
                this.$element.on(eventOut + "." + this.type, this.options.selector, $.proxy(this.leave, this));
            }
        }
        this.options.selector ? this._options = $.extend({}, this.options, {
            trigger: "manual",
            selector: ""
        }) : this.fixTitle();
    };
    Tooltip.prototype.getDefaults = function() {
        return Tooltip.DEFAULTS;
    };
    Tooltip.prototype.getOptions = function(options) {
        options = $.extend({}, this.getDefaults(), this.$element.data(), options);
        if (options.delay && typeof options.delay == "number") {
            options.delay = {
                show: options.delay,
                hide: options.delay
            };
        }
        return options;
    };
    Tooltip.prototype.getDelegateOptions = function() {
        var options = {};
        var defaults = this.getDefaults();
        this._options && $.each(this._options, function(key, value) {
            if (defaults[key] != value) options[key] = value;
        });
        return options;
    };
    Tooltip.prototype.enter = function(obj) {
        var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data("bs." + this.type);
        if (self && self.$tip && self.$tip.is(":visible")) {
            self.hoverState = "in";
            return;
        }
        if (!self) {
            self = new this.constructor(obj.currentTarget, this.getDelegateOptions());
            $(obj.currentTarget).data("bs." + this.type, self);
        }
        clearTimeout(self.timeout);
        self.hoverState = "in";
        if (!self.options.delay || !self.options.delay.show) return self.show();
        self.timeout = setTimeout(function() {
            if (self.hoverState == "in") self.show();
        }, self.options.delay.show);
    };
    Tooltip.prototype.leave = function(obj) {
        var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data("bs." + this.type);
        if (!self) {
            self = new this.constructor(obj.currentTarget, this.getDelegateOptions());
            $(obj.currentTarget).data("bs." + this.type, self);
        }
        clearTimeout(self.timeout);
        self.hoverState = "out";
        if (!self.options.delay || !self.options.delay.hide) return self.hide();
        self.timeout = setTimeout(function() {
            if (self.hoverState == "out") self.hide();
        }, self.options.delay.hide);
    };
    Tooltip.prototype.show = function() {
        var e = $.Event("show.bs." + this.type);
        if (this.hasContent() && this.enabled) {
            this.$element.trigger(e);
            var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]);
            if (e.isDefaultPrevented() || !inDom) return;
            var that = this;
            var $tip = this.tip();
            var tipId = this.getUID(this.type);
            this.setContent();
            $tip.attr("id", tipId);
            this.$element.attr("aria-describedby", tipId);
            if (this.options.animation) $tip.addClass("fade");
            var placement = typeof this.options.placement == "function" ? this.options.placement.call(this, $tip[0], this.$element[0]) : this.options.placement;
            var autoToken = /\s?auto?\s?/i;
            var autoPlace = autoToken.test(placement);
            if (autoPlace) placement = placement.replace(autoToken, "") || "top";
            $tip.detach().css({
                top: 0,
                left: 0,
                display: "block"
            }).addClass(placement).data("bs." + this.type, this);
            this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element);
            var pos = this.getPosition();
            var actualWidth = $tip[0].offsetWidth;
            var actualHeight = $tip[0].offsetHeight;
            if (autoPlace) {
                var orgPlacement = placement;
                var $container = this.options.container ? $(this.options.container) : this.$element.parent();
                var containerDim = this.getPosition($container);
                placement = placement == "bottom" && pos.bottom + actualHeight > containerDim.bottom ? "top" : placement == "top" && pos.top - actualHeight < containerDim.top ? "bottom" : placement == "right" && pos.right + actualWidth > containerDim.width ? "left" : placement == "left" && pos.left - actualWidth < containerDim.left ? "right" : placement;
                $tip.removeClass(orgPlacement).addClass(placement);
            }
            var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight);
            this.applyPlacement(calculatedOffset, placement);
            var complete = function() {
                var prevHoverState = that.hoverState;
                that.$element.trigger("shown.bs." + that.type);
                that.hoverState = null;
                if (prevHoverState == "out") that.leave(that);
            };
            $.support.transition && this.$tip.hasClass("fade") ? $tip.one("bsTransitionEnd", complete).emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete();
        }
    };
    Tooltip.prototype.applyPlacement = function(offset, placement) {
        var $tip = this.tip();
        var width = $tip[0].offsetWidth;
        var height = $tip[0].offsetHeight;
        var marginTop = parseInt($tip.css("margin-top"), 10);
        var marginLeft = parseInt($tip.css("margin-left"), 10);
        if (isNaN(marginTop)) marginTop = 0;
        if (isNaN(marginLeft)) marginLeft = 0;
        offset.top = offset.top + marginTop;
        offset.left = offset.left + marginLeft;
        $.offset.setOffset($tip[0], $.extend({
            using: function(props) {
                $tip.css({
                    top: Math.round(props.top),
                    left: Math.round(props.left)
                });
            }
        }, offset), 0);
        $tip.addClass("in");
        var actualWidth = $tip[0].offsetWidth;
        var actualHeight = $tip[0].offsetHeight;
        if (placement == "top" && actualHeight != height) {
            offset.top = offset.top + height - actualHeight;
        }
        var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);
        if (delta.left) offset.left += delta.left; else offset.top += delta.top;
        var isVertical = /top|bottom/.test(placement);
        var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight;
        var arrowOffsetPosition = isVertical ? "offsetWidth" : "offsetHeight";
        $tip.offset(offset);
        this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical);
    };
    Tooltip.prototype.replaceArrow = function(delta, dimension, isHorizontal) {
        this.arrow().css(isHorizontal ? "left" : "top", 50 * (1 - delta / dimension) + "%").css(isHorizontal ? "top" : "left", "");
    };
    Tooltip.prototype.setContent = function() {
        var $tip = this.tip();
        var title = this.getTitle();
        $tip.find(".tooltip-inner")[this.options.html ? "html" : "text"](title);
        $tip.removeClass("fade in top bottom left right");
    };
    Tooltip.prototype.hide = function(callback) {
        var that = this;
        var $tip = this.tip();
        var e = $.Event("hide.bs." + this.type);
        function complete() {
            if (that.hoverState != "in") $tip.detach();
            that.$element.removeAttr("aria-describedby").trigger("hidden.bs." + that.type);
            callback && callback();
        }
        this.$element.trigger(e);
        if (e.isDefaultPrevented()) return;
        $tip.removeClass("in");
        $.support.transition && this.$tip.hasClass("fade") ? $tip.one("bsTransitionEnd", complete).emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete();
        this.hoverState = null;
        return this;
    };
    Tooltip.prototype.fixTitle = function() {
        var $e = this.$element;
        if ($e.attr("title") || typeof $e.attr("data-original-title") != "string") {
            $e.attr("data-original-title", $e.attr("title") || "").attr("title", "");
        }
    };
    Tooltip.prototype.hasContent = function() {
        return this.getTitle();
    };
    Tooltip.prototype.getPosition = function($element) {
        $element = $element || this.$element;
        var el = $element[0];
        var isBody = el.tagName == "BODY";
        var elRect = el.getBoundingClientRect();
        if (elRect.width == null) {
            elRect = $.extend({}, elRect, {
                width: elRect.right - elRect.left,
                height: elRect.bottom - elRect.top
            });
        }
        var elOffset = isBody ? {
            top: 0,
            left: 0
        } : $element.offset();
        var scroll = {
            scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop()
        };
        var outerDims = isBody ? {
            width: $(window).width(),
            height: $(window).height()
        } : null;
        return $.extend({}, elRect, scroll, outerDims, elOffset);
    };
    Tooltip.prototype.getCalculatedOffset = function(placement, pos, actualWidth, actualHeight) {
        return placement == "bottom" ? {
            top: pos.top + pos.height,
            left: pos.left + pos.width / 2 - actualWidth / 2
        } : placement == "top" ? {
            top: pos.top - actualHeight,
            left: pos.left + pos.width / 2 - actualWidth / 2
        } : placement == "left" ? {
            top: pos.top + pos.height / 2 - actualHeight / 2,
            left: pos.left - actualWidth
        } : {
            top: pos.top + pos.height / 2 - actualHeight / 2,
            left: pos.left + pos.width
        };
    };
    Tooltip.prototype.getViewportAdjustedDelta = function(placement, pos, actualWidth, actualHeight) {
        var delta = {
            top: 0,
            left: 0
        };
        if (!this.$viewport) return delta;
        var viewportPadding = this.options.viewport && this.options.viewport.padding || 0;
        var viewportDimensions = this.getPosition(this.$viewport);
        if (/right|left/.test(placement)) {
            var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll;
            var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight;
            if (topEdgeOffset < viewportDimensions.top) {
                delta.top = viewportDimensions.top - topEdgeOffset;
            } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) {
                delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;
            }
        } else {
            var leftEdgeOffset = pos.left - viewportPadding;
            var rightEdgeOffset = pos.left + viewportPadding + actualWidth;
            if (leftEdgeOffset < viewportDimensions.left) {
                delta.left = viewportDimensions.left - leftEdgeOffset;
            } else if (rightEdgeOffset > viewportDimensions.width) {
                delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;
            }
        }
        return delta;
    };
    Tooltip.prototype.getTitle = function() {
        var title;
        var $e = this.$element;
        var o = this.options;
        title = $e.attr("data-original-title") || (typeof o.title == "function" ? o.title.call($e[0]) : o.title);
        return title;
    };
    Tooltip.prototype.getUID = function(prefix) {
        do prefix += ~~(Math.random() * 1e6); while (document.getElementById(prefix));
        return prefix;
    };
    Tooltip.prototype.tip = function() {
        return this.$tip = this.$tip || $(this.options.template);
    };
    Tooltip.prototype.arrow = function() {
        return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow");
    };
    Tooltip.prototype.enable = function() {
        this.enabled = true;
    };
    Tooltip.prototype.disable = function() {
        this.enabled = false;
    };
    Tooltip.prototype.toggleEnabled = function() {
        this.enabled = !this.enabled;
    };
    Tooltip.prototype.toggle = function(e) {
        var self = this;
        if (e) {
            self = $(e.currentTarget).data("bs." + this.type);
            if (!self) {
                self = new this.constructor(e.currentTarget, this.getDelegateOptions());
                $(e.currentTarget).data("bs." + this.type, self);
            }
        }
        self.tip().hasClass("in") ? self.leave(self) : self.enter(self);
    };
    Tooltip.prototype.destroy = function() {
        var that = this;
        clearTimeout(this.timeout);
        this.hide(function() {
            that.$element.off("." + that.type).removeData("bs." + that.type);
        });
    };
    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("bs.tooltip");
            var options = typeof option == "object" && option;
            if (!data && option == "destroy") return;
            if (!data) $this.data("bs.tooltip", data = new Tooltip(this, options));
            if (typeof option == "string") data[option]();
        });
    }
    var old = $.fn.tooltip;
    $.fn.tooltip = Plugin;
    $.fn.tooltip.Constructor = Tooltip;
    $.fn.tooltip.noConflict = function() {
        $.fn.tooltip = old;
        return this;
    };
}(jQuery);
/*! ========================================================================
 * Bootstrap: popover.js v3.3.2
 * http://getbootstrap.com/javascript/#popovers
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */


+function($) {
    "use strict";
    var Popover = function(element, options) {
        this.init("popover", element, options);
    };
    if (!$.fn.tooltip) throw new Error("Popover requires tooltip.js");
    Popover.VERSION = "3.3.2";
    Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
        placement: "right",
        trigger: "click",
        content: "",
        template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
    });
    Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype);
    Popover.prototype.constructor = Popover;
    Popover.prototype.getDefaults = function() {
        return Popover.DEFAULTS;
    };
    Popover.prototype.setContent = function() {
        var $tip = this.tip();
        var title = this.getTitle();
        var content = this.getContent();
        $tip.find(".popover-title")[this.options.html ? "html" : "text"](title);
        $tip.find(".popover-content").children().detach().end()[this.options.html ? typeof content == "string" ? "html" : "append" : "text"](content);
        $tip.removeClass("fade top bottom left right in");
        if (!$tip.find(".popover-title").html()) $tip.find(".popover-title").hide();
    };
    Popover.prototype.hasContent = function() {
        return this.getTitle() || this.getContent();
    };
    Popover.prototype.getContent = function() {
        var $e = this.$element;
        var o = this.options;
        return $e.attr("data-content") || (typeof o.content == "function" ? o.content.call($e[0]) : o.content);
    };
    Popover.prototype.arrow = function() {
        return this.$arrow = this.$arrow || this.tip().find(".arrow");
    };
    Popover.prototype.tip = function() {
        if (!this.$tip) this.$tip = $(this.options.template);
        return this.$tip;
    };
    function Plugin(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data("bs.popover");
            var options = typeof option == "object" && option;
            if (!data && option == "destroy") return;
            if (!data) $this.data("bs.popover", data = new Popover(this, options));
            if (typeof option == "string") data[option]();
        });
    }
    var old = $.fn.popover;
    $.fn.popover = Plugin;
    $.fn.popover.Constructor = Popover;
    $.fn.popover.noConflict = function() {
        $.fn.popover = old;
        return this;
    };
}(jQuery);
/*
 *  popoverTrigger
 *  is a jquery helper function to register popover listener on the document body.
 *  the popover will stay open until the body is clicked.
 *
 *  author: sstiehl
 *  version 0.0.2
 *
 *
 */
jQuery.popoverTrigger = (function ($) {
  "use strict";

  var triggeredElements = [],
    lastClickedElement = null;

  function createPopover(el) {
    el.popover({
      "html": true,
      "container": "body",
      "trigger": "manual"
    });
  }

  function closeAll() {
    $.each(triggeredElements, function (index, el) {
      el.popover("hide");
    });

    triggeredElements = [];
  }

  return function popoverLoader(trigger) {
    var body = $("body");

    if (!$.fn.popover) {
      return;
    }

    body.on("click", trigger, function (event) {
      var el = $(this);

      closeAll();

      if (!el.is(lastClickedElement)) {
        createPopover(el);
        el.popover("show");
        triggeredElements.push(el);
        lastClickedElement = el;
      } else {
        lastClickedElement = null;
      }

      event.stopPropagation();
      event.preventDefault();
    });

    body.on("click touchstart", function (event) {
      if (triggeredElements.length === 0) {
        return;
      }

      if (!$(event.target).parents(".popover").length) {
        closeAll();
        lastClickedElement = null;
      }
    });
  };
}(jQuery));
}
