function DefaultLayout(config) {

	var $this = this;

	$this.config = $.extend($this.config, config);

	$(window).resize(function () {
		var headerHeight = $('header').height();
		$('#primary').css('padding-top', headerHeight);
		$('.modal-dialog').css('margin-top', headerHeight);
	});

	/* AJAX dialog */
	$('.dialog').click(function () {
		var title = $(this).attr('title') != '' ? $(this).attr('title') : $(this).text();
		var onDialogClose = null;
		$.get(
			$(this).attr('href'),
			null,
			function (response) {
				var script = response.match(/<script(?: +type="text\/javascript")? *>((?:.|\s)*)<\/script>/im);
				$('<div>').html(response).dialog({
					show: {effect: 'drop', duration: 250, direction: 'up'},
					hide: {effect: 'drop', duration: 180, direction: 'down'},
					width: 'auto',
					height: 'auto',
					modal: true,
					resizable: false,
					title: title,
					open: function () {
						onDialogClose = null;
						$('.ui-dialog-content').html('<div class="ui-dialog-content-inner">' + $('.ui-dialog-content').html() + '</div>');
						eval(script[1]);
					},
					close: function () {
						$(this).dialog('destroy');
						if (onDialogClose != null) eval(onDialogClose);
					}
				});
			}
		);
		return false;
	});

	/* On window resize */
	$(window).resize(function () {
		$('#primary').css('margin-top', $('.navbar-fixed-top').outerHeight());
	}).resize();

	(function ($) {
		$('form input[type=text]:enabled:visible:first').focus();

		/* Hover for menu */
		$('#main-menu li').hover(
			function () {
				$(this).children('a').addClass('hover').next('ul').show();
			},
			function () {
				$(this).children('a').removeClass('hover').next('ul').hide();
			}
		);

		/* Tab in textarea */
		$('textarea').tabOverride();


		/* HoverBox init */
		$('.hover-box:not(.disabled)').click(function () {
			$(this).siblings().removeClass('hover-box-active');
			$(this).addClass('hover-box-active');
		});

		/* Datepickers */
		$('.datepicker').datepicker({
			dateFormat: 'yy-mm-dd'
		});

		/* Instant feedback */
		$('[data-click-feedback]').click(function () {
			var element = $(this);
			var feedback = element.attr('data-click-feedback');
			if (element.is('input')) {
				element.val(feedback);
			} else {
				element.text(feedback);
			}
		});

		/* Hide flash message on click */
		$('.alert').click(function (event) {
			$(this).animate(
				{height: 0, opacity: 0, padding: 0},
				'normal',
				'easeOutQuad',
				function () {
					$(this).remove();
				}
			);
		});

		/* Expand sidebar elements */
		$('.modal-header.expandable').disableSelection();
		$('.modal-header.expandable').click(function () {
			$(this).next('div').toggle('blind', 200);
		});

		initializeRichTextEditors();
		$(window).resize();

	})(jQuery);
}

/**
 * Watcher for changes on the form, so that the user can be warned if changes
 * have not been saved.
 */
window.UnsavedFormWatcher = {

	/** @var boolean True if changes have been made on form, but not saved. */
	unsavedChanges: false,

	/**
	 * Initialize the watcher.
	 *
	 * @var {string} selector The selector for jQuery for elements to watch.
	 * @var {string} warning The warning message to show on unsaved changes.
	 */
	init: function (selector, warning) {
		var _ = this;
		this.unsavedChanges = false;

		// Register elements
		this.registerElements(selector);

		// Register warning
		$(window).on('beforeunload', function (event) {
			if (_.unsavedChanges) {
				event.returnValue = warning;
				return warning;
			}
		});
	},

	/**
	 * Register a jQuery element to watch changes on
	 *
	 * @var {string} selector The selector for jQuery for elements to watch.
	 */
	registerElements: function (selector) {
		var _ = this;

		// On change
		$(document).on('change', selector, function () {
			_.registerChange();
		});

		// Set the submit
		$(selector).closest('form').on('submit', function () {
			_.registerSave();
		});
	},

	/**
	 * Notify watch that the form has been saved.
	 */
	registerSave: function () {
		this.unsavedChanges = false;
	},

	/**
	 * Notify watch that the form has edited.
	 */
	registerChange: function () {
		this.unsavedChanges = true;
	}

};

/**
 * Handle the uploads
 *
 * @param {string} model The model for which we upload.
 * @param {string} association The association for the upload.
 * @param {array} uploads The list of already existing uploads.
 * @param {bool} multiple True to allow multiple uploads, false to allow only single file.
 * @param {string} placeholder The path to the image, which acts as a placeholder to help in keeping the universal size between files and upload placeholders.
 */
var UploadHandler = function (model, association, uploads, multiple, placeholder) {
	var this_ = this;

	// Initialize
	this.uploads = uploads;
	this.multiple = multiple;

	// Set the list we are in charge of
	this.list = $('#UploadListFor' + association).html('');

	// Make sure we have at least one empty item
	if (this.multiple || this.uploads.length === 0) {
		uploads.unshift({
			thumbnail: placeholder
		});
	}

	// Actions
	$('a[data-list-action]').click(function () {
		var action = $(this).data('list-action');
		$('[data-list-action]').removeClass('active');
		$(this).addClass('active');
		$(this).closest('.vector-upload-wrapper').attr('class', 'vector-upload-wrapper vector-upload-' + action + '-view')
	});

	function phoneGridView() {
		$('.vector-upload-wrapper').attr('class', 'vector-upload-wrapper vector-upload-grid-view')
	}

	$(window).resize(function () {
		if ($(window).width() < 768) {
			phoneGridView()
		}
	});

	if ($(window).width() < 768) {
		phoneGridView()
	}

	// Create a DOM element
	this.create = function (options) {

		// List of targets for multiple upload
		var targets = {};

		// Make sure every item has unique index
		options.i = UniqueIndex.next();

		// Get the the proper object
		var item = hbs('upload-item', options); // FIXME

		// Allow replacement of the item
		item.data('replace', function (upload) {
			item.replaceWith(this_.create({
				upload: upload,
				model: options.model,
				association: options.association,
				multiple: options.multiple,
				multipleplaceholder: false
			}));
			window.loadShowUp();
		});

		// Set the progress or remove it completely
		item.data('progress', function (progress) {
			if (progress !== null) {
				item.attr('rel', Math.max(0, Math.min(100, progress)));
			} else {
				item.removeAttr('rel');
			}
		});

		// Delete a file
		item.find('[data-action="delete"]').click(function () {
			item.addClass('vector-upload-item-delete');
			item.find('input[data-context="delete"]').val('remove');

			// Notify that the form has changes
			UnsavedFormWatcher && UnsavedFormWatcher.registerChange();
		});

		// Undo delete a file
		item.find('[data-action="undo-delete"]').click(function () {
			item.removeClass('vector-upload-item-delete');
			item.find('input[data-context="delete"]').val('');
		});

		// Apply FileTransport
		item.fileTransport({

			/**
			 * Invoked when file is being uploaded.
			 *
			 * @param {object} event The event that created this action.
			 * @param {object} file The data containing additional details.
			 */
			start: function (event, file) {

				// Notify that the form has changes
				UnsavedFormWatcher && UnsavedFormWatcher.registerChange();

				// Handle multi and single uploads
				if (options.multipleplaceholder) {
					options.i = ++window.staticUploadCounter;
					targets[file.name] = this_.create(options);
					item.after(targets[file.name]);

				} else {
					targets[file.name] = item;
				}

				// Update progress
				targets[file.name].data('progress')(0);
			},

			/**
			 * Invoked periodically on progress update.
			 *
			 * @param {object} event The event that created this action.
			 * @param {object} file The uploaded file.
			 * @param {int} percent The current percentage of upload.
			 * @param {object} data The data containing additional details.
			 */
			progress: function (event, file, percent, data) {
				targets[file.name].data('progress')(percent);
			},

			/**
			 * Invoked when file is successfully uploaded.
			 *
			 * @param {object} event The event that created this action.
			 * @param {object} file The uploaded file.
			 * @param {string} token The secret token for the uploaded file.
			 * @param {object} meta Additional info about the uploaded file.
			 * @param {object} data The data containing additional details.
			 */
			success: function (event, file, token, meta, data) {
				meta.filename = file.name.replace(/(.+)\.[^\.]+/, '$1');

				// Load previous values
				if (options.upload) {
					meta.id = options.upload.id;
				}

				targets[file.name].data('replace')(meta);
			},

			/**
			 * Invoked on any error.
			 *
			 * @param {object} event The event that created this action.
			 * @param {string} type The type error, usually just 'error'.
			 * @param {string}  errorMessage The message describing the error.
			 */
			error: function (event, type, errorMessage) {
				var upload = {
					file: null,
					error: 'Unknown error has occurred.'
				};

				// Known error message
				if (errorMessage) {
					upload.error = errorMessage;
				}

				targets[id].data('replace')(upload);
			}
		});

		return item;
	};

	// Populate with existing uploads
	for (var i in uploads) if (uploads.hasOwnProperty(i)) {
		var options = {
			model: model,
			multiple: multiple,
			multipleplaceholder: multiple && i * 1 === 0,
			association: association,
			upload: uploads[i]
		};

		this.list.append(this.create(options));
		window.loadShowUp();
	}
};

/**
 * Handles the global unique index.
 */
var UniqueIndex = {

	/** @var {number} The current intex. */
	index: 0,

	/**
	 * Get the next unique index.
	 *
	 * @returns {number} The next available unique index.
	 */
	next: function () {
		return ++this.index;
	}
};


// Full page ajax
function fullAjax(url, data, success) {
	var pageLoader = $('#PageLoader');
	var options = {};

	// Get the type
	if (data) {
		options.data = data;
		options.type = 'POST';
	}

	// Set the url
	options.url = url;

	// Set the on success method
	options.success = function (response) {
		if (typeof success == 'function') {
			success(response);
		}
	};

	// Default error
	options.error = function () {
		alert('Unknown error occured.');
	};

	// Hide loader
	options.complete = function () {
		pageLoader.fadeOut();
	};

	// Execute
	pageLoader.fadeIn();
	$.ajax(options);
}

/* Innova RTE initialization */
function initializeRichTextEditors() {

	// Define the list of available editors
	var Editors = {

		// Tiny MCE
		TinyMCE: {
			selector: '.mce-tinymce',
			init: function () {
				tinymce.init({
					height: "380",
					selector: '.rte',
					setup: function (editor) {
						editor.on('change', function () {
							editor.save();
						});
					},
					menubar: false,
					forced_root_block: "",
					force_br_newlines: false,
					force_p_newlines: false,
					plugins: [
						//'assetsmanager',
						'advlist autolink lists link image charmap print preview anchor',
						'searchreplace visualblocks code fullscreen',
						'advlist autolink autosave link image lists charmap print preview hr anchor pagebreak spellchecker',
						'searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking',
						'table contextmenu directionality emoticons template textcolor paste textcolor colorpicker textpattern',
						'insertdatetime media table contextmenu paste code'],
					open_manager_upload_path: '../../assets/',
					toolbar: 'undo redo code | formatselect forecolor backcolor | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
					content_css: []
				});
			}
		},

		// Innova studio
		Innova: {
			selector: '.innova-container',
			init: function () {
				oUtil.initializeEditor('rte', {
					// js:[<?= isset($javascripts) ? '"' . implode('", "', $javascripts) . '"' : '' ?>],
					// css:[<?= isset($stylesheets) ? '"' . implode('", "', $stylesheets) . '"' : '' ?>],
					width: "100%",
					useBR: false,
					useDIV: false,
					width: "100%",
					height: "350px",
					mode: "XHTMLBody",
					toolbarMode: 2,
					initialRefresh: true,
					groups: [
						["grpEdit", "Edit", [
							"XHTMLSource", "FullScreen", "Undo", "Redo",
							"Search", "Cut", "Copy", "Paste", "PasteWord", "PasteText", "RemoveFormat"]],

						["grpFont", "Font", [
							"Paragraph",
							"Bold", "Italic", "Underline"]],

						["grpPara", "Paragraph", [
							"Indent", "Outdent", "Numbering", "Bullets",
							"JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyFull"]],

						["grpObjects", "Objects", [
							"Image", "Flash", "Media", "Characters", "Line"]],

						["grpLinks", "Links", [
							"Hyperlink"]],

						["grpTables", "Tables", [
							"Table"]],
					],

					cmdAssetManager: "modalDialogShow('/plugins/innova/assetmanager/assetmanager.php', 655, 465)",
				});
			}
		}
	};

	// If no configuration supplied, use TinyMCE
	var rtePlugin = 'TinyMCE';
	if (window.Configuration.Feature && window.Configuration.Feature.plugin_rte) {
		rtePlugin = window.Configuration.Feature.plugin_rte;
	}

	// Load the editor
	var plugin = Editors[rtePlugin];
	plugin.init();

	// Additional changes for the editor
	$(plugin.selector).addClass('input-container').parent().addClass('clearfix');
}

$(document).ready(function () {
	DefaultLayout(Configuration);
});

// ShowUp
window.loadShowUp = function () {
	$('.showup').each(function () {
			if (!$(this).is('.showup-loaded')) {
				$(this).addClass('showup-loaded').showUp({
					"duration": 100,
					"overflow": false,
					"closeOnEsc": true,
					"keyboardControls": true,
					"minMargin": 20,
					"gallery": null,
					"galleryThumbs": null,
					"overlay": {
						"show": true,
						"color": "#000000",
						"opacity": 75,
						"closeOnClick": true
					},
					"theme": {
						"name": "social",
						"colorscheme": "white"
					},
					"loop": false,
					"autoplay": false,
					"playInterval": 1500,
					"fadeInTime": 300,
					"fadeOutTime": 300,
					"transitions": {
						"stripes": {
							"count": 20,
							"direction": "outer",
							"duration": 250,
							"delay": 26
						}
					},
					"onShowHTML": function (content, element) {
						if ($(element).is('.gmap')) {
							var loader = $('div.map-loader').show();
							var title = $('#GoogleMapDialog h5').text('Map is loading...');
							setTimeout(function () {

								new GoogleMapChooser($(element).closest('div.input').find('input'), function () {
									"use strict";
									loader.fadeOut(300);
									var title = $('#GoogleMapDialog h5').text('Please select with left click');
								});
							}, 1200);
						}
					}
				});
			}
		}
	);
};
window.loadShowUp();