/**
 *
 * setGo - a jQuery proto-plugin.
 * http://www.github.com/seangirard/setGo
 *
 * Copyright (c) 2010 Sean Girard <sg@seangirard.com>
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Based on the original pattern by Mike Alsup:
 * http://www.learningjquery.com/2007/10/a-plugin-development-pattern
 *
 * This article by Hector Virgen outlines a similar approach to the one used here: 
 * http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html
 *
 * Also see the pattern by Keith Woods for another plugin framework:
 * http://keith-wood.name/pluginFramework.html
 *
 * Thanks to John Resig and the jQuery community!
 *
 * @requires jQuery 1.4.2
 * 
 */
 
 
(function($) {
    
    // Datastore Location
    var __namespace = 'setGo';
    
    /**
     * 
     * setGo provides a simple plugin framework and namespace for your jQuery application.
     * 
     * $(document).ready(function() {
     *     $('#app').setGo();
     * });
     *
     * @param {Object} options (optional) - plugin data object, override and/or add your own
     * @param {Object} target (optional) - change 'this' context to custom *selector* (not object)
     * 
     * @return {Object} jQuery chain
     * @see README
     * 
     */
    $.fn.setGo = function(options, target) {
        // Set default options prior to element iteration
        var opts = $.extend({}, __options, options);
        // Iterate and __construct each matched element
        return this.each(function() {
            // Cache calling/target object
            var $this = target && $(target) ? $(target) : $(this);
            // Get element-specific options; supports metadata plugin
            var $opts = $.metadata ?  $.extend({}, opts, $this.metadata()) : opts;
            // Attach options to calling object
            $this.data(__namespace, $opts);
            // Attach calling/target object to plugin object
            $.fn.setGo.$obj = $this;
            // Plugin: set, Go!
            __construct($this);
        });
    };
    
    /**
     * 
     * Application properties - stored in .data()
     *
     * @see .setGo()
     * @see .setGo.setOptions()
     * 
     */
    __options = {
        // Plugin properties
        
        // Callbacks (reserved)
        __success: function($this, $opts) { __trace($opts); },
        __failure: function($this, $opts, err) { __trace(err); }
    };
    
    /**
     * 
     * App implementation
     *
     * @private 
     *
     * @param {Object} $this - calling/target (this) context
     * @param {Object} $opts - plugin properties
     *
     * @see __construct() 
     * @see __options
     *
     */
    __init = function($this, $opts) {        
        
        $this.load($opts.adv, function() {
            $this.setGo.relExternal();
            $this.setGo.setUniqueKey();
            $this.setGo.prepareCaptcha();
            $this.setGo.populateIdentity();
            $this.setGo.initAdvancedUpload();
        });

    };
    
    //
    // UUID METODS
    //
    
    $.fn.setGo.setUniqueKey = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        $opts.key = $.uuid();
        $('#UPLOAD_IDENTIFIER').val($opts.key);
        
    }
    
    //
    // CAPTCHA METHODS
    // 
    
    $.fn.setGo.prepareCaptcha = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        var fields = $this.setGo.getCaptchaFields();
        $.getJSON($opts.act, {captcha:true}, function(captcha) {
            setCaptchaFields(captcha.salt, captcha.hash);
        });
        
        function setCaptchaFields(salt, hash) {
            $('#salt').val(salt);
            $('#hash').val(hash);
        }
    }
    
    $.fn.setGo.getCaptchaFields = function() {
        var salt = $('#salt').val();
        var hash = $('#hash').val();
        var data = {salt:salt, hash:hash};
        
        return data;
    }
    
    //
    // FORM METHODS
    //
    
    $.fn.setGo.decorateFormFields = function() {
        $('input, textarea').focus(function() {
            $(this).addClass('focus');
            var name = $(this).attr('name');
            $('label[for='+name+']').addClass('focusLabel');
        });
                
        $('input, textarea').blur(function() {
            $(this).removeClass('focus');
            var name = $(this).attr('name');
            $('label[for='+name+']').removeClass('focusLabel');
        });	
    }   
    
    //
    // IDENTITY METHODS
    // 
    
    $.fn.setGo.populateIdentity = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        var identity = $this.setGo.retrieveIdentity(); 
        
        for (x in $opts.identity) {
            var field = $opts.identity[x];
            $('#'+field).val(identity[field]);
        }
    }
    
    $.fn.setGo.retrieveIdentity = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        var identity = new Array();
        
        for (x in $opts.identity) {
            var cookie = $opts.cookieprefix+$opts.identity[x];
            var field = $.cookie(cookie);
            if (null == field || 'null' == field) {
                field = '';
            }
            
            identity[$opts.identity[x]] = field; 
        }
        
        return identity;
    }
    
    $.fn.setGo.removeIdentity = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        for (x in $opts.identity) {
            var cookie = $opts.cookieprefix+$opts.identity[x];
            $.cookie(cookie, null, { path: $opts.cookiepath, expires: -1 }); 
        }
    }
    
    $.fn.setGo.storeIdentity = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        for (x in $opts.identity) {
            var field = $opts.identity[x];
            var value = $('#'+field).val();
            var cookie = $opts.cookieprefix+$opts.identity[x];
            if (!value) {
                value = ''; // Allow empty optional values
            }
            $.cookie(cookie, value, { path: $opts.cookiepath, expires: 1000 });
        }
    }
    
    //
    // UPLOAD METHODS
    //
    
    $.fn.setGo.initAdvancedUpload = function() {
        var $this = this.$obj;
        /*
        // Advanced File Upload
        */
        $this.init = function() {
            $opts = $this.setGo.getOptions();
            
            // Init Form Values
            
            $('#uploadForm').attr('action', $opts.act);
            
            $this.setGo.decorateFormFields();
            
            // Bind click handlers
            
            $this.setGo.preventEvent('#uploadTrigger', uploadTrigger);
            
            $this.setGo.preventEvent('#uploadMessageAdd', uploadMessageAdd);
            $this.setGo.preventEvent('#uploadMessageReset', uploadMessageReset);
            $this.setGo.preventEvent('#uploadMessageCancel', uploadMessageCancel);
            
            $this.setGo.preventEvent('#uploadReset', uploadReset);
            $this.setGo.preventEvent('#uploadRemove', uploadReset);
            
            // Do not prevent bubble on these focus events
            
            $('tr.optional td input').live('focus', uploadMessageResetControl);
            $('tr.optional td textarea').live('focus', uploadMessageResetControl);
            
            // Init Focus
            
            uploadFocus();
            
            // Init Plugins
            
            $('#uploadProgress').progressBar({
                boxImage: '../lib/img/progress/progressbar.gif',
                barImage: '../lib/img/progress/progressbg_green.gif'
            });

            $('#uploadForm').validate({
                //debug: true,
                ignoreTitle: true,
                submitHandler: uploadSubmit,
                validClass: 'valid',
                errorClass: 'invalid',
                wrapper: 'div'
            });
            
            $('#uploadMulti').MultiFile({
                list: '#uploadList',
                autoIntercept:true,
                afterFileAppend:function(element,value,master){
                    var count = master.n-1;
                    uploadControl(count);
                },
                afterFileRemove:function(element,value,master){
                    var count = master.n-2;
                    uploadControl(count);
                },
                STRING: {
                    remove: '<img src="'+$opts.rem+'" alt="x" title="Click to remove."/>'
                }
            });
            
        }
        
        
        // Upload routines
        
        function uploadFocus() {
            
            var fullname =  $('#fullname').val();
            var emailaddr =  $('#emailaddr').val();
            
            if (0 == fullname.length) {
                $('#fullname').focus();
            } else if (0 == emailaddr.length) {
                $('#emailaddr').focus();
            }
        }
        
        function uploadTrigger() {
            $('#uploadForm').submit();
        }
        
        function uploadMessageAdd() {
            $('#uploadMessageAdd').hide();
            $('#uploadMessageCancel').fadeIn();
            
            $('tr.optional').show();
            
            $('#subject').focus();
        }
        
        function uploadMessageReset() {
            $('#subject').val('');
            $('#message').val('');
            $('#phonenumber').val('');
            $('#uploadMessageReset').fadeOut();
            
            $('#subject').focus();
        }
        
        function uploadMessageResetControl() {
            var show = false;
            $('tr.optional td input, tr.optional td textarea').each(function() {
                var value = $(this).val();
                if (0 < value.length) {
                    show = true;
                }
            });
            
            if (show) {
                $('#uploadMessageReset').fadeIn();
            } else {
                $('#uploadMessageReset').fadeOut();
            }
        }
        
        function uploadMessageCancel() {
            uploadMessageReset();
            $('tr.optional').hide();
            
            $('#uploadMessageCancel').hide();
            $('#uploadMessageReset').hide();
            $('#uploadMessageAdd').fadeIn();
        }
        
        function uploadReset() {
            $('#uploadReset').hide();
            $('#uploadRemove').hide();
            $('#uploadStatus').hide();
            $('#uploadInstruct').hide();
            $('#uploadProgress').hide();
            
            $('#uploadComplete').hide();
            $('#uploadManifest').html('');
            
            $('input:file').MultiFile('reset');
            $('#uploadProgress').progressBar(0);
            
            $('#uploadFile').fadeIn();
            $('#uploadOptional').fadeIn();
            
            $('#uploadContainer input').attr('disabled', false);
            $('#uploadContainer textarea').attr('disabled', false);
            
            $('#uploadError').hide();
            
            $this.setGo.setUniqueKey();
            $this.setGo.prepareCaptcha();
        }
        
        function uploadSubmit(form) {
            form.submit();
            uploadBegin();
            uploadContact();
            $opts.uploadInterval = setInterval(function() { 
                $.ajax({
                    url: $opts.act,
                    beforeSend: ajaxBegin,
                    success: ajaxSuccess,
                    complete: ajaxComplete,
                    error: ajaxError,
                    data: {UPLOAD_IDENTIFIER: $opts.key, salt: $('#salt').val(), hash: $('#hash').val()},
                    type: 'POST',
                    dataType: 'json'
                });
            }, 2500);
        }
        
        function uploadBegin() {
            $('#uploadContainer input').attr('disabled', true);
            $('#uploadContainer textarea').attr('disabled', true);
            
            $('#uploadProgress').fadeIn();
            
            $('#uploadList').fadeOut();
            
            $('#uploadFile').hide();
            $('#uploadInstruct').hide();
            $('#uploadTrigger').hide();
            $('#uploadRemove').hide();
            $('#uploadOptional').hide();
            
            $('#uploadStatus').html('Initiating transfer...').show();
        }
        
        function uploadContact() {
            $this.setGo.storeIdentity();
        }
        
        function uploadControl(count) {
            if (count>0) {
                $('#uploadList').show();
                $('#uploadTrigger').show();
                $('#uploadInstruct').show();
            } else {
                $('#uploadList').hide();
                $('#uploadTrigger').hide();
                $('#uploadInstruct').hide();
            }
            if (count>1) {
                $('#uploadRemove').show();
            }
            
            $('#uploadError').hide();
        }
        
        function uploadReport(status) {
            $('#uploadProgress').progressBar(status.percent);
            
            var report = '';
            if (status.remain && status.total) {
                report += status.remain;
                report += ' of ';
                report += status.total;
                report += ' ';
                report += 'remaining';
                if (status.time) {
                    report += '<br />';
                    report += '('+status.time+')';
                }
            } else {
                report = 'Completing transfer...';
            }
            report += '<br />';
            report += '<em>';
            report += 'Please do not close this window.';
            report += '</em>';
            
            $('#uploadStatus').html(report);
        }
        
        function uploadComplete(status) {
            clearInterval($opts.uploadInterval);
            $('#uploadProgress').progressBar(100);
            
            var report = 'Transfer complete.';
            
            var manifest = '<h3>We received the following file(s):</h3>';
            manifest += '<ul>';
            
            var error = true;
            if (status && status.list) {
                for (x in status.list) {
                    manifest += '<li>';
                    manifest += status.list[x].name;
                    manifest += '</li>';
                }
                error = false;
            } else {
                manifest += '<li>';
                manifest += 'Actually, we seem to have encountered a problem...';
                manifest += '<br />';
                manifest += 'Please <a href="./">reload</a> and try again. Sorry!';
                manifest += '</li>';
            }
            
            manifest += '</ul>';
            
            $('#uploadManifest').html(manifest);
            $('#uploadStatus').html(report);
            
            $('#uploadReset').delay(2500).fadeIn();
            if (!error) {
                $('#uploadComplete').delay(2500).fadeIn();
            }
        }
        
        function uploadSpam() {
            clearInterval($opts.uploadInterval);
            uploadReset();
            $('#uploadError').show();
        }
        
        function uploadError() {
            clearInterval($opts.uploadInterval);
            uploadReset();
            $('#uploadError').show();
        }
        
        // Ajax routines (run on an interval)
        
        function ajaxBegin() {
            
        }
        
        function ajaxSuccess(status) {
            if ( (status == null) || (100 == status.percent) ) {
                uploadComplete(status);
            } else if (status == 'spam') {
                uploadSpam();
            } else if (status == 'error') {
                uploadError();
            } else {
                uploadReport(status);
            }
        }
        
        function ajaxComplete(response) {
            
        }
        
        function ajaxError() {
        
        }
        
        // Init
        return __call($this);
    };
    
    //
    // PUBLIC METHODS
    //
    
    /**
     *
     * Sample public method
     *
     * @see __call()
     * @see __private()
     *
     */
    $.fn.setGo.publicOption = function() {
        var $this = this.$obj;
        var $opts = $this.setGo.getOptions();
        
        // Method implementation
        $this.init = function() {
            
            __private();
        
        };
        
        // Init
        return __call($this);
    };
    
    //
    // PUBLIC UTILITIES
    //
    
    /**
     *
     * Anchors w/ rel attribute value of external target _blank viewport
     *
     */
    $.fn.setGo.relExternal = function() {
        var $this = this.$obj;
        
        $this.init = function() {
            $('a[rel=external]').attr('target','_blank');
        };
        
        return __call($this);
    };
    
    /**
     *
     * Convert line endings to <br /> tags
     *
     * @param {String} txt
     *
     */
    $.fn.setGo.newLine = function(txt) {
        var $this = this.$obj;
        
        $this.init = function() {
            if (txt && typeof(txt) !== 'String') {
                return nl2br(txt);
            }    
        };
        
        function nl2br(str) {
            return str.replace(/\r\n|\r|\n/g, '<br />');
        }
        
        return __call($this);
    };
    
    /**
     *
     * Bind handler to event on selected element
     * Experimental wrapper to avoid calling preventDefault everywhere
     *
     * @param {String} selector - Bind to this element
     * @param {Object} custom - Bind this function
     * @param {String} event - (optional ) Event to bind, default=Click 
     * @param {Boolean} live - (optional) Whether to bind as live event, default=True
     * 
     */
    $.fn.setGo.preventEvent = function(selector, custom, event, live) {
        var $this = this.$obj;
        
        $this.init = function() {
            if (selector && custom) {
                if (!event) {
                    event = 'click';
                }
                if (false!==live && $(selector).live) {
                    $(selector).live(event,function(e,d,h) {
                        custom(e,d,h);
                        e.preventDefault();
                    });
                } else {
                    $(selector).bind(event,function(e,d,h) {
                        custom(e,d,h);
                        e.preventDefault();
                    });
                }
            }
        };
        
        return __call($this);
    };
    
    /**
     *
     * Override plugin property defalts, optionally merge
     *
     * @param {Object} $opts - New plugin options
     * @param {Boolean} merge (optional) - Whether to merge new values
     *                  False=overwrite, True=merge (default=True)
     *
     * @see __options
     * @see getOptions() 
     *
     */
    $.fn.setGo.setOptions = function($opts, merge) {
        var $this = this.$obj;
        
        $this.init = function() {
            if (merge) {
                $.extend($this.data(__namespace), $opts);
            } else {
                $this.data(__namespace, $opts);
            }
        };
        
        return __call($this);
    };
    
    /**
     *
     * Get plugin properties
     *
     * @return {Object} $this.data(__namespace)
     *
     * @see setOptions()
     * @see __options
     *
     */
    $.fn.setGo.getOptions = function() {
        var $this = this.$obj;
        
        $this.init = function() {
            return $this.data(__namespace);
        };
        
        return __call($this);
    };
    
    /**
     *
     * Echo plugin properties to console
     *
     * @see getOptions()
     *
     */
    $.fn.setGo.traceOptions = function() {
        var $this = this.$obj;
        
        $this.init = function() {
            __trace($this.setGo.getOptions());
        };
        
        return __call($this);
    };
    
    //
    // PRIVATE FUNCTIONS
    //
    
    /**
     *
     * Sample private function
     *
     * @private
     *
     */
    function __private() {
        alert('');
    }
    
    
    //
    // PLUGIN CORE
    //
    
    /**
     * 
     * Safe method call
     *
     * @private 
     *
     * @param {Object} $this - calling/target (this) context
     *
     * @see .init() - (all public methods) 
     *
     */
    function __call($this) {
        try {
            return $this.init();
        } catch(err) { return __error($this, err); }
    }
    
    /**
     * 
     * Auto-call plugin
     *
     * @private 
     *
     * @param {Object} $this - calling/target (this) context
     *
     * @see __init()
     *
     */
    function __construct($this) {
        try {
            $opts = $this.setGo.getOptions();
            $this.queue(function () {
                 __init($this, $opts);
                $this.dequeue();
            });
            $this.queue(function () {
                __destruct($this, $opts);
                $this.dequeue();
            });
        } catch(err) { __error($this, err); }
    }
    
    /**
     * 
     * Plugin auto-callback (trigger success/failure handlers)
     *
     * @private 
     *
     * @param {Object} $this - calling/target (this) context
     * @param {Object} $opts - plugin properties
     *
     * @see __success()
     *
     */
    function __destruct ($this, $opts) {
        try {
            if ($.isFunction($opts.__success)) {
                $opts.__success($this, $opts);
            }
        } catch(err) { __error($this, $opts, err); }
    }
    
    /**
     * 
     * Error handler
     *
     * @private 
     *
     * @param {Object} $this - calling/target (this) context
     * @param {Object} $opts - plugin properties
     * @param {Object} err - caught error
     *
     * @see __failure()
     * @see __call()
     *
     */
    function __error($this, $opts, err) {
        try {
            if ($.isFunction($opts.__failure)) {
                return $opts.__failure($this, err);
            }
        }
        catch(e) { return __trace(e); }
    }
    
    /**
     * 
     * Debug
     *
     * @private 
     *
     */
    function __trace($obj) {
        if (window.console && window.console.log) {
            return window.console.log($obj);
        }
    }
    
// Closure
})(jQuery);
