


/*
	Slimbox v1.69 - The ultimate lightweight Lightbox clone
	(c) 2007-2009 Christophe Beyls <http://www.digitalia.be>
	MIT-style license.
*/

var Slimbox = (function() {

	// Global variables, accessible to Slimbox only
	var win = window, ie6 = Browser.Engine.trident4, options, images, activeImage = -1, activeURL, prevImage, nextImage, compatibleOverlay, middle, centerWidth, centerHeight,

	// Preload images
	preload = {}, preloadPrev = new Image(), preloadNext = new Image(),

	// DOM elements
	overlay, center, image, sizer, prevLink, nextLink, bottomContainer, bottom, caption, number,

	// Effects
	fxOverlay, fxResize, fxImage, fxBottom;




	// Initialization


	win.addEvent("domready", function() {
		// Append the Slimbox HTML code at the bottom of the document
		$(document.body).adopt(
			$$(
				overlay = new Element("div", {id: "lbOverlay", events: {click: close}}),
				center = new Element("div", {id: "lbCenter"}),
				bottomContainer = new Element("div", {id: "lbBottomContainer"})
			).setStyle("display", "none")
		);

		image = new Element("div", {id: "lbImage"}).injectInside(center).adopt(
			sizer = new Element("div", {styles: {position: "relative"}}).adopt(
				prevLink = new Element("a", {id: "lbPrevLink", href: "#", events: {click: previous}}),
				nextLink = new Element("a", {id: "lbNextLink", href: "#", events: {click: next}})
			)
		);

		bottom = new Element("div", {id: "lbBottom"}).injectInside(bottomContainer).adopt(
			new Element("a", {id: "lbCloseLink", href: "#", events: {click: close}}),
			caption = new Element("div", {id: "lbCaption"}),
			number = new Element("div", {id: "lbNumber"}),
			new Element("div", {styles: {clear: "both"}})
		);
	});



	//	Internal functions


	function position() {
		var scroll = win.getScroll(), size = win.getSize();
		$$(center, bottomContainer).setStyle("left", scroll.x + (size.x / 2));
		if (compatibleOverlay) overlay.setStyles({left: scroll.x, top: scroll.y, width: size.x, height: size.y});
	}

	function setup(open) {
		["object", ie6 ? "select" : "embed"].forEach(function(tag) {
			Array.forEach(document.getElementsByTagName(tag), function(el) {
				if (open) el._slimbox = el.style.visibility;
				el.style.visibility = open ? "hidden" : el._slimbox;
			});
		});

		overlay.style.display = open ? "" : "none";

		var fn = open ? "addEvent" : "removeEvent";
		win[fn]("scroll", position)[fn]("resize", position);
		document[fn]("keydown", keyDown);
	}

	function keyDown(event) {
		var code = event.code;
		// Prevent default keyboard action (like navigating inside the page)
		return options.closeKeys.contains(code) ? close()
			: options.nextKeys.contains(code) ? next()
			: options.previousKeys.contains(code) ? previous()
			: false;
	}

	function previous() {
		return changeImage(prevImage);
	}

	function next() {
		return changeImage(nextImage);
	}

	function changeImage(imageIndex) {
		if (imageIndex >= 0) {
			activeImage = imageIndex;
			activeURL = images[imageIndex][0];
			prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
			nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);

			stop();
			center.className = "lbLoading";

			preload = new Image();
			preload.onload = animateBox;
			preload.src = activeURL;
		}

		return false;
	}

	function animateBox() {
		center.className = "";
		fxImage.set(0);
		image.setStyles({backgroundImage: "url(" + activeURL + ")", display: ""});
		sizer.setStyle("width", preload.width);
		$$(sizer, prevLink, nextLink).setStyle("height", preload.height);

		caption.set("html", images[activeImage][1] || "");
		number.set("html", (((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length));

		if (prevImage >= 0) preloadPrev.src = images[prevImage][0];
		if (nextImage >= 0) preloadNext.src = images[nextImage][0];

		centerWidth = image.offsetWidth;
		centerHeight = image.offsetHeight;
		var top = Math.max(0, middle - (centerHeight / 2)), fn;
		if (center.offsetHeight != centerHeight) {
			fxResize.start({height: centerHeight, top: top});
		}
		if (center.offsetWidth != centerWidth) {
			fxResize.start({width: centerWidth, marginLeft: -centerWidth/2});
		}
		fn = function() {
			bottomContainer.setStyles({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, visibility: "hidden", display: ""});
			fxImage.start(1);
		};
		if (fxResize.check(fn)) fn();
	}

	function animateCaption() {
		if (prevImage >= 0) prevLink.style.display = "";
		if (nextImage >= 0) nextLink.style.display = "";
		fxBottom.set(-bottom.offsetHeight).start(0);
		bottomContainer.style.visibility = "";
	}

	function stop() {
		preload.onload = $empty;
		preload.src = preloadPrev.src = preloadNext.src = activeURL;
		fxResize.cancel();
		fxImage.cancel();
		fxBottom.cancel();
		$$(prevLink, nextLink, image, bottomContainer).setStyle("display", "none");
	}

	function close() {
		if (activeImage >= 0) {
			stop();
			activeImage = prevImage = nextImage = -1;
			center.style.display = "none";
			fxOverlay.cancel().chain(setup).start(0);
		}

		return false;
	}



	//	API


	Element.implement({
		slimbox: function(_options, linkMapper) {
			// The processing of a single element is similar to the processing of a collection with a single element
			$$(this).slimbox(_options, linkMapper);

			return this;
		}
	});

	Elements.implement({

			//options:	Optional options object, see Slimbox.open()
			//linkMapper:	Optional function taking a link DOM element and an index as arguments and returning an array containing 2 elements:
					//the image URL and the image caption (may contain HTML)
			//linksFilter:	Optional function taking a link DOM element and an index as arguments and returning true if the element is part of
				//	the image collection that will be shown on click, false if not. "this" refers to the element that was clicked.
					//This function must always return true when the DOM element argument is "this".

		slimbox: function(_options, linkMapper, linksFilter) {
			linkMapper = linkMapper || function(el) {
				return [el.href, el.title];
			};

			linksFilter = linksFilter || function() {
				return true;
			};

			var links = this;

			links.removeEvents("click").addEvent("click", function() {
				// Build the list of images that will be displayed
				var filteredLinks = links.filter(linksFilter, this);
				return Slimbox.open(filteredLinks.map(linkMapper), filteredLinks.indexOf(this), _options);
			});

			return links;
		}
	});

	return {
		open: function(_images, startImage, _options) {
			options = $extend({
				loop: false,				// Allows to navigate between first and last images
				overlayOpacity: 0.8,			// 1 is opaque, 0 is completely transparent (change the color in the CSS file)
				overlayFadeDuration: 400,		// Duration of the overlay fade-in and fade-out animations (in milliseconds)
				resizeDuration: 400,			// Duration of each of the box resize animations (in milliseconds)
				resizeTransition: false,		// false uses the mootools default transition
				initialWidth: 250,			// Initial width of the box (in pixels)
				initialHeight: 250,			// Initial height of the box (in pixels)
				imageFadeDuration: 400,			// Duration of the image fade-in animation (in milliseconds)
				captionAnimationDuration: 400,		// Duration of the caption animation (in milliseconds)
				counterText: "Image {x} of {y}",	// Translate or change as you wish, or set it to false to disable counter text for image groups
				closeKeys: [27, 88, 67],		// Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67)
				previousKeys: [37, 80],			// Array of keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80)
				nextKeys: [39, 78]			// Array of keycodes to navigate to the next image, default: Right arrow (39), 'n' (78)
			}, _options || {});

			// Setup effects
			fxOverlay = new Fx.Tween(overlay, {property: "opacity", duration: options.overlayFadeDuration});
			fxResize = new Fx.Morph(center, $extend({duration: options.resizeDuration, link: "chain"}, options.resizeTransition ? {transition: options.resizeTransition} : {}));
			fxImage = new Fx.Tween(image, {property: "opacity", duration: options.imageFadeDuration, onComplete: animateCaption});
			fxBottom = new Fx.Tween(bottom, {property: "margin-top", duration: options.captionAnimationDuration});

			// The function is called for a single image, with URL and Title as first two arguments
			if (typeof _images == "string") {
				_images = [[_images, startImage]];
				startImage = 0;
			}

			middle = win.getScrollTop() + (win.getHeight() / 2);
			centerWidth = options.initialWidth;
			centerHeight = options.initialHeight;
			center.setStyles({top: Math.max(0, middle - (centerHeight / 2)), width: centerWidth, height: centerHeight, marginLeft: -centerWidth/2, display: ""});
			compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed"));
			if (compatibleOverlay) overlay.style.position = "absolute";
			fxOverlay.set(0).start(options.overlayOpacity);
			position();
			setup(1);

			images = _images;
			options.loop = options.loop && (images.length > 1);
			return changeImage(startImage);
		}
	};

})();


/**
 * Project gallery
 */
var Projects = (function() {


	/**
	 * Text elements of the current scop
	 *
	 * @var Array
	 */
	var texts = [], imgs = [], ids = [];


	/**
	 * Retrieves the ID of an element, based upon
	 * the ID information
	 */
	function getId(element) {
		return element.id.split("-").getLast().toInt();
	}

	/**
	 * Hides all items, but shows the one with the
	 * given ID
	 *
	 * @param id Integer ID of the item to be visible
	 */
	function showItem(id) {


		ids.each(function(index) {

			if (index == 0) {
				return;
			}
			
			if (imgs[index]) {
				imgs[index].className = index == id  ? 'active' : "";
			}

			texts[index].cancel().start({opacity: index == id ? 1 : 0});
		});
	}


	/**
	 * Initializes the project function
	 *
	 * Instanciates the Morph objects, sets the default view, adds event observers
	 * and saves all needed information in global variables of this namespace
	 */
	return function() {
		$$("#images a").each(function(item) {
			 imgs[ getId(item) ] = item.addEvent("mouseenter", showItem.pass( getId(item) )).getElement("img");
		});
		$("texts").getChildren().each(function(item, index) {

			ids.push(getId(item));
			texts[ getId(item) ] = new Fx.Morph(item.set({style: {overflow: "hidden"}}), {duration: 150});
			
			if (index > 0) {
				texts[ getId(item) ].set({opacity: 0});
			} else {
				imgs[ getId(item) ].className = "active";
			}
		});
	};
})();




/**
 * Simple Validator
 * Replaces the text of an element with an error message if the given validation fails and adds and class to this element.
 * When an Element is focused again, the class is removed and the wrong value (if there was one) is restored
 */
var Validator = new Class({
	
	fields:    [],
	backups:   [],
	form:      null,
	error:     false,
	lastValue: '',
	
	
	regexp : {
		alpha : /^[a-z ._-]+$/i,
		alphanum : /^[a-z0-9 ._-]+$/i,
		digit : /^[-+]?[0-9]+$/,
		nodigit : /^[^0-9]+$/,
		number : /^[-+]?\d*\.?\d+$/,
		email : /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
		phone : /^[\d\s \/().-]+$/
		//url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i
	},
	
	initialize: function(options) {

		this.options = $merge({
			text: { // Text of the Error Message; The key must be the ID of an element with class="validate[xy]"
				name: "Name fehlt",
				first_name: "Vorname fehlt",
				last_name: "Nachname fehlt",
				firma: "Firma fehlt",
				adresse: "Adresse fehlt",
				plz: "PLZ fehlt",
				ort: "Stadt fehlt",
				telefon: "Telefon fehlt",
				fax: "Telefax fehlt",
				email: "eMail Adresse fehlt"
			},
			selector: "error",
			form: "",
			event: "submit" // Valid are "submit" and "blur"
		}, options || {});
		
		this.form = $(this.options.form);

		if ($type(this.form) != "element") return;

			// Push all Elements
		form = '#' + this.options.form + ' ';
		$$(form + 'input', form + 'textarea').each(function(element) {
			if (element.className.test(/validate\[(.+)\]$/i)) { // Is a form
				this.fields.push({
					"el": element,
					"tests": element.className.match(/validate\[(.+)\]$/i)[1].split(",")
				});
			}
		}, this);
	
			// Bind validation to form
		this.form.addEvent("submit", this.validate.bind(this));
		
			// Remove Messages on exit
		window.addEvent('unload', this.removeAllErrors.bind(this));
	},
	
	validate: function(e) {
		
		this.error = false;
		
		this.fields.each(function(field) {
			
			if (!this.form.getElements('input').contains(field.el)) return;
			
			field.tests.each(function(test) {
				this.validateSingeItem(test, field);
			}, this);
			
		}, this);
		

		if (this.error) {
			e = new Event(e).stop();
			return false;
		} else {
			return true;
		}
	},
	
	validateSingeItem: function(test, field) {
		test = test.split('-');
		this.currentElement = field;
		valid = true;
		
		switch(test[0]) {
				
			case 'email':
			case 'mail':
				valid = this._regexp(this.regexp.email);
				break;
				
			case 'number':
			case 'zahl':
				valid = this._regexp(this.regexp.number);
				break;
				
			case 'zip':
			case 'plz': // Contains 4, 5, or 6 numeric characters
				valid = (this._regexp(this.regexp.number) && this._length(4, 6));
				break;
						
			case 'length':
				valid = this._length(test[1], test[2]);
				break;
						
			case 'phone':
			case 'tel':
			case 'telefon':
				valid = this._regexp(this.regexp.phone);
				break;
		}
			
			// Test fails, or value is the error message -> throw an error	
		if (!valid || this.lastValue == this.options.text[this.currentElement.el.id]) this.setError();
			
	},
	
	setError: function() {
		el = this.currentElement.el;
		options = this.options;
		
		if (options.text[el.id] != this.lastValue.clean()) this.backups[ el.id ] = this.lastValue;
		el.setProperty('value', options.text[el.id]).addClass(options.selector).addEvent("focus", this.removeError.bind(this));
		this.error = true;
	},
	
	removeAllErrors: function() {
		this.fields.each(function(item) {
			options = this.options;		
			if (item.el.hasClass(options.selector)) {
				item.el.removeClass(options.selector).value = "";
			}
		}, this);
	},
	
	removeError: function(e) {
	
		var currentEl = this.currentElement = new Event(e).target;
		
		this.fields.each(function(field) {
			
			if (currentEl == field.el && currentEl.hasClass(this.options.selector)) {
				currentEl.removeClass(this.options.selector).setProperty("value", (this.backups[ currentEl.id ] ? this.backups[ currentEl.id ] : ''));
			}
			
		}.bind(this));
	
	},
	
	_regexp: function(exp) {	
		return (this.last().search(exp) != -1);
	},
	
	
	_length: function(min, max) {
		min = min.toInt();		
		max = (max == 'x') ? 999999 : max.toInt();
		val = this.last();	
		
		return (val.length >= min && val.length <= max);
	},
	
	last: function() {
		this.lastValue = (" " + this.currentElement.el.getProperty('value')).trim();	
		return this.lastValue;
	}
	
});


/**
 * Implement features
 *
 * Since all the javascripts are included at the bottom of the page, a
 * domready event is not needed anymore.
 *
 */

	// Slimbox
	// The default scanpage - function, shipped with the default distribution
$$(document.links).filter(function(el) {
		return el.rel && el.rel.test(/^lightbox/i);
}).slimbox({counterText: ""}, null, function(el) {
		return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
});



	// Project gallery
	// Initialized if the needed container element is found

if ($("projects")) {
	window.addEvent("load", Projects);
}

	// Validator
	// Initialises the validator for the form element
if ($("contact_form")) {
	var contact_validator = new Validator({
		form: 'contact_form', 
		text: {
			vorname:  "Vorname fehlt",
			nachname: "Nachname fehlt",
			email:    "eMail fehlt oder ung\u00FCltig",
			telefon:  "Telefonnummer fehlt oder ung\u00FCltig",
			nachricht:"Nachricht fehlt"
		}
	});
}
