/**
 * Track changes to form (with class vm-change-tracking) and prevent
 * browsing away if changed
 * 
 * @author Visual Media
 */

(function($) {

    var forms = [];

    /**
     * Generated serialized version of form
     * 
     * @param {DOMElement} frm Form to serialize
     * 
     * @returns {String} Serialized form
     */
    var serialize = function(frm) {

        // Indicate we are serializing
        frm.triggerHandler('beforeserialize');
        var data = frm.serializeArray();
        frm.triggerHandler('afterserialize');
        
        return JSON.stringify(data);
    };

    var changeTrackingActions = {        
        
        /**
         * Initialize changeTracking 
         * 
         * @param {DOMElement} element Form element
         * 
         * @returns {DOMElement}
         */
        init : function(element) {
            
            changeTrackingActions.resetState(element);
            
            // Give form some time to do its work, and if someone leaves 
            // within 1 sec, its problably not a problem that information 
            // is lost
            setTimeout(function() {
                // Set submit time when submitting, this prevents change tracking
                // to activate on form submit                
                element.on('submit', function() {
                    var now = new Date();
                    var ms = now.getTime();
                    element.data('vm-change-submit-time', ms);
                });
                                
            }, 1000);
            
            // Add element to list to check
            forms.push(element);
            
            return element;
        },
        
        /**
         * Reset state so that changes are only recorder after this point
         *  
         * @param {DOMElement} element DOMElement
         * 
         * @returns {DOMElement}
         */
        resetState : function(element) {
            // Save starting state to form element
            var start = serialize(element);            
            element.data('vm-change-start', start);
            return element;
        },
        
        
        /**
         * Mark form as changed
         *  
         * @param {DOMElement} element DOMElement
         * 
         * @returns {DOMElement}
         */
        markChanged : function(element) {
            
            // Fill start data with random data, this will trigger
            // data changed
            element.data('vm-change-start', '{ "hello" : "world" }');
            element.data('vm-change-submit-time', 0);
            
        },
        
        /**
         * Check if form contents is changed
         * 
         * @param {DOMElement} element Form element
         * 
         * @returns {Boolean} Is changed
         */
        isChanged : function(element) {
            
            // Check if start is already filled (happens after 1 sec)
            // If not there is no way to know.. assume nothing changed
            var start = element.data('vm-change-start');
            if (start === undefined) {
                return false;
            }
            
            // Get current data
            var curr = serialize(element);
            
            // Check if changed
            if (curr != start) {
                return true;
            }
            
            return false;
            
        }
        
        
    };
    
    /**
     * ChangeTracking Plugin
     * 
     * @param {String} Action
     * 
     * @return {Object}
     */
    $.fn.changeTracking = function(action) {

        if ((JSON === undefined) || (JSON.stringify === undefined)) {
            throw "changeTracking needs JSON stringify.";
        }
        
        if (!this.is('form') || this.length > 1) {
            throw "changeTracking expects to be called from a single FORM element";
        }

        if (action === undefined) {
            action = 'init';
        }
        
        if (changeTrackingActions[action] === undefined) {
            throw "action " + action + " is not valid for changeTracking";
        }
        
        // Execute action
        return changeTrackingActions[action](this);
        
        
    };
    

    $(document).ready(function() {        
        
        // Attach change tracking to 
        $('form.vm-change-tracking').each(function() {
            var frm = $(this);
            frm.changeTracking();                 
        });
        
        $(document).on('leaveWithChanges', function(e) {
            return "Er zijn niet opgeslagen wijzigingen, weet u zeker dat u deze pagina wilt verlaten?";
        });
        
        // beforeunload is not working with jQuery event handlers, therefore
        // we add it directly with window.addEventListner        
        window.addEventListener("beforeunload", function(e) {
            
            var changed = [];
            
            // Go trough tracked forms
            $.each(forms, function() {                
                
                // If the user just hitted the submit button ignore the fact
                // that the form has changed, we are submitting
                var ms = new Date().getTime();
                var submit_time = this.data('vm-change-submit-time') + 0;
                if ((ms - submit_time) < 1000) {
                    return;
                }

                if (this.changeTracking('isChanged')) {
                    changed.push(this);
                }      
                
            });
            
            var ev = $.Event((changed.length > 0) ? 'leaveWithChanges' : 'leaveWithoutChanged');
            ev.changed = changed;
            $(document).trigger(ev);
            
            // Check result, if it's undefined (no handler called) or false
            // then show no warning
            if ((ev.result === undefined) || (ev.result === false)) {
                return;
            }
            
            // Most crossbrowser way to show message
            // https://developer.mozilla.org/en-US/docs/Web/Reference/Events/beforeunload
            var confirmationMessage = ev.result;
            (e || window.event).returnValue = confirmationMessage;     //Gecko + IE
            return confirmationMessage;

        });
        
    });

})(jQuery);