/*
	Blackbird - Open Source JavaScript Logging Utility
	Author: G Scott Olson
	Web: http://blackbirdjs.googlecode.com/
	     http://www.gscottolson.com/blackbirdjs/
	Version: 1.0

	The MIT License - Copyright (c) 2008 Blackbird Project
*/
( function() {
    var NAMESPACE = 'log';
    var IE6_POSITION_FIXED = true; // enable IE6 {position:fixed}

    var bbird;
    var outputList;
    var cache = [];

    var state = getState();
    var classes = {};
    var profiler = {};
    var IDs = {
        blackbird: 'blackbird',
        checkbox: 'bbVis',
        filters: 'bbFilters',
        controls: 'bbControls',
        size: 'bbSize'
    }
    var messageTypes = { //order of these properties imply render order of filter controls
        debug: true,
        info: true,
        warn: true,
        error: true,
        profile: true
    };

    function generateMarkup() { //build markup
        var spans = [];
        for ( type in messageTypes ) {
            spans.push( [ '<span class="', type, '" type="', type, '"></span>'].join( '' ) );
        }

        var newNode = document.createElement( 'DIV' );
        newNode.id = IDs.blackbird;
        newNode.style.display = 'none';
        newNode.innerHTML = [
        '<div class="header">',
        '<div class="bbleft">',
        '<div id="', IDs.filters, '" class="filters" title="click to filter by message type">', spans.join( '' ), '</div>',
        '</div>',
        '<div class="bbright">',
        '<div id="', IDs.controls, '" class="controls">',
        '<span id="', IDs.size ,'" title="contract" op="resize"></span>',
        '<span class="bbclear" title="clear" op="clear"></span>',
        '<span class="close" title="close" op="close"></span>',
        '</div>',
        '</div>',
        '</div>',
        '<div class="main">',
        '<div class="bbleft"></div><div class="mainBody">',
        '<ol>', cache.join( '' ), '</ol>',
        '</div><div class="bbright"></div>',
        '</div>',
        '<div class="footer">',
        '<div class="bbleft"><label for="', IDs.checkbox, '"><input type="checkbox" id="', IDs.checkbox, '" />Visible on page load</label></div>',
        '<div class="bbright"></div>',
        '</div>'
        ].join( '' );
        return newNode;
    }

    function backgroundImage() { //(IE6 only) change <BODY> tag's background to resolve {position:fixed} support
        var bodyTag = document.getElementsByTagName( 'BODY' )[ 0 ];

        if ( bodyTag.currentStyle && IE6_POSITION_FIXED ) {
            if (bodyTag.currentStyle.backgroundImage == 'none' ) {
                bodyTag.style.backgroundImage = 'url(about:blank)';
            }
            if (bodyTag.currentStyle.backgroundAttachment == 'scroll' ) {
                bodyTag.style.backgroundAttachment = 'fixed';
            }
        }
    }

    function addMessage( type, content ) { //adds a message to the output list
        if (content == null) content = 'NULL';
        content = ( content.constructor == Array ) ? content.join( '' ) : content;
        if ( outputList ) {
            var newMsg = document.createElement( 'LI' );
            newMsg.className = type;
            newMsg.innerHTML = [ '<span class="icon"></span>', content ].join( '' );
            outputList.appendChild( newMsg );
            scrollToBottom();
        } else {
            cache.push( [ '<li class="', type, '"><span class="icon"></span>', content, '</li>' ].join( '' ) );
        }
    }

    function clear() { //clear list output
        outputList.innerHTML = '';
    }

    function clickControl( evt ) {
        if ( !evt ) evt = window.event;
        var el = ( evt.target ) ? evt.target : evt.srcElement;

        if ( el.tagName == 'SPAN' ) {
            switch ( el.getAttributeNode( 'op' ).nodeValue ) {
                case 'resize':
                    resize();
                    break;
                case 'clear':
                    clear();
                    break;
                case 'close':
                    hide();
                    break;
            }
        }
    }

    function clickFilter( evt ) { //show/hide a specific message type
        if ( !evt ) evt = window.event;
        var span = ( evt.target ) ? evt.target : evt.srcElement;

        if ( span && span.tagName == 'SPAN' ) {

            var type = span.getAttributeNode( 'type' ).nodeValue;

            if ( evt.altKey ) {
                var filters = document.getElementById( IDs.filters ).getElementsByTagName( 'SPAN' );

                var active = 0;
                for ( entry in messageTypes ) {
                    if ( messageTypes[ entry ] ) active++;
                }
                var oneActiveFilter = ( active == 1 && messageTypes[ type ] );

                for ( var i = 0; filters[ i ]; i++ ) {
                    var spanType = filters[ i ].getAttributeNode( 'type' ).nodeValue;

                    filters[ i ].className = ( oneActiveFilter || ( spanType == type ) ) ? spanType : spanType + 'Disabled';
                    messageTypes[ spanType ] = oneActiveFilter || ( spanType == type );
                }
            }
            else {
                messageTypes[ type ] = ! messageTypes[ type ];
                span.className = ( messageTypes[ type ] ) ? type : type + 'Disabled';
            }

            //build outputList's class from messageTypes object
            var disabledTypes = [];
            for ( type in messageTypes ) {
                if ( ! messageTypes[ type ] ) disabledTypes.push( type );
            }
            disabledTypes.push( '' );
            outputList.className = disabledTypes.join( 'Hidden ' );

            scrollToBottom();
        }
    }

    function clickVis( evt ) {
        if ( !evt ) evt = window.event;
        var el = ( evt.target ) ? evt.target : evt.srcElement;

        state.load = el.checked;
        setState();
    }


    function scrollToBottom() { //scroll list output to the bottom
        outputList.scrollTop = outputList.scrollHeight;
    }

    function isVisible() { //determine the visibility
        return ( bbird.style.display == 'block' );
    }

    function hide() {
        bbird.style.display = 'none';
    }

    function show() {
        var body = document.getElementsByTagName( 'BODY' )[ 0 ];
        body.removeChild( bbird );
        body.appendChild( bbird );
        bbird.style.display = 'block';
    }

    //sets the position
    function reposition( position ) {
        if ( position === undefined || position == null ) {
            position = ( state && state.pos === null ) ? 1 : ( state.pos + 1 ) % 4; //set to initial position ('topRight') or move to next position
        }

        switch ( position ) {
            case 0:
                classes[ 0 ] = 'bbTopLeft';
                break;
            case 1:
                classes[ 0 ] = 'bbTopRight';
                break;
            case 2:
                classes[ 0 ] = 'bbBottomLeft';
                break;
            case 3:
                classes[ 0 ] = 'bbBottomRight';
                break;
        }
        state.pos = position;
        setState();
    }

    function resize( size ) {
        if ( size === undefined || size === null ) {
            size = ( state && state.size == null ) ? 0 : ( state.size + 1 ) % 2;
        }

        classes[ 1 ] = ( size === 0 ) ? 'bbSmall' : 'bbLarge'

        var span = document.getElementById( IDs.size );
        span.title = ( size === 1 ) ? 'small' : 'large';
        span.className = span.title;

        state.size = size;
        setState();
        scrollToBottom();
    }

    function setState() {
        var props = [];
        for ( entry in state ) {
            var value = ( state[ entry ] && state[ entry ].constructor === String ) ? '"' + state[ entry ] + '"' : state[ entry ];
            props.push( entry + ':' + value );
        }
        props = props.join( ',' );

        var expiration = new Date();
        expiration.setDate( expiration.getDate() + 14 );
        document.cookie = [ 'blackbird={', props, '}; expires=', expiration.toUTCString() ,';' ].join( '' );

        var newClass = [];
        for ( word in classes ) {
            newClass.push( classes[ word ] );
        }
        bbird.className = newClass.join( ' ' );
    }

    function getState() {
        var re = new RegExp( /blackbird=({[^;]+})(;|\b|$)/ );
        var match = re.exec( document.cookie );
        return ( match && match[ 1 ] ) ? eval( '(' + match[ 1 ] + ')' ) : {
            pos:null,
            size:null,
            load:null
        };
    }

    //event handler for 'keyup' event for window
    function readKey( evt ) {
        if ( !evt ) evt = window.event;
        var code = 113; //F2 key

        if ( evt && evt.keyCode == code ) {

            var visible = isVisible();

            if ( visible && evt.shiftKey && evt.altKey ) clear();
            else if	 (visible && evt.shiftKey ) reposition();
            else if ( !evt.shiftKey && !evt.altKey ) {
                ( visible ) ? hide() : show();
            }
        }
    }

    //event management ( thanks John Resig )
    function addEvent( obj, type, fn ) {
        var obj = ( obj.constructor === String ) ? document.getElementById( obj ) : obj;
        if ( obj.attachEvent ) {
            obj[ 'e' + type + fn ] = fn;
            obj[ type + fn ] = function(){
                obj[ 'e' + type + fn ]( window.event )
            };
            obj.attachEvent( 'on' + type, obj[ type + fn ] );
        } else obj.addEventListener( type, fn, false );
    }
    function removeEvent( obj, type, fn ) {
        var obj = ( obj.constructor === String ) ? document.getElementById( obj ) : obj;
        if ( obj.detachEvent ) {
            obj.detachEvent( 'on' + type, obj[ type + fn ] );
            obj[ type + fn ] = null;
        } else obj.removeEventListener( type, fn, false );
    }

    window[ NAMESPACE ] = {
        toggle:
        function() {
            ( isVisible() ) ? hide() : show();
        },
        resize:
        function() {
            resize();
        },
        clear:
        function() {
            clear();
        },
        move:
        function() {
            reposition();
        },
        debug:
        function( msg ) {
            addMessage( 'debug', msg );
        },
        warn:
        function( msg ) {
            addMessage( 'warn', msg );
        },
        info:
        function( msg ) {
            addMessage( 'info', msg );
        },
        error:
        function( msg ) {
            addMessage( 'error', msg );
        },
        profile:
        function( label ) {
            var currentTime = new Date(); //record the current time when profile() is executed

            if ( label == undefined || label == '' ) {
                addMessage( 'error', '<b>ERROR:</b> Please specify a label for your profile statement' );
            }
            else if ( profiler[ label ] ) {
                addMessage( 'profile', [ label, ': ', currentTime - profiler[ label ],	'ms' ].join( '' ) );
                delete profiler[ label ];
            }
            else {
                profiler[ label ] = currentTime;
                addMessage( 'profile', label );
            }
            return currentTime;
        }
    }

    addEvent( window, 'load',
        /* initialize Blackbird when the page loads */
        function() {
            var body = document.getElementsByTagName( 'BODY' )[ 0 ];
            bbird = body.appendChild( generateMarkup() );
            outputList = bbird.getElementsByTagName( 'OL' )[ 0 ];

            backgroundImage();

            //add events
            addEvent( IDs.checkbox, 'click', clickVis );
            addEvent( IDs.filters, 'click', clickFilter );
            addEvent( IDs.controls, 'click', clickControl );
            addEvent( document, 'keyup', readKey);

            resize( state.size );
            reposition( state.pos );
            if ( state.load ) {
                show();
                document.getElementById( IDs.checkbox ).checked = true;
            }

            scrollToBottom();

            window[ NAMESPACE ].init = function() {
                show();
                window[ NAMESPACE ].error( [ '<b>', NAMESPACE, '</b> can only be initialized once' ] );
            }

            addEvent( window, 'unload', function() {
                removeEvent( IDs.checkbox, 'click', clickVis );
                removeEvent( IDs.filters, 'click', clickFilter );
                removeEvent( IDs.controls, 'click', clickControl );
                removeEvent( document, 'keyup', readKey );
            });
        });
})();
