
/*===============================================================================
	AutoSaver.js
	John Larson
	1/27/08
	
	Component for listening to a collection of form inputs and autosaving
	on a regular basis.
	
	Example:    
<form>
  <div class="mySavers"></div>
  <textarea name="value1" class="myInputs">autosave </textarea><br />
  <textarea name="value2" class="myInputs">does not suck</textarea><br />
  <textarea name="value3" class="myInputs">I swear!</textarea><br />
  <div class="mySavers"></div>
</form>
	
	new AutoSaver($$('.myInputs'), $$('.mySavers'), 'AJAXAction.asp?a=save', 
		{inveral: 30, buttonTextLive: 'Save Now.', 
		 buttonTextSaved: 'Saved.', buttonTextAutoSaved: 'Autosaved.'});
	
===============================================================================*/


var AutoSaver = new Class({
	
	options: {
		interval:					10,
		buttonTextLive:				'Save',
		buttonTextSaved:			'Saved',
		buttonTextAutoSaved:		'Saved',
		useButtonImages:			true,
		buttonImagePath:			'images/buttons/',
		buttonImageNameLive:		'buttonSave.gif',
		buttonImageNameSaved:		'buttonSaved.gif',
		buttonImageNameAutoSaved:	'buttonSaved.gif',
		autosavedNoticeText:		' Autosaved at %time%'
	},

	initialize: function(formElements, saveElements, saveURL, options){
		this.setOptions(options);
		
		this.formElements = $$(formElements);
		
		// If we were given one or more forms we will replace them with the inputs within:
		this.formElements.each(function(element) {
			if (element.tagName == 'FORM') {
				this.formElements.remove(element);
				this.formElements.merge(element.getFormElements());
			}
		}, this);
		
		this.saveElements = $$(saveElements);
		
		this.saveEnabled = false;
		this.saveURL = saveURL;
		
		
		// Build our save inputs:
		var saveAction = this.performSave.bind(this);
		
		if (this.options.useButtonImages) {
			this.buttonPathSaved = 
				this.options.buttonImagePath + this.options.buttonImageNameSaved;
			this.buttonPathAutoSaved = 
				this.options.buttonImagePath + this.options.buttonImageNameAutoSaved;
			this.buttonPathLive = 
				this.options.buttonImagePath + this.options.buttonImageNameLive;
		}
		
		this.saveElements.each(function(saveRegion) {
			
			if(this.options.useButtonImages) {
				saveRegion.myButton = new Element('input',
					{type: 'image', src: this.buttonPathSaved});
				saveRegion.myButton.disabled = true;
			}
			else {
				saveRegion.myButton = new Element('input',
					{type: 'button', value: this.options.buttonTextSaved});
				saveRegion.myButton.disabled = true;
			}
			
			saveRegion.myMessageSpan = new Element('span');
			
			saveRegion.adopt(saveRegion.myButton);
			saveRegion.adopt(saveRegion.myMessageSpan);
			
			saveRegion.myButton.addEvent('click', function() {
				saveAction();
				return false;
			});
		}, this);
		
		
		
		// Let's set up listening to our inputs:
		var enableSaveButtonAction = this.enableSaveButtons.bind(this);
		
		this.formElements.each(function(formElement) {
			formElement.addEvents({
				'keyup': function() {
					enableSaveButtonAction();
				}, 
				'change': function() {
					enableSaveButtonAction();
				}
			}, this);
		}, this);
		
		// Let's set ourselves up to autosave at the prescribed interval:
		this.timer = function(){
			this.performAutoSave();
		}.periodical(this.options.interval * 1000, this, {});
		
		
	},
	
	addFormElement: function(formElement) {
		this.addFormElements([formElement]);
	},
	
	addFormElements: function(formElements) {
		
		var enableSaveButtonAction = this.enableSaveButtons.bind(this);
		
		formElements.each(function(formElement) {
			this.formElements.include(formElement);
			formElement.addEvents({
				'keyup': function() {
					enableSaveButtonAction();
				}, 
				'change': function() {
					enableSaveButtonAction();
				}
			}, this);
		}, this);
	},
	
	performSave: function(event) {
		
		// No need to do this again for now:
		this.saveEnabled = false;
		
		var saveData = this.getSaveData();
		var completeSaveAction = this.completeSave.bind(this)
		
		new Ajax(this.saveURL, {
			method: 'post',
			data: saveData,
			evalScripts: true,
			onComplete: completeSaveAction
		}).request();
	},
	
	performAutoSave: function() {
		
		if (this.saveEnabled) {
			this.saveEnabled = false;
			
			var saveData = this.getSaveData();
			var completeAutoSaveAction = this.completeAutoSave.bind(this)
			
			new Ajax(this.saveURL, {
				method: 'post',
				data: saveData,
				evalScripts: true,
				onComplete: completeAutoSaveAction
			}).request();
		}
	},
	
	
	completeSave: function() {
		
		// Update our save inputs:
		this.saveElements.each(function(saveRegion) {
			
			if(this.options.useButtonImages) {
				saveRegion.myButton.src = this.buttonPathSaved;
				saveRegion.myButton.removeClass('clickable');
				saveRegion.myButton.disabled = true;
			}
			else {
				saveRegion.myButton.value = this.options.buttonTextSaved;
				saveRegion.myButton.disabled = true;
			}
			
			saveRegion.myMessageSpan.setText('');
		}, this);
		
		this.saveEnabled = false;
	},
	
	
	completeAutoSave: function() {
		
		// Update our save inputs:
		this.saveElements.each(function(saveRegion) {
			
			if(this.options.useButtonImages) {
				saveRegion.myButton.src = this.buttonPathAutoSaved;
				saveRegion.myButton.removeClass('clickable');
				saveRegion.myButton.disabled = true;
			}
			else {
				saveRegion.myButton.value = this.options.buttonTextAutoSaved;
				saveRegion.myButton.disabled = true;
			}
			
			saveRegion.myMessageSpan.setText(
				this.options.autosavedNoticeText.replace('%time%', new Date().format("%X")));
		}, this);
		
		this.saveEnabled = false;
	},
	
	
	getSaveData: function() {
		
		var queryString = [];
		this.formElements.each(function(el){
			var name = el.name;
			var value = el.getValue();
			if (value === false || !name || el.disabled) return;
			var qs = function(val){
				queryString.push(name + '=' + encodeURIComponent(val));
			};
			if ($type(value) == 'array') value.each(qs);
			else qs(value);
		});
		return queryString.join('&');
	},
	
	enableSaveButtons: function() {
		if(!this.saveEnabled) {
			this.saveElements.each(function(saveRegion) {
			
				if(this.options.useButtonImages) {
					saveRegion.myButton.src = this.buttonPathLive;
					saveRegion.myButton.addClass('clickable');
				}
				else
					saveRegion.myButton.value = this.options.buttonTextLive;
				
				saveRegion.myButton.disabled = false;
			}, this);
			
			this.saveEnabled = true;
		}
	}
	
});


AutoSaver.implement(new Options, new Events);