/**
 * 
 * Igglo namespace
 * ---------------
 * 
 * Igglo is the namespace for all custom javascript classes. It handles 
 * dynamic class and stylesheet loading by ajax, initializing of scripts 
 * with different priorities and some other utility functions like console 
 * logging. 
 * 
 * 
 * 
 * 
 * 
 * Dynamic Javascript class and stylesheet loading
 * -----------------------------------------------
 * 
 * Classes in the Igglo namespace can be defined in this file or they can 
 * simply exist in the file system in corresponding directory. The basic 
 * principle is that if the needed class is not already defined (i.e. it does
 * not exist in the namespace), it is then loaded with ajax. For example an 
 * instance of a class is created with following syntax:  
 * 
 * var myInstance = new (Igglo('MyPackage.MyClass'))(parameter1, parameter2);
 * 
 * It is the same as a normal syntax would be:
 * 
 * var myInstance = new Igglo.MyPackage.MyClass(parameter1, parameter2);
 * 
 * Igglo, which is actually a function, checks if "MyPackage.MyClass" exists 
 * under the Igglo namespace. If it "MyClass" or even the whole "MyPackage" 
 * is omitted, Igglo then tries to load first the file that contains 
 * "MyPackage" and the the file that contains "MyClass" and evaluates scripts 
 * inside them. The files themselves handles the definition of the classes as 
 * usual.
 * 
 * A class even in another namespace can be dynamically loaded also with 
 * different syntax:
 * 
 * Igglo.loadClass('MyNamespace.MyPackage.MyClass');
 * 
 * The corresponding file is located in 
 * .../lib/MyNamespace/MyPackage/MyClass.js.
 * 
 * Stylesheets can be loaded in the same way using Igglo.loadStylesheet().
 * 
 * 
 * 
 * 
 * 
 * Script initialization
 * ---------------------
 * 
 * All scripts should be initialized with Igglo.addInitializer(). For example:
 * 
 * Igglo.addInitializer(function() {
 *     alert('Hello world!');
 * });
 * 
 * A second parameter can be added that stands for priority. Priorities are: 
 * Igglo.PRIORITY_HIGH, Igglo.PRIORITY_MODERATE and Igglo.PRIORITY_LOW. 
 * Default priority is medium.
 * 
 * All initalizers are triggered with Igglo.initialize() which is called with 
 * defer (i.e. setTimeout(..., 0)) in the end of the html, right before 
 * </body>. It corresponds DOMContentLoaded event and is much faster than 
 * triggering scripts with onload event.
 * 
 * 
 * 
 * 
 * 
 * Logging
 * -------
 * 
 * Firebug console methods are enabled in the Igglo namespace. For example:
 * 
 * Igglo.log('Hello world!');
 * 
 * Logging is disabled by default in browsers that do not support it and in 
 * production mode.
 * 
 * 
 * @author Sakari Tuominen
 * 
 */





Igglo = function(className) {
	return Igglo.loadClass('Igglo.' + className);
}

Object.extend(Igglo, {

	initialized: false,

	initializers: {},

	classes: [],

	stylesheets: [],
	
	initialize: function() {

		if (this.initialized) {
			return;
		}

		this.initialized = true;

		Igglo.log('Igglo: Initializing (context: ' + (opener ? 'popup' : (top != window ? 'frame' : 'default')) + ').');

		var priorities = [Igglo.PRIORITY_HIGH, Igglo.PRIORITY_MODERATE, Igglo.PRIORITY_LOW];

		for (var i = 0; i < priorities.length; i++) {
			var priority = priorities[i];
			if (this.initializers[priority]) {
				for (var j = 0; j < this.initializers[priority].length; j++) {
					this.callInitializer(this.initializers[priority][j], priority);
				}				
			}
		}

	},

	addInitializer: function(initializer, priority) {

		if (!priority) {
			priority = Igglo.PRIORITY_MODERATE;
		}

		if (!this.initializers[priority]) {
			this.initializers[priority] = [];
		}

		this.initializers[priority].push(initializer);
		
		if (this.initialized) {
			// If already initialized, run at once
			this.callInitializer(initializer, priority);
		}

	},
	
	callInitializer: function(initializer, priority) {
		
		setTimeout(function() {
			
			var startTime = (new Date()).getTime();
			
			initializer();
			
			Igglo.log('Igglo: Initializer (', initializer, ') called with priority ' + priority + ' at ' + startTime + ' (took ' + ((new Date()).getTime() - startTime) + ' ms).');
			
		}, priority);
		
	},

	loadClass: function(classPath) {

		if (/[^A-Za-z\.]/.test(classPath)) {
			Igglo.error('Igglo: Class (' + classPath + ') contains illegal characters. Available characters are: A-Za-z.');
			return;
		}
		
		// Start crawling the path and load any missing pieces
		
		var path = classPath.split('.'), object = window;

		for (var i = 0; i < path.length; i++) {
		
			className = path[i];
			
			if (!object[className]) {
				
				var classPath = path.slice(0, i + 1).join('.');
				
				new Ajax.Request(
					Igglo.Url.revisionize('/lib/' + classPath.replace(/\./g, '/') + '.js', true),
					{
						method: 'get',
						asynchronous: false
					}
				);
	
				this.classes.push(classPath);
	
				Igglo.log('Igglo: Class loaded: ' + classPath);
				
			}
			
			object = object[className];
			
		}
		
		return object;

	},

	loadStylesheet: function(stylesheetName, media) {

		if (/[^A-Za-z\.]/.test(stylesheetName)) {
			Igglo.error('Igglo: Stylesheet (' + stylesheetName + ') contains illegal characters. Available characters are: A-Za-z.');
			return;
		}

		if (this.stylesheets.indexOf(stylesheetName) >= 0) {
			return;
		}
		
		this.stylesheets.push(stylesheetName);

		if (!this.headElement) {
			this.headElement = document.getElementsByTagName('head')[0];
		}

		var 
			href = Igglo.Url.revisionize('/lib/' + stylesheetName.replace(/\./g, '/') + '.css'),
			linkElements = this.headElement.getElementsByTagName('link');
		
		for (var i = 0; i < linkElements.length; i++) {
			if (linkElements[i].href == href) {
				Igglo.warn('Igglo: Stylesheet (', href, ') already loaded.');
				return;
			}
		}
		
		var linkElement = document.createElement('link');

		linkElement.rel = 'stylesheet';
		linkElement.type = 'text/css';

		if (media) {
			linkElement.media = media;
		}

		linkElement.href = href;

		this.headElement.appendChild(linkElement);

		Igglo.log('Igglo: Stylesheet loaded: ' + stylesheetName);

	},
	
	PRIORITY_HIGH: 1,
	
	PRIORITY_MEDIUM: 2,

	PRIORITY_LOW: 3
	
});





/**
 * Extend Igglo with Console methods
 */
(function() {

	if (typeof PRODUCTION == 'undefined' || PRODUCTION) {
		function consolify() {}
	}
	else {
		if (typeof console != 'undefined') {
			function consolify(method, parameters) {
				console[method].apply(console, parameters);		
			}
		}
		else {
			function consolify(method, parameters) {
				if (['error', 'warn'].indexOf(method) >= 0) {
					var arraynizedParameters = [];
					for (var i = 0; i < parameters.length; i++) {
						if (typeof parameters[i] == 'object') {
							if (parameters[i] instanceof Array) {
								arraynizedParameters[i] = $A(parameters[i]).inspect();
							}
							else {
								arraynizedParameters[i] = $H(parameters[i]).inspect();
							}
						}
						else {
							arraynizedParameters[i] = parameters[i].toString();
						}
					}
					//alert(method.toUpperCase() + ': ' + arraynizedParameters);
				}
			}
		}
	}
	
	Object.extend(Igglo, {
		
		log: function() {
			consolify('log', arguments)			
		},
		
		debug: function() {
			consolify('debug', arguments)			
		},
		
		warn: function() {
			consolify('warn', arguments)			
		},
		
		error: function() {
			consolify('error', arguments)			
		},
		
		info: function() {
			consolify('info', arguments)			
		}
		
	});
	
})();





/**
 * Global registry to store stuff to, works between windows and frames too
 * Note that dom objects cannot be moved to other windows or frames
 * 
 * @author Sakari Tuominen
 * 
 */
Igglo.Registry = {
	
	registry: {},
	
	set: function(key, value) {
		this.registry[key] = value;
	},
	
	get: function(key) {
		return this.registry[key];		
	}
	
};

try {
	Igglo.Registry.registry = opener ? opener.Igglo.Registry.registry : (top != window ? top.Igglo.Registry.registry : {}); // Creates an object reference to parent window's registry if present
}
catch (e) {}





/**
 * Utility for creating urls for js and css files
 * 
 * @author Sakari Tuominen
 * @param {Object} url
 * @param {Object} front
 */
Igglo.Url = {

	revisionize: function(url, front) {
		return (front || false ? URL_FRONT : URL_STATIC) + url + REVISION_URL;
	}

};





/**
 * Create nested html structures with ease  
 * 
 * @author Sakari Tuominen
 * @param {Object} tagName
 * @param {Object} attributes
 * @param {Object} content
 */
Igglo.Element = function(tagName, attributes, content) {

	var element;

	if (Prototype.Browser.IE) { // IE is so stupid
		var html = '<' + tagName;
		if (attributes && (attributes.type || attributes.name)) {
			html += (attributes.type ? ' type="' + attributes.type + '"' : '') + (attributes.name ? ' name="' + attributes.name + '"' : '');
			delete attributes.type, attributes.name;
		}
		html += '>';
		element = document.createElement(html);
	}
	else {
		element = document.createElement(tagName);
	}

	if (attributes) {
		for (var key in attributes) {
			var attribute = document.createAttribute(key);
			attribute.value = attributes[key];
			element.setAttributeNode(attribute);
		}
	}

	if (content) {
		if (!(content instanceof Array)) {
			content = [content];
		}
		for (var i = 0; i < content.length; i++) {
			if (content[i]) {
				if (content[i].nodeType) { // Content was a DOM object
					element.appendChild(content[i]);
				}
				else {
					var divElement = document.createElement('div');
					divElement.innerHTML = content[i].toString(); // Must go through innerHTML for entities to work (unescapeHTML() would do it anyway)
					while (divElement.firstChild) {
						element.appendChild(divElement.firstChild);
					}
					delete divElement;
				}
			}
		}
	}

	return element;

};





/**
 * Igglo.Form makes forms self posting using Igglo.Request. It processes 
 * responses, marks errors fields, disables submitting during posting,  
 * detects changes on edit, serializes and unserializes. Igglo.Form can not be 
 * used when form contais a file input.
 * 
 * Class method request() can be overwritten for example when posting form into
 * a iframe. See Igglo.LoginBox for an example.
 *  
 * 
 * Usage:
 * 
 * new Igglo.Form(
 *     formElement,
 *     {
 *         cacheable: true, // If form resposens can be cached, used for example in search page
 *         describeErrors: true, // Marks errorneous fields with red color
 *         onChange: function() {
 *             // Triggered when changes are made to the form
 *         },
 *         onResponse: function(response) {
 *             // Triggered when response is returned
 *         }
 *     }
 * );
 *  
 *  
 *
 * @author Sakari Tuominen
 * 
 */
Igglo.Form = Class.create({
	
	cacheable: false,
	
	submitting: false,
	
	describeErrors: false,
	
	initialize: function(form, object) {
		
		this.form = form;
		
		// Extend with options
		if (object) {
			Object.extend(this, object);	
		}
		
		// Register submit event
		Event.observe(this.form, 'submit', this.handleSubmit.bindAsEventListener(this));
		
		// Find submit button if present and register listening events
		var tags = ['INPUT', 'SELECT', 'TEXTAREA'];
		for (var i = 0; i < this.form.elements.length; i++) {
			var element = this.form.elements[i];
			if (!this.submitButton && element.type == 'submit') {
				this.submitButton = element;
			}
			if (tags.indexOf(element.tagName) >= 0) {
				if (Prototype.Browser.IE && (element.type == 'checkbox' || element.type == 'radio')) { // Do it the IE way - http://www.nabble.com/Checkbox-onchange-event-differences-between-IE-and-FF-to10339472.html
					Event.observe(element, 'click', this.change.bind(this));
				}
				else {
					Event.observe(element, 'change', this.change.bind(this));
				}
			}
		}
		
		// Initialize autoclearing
		Igglo.Form.clearize(this.form);
		
	},
	
	/**
	 * Get form's action				
	 */
	getAction: function() {
		
		// getAttribute cannot be used, because IE has some nice features if input field which name is "action" is present
		var attribute = this.form.getAttributeNode('action');
		
		return attribute ? attribute.value : ''; 
		
	},

	/**
	 * Get form's method
	 */
	getMethod: function() {
		
		var attribute = this.form.getAttributeNode('method');
		
		return attribute ? attribute.value : 'get';
		
	},
	
	/**
	 * Get form's parameters
	 */
	getParameters: function() {
	
		return this.serialize();
			
	},
	
	handleSubmit: function(event) {
		
		Event.stop(event);
		
		this.submit();
		
	},
	
	isSubmitting: function() {
		return this.submitting;
	},
	
	submit: function() {
		
		if (!this.submitting) {

			this.submitting = true;
			
			if (this.onBeforeSubmit) {
				this.onBeforeSubmit(); // Hook
			}
			
			// Disable submit button
			if (this.submitButton) {
				this.submitButton.disabled = true;
			}
			
			Igglo.Form.unerrorizeElements(this.form);
			
			this.request();
			
			if (this.onSubmit) {
				this.onSubmit(); // Hook	
			}
		
		}
		
		return this;
		
	},
	
	abort: function() {
		
		if (this.isSubmitting()) {
			 
			if (this.transport) {
				this.transport.abort();
			}
			
			this.submitting = false;
							
			// Enable submit button
			if (this.submitButton) {
				this.submitButton.disabled = false;
			}
			
		}
		 
	},
	
	/**
	 * Where the actual request takes place. Can be overwritten for example with "this.form.submit()" if form is posted to an iframe
	 */
	request: function() {
		
		this.transport = new Igglo.Request(
			this.getAction(),
			{
				format: 'json',
				cacheable: this.cacheable,
				method: this.getMethod(),
				parameters: this.getParameters(),
				onComplete: this.handleResponse.bind(this),
				onException: this.handleException.bind(this),
				visible: true
			}
		);
		
	},
	
	reset: function() {
		
		this.form.reset();
		
		return this;
			
	},
	
	change: function() {
		
		Igglo.info('Igglo.Form: Change detected!');
		
		if (this.onChange) {
			this.onChange(); // Hook	
		}
		
		return this;
		
	},
	
	handleResponse: function(response) {
	
		this.submitting = false;
						
		// Enable submit button
		if (this.submitButton) {
			this.submitButton.disabled = false;
		}
		
		if (Igglo.Response.isError(response)) {
			Igglo.Form.errorizeElements(this.form, Igglo.Response.getErrorFields(response), this.describeErrors);
		}
		
		if (this.onResponse) {
			this.onResponse(response); // Hook
		}
				
	},
	
	handleException: function() {
		
		this.submitting = false;
		
		// Enable submit button
		if (this.submitButton) {
			this.submitButton.disabled = false;
		}
		
	},
	
	serialize: function() {
		
		Igglo.Form.clear(this.form); // Clear alt texts			
		
		var parameters = Form.serialize(this.form, true); 
		
		Igglo.Form.unclear(this.form); // Unclear alt texts
		
		return parameters;
		
	},
	
	unserialize: function(object) {
		Igglo.Form.unserialize(this.form, object);
		return this;
	}
	
});





/**
 * Extend Igglo.Form with some utility methods 
 * 
 * @author Sakari Tuominen
 */
Object.extend(Igglo.Form, {

	unserializeElements: function(elements, hash) {
		elements.each(function(element) {
			if (!element.disabled && element.name) {
				var key = element.name;
				if (hash[key] != undefined) {
					Igglo.Form.Element.setValue(element, hash[key]);
				}
			}
		});
	},

	/**
	 * Does the opposite of Prototype's Form.serialize. Prototype's 
	 * serialize() supports unserilizing too, but it is buggy in some cases. 
	 * 
	 * @param {Object} form
	 * @param {Object} hash
	 */
	unserialize: function(form, hash) {
		Igglo.Form.unserializeElements(Form.getElements(form), hash);
	},

	unerrorizeElements: function(form) {
		Element.select(form, '.' + 'errorized-form').invoke('remove');
		Element.select(form, '.' + 'errorized').invoke('removeClassName', 'errorized');
		Element.select(form, '.' + 'errorized-description').invoke('remove');
	},

	errorizeElements: function(form, errorFields, describeErrors) {
		
		this.unerrorizeElements(form);
		
		if (errorFields) {
		
			if (describeErrors) {
				
				var errorElement = Igglo.Element('div', {'class': 'errorized-form'}, 
					[
						Igglo.Element('strong', null, 'common/form/error/title'.translate()),
						Igglo.Element('br'),
						'common/form/error/description'.translate()
					]
				);
				
				if (form.firstChild) {
					form.insertBefore(errorElement, form.firstChild);
				}
				else {
					form.appendChild(errorElement);	
				}
			
			}
			
			for (var name in errorFields) {
				var element = form.elements[name];
				if (element) {
					/**
					 * Element can be an array of elements, so we add the error message after the last element
					 */
					Igglo.Form.errorizeElement(element.length ? element[element.length - 1] : element, errorFields[name], describeErrors);
				}
			}
			
		}
		
	},
	
	errorizeElement: function(element, errorFields, describeErrors) {

		Element.addClassName(element, 'errorized')
		
		// Find label element
		var labelElement = element.id ? Element.select(element.form, 'label[for=' + element.id + ']')[0] : Element.up(element, 'label');  
		
		if (labelElement) {
			Element.addClassName(labelElement, 'errorized');
		}
		
		// Add error messages
		if (errorFields && describeErrors) {
			
			var errorElements = [];

			/**
			 * errorFields can be an array if custom error messages are used
			 */
			if (errorFields instanceof Array) {
				for (var i = 0; i < errorFields.length; i++) {
					errorElements.push(Igglo.Element('div', null, errorFields[i]));
				}
			}
			else {
				for (var name in errorFields) {
					errorElements.push(Igglo.Element('div', null, errorFields[name]));
				}
			}
	
			if (errorElements.length > 0) {
				element.parentNode.appendChild(Igglo.Element('div', {'class': 'errorized-description'}, errorElements));
			}
			
		}
		
	}, 

	clearize: function(element) {
	
		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.clearize(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt) {
			this.unclear(element);
			Event.observe(element, 'focus', this.clear.bind(this, element));
			Event.observe(element, 'blur', this.unclear.bind(this, element));
		}
		
	},

	clear: function(element) {
		
		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.clear(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt && element.value == element.alt) {
			element.value = '';
			Element.removeClassName(element, 'blur');
		}

	},

	unclear: function(element) {

		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.unclear(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt && (element.value == element.alt || !element.value)) {
			Element.addClassName(element, 'blur');
			element.value = element.alt;
		}

	},
	
	isCleared: function(element) {
		
		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				if (!this.isCleared(element.elements[i])) {
					return false;
				}
			}
			return true;
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt) {
			return element.value != element.alt;
		}
		
		return true;
		
	}

});





Igglo.Form.Element = {
		
	setValue: function(element, value) {
		element = $(element);
		var method = element.tagName.toLowerCase();
		Igglo.Form.Element.Unserializers[method](element, value);
	}

};





Igglo.Form.Element.Unserializers = {

	input: function(element, value) {
		switch (element.type.toLowerCase()) {
			case 'checkbox':
			case 'radio':
				Igglo.Form.Element.Unserializers.inputSelector(element, value);
				break;
			default:
				Igglo.Form.Element.Unserializers.textarea(element, value);
		}
	},

	inputSelector: function(element, value) {
		element.checked = value instanceof Array ? value.indexOf(element.value) >= 0 : value == element.value;
	},

	textarea: function(element, value) {
		element.value = value;
	},

	select: function(element, values) {
		if (typeof(values) != 'object') {
			values = [values.toString()];
		}
		for (var i = 0; i < element.options.length; i++) {
			var opt = element.options[i];
			if (values.indexOf(opt.value.toString()) != -1) {
				opt.selected = true;
			}
		}
	}

};





/**
 * Utility methods for rendering media paths etc. 
 *
 * @author Sakari Tuominen
 */
Igglo.Media = {

	path: function(media, version) {
		var mediaId;
		if (typeof media == 'object') {
			if (media.status != STATUS_ACTIVE) {
				return URL_FRONT + '/media/render/media_id/' + media.media_id + '/version/' + version; 			
			}
			else {
				mediaId = media.media_id;
			}
		}
		else {
			mediaId = media;
		}
		return URL_MEDIA + '/' + Math.ceil(mediaId / 500) + '/' + version + '/' + mediaId + '.jpg';
	},
	
	findPrimaryMedia: function(medias) {
		for (var i = 0; i < medias.length; i++) {
			if (medias[i].status == STATUS_ACTIVE) {
				return medias[i];
			}
		}
	},
	
	placeholder: function(cardType, version, additionalType) {
		
		var path;
		
		switch (cardType) {
			case CARD_TYPE_AD_APARTMENT_RENT:
			case CARD_TYPE_AD_APARTMENT_SELL:
			case CARD_TYPE_BUILDING:
				path = 'building/' + additionalType;
				break;
			case CARD_TYPE_AD_BUSINESS_RENT:
			case CARD_TYPE_AD_BUSINESS_SELL:
				path = 'business';
				break;
			case CARD_TYPE_AD_FARM_SELL:
				path = 'farm';
				break;
			case CARD_TYPE_AD_LOT_SELL:
				path = 'lot';
				break;
			case CARD_TYPE_AD_PARKING_RENT:
			case CARD_TYPE_AD_PARKING_SELL:
				path = 'parking';
				break;
			case CARD_TYPE_AD_VACATIONHOME_RENT:
			case CARD_TYPE_AD_VACATIONHOME_SELL:
				path = 'vacationhome';
				break;
		}
		
		return '/lib/images/placeholder/' + path + '/' + version + '.gif';
		
	}
	
};





/**
 * Magical "Session" that stores data into window.name. All data is 
 * automatically stored when page is unloaded. 
 * 
 * Usage:
 * 
 * Igglo.Session.myValue = myValue;
 * 
 * var myValue = Igglo.Session.myValue.
 * 
 * @author Sakari Tuominen
 */ 
Igglo.Session = {};

(function() {
	
	if (window.name) {
		try {
			Object.extend(Igglo.Session, eval('(' + window.name + ')'));
			Igglo.log('Igglo.Session: ', window.name);
		}
		catch(e) {
			// window.name was not a json string
		}
	}
	
	Event.observe(window, 'unload', function() {
		window.name = $H(Igglo.Session).toJSON();
	});
	
})();





/**
 * A helper for getting and setting cookie values
 *
 *
 * @author Mikko Hämäläinen
 * @since 05.03.2007
 * @package JS
 * @subpackage Common
 */
Igglo.Cookie = {

	/**
	 * Sets a cookie
	 * @param {String} name
	 * @param {String} value
	 * @param {Float} expires
	 */
	set: function(name, value, expires) {
		var expire = '';
		var path = '; path=/';
		if (expires) {
			var d = new Date();
			d.setTime(d.getTime() + (1000 * parseFloat(expires)));
			expire = '; expires=' + d.toGMTString();
		}

		return (document.cookie = escape(name) + '=' + escape(value || '') + expire + path);
	},

	/**
	 * Returns the value of the cookie $name
	 * @param {String} name
	 * @return {Object}|null The cookie value or null if the cookie is not set
	 */
  	get: function(name) {
		var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
		return (cookie ? unescape(cookie[2]) : null);
  	}

};





/**
 * Generic static user class
 * 
 * Observes event 'igglo:user-login'.
 * Fires event 'igglo:user-update'.
 *  
 * @author Sakari Tuominen 
 * @author Mikko Hämäläinen <mikko.hamalainen@brainalliance.com>
 */
Igglo.User = {

	initialized: false,

	initialize: function() {

		if (!this.user) {
			this.update();
		}
		
		if (!this.initialized) {
		
			this.initialized = true;
			
			Event.observe(document, 'igglo:user-login', this.update.bind(this));
			
		}

	},

	update: function() {
		
		var user, lifetime;

		new Igglo.Request(
			'user/get',
			{
				format: 'json',
				method: 'post', // Post to avoid caching
				asynchronous: false,
				onComplete: function(response) {
					user = response.user;
					lifetime = response.lifetime;
				}
			}
		);

		this.user = user || {};

		Event.fire(document, 'igglo:user-update');
		
		this.flush.bind(this).delay(lifetime);

		Igglo.log('Igglo.User: User updated, level ' + this.getUserLevel() + ', lifetime ' + lifetime + '.');

	},
	
	flush: function() {
		delete this.user;
	},

	/**
	 * Returns the requested property from user instance
	 * @param {String} property
	 * @return {Object} the requested property
	 */
	get: function(key) {
		this.initialize();
		return this.user[key];
	},

	/**
	 * Sets a property of the user instance. The property must already be available, no new properties can be
	 * assigned with this.
	 * @param {String} property
	 * @param {Object} value
	 * @return {Object} The value assigned
	 * @throws Exception if the property is not known
	 */
	set: function(key, value) {
		this.initialize();
		if (!this.user[key]) {
			throw 'Tried to set unknown property value';
		}
		this.user[key] = value;
		return value;
	},

	/**
	 * Returns the level of the user
	 * @return {Integer} Type constant representing the user class
	 */
	getUserLevel: function() {
		this.initialize();
		return this.user.user_level;
	},

	getInstance: function() {
		this.initialize();
		return this.user;
	}

};





/**
 * Igglo.MainNavigation sets the correct section selected on the main 
 * navigation. The correct section is determined based on the selected section 
 * (obviously), selected tab or the selected section on the previous page. 
 * Uses Igglo.Session to store the selected section.
 * 
 * 
 * @author Sakari Tuominen
 */
Igglo.MainNavigation = {

	initialize: function(object) {

		Object.extend(this, object);

		
		// Find the selected tabs to determine which section might be selected
		
		var sectionIndexes = [];

		for (var a = 0; a < this.sectionElements.length; a++) {

			if (Element.hasClassName(this.sectionElements[a], 'section-selected')) { // This overrides everything
				this.sectionIndex = a;
				break;
			}

			var tabElements = Element.select(this.tabsElements[a], 'li.tab');

			for (var b = 0; b < tabElements.length; b++) {
				if (Element.hasClassName(tabElements[b], 'tab-selected')) {
					if (sectionIndexes.indexOf(a) < 0) {
						sectionIndexes.push(a);
					}
				}
			}

		}

		if (this.sectionIndex == undefined) {
			
			// If more than one tabs are selected, default to first section with selected tab
			if (sectionIndexes.length > 0) {
				this.sectionIndex = sectionIndexes[0];
			}
			else {
				this.sectionIndex = 0;
			}
	
			// Fuzzy logic: if more than one section is selected, let the magical session do the decision
			if (Igglo.Session.MainNavigation) {
				if (sectionIndexes.length > 0) {
					if (sectionIndexes.indexOf(Igglo.Session.MainNavigation.sectionIndex) >= 0) {
						this.sectionIndex = Igglo.Session.MainNavigation.sectionIndex;
					}
				}
				else {
					this.sectionIndex = Igglo.Session.MainNavigation.sectionIndex;
				}
			}
			
		}

		
		
		Igglo.Session.MainNavigation = {sectionIndex: this.sectionIndex};

		Element.addClassName(this.sectionElements[this.sectionIndex], 'section-selected');

	},
	
	getSectionIndex: function() {
		return this.sectionIndex;
	}

};





/**
 * Igglo.UserNavigation creates the user menu on the right top corner of a 
 * page. 
 * 
 * Observes event 'igglo:user-update'.
 * 
 * 
 * @author Sakari Tuominen
 * @param {Object} object
 */
Igglo.UserNavigation = {

	initialize: function(object) {

		Object.extend(this, object);

		while (this.element.firstChild) {
			this.element.removeChild(this.element.firstChild);
		}
		
		this.element.appendChild(this.registerElement = Igglo.Element('div', {'id': 'user-navigation-register'}));
		this.element.appendChild(this.loginLogoutElement = Igglo.Element('div', {'id': 'user-navigation-loginlogout'}));
		
		this.update();
		
		Event.observe(document, 'igglo:user-update', this.update.bind(this));

	},

	update: function() {

		// Empty

		while (this.registerElement.firstChild) {
			this.registerElement.removeChild(this.registerElement.firstChild);
		}
		while (this.loginLogoutElement.firstChild) {
			this.loginLogoutElement.removeChild(this.loginLogoutElement.firstChild);
		}
		
		// Update
				
		if (Igglo.User.getUserLevel() >= USER_TYPE_USER_REGISTERED || Igglo.User.get('firstname')) {
			
			this.registerElement.appendChild(
				Igglo.Element('span', {'id': 'user-navigation-hello'}, 'common/user_navigation/hello'.translate() + ' ' + Igglo.User.get('firstname') + '.')
			);
			
			this.registerElement.appendChild(Igglo.Element('br'));
			
			this.registerElement.appendChild(
				this.menuElement = Igglo.Element('span', {'id': 'user-navigation-menu'},
					Igglo.Element('span', {'id': 'user-navigation-menu-expander'}, 'common/user_navigation/menu'.translate())
				)
			);
			
		}

		if (Igglo.User.getUserLevel() >= USER_TYPE_USER_REGISTERED) {
			
			var linkElements = [
				Igglo.Element('a', {'href': '/user-earmarks', 'class': 'link-3'}, 'common/user_navigation/user_earmarks'.translate())
			];
			
			if (Igglo.User.get('COMPANY_ID') && Igglo.User.get('company_has_contract')) {
				if (Igglo.User.get('company_admin')) {
					linkElements.push(Igglo.Element('a', {'href': '/pro/info/', 'class': 'link-3'}, 'common/user_navigation/pro/edit-company'.translate()));
				}
				
				linkElements.push(Igglo.Element('a', {'href': '/pro/announcements', 'class': 'link-3'}, 'common/user_navigation/pro/ads'.translate()));
				linkElements.push(Igglo.Element('a', {'href': '/pro/leads', 'class': 'link-3'}, 'common/user_navigation/pro/leads'.translate()));
				linkElements.push(Igglo.Element('a', {'href': '/pro/stats', 'class': 'link-3'}, 'common/user_navigation/pro/stats'.translate()));
			}
			else {
				linkElements.push(Igglo.Element('a', {'href': '/user-ads', 'class': 'link-3'}, 'common/user_navigation/user_ads'.translate()));
			}
			
			// moved to last IGGLO-4043
			linkElements.push(Igglo.Element('a', {'href': 'http://' + OIKOTIE_HOST + '/omat_tiedot/' + encodeURIComponent(Igglo.User.get('login')), 'class': 'link-3'}, 'common/user_navigation/user_edit'.translate()));

			this.menuElement.appendChild(
				Igglo.Element('span', {'id': 'user-navigation-menu-expansion'},
					linkElements
				)
			);
			
			Event.observe(this.menuElement, 'mouseover', this.expand.bindAsEventListener(this));
			Event.observe(this.menuElement, 'mouseout', this.contract.bindAsEventListener(this));
			
			this.loginLogoutElement.appendChild(
				this.logoutElement = Igglo.Element('a', {'href': '/logout', 'class': 'link-2'}, 'common/user_navigation/log_out'.translate())
			);
			
		}
		else {
			
			this.loginLogoutElement.appendChild(
				this.loginElement = Igglo.Element('a', {'href': '', 'class': 'link-2'}, 'common/user_navigation/log_in'.translate())
			);
			
			if (Igglo.User.get('firstname')) {
				
				this.loginLogoutElement.appendChild(Igglo.Element('br'));
				
				this.loginLogoutElement.appendChild(
					this.notLarsPepiElement = Igglo.Element('a', {'href': '', 'class': 'link-4'}, 'common/user_navigation/i_am_not'.translate() + ' ' + Igglo.User.get('firstname') + '.')
				);
				
				Event.observe(this.menuElement, 'click', this.login.bindAsEventListener(this));
				Event.observe(this.notLarsPepiElement, 'click', this.notLarsPepi.bindAsEventListener(this));
				
			}
			else {
				
				this.registerElement.appendChild(
					Igglo.Element('a', {'href': 'https://' + OIKOTIE_HOST + '/login?exit=to_registration', 'class': 'link-2'}, 'common/user_navigation/register'.translate())
				);
				
				this.loginLogoutElement.appendChild(Igglo.Element('br'));
	
				this.loginLogoutElement.appendChild(
					this.forgotPasswordElement = Igglo.Element('a', {'href': '', 'class': 'link-4'}, 'common/user_navigation/forgot_password'.translate())
				);
				
				Event.observe(this.forgotPasswordElement, 'click', this.forgotPassword.bindAsEventListener(this));
				
			}

			
			Event.observe(this.loginElement, 'click', this.login.bindAsEventListener(this));

		}

	},

	login: function(event) {
		Event.stop(event);
		(new Igglo.LoginBox()).positionByEvent(event, 15, 15).enable();
	},

	forgotPassword: function(event) {
		Event.stop(event);
		(new Igglo.LoginBox()).positionByEvent(event, 15, 15).handleForgotPassword(event).enable();
	},

	notLarsPepi: function(event) {
		Event.stop(event);
		(new Igglo.LogoutBox()).positionByEvent(event, 15, 15).enable();
	},

	expand: function(event) {
		if (this.expandTimeout) {
			clearTimeout(this.expandTimeout);
		}
		else {
			Element.addClassName(this.menuElement, 'expanded');
		}
	},

	contract: function(event) {
		this.expandTimeout = (function() {
			if (this) {
				Element.removeClassName(this.menuElement, 'expanded');
				delete this.expandTimeout;
			}
		}).bind(this).delay(0.5);
	}

};





/**
 * Igglo.Popupper listens click events on the page. If an anchor with 
 * target="popup*" is clicked then a new window (popup) is opened.
 *  
 * @author Sakari Tuominen
 */
Igglo.Popupper = {

	initialize: function() {
		Event.observe(document.body, 'click', this.handleClick.bindAsEventListener(this));
	},

	handleClick: function(event) {
		var element = Event.findElement(event, 'A');
		if (element && (element.target || '').substr(0, 5) == 'popup') {
			open(
				'',
				element.target,
				{
					popup: 'width=700,height=500,scrollbars=yes,resizable=no', // Same as default
					popup_default: 'width=700,height=500,scrollbars=yes,resizable=no',
					popup_small: 'width=500,height=450,scrollbars=yes,resizable=no',
					popup_large: 'width=900,height=600,scrollbars=yes,resizable=no',
					popup_design_instructions: 'width=700,height=600,scrollbars=no,resizable=no',
					popup_design: 'width=900,height=650,scrollbars=no,resizable=yes',
					popup_rent_example: 'width=900,height=650,scrollbars=yes,resizable=no',
					popup_pro_example: 'width=600,height=650,scrollbars=yes,resizable=no'					
				}[element.target]
			).focus();
		}
	}

};





/**
 * Igglo busy creates an rolling gif image to the right top corner of the 
 * page. Can be customized with specified parentElement (default is 
 * document.body).
 * 
 * @author Sakari Tuominen
 * @param {Object} object
 */
Igglo.Busy = Class.create({

	className: 'busy',

	initialize: function(object) {

		Object.extend(this, object);

		this.element = Igglo.Element('div', {'class': this.className});

		(this.parentElement || document.body).appendChild(this.element);
		
		if (Prototype.Browser.IE6) {
			this.fixed = new Igglo.Proprietary.FixedElement(this.element);
			this.fixed.enable();
		}

		this.timeout = this.disable.bind(this).delay(30); // Timeout to disable busy if something goes wrong

	},

	disable: function() {

		clearTimeout(this.timeout);

		if (Prototype.Browser.IE6) {
			this.fixed.disable();
		}
		
		(this.parentElement || document.body).removeChild(this.element);
		
		delete this.element;

	}

});





/**
 * Igglo.Request is a wrapper for Prototype's Ajax.Request. It adds caching, 
 * a rotating gif and automatic error handling.
 * 
 * Usage:
 * 
 * new Igglo.Request(
 *     url, 
 *     {
 *         cacheable: false, // Default is true
 *         method: 'post', // Default is 'get'
 *         visible: true, // Default is false,
 *         asyncronous: false, // Defaul is true
 *         format: 'json', // json or html
 *         onComplete: function(response) {
 *             
 *             // Response can be json object or html in text format
 *             
 *         }
 *     }
 * );
 * 
 * @author Sakari Tuominen
 *  
 */
Igglo.Request = Class.create({

	method: 'get',

	visible: false,

	asynchronous: true,
	
	/**
	 * Global cache object
	 */
	cache: [],
	
	cacheable: false,
	
	initialize: function(action, object) {

		this.action = action;

		this.parameters = {};

		if (object) {
			Object.extend(this, object);
		}

		if (this.visible) {
			this.visualize();
		}
		
		if (this.format) {
			this.parameters.format = this.format;
		}
		
		this.url = this.action.toLowerCase();
		if (this.url.charAt(0) != '/') {
			this.url = '/' + this.url;
		}
		
		this.request();
		
	},

	request: function() {
		
		// Check cache 	
		
		if (this.cacheable) {
		
			this.key = this.url + '?' + $H(this.parameters).toQueryString();
			
			for (var i = 0; i < this.cache.length; i++) {
				if (this.cache[i] && this.cache[i][0] == this.key) { // We has a match!
					// Skip to end
					this.complete(this.cache[i][1]);
					Igglo.log('Igglo.Request: Cache match (index: ' + i + ').');
					return;
				}
			}
			
		}
		
		// No cache match, do the request
		
		this.transport = new Ajax.Request(
			this.url, 
			{
				method: this.method,
				parameters: this.parameters,
				asynchronous: this.asynchronous,
				onComplete: this.handleResponse.bind(this),
				onException: this.handleException.bind(this)
			}
		);
		
	},
	
	abort: function() {
		
		if (this.transport) {
			/**
			 * Prototype does not provide an interface for aborting so do it directly
			 * If readyState is 4 (complete) it is already too late 
			 */
			if (this.transport.transport.readyState != 4) {
				
				this.aborted = true;
				
				var state = Ajax.Request.Events[this.transport.transport.readyState];
				
				try {
					/**
					 * All browsers do not support aborting, but the onComplete will not be called in handleRespose()
					 * as this.aborted is set to true 
					 */
					this.transport.transport.abort();
				}
				catch (e) {
				}
				
				Igglo.info('Igglo.Request: Query aborted (state was \'' + state + '\').' );
				
			}
		}
		
	},

	handleResponse: function(response) {

		/**
		 * If request is aborted, handleResponse is still called
		 * So we just do nothing
		 */
		if (this.aborted) {
			if (this.visible) {
				this.unvisualize();
			}
			return; 
		}

		switch (this.format) {
			case 'json':
				response = response.responseJSON;
				break;
			case 'html':
			default:
				response = response.responseText;
				break;
		}
		
		if (this.cacheable) {
			this.cache.push([
				this.key,
				response
			]);
		}
		
		this.complete(response);
		
	},
	
	handleException: function(object, exception) {
		
		Igglo.warn(
			'Igglo.Request: Request failed (' + object.url + '):',
			exception,
			{
				readyState: object.transport.readyState,
				status: object.transport.status,
				responseText: object.transport.responseText
			}
		);
		
		if (this.onException) {
			this.onException();
		}
		
		if (this.visible) {
			this.unvisualize();
		}
		
	},
	
	complete: function(response) {

		if (this.onComplete) {
			this.onComplete(response);
		}

		if (this.visible) {
			this.unvisualize();
		}

	},

	visualize: function() {
		this.visualizer = new Igglo.Busy();
	},

	unvisualize: function() {
		if (this.visualizer) {
			this.visualizer.disable();
		}
	}

});





/**
 * Igglo.Response is a utility for handling the response object (json only).
 * 
 * @author Sakari Tuominen
 * 
 */
Igglo.Response = {

	getMessages: function(response) {
	
		return response && response.messages instanceof Array ? response.messages : [];
		
	},

	getErrorFields: function(response) {
		
		var 
			messages = this.getMessages(response), 
			errorFields = {},
			hasErrorFields = false;
		
		for (var i = 0; i < messages.length; i++) {
			
			var message = messages[i];
			
			if (typeof message.errorFields == 'object' && !(message.errorFields instanceof Array)) { // Object expected. If errorFields is an array, it's probably empty.
				hasErrorFields = this.processErrorFields(message.errorFields, errorFields) || hasErrorFields;
			}
			
		}
		
		if (hasErrorFields) {
			return errorFields; // Return an object only if errors exist 
		}
		
	},
	
	processErrorFields: function(errorFields, processedErrorFields, path) {
		
		var hasErrorFields = false;
		
		for (var name in errorFields) {
			
			var subPath = path ? path + '[' + name + ']' : name;
			
			if (this.hasSubErrorFields(errorFields[name])) { // If errors have suberrors
				hasErrorFields = this.processErrorFields(errorFields[name], processedErrorFields, subPath) || hasErrorFields;
			}
			else {
				processedErrorFields[subPath] = errorFields[name];
				hasErrorFields = true;
			}
			
		}
		
		return hasErrorFields;
		
	},
	
	hasSubErrorFields: function(object) {
		for (var key in object) {
			if (typeof object[key] == 'object') {
				return true;
			}
		}
		return false;
	},
	
	isSuccess: function(response) {
		
		var messages = this.getMessages(response);
		
		for (var i = 0; i < messages.length; i++) {
			if (messages[i].type != MESSAGE_TYPE_SUCCESS) {
				return false; // All must be success
			}
		}
		
		return true;
		
	},

	isError: function(response) {
		
		var messages = this.getMessages(response);
		
		for (var i = 0; i < messages.length; i++) {
			if (messages[i].type == MESSAGE_TYPE_ERROR) {
				return true; // Only one error is needed
			}
		}
		
		return false;
		
	}

};





/**
 * Igglo.Tooltipper is a helper class for creating tooltips. 
 * 
 * Usage:
 * 
 * new Igglo.Tooltipper({
 *     element: $('myElement'),
 *     content: 'This element here is myElement.'
 * });
 * 
 * @author Sakari Tuominen
 * @param {Object} object
 */
Igglo.Tooltipper = Class.create({

	size: [0, 0], // No size = auto sizing

	initialize: function(object) {

		Object.extend(this, object);

		// Common resource for all tooltips
		if (!this.constructor.instance) {
			this.constructor.instance = (new Igglo.TooltipBox());
		}

		Event.observe(this.element, 'mouseover', this.show.bindAsEventListener(this));
		Event.observe(this.element, 'mouseout', this.hide.bindAsEventListener(this));

	},

	show: function(event) {
		this.constructor.instance.setSize(this.size).setContent(this.content).positionByEvent(event, 10, 10).show();
	},

	hide: function() {
		this.constructor.instance.hide();
	},

	setContent: function(content) {
		this.content = content;
	}

});





/**
 * Igglo.BackButton creates back button dynamically (currently on building 
 * and ad pages). Accepts target and comma separated action names (for custom 
 * link text)
 * 
 * @author Sakari Tuominen
 */
Igglo.BackButton = {

	initialize: function(object) {

		Object.extend(this, object);
		
		if (document.referrer) {

			var 
				path = this.path() || 'common/return',
				element = Igglo.Element('a', {'href': '', 'class': 'link-2'}, path.translate());
				
			Event.observe(element, 'click', this.back.bindAsEventListener(this));
			
			this.element.appendChild(element);

		}

	},
	
	path: function() {
	
		for (var path in this.actions) {
			for (var i = 0; i < this.actions[path].length; i++) {
				if (document.referrer.indexOf(this.actions[path][i]) >= 0) {
					return 'common/return/' + path;
				}
			}
		}
		
	},

	back: function(event) {
		
		Event.stop(event);
		
		history.back(-1);
		
	}

};





/**
 * Igglo.ClassCycler cycles class through for example a table. For example
 * makes every other line's background a diffrent color.  
 *  
 * @author Sakari Tuominen
 * @param {Object} parentSelector
 * @param {Object} childSelector
 * @param {Object} classes
 */
Igglo.ClassCycler = {

	initialize: function(parentSelector, childSelector, classes) {

		var length = classes.length;

		$$(parentSelector).each(function(element) {

			var index = 0;

			element.select(childSelector).each(function(childElement) {

				Element.addClassName(childElement, classes[index]);

				if (++index == length) {
					index = 0;
				}

			});

		});

	}

};





/**
 * Igglo.Instancer is a class instance handler.
 *
 * @author Sakari Tuominen
 *
 */
Igglo.Instancer = {

	add: function(object) {
		if (!object.constructor.instances) {
			object.constructor.instances = [];
		}
		object.constructor.instances.push(object);
		return object.constructor.instances.length - 1; // Returns instance index
	},

	call: function(object, method) {
		if (object.constructor.instances) {
			for (var i = 0; i < object.constructor.instances.length; i++) {
				if (object.constructor.instances[i][method]) {
					object.constructor.instances[i][method]();
				}
			}
		}
		return this;
	},

	get: function(object) {
		return object.constructor.instances;
	}

};





Igglo.Localization = {

	SPACES_FORMAT_SHORT: 1,

	SPACES_FORMAT_LONG: 2,
	
	moneyFormat: function(currency, includeCurrency) {

		if (includeCurrency == undefined) {
			includeCurrency = true;
		}

		var s = '';

		switch (DEFAULT_LANGUAGE) {
			case 'fi':
				s = this.separate(parseInt(currency).toString(), ' ');
				if (includeCurrency) {
					s += ' ' + this.translate('common/currency_symbol');
				}
				break;
			case 'en':
			default:
		}

		return s;

	},

	/**
	 * Adds thousand separator to a number
	 *
	 * @param {Object} source_number
	 * @param {Object} separator, the character used as a thousand separator, default is "."
	 */
	separate: function(value, separator) {

		var parts = [], length = 3; // example: 1.000.300 = 3 digits

		// we'll slice the number to x digit groups (starting from the least significant end) and join them with the separator
		while (value.length > length) {
			parts.unshift(value.substr(value.length - length, length));
			value = value.substr(0, (value.length - length));
		}

		parts.unshift(value);

		return parts.join(separator || '.');

	},

	dateFormat: function(date, format) {

		if (!format) {
			switch (DEFAULT_LANGUAGE) {
				case 'fi':
					format = '%j.%n.%Y';
					break;
				case 'en':
				default:
					format = '%Y-%m-%d';
			}
		}

		var matches;

		if (date instanceof Date) {
			// Do nothing
		}
		else if (matches = date.match(/^(\d\d\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)$/)) {
			date = new Date(
				parseInt(matches[1], 10),
				parseInt(matches[2], 10) - 1,
				parseInt(matches[3], 10),
				parseInt(matches[4], 10),
				parseInt(matches[5], 10),
				parseInt(matches[6], 10)
			);
		}
		else if (matches = date.match(/^(\d\d\d\d)\D(\d\d)\D(\d\d)$/)) {
			date = new Date(
				parseInt(matches[1], 10),
				parseInt(matches[2], 10) - 1,
				parseInt(matches[3], 10)
			);
		}

		if (date) {
			
			var day = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][date.getDay()];

			// Mimics php's date function
			return format.
				replace('%l', ('common/' + day).translate()).
				replace('%D', ('common/short/' + day).translate()).
				replace('%d', date.getDate().toPaddedString(2)).
				replace('%j', date.getDate()).
				replace('%m', (date.getMonth() + 1).toPaddedString(2)).
				replace('%n', (date.getMonth() + 1)).
				replace('%y', date.getFullYear().toPaddedString(2)).
				replace('%Y', date.getFullYear()).
				replace('%H', date.getHours().toPaddedString(2)).
				replace('%G', date.getHours()).
				replace('%i', date.getMinutes().toPaddedString(2)).
				replace('%s', date.getSeconds().toPaddedString(2));

		}

		return '';

	},

	dateFormatNoYear: function(date) {
		var format;
		switch (DEFAULT_LANGUAGE) {
			case 'fi':
				format = '%j.%n.';
				break;
			case 'en':
				format = '%n/%j';
				break;
			default:
				format = '%Y-%n-%j';
				break;
		}

		return this.dateFormat(date, format);
	},

	dayShort: function(dayNum) {

		var day, daysShort = {
			'fi': ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
			'en': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
		};

		if (daysShort[DEFAULT_LANGUAGE] && (day = daysShort[DEFAULT_LANGUAGE][dayNum % 7])) {
			return day;
		}

		return '';

	},

	decimalPoint: function() {

		switch (DEFAULT_LANGUAGE) {
			case 'fi':
				return ',';
			case 'en':
			default:
				return '.';
		}

	},

	sizeFormat: function(size, decimalAlways, unit) {

		var
			sizeIntegerPart = Math.floor(size),
			sizeDecimalPart = Math.round(10 * (size - sizeIntegerPart)), 
			value = sizeIntegerPart.toString();
		
		if (sizeDecimalPart != 0 || decimalAlways) {
			value += this.decimalPoint() + sizeDecimalPart.toString();
		}
		
		if (unit) {
			value += ' ' + 'common/area_unit'.translate();
		}

		return value;

	},

	spacesFormatAd: function(object, format, includeAreal) {

		if (includeAreal == undefined) {
			includeAreal = true;
		}

		if (!object.kitchen) {
			object.kitchen = 1;
		}

		if (object.rooms == null) {
			object.rooms = 0;
		}

		switch ((format || this.SPACES_FORMAT_SHORT)) { // short is default

			case this.SPACES_FORMAT_SHORT:
				return this.spacesFormatAdShort(object, includeAreal);

			case this.SPACES_FORMAT_LONG:
				return this.spacesFormatAdLong(object, includeAreal);

		}

	},

	spacesFormatAdShort: function(object, includeAreal) {

		var s = '';

		switch (DEFAULT_LANGUAGE) {

			case 'fi':
				s += object.rooms + ' ' + this.translate('common/short/rooms') + ' + ' + this.translate('constant/kitchen/short/' + object.kitchen);
				if (object.sauna == 1) {
					s += ' + ' + this.translate('common/short/sauna');
				}
				if (includeAreal && object.size > 0) {
					s += ' ' + object.size.replace('.', ',') + ' ' + this.translate('common/area_unit');
				}
				return s.toLowerCase();

		}

	},

	spacesFormatAdLong: function(object, includeAreal) {

		var s = '';

		switch (DEFAULT_LANGUAGE) {

			case 'fi':
				s += object.rooms + ' ' + this.translate('common/rooms') + ' + ' + this.translate('constant/short/' + object.kitchen);
				if (object.sauna == 1) {
					s += ' + ' + this.translate('common/sauna');
				}
				if (includeAreal && object.size > 0) {
					s += ' ' + object.size.replace('.', ',') + ' ' + this.translate('common/area_unit');
				}
				return s.toLowerCase();

		}

	},

	addressFormat: function(building) {

		var address = building.addresses[0];
		return address.street + ' ' + address.street_number + ' ' + (building.building_letter || '');

	},

	initializeTranslations: function(path) {

		new Igglo.Request(
			'translations/get',
			{
				format: 'json',
				asynchronous: false,
				onComplete: this.onInitializeTranslationsResponse.bind(this)
			}
		);

	},

	onInitializeTranslationsResponse: function(response) {
		this.setTranslations(response && response.translations ? response.translations : {});
	},
	
	setTranslations: function(translations) {
		this.translations = translations;
	},

	translate: function(path, parameters) {

		if (!this.translations) {
			this.initializeTranslations();
		}

		if (this.translations && this.translations[path]) {

			var translation = this.translations[path];

			if (parameters) {
				for (key in parameters) {
					translation = translation.replace(new RegExp('%' + key + '%', 'gi'), parameters[key]);
				}
			}

			return translation;

		}

		Igglo.warn('Igglo.Localization: Translation not found: ' + path);

		return path;

	}
	
};





/**
 * Extends string class that we can call 'translation/path'.translate() 
 */
String.prototype.translate = function(parameters) {
	return Igglo.Localization.translate(this.toString(), parameters);
}





/**
 *
 * Hash manager
 * For storing url parameters to location.hash and observing changes in it
 *
 * @author Sakari Tuominen <sakari.tuominen@igglo.fi>
 *
 */

Igglo.Historizer = {
	
	anchors: [],
	
	anchorElements: [],
	
	initialize: function(observer) {
		
		this.observer = observer;
		
		this.hash = location.hash;
		
		Igglo.info('Igglo.Historizer: ', this.hash);
		
		if (!this.interval) {
			this.interval = setInterval(this.check.bind(this), 250);
		}
		
	},
	
	set: function(object) {

		var hash = $H(object).toQueryString();
	
		if (this.anchors.indexOf(hash) < 0) {
			
			this.anchors.push(hash);
				
			// The actual anchor element has to exist for history item to be saved (in most browsers)
			
			var anchorElement = Igglo.Element('a', {'name': hash});
			anchorElement.style.position = 'fixed'; // Fixes the page moving troubles in map search with Chrome
			anchorElement.style.left = '0';
			anchorElement.style.top = '0';
			document.body.insertBefore(anchorElement, document.body.firstChild);
			this.anchorElements.push(anchorElement);
			
		}
		
		location = '#' + hash;
		
		this.hash = location.hash;
		
	},
	
	get: function() {
		
		if (this.hash.length > 1) {
			return this.hash.substr(1).toQueryParams(); 
		}
		
	},
	
	check: function() {
		
		if (this.hash != location.hash) {
			Igglo.info('Igglo.Historizer: Hash changed!');
			this.hash = location.hash;
			this.observer();
		}
		
	}
	
};

if (Prototype.Browser.IE) { // IE needs special care

	/**
	 * IE does not take a hash change as a new history item like other browsers do so we do things bit differenly by using an iframe.
	 * We change the hash normally and make a page load in the iframe to generate a new history item. If user clicks back or forward button
	 * it only affects the page in the iframe and we can catch it.
	 *  
	 * So we write an iframe tag to every page just in case we need it.
	 * The iframe actually has to be there right from the start for it work properly
	 * and we can not know if we need it before it's too late. So add it anyway.
	 * 
	 * This js file is probably loaded in the html's head so the iframe will be written there too. But it doesn't matter. 
	 * IE moves it to the body where it belongs.
	 */

	document.write('<iframe id="igglo-historizer" style="display: none; height: 0; width: 0;"></iframe>');

	Igglo.Historizer = {
		
		initialize: function(observer) {
			
			this.observer = observer;
			
			this.iframeElement = $('igglo-historizer');
			
			this.hash = location.hash.substr(1);
			
			Event.observe(this.iframeElement, 'load', this.handleIframeLoad.bindAsEventListener(this));
			
		},
		
		set: function(object) {
			
			var hash = $H(object).toQueryString();
			
			/**
			 * The limit for address string in IE is 2083 characters and for the path part 2048 characters.
			 * We respect the senseless limit.
			 */
			location = '#' + hash.substr(0, 2000);
			
			this.hash = hash;
			
			/**
			 * We store the data as JSON because it takes much less characters so can avoid the 2083 
			 * characters limit (at least to some point)
			 */
			this.iframeElement.contentWindow.location = '/history?' + encodeURIComponent($H(object).toJSON());
			
		},
		
		get: function() {
			
			if (this.hash.length > 0) {
				return this.hash.toQueryParams(); 
			}
			
		},
		
		handleIframeLoad: function() {
			
			/**
			 * Change the iframe's title for proper history listing
			 */
			this.iframeElement.contentWindow.document.title = document.title;
			
			var 
				object = decodeURIComponent(this.iframeElement.contentWindow.location.search.substr(1)).evalJSON(),
				hash = object ? $H(object).toQueryString() : ''; 
			
			//this.iframeElement.contentWindow.document.body.innerHTML = hash; // Debug info
			
			if (this.hash != hash) {
				
				this.hash = hash;
				location = hash ? '#' + hash.substr(0, 2000) : ''; // Update hash
							
				/**
				 * Trigger observer only if iframe is initially loaded
				 */
				if (this.iframeInitiallyLoaded) {
					Igglo.info('Igglo.Historizer: Hash changed!');
					this.observer();
				}				
					
			}
			
			/**
			 * If the iframe initially loads (i.e. it's location.href changes from 'about:blank' to a real address) 
			 * it means that it has been used on this page before.
			 * At this point we do not trigger the observer (above), we just update the hash data if it differs from the location.hash.
			 * It might differ because of truncating it to 2000 characters.
			 * 
			 */
			if (!this.iframeInitiallyLoaded) {
				this.iframeInitiallyLoaded = true;
			} 
			
		}
		
	};	
	
}





/**
 * This is the root of all boxes, respect it
 */
Igglo.Box = Class.create({

	modal: false,

	closable: true,

	centerized: false,
	
	size: [0, 0],

	position: [0, 0],
	
	type: 1,

	initialize: function(object) {
		
		Object.extend(this, object);
		
		this.create();
		
	},

	/**
	 * Create-function, creates five enssential components of a box:
	 *
	 * blockElement - block's the background if block is set to true,
	 * containerElement - some kind of wrapper for centering,
	 * element - the actual box element,
	 * closeElement - button for closing the box,
	 * contentElement - this element contains the content of the box,
	 *
	 * create() can be overwritten but containerElement, element and contentElement have to be created
	 *
	 */
	create: function() {
		
		this.element = Igglo.Element('div', {'class': 'box-floater box-floater-type-' + this.type},
			this.contentElement = Igglo.Element('div', {'class': 'box-floater-content'})
		);
		
		if (this.closable) {
			this.element.appendChild(this.closeElement = Igglo.Element('div', {'class': 'box-floater-close'}));
			Event.observe(this.closeElement, 'click', this.close.bindAsEventListener(this));
		}
		
		this.containerElement = Igglo.Element('div', {'class': 'box-floater-container'});
		
		if (this.centerized) {
			Element.addClassName(this.containerElement, 'box-floater-center');
		}
		
		if (this.modal) {
			Element.addClassName(this.element, 'box-floater-modal');
			this.blockElement = Igglo.Element('div', {'class': 'box-floater-block'});
		}
		
		this.containerElement.appendChild(this.shadowElement = Igglo.Element('div', {'class': 'box-floater-shadow'}));
		
		// IE6 needs special care
		if (Prototype.Browser.IE6) {
			this.containerElement.appendChild(this.frameElement = Igglo.Element('iframe', {'src': '', 'frameborder': '0', 'scrolling': 'no', 'class': 'box-floater-frame'}));
		}
		
		this.containerElement.appendChild(this.element);
		
		this.setSize(this.size);
		
		// Start hidden, update() will make the box visible after positioning
		this.containerElement.style.visibility = 'hidden'; 
		
		return this;
		
	},

	empty: function() {
		while (this.contentElement.firstChild) {
			this.contentElement.removeChild(this.contentElement.firstChild);
		}
		this.update();
		return this;
	},

	close: function() {
		this.disable();
		if (this.onClose) { // Hook
			this.onClose();
		}
		return this;
	},

	enable: function() {
		if (!this.enabled) {
			this.enabled = true;
			if (this.modal) {
				document.body.appendChild(this.blockElement);
				if (Prototype.Browser.IE6) { // IE6
					if (!this.blockElementFixed) {
						this.blockElementFixed = new Igglo.Proprietary.FixedElement(this.blockElement);
					}
					this.blockElementFixed.enable();
				}
			}
			document.body.appendChild(this.containerElement);
			if (Prototype.Browser.IE6) { // IE6
				if (this.centerized) {
					if (!this.containerElementFixed) {
						this.containerElementFixed = new Igglo.Proprietary.FixedElement(this.containerElement);
					}
					this.containerElementFixed.enable();
				}
			}
		}
		this.update();
		return this;
	},

	disable: function() {
		if (this.enabled) {
			this.enabled = false;
			if (Prototype.Browser.IE6) { // IE6
				if (this.centerized) {
					this.containerElementFixed.disable();
				}
			}
			document.body.removeChild(this.containerElement);
			if (this.modal) {
				if (Prototype.Browser.IE6) { // IE6
					this.blockElementFixed.disable();
				}
				document.body.removeChild(this.blockElement);
			}
		}
		this.update();
		return this;
	},

	isEnabled: function() {
		return this.enabled;
	},

	show: function() {
		this.enable();
		Element.show(this.containerElement);
		this.update();
		return this;
	},

	hide: function() {
		Element.show(this.containerElement);
		this.update();
		return this;
	},

	isVisible: function() {
		return Element.visible(this.element);
		return this;
	},

	setStyle: function(object) {
		Element.setStyle(this.contentElement, object);
		this.update();
		return this;
	},

	setContent: function(content) {
		return this.empty().appendContent(content);
	},

	appendContent: function(content) {
		if (!(content instanceof Array)) {
			content = [content];
		}
		for (var i = 0; i < content.length; i++) {
			if (typeof(content[i]) == 'string') {
				content[i] = document.createTextNode(content[i]);
			}
			this.contentElement.appendChild(content[i]);
		}
		this.update();
		return this;
	},

	setSize: function(size) {
		this.size = size;
		this.element.style.width = this.size[0] > 0 ? this.size[0] + 'px' : '';
		this.element.style.height = this.size[1] > 0 ? this.size[1] + 'px' : '';
		this.update();
		return this;
	},

	positionByElement: function(element, offsetX, offsetY) {
		this.positionByPosition(Element.cumulativeOffset(element), offsetX, offsetY);
		return this;
	},

	positionByEvent: function(event, offsetX, offsetY) {
		this.positionByPosition([Event.pointerX(event), Event.pointerY(event)], offsetX, offsetY);
		return this;
	},

	positionByPosition: function(position, offsetX, offsetY) {
		if (this.centerized) {
			Element.removeClassName(this.containerElement, 'box-floater-center');
			this.centerized = false;
		}
		this.position = [
			position[0] + (offsetX || 0),
			position[1] + (offsetY || 0)
		];
		this.update();
		return this;
	},

	positionToCenter: function() {
		this.centerized = true;
		Element.addClassName(this.containerElement, 'box-floater-center');
		this.update();
		return this;
	},

	update: function() {

		if (this.enabled) {

			this.containerElement.style.visibility = 'hidden';

			// Move element temporarily to top left corner, because size cannot be calculated if it goes over the right or bottom edge (text wraps and stuff)
			this.element.style.left = 0;
			this.element.style.top = 0;

			var 
				width = this.element.offsetWidth,
				height = this.element.offsetHeight,
				left, 
				top;

			if (this.centerized) {
				left = Math.round(width / -2);
				top = Math.round(height / -2);
			}
			else {
				var limits = this.getLimits();
				left = this.position[0] > limits[1][0] - width - 10 ? Math.max(limits[0][0], limits[1][0] - width - 10) : this.position[0];
				top = this.position[1] > limits[1][1] - height - 10 ? Math.max(limits[0][1], limits[1][1] - height - 10) : this.position[1];
			}
			
			this.element.style.left = left + 'px';
			this.element.style.top = top + 'px';
			
			this.shadowElement.style.left = (left + 2) + 'px';
			this.shadowElement.style.top = (top + 2) + 'px';
			this.shadowElement.style.width = width + 'px';
			this.shadowElement.style.height = height + 'px';
			
			if (Prototype.Browser.IE6) { // IE6
				this.frameElement.style.left = left + 'px';
				this.frameElement.style.top = top + 'px';
				this.frameElement.style.width = width + 'px';
				this.frameElement.style.height = height + 'px';
			}

			this.containerElement.style.visibility = '';
			
		}

	},

	getLimits: function() {
		// Get limits from container element
		if (this.limitingElement) {
			var
				paddingTop = parseInt(Element.getStyle(this.limitingElement, 'padding-top')),
				paddingRight = parseInt(Element.getStyle(this.limitingElement, 'padding-right')),
				paddingBottom = parseInt(Element.getStyle(this.limitingElement, 'padding-bottom')),
				paddingLeft = parseInt(Element.getStyle(this.limitingElement, 'padding-left')),
				offset = Element.cumulativeOffset(this.limitingElement);
			return [
				[offset.left + paddingLeft, offset.top + paddingTop],
				[offset.left + this.limitingElement.clientWidth - paddingRight, offset.top + this.limitingElement.clientHeight - paddingBottom]
			];
		}
		else {
			// document.body.clientWidth/Height are the proper values to be used: http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
			return [
				[0, 0],
				[document.body.clientWidth, document.body.clientHeight]
			];
		}
	}

});

Object.extend(Igglo.Box, {
	
	setLimitingElement: function(limitingElement) {
		this.prototype.limitingElement = limitingElement;
	}
	
});





Igglo.TooltipBox = Class.create(Igglo.Box, {

	initialize: function() {
		
		this.create();
		
	},

	create: function() {
		
		this.containerElement = Igglo.Element('div', {'class': 'box-tooltip-container'},
			this.element = Igglo.Element('div', {'class': 'box-tooltip'},
				this.contentElement = Igglo.Element('div', {'class': 'box-tooltip-content'})
			)
		);
		
		this.setSize(this.size);
		
		this.element.style.visibility = 'hidden'; /* Start hidden, update() will make the box visible after positioning */
		
	}

});





Igglo.MessageBox = Class.create(Igglo.Box, {

	initialize: function(object) {

		Object.extend(this, object);

		this.create();

		if (this.messages) {
			for (var i = 0; i < this.messages.length; i++) {
				this.appendContent(
					Igglo.Element('div', {'class': 'message ' + this.getClass(this.messages[i].type)},
						Igglo.Localization.translate(this.messages[i].translationPath, this.messages[i].translationParameters)
					)
				);
			}
		}

	},

	getClass: function(type) {
		switch (type) {
			case MESSAGE_TYPE_SUCCESS: return 'message-success';
			case MESSAGE_TYPE_INFO: return 'message-info';
			case MESSAGE_TYPE_NOTICE: return 'message-notice';
			case MESSAGE_TYPE_ERROR: return 'message-error';
		}
	},

	enable: function($super, seconds) {

		Igglo.Instancer.call(this, 'disable').add(this);

		$super();

		this.disable.bind(this).delay(seconds || 2);

	}

});





Igglo.DialogBox = Class.create(Igglo.Box, {
	
	closable: false,

	modal: true,
	
	centerized: true,
	
	size: [400, 0],
	
	setTitle: function(title) {
		
		if (!this.titleElement) {
			this.titleElement = Igglo.Element('h1');
			if (this.descriptionElement || this.buttonsElement) {
				this.contentElement.insertBefore(this.titleElement, this.descriptionElement || this.buttonsElement);				
			}
			else {
				this.appendContent(this.titleElement);
			}
		}
		
		/**
		 * innerHTML because translations can contain html tags.
		 * Please do not use to add custom HTML content, use appendContent() for that!
		 */
		this.titleElement.innerHTML = title; 
		
		return this;
		
	},
	
	setDescription: function(description) {
		
		if (!this.descriptionElement) {
			this.descriptionElement = Igglo.Element('p');
			if (this.buttonsElement) {
				this.contentElement.insertBefore(this.descriptionElement, this.buttonsElement);
			}
			else {
				this.appendContent(this.descriptionElement);
			}
		}
		
		/**
		 * innerHTML because translations can contain html tags.
		 * Please do not use to add custom HTML content, use appendContent() for that!
		 */
		this.descriptionElement.innerHTML = description;
		
		return this;
		
	},
	
	addButton: function(object) {
		
		if (!this.buttonsElement) {
			this.appendContent(this.buttonsElement = Igglo.Element('div', {'class': 'box-floater-buttons'}));
		}
		
		var buttonElement = Igglo.Buttonizer.create(object.description, object.type);
		
		if (object.onClick) {
			Event.observe(buttonElement, 'click', object.onClick)
		}
		
		this.buttonsElement.appendChild(buttonElement);
		
		return this;
				
	}
	
});





/**
 * Igglo.ConfirmBox
 * Can be used as confirm or alert
 *
 * @author Sakari Tuominen <sakari.tuominen@igglo.fi>
 *
 * Example:
 *
 * (new Igglo.ConfirmBox(
 *     'Are you sure?',
 *     {
 *         accept: 'Yes',
 *         decline: 'No',
 *         onComplete: function(value) {
 *             new Igglo.ConfirmBox(value);
 *         }
 *     }
 * )).enable();
 *
 */
Igglo.ConfirmBox = Class.create(Igglo.DialogBox, {

	initialize: function($super, title, object) {

		$super();

		this.setTitle(title);

		if (object) {
			Object.extend(this, object);
		}

		if (this.description) {
			this.setDescription(this.description);
		}
		else if (this.content) {
			this.appendContent(this.content);
		}
		
		this.addButton({
			description: this.accept || 'common/close'.translate(),
			onClick: this.onAcceptClick.bindAsEventListener(this)
		});
		
		if (this.decline) {
			this.addButton({
				description: this.decline,
				type: Igglo.Buttonizer.BUTTON_CLASS_SECONDARY,
				onClick: this.onDeclineClick.bindAsEventListener(this)
			});
		}

	},

	onAcceptClick: function(event) {
		
		Event.stop(event);
		
		this.disable();
		
		if (this.onAccept) {
			this.onAccept();
		}
		
		if (this.onComplete) {
			this.onComplete(true);
		}
		
	},

	onDeclineClick: function(event) {
		
		Event.stop(event);
		
		this.disable();
		
		if (this.onDecline) {
			this.onDecline();
		}
		
		if (this.onComplete) {
			this.onComplete(false);
		}
		
	}
	
});





/**
 * Igglo.LoginBox class
 * Uses Igglo.LoginBox action to fecth the template
 *
 * @author Sakari Tuominen <sakari.tuominen@igglo.fi>
 *
 */
Igglo.LoginBox = Class.create(Igglo.Box, {

	size: [275, 0],

	initialize: function(object) {

		Igglo.Instancer.call(this, 'disable').add(this); // Disables other instances of Igglo.LoginBox
		
		Object.extend(this, object);

		this.create();


		Element.addClassName(this.element, 'box-login');


		if (Igglo.User.getUserLevel() >= USER_TYPE_USER_GUEST || !this.loginType || this.loginType == Igglo.LoginBox.LOGIN_TYPE_NORMAL) {

			this.appendContent(
				Igglo.Element('h2', null, 'common/login/log_in'.translate())
			);

			this.createLoginForm();
			
			this.appendContent(
				Igglo.Element('p', null,
					[
						'common/login/not_registered'.translate(),
						Igglo.Element('a', {'href': 'https://' + OIKOTIE_HOST + '/login?exit=to_registration', 'class': 'link-2'}, 'common/login/register'.translate())
					]
				)
			);

		}
		else if (this.loginType == Igglo.LoginBox.LOGIN_TYPE_EARMARK || this.loginType == Igglo.LoginBox.LOGIN_TYPE_REGISTER) {

			this.appendContent(
				Igglo.Element('h2', null, 'common/login/log_in'.translate())
			);
			
			this.createLoginForm();

			this.appendContent(
				Igglo.Element('p', null,
					[
						'common/login/not_registered'.translate(),
						this.notRegisteredElement = Igglo.Element('a', {'href': '', 'class': 'link-2'}, 'common/login/register'.translate())
					]
				)
			);

			Event.observe(this.notRegisteredElement, 'click', this.handleNotRegistered.bindAsEventListener(this));

		}

		this.focusElement = this.loginElement || this.passwordElement;

	},

	enable: function($super) {

		$super();

		if (this.focusElement) {

			// Set focus to input element automatically

			(function() {
				this.focusElement.focus();
			}).bind(this).defer();

		}

	},

	createLoginForm: function() {

		var target = 'login' + (new Date()).getTime();

		this.loginFormElement = Igglo.Element('form', {'action': URL_FRONT.replace(/^http/, 'https') + '/login/handle', 'method': 'post', 'target': target},
			[
				Igglo.Element('input', {'type': 'hidden', 'name': 'format', 'value': 'json'}),			 
				Igglo.Element('table', {'class': 'login'},
					this.loginFormTableBodyElement = Igglo.Element('tbody')
				)
			]
		);

		if (Igglo.User.getUserLevel() >= USER_TYPE_USER_GUEST) {
			
			this.loginFormElement.appendChild(
				Igglo.Element('input', {'type': 'hidden', 'name': 'login', 'value': Igglo.User.get('login')})
			);
			
		}
		else {

			this.loginFormTableBodyElement.appendChild(
				Igglo.Element('tr', null,
					[
						Igglo.Element('th', null,
							Igglo.Element('label', null, 'common/login/login'.translate())
						),
						Igglo.Element('td', null,
							this.loginElement = Igglo.Element('input', {'type': 'text', 'name': 'login', 'value': this.email || '', 'maxlength': 1000})
						)
					]
				)
			);
			
		}
		
		this.loginFormTableBodyElement.appendChild(
			Igglo.Element('tr', null,
				[
					Igglo.Element('th', null,
						Igglo.Element('label', null, 'common/login/password'.translate())
					),
					Igglo.Element('td', null,
						[
							this.passwordElement = Igglo.Element('input', {'type': 'password', 'name': 'password', 'maxlength': 40}),
							this.forgotPasswordElement = Igglo.Element('a', {'href': '', 'class': 'link-4'}, 'common/login/forgot_password'.translate())
						]
					)
				]
			)
		);
		
		this.loginFormTableBodyElement.appendChild(
			Igglo.Element('tr', null,
				[
					Igglo.Element('th'),
					Igglo.Element('td', {'class': 'remember-me'},
						Igglo.Element('label', null, 
							[
								Igglo.Element('input', {'type': 'checkbox', 'name': 'remember_me', 'value': 1, 'checked': 'checked'}),
								'common/login/remember_me'.translate()
							]
						)
					)
				]
			)
		);
		
		this.loginFormTableBodyElement.appendChild(
			Igglo.Element('tr', null,
				[
					Igglo.Element('th'),
					Igglo.Element('td', null,
						Igglo.Buttonizer.create('common/login/submit'.translate(), Igglo.Buttonizer.BUTTON_CLASS_SECONDARY, 'submit')
					)
				]
			)
		);
		
		this.appendContent(
			[
				this.loginFormElement,
				this.iframeElement = Igglo.Element('iframe', {'name': target})
			]
		);
		
		Element.hide(this.iframeElement);
		

		this.loginForm = new Igglo.Form(
			this.loginFormElement,
			{
				request: this.handleIframeSubmit.bind(this),
				onResponse: this.handleLoginResponse.bind(this)
			}
		);
		

		Event.observe(this.iframeElement, 'load', this.handleIframeLoad.bindAsEventListener(this));
		Event.observe(this.forgotPasswordElement, 'click', this.handleForgotPassword.bindAsEventListener(this));

	},
	
	handleIframeSubmit: function() {
		
		if (!this.submitted) {
		
			this.submitted = true;
			
			this.visualizer = new Igglo.Busy();
			
			this.loginForm.form.submit();
			
		}
		
	},
	
	handleIframeLoad: function(event) {

		if (this.submitted) {
			
			this.submitted = false;
			
			this.visualizer.disable();
			
			var response;
			
			try {
				if (Prototype.Browser.IE) {
					response = this.iframeElement.contentWindow.document.body.firstChild.firstChild.nodeValue; // Lets just hope it's there	
				} 
				else {
					response = this.iframeElement.contentWindow.document.body.textContent;
				}
			}
			catch (e) {
				Igglo.error('Iframe login failed (check SSL certificate): ', e);
				return;
			}
			
			this.iframeElement.src = null; // To avoid browser's progress bar stalling
			
			this.loginForm.handleResponse(response.evalJSON());
					
		}
		
	},
	
	handleNotRegistered: function(event) {

		Event.stop(event);

		if (this.loginType == Igglo.LoginBox.LOGIN_TYPE_EARMARK) {
			
			this.disable();
	
			if (this.onNotRegistered) {
				this.onNotRegistered();
			}
			
		}
		else if (this.loginType == Igglo.LoginBox.LOGIN_TYPE_REGISTER) {
			
			this.setContent(
				[
					Igglo.Element('h2', null, 'common/login/register'.translate()),
					this.registerFormElement = Igglo.Element('form', {'action': 'user/create-mini', 'method': 'post', 'class': 'login-form'}, 
						[
							Igglo.Element('table', {'class': 'login-register'},
								Igglo.Element('tbody', null,
									[
										Igglo.Element('tr', null,
											[
												Igglo.Element('th', null,
													Igglo.Element('label', {'class': 'required'}, 
														[
															'common/register/login'.translate(),
															' ',
															Igglo.Element('span', null, '*')
														]
													)
															
												),
												Igglo.Element('td', null,
													this.loginElement = Igglo.Element('input', {'type': 'text', 'name': 'login', 'maxlength': 1000})
												)
											]
										),
										Igglo.Element('tr', null,
											[
												Igglo.Element('th', null,
													Igglo.Element('label', {'class': 'required'}, 
														[
															'common/register/firstname'.translate(),
															' ',
															Igglo.Element('span', null, '*')
														]
													)
												),
												Igglo.Element('td', null,
													Igglo.Element('input', {'type': 'text', 'name': 'firstname', 'maxlength': 120})
												)
											]
										),
										Igglo.Element('tr', null,
											[
												Igglo.Element('th', null,
													Igglo.Element('label', {'class': 'required'}, 
														[
															'common/register/mobile_phone'.translate(),
															' ',
															Igglo.Element('span', null, '*')
														]
													)
												),
												Igglo.Element('td', null,
													Igglo.Element('input', {'type': 'text', 'name': 'mobile_phone', 'maxlength': 60})
												)
											]
										),
										Igglo.Element('tr', null,
											[
												Igglo.Element('th', null, 
													Igglo.Element('label', {'class': 'required'},
														[
															'common/register/terms'.translate(),
															' ',
															Igglo.Element('span', null, '*')
														]
													)
												),
												Igglo.Element('td', {'class': 'terms'},
													Igglo.Element('input', {'type': 'checkbox', 'name': 'terms', 'value': 1})
												)
											]
										),
										Igglo.Element('tr', null,
											[
												Igglo.Element('th'),
												Igglo.Element('td', null,
													Igglo.Buttonizer.create('common/login/register/submit'.translate(), Igglo.Buttonizer.BUTTON_CLASS_PRIMARY, 'submit')
												)
											]
										)
									]
								)
							)
						]
					)
				]
			);

			new Igglo.Form(
				this.registerFormElement,
				{
					onSubmit: this.handleRegisterSubmit.bind(this),
					onResponse: this.handleRegisterResponse.bind(this),
					describeErrors: true	
				}
			);

		}

	},

	handleForgotPassword: function(event) {

		Event.stop(event);

		delete this.loginFormElement, this.loginElement, this.passwordElement, this.forgotPasswordElement;

		this.setContent(
			[
				Igglo.Element('h2', null, 'common/login/forgot_password'.translate()),
				Igglo.Element('div', {'class': 'login-information'},
					Igglo.Element('p', null, 'common/login/forgot_password/description'.translate())
				),
				this.passwordFormElement = Igglo.Element('form', {'action': 'user/forgot-password', 'method': 'post', 'class': 'login-form'},
					Igglo.Element('table', null,
						Igglo.Element('tbody', null,
							[
								Igglo.Element('tr', null,
									[
										Igglo.Element('th', null,
											Igglo.Element('label', null, 'common/login/forgot_password/login'.translate())
										),
										Igglo.Element('th')
									]
								),
								Igglo.Element('tr', null,
									[
										Igglo.Element('td', null,
											this.loginElement = Igglo.Element('input', {'type': 'text', 'name': 'login', 'value': this.email || ''})
										),
										Igglo.Element('td', null,
											this.passwordSubmitElement = Igglo.Buttonizer.create('common/login/forgot_password/send'.translate(), Igglo.Buttonizer.BUTTON_CLASS_PRIMARY, 'submit')
										)
									]
								)
							]
						)
					)
				)
			]
		);

		new Igglo.Form(
			this.passwordFormElement,
			{
				onResponse: this.handleNewPasswordResponse.bind(this)
			}
		);

		// Set focus to input
		(function() {
			this.loginElement.focus();
		}).bind(this).defer();

		return this;

	},

	handleRegisterSubmit: function() {
		
		this.update();
		
	},

	handleRegisterResponse: function(response) {

		if (Igglo.Response.isSuccess(response)) {

			this.disable();

			Event.fire(document, 'igglo:user-login');

			(new Igglo.ConfirmBox(
				'common/login/registered'.translate(),
				{
					description: 'common/login/registered/description'.translate(),
					onComplete: this.handleRegisterComplete.bind(this)
				}
			)).enable();

		}
		else {

			(new Igglo.MessageBox({messages: Igglo.Response.getMessages(response)}))
				.positionToCenter()
				.enable();

			this.update();
			
		}

	},

	handleRegisterComplete: function() {
		if (this.onSuccess) {
			this.onSuccess();
		}
		if (this.onComplete) {
			this.onComplete();
		}
	},

	handleLoginResponse: function(response) {

		this.disable();

		if (Igglo.Response.isSuccess(response)) {
			Event.fire(document, 'igglo:user-login');
			if (this.onSuccess) {
				this.onSuccess(response);
			}
		}
		else {
			if (this.onError) {
				this.onError(response);
			}
		}

		if (this.onComplete) {
			this.onComplete(response);
		}

	},

	handleNewPasswordResponse: function(response) {

		if (Igglo.Response.isSuccess(response)) {

			this.setContent(
				[
					Igglo.Element('h2', null, 'common/login/forgot_password'.translate()),
					Igglo.Element('p', null, 'common/login/forgot_password/sent'.translate())
				]
			);

		}
		else {

			(new Igglo.MessageBox({messages: Igglo.Response.getMessages(response)})).positionByElement(this.passwordFormElement, 100, 25).enable();

		}

	},

	onError: function(response) {
		(new Igglo.MessageBox({messages: Igglo.Response.getMessages(response)})).positionByPosition(this.position, 150, 25).enable();
	}

});

Object.extend(Igglo.LoginBox, {
	LOGIN_TYPE_NORMAL: 1,
	LOGIN_TYPE_EARMARK: 2,
	LOGIN_TYPE_REGISTER: 3
});





Igglo.LogoutBox = Class.create(Igglo.Box, {
	
	size: [400, 0],
	
	initialize: function(object) {
		
		Igglo.Instancer.call(this, 'disable').add(this); // Disables other instances of Igglo.LogoutBox
		
		Object.extend(this, object);
		
		this.create();
		
		this.setContent(
			[
				Igglo.Element('h2', null, 'common/logout/log_out'.translate()),
				Igglo.Element('p', null, 'common/logout/we_remember_you'.translate({'firstname': Igglo.User.get('firstname')})),
				Igglo.Element('p', null, 'common/logout/log_out_instruction'.translate({'firstname': Igglo.User.get('firstname')})),
				Igglo.Element('div', {'class': 'box-floater-buttons'}, 
					this.forgetElement = Igglo.Buttonizer.create('common/logout/submit'.translate(), Igglo.Buttonizer.BUTTON_CLASS_PRIMARY)
				)
			]
		);
		
		this.forgetElement.href = '/logout?delete_cookie=1';
		
	}
	
});





/*
 * Igglo.Buttonizer object for dynamic button creating
 * Uses css-classes that can be found from common.css
 *
 * @author Sakari Tuominen
 *
 */
Igglo.Buttonizer = {

	BUTTON_CLASS_PRIMARY: 'button-primary',
	
	BUTTON_CLASS_SECONDARY: 'button-secondary',
	
	initialize: function(elements) {
		$A(elements).each((function(element) {
			if (!Element.hasClassName(element, 'buttonized')) {
				Element.addClassName(element, 'buttonized');
				element.appendChild(this.structurize($A(element.childNodes)));	
			}
		}).bind(this));
	},
	
	create: function(content, buttonClass, type) {

		var className = 'button buttonized ' + (buttonClass || this.BUTTON_CLASS_PRIMARY);

		if (typeof content == 'string') {
			content = document.createTextNode(content);
		}
		
		if (type) { // If type is specified, we want a <BUTTON>
			return Igglo.Element('button', {'type': type, 'class': className}, this.structurize(content));
		}
		else {
			return Igglo.Element('a', {'class': className}, this.structurize(content));
		}

	},
	
	structurize: function(content) {
		return Igglo.Element('span', {'class': 'button'}, 
			Igglo.Element('span', {'class': 'button-inner'}, 		
				Igglo.Element('span', {'class': 'button-innest'}, content)
			)
		);
	},

	enable: function(element) {
		Element.removeClassName(element, 'disabled');
		return element;
	},

	disable: function(element) {
		Element.addClassName(element, 'disabled');
		return element;
	},
	
	set: function(element, text) {
		var contentElement = Element.hasClassName(element, 'buttonized') ? Element.down(element, 'span', 2) : element;
		if (contentElement.firstChild) {
			contentElement.firstChild.nodeValue = text;
		}
		else {
			contentElement.appendChild(document.createTextNode(text));
		}
	},

	get: function(element) {
		var contentElement = Element.hasClassName(element, 'buttonized') ? Element.down(element, 'span', 2) : element;
		return contentElement.firstChild ? contentElement.firstChild.nodeValue : null;
	}

};





Igglo.Earmark = {

	initialized: false,
	
	earmarks: {},
	
	earmarkables: {},

	initialize: function() {
		
		if (!this.initialized) {
			
			this.initialized = true;
			
			Igglo.User.initialize(); // Needs user to be initialized
			
			this.update();
									
			Event.observe(document, 'igglo:user-update', this.update.bind(this));
			
		}

	},

	update: function() {

		new Igglo.Request(
			'earmark/get',
			{
				format: 'json',
				method: 'post', // Post to avoid caching
				asynchronous: false,
				onComplete: this.set.bind(this)
			}
		);

	},

	set: function(response) {
		
		this.earmarks = response.earmarks;
		
	},
	
	isProfile: function(cardType) {
		
		this.initialize();
		
		return this.earmarks[cardType] != undefined;
		
	},
	
	cardIsEarmarked: function(cardType, cardId) {

		this.initialize();
		
		if (!this.earmarks[cardType] || this.earmarks[cardType][cardId] == undefined) {
			return false;
		}
		
		return this.earmarks[cardType][cardId];

	},
	
	canEarmark: function(cardType, earmarkCardType) {
		return this.earmarkables[cardType] && this.earmarkables[cardType].indexOf(earmarkCardType) >= 0;
	},
	
	isEarmarkable: function(cardType) {
		return this.earmarkables[cardType] instanceof Array;	
	},
	
	getEarmarkables: function() {
	
		var earmarkables = [];
	
		for (var cardType in this.earmarkables) {
			earmarkables.push(cardType);
		}	
		
		return earmarkables;
		
	}, 
		
	getEarmarkableCardTypes: function(cardType) {
		return this.earmarkables[cardType];
	},

	earmark: function(cardType, cardId) {

		this.initialize();

		var returnResponse;

		new Igglo.Request(
			'earmark/set',
			{
				format: 'json',
				method: 'post', // Post to avoid caching
				parameters: {
					card_id: cardId,
					card_type: cardType
				},
				asynchronous: false,
				onComplete: (function(response) {

					returnResponse = response;

					if (Igglo.Response.isSuccess(response)) {
						if (!this.earmarks[cardType]) {
							this.earmarks[cardType] = {};
						}
						this.earmarks[cardType][cardId] = true;
					}

				}).bind(this)
			}
		);

		return returnResponse;

	},

	unearmark: function(cardType, cardId) {

		this.initialize();
		
		var returnResponse;

		new Igglo.Request(
			'earmark/delete',
			{
				format: 'json',
				method: 'post', // Post to avoid caching
				parameters: {
					card_id: cardId,
					card_type: cardType
				},
				asynchronous: false,
				onComplete: (function(response) {

					returnResponse = response;

					if (Igglo.Response.isSuccess(response)) {
						if (!this.earmarks[cardType]) {
							this.earmarks[cardType] = {};
						}
						delete this.earmarks[cardType][cardId];
					}

				}).bind(this)
			}
		);

		return returnResponse;

	}
};

Igglo.Earmark.earmarkables[CARD_TYPE_AD_APARTMENT_SELL] = [
	CARD_TYPE_COUNTRY,
	CARD_TYPE_COUNTY,
	CARD_TYPE_CITY,
	CARD_TYPE_ZIP_CODE,
	CARD_TYPE_DISTRICT,
	CARD_TYPE_STREET,
	CARD_TYPE_BUILDING,
	CARD_TYPE_AD_APARTMENT_SELL
];
	
Igglo.Earmark.earmarkables[CARD_TYPE_AD_APARTMENT_RENT] = [
	CARD_TYPE_COUNTRY,
	CARD_TYPE_COUNTY,
	CARD_TYPE_CITY,
	CARD_TYPE_ZIP_CODE,
	CARD_TYPE_DISTRICT,
	CARD_TYPE_STREET,
	CARD_TYPE_BUILDING,
	CARD_TYPE_AD_APARTMENT_RENT
];

Igglo.Earmark.earmarkables[CARD_TYPE_AD_LOT_SELL] = [
	CARD_TYPE_COUNTRY,
	CARD_TYPE_COUNTY,
	CARD_TYPE_CITY,
	CARD_TYPE_ZIP_CODE,
	CARD_TYPE_DISTRICT,
	CARD_TYPE_STREET,
	CARD_TYPE_AD_LOT_SELL
];

Igglo.Earmark.earmarkables[CARD_TYPE_AD_VACATIONHOME_SELL] = [
	CARD_TYPE_COUNTRY,	
	CARD_TYPE_COUNTY,	
	CARD_TYPE_CITY,
	CARD_TYPE_ZIP_CODE,
	CARD_TYPE_DISTRICT,
	CARD_TYPE_STREET,
	CARD_TYPE_AD_VACATIONHOME_SELL
];
			




Igglo.Explanation = Class.create({
	
	initialize: function(object) {
		Object.extend(this, object);
		Event.observe(this.element, 'click', this.explain.bindAsEventListener(this));
	},

	explain: function(event) {

		Event.stop(event);

		if (!this.box) {
			this.box = 
				(new Igglo.Box({size: [250, 0]}))
				.setContent(Igglo.Element('p', null, this.path.translate()))
		}

		this.box.positionByEvent(event, 5, 5).enable();

	}

});





/**
 * Note that if used with links, the possibly-bound event listeners are not executed (just redirects to href)
 */
Igglo.Authenticator = {

	initialize: function(elements) {

		$A(elements).invoke('observe', 'click', this.authenticate.bindAsEventListener(this));

	},

	authenticate: function(event) {

		if (Igglo.User.getUserLevel() < USER_TYPE_USER_REGISTERED) {

			Event.stop(event);

			var element = Event.findElement(event, 'A') || Event.findElement(event, 'BUTTON'); // The event might originate from for example a span-element inside a a-element

			(new Igglo.LoginBox({
				loginType: Igglo.LoginBox.LOGIN_TYPE_REGISTER,
				onSuccess: function() {
					if (element.click) {
						element.click();
					}
					else if (element.href) {
						location = element.href;
					}
				},
				onError: function(response) {
					(new Igglo.MessageBox({messages: Igglo.Response.getMessages(response)})).positionToCenter().enable(5);
				},
				modal: true
			})).positionToCenter().enable();

			return false;

		}

		return true;

	}

};





Igglo.Event = {

	position: function(event) {
		return [
			Event.pointerX(event),
			Event.pointerY(event)
		];
	},

	key: function(event) {
		return event.keyCode || event.which;
	}

};





Igglo.XBrowserSupport = {

	initialize: function() {
		
		var tests = {
			ie: /MSIE/,
			ie6: /MSIE 6/,
			ie7: /MSIE 7/,
			ff: /Firefox/,
			ff2: /Firefox\/2/,
			ff3: /Firefox\/3/
		};
		
		for (var browser in tests) {
			if (tests[browser].test(navigator.userAgent)) {
				Element.addClassName(document.body, browser);
			}
		}
		
		Igglo.log('Igglo.XBrowserSupport: Body classes: ' + document.body.className + ' (' + navigator.userAgent + ')');
		
		this.update();		
		
	},
	
	update: function() {
		
		if (Prototype.Browser.IE6) {
			$$('input').each(function(element){
				Element.addClassName(element, element.type);
			});
		}
	
	}
	
};





Igglo.Tabs = Class.create({
	
	initialize: function(object) {
	
		Object.extend(this, object);
		
		this.select(0);
		
		for (var i = 0; i < this.tabElements.length; i++) {
			Event.observe(this.tabElements[i], 'click', this.onClick.bindAsEventListener(this, i));
		}
	
	},
	
	onClick: function(event, index) {
		
		Event.stop(event);
		
		this.select(index);
		
	},
	
	select: function(index) {
		for (var i = 0; i < this.tabElements.length; i++) {
			if (i == index) {
				Element.addClassName(this.tabElements[i], 'selected');
				Element.show(this.contentElements[i]);
			}
			else {
				Element.removeClassName(this.tabElements[i], 'selected');
				Element.hide(this.contentElements[i]);
			}
		}
	}
		
});
