/**
 * @fileOverview Collection of base functionalities
 * @author Wouter Bos
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */



if (typeof(Estate) != "undefined") {
	if (typeof (Estate.Develop) != "undefined") {
		throw new Error("Estate object can be loaded only once")
	}
}


 
/**
 * @namespace Holds shortcuts to other code. The methods won't do anything if
 *            the targetted method is not loaded in the namespace.
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate = ( function() {
	/* Start public */
	return {
		/**
		 * Trace method. Requires develop CSS to display correctly
		 * @param {string} string The string that will be pushed to the trace console in the website
		 * @example
		 * Estate.Trace('print this')
		 */
		Trace: function( string ) {
			if ( Estate.Develop && Estate.Develop.Trace ) {
				Estate.Develop.Trace( string )
			}
		},
		
		/**
		 * Trace method. Shows all attributes of a given element
		 * @param {element} element An HTML element.
		 * @example
		 * Estate.Trace(document.getElementById('foo'))
		 */
		TraceAttr: function( element ) {
			if ( Estate.Develop && Estate.Develop.Trace ) {
				Estate.Develop.TraceAttr( element )
			}
		}
	}
	/* End public */
})();






/**
 * @namespace Methods to validate function arguments. Use these methods liberally in all
 *            code that is part of the Estate object.
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */		
Estate.Check = ( function() {
	function literalsAreCompatible( mainLiteral, updateLiteral ) {
		for (prop in updateLiteral) {
			if ( typeof(mainLiteral[prop]) == "undefined" ) {
				return "The variable '" + prop + "'in the object literal cannot be merged with the original object literal.";
			}

			if ( typeof(updateLiteral[prop]) != typeof(mainLiteral[prop]) ) {
				return "The variable '" + prop + " is of the wrong type. It is '" + typeof(updateLiteral[prop]) + "' but it should be '" + typeof(mainLiteral[prop]) + "'";
			}
			
			if ( typeof( updateLiteral[prop] ) == "object" ) {
				literalsAreCompatible( mainLiteral[prop], updateLiteral[prop] );
			}
		}
	}
	


	/* Start public */
	return {
		/**
		 * Checks if the right amount of arguments are used when calling the function
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-03-22
		 * @param {Number} CurrentArgumentsLength The number of arguments that have been supplied
		 * @param {Number|Number[]} CorrectArgumentsLength The number of arguments that are required
		 * @example
		 * error = Estate.Check.ArgumentsCount( arguments.length, [0, 1] );
		 * if ( error != "" ) throw new Error( error );
		 */
		ArgumentsCount: function(CurrentArgumentsLength, CorrectArgumentsLength) {
			var error
			if (arguments.length != 2) throw new Error("Arguments count must be 2");
			error = Estate.Check.VariableType(CurrentArgumentsLength, "number");
			if (error != "") throw new Error(error);
			
			var CorrectArgumentsCount = false;
			if ( typeof(CorrectArgumentsLength) == "number" ) {
				if ( CurrentArgumentsLength == CorrectArgumentsLength ) {
					CorrectArgumentsCount = true;
				}
			} else if (typeof (CorrectArgumentsLength) == "array" || typeof (CorrectArgumentsLength) == "object") {
				for (var i = 0; i < CorrectArgumentsLength.length; i++) {
					if ( CurrentArgumentsLength == CorrectArgumentsLength[i] ) {
						CorrectArgumentsCount = true
					}
				}
			}
			
			if (CorrectArgumentsCount == false) {
				return "Wrong number of arguments. There argument count should be "+ CorrectArgumentsLength +", but it is "+ CurrentArgumentsLength;
			} else {
				return ""
			}
		},
		
		/**
		 * Checks if an element with a particular id exists
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} ElementID The ID of the element that will be checked if it exists.
		 * @param {String} [RequiredTagName] The tagname that the element should have.
		 * @example
		 * error = Estate.Check.ElementById( 'elID', 'div' );
		 * if ( error != "" ) throw new Error( error );
		 */
		ElementById: function( ElementID, RequiredTagName ) {
			if ( typeof( ElementID ) != "string" ) {
				return "Provided element id is not a string but  '"+ typeof( ElementID ) +"'.";
			}
			if ( !document.getElementById(ElementID) ) {
				return "Cannot find HTML element with the id '"+ ElementID +"'";
			}
			if ( arguments.length > 1 && typeof( RequiredTagName ) == "string" ) {
				if ( document.getElementById(ElementID).tagName.toLowerCase() != RequiredTagName && RequiredTagName != "" ) {
					return "HTML element with ID '"+ ElementID +"' has the tagname '"+ document.getElementById(ElementID).tagName +"' but it should be '"+ RequiredTagName +"'";
				}
			}
			return ""
		},

		/**
		 * Checks if the referenced object is an HTML element
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} Element The element that will be checked if it exists.
		 * @example
		 * error = Estate.Check.Element( document.getElementsByTagName('a')[0] );
		 * if ( error != "" ) throw new Error( error );
		 */
		Element: function( Element ) {
			if ( typeof( Element.tagName ) == "undefined" ) {
				return "HTML element expected. Type of checked variable is " + typeof( Element )
			}
			return ""
		},

		/**
		 * Checks if argument is of the expected variable type
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {anything} Variable The variable that will be checked if it has the right type
		 * @param {String} ExpectedVariableType The variable type of the first argument has to be equal to this string
		 * @example
		 * error = Estate.Check.VariableType( id, "string" );
		 * if ( error != "" ) throw new Error( error );
		 */
		VariableType: function( Variable, ExpectedVariableType ) {
			if ( typeof( Variable ) != ExpectedVariableType ) {
				return "Unexpected variable type. There variable type should be "+ ExpectedVariableType +", but it is "+ typeof( Variable );
			}
			return ""
		},

		/**
		 * Returns a value from an object literal. If that vaule does not exist
		 * it will fallback on the value in the old object literal
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} oldVariable
		 * @param {Object} newVariable
		 * @param {String} arrayID Key of the object literal
		 * @example
		 * oLiteral.foo = Estate.Check.SetLiteralIfDefined( oLiteral, oNewLiteral, "foo" )
		 */
		SetLiteralIfDefined: function( oldVariable, newVariable, arrayID ) {
			if ( typeof( newVariable ) == "undefined" ) {
				return oldVariable[arrayID]
			}

			if ( typeof( newVariable[arrayID] ) == "undefined" ) {
				return oldVariable[arrayID]
			} else {
				return newVariable[arrayID]
			}
		},

		/**
		 * Updates object literal. The second argument is merged with the first. Please use
		 * Estate.Check.LiteralUpdatable if you want to be sure that this method
		 * only updates variables and doesn't create new ones.
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} mainLiteral
		 * @param {Object} updateLiteral
		 * @example
		 * Estate.Check.UpdateLiteral( mainLiteral, updatingLiteral )
		 */
		UpdateLiteral: function( mainLiteral, updateLiteral ) {
			for (prop in updateLiteral) {
				mainLiteral[prop] = updateLiteral[prop]
				
				if ( typeof( updateLiteral[prop] ) == "object" ) {
					Estate.Check.UpdateLiteral( mainLiteral[prop], updateLiteral[prop] );
				}
			}
		},

		/**
		 * Compares 2 object literals and checks if the 2nd argument can be merged
		 * with the 1st. If there's a variable in the 1st argument that's not
		 * been defined in the 2nd, the function returns the name of the variable.
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} mainLiteral
		 * @param {Object} updateLiteral
		 * @example
		 * error = Estate.Check.LiteralUpdatable( mainLiteral, updatingLiteral );
		 * if ( error != "" ) throw new Error( error );
		 */
		LiteralUpdatable: function( mainLiteral, updateLiteral ) {
			if ( typeof(mainLiteral) != "object") {
				throw new Error( "Cannot check literal: 'mainLiteral' is not an object" )
			}
			if ( typeof(updateLiteral) != "object") {
				throw new Error( "Cannot check literal: 'updateLiteral' second argument is not an object" )
			}
			
			
			var isNotUpdatableVariable = literalsAreCompatible( mainLiteral, updateLiteral )
			
			if ( typeof( isNotUpdatableVariable ) == "undefined" ) {
				return ''
			} else {
				return isNotUpdatableVariable;
			}
		}
	}
	/* End public */
})();






/**
 * @namespace Helper methods for getting, setting and deleting cookies
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.Cookies = (function() {
    /* Start public */
    return {
		/**
		 * Sets a cookie
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} name Name of the cookie
		 * @param {String} value Value of the cookie
		 * @param {Date} expires Expire date
		 * @param {String} path Path where the cookie is valid (default: path of calling document)
		 * @param {String} domain
		 * @param {Boolean} secure Indicating if the cookie transmission requires a secure transmission
		 * @example
		 * Estate.Cookies.Set( "cookieName", "cookieValue", new Date(2015, 12, 31, 23, 59, 59, 0), "/", "www.domain.com", "")
		 */
		Set: function(name,
					   value,
					   expires,
					   path,
					   domain,
					   secure
					   ) {
			document.cookie = name + "=" + encodeURIComponent(value) +
				((expires) ? "; expires=" + expires.toGMTString() : "") +
				((path) ? "; path=" + path : "") +
				((domain) ? "; domain=" + domain : "") +
				((secure) ? "; secure" : "");
		},

 		/**
		 * Gets cookie data
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @example
		 * Estate.Cookies.Get()
		 */
        Get: function(name) {
            var dc = document.cookie;
            var prefix = name + "=";
            var begin = dc.indexOf("; " + prefix);
            if (begin == -1) {
                begin = dc.indexOf(prefix);
                if (begin != 0) return null;
            } else {
                begin += 2;
            }
            var end = document.cookie.indexOf(";", begin);
            if (end == -1) {
                end = dc.length;
            }
            return decodeURIComponent(dc.substring(begin + prefix.length, end));
        },

  		/**
		 * Deletes cookie data
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} name Name of the cookie
		 * @param {String} path Path where the cookie is valid (default: path of calling document)
		 * @param {String} domain
		 * @example
		 * Estate.CookiesDelete()
		 */
        Delete: function(name,
						  path,
						  domain
						  ) {
            if (Estate.Cookies.Get(name)) {
                document.cookie = name + "=" +
				((path) ? "; path=" + path : "") +
				((domain) ? "; domain=" + domain : "") +
				"; expires=Thu, 01-Jan-70 00:00:01 GMT";
            }
        }
    }
    /* End public */
})();






/**
 * @namespace CSS related methods
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.CSSTools = ( function() {
	/* Start public */
	return {
  		/**
		 * Calling the method adds or removes a class from an element
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} el An HTML element
		 * @param {String} className The name of the class that has to be added or removed
		 * @example
		 * Estate.CSSTools.ClassToggle( document.getElementById('elementId'), 'className')
		 */
		ClassToggle: function( el, className ) {
			var error;
			error = Estate.Check.Element( el );
			if ( error != "" ) throw new Error( error );
			error = Estate.Check.VariableType( className, "string" )
			if ( error != "" ) throw new Error( error );


			if ( el.className.indexOf( className ) < 0) {
				el.className += " "+ className
			} else {
				while ( el.className.indexOf( className ) >= 0) {
					el.className = el.className.replace( " "+ className, "" )
					el.className = el.className.replace( className, "" )
				}
			}
		},

  		/**
		 * Adds class to an element. This function is used for components in the Estate object that musn't use jQuery. If that's not the case, just use jQuery
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} el An HTML element
		 * @param {String} className The name of the class that has to be added
		 * @example
		 * Estate.CSSTools.AddClass( document.getElementById('elementId'), 'className')
		 */
		AddClass: function( el, className ) {
			var error;
			error = Estate.Check.Element( el );
			if ( error != "" ) throw new Error( error );
			error = Estate.Check.VariableType( className, "string" )
			if ( error != "" ) throw new Error( error );


			if ( el.className.indexOf( className ) < 0) {
				el.className += " "+ className
			}
		},

  		/**
		 * Removes class to an element. This function is used for components in the Estate object that musn't use jQuery. If that's not the case, just use jQuery
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} el An HTML element
		 * @param {String} className The name of the class that has to be removed
		 * @example
		 * Estate.CSSTools.RemoveClass( document.getElementById('elementId'), 'className')
		 */
		RemoveClass: function( el, className ) {
			var error;
			error = Estate.Check.Element( el );
			if ( error != "" ) throw new Error( error );
			error = Estate.Check.VariableType( className, "string" )
			if ( error != "" ) throw new Error( error );


			while ( el.className.indexOf( className ) >= 0) {
				el.className = el.className.replace( " "+ className, "" )
				el.className = el.className.replace( className, "" )
			}
		},
		

  		/**
		 * Toggles class on the body between "fontSize1" and and empty string.
		 * The class can be recoded to cater
		 * more font sizes: "fontSize1", "fontSize2", etc.
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} el An HTML element
		 * @example
		 * Estate.Custom.ToggleFontSize(linkTextHolder)
		 */
        ToggleFontSize: function(linkTextHolderId) {
            if (arguments.length > 0) {
                var error
                error = Estate.Check.ElementById(linkTextHolderId);
                if (error != "") throw new Error(error);
            }


            var fontSizeClassName = "FontSize"
            var smallerTextLink = "Kleinere letters"
            var largerTextLink = "Grotere letters"

            if (Estate.Cookies.Get("fontSize") == null) {
                Estate.Cookies.Set("fontSize", "1")
                Estate.CSSTools.AddClass(document.body, fontSizeClassName + '1')
                if (linkTextHolderId != undefined) {
                    document.getElementById(linkTextHolderId).innerHTML = smallerTextLink
                }
            } else {
                Estate.Cookies.Delete("fontSize")
                Estate.CSSTools.RemoveClass(document.body, fontSizeClassName + '1')
                if (linkTextHolderId != undefined) {
                    document.getElementById(linkTextHolderId).innerHTML = largerTextLink
                }
            }
        }
	}
	/* END PUBLIC */
})();






/**
 * @namespace Adds a function to an event of a single element. Use this if
 * you don't want to use jQuery
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.Events = ( function() {
	/* Start public */
	return {
  		/**
		 * Adds a function to an event of a single element
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} element The element on which the event is placed
		 * @param {Function} newFunction The function that has to be linked to the event
		 * @param {String} eventType Name of the event
		 * @example
		 * Estate.Events.AddEvent( document.getElementById('elementId'), functionName, "onclick" )
		 */
		AddEvent: function( element,
						   	newFunction,
							eventType
							  ) {
			var error;
			error = Estate.Check.VariableType( element, "object" )
			if ( error != "" ) throw new Error( error );
			error = Estate.Check.VariableType( newFunction, "function" )
			if ( error != "" ) throw new Error( error );
			
			
			var oldEvent = eval("element." + eventType);
			var eventContentType = eval("typeof element." + eventType)
			
			if ( eventContentType != 'function' ) {
				eval("element." + eventType + " = newFunction")
			} else {
				eval("element." + eventType + " = function(e) { oldEvent(e); newFunction(e); }")
			}
		}
	}
	/* End public */
})();






/**
 * @namespace Creates a gallery from a predefined list with images.
 * @requires jQuery
 * @see ESDN for related HTML and CSS
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-03-16
 * @constructor
 * @example
 * var galleryConfig
 * galleryConfig.containerSelector = "#Gallery";
 * var gallery = new Estate.Gallery();
 * gallery.Run(galleryConfig);
 */
Estate.Gallery = (function() {
	/**
	 * The gallery class
	 * @class
	 * @constructs
	 */
	var Class = function() {
		var config = {
			containerSelector: "#Gallery",
			popupAnimationSpeed: 250,
			showImageSizeMedium: true,
			showImageSizeLarge: true,
			thumbWidth: 80,
			thumbHeight: 80,
			thumbLoopType: "continuous",
			thumbScrollSpeed: 200,
			backgroundOpacity: 0.5,
			navigationTemplate: "<div style='width: 100%; height: 0; clear: both;'></div> <span class='status'></span> <span class='back'></span> <span class='forward'></span>",
			mediumSizeTemplate: "<a class='mediumSize'><img alt=''></a>"
		}
		var pager;

		/**
		 * Returns object literal with URL to medium and large size version of the thumbnail
		 * @ignore
		 */
		function getImageSet(el) {
			var imageSet = {
				medium: "",
				large: ""
			}
			var imageSetArray = jQuery(el).attr("rel").split(",");
			imageSet.medium = imageSetArray[0];
			imageSet.large = imageSetArray[1];

			return imageSet;
		}

		/**
		 * Shows selected medium size image & adds link to large size image
		 * @ignore
		 */
		function setMediumSize(gallery, imageSet) {
			if (config.showImageSizeMedium == true) {
				jQuery(gallery).find("a.mediumSize img").attr("src", imageSet.medium);
				if (config.showImageSizeLarge == true) {
					jQuery(gallery).find("a.mediumSize").attr("href", imageSet.large);
					jQuery(gallery).find("a.mediumSize").click(function() {
						showPopup(jQuery(this).attr("href"))
						return (false);
					})
				}
				jQuery(gallery).find("span.status").text((pager.ImageIndex() + 1) + " van " + pager.ImageCount())
			} else if (config.showImageSizeLarge == true) {
				showPopup(imageSet.large);
			}
		}

		/**
		 * Moves focus to an image in the thumblist
		 * @ignore
		 */
		function scroll(gallery, index, scrollType) {
			pager.ImageIndex(index, scrollType);

			var imageSet = getImageSet(jQuery(gallery).find("li:eq(" + (pager.ImageIndex()) + ") img"))
			setMediumSize(gallery, imageSet);

			if (pager.ThumbBarIndexSkip() != false) {
				jQuery(gallery).find("ul").css("margin-left", -((pager.ThumbBarIndexSkip() * config.thumbWidth) + 1) + "px")
			}

			if (Estate.IsIE6) {
				jQuery(gallery).find("ul").animate({ marginLeft: -((pager.ThumbBarIndex() * (config.thumbWidth / 2)) + 1) }, config.thumbScrollSpeed)
			} else {
				jQuery(gallery).find("ul").animate({ marginLeft: -((pager.ThumbBarIndex() * config.thumbWidth) + 1) }, config.thumbScrollSpeed)
			}
		}

		/**
		 * Shows large image in div popup
		 * @ignore
		 */
		function showPopup(url) {
			var popupHTML = ''
			var noEventsAttached = false;
			popupHTML += '<div id="DivPopupWindowGallery" class="divPopupWindow">'
			popupHTML += '	<span class="divPopupContent">'
			popupHTML += '		<span class="close">Sluiten <span>x</span></span>'
			popupHTML += '		<br />'
			popupHTML += '		<img src="" alt="" id="DivPopupImage">'
			popupHTML += '	</span>'
			popupHTML += '</div>'
			popupHTML += '<div id="DivPopupBackgroundGallery" class="divPopupBackground"></div>'

			if (jQuery("#DivPopupWindowGallery").size() == 0) {
				jQuery("body").append(popupHTML)
				noEventsAttached = true
			}
 			jQuery("#DivPopupImage").attr("src", url)
			jQuery("#DivPopupWindowGallery").slideDown(config.popupAnimationSpeed, function() { jQuery('html, body').animate({ scrollTop: 0 }, 500) });
			jQuery("#DivPopupBackgroundGallery").css("opacity", 0)
			jQuery("#DivPopupBackgroundGallery").show()
			jQuery("#DivPopupBackgroundGallery").animate({ opacity: config.backgroundOpacity }, config.popupAnimationSpeed);
			
			if (noEventsAttached == true) {
				jQuery("#DivPopupWindowGallery span.close").click(function() {
					jQuery("#DivPopupWindowGallery").slideUp(config.popupAnimationSpeed);
					jQuery("#DivPopupBackgroundGallery").animate({ opacity: 0 }, config.popupAnimationSpeed, function() { jQuery("#DivPopupBackgroundGallery").hide() });
				})
			}
		}

		/**
		 * updates the internal config literal
		 * @ignore
		 */
		function setConfig(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, 1);
			if (error != "") throw new Error(error);
			error = Estate.Check.LiteralUpdatable(config, newConfig);
			if (error != "") throw new Error(error);

			if (newConfig != null) {
				Estate.Check.UpdateLiteral(config, newConfig)
			}
		}



		/* START PUBLIC */
		return function(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, [0, 1]);
			if (error != "") throw new Error(error);

			if (arguments.length > 0) {
				setConfig(newConfig)
			}

			/**
			 * Finds all containers in the selectors and makes galleries of it.
			 *
			 * @since 1.0 - 2010-02-23
			 * @version 1.0 - 2010-02-23
			 * @param {Object} [newConfig] Configuration object.
			 * @param {String} [newConfig.containerSelector] The selector to the gallery container
			 * @param {Number} [newConfig.popupAnimationSpeed] Duration of the div popup in milliseconds
			 * @param {Boolean} [newConfig.showImageSizeMedium] If false, no popup is offered
			 * @param {Boolean} [newConfig.showImageSizeLarge] If false, no popup is offered
			 * @param {Number} [newConfig.thumbWidth] Width of a single thumbnail
			 * @param {Number} [newConfig.thumbHeight] Height of a single thumbnail
			 * @param {String} [newConfig.thumbLoopType] Looping through the thumbnails is either 'scrollback' or 'continuous'
			 * @param {Number} [newConfig.thumbScrollSpeed] Duration of animating to the new thumb
			 * @param {Number} [newConfig.backgroundOpacity] Opacity level of the layer below the popup that obscures the website
			 * @param {String} [newConfig.navigationTemplate] HTML template of the navigation (arrow buttons)
			 * @param {String} [newConfig.mediumSizeTemplate] HTML template of the medium size image
			 * @example
			 * var galleryConfig
			 * galleryConfig.containerSelector = "#Gallery";
			 * var gallery = new Estate.Gallery();
			 * gallery.Run(galleryConfig);
			 */
			this.Run = function() {
				jQuery(config.containerSelector).each(function() {
					var gallery = this;
					var totalListItems = 0;

					if (config.thumbLoopType == "continuous") {
						totalListItems = jQuery(gallery).find("ul").html()
						jQuery(gallery).find("ul").html(totalListItems + totalListItems)
					}

					var imageSet = getImageSet(jQuery(gallery).find("li:first img"))
					jQuery(gallery).find("ul").width(jQuery(gallery).find("li").size() * config.thumbWidth);   // Set width thumbs container
					jQuery("body").addClass("JsEnabled"); /* rm */
					jQuery(gallery).prepend(config.mediumSizeTemplate)   // Shows medium size image
					jQuery(gallery).append(config.navigationTemplate)   // Add navigation to gallery

					// Create pager instance
					var pagerConfig = {
						imageCount: jQuery(gallery).find("li").size(),
						thumbLoopType: config.thumbLoopType
					}
					pager = new Estate.Gallery.Pager(pagerConfig)

					// Selects first item in the gallery
					setMediumSize(gallery, imageSet)
					scroll(gallery, pager.ImageIndex(), "")

					// Handles click on a thumbnail
					jQuery(gallery).find("li img").click(function() {
						var index = jQuery(gallery).find("ul li").index(jQuery(this).parent());
						scroll(gallery, index + 1, "goto")
						var imageSet2 = getImageSet(jQuery(this))
						setMediumSize(gallery, imageSet2)
					})

					// Handles click on back button
					jQuery(gallery).find("span.back").click(function() {
						scroll(gallery, -1, "move")
					})

					// Handles click on forward button
					jQuery(gallery).find("span.forward").click(function() {
						scroll(gallery, 1, "move")
					})
				})
			}
		}
		/* END PUBLIC */
	} ()

	return Class;
})();






/**
 * @namespace Helper class for Estate.Gallery. Manages the logic behind the
 * navigation through the thumbs
 * @class
 * @constructor
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-03-16
 */
Estate.Gallery.Pager = (function() {
	var Class = function() {
		var config = {
			imageCount: 0,
			imageIndex: 1,
			imagePreviousIndex: 1,
			thumbLoopType: "undefined"
		}

		function setConfig(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, 1);
			if (error != "") throw new Error(error);
			error = Estate.Check.LiteralUpdatable(config, newConfig);
			if (error != "") throw new Error(error);

			if (newConfig != null) {
				Estate.Check.UpdateLiteral(config, newConfig)
			}
		}

		function getImageIndex() {
			if (config.thumbLoopType == "continuous") {
				var imageIndex = config.imageIndex - config.imageCount
				if (imageIndex < 0) {
					imageIndex = (config.imageCount - 1)
				}
				return imageIndex;
			} else if (config.thumbLoopType == "scrollback") {
				return config.imageIndex;
			}
		}

		function setImageIndex(index, moveCursorType) {
			config.imagePreviousIndex = config.imageIndex;
			if (moveCursorType == "move") {
				config.imageIndex += index; // Moves cursor
			} else if (moveCursorType == "goto") {
				config.imageIndex = index - 1; // Sets cursor
			}

			if (config.thumbLoopType == "scrollback") {
				if (config.imageIndex < 0) {
					// scrolls from first to last
					config.imageIndex = config.imageCount - 1;
				}
				if (config.imageIndex >= config.imageCount) {
					// scrolls from last to first
					config.imageIndex = 0;
				}
			} else if (config.thumbLoopType == "continuous") {
				if (config.imageIndex < (config.imageCount - 2)) {
					// scrolls from first to last
					config.imageIndex = (config.imageCount * 2) - 3;
				}
				if ((config.imageIndex + 1) >= (config.imageCount * 2)) {
					// scrolls from last to first
					config.imageIndex = config.imageCount - 1;
				}
			}
		}

		function thumbBarIndex() {
			var moveIndex = config.imageIndex - 1;

			if (config.thumbLoopType == "scrollback") {
				if (moveIndex < 1) {
					moveIndex = 0;
				}
				if (moveIndex > config.imageCount - 3) {
					moveIndex = config.imageCount - 3;
				}
			}
			return moveIndex;
		}



		/* START PUBLIC */
		return function(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, [0, 1]);
			if (error != "") throw new Error(error);

			if (arguments.length > 0) {
				setConfig(newConfig)
				if (config.thumbLoopType == "continuous") {
					config.imageCount = config.imageCount / 2
					config.imageIndex = config.imageCount
				}
			}

			this.Init = function(newConfig) {
				setConfig(newConfig)
			},

			// Image count getter
			this.ImageCount = function() {
				return config.imageCount
			},

			// Returns value of what the new selected image index should be
			this.ThumbBarIndex = function() {
				return thumbBarIndex();
			},

			// Indicates if there has to be a jump when a user is moving through the image index.
			this.ThumbBarIndexSkip = function() {
				var difference = config.imagePreviousIndex - config.imageIndex;
				if (difference > 1) {
					return thumbBarIndex() - 1;
				} else if (difference < -1) {
					return thumbBarIndex() + 1;
				} else {
					return false;
				}
			},

			// Either gets or sets the index of the selected image
			this.ImageIndex = function(index, moveCursorType) {
				if (arguments.length == 0) {
					return getImageIndex();
				} else {
					setImageIndex(index, moveCursorType);
				}
			}
		}
		/* END PUBLIC */
	} ()

	return Class;
})();






/**
 * @namespace Helper methods for placing absolute positioned boxes
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.Layers = ( function() {
	/* Start public */
	return {
  		/**
		 * Returns the position of the element on the y-axis
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} obj The element on which the event is placed
		 * @example
		 * Estate.Layers.GetPositionX( document.getElementById('elementId') )
		 */
		GetPositionX: function(obj) {
			var error
			error = Estate.Check.Element( obj );
			if ( error != "" ) throw new Error( error );


			var x = 0
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					x += obj.offsetLeft
					obj = obj.offsetParent
				}
			} else 	if (obj.x) {
				x += obj.x
			}
			return x
		},
		
  		/**
		 * Returns the position of the element on the x-axis
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} obj The element on which the event is placed
		 * @example
		 * Estate.Layers.GetPositionX( document.getElementById('elementId') )
		 */
		GetPositionY: function(obj) {
			var error
			error = Estate.Check.Element( obj );
			if ( error != "" ) throw new Error( error );


			var y = 0
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					y += obj.offsetTop
					obj = obj.offsetParent
				}
			} else 	if (obj.y) {
				y += obj.y
			}
			return y
		}
	}
	/* End public */
})();







/**
 * @namespace Loads Javascript library when needed
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.LoadLibrary = (function() {
	/* Start public */
	return {
  		/**
		 * Loads Google analytics
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} trackerCode Key string that Google provided for this website
		 * @example
		 * Estate.LoadLibrary.GoogleAnalytics("UA-9330954-1")
		 */
		GoogleAnalytics: function(trackerCode) {
			var script = document.createElement("script");
			var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
			script.type = "text/javascript";
			script.src = gaJsHost + "google-analytics.com/ga.js";
			document.body.appendChild(script);
			try {
				var pageTracker = _gat._getTracker(trackerCode);
				pageTracker._trackPageview();
			}
			catch(err) {}
		}
	}
	/* End public */
})();






/**
 * @namespace Navigation related methods
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.Navigation = ( function() {
	/* Start public */
	return {
  		/**
		 * Navigates one step backwards in browser history. If no browser
		 * history is available the static link in the href is used
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @example
		 * <a href="contact.asp" onclick="return Estate.Navigation.LinkBack()">Terug</a>
		 */
		LinkBack: function () {
			if ( history.length > 0 ) {
				history.go( -1 )
				return false
			} else {
				return true
			}
		}
	}
	/* End public */
})();






/**
 * @namespace Manages html popups.
 * @requires jQuery
 * @see ESDN for related HTML and CSS
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 * @constructor
 * @example
 * var popupConfig = {
 *     animationSpeed: 250
 * }
 * var popup = new Estate.Popup(popupConfig);
 * popup.Open();
 */
Estate.Popup = (function() {
	/**
	 * The popup class
	 * @class
	 * @constructs
	 */
	var Class = function() {
		var isIE6 = /MSIE 6/i.test(navigator.userAgent)
		var popCounter = 0
		var config = {
			popupCookieName: "deactivated",
			popupCookieExpire: new Date(2015, 12, 31, 23, 59, 59, 0),
			popupDeactivated: false,
			scrollToWindow: false,
			animationSpeed: 250,
			closeText: "x",
			windowSelector: "#DivPopupWindow",
			backgroundSelector: "#DivPopupBackground",
			backgroundOpacity: 0.5
		}

		function isActive() {
			checkDeactivation();
		}

		function checkDeactivation() {
			if (Estate.Cookies.Get(config.popupCookieName) == null) {
				config.popupDeactivated = false;
			} else {
				config.popupDeactivated = true;
			}
		}

		function scrollToPopupWindow() {
			if (config.scrollToWindow == true) {
				jQuery('html, body').animate({ scrollTop: jQuery(config.windowSelector).offset().top }, 500)
			}
		}

		function setConfig(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, 1);
			if (error != "") throw new Error(error);
			error = Estate.Check.LiteralUpdatable(config, newConfig);
			if (error != "") throw new Error(error);

			if (newConfig != null) {
				Estate.Check.UpdateLiteral(config, newConfig)
			}
		}

		function close() {
			jQuery("flash").innerHTML = "";
			jQuery(config.windowSelector).slideUp(config.animationSpeed);
			jQuery(config.backgroundSelector).animate({ opacity: 0 }, config.animationSpeed, function() { jQuery(config.backgroundSelector).hide() });
		}

		/* Start public */
		/**
		 * Creates popup object
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {Object} [newConfig] Configuration object.
		 * @param {String} [newConfig.popupCookieName] The name of the cookie that determines if a popup should be shown or not
		 * @param {Date} [newConfig.popupCookieExpire] The date and time  on which the cookie will expire
		 * @param {Boolean} [newConfig.popupDeactivated] The value of the cookie that determines if a popup should be shown or not
		 * @param {Boolean} [newConfig.scrollToWindow] If true, the page will be scrolled to the popup window
		 * @param {Number} [newConfig.animationSpeed] Sets the animation speed in milliseconds
		 * @param {String} [newConfig.closeText] Sets the text in the close button
		 * @param {String} [newConfig.windowSelector] The selector of the popup div
		 * @param {String} [newConfig.backgroundSelector] The selector of the background div
			 * @param {Number} [newConfig.backgroundOpacity] Opacity level of the layer below the popup that obscures the website
		 * @see ESDN for related HTML and CSS
		 * @example
		 * var popupConfig = {
		 *     animationSpeed: 250
		 * }
		 * var popup = new Estate.Popup(popupConfig);
		 * popup.Open();
		 */
		return function(newConfig) {
			var error
			error = Estate.Check.ArgumentsCount(arguments.length, [0, 1]);
			if (error != "") throw new Error(error);

			if (arguments.length > 0) {
				setConfig(newConfig)
			}

			/**
			 * Shows popup
			 */
			this.Open = function() {
				isActive()

				if (config.popupDeactivated == false) {
					jQuery(config.windowSelector).find("div.close").text(config.closeText);
					jQuery(config.windowSelector).slideDown(config.animationSpeed, scrollToPopupWindow);
					jQuery(config.backgroundSelector).css("opacity", 0)
					jQuery(config.backgroundSelector).animate({ opacity: config.backgroundOpacity }, config.animationSpeed);
					jQuery(config.backgroundSelector).show()
					if (isIE6 == true) {
						jQuery(config.backgroundSelector).css("height", jQuery("body").height() + "px")
					}
					if (popCounter == 0) {
						jQuery(config.backgroundSelector + ", " + config.windowSelector + " div.close").click(function() {
							close()
						})
						jQuery(config.windowSelector + " input.tag_deactivate").change(function() {
							if (jQuery(this).is(':checked')) {
								Estate.Cookies.Set(config.popupCookieName, "true", config.popupCookieExpire, "/", "", false);
							} else {
								Estate.Cookies.Delete(config.popupCookieName);
							}
						})
					}
					popCounter++
				}
			},
			
			/**
			 * Hides popup
			 */
			this.Close = function() {
				close();
			}
		}
		/* End public */
	} ()

	return Class;
})();





/**
 * @namespace Helper functions for Sitefinity
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.Sitefinity = (function() {
	/* Start public */
	return {
  		/**
		 * Indicates if a user is editing the page
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @example
		 * Estate.Sitefinity.IsInEditMode()
		 */
		IsInEditMode: function() {
			var pagemode = Estate.StringTools.GetQueryString("cmspagemode")
			if (pagemode == "edit") {
				return true;
			} else {
				return false;
			}
		}
	}
	/* End public */
})();






/**
 * @namespace Some string manipulation methods
 * @class
 * @since 1.0 - 2010-02-23
 * @version 1.0 - 2010-02-23
 */
Estate.StringTools = ( function() {
	function TranslateScrambledAddress( string ) {
		var returnString = "";
		var aCharacters;

		string = string.replace( "scrambled:", "" )
		string = string.substring( 1, (string.length - 1) )
		aCharacters = string.split("][")
		for ( var i = 0; i < aCharacters.length; i++ ) {
			returnString += String.fromCharCode( aCharacters[i] )
		}

		return returnString
	}

	/* Start public */
	return {
		/**
		 * Returns the filename is it exists in the address bar
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @example
		 * var filename = Estate.StringTools.GetFilenameFromUrl()
		 */
		GetFilenameFromUrl: function() {
			var error;
			error = Estate.Check.ArgumentsCount( arguments.length, 0 );
			if ( error != "" ) throw new Error( error );


	        var file_name = document.location.href;
	        var end = ( file_name.indexOf("?") == -1 ) ? file_name.length : file_name.indexOf("?");
	        return file_name.substring( file_name.lastIndexOf("/")+1, end );
		},

		/**
		 * Removes extension from a filename
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} eventType Filename with attachment
		 * @example
		 * var filename = Estate.StringTools.GetFilenameFromUrl()
		 */
		GetFilenameWithoutExtension: function( str ) {
			var str = str.replace(/^\s|\s$/g, "");
			if (/\.\w+$/.test(str)) {
				var m = str.match(/([^\/\\]+)\.(\w+)$/);
				if (m)
					return m[1];
				else
					return "no file name";
			} else {
				var m = str.match(/([^\/\\]+)$/);
				if (m)
					return m[1];
				else
					return "no file name";
			}
		},

		/**
		 * Removes measurement unit from a string. So '14px' and '1em' become '14' and '1'
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} SuppliedString
		 * @example
		 * width = Estate.StringTools.RemoveMeasurement( width )
		 */
		RemoveMeasurement: function( SuppliedString ) {
			var error;
			error = Estate.Check.ArgumentsCount( arguments.length, 1 );
			if ( error != "" ) throw new Error( error );
			error = Estate.Check.VariableType( SuppliedString, "string" );
			if ( error != "" ) throw new Error( error );


			var StringWithoutMeasure = SuppliedString
			StringWithoutMeasure = StringWithoutMeasure.replace( "px", "" )
			StringWithoutMeasure = StringWithoutMeasure.replace( "em", "" )
			StringWithoutMeasure = StringWithoutMeasure.replace( "pt", "" )
			
			return StringWithoutMeasure
		},

		/*
		 * Summary:
		 * Searches all scrambled mailto:links and unscrambles them
		 *
		 * Requires:
		 * jQuery
		 *
		 * Usage:
		 * Estate.StringTools.ShowScrambledMailtoLinks()
		 */
		/*ShowScrambledMailtoLinks: function() {
			var error;
			error = Estate.Check.ArgumentsCount( arguments.length, 0 );
			if ( error != "" ) throw new Error( error );

			//alert( jQuery( 'a[href^="scrambled:"]').size() )
			var mailaddress
			jQuery( 'a[href^="scrambled:"]').each( function() {
				mailaddress = TranslateScrambledAddress( jQuery(this).attr('href') )
				jQuery(this).attr( 'href', "mailto:" + mailaddress )
				jQuery(this).html( mailaddress )
			})
		},*/

		/**
		 * Returns the value of variable in a GET url
		 *
		 * @since 1.0 - 2010-02-23
		 * @version 1.0 - 2010-02-23
		 * @param {String} SuppliedString
		 * @example
		 * // http://www.domain.com/index.aspx?foo=bar
		 * value = Estate.StringTools.GetQueryString("foo")
		 */
		GetQueryString: function(variable) {
			var query = window.location.search.substring(1);
			var vars = query.split("&");
			for (var i = 0; i < vars.length; i++) {
				var pair = vars[i].split("=");
				if (pair[0] == variable) {
					return pair[1];
				}
			}
		}
	}
	/* End public */
})();
