/* 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 prevtracetime; 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 = [ '
', '
', '
', spans.join(''), '
', '
', '
', '
', '', '', '', '
', '
', '
', '
', '
', '
    ', 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 = 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)) ? 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]); } if (bbird != null) { 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); }, trace: function(msg) { msg += new Date().toLocaleString(); addMessage('debug', msg); prevtracetime = new Date(); }, diff: function(msg) { if (prevtracetime == null) { addMessage("debug", "上次记录时间不存在"); } else { var now = new Date(); var diff = now.getTime() - prevtracetime.getTime(); if (msg == undefined || msg == "") { addMessage('debug', '距离上次记录过去了' + diff + '毫秒'); } else { addMessage('debug', msg + ":" + diff); } prevtracetime = new Date(); } }, 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', /* 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(['', NAMESPACE, ' 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); }); }); })();