/*******
* Credits: Some objects and functions are based on the Mootools library. www.mootools.net
******/
/**
* NC namespace
*/
if (NC == undefined) var NC = window.NC || {};

/**
* Namespace generation
*/
NC.namespace = function(ns) {
    if (!ns || ns.length == 0) {
        return null;
    }
    var n = ns.split('.');
    var nsobj = NC;
    for (var i =(n[0] == 'NC') ? 1 : 0; i < n.length; i ++) {
        if (nsobj[n[i]] == undefined) nsobj[n[i]] = nsobj[n[i]] || {}
        nsobj = nsobj[n[i]];
    }
    return nsobj
}


/**
* Find out what an object is
*/
NC.type = function(obj) {
    if (!obj) return false;
    var result = false;
    if (obj.nodeType) {
        if (obj.nodeType == 1) result = 'elementNode';
        else if (obj.nodeType == 2) result = 'attributeNode';
        else if (obj.nodeType == 3 && !/\S/.test(obj.nodeValue)) result = 'textNode';
    }
    else if (obj instanceof Function) result = 'function';
    else if (obj instanceof Array) result = 'array';
    else if (typeof obj == "object") result = 'object';
    else if (typeof obj == "string") result = 'string';
    else if (typeof obj == "number" && isFinite(obj)) result = 'number';
    return result;
}
/*
Property: rgbToHex
Converts an RGB value to hexidecimal.

Arguments:
r - integer. the "r" value of "rgb"
g - integer. the "g" value of "rgb"
b - integer. the "b" value of "rgb"
a - integer. the "a" (alpha) value of rgba

Returns:
hex string or array. returns "transparent" if a is 0,
*/
NC.rgbToHex = function(r,g,b,a){
    if (a && a == 0) return 'transparent';
    r = r.toString(16); if (r.length == 1) r = '0' + r;
    g = g.toString(16); if (g.length == 1) g = '0' + g;
    b = b.toString(16); if (b.length == 1) b = '0' + b;
    return "#" + r + g + b;
}
/**
* Clears a timeout or interval
*/
NC.clearTimer = function(timer) {
    clearTimeout(timer);
    clearInterval(timer);
    return null;
}
/**
* Short function for getting an element
*/
NC.$ = function(el, env) {
    //The environment to search in. Defaults to document, but could be a dom element
    var env = env || document;
    var type = NC.type(el);
    if (type == 'string') {
        if (env.getElementById) el = env.getElementById(el);
        else if (env.all) el = env.all(el);
        else el = null;
    }
    if (NC.type(el) == 'elementNode') {
        if (!el.extend) {
            el.extend = Object.extend;
            el.extend(NC.Element.prototype);
        }
        return el;
    }
    else return false;

}
/**
* Short function for getting elements by class name
* Based on function by Dustin Diaz http://www.dustindiaz.com/top-ten-javascript/
*/
NC.$$ = function(className, env, tag) {
    var classElements = new Array();
    //The environment to search in. Defaults to document, but could be a dom element
    var env = env || document;
    //Specific tag to work with
    tag = tag || '*';
    var tagType = NC.type(tag);
    if (tagType == 'string') {
        var els = env.getElementsByTagName(tag);
    }
    else if (tagType == 'array') {
        els = new Array();
        for (var x = 0; x < tag.length; x ++) {
            els.push(env.getElememtsByTagName(tag[x]));
        }
    }
    var pattern = new RegExp('(^|\\s)' + className + '(\\s|$)');
    for (i = 0, j = 0; i < els.length; i++) {
        if (pattern.test(els[i].className)) {
            classElements[j] = els[i];
            j++;
        }
    }
    return classElements;
}
NC.getElementsByClassName = function(className, env, tag) {
    return NC.$$(className, env, tag);
}
/**
* Short function for the Array.copy() function
*/
NC.$AC = function(array) {
    return Array.prototype.copy.call(array);
}
/**
* Short function for getElementsByTagName()
*/
NC.$T = function(tag, env) {
    tag = tag || '*';
    env = env || document;
    var elements = null;
    if (document.getElementsByTagName) {
        elements = env.getElementsByTagName(tag);
        if (tag == '*' && (!elements || !elements.length)) elements = env.all; //IE 5 bug
    }
    return elements || new Array();
}

/*******************************************************
* Base object for creating new classes
* Allows class to be extended.
* Based on MooTools http://mootools.net/
********************************************************/
NC.Class = function(properties) {
    var thisClass = function() {
        if (arguments[0] !== 'noinit' && this.init) return this.init.apply(this, arguments);
    };
    thisClass.prototype = properties;
    thisClass.implement = this.implement;
    thisClass.extend = this.extend;
    return thisClass;
}
/**
* Returns an empty class
*/
NC.Class.empty = function() {}

/**
* Same as new NC.Class
*/
NC.Class.create = function(properties) {
    return new NC.Class(properties);
}

NC.Class.prototype = {
    /**
    * Returns a copy of the class extended with the new properties
    */
    extend: function(properties) {
        var newPrototype = new this('noinit');
        for (var property in properties) {
            //			newPrototype[property] = properties[property];
            //Get the property from the class being extended
            var previous = newPrototype[property];
            //Get the property from the new class
            var current = properties[property];
            if (previous && previous != current) current = previous.parentize(current) || current;
            newPrototype[property] = current;
        }
        return new NC.Class(newPrototype);
    },

    /**
    * Adds the properties to the object's prototype
    */
    implement: function(properties) {
        for (var property in properties) {
            this.prototype[property] = properties[property];
        }
    }
};

/***********
* Object extension
************/
//Copies all of the properties from the second object to the first object
Object.extend = function() {
    var args;
    if (arguments[1]) {
        args = [arguments[0], arguments[1]];
    }
    else {
        args = [this, arguments[0]];
    }
    for (var property in args[1]) {
        //		alert(property + ' = ' + args[1][property]);
        args[0][property] = args[1][property] };
        return args;
}
Array.extend = NC.Class.prototype.implement;
Function.extend = NC.Class.prototype.implement;
String.extend = NC.Class.prototype.implement;
Number.extend = NC.Class.prototype.implement;

Function.extend({
    //Adds the this.parent property to the function.
    //this.parent points to the same function name in the previous
    //function being exteded
    parentize: function(current){
        var previous = this;
        return function(){
            this.parent = previous;
            return current.apply(this, arguments);
        };
    }

});
/******
* Array prototype
******/
//See http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fn , thisOption) {
        if (typeof fn != "function") throw new TypeError();
        for (var i = 0; i < this.length; i++) {
            if (i in this) fn.call(thisOption || this, this[i], i, this);
        }
    };
}

Array.extend({
    each: Array.prototype.forEach,
    test: function(item) {
        for (var i = 0; i < this.length; i ++) {
            if (this[i] == item) return true;
        }
        return false;
    },
    copy: function(){
        var newArray = [];
        for (var i = 0; i < this.length; i++) newArray.push(this[i]);
        return newArray;
    },
    remove: function(item){
        for (var i = 0; i < this.length; i++){
            if (this[i] == item) this.splice(i, 1);
        }
        return this;
    },
    extend: function(newArray){
        for (var i = 0; i < newArray.length; i++) this.push(newArray[i]);
        return this;
    },
    first: function() {
        return this[0];
    },
    last: function() {
        return this[this.length - 1];
    }
});

/***************
* String prototype
***************/
String.extend({
    test: function(regex, params) {
        return this.match(new RegExp(regex, params));
    },
    /**
    * Converts a hyphenated string to camelCase
    * this-string to thisString
    */
    camelCase: function(){
        return this.replace(/-\D/gi, function(match){
            return match.charAt(match.length - 1).toUpperCase();
        });
    },
    /*
    Property: capitalize
    Converts the first letter in each word of a string to Uppercase.

    Example:
    >"i like cookies".capitalize(); //"I Like Cookies"

    Returns:
    the capitalized string
    */
    capitalize: function(){
        return this.toLowerCase().replace(/\b[a-z]/g, function(match){
            return match.toUpperCase();
        });
    },
    /**
    * Trims the leading and trailing spaces off a string.
    */
    trim: function(){
        return this.replace(/^\s*|\s*$/g, '');
    },
    /**
    * trims a string AND removes all the double spaces in a string.
    */
    clean: function(){
        return this.replace(/\s\s/g, ' ').trim();
    },
    /*
    Property: toInt
    parses a string to an integer.

    Returns:
    either an int or "NaN" if the string is not a number.

    Example:
    >var value = "10px".toInt(); // value is 10
    */
    toInt: function(){
        return parseInt(this);
    },
    /**
    * Tests to see if a string is an integer
    */
    isInt: function() {
        if (isNaN(this) || typeof(this) != 'number') return false;
        return true;
    },
    /*
    Property: rgbToHex
    Converts an RGB value to hexidecimal. The string must be in the format of "rgb(255, 255, 255)" or "rgba(255, 255, 255, 1)";

    Arguments:
    array - boolean value, defaults to false. Use true if you want the array ['FF', '33', '00'] as output instead of #FF3300

    Returns:
    hex string or array. returns transparent if the fourth value of rgba in input string is 0,

    Example:
    >"rgb(17,34,51)".rgbToHex(); //"#112233"
    >"rgba(17,34,51,0)".rgbToHex(); //"transparent"
    >"rgb(17,34,51)".rgbToHex(true); //[11,22,33]
    */

    rgbToHex: function(array){
        var rgb = this.test('([\\d]{1,3})', 'g');
        if (rgb[3] == 0) return 'transparent';
        var hex = [];
        for (var i = 0; i < 3; i++){
            var bit = (rgb[i]-0).toString(16);
            hex.push(bit.length == 1 ? '0'+bit : bit);
        }
        var hexText = '#'+hex.join('');
        if (array) return hex;
        else return hexText;
    },

    /*
    Property: hexToRgb
    Converts a hexidecimal color value to RGB. Input string must be the hex color value (with or without the hash). Also accepts triplets ('333');

    Arguments:
    array - boolean value, defaults to false. Use true if you want the array ['255', '255', '255'] as output instead of "rgb(255,255,255)";

    Returns:
    rgb string or array.

    Example:
    >"#112233".hexToRgb(); //"rgb(17,34,51)"
    >"#112233".hexToRgb(true); //[17,34,51]
    */

    hexToRgb: function(array){
        var hex = this.test('^[#]{0,1}([\\w]{1,2})([\\w]{1,2})([\\w]{1,2})$');
        var rgb = [];
        for (var i = 1; i < hex.length; i++){
            if (hex[i].length == 1) hex[i] += hex[i];
            rgb.push(parseInt(hex[i], 16));
        }
        var rgbText = 'rgb('+rgb.join(',')+')';
        if (array) return rgb;
        else return rgbText;
    }
});

/*
Class: Number
contains the internal method toInt.
*/

Number.extend({

    /*
    Property: toInt
    Returns this number; useful because toInt must work on both Strings and Numbers.
    */

    toInt: function(){
        return this;
    },

    /*
    Property: isInt
    Returns true; useful because isInt must work on both Strings and Numbers.
    */
    isInt: function() {
        return true;
    }

});

/**************
* Function prototype
******************/
Function.extend({
    /**
    * Creates a closure with the specified arguments and alters the "this" if the function to be the bind
    * Allows you to call a function with arguments and alter the object that the "this" of the function refers to
    * myFunction.pass([arg1, arg2], myObject);
    */
    pass: function(args, bind){
        var f = this;
        if (NC.type(args) != 'array') args = [args];
        return function(){ return f.apply(bind || f, args); };
    },
    /**
    * Binds a function to an element so that the element is object that the "this" in the function
    * will refer to.
    * Creates closures with "this" altered
    */
    bind: function(bind) {
        var f = this;
        return function() {
            f.apply(bind, arguments);
        };
    },
    /**
    * cross browser method to pass event firer
    * Returns a function with the parameter bind as its "this" and as a pre-passed argument event or window.event, depending on the browser.

    Example:
    >function myFunction(event){
    >	alert(event.clientx) //returns the coordinates of the mouse..
    >};
    >myElement.onclick = myFunction.bindAsEventListener(myElement);
    */
    bindAsEventListener: function(bind){
        var fn = this;
        return function(event){
            fn.call(bind, event || window.event);
            return false;
        };
    },
    /**
    * Delays the execution of a function by the specified number of milliseconds
    */
    delay: function(ms, bind) {
        return setTimeout(this.bind.apply(bind || this), ms);
    },
    /**
    * Sets a function to be called at the specified intervals
    * Arguments:
    * ms - number of milliseconds delay
    * bind - the "this" value of the function to call
    */
    interval: function(ms, bind) {
        return setInterval(this.bind.apply(bind || this), ms);
    }

});
/************************
* Dom element creation
*************************/
NC.Element = new NC.Class({
    init: function() {
        return this.create.apply(this, arguments);
    },

    create: function(el, env) {
        env = env || document;
        var types = ("b|i|p|div|span|strong|em|img|table|tr|td|th|thead|tbody|tfoot|pre|code|h1|h2|h3|h4|h5|h6|ul|ol|li|form|input|textarea|legend|fieldset|select|option|blockquote|iframe|cite|br|hr|dd|dl|dt|address|a|button|abbr|acronym|script|link|style|bdo|ins|del|object|param|col|colgroup|optgroup|caption|label|dfn|kbd|samp|var").split("|");
        if (el && el.length > 0) {
            var i = 0;
            if (types.test(el)) {
                var e = env.createElement(el);
            }
            if (e == undefined) {
                var e = env.createTextNode(el);
                return e;
            }
            return NC.$(e);
        }
        else {
            return null;
        }
    },
    hasClass: function(className){
        return !!this.className.test("\\b"+className+"\\b");
    },
    addClass: function(className){
        if (!this.hasClass(className)) this.className = (this.className+' '+className.trim()).clean();
        return this;
    },
    removeClass: function(className){
        if (this.hasClass(className)) this.className = this.className.replace(className.trim(), '').clean();
        return this;
    },
    setStyle: function(property, value) {
        if (property == 'opacity') return this.setOpacity(parseFloat(value));
        if (property == 'display') return this.setDisplay(value, arguments[2] || '');
        this.style[property.camelCase()] = value;
        return this;
    },
    setStyles: function(styles){
        if (NC.type(styles) == 'object') {
            for (var property in styles) this.setStyle(property, styles[property]);
        }
        else if (NC.type(styles) == 'string') {
            if (window.ActiveXObject) this.cssText = styles;
            else this.setAttribute('style', styles);
        }
        return this;
    },
    /*
    Property: setOpacity
    Sets the opacity of the Element, and sets also visibility == "hidden" if opacity == 0, and visibility = "visible" if opacity == 1.

    Arguments:
    opacity - Accepts numbers from 0 to 1.

    Example:
    >$('myElement').setOpacity(0.5) //make it 50% transparent
    */

    setOpacity: function(opacity){
        if (opacity == 0){
            if(this.style.visibility != "hidden") this.style.visibility = "hidden";
        } else {
            if(this.style.visibility != "visible") this.style.visibility = "visible";
        }
        if (window.ActiveXObject) this.style.filter = "alpha(opacity=" + opacity*100 + ")";
        this.style.opacity = opacity;
        return this;
    },
    /**
    * Sets the display for an element
    * @param    alt    The value to set the display to if the element display does not equal "value"
    */
    setDisplay: function(value, alt) {
        if (value.substring(0, 5) == 'table' && window.ActiveXObject) value = 'block'; //IE doesn't like the table-... display values
        if (!this.style.display || this.style.display != value) this.style.display = value;
        else if (alt) {
            if (alt.substring(0, 5) == 'table' && window.ActiveXObject) alt = 'block'; //IE doesn't like the table-... display values
            this.style.display = alt;
        }
        return this;
    },
    setProperty: function(property, value) {
        switch(property){
            case 'style': this.setStyles(value); break;
            case 'class': this.className = value; break;
            default: this.setAttribute(property, value);
        }
        return this;
    },
    setProperties: function(properties) {
        for (var property in properties) this.setProperty(property, properties[property]);
        return this;
    },
    setHtml: function(html) {
        this.innerHTML = html;
        return this;
    },
    /*
    Property: getStyle
    Returns the style of the Element given the property passed in.

    Arguments:
    property - the css style property you want to retrieve

    Example:
    >$('myElement').getStyle('width'); //returns "400px"
    >//but you can also use
    >$('myElement').getStyle('width').toInt(); //returns "400"

    Returns:
    the style as a string
    */

    getStyle: function(property){
        var proPerty = property.camelCase();
        var style = this.style[proPerty] || false;
        //		alert(property + ' = ' + String(style));
        if (!style) {
            if (document.defaultView) style = document.defaultView.getComputedStyle(this,null).getPropertyValue(property);
            else if (this.currentStyle) style = this.currentStyle[proPerty];
        }
        if (style && ['color', 'backgroundColor', 'borderColor'].test(proPerty) && style.test('rgb')) {
            style = style.rgbToHex();
        }
        return style;
    },
    getProperty: function(property) {
        return this.getAttribute(property);
    },
    getTag: function() {
        return this.tagName.toLowerCase();
    },
    getFirst: function() {
        var el = this.firstChild;
        while (NC.type(el) == 'textNode') el = el.nextSibling;
        return NC.$(el);
    },
    getLast: function() {
        var el = this.lastChild;
//        while (NC.type(el) == 'textNode') el = el.getSibling('previous');
        while (NC.type(el) == 'textNode') el = el['previousSibling'];
        return NC.$(el);
    },
    getNext: function() {
        return this.getSibling('next');
    },
    getPrevious: function() {
        return this.getSibling('previous');
    },
    getSibling: function(type) {
        if (type != 'next' && type != 'previous') return;
        var el = this[type + 'Sibling'];
        while (NC.type(el) == 'textNode') el = el[type + 'Sibling'];
        return NC.$(el);
    },
    add: function(el, location) {
        el = NC.$(el) || new NC.Element(el);
        if (location == 'before') el.parentNode.insertBefore(this, el);
        else if (location == 'after') {
            if (!el.getNext()) el.parentNode.appendChild(this);
            else el.parentNode.insertBefore(this, el.getNext());
        }
        else if (location == 'inside') el.appendChild(this);
        else if (location == 'first') {
            var first = el.getFirst();
            if (first) el.insertBefore(this, first);
            else el.appendChild(this);
        }
        return this;
    },
    addBefore: function(el) {
        return this.add(el, 'before');
    },
    addAfter: function(el) {
        return this.add(el, 'after');
    },
    addInside: function(el) {
        return this.add(el, 'inside');
    },
    addto: function(el) {
        el = el || document.body;
        return this.add(el, 'inside');
    },
    addFirst: function(el) {
        return this.add(el, 'first');
    },
    addText: function(text, location) {
        if (text.nodeType == undefined) text = document.createTextNode(text);
        location = location || 'inside';
        if (location == 'inside') this.appendChild(text);
        else if (location == 'after') {
            if (!this.getNext()) this.parentNode.appendChild(text);
            else this.parentNode.insertBefore(text, this.getNext());
        }
        else if (location == 'before') this.parentNode.insertBefore(text, this);
        if (location == 'before') el.parentNode.insertBefore(this, el);
        else if (location == 'first') {
            var first = this.getFirst();
            if (first) this.insertBefore(text, first);
            else this.appendChild(text);
        }
        return this;
    },
    clone: function(deep) {
        return NC.$(this.cloneNode(deep || true));
    },
    getOffset: function(what){
        what = what.capitalize();
        var el = this;
        var offset = 0;
        do {
            offset += el['offset'+what] || 0;
            el = el.offsetParent;
        } while (el);
        return offset;
    },

    /*
    Property: getTop
    Returns the distance from the top of the window to the Element.
    */

    getTop: function(){
        return this.getOffset('top');
    },

    /*
    Property: getLeft
    Returns the distance from the left of the window to the Element.
    */

    getLeft: function(){
        return this.getOffset('left');
    },

    getHeight: function() {
        return this.offsetHeight;
    },

    /*
    Property: getPosition
    Returns an object with width, height, left, right, top, and bottom, representing the values of the Element

    Example:
    >var myValues = $('myElement').getPosition();
    >//myValues will be..
    >{
    >	width: 200,
    >	height: 300,
    >	left: 100,
    >	top: 50,
    >	right: 300,
    >	bottom: 350
    >}
    */

    getPosition: function(){
        var obj = {};
        obj.width = this.offsetWidth;
        obj.height = this.offsetHeight;
        obj.cWidth = this.clientWidth;
        obj.cHeight = this.clientHeight;
        obj.left = this.getLeft();
        obj.top = this.getTop();
        obj.right = obj.left + obj.width;
        obj.bottom = obj.top + obj.height;
        return obj;
    },

    setHeight: function(value) {
        if (value.isInt()) value = value + 'px';
        this.setStyle('height', value);
        return this;
    },

    hide: function(type) {
        type = type || 'display';
        if (type == 'display') {
            this.setDisplay('none');
        }
        else if (type == 'visibility') {
            this.setStyle(type, 'hidden');
        }
        return this;
    },

    show: function(type, display) {
        type = type || 'display';
        if (type == 'display') {
            display = display || 'block';
            if (display == 'block') {
                var name = this.nodeName.toLowerCase();
                if (name == 'tr') display = 'table-row';
            }
            if (display.substring(0, 5) == 'table' && window.ActiveXObject) display = 'block'; //IE doesn't like the table-... display values
            this.setDisplay(display);
        }
        else if (type == 'visibility') {
            this.setStyle(type, 'visible');
        }
        return this;
    },

    toggle: function() {
        if (this.style.display != 'none' ) {
            this.style.display = 'none';
        }
        else {
            this.style.display = '';
        }
        return this;
    },

    setFocus: function(callback) {
        this.focus();
        if (callback) callback;
        return this;
    },
    /**
    * @depreciated
    * Use removeThis() method instead
    * This interfears with some built-in remove() methods of some Dom elements
    */
    remove: function() {
        this.removeThis();
    },
    removeThis: function() {
        this.parentNode.removeChild(this);
        return this;
    },
    removeAllChildren: function() {
        while (this.firstChild) {
            this.removeChild(this.firstChild);
        }
    }
});

var Chain = new NC.Class({

    /*
    Property: chain
    adds a function to the Chain instance stack.

    Arguments:
    fn - the function to append.

    Returns:
    the instance of the <Chain> class.

    Example:
    >var myChain = new Chain();
    >myChain.chain(myFunction).chain(myFunction2);
    */

    chain: function(fn){
        this.chains = this.chains || [];
        this.chains.push(fn);
        return this;
    },

    /*
    Property: callChain
    Executes the first function of the Chain instance stack, then removes it. The first function will then become the second.

    Example:
    >myChain.callChain(); //executes myFunction
    >myChain.callChain(); //executes myFunction2
    */

    callChain: function(){
        if (this.chains && this.chains.length) this.chains.splice(0, 1)[0].delay(10, this);
    },

    /**
    * Clears the stack of a Chain instance.
    */

    clearChain: function(){
        this.chains = [];
    }

});
/******************************************
* Window
******************************************/
NC.Window = {
    /*
    * Onload event written by Simon Willison (http://simon.incutio.com, http://simonwillison.net/2004/May/26/addLoadEvent/)
    * Sets a function to be executed when the page loads
    * @param    string    func    The function name to execute
    */
    onLoad: function(func) {
        this.onload(func);
    },
    onload: function(func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
            window.onload = func
        }
        else {
            window.onload = function() {
                if (oldonload) oldonload();
                func();
            }
        }
    },

    onDomReady: function(func){
        //NC.Window.__onDomReady is used by Gecko browsers only
        var oldOnDomReady = NC.Window.__onDomReady;
        if (typeof NC.Window.__onDomReady != 'function') {
            NC.Window.__onDomReady = func;
        }
        else {
            NC.Window.__onDomReady = function() {
                if (oldOnDomReady) oldOnDomReady();
                func();
            }
        }
        var state = document.readyState;
        if (state && document.childNodes && !document.all && !navigator.taintEnabled){ //khtml
            if (state.test(/loaded|complete/)) return  NC.Window.__onDomReady();
            else return NC.Window.onDomReady.pass(func).delay(100) ;
        }
        else if (state && window.ActiveXObject){ //ie
            //            var script = NC.$('_ie_ready_');
            if (!NC.$('_ie_ready_')) {
                var src = (window.location.protocol == 'https:') ? '//0' : 'javascript:void(0)';
                //document.write('<script id="ie_ready" defer="true" src="' + src + '"><\/script>');
                //document.write("<script id='_ie_ready_' defer='defer' src='://'></script>");
                document.write("<script id='_ie_ready_' defer='defer' src='//:'></script>");
            }
            NC.$('_ie_ready_').onreadystatechange = function(){
                if (this.readyState == 'complete') NC.Window.__onDomReady();
            }
            return;
        }
        else { //others
            if (document.addEventListener) {
                document.addEventListener("DOMContentLoaded", func, false);
            }
            else {
                NC.Window.onload(NC.Window.__onDomReady);
            }
        }
    },

    //	onDomReady: function(func){
    //		var oldOnDomReady = NC.Window.__onDomReady;
    //		if (typeof NC.Window.__onDomReady != 'function') {
    //			NC.Window.__onDomReady = func;
    //		}
    //		else {
    //			NC.Window.__onDomReady = function() {
    //				if (oldOnDomReady) oldOnDomReady();
    //				func();
    //			}
    //		}
    //		var state = document.readyState;
    //		if (state && document.childNodes && !document.all && !navigator.taintEnabled){ //khtml
    //			if (state.test(/loaded|complete/)) return  NC.Window.__onDomReady();
    //			else return NC.Window.onDomReady.pass(func).delay(100) ;
    //		}
    //		else if (state && window.ActiveXObject){ //ie
    //			var script = NC.$('_ie_ready_');
    //			if (!script) document.write("<script id='_ie_ready_' defer='true' src='://'></script>");
    //			NC.$('_ie_ready_').onreadystatechange = function(){
    //				if (this.readyState == 'complete') NC.Window.__onDomReady();
    //			}
    //			return;
    //		}
    //		else { //others
    //			if (document.addEventListener) {
    //				document.addEventListener("DOMContentLoaded", NC.Window.__onDomReady, false);
    //			}
    //			else {
    //				NC.Window.onload(NC.Window.__onDomReady);
    //			}
    //		}
    //	},

    __onDomReady: "",
    /*
    Property: disableImageCache
    Disables background image chache for internex explorer, to prevent flickering.
    To be called if you have effects with background images, and they flicker.

    Example:
    Window.disableImageCache();
    */

    disableImageCache: function(){
        if (window.ActiveXObject) document.execCommand("BackgroundImageCache", false, true);
    },

    extend: Object.extend,

    /*
    Property: getWidth
    Returns an integer representing the width of the browser.
    */

    getWidth: function(){
        return window.innerWidth || document.documentElement.clientWidth || 0;
    },

    /*
    Property: getHeight
    Returns an integer representing the height of the browser.
    */

    getHeight: function(){
        return window.innerHeight || document.documentElement.clientHeight || 0;
    },

    /*
    Property: getScrollHeight
    Returns an integer representing the scrollHeight of the window.

    See Also:
    <http://developer.mozilla.org/en/docs/DOM:element.scrollHeight>
    */

    getScrollHeight: function(){
        return document.documentElement.scrollHeight;
    },

    /*
    Property: getScrollWidth
    Returns an integer representing the scrollWidth of the window.

    See Also:
    <http://developer.mozilla.org/en/docs/DOM:element.scrollWidth>
    */

    getScrollWidth: function(){
        return document.documentElement.scrollWidth;
    },

    /*
    Property: getScrollTop
    Returns an integer representing the scrollTop of the window (the number of pixels the window has scrolled from the top).

    See Also:
    <http://developer.mozilla.org/en/docs/DOM:element.scrollTop>
    */

    getScrollTop: function(){
        return document.documentElement.scrollTop || window.pageYOffset || 0;
    },

    /*
    Property: getScrollLeft
    Returns an integer representing the scrollLeft of the window (the number of pixels the window has scrolled from the left).

    See Also:
    <http://developer.mozilla.org/en/docs/DOM:element.scrollLeft>
    */

    getScrollLeft: function(){
        return document.documentElement.scrollLeft || window.pageXOffset || 0;
    }
};

NC.namespace('NC.Ajax');
NC.Ajax = new NC.Class({
    init: function(url, options, postData) {
        this.create();
        this.url = url;
        this.setOptions(options);
        this.options.method = this.options.method.toUpperCase();
        this.failed = false;
        this.response = null;
        this.responseXML = null;
        this.postData = postData;
    },

    setOptions: function(options) {
        this.options = {
            method: 'get',
            //Whether or not to eval the response
            evaluate: false,
            //A dom element to apply innerHTML to
            element: null,
            onReadyArgs: null,
            async: true,
            onFail: NC.Class.empty,
            onLoading:  NC.Class.empty,
            onLoaded:  NC.Class.empty,
            onInteractive:  NC.Class.empty,
            onReady:  NC.Class.empty
        };
        Object.extend(this.options, options || {});
    },

    create: function() {
        if (window.XMLHttpRequest) {
            this.xmlhttp = new XMLHttpRequest();
        }
        else if (window.ActiveXObject) {
            try {
                this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch (e) {
                try {
                    this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                }
                catch (e) {}
            }
        }
        //if xmlhttp still doesn't exist, then it could not be created
        if (!this.xmlhttp) {
            this.failed = true;
        }
    },

    request: function() {
        if (!this.failed) {
            this.xmlhttp.open(this.options.method, this.url, this.options.async);
            var thisObj = this;
            this.xmlhttp.onreadystatechange = function() {
                var state = thisObj.xmlhttp.readyState;
                if (state == 1) {
                    thisObj.options.onLoading.pass([state], this);
                }
                else if (state == 2) {
                    thisObj.options.onLoaded.pass([state], this);
                }
                else if (state == 3) {
                    thisObj.options.onInteractive.pass([state], this);
                }
                else if (state == 4) {
                    thisObj.complete();
                }
            }
            if (this.options.method == 'POST') {
                this.xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                this.xmlhttp.send(this.postData);
            }
            else {
                this.xmlhttp.send(null);
            }
        }
        else {
            this.onFail();
        }
    },

    cancel: function() {
        if (this.xmlhttp.readyState !== 4) {
            this.xmlhttp.abort();
        }
    },

    complete: function() {
        if (this.xmlhttp.status == 200) {
            this.response = this.xmlhttp.responseText;
            this.responseXML = this.xmlhttp.responseXML;

            if (this.options.evaluate) {
                this.evalResponse();
            }
            else if (this.options.element) {

                var element = NC.$(this.options.element);
                var nodeName = element.nodeName.toLowerCase();
                if (nodeName == 'text' || nodeName == 'input' || nodeName == 'textarea') {
                    //Apply the response to a form field
                    element.value = this.response;
                }
                else if (nodeName == 'select') {
                    //Apply the response to a select menu
                }
                else {
                    //Apply the response to an html tag
                    element.innerHTML = this.response;
                    //If there are any script tags in the response text, evaluate the javascript
                    var jsTags = element.getElementsByTagName('SCRIPT');
                    for (i = 0; i < jsTags.length; i ++) {
                        eval(jsTags[i].innerHTML);
                    }
                }
            }
            //Call the custom function
            //"this" is passed as an argument in case the onReady function has been altered to use a different value for it's "this" keyword
            //This instance of NC.Ajax can then be retrieved as just the first parameter to the onReady function
            this.options.onReady.apply(this, [this,this.options.onReadyArgs]);
        }
        else {
            //The request was not successful.  A http status other than 200 was returned
            this.options.onFail();
        }
    },

    evalResponse: function() {
        eval(this.response);
    }
});

/**
* Class: NC.Ajax.Post
*    Builds and submits an ajax request with form data that will be sent as a POST request
*
* Arguments
*    form:     (required) The form to be submitted
*    url:      (required) The url to send the ajax request to
*    options:  (optional) And options for the ajax request
*    postData: (optional) The form data to be posted. This can also be created by using the buildPost method.  Only
*              pass this data if you've implimented your own method for setting the post data and will not be using the
*              buildPost method.
*/
NC.Ajax.Post = NC.Ajax.extend({
    init: function(form, url, options, postData) {
        this.form = form;
        this.parent(url, options);
        this.options.method = 'POST';
        var ignoreFields = options.ignoreFields || [];
        if (postData) {
            this.postData = postData;
        }
        else {
            this.postData = '';
            this.buildPost(ignoreFields);
        }
    },
    buildPost: function(ignoreFields) {
        //ignoreFields must be an array
        ignoreFields = ignoreFields || [];
        var formData = '';
        var element = '';
        for (var i = 0; i < this.form.length; i ++) {
            element = this.form.elements[i];
            if (!ignoreFields.test(element.name)) {
                var name = element.nodeName.toLowerCase();
                var value = '';
                var use = true;
                if (name == 'input') {
                    var type = element.type;
                    if (type == 'checkbox' || type == 'radio') {
                        if (element.checked == true) {
                            value = element.value;
                        }
                        else {
                            use = false;
                        }
                    }
                    else {
                        value = element.value;
                    }
                }
                else if (name == 'select') {
                    if (element.length > 0) {
                        value = element.options[element.selectedIndex].value;
                    }
                    else {
                        use = false;
                    }
                }
                else if (name == 'textarea') {
                    value = element.value;
                }
                if (element.name == undefined) {
                    //For fieldsets
                    use = false;
                }
                if (use) {
                    if (this.postData.length > 0) {
                        this.postData += '&';
                    }
                    //					this.postData += element.name + '=' + escape(value);
                    this.postData += element.name + '=' + URLEncode(value);
                    //					this.postData += element.name + '=' + value;
                }
            }
        }
    },
    addPostData: function(name, value) {
        if (this.postData.length > 0) {
            this.postData += '&';
        }
        this.postData += name + '=' + escape(value);
    },
    getPostData: function() {
        return this.postData;
    }
});

/**
* From http://cass-hacks.com/articles/discussion/js_url_encode_decode/
*/
function URLEncode (clearString) {
    var output = '';
    var x = 0;
    clearString = clearString.toString();
    var regex = /(^[a-zA-Z0-9_.\n\r]*)/; //Modified to add "\n\r" to not encode new line charactors
    while (x < clearString.length) {
        var match = regex.exec(clearString.substr(x));
        if (match != null && match.length > 1 && match[1] != '') {
            output += match[1];
            x += match[1].length;
        } else {
            if (clearString[x] == ' ')
            output += '+';
            else {
                var charCode = clearString.charCodeAt(x);
                var hexVal = charCode.toString(16);
                output += '%' + hexVal.toUpperCase();
            }
            x++;
        }
    }
    return output;
}

/***********
** XML
************/
NC.Xml = new NC.Class({
    init: function(xml) {
        this.xml = xml;
    },

    getTags: function(tag, env) {
        env = env || this.xml;
        this.tag = NC.$T(tag, env);
        return this.tag;
    },

    getTagValue: function(tag, defaultValue, env) {
        this.getTags(tag, env);
        var value = '';
        if (this.tag && this.tag.length > 0) {
            var fc = this.tag[0].firstChild;
            if (fc) {
                value = fc.data;
            }
        }
        if (NC.type(value) == 'object') value = '';
        if (value.length == 0 && defaultValue) value = defaultValue;
        return value;
    }

});

/**
* Effects
*/
NC.namespace('NC.Fx');
/*
Class: NC.Fx
Base class for the effects library

Options:
onStart - the function to execute as the effect begins; nothing (<Class.empty>) by default.
onComplete - the function to execute after the effect has processed; nothing (<Class.empty>) by default.
duration - the duration of the effect in ms; 500 is the default.
fps - the frames per second for the transition; default is 30
*/
NC.Fx = new NC.Class({
    init: function(options) {
        this.setOptions(options);
        //Calculate the number of frames
        this.frames = Math.round(this.options.fps * (this.options.duration / 1000));
        this.interval = this.options.duration / this.frames;
        this.delay = this.interval;
        this.frame = 0;
    },
    setOptions: function(options) {
        this.options = {
            onStart: NC.Class.empty,
            onComplete: NC.Class.empty,
            duration: 500,
            fps: 30
        };
        Object.extend(this.options, options || {});
    },
    step: function() {

    }

});
/*
Class: NC.Fx.Highlight
Highlights a dom element.  Can be used to create the "yellow fade" technique

//Adapted from the Fade Anything Technique http://www.axentric.com/posts/default/7

Arguments:
element - the element to apply the effect to
from - the hex color value to start at
to - the hex color to end at
options - The NC.Fx options
*/
NC.Fx.Highlight = NC.Fx.extend({
    init: function(element, from, to, options) {
        this.element = NC.$(element);
        this.from = from || '#FFFF66';
        this.to = to || '#FFFFFF';
        //Allows you to pass #FFF instead of #FFFFFF
        if (this.from.length < 7) this.from += this.from.substr(1,3);
        if (this.to.length < 7) this.to += this.to.substr(1,3);

        this.parent(options);
        this.bgOriginal = this.element.getStyle('backgroundColor');
        this.getColors();
        this.stepColors();
    },
    getColors: function() {
        //r = red, g = green, b = blue, t = to, f = from
        //rf = red from, rt = red to
        this.rf = parseInt(this.from.substr(1,2),16);
        this.gf = parseInt(this.from.substr(3,2),16);
        this.bf = parseInt(this.from.substr(5,2),16);
        this.rt = parseInt(this.to.substr(1,2),16);
        this.gt = parseInt(this.to.substr(3,2),16);
        this.bt = parseInt(this.to.substr(5,2),16);
    },
    stepColors: function() {
        var r,g,b,h;
        while (this.frame <= this.frames) {
            r = Math.floor(this.rf * ((this.frames-this.frame)/this.frames) + this.rt * (this.frame/this.frames));
            g = Math.floor(this.gf * ((this.frames-this.frame)/this.frames) + this.gt * (this.frame/this.frames));
            b = Math.floor(this.bf * ((this.frames-this.frame)/this.frames) + this.bt * (this.frame/this.frames));
            hex = NC.rgbToHex(r,g,b);
            this.setColor.pass([this.element, hex]).delay(this.delay);
            this.frame++;
            if (this.frame > this.frames) {
                //The highlight is done, put the element back to it's original background color
                if (this.bgOriginal) {
                    this.setColor.pass([this.element, this.bgOriginal]).delay(this.delay);
                }
                else {
                    this.setColor.pass([this.element, '']).delay(this.delay);
                }
                this.options.onComplete.delay(this.delay);
                //				setTimeout(this.options.onComplete.apply(this), this.delay);
            }
            this.delay = this.interval * this.frame;

        }
    },
    setColor: function(element, color) {
        element.setStyle('backgroundColor', color);
    },
    finish: function() {

    }
});
/****
* Dragable
*/
NC.namespace('NC.Drag');
/**
* Class: NC.Drag
*    Enables an element to be dragable
*
* Arguments
*    el: (required) The element that is being moved
*    xModifier: (required) The css property for setting the x-axis position.  Example value: "left"
*    yModifier: (required) The css property for setting the y-axis position.  Example value: "top"
*    options: (optional)  Any additonal options
*
* Options
*    handle: (optional) The element to attach the drag events to. If not set the the element that is being draged will be used.
*    window: (optional) The "window" environment where events will come from in IE browsers.  If not set the current
*             window will be used.  Useful if setting a dragable item either from an iframe or the element is in an iframe.
*             Example: element.makeDragable({window: window.top});
*/
NC.Drag = new NC.Class({

    init: function(el, xModifier, yModifier, options) {
        this.setOptions(options);
        this.element = el;
        if (xModifier) this.xMod = xModifier.camelCase();
        if (yModifier) this.yMod = yModifier.camelCase();


        this.handle = NC.$(this.options.handle) || this.element;
        this.setCursor();
        if(this.options.zindex) {
            this.originalZ = parseInt(this.element.getStyle('z-index') || 0);
            this.element.style.zIndex = this.options.zindex;
        }
        this.handle.onmousedown = this.start.bind(this);
        return el;
    },

    setOptions: function(options) {
        this.options = {
            unit: 'px',
            handle: false,
            window: false,
            document: false,
            cursor: 'move',
            constraint: false,
            xMax: false,
            xMin: false,
            yMax: false,
            yMin: false,
            zindex: 1000,
            position: 'absolute',
            container: false,
            ghost: false
        };
        Object.extend(this.options, options || {});
        //Get the correct "window" and "document" environment
        this.window = this.options.window || window;
        this.document = this.options.document || document;
    },

    setCursor: function() {
        if (this.xMod == 'width' || this.yMod == 'height') {
            if (this.options.constraint == 'horizontal') this.options.cursor = 'n-resize';
            else if (this.options.constraint == 'vertical') this.options.cursor = 'e-resize';
            else this.options.cursor = 'nw-resize';
        }
        this.handle.setStyle('cursor', this.options.cursor);
    },

    //evt = the mouse event
    start: function(evt) {
        evt = evt || this.window.event;
        this.elementMove = this.element;
        this.setResize();
        //Get the starting position of the mouse
        this.startX = evt.clientX;
        this.startY = evt.clientY;

        this.getParentOffset();
        //get the offset position of the mouse in relation to the element
        //By adding the parent postion to the mouse offset, the actual position calculated
        //in drop() is lower or negative to give the correct position of the element in
        //relation to it's positioned parent element
        this.offsetX = this.startX - this.handle.getLeft() + this.parentOffsetX;
        this.offsetY = this.startY - this.handle.getTop() + this.parentOffsetY;
        this.getConstraints();
        if (this.options.ghost) {
            this.clone = this.handle.clone().setOpacity(.7).setStyles({position: 'absolute', left: this.handle.getLeft(), top: this.handle.getTop()}).addto(this.document.body);
            this.element.setOpacity(.9);
            this.elementMove = this.clone;
        }
        if (this.options.position) {
            this.element.setStyle('position', this.options.position);
        }
        this.document.onmousemove = this.drag.bind(this);
        this.document.onmouseup = this.end.bind(this);
    },

    setResize: function() {
        this.resize = false;
        this.elementWidth = 0;
        this.elementHeight = 0;
        if (this.xMod == 'width' || this.yMod == 'height') {
            this.resize = true;
            this.elementWidth = this.element.getStyle('width').toInt();
            this.elementHeight = this.element.getStyle('height').toInt();
            //Set the x and y minimums to 0 because negative heights and widths can't be set
            if (!this.options.xMin) this.options.xMin = '0';
            if (!this.options.yMin) this.options.yMin = '0';
        }
    },

    getConstraints: function() {
        //If a container is set, set the constraints based on the container
        if (this.options.container) {
            var container = NC.$(this.options.container);
            var cont = container.getPosition();
            if (!this.resize) {
                if (this.parentPositioned) {
                    this.options.xMin = '0';
                    this.options.yMin = '0';
                    this.options.xMax = cont.cWidth - this.element.offsetWidth;
                    this.options.yMax = cont.cHeight - this.element.offsetHeight;
                }
                else {
                    this.options.xMax =  cont.right - this.element.offsetWidth - ((cont.width - cont.cWidth) / 2);
                    this.options.xMin = cont.left + ((cont.width - cont.cWidth) / 2);
                    this.options.yMax = cont.bottom - this.element.offsetHeight - ((cont.height - cont.cHeight) /2);
                    this.options.yMin = cont.top + ((cont.height - cont.cHeight) /2);
                }
            }
            else {
                this.options.xMax = container.clientWidth - (this.element.getLeft() - cont.left);
                this.options.yMax = container.clientHeight - (this.element.getTop() - cont.top);
            }
        }
        //If the element is constrained, get the constraining values
        if (this.options.constraint == 'horizontal') {
            if (this.xMod != 'width') {
                this.options.xMax = this.options.xMin = this.startX - this.offsetX;
            }
            else {
                this.options.xMax = this.options.xMin = this.element.getStyle('width').toInt();
                if (!this.options.yMin) this.options.yMin = '0';
                this.options.position = 'relative';
            }
        }
        else if (this.options.constraint == 'vertical') {
            if (this.yMod != 'height') {
                this.options.yMax = this.options.yMin = this.startY - this.offsetY;
            }
            else {
                //Resizing the element
                this.options.yMax = this.options.yMin = this.element.getStyle('height').toInt();
                //Set xMin to 0 if it's not already set because you can't have negative widths
                if (!this.options.xMin) this.options.xMin = '0';
                this.options.position = false;
            }
        }
    },

    /**
    * Gets the position of the first parent container that has an absolute or relative positioning
    * This is used to calculate the correct position of the element in relation to it's positioned
    * parent element
    */
    getParentOffset: function() {
        this.parentOffsetX = 0;
        this.parentOffsetY = 0;
        this.parentPositioned = false;
        var parent = NC.$(this.element.parentNode);
        do {
            var pos = parent.getStyle('position');
            if (pos == 'absolute' || pos == 'relative') {
                this.parentOffsetX = parent.getLeft();
                this.parentOffsetY = parent.getTop();
                this.parentPositioned = true;
                parent = null;
            }
            else {
                parent = NC.$(parent.parentNode);
            }
        }
        while (parent);
    },

    drag: function(evt) {
        evt = evt || this.window.event;
        //Get the current position of the mouse
        this.mouseX = evt.clientX;
        this.mouseY = evt.clientY;
        this.getXY();
        this.setStyles(this.elementMove);
    },

    setStyles: function(el) {
        el.setStyle(this.xMod, this.xPos);
        el.setStyle(this.yMod, this.yPos);
    },

    /**
    * If not resizing calculate the actual position of the element (mouse position - mouse offset).
    * If resizing, get the new size: startX - mouseX
    */
    getXY: function() {
        if (!this.resize) var x = this.mouseX - this.offsetX;
        else var x = this.elementWidth - (this.startX - this.mouseX);
        if (this.options.xMin && x < parseInt(this.options.xMin)) x = this.options.xMin;
        if (this.options.xMax && x > parseInt(this.options.xMax)) x = this.options.xMax;
        this.xPos = x + this.options.unit;

        if (!this.resize) var y = this.mouseY - this.offsetY;
        else var y = this.elementHeight - (this.startY - this.mouseY);
        if (this.options.yMin && y < parseInt(this.options.yMin)) y = this.options.yMin;
        if (this.options.yMax && y > parseInt(this.options.yMax)) y = this.options.yMax;
        this.yPos = y + this.options.unit;
    },

    end: function() {
        if(this.options.zindex) {
            this.element.style.zIndex = this.originalZ;
        }
        if (this.options.ghost) {
            this.setStyles(this.element);
            this.document.body.removeChild(this.clone);
            this.clone = null;
            this.element.setOpacity(1);
        }
        this.elementMove = null;
        this.document.onmousemove = null;
        this.document.onmouseup = null;
    }
});

NC.Drag.Move = NC.Drag.extend({

    init: function(el, options) {
        this.extendOptions(options);
        this.parent(el, this.options.xModifier, this.options.yModifier, options);
    },

    extendOptions: function(options){
        this.options = {
            xModifier: 'left',
            yModifier: 'top',
            container: false
        };
        Object.extend(this.options, options || {});
    }
});
/**
* Add dragable properties to NC.Element
*/
NC.Element.implement({
    makeDragable: function(options) {
        return new NC.Drag(this, 'left', 'top', options);
    },

    makeResizable: function(options) {
        return new NC.Drag(this, 'width', 'height', options);
    }
});

/***
* Date functions
***/
NC.Date = new NC.Class({
    init: function(options) {
        this.setOptions(options);
        //Get the date for today
        this.today = new Date();
        this.currentYear = this.today.getFullYear();
        this.currentMonth = this.today.getMonth() + 1;
        this.currentDayName = this.options.days[this.today.getDay()];
        this.currentDay = this.today.getDate();
        this.currentHours = this.today.getHours();
        this.currentMinutes = this.today.getMinutes();
        this.currentSeconds = this.today.getSeconds();
    },
    setOptions: function(options) {
        this.options = {
            days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday','Friday','Saturday']
        };
        Object.extend(this.options, options || {});
    }
});
/*****
* Table
*****/
//NC.namespace('NC.Table');
//NC.Table = new NC.Class({
//	setupTable: function(table) {
//		this.table = NC.$(table);
//		this.numRows = table.rows.length;
//	},
//	setTable: function(el) {
//		if (el.nodeName.toLowerCase() != 'table') {
//			var parent = el.parentNode;
//			while (parent.nodeName.toLowerCase() != 'table') {
//				parent = parent.parentNode;
//			}
//			if (parent.nodeName.toLowerCase() == 'table') {
//				this.setupTable(parent);
//				return this;
//			}
//		}
//	},
//	getTr: function(el) {
//		var parent = el.parentNode;
//		while (parent.nodeName.toLowerCase() != 'tr') {
//			parent = parent.parentNode;
//		}
//		if (parent.nodeName.toLowerCase() != 'tr') {
//			return new NC.Table.Tr(parent);
//		}
//		else {
//			return false;
//		}
//	}
//});
//NC.Table.Tr = new NC.Class({
//	init: function(tr) {
//		this.tr = NC.$(tr);
//		this.cells = NC.$T('td', this.tr);
//		this.index = this.tr.rowIndex;
//	},
//	hideCellContents: function(cellNumber) {
//		return this.setCellContentsVisibility(cellNumber, 'hidden');
//	},
//	showCellContents: function(cellNumber) {
//		return this.setCellContentsVisibility(cellNumber, 'hidden');
//	},
//	setCellContentsVisibility: function(cellNumber, cssVisibility) {
//		if (this.cell[cellNumber]) {
//			var children = this.cell[cellNumber].childNodes;
//			for (var i = 0; i < children.length; i ++) {
//				if (NC.type(children[i]) != 'textNode') {
//					children[i].style.visibility = cssVisibility;
//				}
//			}
//		}
//		return this;
//	}
//});
/******
* Singleton pattern to create just one instance of the logger
*****/
NC.Log = function(value) {
    var log = NC.SingleLogger();
    log.add(value);
    return log;
}
NC.SingleLogger = function(options) {
    if (this.__instance__ == null) {
        this.__instance__ = new NC.Logger(options);
    }
    return this.__instance__;
};
NC.SingleLogger.__instance__ = null

/******
* Logger
*****/
NC.Logger = new NC.Class({
    init: function(options) {
        this.setOptions(options);
        var xPos = this.options.xPos + this.options.unit;
        var yPos = this.options.yPos + this.options.unit;
        var log = new NC.Element('div').setStyles({position: 'absolute', textAlign: 'left', width: this.options.width, height: this.options.height, background: this.options.background});
        log.setStyle(this.options.xModifier, this.options.xPos + this.options.unit);
        log.setStyle(this.options.yModifier, this.options.yPos + this.options.unit);
        var dragHandle = new NC.Element('div').setStyles({padding: '4px', background: '#333333', color: '#ffffff'}).addText('Logger (click to drag)').addto(log);

        this.logger = new NC.Element('div').setStyles({padding: '4px',border: this.options.border, height: '100%', overflow: 'auto'}).addto(log);
        //				var resizeHandle = new NC.Element('div').setStyles({padding: '4px', background: '#333333', color: '#ffffff', position: 'absolute', bottom: '0'}).addText('(click to resize)').addto(log);
        log.addto(document.body).makeDragable({zindex: 9999999999, handle: dragHandle});
        //				log.makeResizable({handle: resizeHandle});
    },

    setOptions: function(options) {
        this.options = {
            xPos: 0,
            yPos: 0,
            unit: 'px',
            xModifier: 'right',
            yModifier: 'top',
            width: '300px',
            height: '400px',
            border: '1px solid black',
            background: '#ffffff'
        };
        Object.extend(this.options, options || {});
        this.options.xModifier = this.options.xModifier.camelCase();
        this.options.yModifier = this.options.yModifier.camelCase();
    },

    add: function(value) {
        new NC.Element('div').addText(String(value)).addFirst(this.logger);
        return this;
    },

    br: function() {
        new NC.Element('br').addFirst(this.logger);
        return this;
    }
});

