/*
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( [ ''].join( '' ) );
}
var newNode = document.createElement( 'DIV' );
newNode.id = IDs.blackbird;
newNode.style.display = 'none';
newNode.innerHTML = [
'
',
'',
'
',
'
', cache.join( '' ), '
',
'
',
'
',
''
].join( '' );
return newNode;
}
function backgroundImage() { //(IE6 only) change 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
content = ( content.constructor == Array ) ? content.join( '' ) : content;
if ( outputList ) {
var newMsg = document.createElement( 'LI' );
newMsg.className = 'bb-' + type;
newMsg.title = type;
newMsg.innerHTML = [ '', content ].join( '' );
outputList.appendChild( newMsg );
scrollToBottom();
} else {
cache.push( [ '', content, '' ].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 ) ) ? 'bb-' + spanType : 'bb-' + spanType + ' bb-disabled';
messageTypes[ spanType ] = oneActiveFilter || ( spanType == type );
}
}
else {
messageTypes[ type ] = ! messageTypes[ type ];
span.className = ( messageTypes[ type ] ) ? 'bb-' + type : 'bb-' + type + ' bb-disabled';
}
//build outputList's class from messageTypes object
var disabledTypes = [];
for ( type in messageTypes ) {
if ( ! messageTypes[ type ] ) disabledTypes.push( 'bb-' + 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;
var checkbox = ( el.tagName == 'SPAN' ) ? el : el.getElementsByTagName( 'SPAN' )[ 0 ];
checkbox.className = ( checkbox.className == '' ) ? 'checked' : '';
state.load = ( checkbox.className == 'checked' ) ? true : false;
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 ] = 'bb-top-left'; break;
case 1: classes[ 0 ] = 'bb-top-right'; break;
case 2: classes[ 0 ] = 'bb-bottom-left'; break;
case 3: classes[ 0 ] = 'bb-bottom-right'; 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 ) ? 'bb-small' : 'bb-large'
var span = document.getElementById( IDs.size );
span.title = ( size === 1 ) ? 'small' : 'large';
span.className = ( size === 1) ? 'bb-contract' : 'bb-expand';
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( visible ) {
if ( visible === true || visible === false ) {
( visible ) ? show() : hide()
} else if ( isVisible() ) {
hide();
} else {
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', 'ERROR: 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', function() { /* initialize Blackbird when the page loads */
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 ).getElementsByTagName( 'SPAN')[ 0 ].className = 'checked';
}
scrollToBottom();
addEvent( window, 'unload', function() {
removeEvent( IDs.checkbox, 'click', clickVis );
removeEvent( IDs.filters, 'click', clickFilter );
removeEvent( IDs.controls, 'click', clickControl );
removeEvent( document, 'keyup', readKey );
});
});
window.blackbird = {
namespace:
function( newNS ) {
if ( newNS && newNS.constructor === String ) {
window[ newNS ] = {};
for ( method in window[ NAMESPACE ] ) {
window[ newNS ][ method ] = window[ NAMESPACE ][ method ];
}
delete window[ NAMESPACE ];
NAMESPACE = newNS;
}
}
}
})();