/*
 * 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)
 
 The MIT License

Copyright (c) 2007-2010 Filament Group, Inc

This software consists of contributions made by many
individuals (authors.txt) For exact contribution history, 
see the revision history and logs, available
at http://enhancejs.googlecode.com/svn/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

The end-user documentation included with the redistribution, if any, must 
include the following acknowledgment: "This product includes software 
developed by Filament Group, Inc (http://www.filamentgroup.com/) and its 
contributors", in the same place and form as other third-party acknowledgments. 
Alternately, this acknowledgment may appear in the software itself, in the same 
form and location as other such third-party acknowledgments.

EnhanceJS is developed and maintained by Filament Group, Inc (filamentgroup.com)
with contributions of code and ideas by the following individuals: 

Brandon Aaron
Scott Jehl
Todd Parker
Patty Toland
Maggie Costello Wachs

*/
(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";
			head.appendChild(styleBlock);
			/*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)); }     
			body.appendChild(testDiv);
			var divWidth = testDiv.offsetWidth;
			body.removeChild(testDiv);
			head.removeChild(styleBlock);
			removeFakeBody();
			cache[q] = (divWidth == 10);
		}
		return cache[q];
	}
})();
enhance.query = 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);
