// This is a fix so that rails recognises that a jQuery request is a javscript request
jQuery.ajaxSetup({
	'beforeSend': function(xhr) { xhr.setRequestHeader("Accept",
		"text/javascript")}
})

/*
	Extend jQuery with functions for PUT and DELETE requests.
	Taken from http://homework.nwsnet.de/news/view/41-put-and-delete-with-jquery
*/

function _ajax_request(url, data, callback, type, method) {
    if (jQuery.isFunction(data)) {
        callback = data;
        data = {};
    }
    return jQuery.ajax({
        type: method,
        url: url,
        data: data,
        success: callback,
        dataType: type
        });
}

jQuery.extend({
    put: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'PUT');
    },
    delete_: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'DELETE');
    }
});

// Make jQuery work with rails authenticity token.
// Taken from http://henrik.nyh.se/2008/05/rails-authenticity-token-with-jquery
$(document).ajaxSend(function(event, request, settings) {
  if (typeof(AUTH_TOKEN) == "undefined") return;
  // settings.data is a serialized string like "foo=bar&baz=boink" (or null)
  settings.data = settings.data || "";
  settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
});

jQuery(function($) {
	// All elements with class visible should be shown on page load
	$('.visible').show();
	
	$('.info .hide').click(function() {
		$(this).parent().slideUp();
		jQuery.put(NAMESPACE+'/account', 'account[add_preference][' + $(this).parent().attr('id') + ']=0');
		$('#show_'+$(this).parent().attr('id')).fadeIn();
		return false;
	});
	
	$('.show_info').click(function() {
		$(this).parent().parent().find('.info').slideDown();
		$(this).fadeOut();
		jQuery.put(NAMESPACE+'/account', 'account[add_preference][' + $(this).attr('id').replace(/show_/, '') + ']=1')
		return false;
	});
	
	$('#flash_notice').highlight(2000).fadeOut(5000);
	$('#flash_error').highlight(2000).fadeOut(5000);
})

// Shared

function fadeFlash() {
	$('#js_flash').highlight(2000).fadeOut(5000);
}

function loadingGif(display) {
	if (display == null) { display = 'none'; }
	return '<img src="/images/ajax-loader.gif" class="loading" alt="loading" style="display:'+display+';"/>';
}

function ajaxifyForm(message) {
	$('form').ajaxForm();
	// When form save is clicked, setup flash
	$('#save').click(function() {
		$('#js_flash').html(message).show();
		$.scrollTo(0, 1000);
		$('form').submit();
		fadeFlash();
		return false;
	});
}

/*
	Sets up a section of the page to work with JS.  Only ever called once, on page ready.
	
	Finds the .add item button, within the elem supplied.
	
	Adds event listeners to any elements already on the page in the partial collection, using
	setPartialLinks
	
	Adds click event listener to the .add item button, to send a GET request to uri/new.  The
	response from the server will be a new item form.  This form is put into the .form div
	using formReplace.
*/
function initSection(elem, uri) {
	setPartialLinks(elem);
	
	elem.find('.add').click(function() {
		
		clearAllFormHolders();
		
		$(this).toggle();
		elem.find('.collection').next().find('.loading').toggle();
		jQuery.post(uri + '/new', '_method=get', function(responseText) {
			formReplace(elem, responseText, elem.find('.collection').next());
			setupAddForm(elem, uri);
		});
		return false;
	});
}

/* 
	Sets up the form actions for a new item form
	
	All forms are created within a div with class 'form'
	This function finds the form div, and adds cancel and save functionality
	to the buttons
	
	The cancel button setup is contained within it's own method, this is because
	it is shared by setupAddForm and setupEditForm.
	
	The save (add) button has class 'save'.  The save button is found within the scope of elem
	and has a click action added to it.  On click the main form on the page is located and
	the action is changed to the uri supplied as an arguement.  The form is also changed to a
	POST request, (just in case it was set to PUT or DELETE).
	
	Finally the form is turned into an ajax submit using .ajaxForm.  On success the partialReplace
	function is called with the elem everything is scoped in and the responseText (the server's response),
	which is HTML.
*/
function setupAddForm(elem, uri) {
	form_holder = elem.find('.collection').next().find('.form');
	setupCancelButton(elem, form_holder);
	form_holder.find('.save').click(function() {
		$('form').attr('action', uri);
		$('form').find('input[name=_method]').attr('value', 'post');
		$('form').ajaxForm({
			success: function(responseText, status) {
				if(responseText.match(/label/)) {
					formReplace(elem, responseText, form_holder);
					setupAddForm(elem, uri, form_holder);
				}
				else {
					partialReplace(elem, responseText);
				}
				elem.find('.loading').hide();
			}
		});
		$('form').submit();
		clearFormHolder(form_holder);
		form_holder.find('.loading').show();
		return false;
	})
	
	afterSetupForm();
	afterSetupAddForm();
}

/* 
	Sets up the form actions for an edit item form.
	
	This function behaves in EXACTLY the same way as the setupAddForm function, except, it turns the form
	into a PUT request
*/
function setupEditForm(elem, href, form_holder) {
	$('form').attr('action', href)
	$('form').find('input[name=_method]').attr('value', 'put');
	
	setupCancelButton(elem, form_holder);
	
	form_holder.find('.save').html('Save');
	form_holder.find('.save').click(function() {
		$('form').ajaxForm({
			success: function(responseText) {
				if(responseText.match(/label/)) {
					formReplace(elem, responseText, form_holder);
					setupEditForm(elem, href, form_holder);
				}
				else {
					partialReplace(elem, responseText);
				}
				elem.find('.loading').hide();
			}
		})
		$('form').submit();
		clearFormHolder(form_holder);
		form_holder.find('.loading').show();
		return false;
	})

	afterSetupForm();
	afterSetupEditForm();
}

/*
  Resets the page form to it's original state

  Obviously after each request, we want to return the form to the original action and method.
	That's what this does.  It should be called after every successful AJAX update
*/
function resetForm() {
	$('form').attr('action', '/candidate');
	$('form').find('input[name=_method]').attr('value', 'put');
}

/*
	Sets the cancel button of the supplied element to remove the form.
	
	Takes the current form element as an arguement.  This function simply
	adds a click event listener to the cancel button, which sets the HTML
	with the form element to '', and then shows the add item button again.
	(The method of finding the '.add' button is a little brittle.  May refactor this
	soon).
*/

function setupCancelButton(elem, form_holder) {
	form_holder.find('.cancel').click(function() {
		setPartialLinks(elem);
		clearFormHolder(form_holder);
		elem.find('.add').show();
		return false;
	})
}

/*
	Replaces the collection partial, and updates the links, etc
	
	The collection of items is always rendered within a div with class .collection.
	This removes clears the collection div and then appends the new partial
	collection to the div with class collection.  setPartialLinks is called to add event
	listeners to the newly rendered partial 'edit' and 'delete' links. Otherwise they would
	all start working using HTML again.
	
	Also, this function clears .form div (in case the form is still showing), and shows the
	'add item', button, so a new item can be created.
	
	Finally, resetForm is called, to return the form containing the entire page to it's
	original state.
*/
function partialReplace(elem, responseText) {
	elem.find('.collection').html('');
	elem.find('.collection').prepend(responseText).effect('slide');
	setPartialLinks(elem);
	elem.find('.add').show();
	resetForm();
}

/*
	Takes the responseText from an AJAX response, and puts it into the .form div contained
	within elem.  Also appends the Add and Cancel buttons.  setupAddForm or setupEditForm
	must always be called after this to setup the buttons.
	
	Then finds the 'Add item' button and hides it, so not more than on form can be added to
	the page at any one time.
	
	NOTE: the setupCancelButton could actually be moved into this function.
*/
function formReplace(elem, responseText, form_holder) {
	form_holder.html(responseText).slideDown();
	
	form_holder.append('<div class="submit_right"><button class="save">Add</button><button class="cancel">Cancel</button></div>');
	
	elem.find('.add').hide();
	afterSetupForm();
}

/*
	Updates the partial links to use js, particularly used after re-rendering a collection with AJAX
	
	Finds all the delete buttons in the collection partial, and adds click event listeners to them.
	On click they DELETE /href/:id and the partial returned by the server (responseText), is used to
	refresh the partial collection using partialReplace.
	
	Also, finds all the edit buttons and adds click event listeners to them.  On click they make a GET
	request to the edit link (contained within the href of the link being modified).  The response from
	the server will be an edit form, which is added to the page using formReplace.  setupEditForm is then
	called to make sure it is PUTing, etc.
*/
function setPartialLinks(elem) {
	elem.find('.delete').each(function() {
		$(this).click(function() {
			if(confirm("Are you sure you want to delete this item?")) {
				$(this).parent().parent().remove();
				href = $(this).attr('href').split('/confirm_delete')[0];
				jQuery.post(href, '_method=delete');
			}
			return false;
		})
	});
	
	elem.find('.edit').removeClass('enabled');
	elem.find('.edit').each(function() {
		$(this).click(function() {
			
			disableEditButtons(elem);
			clearAllFormHolders(elem);
			href = $(this).attr('href');
			form_holder = $(this).parent().parent().next();
			form_holder.find('.loading').show();
			jQuery.post($(this).attr('href'), '_method=get', function(responseText) {
				formReplace(elem, responseText, form_holder);
				href = href.split('edit')[0];
				form_holder.find('.loading').hide();
				setupEditForm(elem, href, form_holder);
			})
			return false;
		})
	})
}

/*
	Disables all edit buttons within the supplied element.  Used after an edit button has been clicked
	so that only one edit form can be displayed within each section.
*/
function disableEditButtons(elem) {
	elem.find('.edit').each(function() {
		$(this).unbind('click');
		$(this).addClass('disabled');
		$(this).click(function() {
			return false;
		});
	})
}

/*
	Sets the html of the form holder div back to just an invisible loading gif
*/
function clearFormHolder(form_holder) {
	form_holder.html('<span class="loading" style="display:none;"><img src="/images/ajax-loader.gif" alt="loading"/></span>');
}

/*
	Clears all the forms on the page.  Used to prevent weird JS behaviour of forms rendering in the wrong place. I don't
	actually like this solution.  Should refactor all of this JS soon, to scope within form holder div, or some other
	better solution maybe...
*/
function clearAllFormHolders() {
	$('.form').each(function() {
		clearFormHolder($(this));
	});
	$('.add').show();
}

/*
	These are abstract functions which should be overridden in individual javascript files
	for specific control of what happens after the main functions
*/
function afterSetupForm() {}

function afterSetupAddForm() {}

function afterSetupEditForm() {}

/*
	Adds on change event listeners to all input types within the specified fieldset
*/
function addFieldListeners(elem) {
	elem.find('input[type=checkbox]').focus(function() {
		$('form').submit();
	});
	elem.find('input[type=checkbox]').change(function() {
		$('form').submit();
	})
	
	elem.find('input[type!=checkbox]').change(function() {
		$('form').submit();
	});
	
	elem.find('select').change(function() {
		$('form').submit();
	});
	
	elem.find('textarea').change(function() {
		$('form').submit();
	});
}

/*
	All checkboxes are reset to their original checked state, by using the attached 'original' attribute
*/
function resetCheckboxes(elem) {
	elem.find('input[original=checked]').each(function() {
		$(this).attr('checked', 'checked');
	});
	
	elem.find('input[original=unchecked]').each(function() {
		$(this).attr('checked', '');
	});
}

/*
	Updates the 'original' attributes of checkboxes, to be the same as their checked attribute.  This is
	useful when a set of checkboxes are saved for example.  Because after an ajax save, the cancel button
	should reset all checkboxes back to their state just after save, not after page load
*/
function updateCheckboxes(elem) {
	elem.find('input[type=checkbox]').each(function() {
		$(this).attr('original', 'unchecked');
	});
	
	elem.find(':checked').each(function() {
		$(this).attr('original', 'checked');
	});
}

function clearCheckboxes(elem) {
	elem.find('input[type=checkbox]').each(function() {
		$(this).attr('checked', '');
	});
}

function nameReplace(name) {
	return name.replace(/[^\w+]/,'');
}

function fieldsetReset(elem) {
	var form = elem.find('.form fieldset').html();
	elem.find('.form fieldset').remove();
	elem.find('.form').prepend(form);
	elem.find('.form h2').remove();
}

function applyPasswordEvents() {
	$('#passwords').find('.buttons').show();
	$('#password').find('#change').show();
	$('#password').find('#change').click(function() {
		$('#password').hide();
		$('#passwords').slideDown();
		return false;
	});
	$('.save').click(function() {
		$('#passwords .loading').show();
		$(this).hide();
		jQuery.put(NAMESPACE+'/account', 'account[password]='+$('#account_password').val()+
			'&account[password_confirmation]='+$('#account_password_confirmation').val()+'&password_change=true',
			function(responseText) {
				if(responseText.match(/input/)) {
					$('#passwords').html(responseText);
					$('#passwords .save').show();
					$('#passwords .loading').hide();
					applyPasswordEvents();
				}
				else {
					$('#passwords .save').show();
					$('#passwords .loading').hide();
					$('#passwords').slideUp();
					$('#password').show();
					$('#password').append('<span class="message">your password was changed.</span>');
					$('#password').find('.message').fadeOut(5000);
					$('#passwords').find('.formError').remove();
				}
			});
		return false;
	});
	$('#passwords .cancel').click(function() {
		$('#passwords').hide();
		$('#password').show();
		return false;
	});
}