/*
 * Wrapped in closure to prevent external manipulation;
 */

var evLog;
( function () {

    var startTime = new Date().getTime();

    function addEvent ( element, event, callback, capture ) {
        if ( typeof element === "undefined" || element === null) {
            return;
        }
        if ( typeof element.addEventListener === 'function' ) {
            element.addEventListener( event, callback, capture );
        } else if ( element.attachEvent ) {
            element.attachEvent( 'on' + event, callback );
        }
    }

    evLog = evLog || (function () {
            var log = {};
            return function ( ev, elem, attr ) {

                if ( ev === 'bind' ) {

                    evLog( attr + "Bind" );

                    addEvent( elem, 'change', function ( ev ) {
                        evLog( attr + 'FieldChangeCount' );
                        evLog( 'log', attr, 'ch' );
                        try {
                            evLog( 'set', attr + 'FieldEvHa', countEventHandlers( elem ) );
                        } catch ( e ) {
                            evLog( 'set', attr + 'FieldEvHa', 'Err' );
                        }
                    }, true );
                    addEvent( elem, 'click', function () {
                        evLog( attr + 'FieldClickCount' );
                        evLog( 'log', attr, 'cl' );
                    }, true );
                    addEvent( elem, 'focus', function () {
                        evLog( attr + 'FieldFocusCount' );
                        evLog( 'log', attr, 'fo' );
                    }, true );
                    addEvent( elem, 'blur', function () {
                        evLog( attr + 'FieldBlurCount' );
                        evLog( 'log', attr, 'bl' );
                    }, true );
                    addEvent( elem, 'touchstart', function () {
                        evLog( attr + 'FieldTouchStartCount' );
                        evLog( 'log', attr, 'Ts' );
                    }, true );
                    addEvent( elem, 'touchend', function () {
                        evLog( attr + 'FieldTouchEndCount' );
                        evLog( 'log', attr, 'Te' );
                    }, true );
                    addEvent( elem, 'touchcancel', function () {
                        evLog( attr + 'FieldTouchCancelCount' );
                        evLog( 'log', attr, 'Tc' );
                    }, true );
                    addEvent( elem, 'keyup', function ( ev ) {
                        if ( ev.keyCode === 16 ) {
                            evLog( 'log', attr, 'Su' );
                        } else if ( ev.keyCode === 17 ) {
                            evLog( 'log', attr, 'Cu' );
                        } else if ( ev.keyCode === 18 ) {
                            evLog( 'log', attr, 'Au' );
                        }
                    } );
                    addEvent( elem, 'keydown', function ( ev ) {
                        evLog( attr + 'FieldKeyCount' );
                        switch ( ev && ev.keyCode ) {
                        case 8: //backspace
                            evLog( 'log', attr, 'Kb' );
                            break;
                        case 16: // shift
                            evLog( 'log', attr, 'Sd' );
                            break;
                        case 17: // ctrl
                            evLog( 'log', attr, 'Cd' );
                            break;
                        case 18: // alt
                            evLog( 'log', attr, 'Ad' );
                            break;
                        case 37: // Left arrow
                            evLog( 'log', attr, 'Kl' );
                            break;
                        case 39: // right arrow
                            evLog( 'log', attr, 'Kr' );
                            break;
                        case 46: //delete
                            evLog( 'log', attr, 'Kd' );
                            break;
                        case 32: //space
                            evLog( 'log', attr, 'Ks' );
                            break;
                        default:
                            if ( ev.keyCode >= 48 && ev.keyCode <= 57 || ev.keyCode >= 96 && ev.keyCode <= 105 ) {
                                // numbers
                                evLog( 'log', attr, 'KN' );
                            } else if ( ev.keyCode >= 65 && ev.keyCode <= 90 ) {
                                // numbers
                                evLog( 'log', attr, 'KL' );
                            } else {
                                evLog( 'log', attr, 'KU' );
                                evLog( 'log', attr + 'UnkKeys', ev.keyCode );
                            }
                            break;
                        }
                    }, true );
                    return;
                }
                if ( ev === 'set' ) {
                    log [ elem ] = attr;
                    return;
                }
                if ( ev === 'log' ) {
                    var logEntry = elem + "FieldLog";
                    var relativeTime = (new Date().getTime()) - startTime;
                    relativeTime = Math.round( relativeTime / 100 );
                    if ( !log.hasOwnProperty( logEntry ) ) {
                        log[ logEntry ] = attr + "@" + relativeTime;
                    } else {
                        log[ logEntry ] += "," + attr + "@" + relativeTime;
                    }

                    if ( log[ logEntry ].length > 1500 ) {
                        log[ logEntry ] = log[ logEntry ].substring( log[ logEntry ].length - 1500 );
                        log[ logEntry ] = log[ logEntry ].substring( log[ logEntry ].indexOf( "," ) + 1 );
                    }

                    return;
                }

                if ( ev === 'extend' ) {

                    for ( var i in log ) {
                        if ( log.hasOwnProperty( i ) ) {
                            if ( i === 'number' || i === 'expiryMonth' || i === 'expiryYear' || i === 'generationtime' || i === 'holderName' || i === 'cvc' ) {
                                continue;
                            }
                            elem[ i ] = "" + log[ i ];
                        }
                    }

                    return;
                }

                if ( !log.hasOwnProperty( ev ) ) {
                    log[ ev ] = 1;
                } else {
                    log[ ev ]++;
                }

            };
        })();

    /* based on http://www.nonobtrusive.com/2009/07/23/count-the-number-of-javascript-eventhandlers-in-the-dom-tree/ */
    function countEventHandlers ( node ) {

        var testNonNative = function () {
            return {};
        };

        if ( window.jQuery && typeof window.jQuery._data === "function" ) {

            testNonNative = function ( n ) {
                return window.jQuery._data( n, 'events' );
            };
        }

        var element = node, counter = 0,
            countermap = [],
        /* fill up with more events if needed or just use those you want to look for */
            events = [ 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout',
                'onclick', 'onmousemove', 'ondblclick', 'onerror', 'onresize', 'onscroll',
                'onkeydown', 'onkeyup', 'onkeypress', 'onchange', 'onsubmit' ], ownSuffix = 'Own', parentSuffix = 'Par',
            eventlen = events.length;

        var iterations = 0;

        while ( element && element !== element.documentElement ) {  //go through all DOM-nodes

            iterations++;

            var tmp = eventlen, evName, evCount, elemName = (element.nodeName || element.tagName || '').toUpperCase().substring( 0, 3 );

            while ( tmp-- ) {  //go through all events defined above for each node and see if it exists.

                evName = events[ tmp ];

                if ( element[ name ] ) {
                    evName = evName + ((element === node) ? ownSuffix : parentSuffix) + elemName;
                    counter++;
                    countermap[ evName ] = countermap[ evName ] || 0;
                    countermap[ evName ]++;
                }

            }

            var nonNativeEvents = testNonNative( element );

            if ( typeof nonNativeEvents === "object" ) {
                for ( evName in nonNativeEvents ) {
                    if ( nonNativeEvents.hasOwnProperty( evName ) ) {
                        evCount = nonNativeEvents[ evName ].length;
                        evName = evName + ((element === node) ? ownSuffix : parentSuffix) + elemName;
                        countermap[ evName ] = countermap[ evName ] || 0;
                        countermap[ evName ] += evCount;
                        counter += evCount;
                    }
                }
            }

            if ( !element.parentNode ) {
                break;
            }

            element = element.parentNode;
        }

        var someStats = [ "total=" + counter ];
        for ( var o in countermap ) {
            if ( countermap.hasOwnProperty( o ) && countermap[ o ] > 0 ) {
                someStats.push( o + "=" + countermap[ o ] );
            }
        }

        return someStats.join( "&" );
    }

    if ( window && (window.attachEvent || window.addEventListener) ) {

        addEvent( window, "focus", function () {
            evLog( "activate" );
        } );

        addEvent( window, "blur", function () {
            evLog( "deactivate" );
        } );
    }
}());
