// Application-wide JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

// Don't cause errors on IE and other browsers w/o console logging capability.
if (typeof console == "undefined" || typeof console.log == "undefined") var console = { log: function() {} };

var Y = {}; // Our top-level namespace
Y.User = REST.ActiveResource.extend({
	_collection: REST.Root.graft(new REST.CollectionResource('users'))
});
REST.Root.graft(new Y.User({}), 'account');

Y.Manufacturer = REST.ActiveResource.extend({
	_collection: REST.Root.graft(new REST.CollectionResource('manufacturers')),
	_associations: ['models'],
	defaults: {models: []},
	// Build proper Model instances from the options in a select optgroup.
	modelsFromOptgroup: function(){
		var manufacturer = this;
		this.model_optgroup.find("option").each(function(i){
			var model_option = $(this).remove();
			if (model_option.val() != "") new Y.Model({id: parseInt(model_option.val()), name: model_option.text(), manufacturer_id: manufacturer.id});
		})
	},
	toString: function(){return this.$.name}
});

Y.Model = REST.ActiveResource.extend({
	_collection: new REST.CollectionResource('models'),
	_associations: ['manufacturer'],
	init: function(attrs){
		this._super(attrs);
		this.manufacturer.models.graft(this);  // Canonical path is under manufacturer, not _collection.
	},
	toString: function(){return this.$.name}
});

Y.Yacht = REST.ActiveResource.extend({
	_collection: REST.Root.account.graft(new REST.CollectionResource('yachts')),
	_associations: ['manufacturer', 'model'],
	selectManufacturer: function(event){
		var yacht = event.data;
		var id = parseInt($(this).val());
		yacht.updateAttributes({manufacturer_id: id, model_id: null});
		yacht.updateModelSelectOptions();
	},
	updateModelSelectOptions: function(selected_id){
		var model_select = this.form.find("select#yacht_model_id");
		model_select.children().not("option[value=]").remove(); // Remove all but nil option
		if (this.manufacturer) {
			this.manufacturer.modelsFromOptgroup(); // In case they haven't been hydrated yet
			var a = this.manufacturer.models.toArray();
			for (i=0;i<a.length;i++) {model_select.append(jQuery("<option>" + a[i].toString() + "</option>").attr('value', a[i].toParam()))}
		}
		model_select.val(selected_id || "");
	},
	selectModel: function(event){
		var yacht = event.data;
		var id = parseInt($(this).val());
		yacht.updateAttributes({model_id: id});
		if (yacht.model) yacht.model.load(function(y){
			yacht.form.find("input#yacht_beam").val(yacht.model.$.beam);
			yacht.form.find("select#yacht_category").val(yacht.model.$.category);
			yacht.form.find("input#yacht_displacement").val(yacht.model.$.displacement);
			yacht.form.find("input#yacht_draft").val(yacht.model.$.draft);
			yacht.form.find("input#yacht_length_over_all").val(yacht.model.$.length_over_all);
		})
	},
	/* Update attributes from the form's current values and track changes */
	reflectForm: function(form){
		this.form = form;
		var attrs = {};
		attrs.manufacturer_id = parseInt(form.find("select#yacht_manufacturer_id").val()) || null;
		attrs.model_id = parseInt(form.find("select#yacht_model_id").val()) || null;
		attrs.beam = form.find("input#yacht_beam").val();
		attrs.category = form.find("input#yacht_category").val();
		attrs.displacement = form.find("input#yacht_displacement").val();
		attrs.draft = form.find("input#yacht_draft").val();
		attrs.length_over_all = form.find("input#yacht_length_over_all").val();
		this.updateAttributes(attrs);
		form.find('select#yacht_manufacturer_id').bind('change', this, this.selectManufacturer);
		form.find('select#yacht_model_id').bind('change', this, this.selectModel);
	}
});

/********************************************************************************\
 * Promote properly formatted objects (include_root_in_json = true) to models.
 * TODO: Allow models map to be supplied by client.
\********************************************************************************/
REST.models = {user: Y.User, manufacturer: Y.Manufacturer, model: Y.Model, yacht: Y.Yacht};

// Responds to changes in the login field
function conditionalPremiumNameAgreementHandler(event) {
	var name = $(this).val();
	if (name == '') // check for obvious solution and avoid AJAX round trip.
		processErrors([], ['login']);
	else
		var params = {'account[login]': name, 'account[terms_of_premium_service]': '0'};
		$.getJSON("/account/validator", params, function(data, textStatus) {
			var errors = {};
			jQuery.each(data, function(i, v){ errors[v[0]] = v[1]; });
			processErrors(errors, ['login']);
			var atops = $('#account_terms_of_premium_service_formField');
			errors.terms_of_premium_service ? atops.show() : atops.hide();
		});
}

// Removes from the DOM the element wrapping the matched element(s) and returns the wrapper fragment(s).
$.fn.unwrap = function(expr) {
  return this.parent().each(function(){
	$(this.childNodes).prependTo(this.parentNode);
  });
};

// Process the supplied array of errors for the given attributes.
function processErrors(errors, attributes) {
	var wrapper = $("<div class='fieldWithErrors'></div>");
	jQuery.each(attributes, function(i, attribute) {
		var formField = jQuery("div#account_" + attribute + "_formField");
		var hasErrorContent = formField.children().is('.formError');
		if (errors[attribute] && formField.length == 1) {
			formField.find('> label').wrap(wrapper.clone()); // Wrap label with error wrapper.
			formField.find('.center').addClass('fieldWithErrors'); //Add error class.
			formField.find('.formError').remove();
			var error = jQuery("<div></div>").addClass('formError').text(errors[attribute]);
			formField.append(error); // Append error text.
		}
		else {
			formField.find('div.fieldWithErrors > label').unwrap().remove();
			formField.find('.center').removeClass('fieldWithErrors');
			formField.find('.formError').remove();
		}
	});
}

var Rotator = {
	/* Manage the rotation through a set of elements.  This function should be called with "this" set to an already-displayed element.
	 * Rotate through a sequence of images.  The first image must already be placed in the DOM and be styled via its class such that crossfading into a clone achieves the desired effect (such
	 * as by appearing in exactly the same position and with the same size).  Subsequent images are loaded as needed and are conserved in the DOM.	*/
	process: function(event) {
		var currentImage = $(this);
		var rotatorDiv = currentImage.parent();
		var rotator = Rotator[rotatorDiv.attr('id')];
		var rotatorImages = rotatorDiv.children('img');
		rotator.index = (rotator.index + 1) % rotator.sources.length;
		var nextImage = rotatorImages.eq(rotator.index);
		if (nextImage.size() == 0) {
			/* build an img element for this source, using the first element in the rotator as the template */
			nextImage = currentImage.clone(false).attr('src', '#').hide().appendTo(rotatorDiv);
			nextImage.one('load readystatechange', function(){
				setTimeout(function(){
					/* when it loads, start the hold delay and then crossfade into it */
					var callback = Rotator.process.apply(nextImage[0]);
					Rotator.crossfade(currentImage, nextImage, rotator.crossfade_delay, callback);
				}, rotator.hold_delay)
			});
			nextImage.attr('src', rotator.sources[rotator.index]);
		} else {
			/* Delay a bit and then crossfade into the next image */
			setTimeout(function(){
				var callback = Rotator.process.apply(nextImage[0]);
				Rotator.crossfade(currentImage, nextImage, rotator.crossfade_delay, callback);
			}, rotator.hold_delay);
		}
	},
	/* Crossfade from one element to another.  This function makes no assumptions about the relative z-axis placement of the elements */
	crossfade: function(i1, i2, duration, callback) {
		i1.fadeOut(duration);
		i2.fadeIn(duration, callback);
		}
};

function ajaxDelete(url, element) {
	$.post(url, {
		"_method": "delete",
		"authenticity_token": window._auth_token
	}, function(responseText, textStatus, XMLHttpRequest){
			var table = element.closest("table.fcotable");
			var count_span = $("caption > span#table_count", table);
			element.fadeOut();
			adjustCount(count_span, -1);
		}, 'json')
}

/* Adjust the displayed count by delta. */
function adjustCount(el, delta) {
	var current = parseInt(el.text());
	el.text((current + delta).toString());
}

/* Show the feed with the given id.  If not available, fetch it. */
function showFeed(id) {
	$('.feed').hide();
	var newId = 'feed_' + id.toString();
	var newFeed = $("#" + newId);
	if (newFeed.length == 0) {
		$('#feeds_list .spinner').show();
		/* To load news into the home page, don't forget to allow SSL for this request. */
		jQuery("<div />").load('/feeds/' + id.toString(), function(responseText, textStatus, XmlHttpRequest){
			var feed_partial = $(responseText);
			feed_partial.appendTo('#feeds_list');
			addFeedBehavior(feed_partial);
			$('#feeds_list .spinner').hide();
		});
	} else
		newFeed.show();
}

/* Add "standard" behavior to displayed feeds */
function addFeedBehavior(feed_partial) {
//	feed_partial.find("form.new_caravel_vote").submit(processVote);	/* Bind voting behavior to virgin forms */
	/*remove Cufon font */
	/*Cufon.replace(feed_partial.find('h4'));*/
	/* Toggle the slider's visibility and swap toggler images */
	feed_partial.find('h4.slide_toggle').css('cursor', 'pointer').click(function(){
		$(this).find("img").toggle();
		$(this).next('.slide').slideToggle('slow');
	});
	/* Show the control, and then bind a handler to close all sliders */
	feed_partial.find('.slide_up_all').show().click(function(){
		feed_partial.find('.slide').slideUp('slow').prev(".slide_toggle").each(function(){
			$(this).find("img.slide_dn").show();
			$(this).find("img.slide_up").hide();
		});
	});
	/* Show the control, and then bind a handler to open all sliders */
	feed_partial.find('.slide_dn_all').show().click(function(){
		feed_partial.find('.slide').slideDown('slow').prev(".slide_toggle").each(function(){
			$(this).find("img.slide_dn").hide();
			$(this).find("img.slide_up").show();
		});
	});
	// feed_partial.find(".slide_up_all").click();  Slide down by default for feed items
	feed_partial.find('.photo:even').addClass("photoL");
	feed_partial.find('div.scroller').serialScroll({
		target: '.slides',
		items: 'li',
		axis: 'xy',
		duration: 700,
		force: true,
		prev: '.goprev',// Selector to the 'prev' button (absolute!, meaning it's relative to the document)
		next: '.gonext',// Selector to the 'next' button (absolute too)
		//queue:false,// We scroll on both axes, scroll both at the same time.
		//event:'click',// On which event to react (click is the default, you probably won't need to specify it)
		//stop:false,// Each click will stop any previous animations of the target. (false by default)
		//lock:true, // Ignore events if already animating (true by default)
		//start: 0, // On which element (index) to begin ( 0 is the default, redundant in this case )
		//step:2, // How many items to scroll each time ( 1 is the default, no need to specify )
		//jump:true, // If true, items become clickable (or w/e 'event' is, and when activated, the pane scrolls to them)
		//lazy:false,// (default) if true, the plugin looks for the items on each event(allows AJAX or JS content, or reordering)
		//interval:1000, // It's the number of milliseconds to automatically go to the next
		//constant:true, // constant speed
		onBefore: function( e, elem, $pane, $items, pos ){
			e.preventDefault();
			if( this.blur )
			this.blur();
		},
		onAfter: function( elem ){
		}
	});
	// TODO: Update "View All" link at the bottom of the tile to link to currently displayed feed.
}

/* Process a vote (thumbs) form and give feedback with an image swap whose source is sneakily injected in the button's value */
function processVote() {
	var form = $(this);
	var iconImage = form.find("input[type='image']");
	$.post(form.attr('action'), form.serialize(), function(responseText, textStatus) {
		iconImage.attr("src", iconImage.attr("value"));
	}, 'json');
	return false;
}

function scaleImage(image, container) {
	var window_height = container.height();
	var window_width  = container.width();
	var nativeImage = new Image();
	nativeImage.src = image.attr("src");
	var image_width   = nativeImage.width;
	var image_height  = nativeImage.height;
	var height_ratio  = image_height / window_height;
	var width_ratio   = image_width / window_width;
	if (height_ratio > width_ratio) {
		image.css('width', "auto");
		image.css('height', "100%");
	} else {
		image.css('width', "100%");
		image.css('height', "auto");
	}
}

/* Run when the document's ready event is fired.  Note that all elements (images, etc.) may not yet be loaded. */
$(function() {
	/* Attach behavior to form fields that trigger conditionally visible fields. */
	$('form#new_account #account_login:enabled').bind('change', conditionalPremiumNameAgreementHandler).change();

	/* Embed labels in text form fields */
	$('input:text#account_login[value=]').labelify({text: "label", labelledClass: "embeddedLabel"});

	$('input:text.labelify').labelify({text: "label", labelledClass: "embeddedLabel"}).each(function(i){
		var s = 'label[for=' + this.id + ']';
		jQuery(this).parents("form").find(s).css('visibility', 'hidden'); // Hide causes floated labels to vaporize and screw up positioning.
	});

	/* focus on the first field of the form. */
	$('input[type=text]:enabled:visible:first').focus().select();

	/* Attach AJAX delete behavior to configured table rows */
	$('tr a.ajax_delete').click(function() {
		var row = $(this).closest("tr");
		if (confirm("Are you sure you want to delete this record?")) { ajaxDelete(this.href, row); }
		return false;
	});

	/* Attach AJAX delete behavior to configured tiles */
	$('div.contentTile form.ajax_delete').click(function() {
		var tile = $(this).closest("div.contentTile");
		if (confirm("Are you sure you want to delete this record?")) { ajaxDelete(this.action, tile); }
		return false;
	});

	/* Catch news feed selector changes for subscriptions */
	$('form#changefeed2 select#order').change(function() {
		var form = $(this).parent().parent();
		$.post(form.attr('action'), form.serialize(), function(responseText, textStatus) {
			showFeed(responseText[0].feed_subscription.feed_id); // Ensure feed_subscription JSON includes this foreign key
		}, 'json')
	 });

	/* Catch news feed selector changes for simple feed display */
	$('select#feeds').change(function() {
		showFeed($(this).val());
	 });

	/* Shortcut jump menus */
	$('form.jumpmenu').each(function(el){
		var jmf = $(this);
		jmf.find('input').hide();
		jmf.find('select').change(function() {
			var url = $(this).val();
			if (url != '') { location.href = url; }	/* jump */
		})
	});

	/* Process voting buttons */
	$("form.new_caravel_vote").submit(processVote);

	/* Start the image rotator */
	// IE is a PITA: http://dev.jquery.com/ticket/3930
	$('img.rotator').one('load', Rotator.process);
	$('img.rotator').attr('src', $('img.rotator').attr('src')); /* This silliness helps IE trigger the load event */

	/* Process Yacht association selections */
	$('form.edit_yacht').add('form.new_yacht').each(function(el){
		var form = $(this);
		var yacht = new Y.Yacht({});
		form.find("select#yacht_manufacturer_id option").each(function(i){ // Prehydrate the manufacturers with the little we have
			var manufacturer_option = $(this);
			if (manufacturer_option.val() == "") return true;
			var manufacturer = new Y.Manufacturer({id: parseInt(manufacturer_option.val()), name: manufacturer_option.text()});
			// Squirrel away the model options -too many to process now but we don't want the whole set visible.
			manufacturer.model_optgroup = form.find("select#yacht_model_id optgroup").filter(function(index){
				// This should be done with a dynamic selector, but embedded quotes befuddle jQuery
				return this.label == manufacturer.toString();
			});
		});
		yacht.reflectForm(form);
		yacht.updateModelSelectOptions(yacht.$.model_id);
	});

	/* Open certain hyperlinks in a new window.  Note that XHTML Strict does not allow the target attribute
	 * so we, properly, resort to a behavior-based implementation instead of a markup-based one.  Beware the
	 * HTTP_REFERER header may work differently with hyperlinks opened this way. */
	$('a[rel=external]').click(function(){
		window.open(this.href);
		return false;
	});

	$(".wizard").formToWizard();

	/*Show View toggle control, and bind behavior to them */
	$('div.tileGallery a.gallery').css('display', 'inline').click(function(){
		$('div.tileGallery ol').fadeOut('fast', function(){
			$(this).removeClass('list-view').fadeIn('fast').addClass('gallery-view');
		});
	});
	$('div.tileGallery a.list').css('display', 'inline').click(function(){
		$('div.tileGallery ol').fadeOut('fast', function(){
			$(this).removeClass('gallery-view').fadeIn('fast').addClass('list-view');
		});
	});

	$('div.tileGallery ol li input:checkbox[checked]').each(function(){
		$(this).parent('div').addClass('selected');
	});

	$('div.tileGallery ol li input:checkbox').click(function(){
		if ($(this).attr('checked'))
			$(this).parent('div').addClass('selected');
		else
			$(this).parent('div').removeClass('selected');
	});

	/* show overlay */
	$('div.tileGallery ol li.attachment_photo a.thumb').click(function(){
		/* get the index of the thumbnail that is clicked on */
		var listItem = $(this).parents('li');
		var indexNumber = $('div.tileGallery ol li.attachment_photo').index(listItem);

		$('div.tileGallery div#overlay').fadeIn('fast', function(){

			$('div.tileGallery div#lightbox').fadeIn('slow');
			var gallery_page = $('div.tileGallery').galleriffic({
				thumbNav: false,
				autoStart: false,
				indexNum: indexNumber
			});
		});
		return false;
	});

	/* hide overlay */
	$('div.tileGallery div#lightbox a, div.tileGallery div#overlay').click(function(){
		$('div.tileGallery div#lightbox').fadeOut('fast', function(){
			$('div.tileGallery div#overlay').fadeOut('slow');
		});
		return false;
	});

	/* when Delete button is clicked */
	$('div.tileGallery .attachment-controls button').click(function(){
		if (!($('div.tileGallery ol li input').is(':checked'))){
			alert('Please select the file you want to delete.');
			return false;
		}

		if (confirm('Are you suer you want to delete the selected files?')) {
			var url = $('div.tileGallery .header .right form.button-to').attr('action');
			$('div.tileGallery ol li input:checkbox[checked]').each(function(){
				var selectedAttachment = $(this).parent('div');
				var deleted = '{"authenticity_token":"' + window._auth_token + '", "id":"' + $(this).val() + '"}';
				$.ajax({
					url: url + '/attachments/' + $(this).val(),
					type: 'DELETE',
					data: deleted,
					dataType: 'json',
					contentType: 'application/json; charset=utf-8',
					success: function(data){
						$(selectedAttachment).siblings('span.loading').remove();
						$(selectedAttachment).removeClass('loading').addClass('removed').children().remove();
						$(selectedAttachment).append('<span>Deleted successfully.</span>');
						$(selectedAttachment).parent('li').removeClass('attachment').removeClass('attachment_photo').removeClass('attachment_doc');
					},
					error: function(XMLHttpRequest, textStatus, errorThrown){
						$(selectedAttachment).siblings('span.loading').remove();
						$(selectedAttachment).removeClass('loading').removeClass('selected').find('input').attr('checked', false);
						$(selectedAttachment).children('div').append('<div class="error">Sorry, the deletion was not successful. We are analyzing the cause of this problem.</div>');
					},
					beforeSend: function(){
						$(selectedAttachment).find('div.error').remove();
						$(selectedAttachment).addClass('loading');
						$(selectedAttachment).parent().prepend('<span class="loading" />')
					}
				});
			});
		} else {
			return false;
		}
	});

	/* Toggle the slider's visibility and swap toggler images */
	$('div.tile:not(div.tileFeed1) h4.slide_toggle').css('cursor', 'pointer').click(function(){
		$(this).find("img").toggle();
		$(this).next(".slide").slideToggle("slow");
	});
	/* Show the control, and then bind a handler to close all sliders */
	$('div.tile:not(div.tileFeed1) .slide_up_all').show().click(function(){
		$('.slide').slideUp("slow").prev(".slide_toggle").each(function(){
			$(this).find("img.slide_dn").show();
			$(this).find("img.slide_up").hide();
		});
	});
	/* Show the control, and then bind a handler to open all sliders */
	$('div.tile:not(div.tileFeed1) .slide_dn_all').show().click(function(){
		$('.slide').slideDown('slow').prev(".slide_toggle").each(function(){
			$(this).find("img.slide_dn").hide();
			$(this).find("img.slide_up").show();
		});
	});

	$('div.tile:not(div.tileFeed1, div.tileYacht) .slide_up_all').click();
	$('.connector').find('h4.slide_toggle').click().find('img.slide_up').hide();

	$('div.tile:not(div.tileFeed1) .feed').find('.photo:even').addClass("photoL");

	$("div.tileFeed1 .feed").each(function(i){addFeedBehavior($(this))});

	/* Upload multiple files */
	$("div.tileAttachmentUploadNew form#new_attachment").multifile();

	/* photo carousel */
	var onMouseOutOpacity = 0.67;
	// Initialize Galleriffic Gallery
	var carousel = $('#thumbs').galleriffic({

		onSlideChange:             function(prevIndex, nextIndex) {
			// 'this' refers to the gallery, which is an extension of $('#thumbs')
			this.find('ul.thumbs').children()
				.eq(prevIndex).fadeTo('fast', onMouseOutOpacity).end()
				.eq(nextIndex).fadeTo('fast', 1.0);

			// Update the photo index display
			this.$captionContainer.find('div.photo-index')
				.html('Photo '+ (nextIndex+1) +' of '+ this.data.length);
		},
		onPageTransitionIn:        function() {
			var prevPageLink = this.find('a.prevPage').css('visibility', 'hidden');
			var nextPageLink = this.find('a.nextPage').css('visibility', 'hidden');

			// Show appropriate next / prev page links
			if (this.displayedPage > 0)
				prevPageLink.css('visibility', 'visible');

			var lastPage = this.getNumPages() - 1;
			if (this.displayedPage < lastPage)
				nextPageLink.css('visibility', 'visible');

			this.fadeTo('fast', 1.0);
		}
	});

	/* Event handlers for custom next / prev page links */

	carousel.find('a.prevPage').click(function(e) {
		carousel.previousPage();
		e.preventDefault();
	});

	carousel.find('a.nextPage').click(function(e) {
		carousel.nextPage();
		e.preventDefault();
	});
})

/* Run when all content is available. */
$(window).bind('load', function() {
});