
/**
 * compares text-content of expected to actual result by using eval.
 * if result is incorrect, adds the "incorrect" className.
 */
function assertEquals( baseId ) {
	var src = document.getElementById( baseId + "-source" );
	var actual = eval( src.textContent || src.innerText ).toString();
	var expectedEl = document.getElementById( baseId + "-expected" );
	var expected = expectedEl.textContent || expectedEl.innerText;
	var resultEl = document.getElementById( baseId + "-result" );
	resultEl.innerHTML = "&nbsp;"
	resultEl.firstChild.data = actual;
// Because IE does not properly preserve white space, Our test must check the text 
// w/ nbsp removed.
	var actualNoNbsp = actual.replace(/(?:\u00a0)|[\s\s]+/g,"");
	var expectedNoNbsp = expected.replace(/(?:\u00a0)|[\s\s]+/g,"");
    var actualTrimmed = String.trim( actualNoNbsp );
    var expectedTrimmed = String.trim( expectedNoNbsp );
	resultEl.className = (expectedNoNbsp == actualTrimmed ? "correct" : "incorrect");
}

/* Adapter for IE vs textContent */
function setTextContent(el, s) {
    if(typeof el.textContent == "string")
        el.textContent = s;
    else 
        el.innerText = s;
}

/** Return type is void. */
function evalTextContent(src) {
    var el = src;
    if(typeof src == "string")
    	var el = document.getElementById(src);
	new Function(el.textContent || el.innerText)();
}

Template = new function() {
    var instances = {};

    // Match ${ anything }
    var replacePattern = new RegExp("\\$\\{\\s?(.*?)\s?\\}", "g");

// Leaves me with a clean, refreshing feeling.
    var __scope__ = []; 

    /** Replaces the template's HTML with eval'd code. */
    function replaceFn(m, regexp1 ) {
         if(__scope__) {
             var vars = [];
             for(var i = 0; i < __scope__.length; i+=2) {
                 vars.push(__scope__[i]+"=__scope__["+(i+1)+"]");
             }
             eval( "var " + vars.join(",") + ";" );
         }
         return eval(regexp1)||""; // Return may be void.
    };

    this.getById = function getTemplateById( id ) {
        if( instances.hasOwnProperty( id ) ) 
            return instances[ id ];
        var el = document.getElementById( id );
        instances[ id ] = ("innerText" in el) ? el.innerText : el.textContent;
        return instances[ id ];
    };

    this.evalTemplate = function evalTemplate(id, scope) {   
        var template = Template.getById( id );
        var el = document.getElementById( id );
        __scope__ = scope;
        var newHTML = template.replace( replacePattern, replaceFn );
        setInnerHTML( el, newHTML );
    };

    this.reset = function resetTemplate( id ) {
        var el = document.getElementById( id );
        var newHTML = Template.getById( id );
        setInnerHTML( el, newHTML );
    };

    /** This method will work with PRE but not TD elements
    */
    var isIE = /*@cc_on!@*/false;

    function setInnerHTML( el, s ) {
        if(typeof el["innerHTML"] != "string" ) {
            throw new TypeError("setInnerHTML: first argument does not support innerHTML. Is it an element?");
        }

        if( isIE && /\n/.test(s) ) {
            var innerHTML = new RegExp(">([^<]*)<");
            var outerHTML = el.outerHTML;
            var oldContent = outerHTML.match(innerHTML)[1];
            el.outerHTML = outerHTML.replace( oldContent, s );
        }
        else {
            el.innerHTML = s;
        }
    }
};

function toggleAbsPos(id) {
    var st = document.getElementById(id).style;
    st.position = st.position == "" ? "absolute" : "";
}

/** For safari 2's missing Object.prototype.propertyIsEnumerable
 */
function isPropertyEnumerable(o, p) {
    if( Object.prototype.propertyIsEnumerable ) {
        return Object.prototype.propertyIsEnumerable.call( o, p );
    }
    
    if(o.hasOwnProperty(p)) {
        for( var prop in o ) {
            if(prop == p) return true;
        }
    }
    return false;
}

/**
 * A fix for every script engine's (except Safari 3 JavaScriptCore) broken implementation of 
 * Number.prototype.toFixed
 * Fix for rounding error in JScript.
 * Fix for rounding error in JavaScript
 * Range for fractionDigits expanded to {-20...100}
 */
function fixedToFixed(fractionDigits) {
    var x = Number(this);
    if( isNaN(x) ) {
        return "NaN";
    }
    var f = parseInt(fractionDigits, 10) || 0;
    if( f < -20 || f > 100 ) { 
        throw new RangeError("Precision of " + f + " fractional digits is out of range");
    }
    var s = "";
    if(x <= 0) {
        s = "-";
        x = -x;
    }
    if( x >= Math.pow(10, 21) ) {
        return s + x.toString();
    }
    var m;
// 10. Let n be an integer for which the exact mathematical value of n Ö 10^f - x is 
// as close to zero as possible. If there are two such n, pick the larger n.
    var fTo10 = Math.pow(10, f);
    
    var n = Math.floor(x * fTo10 );
    
    if (Math.abs(n / fTo10 - x) >= Math.abs((n + 1) / fTo10 - x)) {
        ++n;
    }
        
    if( n == 0 ) {
        m = "0";
  	}
    else {
        // let m be the string consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
        m = n.toString();
    }
    if( f == 0 ) {
        return s + m;
    }
    var k = m.length;

    // build up leading 0's.
    if(k <= f ) {
// 15. Let z be the string consisting of f+ 1- k occurrences of the character '0'.
        var z = Math.pow(10, f+1-k).toString().substring(1); 
        m = z + m;
        k = f+1;
    }
// put the dot in.
    if(f > 0) {
        var a = m.substring(0, k-f);
        var b = m.substring(k-f);
        m = a + "." + b;
    }
    else if( f < 0 ) {
// Improvise: Trailing 0's for negative fractionDigits.
        m += Math.pow(10, -f+1-k).toString().substring(1); 
    }
    return s + m; 
};
String.trim = function( s ) {
    return s.replace(/^\s+|\s+$/g,"");
};

// "\r\n" for IE, "\n" for everyone else.
String.nl = /*@cc_on!@*/"\n"||"\r\n";
