/* jQuery editable Copyright Dylan Verheul <dylan@dyve.net>
 * Heavily modified by Simon Plenderleith <simon@nstwdesign.com>
 * Licensed like jQuery, see http://docs.jquery.com/License
 */

$.fn.editable = function(getURL, updateURL, options) {
    
	// Options
	options = arrayMerge({
	    "type":           "eip-single",
		"helper":         "",
		"rolloverClass":  "",
	    
	    "getURL":         getURL,
		"updateURL":      updateURL,
		"paramName":      "q",
		"extraParams":    {},
		"callback":       null,
		"emptyCallback":  null,
		
		"savingText":     "Saving...",
		"errorText":      "Error saving",
		
		"delayOnBlur":    200,
		
		"allowEmpty":     false
	}, options);
	
	// Setup options
	options.rolloverClass   = options.type + '-hover';
	
	// Setup objects
	var me      = this;
	var $me     = $(me);
	var meId    = $me.attr('id');
    var $eip    = $('#' + meId + '-eip');
    var $inputField = $('#' + options.paramName);
	options.helper  = '#' + meId + '-eip-helper';
	var $helper     = $(options.helper);
	
	// Sizes, positions and shows EIP helper
	var showHelper = function(){
	    if(this.editing || !options.rolloverClass || !options.helper){
	        return;
        }
        // Unbind EIP target mouseover event
	    $me.unbind('mouseover.EIPTarget');
	    $me.addClass(options.rolloverClass);
    	// Get EIP target position and dimensions
    	var targetPosition  = $me.position();
    	var targetWidth     = $me.outerWidth();
    	var targetHeight    = $me.outerHeight();
    	var targetExtraLeft = parseInt($me.css('marginLeft'));
    	if($helper.length){
    	    if(options.type=='eip-block'){
        	    // Scale and position EIP helper
        	    $helper.css({
        	                  width:targetWidth, height:targetHeight,
        	                  top:targetPosition.top, left:targetPosition.left
        	                });
    	    }
    	    else if(options.type=='eip-single'){
        	    // Scale and position EIP helper
        	    $helper.css({
        	                  width:targetWidth,
        	                  height:23,
                              paddingTop:targetHeight,
        	                  top:targetPosition.top, left:(targetPosition.left + targetExtraLeft)
        	                });
    	    }
    	    // Show EIP helper
    	    $helper.removeClass('hide');
	    }
	};
	// Hides helper
	var hideHelper = function(){
	    if(this.editing || !options.rolloverClass || !options.helper){
	        return;
        }
        $(this).addClass('hide');
	    $me.removeClass(options.rolloverClass);
	    // Re-bind EIP target mouseover event
	    $me.bind('mouseover.EIPTarget', showHelper);
	}
    
    // Initial binding of mouseover event handler for EIP target
    $me.bind('mouseover.EIPTarget', showHelper);
	// Initial binding of mouseout event handler for EIP helper
	$helper.bind('mouseout.EIPHelper', hideHelper);
    
	// Helper click event handler
	$helper.bind('click.EIPHelper', function(e){
	    // Check if currently in EIP mode
    	if(this.editing){
    	    return;
        }
    	// Make element editable
    	editable();
	});
	
    // Define editable function
    function editable(){
        // Remove rollover class from EIP target and helper
        $me.removeClass(options.rolloverClass);
        $helper.addClass('hide');
        
        // Setup variables
        me.inputHTML    = '';
        me.origHTML     = $me.html();
        me.timer        = 0;
        
        // Toggle EIP
        toggleEIP(true);
    };
    
    function toggleEIP(mode){
        // Displayed
        if(mode){
            // Unbind EIP helper mouseout event
            $helper.unbind('mouseout.EIPHelper');
            function populateEIPInput(result, textStatus){
                if(result.status){
                    // Set input value
                    me.inputHTML = result.content;
                    $inputField.val(me.inputHTML);
                    // Remove rollover effect class
                    if(options.rolloverClass){
                        $me.removeClass(options.rolloverClass);
                    }
                    me.editing      = true;     // EIP editing flag
                    me.resetCalled  = false;    // Reset flag
                    $me.addClass('hide');       // Hide EIP target
                    $eip.removeClass('hide');   // Display EIP form
                    // Set focus to input
                    $inputField.get()[0].focus();
                }
            }
            $.ajax({type:'GET',url:options.getURL,success:populateEIPInput});
        }
        // Hidden
        else{
            // Re-bind EIP helper mouseout event
            $helper.bind('mouseout.EIPHelper', hideHelper);
    	    // Re-bind EIP target mouseover event
    	    $me.bind('mouseover.EIPTarget', showHelper);
            me.editing      = false;    // EIP editing flag
            $me.removeClass('hide');    // Show EIP target
            $eip.addClass('hide');      // Hide EIP form
        }
    }
    
    // Input onblur event handler
    $inputField.blur(
        (options.delayOnBlur ?
            function() {
                me.timer = setTimeout(function(){ reset(false); }, options.delayOnBlur);
            }
            : function() { reset(false); }
        )
    );
    
    // Keyboard Event Handling
    $inputField.keydown(function(e){
        // Cancel (ESC Key)
        if(e.keyCode==27) {
            e.preventDefault();
            reset(false);
        }
    });
    // Cancel (Button)
    $eip.find('a.negative').click(function(){
        clearTimeout(me.timer);
        reset(true);    // Override save
        return false;
    });

    // Form submit event handler
    $eip.children('form').submit(function(e){
        // Clear timer
        clearTimeout(me.timer);
        e.preventDefault();
        // Construct object of form input values
        var p = {};
        p[options.paramName] = $inputField.val();
        // Check if value is empty
        if(!options.allowEmpty && empty(p[options.paramName])){
            me.innerHTML = me.origHTML;
            toggleEIP(false);
        }
        else{
            $me.html(options.savingText);
            toggleEIP(false);
            function updateEIPTargetSuccess(result, textStatus){
                if(result.status){
                    // Update EIP target with new text
                    $me.html(result.content);
                    // Callback if necessary
                    if(options.callback){
                        options.callback(me);
                    }
                    // Empty callback if necessary
                    if(options.emptyCallback && empty($me.html())){
                        options.emptyCallback(me);
                    }
                }
                else{
                    updateEIPTargetError();
                }
            }
            function updateEIPTargetError(xmlHttpObj, result, errorObj){
                $me.html(options.errorText);
            }
            $.ajax({url:options.updateURL,data:arrayMerge(options.extraParams, p),success:updateEIPTargetSuccess,error:updateEIPTargetError});
        }
        return false;
    });

    // Reset form and turn off editing mode
    function reset(overrideSave) {
        var returnValue = true;
        // Check if reset has already been called
        if(me.resetCalled){
            return returnValue;
        }
        me.resetCalled  = true;
        // Check if user wants to save changes
        if(!overrideSave && (me.inputHTML != $inputField.val())){
            if(confirm('Do you want to save the changes you\'ve made?')){
                $eip.children('form').submit();
                return true;
            }
        }
        me.innerHTML = me.origHTML;
        // Call emptyCallback if origHTML is empty
        if(options.emptyCallback && empty(me.origHTML)){
            options.emptyCallback(me);
        }
        toggleEIP(false);
        return returnValue;
    };
	
	// -------------------------------------------------------------------------------------
	
	// Don't break the chain
	return this;
	
	// Helper functions
	
	// Merge two arrays
	function arrayMerge(a, b){
		if(a){
			if(b){
			    for(var i in b){
			        a[i] = b[i];
			    }
			}
			return a;
		}
		else{
			return b;		
		}
	}
	// Checks if a string is empty
	function empty(value){
	    if(value.replace(/\s*/, '')==''){
	        return true;
	    }
	    return false;
	}
	
};
