/*
 * EnhanceJS version 1.1 - Test-Driven Progressive Enhancement
 * http://enhancejs.googlecode.com/
 * Copyright (c) 2010 Filament Group, Inc, authors.txt
 * Licensed under MIT (license.txt)
*/
(function(win, doc, undefined) {
var settings, body, fakeBody, windowLoaded, head, 
	docElem = doc.documentElement,
	testPass = false,
	mediaCookieA, mediaCookieB, 
	toggledMedia = [];
	
if(doc.getElementsByTagName){ head = doc.getElementsByTagName('head')[0] || docElem; }
else{ head = docElem; }

win.enhance = function(options) {
    options  = options || {};
    settings = {};
    // mixin settings
    for (var name in enhance.defaultSettings) {
        var option = options[name];
        settings[name] = option !== undefined ? option : enhance.defaultSettings[name];
    }
    // mixin additional tests
    for (var test in options.addTests) {
        settings.tests[test] = options.addTests[test];
    }
    //add testName class immediately for FOUC prevention, remove later on fail
    if (docElem.className.indexOf(settings.testName) === -1) {
        docElem.className += ' ' + settings.testName;
    }
    //cookie names for toggled media types
    mediaCookieA = settings.testName + '-toggledmediaA';
	mediaCookieB = settings.testName + '-toggledmediaB';
	toggledMedia = [readCookie(mediaCookieA), readCookie(mediaCookieB)];
    //fallback for removing testName class
    setTimeout(function(){ if(!testPass){ removeHTMLClass(); } }, 3000);

    runTests();
    
    applyDocReadyHack();
    
    windowLoad(function() { windowLoaded = true; });
};

enhance.defaultTests = {
    getById: function() {
        return !!doc.getElementById;
    },
    getByTagName: function() {
        return !!doc.getElementsByTagName;
    },
    createEl: function() {
        return !!doc.createElement;
    },
    boxmodel: function() {
        var newDiv = doc.createElement('div');
        newDiv.style.cssText = 'width: 1px; padding: 1px;';
        body.appendChild(newDiv);
        var divWidth = newDiv.offsetWidth;
        body.removeChild(newDiv);
        return divWidth === 3;
    },
    position: function() {
        var newDiv = doc.createElement('div');
        newDiv.style.cssText = 'position: absolute; left: 10px;';
        body.appendChild(newDiv);
        var divLeft = newDiv.offsetLeft;
        body.removeChild(newDiv);
        return divLeft === 10;
    },
    floatClear: function() {
        var pass = false,
            newDiv = doc.createElement('div'),
            style = 'style="width: 5px; height: 5px; float: left;"';
        newDiv.innerHTML = '<div ' + style + '></div><div ' + style + '></div>';
        body.appendChild(newDiv);
        var childNodes = newDiv.childNodes,
            topA = childNodes[0].offsetTop,
            divB = childNodes[1],
            topB = divB.offsetTop;
        if (topA === topB) {
            divB.style.clear = 'left';
            topB = divB.offsetTop;
            if (topA !== topB) {
                pass = true;
            }
        }
        body.removeChild(newDiv);
        return pass;
    },
    heightOverflow: function() {
        var newDiv = doc.createElement('div');
        newDiv.innerHTML = '<div style="height: 10px;"></div>';
        newDiv.style.cssText = 'overflow: hidden; height: 0;';
        body.appendChild(newDiv);
        var divHeight = newDiv.offsetHeight;
        body.removeChild(newDiv);
        return divHeight === 0;
    },
    ajax: function() {
        //factory test borrowed from quirksmode.org
        var xmlhttp = false, index = -1, factory,
            XMLHttpFactories = [
                function() { return new XMLHttpRequest() },
                function() { return new ActiveXObject("Msxml2.XMLHTTP") },
                function() { return new ActiveXObject("Msxml3.XMLHTTP") },
                function() { return new ActiveXObject("Microsoft.XMLHTTP") }
            ];
        while ((factory = XMLHttpFactories[++index])) {
            try { xmlhttp = factory(); }
            catch (e) { continue; }
            break;
        }
        return !!xmlhttp;
    },
    resize: function() {
        return win.onresize != false;
    },
    print: function() {
        return !!win.print;
    }
};

enhance.defaultSettings = {
    testName: 'enhanced',
    loadScripts: [],
    loadStyles: [],
    queueLoading: true,
    appendToggleLink: true,
    forcePassText: 'View high-bandwidth version',
    forceFailText: 'View low-bandwidth version',
    tests: enhance.defaultTests,
    addTests: {},
    alertOnFailure: false,
    onPass: function(){},
    onFail: function(){},
    onLoadError: addIncompleteClass,
    onScriptsLoaded: function(){}
};

function cookiesSupported(){
	return !!doc.cookie;
}
enhance.cookiesSupported = cookiesSupported();

function forceFail() {
    createCookie(settings.testName, 'fail');
    win.location.reload();
}
if(enhance.cookiesSupported){ enhance.forceFail = forceFail; }

function forcePass() {
    createCookie(settings.testName, 'pass');
    win.location.reload();
}
if(enhance.cookiesSupported){ enhance.forcePass = forcePass; }

function reTest() {
    eraseCookie(settings.testName);
    win.location.reload();
}
if(enhance.cookiesSupported){ enhance.reTest = reTest; }

function addFakeBody(){
	fakeBody = doc.createElement('body'); 
	docElem.insertBefore(fakeBody, docElem.firstChild);
	body = fakeBody;
}
function removeFakeBody(){
	docElem.removeChild(fakeBody);
	body = doc.body;
}

function runTests() {
    var result = readCookie(settings.testName);
    //check for cookies from a previous test
    if (result) {
        if (result === 'pass') {
            enhancePage();
            settings.onPass();
        } else {
            settings.onFail();
            removeHTMLClass();
        }
        
        // append toggle link
        if (settings.appendToggleLink) {
            windowLoad(function() { 
                appendToggleLinks(result);
            });
        }
    }
    //no cookies - run tests
    else {
        var pass = true;
        addFakeBody();
        for (var name in settings.tests) {
            pass = settings.tests[name]();
            if (!pass) {
                if (settings.alertOnFailure) {
                    alert(name + ' failed');
                }
                break;
            }
        }
        removeFakeBody();
        result = pass ? 'pass' : 'fail';
        createCookie(settings.testName, result);
        if (pass) {
            enhancePage();
            settings.onPass();
        }
        else {
            settings.onFail();
            removeHTMLClass();
        }
                    
        if (settings.appendToggleLink) {
            windowLoad(function() { 
                appendToggleLinks(result);
            });
        }
    }
}

function windowLoad(callback) {
    if (windowLoaded) {
        callback();
    } else {
        var oldonload = win.onload
        win.onload = function() {
            if (oldonload) { oldonload(); }
            callback();
        }
    }
}

function appendToggleLinks(result) {
    if (!settings.appendToggleLink || !enhance.cookiesSupported) { return; }
    if (result) {
        var a = doc.createElement('a');
        a.href = "#";
        a.className = settings.testName + '_toggleResult';
        a.innerHTML = result === 'pass' ? settings.forceFailText : settings.forcePassText;
        a.onclick   = result === 'pass' ? enhance.forceFail : enhance.forcePass;
        doc.getElementsByTagName('body')[0].appendChild(a);
    }
}

function removeHTMLClass(){
	docElem.className = docElem.className.replace(settings.testName,'');
}

function enhancePage() {
	testPass = true;
    if (settings.loadStyles.length) {
    	appendStyles();
    }
    if (settings.loadScripts.length) {
    	appendScripts();        
    }
    else{
    	settings.onScriptsLoaded();
    }
}

//media toggling methods and storage
function toggleMedia(mediaA,mediaB){
	if(readCookie(mediaCookieA) && readCookie(mediaCookieB)){
		eraseCookie(mediaCookieA);
		eraseCookie(mediaCookieB);
	}
	else{
		createCookie(mediaCookieA, mediaA);
		createCookie(mediaCookieB, mediaB);
	}
	win.location.reload();
}
enhance.toggleMedia = toggleMedia;

//return a toggled media type/query
function mediaSwitch(q){
	if(toggledMedia.length == 2){
		if(q == toggledMedia[0]){ q = toggledMedia[1]; }
		else if(q == toggledMedia[1]){ q = toggledMedia[0]; }
	}
	return q;
}

function addIncompleteClass (){
	var errorClass = settings.testName + '-incomplete';
	if (docElem.className.indexOf(errorClass) === -1) {
        docElem.className += ' ' + errorClass;
    }
}

function appendStyles() {
    var index = -1,
        item;
    while ((item = settings.loadStyles[++index])) {
        var link  = doc.createElement('link');
        link.type = 'text/css';
        link.rel  = 'stylesheet';
        link.onerror = settings.onLoadError;
        
        if (typeof item === 'string') {
            link.href = item;
            head.appendChild(link);
        }
        else {
        	if(item['media']){ item['media'] = mediaSwitch(item['media']); }
        	if(item['excludemedia']){ item['excludemedia'] = mediaSwitch(item['excludemedia']); }
        	
            for (var attr in item) {
                if (attr !== 'iecondition' && attr !== 'excludemedia') {
                    link.setAttribute(attr, item[attr]);
                }    
            }
            var applies = true;
            if(item['media'] && item['media'] !== 'print' && item['media'] !== 'projection' && item['media'] !== 'speech' && item['media'] !== 'aural' && item['media'] !== 'braille'){
	        	applies = mediaquery(item['media']);
	        }
            if(item['excludemedia']){
            	applies = !mediaquery(item['excludemedia']);
	        }
	        if (item['iecondition']) {
                applies = isIE(item['iecondition']);
            }
	        if(applies){ 
	        	head.appendChild(link); 
	        }
        }
    }
}

var isIE = (function() {
	var cache = {},
		b;
    return function(condition) {	
    	if(/*@cc_on!@*/true){return false;}
		var cc = 'IE';
		if(condition){ 
			if(condition !== 'all'){ //deprecated support for 'all' keyword
				if( !isNaN(parseFloat(condition)) ){ 
					cc += ' ' + condition; //deprecated support for straight version #
				}
				else {
					cc = condition; //recommended (conditional comment syntax)
				}
			}
		}
		if (cache[cc] === undefined) {
			b = b || doc.createElement('B');
			b.innerHTML = '<!--[if '+ cc +']><b></b><![endif]-->';
			cache[cc] = !!b.getElementsByTagName('b').length;
		}
		return cache[cc];
	}	
})();

//test whether a media query applies
var mediaquery = (function(){
	var cache = {},
		testDiv = doc.createElement('div');
	
	testDiv.setAttribute('id','ejs-qtest');
	return function(q){
		//check if any media types should be toggled
		if (cache[q] === undefined) {
			addFakeBody();
			var styleBlock = doc.createElement('style');
			styleBlock.type = "text/css";
			/*set inner css text. credit: http://www.phpied.com/dynamic-script-and-style-elements-in-ie/*/
			var cssrule = '@media '+q+' { #ejs-qtest { position: absolute; width: 10px; } }';
			if (styleBlock.styleSheet){ styleBlock.styleSheet.cssText = cssrule; }
			else { styleBlock.appendChild(doc.createTextNode(cssrule)); }     
			head.appendChild(styleBlock);
			body.appendChild(testDiv);
			var divWidth = testDiv.offsetWidth;
			body.removeChild(testDiv);
			head.removeChild(styleBlock);
			removeFakeBody();
			cache[q] = (divWidth == 10);
		}
		return cache[q];
	}
})();
enhance.mediaquery = mediaquery;

function appendScripts(){
	settings.queueLoading ? appendScriptsSync() : appendScriptsAsync();
}

function appendScriptsSync() {
    var queue = [].concat(settings.loadScripts);
    function next() {
        if (queue.length === 0) {
            return false;
        }
        var item    = queue.shift(),
            script = createScriptTag(item),
            done   = false;
        if(script){
	        script.onload = script.onreadystatechange = function() {
	            if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
	                done = true;
	                if(next() === false){
	                	settings.onScriptsLoaded();
	                }
	                this.onload = this.onreadystatechange = null;
	            }
	        }
	        head.insertBefore(script, head.firstChild);
        }
        else{
        	return next();
        }
    }
    next();
}

function appendScriptsAsync() {
    var index = -1,
        item;
    while ((item = settings.loadScripts[++index])) {
    	var script = createScriptTag(item);
        if(script){
        	head.insertBefore(script, head.firstChild);
        }	
    }
    settings.onScriptsLoaded();
}

function createScriptTag(item) {
    var script  = doc.createElement('script');
    script.type = 'text/javascript';
    script.onerror = settings.onLoadError;
    if (typeof item === 'string') {
        script.src  = item;
        return script;
    }
    else {
    	if(item['media']){ item['media'] = mediaSwitch(item['media']); }
        if(item['excludemedia']){ item['excludemedia'] = mediaSwitch(item['excludemedia']); }
        	
        for (var attr in item) {
            if (attr !== 'iecondition' && attr !== 'media' && attr !== 'excludemedia') {
            	script.setAttribute(attr, item[attr]);
            }    
        }
        var applies = true;
        if(item['media']){
        	applies = mediaquery(item['media']);
        }
        if(item['excludemedia']){
        	applies = !mediaquery(item['excludemedia']);
        }
        if (item['iecondition']) {
                applies = isIE(item['iecondition']);
        }
        return applies ? script : false;
    }
}

/* cookie functions from quirksmode.org (modified) */
function createCookie(name, value, days) {
    days = days || 90;
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
    doc.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = doc.cookie.split(';');
    for (var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
    return null;
}
	
function eraseCookie(name) {
    createCookie(name,"",-1);
}

function applyDocReadyHack() {
    // via http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
    // verify that document.readyState is undefined
    // verify that document.addEventListener is there
    // these two conditions are basically telling us
    // we are using Firefox < 3.6
    if (doc.readyState == null && doc.addEventListener){
        // on DOMContentLoaded event, supported since ages
        doc.addEventListener("DOMContentLoaded", function DOMContentLoaded(){
            // remove the listener itself
            doc.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
            // assign readyState as complete
            doc.readyState = "complete";
        }, false);
        // set readyState = loading or interactive
        // it does not really matter for this purpose
        doc.readyState = "loading";
	}
}
})
(window, document);
