/**
 * This plugin provides functionality to expand a text box on focus to double it's current width
 *
 * Usage:
 *
 * Set options:
 *		$('#textbox').expandableField( { option1: value1, option2: value2 } );
 *		$('#textbox').expandableField( option, value );
 * Get option:
 *		value = $('#textbox').expandableField( option );
 * Initialize:
 *		$('#textbox').expandableField();
 *
 * Options:
 *
 */
( function ( $ ) {

	$.expandableField = {
		/**
		 * Expand the field, make the callback
		 */
		expandField: function ( e, context ) {
			context.config.beforeExpand.call( context.data.$field, context );
			context.data.$field
				.animate( { width: context.data.expandedWidth }, 'fast', function () {
					context.config.afterExpand.call( this, context );
				} );
		},
		/**
		 * Condense the field, make the callback
		 */
		condenseField: function ( e, context ) {
			context.config.beforeCondense.call( context.data.$field, context );
			context.data.$field
				.animate( { width: context.data.condensedWidth }, 'fast', function () {
					context.config.afterCondense.call( this, context );
				} );
		},
		/**
		 * Sets the value of a property, and updates the widget accordingly
		 *
		 * @param {String} property Name of property
		 * @param {Mixed} value Value to set property with
		 */
		configure: function ( context, property, value ) {
			// TODO: Validate creation using fallback values
			context.config[ property ] = value;
		}

	};

	$.fn.expandableField = function () {

		// Multi-context fields
		var returnValue,
			args = arguments;

		$( this ).each( function () {
			var key, context, timeout;

			/* Construction / Loading */

			context = $( this ).data( 'expandableField-context' );

			// TODO: Do we need to check both null and undefined?
			if ( context === undefined || context === null ) {
				context = {
					config: {
						// callback function for before collapse
						beforeCondense: function () {},

						// callback function for before expand
						beforeExpand: function () {},

						// callback function for after collapse
						afterCondense: function () {},

						// callback function for after expand
						afterExpand: function () {},

						// Whether the field should expand to the left or the right -- defaults to left
						expandToLeft: true
					}
				};
			}

			/* API */
			// Handle various calling styles
			if ( args.length > 0 ) {
				if ( typeof args[ 0 ] === 'object' ) {
					// Apply set of properties
					for ( key in args[ 0 ] ) {
						$.expandableField.configure( context, key, args[ 0 ][ key ] );
					}
				} else if ( typeof args[ 0 ] === 'string' ) {
					if ( args.length > 1 ) {
						// Set property values
						$.expandableField.configure( context, args[ 0 ], args[ 1 ] );

					// TODO: Do we need to check both null and undefined?
					} else if ( returnValue === null || returnValue === undefined ) {
						// Get property values, but don't give access to internal data - returns only the first
						returnValue = ( args[ 0 ] in context.config ? undefined : context.config[ args[ 0 ] ] );
					}
				}
			}

			/* Initialization */

			if ( context.data === undefined ) {
				context.data = {
					// The width of the field in it's condensed state
					condensedWidth: $( this ).width(),

					// The width of the field in it's expanded state
					expandedWidth: $( this ).width() * 2,

					// Reference to the field
					$field: $( this )
				};

				$( this )
					.addClass( 'expandableField' )
					.focus( function ( e ) {
						clearTimeout( timeout );
						$.expandableField.expandField( e, context );
					} )
					.blur( function ( e ) {
						timeout = setTimeout( function () {
							$.expandableField.condenseField( e, context );
						}, 250 );
					} );
			}
			// Store the context for next time
			$( this ).data( 'expandableField-context', context );
		} );
		return returnValue !== undefined ? returnValue : $( this );
	};

}( jQuery ) );
