﻿/// <reference name="MicrosoftAjax.js" />
/// <reference name="dnn.js" assembly="DotNetNuke.WebUtility" />

var DNN_HIGHLIGHT_COLOR = '#9999FF';
var COL_DELIMITER = String.fromCharCode(18);
var ROW_DELIMITER = String.fromCharCode(17);
var QUOTE_REPLACEMENT = String.fromCharCode(19);
var KEY_LEFT_ARROW = 37;
var KEY_UP_ARROW = 38;
var KEY_RIGHT_ARROW = 39;
var KEY_DOWN_ARROW = 40;
var KEY_RETURN = 13;
var KEY_ESCAPE = 27;

Type.registerNamespace('dnn');
dnn.extend = function(dest, src) 
{
	for (s in src)
		dest[s] = src[s];
	return dest;
}
	
dnn.extend(dnn, {
    apiversion: new Number('04.01'),
    pns: '',
    ns: 'dnn',
    diagnostics: null,
    vars: null,
    dependencies: new Array(),
    isLoaded: false,
    delay: [],
    _delayedSet: null,  //used to delay setting variable until page is loaded - perf

	getVars: function()
	{
        /// <summary>
        /// Gets array of name value pairs set on the server side by the RegisterClientVariable method.
        /// </summary>
        /// <value type="String" />
		if (this.vars == null)
		{
			//this.vars = new Array();
			var ctl = dnn.dom.getById('__dnnVariable');
			if (ctl.value.indexOf('`') == 0)
			    ctl.value = ctl.value.substring(1).replace(/`/g, '"');
			
			if (ctl.value.indexOf('__scdoff') != -1) //back compat
			{
				COL_DELIMITER = '~|~';
				ROW_DELIMITER = '~`~';
				QUOTE_REPLACEMENT = '~!~';
			}
			
			if (ctl != null && ctl.value.length > 0)
			    this.vars = Sys.Serialization.JavaScriptSerializer.deserialize(ctl.value);
			else
			    this.vars = [];
		}
		return this.vars;	
	},

	getVar: function(key, def)
	{
        /// <summary>
        /// Gets value for passed in variable name set on the server side by the RegisterClientVariable method.
        /// </summary>
        /// <param name="key" type="String">
        /// Name of parameter to retrieve value for
        /// </param>
        /// <param name="def" type="String">
        /// Default value if key not present
        /// </param>
        /// <returns type="String" />
		if (this.getVars()[key] != null)
		{
			var re = eval('/' + QUOTE_REPLACEMENT + '/g');
			return this.getVars()[key].replace(re, '"');
		}
        return def;
	},

	setVar: function(key, val)
	{			
        /// <summary>
        /// Sets value for variable to be sent to the server
        /// </summary>
        /// <param name="key" type="String">
        /// Name of parameter to set value for
        /// </param>
        /// <param name="val" type="String">
        /// value
        /// </param>
        /// <returns type="Boolean" />
		if (this.vars == null)
			this.getVars();			
		this.vars[key] = val;
		var ctl = dnn.dom.getById('__dnnVariable');
		if (ctl == null)
		{
			ctl = dnn.dom.createElement('INPUT');
			ctl.type = 'hidden';
			ctl.id = '__dnnVariable';
			dnn.dom.appendChild(dnn.dom.getByTagName("body")[0], ctl);		
		}
		if (dnn.isLoaded)
		    ctl.value = Sys.Serialization.JavaScriptSerializer.serialize(this.vars);
		else
		    dnn._delayedSet = {key: key, val: val}; //doesn't matter how many times this gets overwritten, we just want one value to set after load so serialize is called
		return true;
	},

	callPostBack: function(action)
	{
        /// <summary>
        /// Initiates a postback call for the passed in action. In order to work the action will need to be registered on the server side.
        /// </summary>
        /// <param name="action" type="String">
        /// Action name to be raised
        /// </param>
        /// <param name="N Params" type="Array">
        /// Pass in any number of parameters the postback requires. Parameters should be in the form of 'paramname=paramvalue', 'paramname=paramvalue', 'paramname=paramvalue'
        /// </param>
        /// <returns type="Boolean" />
		var postBack = dnn.getVar('__dnn_postBack');
		var data = '';
		if (postBack.length > 0)
		{
			data += action;
			for (var i=1; i<arguments.length; i++)
			{
				var aryParam = arguments[i].split('=');
				data += COL_DELIMITER + aryParam[0] + COL_DELIMITER + aryParam[1];
			}
			eval(postBack.replace('[DATA]', data));
			return true;
		}
		return false;
	},

    //atlas
    createDelegate: function(oThis, ptr) 
    {
        /// <summary>
        /// Creates delegate (closure)
        /// </summary>
        /// <param name="oThis" type="Object">
        /// Object to create delegate on
        /// </param>
        /// <param name="ptr" type="Function">
        /// Function to invoke
        /// </param>
        /// <returns type="Function" />
        return Function.createDelegate(oThis, ptr);
    },

	doDelay: function(key, time, ptr, ctx) 
	{
        /// <summary>
        /// Allows for a setTimeout to occur that will also pass a context object.
        /// </summary>
        /// <param name="key" type="String">
        /// Key to identify the particular delay. If you wish to cancel this delay you need to call cancelDelay passing this key.
        /// </param>
        /// <param name="time" type="Number">
        /// Number of milliseconds to wait before firing timer. This value is simply passed into the second parameter in setTimeout.
        /// </param>
        /// <param name="ptr" type="Function">
        /// Pointer to the function to invoke after time has elapsed
        /// </param>
        /// <param name="ctx" type="Object">
        /// Context to be passed to the function
        /// </param>
        /// <returns />
		if (this.delay[key] == null)
		{
			this.delay[key] = new dnn.delayObject(ptr, ctx, key);
			this.delay[key].num = window.setTimeout(dnn.createDelegate(this.delay[key], this.delay[key].complete), time);
		}
	},

	cancelDelay: function(key) 
	{
        /// <summary>
        /// Allows for delay to be canceled.
        /// </summary>
        /// <param name="key" type="String">
        /// Key to identify the particular delay. 
        /// </param>
        /// <returns />
		if (this.delay[key] != null)
		{
			window.clearTimeout(this.delay[key].num);
			this.delay[key] = null;
		}
	},

	decodeHTML: function(html)	
	{
        /// <summary>
        /// Unencodes html string
        /// </summary>
        /// <param name="html" type="String">
        /// encoded html
        /// </param>
        /// <returns type="String" />
		return html.toString().replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"');
	},

	encode: function(arg, doubleEncode)
	{
        /// <summary>
        /// Encodes string using either encodeURIComponent or escape
        /// </summary>
        /// <param name="arg" type="String">
        /// string to encode
        /// </param>
        /// <returns type="String" />
        var ret = arg;
		if (encodeURIComponent)
			ret = encodeURIComponent(ret);
		else
			ret = escape(ret);

		if (doubleEncode == false)
		    return ret;
	    //handle double encoding for encoded value "+" encode-> "%2B" replace-> "%252B"
	    return ret.replace(/%/g,"%25");
	},

	encodeHTML: function(html)	
	{
        /// <summary>
        /// Encodes html string
        /// </summary>
        /// <param name="html" type="String">
        /// html to encode
        /// </param>
        /// <returns type="String" />
		return html.toString().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&apos;").replace(/\"/g, "&quot;");
	},

    encodeJSON: function(json)
    {
        /// <summary>
        /// Encodes json string
        /// </summary>
        /// <param name="json" type="String">
        /// json to encode
        /// </param>
        /// <returns type="String" />

        //todo: does Atlas provide method for this?
        return json.toString().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "\u0027").replace(/\"/g, "&quot;").replace(/\\/g, "\\\\");
    },

    //atlas
	evalJSON: function(data)
	{
        /// <summary>
        /// dencodes data
        /// </summary>
        /// <param name="data" type="String">
        /// json to dencode
        /// </param>
        /// <returns type="Object" />
	    return Sys.Serialization.JavaScriptSerializer.deserialize(data);
	},

	escapeForEval: function(s)	//needs work...
	{
        /// <summary>
        /// Allows a string to be evaluated successfully without worry of inappropriate characters. For example ' will be replaced with \' so when evaluated it is equal to 
        /// </summary>
        /// <param name="s" type="String">
        /// string to escape
        /// </param>
        /// <returns type="String" />
		return s.replace(/\\/g, '\\\\').replace(/\'/g, "\\'").replace(/\r/g, '').replace(/\n/g, '\\n').replace(/\./, '\\.');
	},

    getEnumByValue: function(enumType, val)
    {
        /// <summary>
        /// Obtains enum from value passed in
        /// </summary>
        /// <param name="enumType" type="Object">
        /// Enumeration type
        /// </param>
        /// <param name="val" type="Number">
        /// Value of enumerator
        /// </param>
        /// <returns type="Object" />
        for (var prop in enumType)
        {
            if (typeof(enumType[prop]) == 'number' && enumType[prop] == val)
                return prop;
        }
    },
    
    _onload: function()
    {
        dnn.isLoaded = true;
        if (dnn._delayedSet)
            dnn.setVar(dnn._delayedSet.key, dnn._delayedSet.val);
    }	
    
});

//delayObject
dnn.delayObject = function(ptr, ctx, type)
{
    /// <summary>
    /// Object used to hold context for the doDelay functionality
    /// </summary>
	this.num = null;
	this.pfunc = ptr;
	this.context = ctx;
	this.type = type;
}

dnn.delayObject.prototype =
{
	complete: function()
	{
        /// <summary>
        /// This function is invoked internally by the setTimout of the doDelay. It in turn will invoke the function referenced by the pfunc property, passing the context
        /// </summary>
        /// <returns />
		dnn.delay[this.type] = null;
		this.pfunc(this.context);
	}
}
dnn.delayObject.registerClass('dnn.delayObject');

dnn.ScriptRequest = function(src, text, fCallBack)
{
    /// <summary>
    /// The ScriptRequest object allows the loading of external script files from script
    /// </summary>

	this.ctl = null;
	this.xmlhttp = null;
	this.src = null;
	this.text = null;
	if (src != null && src.length > 0)
	{
	    var file = dnn.dom.scriptFile(src);
	    var embedSrc = dnn.getVar(file + '.resx', '');
	    if (embedSrc.length > 0)
	        this.src = embedSrc;
	    else
		    this.src = src;
    }
	if (text != null && text.length > 0)
		this.text = text;
	this.callBack = fCallBack;
	this.status = 'init';
	this.timeOut = 5000;
	this._xmlhttpStatusChangeDelegate = dnn.createDelegate(this, this.xmlhttpStatusChange);
	this._statusChangeDelegate = dnn.createDelegate(this, this.statusChange);
	this._completeDelegate = dnn.createDelegate(this, this.complete);
	this._reloadDelegate = dnn.createDelegate(this, this.reload);
	//this.alreadyLoaded = false;
}
dnn.ScriptRequest.prototype = 
{
	load: function()
	{
        /// <summary>
        /// Loads script
        /// </summary>
		this.status = 'loading';
		this.ctl = document.createElement('script');
		this.ctl.type = 'text/javascript';

		if (this.src != null)
		{
			if (dnn.dom.browser.isType(dnn.dom.browser.Safari))
			{
				this.xmlhttp=new XMLHttpRequest();
				this.xmlhttp.open('GET',this.src,true);
				this.xmlhttp.onreadystatechange=this._xmlhttpStatusChangeDelegate;
				this.xmlhttp.send(null);
				return;
			}
			else
			{
				if (dnn.dom.browser.isType(dnn.dom.browser.InternetExplorer))
					this.ctl.onreadystatechange = this._statusChangeDelegate;
				else if (dnn.dom.browser.isType(dnn.dom.browser.Opera) == false)	//opera loads synchronously
					this.ctl.onload = this._completeDelegate;
				
				this.ctl.src = this.src;
			}
    	    dnn.dom.scriptElements[this.src] = this.ctl; //JON VERIFY THIS!!!			
		}
		else
		{
			if (dnn.dom.browser.isType(dnn.dom.browser.Safari))
				this.ctl.innerHTML = dnn.encodeHTML(this.text);			
			else
				this.ctl.text = this.text;			
		}
					
		var oHeads = dnn.dom.getByTagName('HEAD');
		if (oHeads)
		{
			//opera will load script twice if inline and appended to page 
			if (dnn.dom.browser.isType(dnn.dom.browser.Opera) == false || this.src != null)
				oHeads[0].appendChild(this.ctl);
		}
		else
			alert('Cannot load dynamic script, no HEAD tag present.');
		
		if (this.src == null || dnn.dom.browser.isType(dnn.dom.browser.Opera))	//opera loads script synchronously
			this.complete();
		else if (this.timeOut)
			dnn.doDelay('loadScript_' + this.src, this.timeOut, this._reloadDelegate, null);
	},

	xmlhttpStatusChange: function()
	{
        /// <summary>
        /// Event fires when script request status changes
        /// </summary>
		if(this.xmlhttp.readyState != 4)
			return;
		
		this.src = null;
		this.text = this.xmlhttp.responseText;
		this.load();	//load as inline script
	},

	statusChange: function()
	{
        /// <summary>
        /// Event fires when script request status changes
        /// </summary>
		if ((this.ctl.readyState == 'loaded' || this.ctl.readyState == 'complete') && this.status != 'complete')
			this.complete();
	},
	
	reload: function()
	{
        /// <summary>
        /// Reloads a script reference
        /// </summary>
		if (dnn.dom.scriptStatus(this.src) == 'complete')
		{	
			this.complete();
		}
		else
		{
			this.load();
		}
	},
			
	complete: function()
	{
        /// <summary>
        /// Event fires when script request loaded
        /// </summary>
		dnn.cancelDelay('loadScript_' + this.src);
		this.status = 'complete';
		if (typeof(this.callBack) != 'undefined')
			this.callBack(this);
	    this.dispose();			
	},
	
	dispose: function()
	{
        /// <summary>
        /// Cleans up memory
        /// </summary>
		this.callBack = null;
		if (this.ctl)
		{
			if (this.ctl.onreadystatechange)
				this.ctl.onreadystatechange = new function() {};//stop IE memory leak.  Not sure why can't set to null;
			else if (this.ctl.onload)
				this.ctl.onload = null;
			this.ctl = null;
		}
		this.xmlhttp = null;
		this._xmlhttpStatusChangeDelegate = null;
		this._statusChangeDelegate = null;
		this._completeDelegate = null;
		this._reloadDelegate = null;
	}
}
dnn.ScriptRequest.registerClass('dnn.ScriptRequest');
    
//--- dnn.dom
Type.registerNamespace('dnn.dom');

dnn.extend(dnn.dom, {

    pns: 'dnn',
    ns: 'dom',
    browser: null,
    __leakEvts: [],
    scripts: [],
    scriptElements: [],
    tweens: [],

    attachEvent: function(ctl, type, fHandler)
    {
        /// <summary>
        /// Attatches an event to an element. - you are encouraged to use the $addHandler method instead - kept only for backwards compatibility
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        /// <param name="type" type="String">
        /// Event name to attach
        /// </param>
        /// <param name="fHandler" type="Function">
        /// Reference to the function that will react to event
        /// </param>
        /// <returns type="Boolean" />		
        if (dnn.dom.browser.isType(dnn.dom.browser.InternetExplorer) == false)
        {
            var name = type.substring(2);
            ctl.addEventListener(name, function(evt) { dnn.dom.event = new dnn.dom.eventObject(evt, evt.target); return fHandler(); }, false);
        }
        else
            ctl.attachEvent(type, function() { dnn.dom.event = new dnn.dom.eventObject(window.event, window.event.srcElement); return fHandler(); });
        return true;
    },

    cursorPos: function(ctl)
    {
        /// <summary>
        /// Obtains the current cursor position within a textbox
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        /// <returns type="Number" />		

        // empty control means the cursor is at 0
        if (ctl.value.length == 0)
            return 0;

        // -1 for unknown
        var pos = -1;

        if (ctl.selectionStart)	// Moz - Opera
            pos = ctl.selectionStart;
        else if (ctl.createTextRange)// IE
        {
            var sel = window.document.selection.createRange();
            var range = ctl.createTextRange();

            // if the current selection is within the edit control			
            if (range == null || sel == null || ((sel.text != "") && range.inRange(sel) == false))
                return -1;

            if (sel.text == "")
            {
                if (range.boundingLeft == sel.boundingLeft)
                    pos = 0;
                else
                {
                    var tagName = ctl.tagName.toLowerCase();
                    // Handle inputs.
                    if (tagName == "input")
                    {
                        var text = range.text;
                        var i = 1;
                        while (i < text.length)
                        {
                            range.findText(text.substring(i));
                            if (range.boundingLeft == sel.boundingLeft)
                                break;

                            i++;
                        }
                    }
                    // Handle text areas.
                    else if (tagName == "textarea")
                    {
                        var i = ctl.value.length + 1;
                        var oCaret = document.selection.createRange().duplicate();
                        while (oCaret.parentElement() == ctl && oCaret.move("character", 1) == 1)
                            --i;

                        if (i == ctl.value.length + 1)
                            i = -1;
                    }
                    pos = i;
                }
            }
            else
                pos = range.text.indexOf(sel.text);
        }
        return pos;
    },

    cancelCollapseElement: function(ctl)
    {
        /// <summary>
        /// Allows animation for the collapsing of an element to be canceled
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        dnn.cancelDelay(ctl.id + 'col');
        ctl.style.display = 'none';
    },

    collapseElement: function(ctl, num, pCallBack)
    {
        /// <summary>
        /// Animates the collapsing of an element
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        /// <param name="num" type="Number">
        /// Number of animations to perform the collapse. The more you specify, the longer it will take
        /// </param>
        /// <param name="pCallback" type="Function">
        /// Function to call when complete
        /// </param>
        if (num == null)
            num = 10;
        ctl.style.overflow = 'hidden';
        var ctx = new Object();
        ctx.num = num;
        ctx.ctl = ctl;
        ctx.pfunc = pCallBack;
        ctl.origHeight = ctl.offsetHeight;
        dnn.dom.__collapseElement(ctx);
    },

    __collapseElement: function(ctx)
    {
        var num = ctx.num;
        var ctl = ctx.ctl;

        var step = ctl.origHeight / num;
        if (ctl.offsetHeight - (step * 2) > 0)
        {
            ctl.style.height = (ctl.offsetHeight - step).toString() + 'px';
            dnn.doDelay(ctl.id + 'col', 10, dnn.dom.__collapseElement, ctx);
        }
        else
        {
            ctl.style.display = 'none';
            if (ctx.pfunc != null)
                ctx.pfunc();
        }
    },

    cancelExpandElement: function(ctl)
    {
        /// <summary>
        /// Allows animation for the expanding of an element to be canceled
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        dnn.cancelDelay(ctl.id + 'exp');
        ctl.style.overflow = '';
        ctl.style.height = '';
    },

    disableTextSelect: function(ctl)
    {
        if (typeof ctl.onselectstart != "undefined") //ie
            ctl.onselectstart = function() { return false }
        else if (typeof ctl.style.MozUserSelect != "undefined") //ff
            ctl.style.MozUserSelect = "none"
        else //others
            ctl.onmousedown = function() { return false }
    },

    expandElement: function(ctl, num, pCallBack)
    {
        /// <summary>
        /// Animates the expanding of an element
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control
        /// </param>
        /// <param name="num" type="Number">
        /// Number of animations to perform the collapse. The more you specify, the longer it will take
        /// </param>
        /// <param name="pCallback" type="Function">
        /// Function to call when complete
        /// </param>
        if (num == null)
            num = 10;

        if (ctl.style.display == 'none' && ctl.origHeight == null)
        {
            ctl.style.display = '';
            ctl.style.overflow = '';
            ctl.origHeight = ctl.offsetHeight;
            ctl.style.overflow = 'hidden';
            ctl.style.height = '1px';
        }
        ctl.style.display = '';

        var ctx = new Object();
        ctx.num = num;
        ctx.ctl = ctl;
        ctx.pfunc = pCallBack;
        dnn.dom.__expandElement(ctx);
    },

    __expandElement: function(ctx)
    {
        var num = ctx.num;
        var ctl = ctx.ctl;
        var step = ctl.origHeight / num;
        if (ctl.offsetHeight + step < ctl.origHeight)
        {
            ctl.style.height = (ctl.offsetHeight + step).toString() + 'px';
            dnn.doDelay(ctl.id + 'exp', 10, dnn.dom.__expandElement, ctx);
        }
        else
        {
            ctl.style.overflow = '';
            ctl.style.height = '';
            if (ctx.pfunc != null)
                ctx.pfunc();
        }
    },

    deleteCookie: function(name, path, domain)
    {
        /// <summary>
        /// Deletes a cookie
        /// </summary>
        /// <param name="name" type="String">
        /// Name of the desired cookie to delete
        /// </param>
        /// <param name="path" type="String">
        /// Path for which the cookie is valid
        /// </param>
        /// <param name="domain" type="String">
        /// Domain for which the cookie is valid
        /// </param>
        /// <returns type="Boolean" />		
        if (this.getCookie(name))
        {
            this.setCookie(name, '', -1, path, domain);
            return true;
        }
        return false;
    },

    getAttr: function(node, attr, def)
    {
        /// <summary>
        /// Utility funcion used to retrieve the attribute value of an object. Allows for a default value to be returned if null.
        /// </summary>
        /// <param name="node" type="Object">
        /// Object to obtain attribute from 
        /// </param>
        /// <param name="attr" type="String">
        /// Name of attribute to retrieve
        /// </param>
        /// <param name="def" type="String">
        /// Default value to retrieve if attribute is null or zero-length
        /// </param>
        /// <returns type="String" />		
        if (node.getAttribute == null)
            return def;
        var val = node.getAttribute(attr);

        if (val == null || val == '')
            return def;
        else
            return val;
    },

    //Atlas
    getById: function(id, ctl)
    {
        /// <summary>
        /// Retrieves element on page based off of passed in id. - use $get instead - backwards compat only
        /// </summary>
        /// <param name="id" type="String">
        /// Control's id to retrieve
        /// </param>
        /// <param name="ctl" type="Object" optional="true">
        /// If you wish to narrow down the search, pass in the control whose children you wish to search.
        /// </param>
        /// <returns type="Object" />		
        return $get(id, ctl);
    },

    getByTagName: function(tag, ctl)
    {
        /// <summary>
        /// Retrieves element on page based off of passed in id. - use $get instead - backwards compat only
        /// </summary>
        /// <param name="tag" type="String">
        /// TagName to retrieve
        /// </param>
        /// <param name="ctl" type="Object" optional="true">
        /// If you wish to narrow down the search, pass in the control whose children you wish to search.
        /// </param>
        /// <returns type="Array" />		
        if (ctl == null)
            ctl = document;
        if (ctl.getElementsByTagName)
            return ctl.getElementsByTagName(tag);
        else if (ctl.all && ctl.all.tags)
            return ctl.all.tags(tag);
        else
            return null;
    },

    getParentByTagName: function(ctl, tag)
    {
        /// <summary>
        /// Retrieves parent element of a particular tag. This function walks up the control's parent references until it locates control of particular tag or parent no longer exists.
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control you wish to start the lookup at
        /// </param>
        /// <param name="tag" type="String">
        /// TagName to of parent control retrieve
        /// </param>
        /// <returns type="Object" />		
        var parent = ctl.parentNode;
        tag = tag.toLowerCase();
        while (parent != null)
        {
            if (parent.tagName && parent.tagName.toLowerCase() == tag)
                return parent;
            parent = parent.parentNode;
        }
        return null;
    },

    getCookie: function(name)
    {
        /// <summary>
        /// Retrieves a cookie
        /// </summary>
        /// <param name="name" type="String">
        /// Name of the desired cookie 
        /// </param>
        /// <returns type="String" />		
        var cookie = " " + document.cookie;
        var search = " " + name + "=";
        var ret = null;
        var offset = 0;
        var end = 0;
        if (cookie.length > 0)
        {
            offset = cookie.indexOf(search);
            if (offset != -1)
            {
                offset += search.length;
                end = cookie.indexOf(";", offset)
                if (end == -1)
                    end = cookie.length;
                ret = unescape(cookie.substring(offset, end));
            }
        }
        return (ret);
    },

    getNonTextNode: function(node)
    {
        /// <summary>
        /// Retrieves first non-text node.  If passed in node is textnode, it looks at each sibling
        /// </summary>
        /// <param name="node" type="Object">
        /// Node to start looking at
        /// </param>
        /// <returns type="Object" />		
        if (this.isNonTextNode(node))
            return node;

        while (node != null && this.isNonTextNode(node))
        {
            node = this.getSibling(node, 1);
        }
        return node;
    },

    addSafeHandler: function(ctl, evt, obj, method)
    {
        /// <summary>
        /// Creates memory safe event handler (closure) over element - use createDelegate instead with dispose - kept for backwards compatibility
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control to attach event on
        /// </param>
        /// <param name="evt" type="String">
        /// Event to attach
        /// </param>
        /// <param name="obj" type="Object">
        /// Instance of object to invoke method on
        /// </param>
        /// <param name="method" type="String">
        /// Method to invoke on object for event 
        /// </param>
        ctl[evt] = this.getObjMethRef(obj, method);

        if (dnn.dom.browser.isType(dnn.dom.browser.InternetExplorer))	//handle IE memory leaks with closures
        {
            if (this.__leakEvts.length == 0)
                dnn.dom.attachEvent(window, 'onunload', dnn.dom.destroyHandlers);

            this.__leakEvts[this.__leakEvts.length] = new dnn.dom.leakEvt(evt, ctl, ctl[evt]);
        }
    },

    destroyHandlers: function()
    {
        /// <summary>
        /// Automatically called (internal) - handles IE memory leaks with closures
        /// </summary>
        var iCount = dnn.dom.__leakEvts.length - 1;
        for (var i = iCount; i >= 0; i--)
        {
            var oEvt = dnn.dom.__leakEvts[i];
            oEvt.ctl.detachEvent(oEvt.name, oEvt.ptr);
            oEvt.ctl[oEvt.name] = null;
            dnn.dom.__leakEvts.length = dnn.dom.__leakEvts.length - 1;
        }
    },

    getObjMethRef: function(obj, methodName)
    {
        /// <summary>
        /// Creates event delegate (closure) - use createDelegate instead with dispose - kept for backwards compatibility
        /// adapted from http://jibbering.com/faq/faq_notes/closures.html (associateObjWithEvent)
        /// </summary>
        /// <param name="obj" type="Object">
        /// Instance of object to invoke method on
        /// </param>
        /// <param name="methodName" type="String">
        /// Method to invoke on object for event 
        /// </param>
        return (function(e) { e = e || window.event; return obj[methodName](e, this); });
    },

    getSibling: function(ctl, offset)
    {
        /// <summary>
        /// Starts at the passed in control and retrieves the sibling that is at the desired offset
        /// </summary>
        /// <param name="ctl" type="Object">
        /// Control in which to find the sibling related to it
        /// </param>
        /// <param name="offset" type="Number">
        /// How many positions removed from the passed in control to look for the sibling. For example if you wanted your immediate sibling below you would pass in 1
        /// </param>
        /// <returns type="Object" />		
        if (ctl != null && ctl.parentNode != null)
        {
            for (var i = 0; i < ctl.parentNode.childNodes.length; i++)
            {
                if (ctl.parentNode.childNodes[i].id == ctl.id)
                {
                    if (ctl.parentNode.childNodes[i + offset] != null)
                        return ctl.parentNode.childNodes[i + offset];
                }
            }
        }
        return null;
    },

    isNonTextNode: function(node)
    {
        /// <summary>
        /// Determines if passed in control is a text node (i.e. nodeType = 3 or 8)
        /// </summary>
        /// <param name="node" type="Object">
        /// Node object to verify
        /// </param>
        /// <returns type="Boolean" />		
        return (node.nodeType != 3 && node.nodeType != 8); //exclude nodeType of Text (Netscape/Mozilla) issue!
    },

    getScript: function(src)
    {
        if (this.scriptElements[src]) //perf
            return this.scriptElements[src];

        var oScripts = dnn.dom.getByTagName('SCRIPT'); //safari has document.scripts
        for (var s = 0; s < oScripts.length; s++) //safari
        {
            if (oScripts[s].src != null && oScripts[s].src.indexOf(src) > -1)
            {
                this.scriptElements[src] = oScripts[s]; //cache for perf
                return oScripts[s];
            }
        }
    },

    getScriptSrc: function(src)
    {
        var resx = dnn.getVar(src + '.resx', '');
        if (resx.length > 0)
            return resx;
        return src;
    },

    getScriptPath: function()
    {
        var oThisScript = dnn.dom.getScript('dnn.js');
        if (oThisScript)
            return oThisScript.src.replace('dnn.js', '');
        var sSP = dnn.getVar('__sp');   //try and get from var
        if (sSP)
            return sSP;
        return '';

    },

    scriptFile: function(src)	//trims off path
    {
        var ary = src.split('/');
        return ary[ary.length - 1];
    },

    loadScript: function(src, text, callBack)
    {
        var sFile;
        if (src != null && src.length > 0)
        {
            sFile = this.scriptFile(src);
            if (this.scripts[sFile] != null)	//already loaded
                return;
        }
        var oSR = new dnn.ScriptRequest(src, text, callBack);
        if (sFile)
            this.scripts[sFile] = oSR;
        oSR.load();
        return oSR;
    },

    loadScripts: function(aSrc, aText, callBack)
    {
        if (dnn.scripts == null)
        {
            var oRef = function(aSrc, aText, callBack) //closure to invoke self with same params when done
            { return (function() { dnn.dom.loadScripts(aSrc, aText, callBack); }); };
            dnn.dom.loadScript(dnn.dom.getScriptPath() + 'dnn.scripts.js', null, oRef(aSrc, aText, callBack));
            //dnn.dom.loadScript(dnn.dom.getScriptPath() + 'dnn.scripts.js', null);
            return;
        }
        var oBatch = new dnn.scripts.ScriptBatchRequest(aSrc, aText, callBack);
        oBatch.load();
    },

    scriptStatus: function(src)
    {
        var sFile = this.scriptFile(src);
        if (this.scripts[sFile])
            return this.scripts[sFile].status; //dynamic load

        var oScript = this.getScript(src);
        if (oScript != null)	//not a dynamic load, must be complete if found
            return 'complete';
        else
            return '';
    },

    setScriptLoaded: function(src)	//called by pages js that is dynamically loaded.  Needed since Safari doesn't support onload for script elements
    {
        var sFile = this.scriptFile(src);
        if (this.scripts[sFile] && dnn.dom.scripts[sFile].status != 'complete')
            dnn.dom.scripts[sFile].complete();
    },

    navigate: function(sURL, sTarget)
    {
        if (sTarget != null && sTarget.length > 0)
        {
            if (sTarget == '_blank')	//todo: handle more
                window.open(sURL);
            else
                document.frames[sTarget].location.href = sURL;
        }
        else
        {
            if (Sys.Browser.agent === Sys.Browser.InternetExplorer)
                window.navigate(sURL);  //include referrer (WCT-8821)
            else
                window.location.href = sURL;
        }
        return false;
    },

    setCookie: function(name, val, days, path, domain, isSecure)
    {
        /// <summary>
        /// Sets a cookie
        /// </summary>
        /// <param name="name" type="String">
        /// Name of the desired cookie to delete
        /// </param>
        /// <param name="val" type="String">
        /// value
        /// </param>
        /// <param name="days" type="Number">
        /// days cookie is valid for
        /// </param>
        /// <param name="path" type="String">
        /// Path for which the cookie is valid
        /// </param>
        /// <param name="domain" type="String">
        /// Domain for which the cookie is valid
        /// </param>
        /// <param name="isSecure" type="Boolean">
        /// determines if cookie is secure
        /// </param>
        /// <returns type="Boolean" />		
        var sExpires;
        if (days)
        {
            sExpires = new Date();
            sExpires.setTime(sExpires.getTime() + (days * 24 * 60 * 60 * 1000));
        }
        document.cookie = name + "=" + escape(val) + ((sExpires) ? "; expires=" + sExpires.toGMTString() : "") +
				((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + ((isSecure) ? "; secure" : "");

        if (document.cookie.length > 0)
            return true;
    },

    //Atlas
    getCurrentStyle: function(node, prop)
    {
        var style = Sys.UI.DomElement._getCurrentStyle(node);
        if (style)
            return style[prop];
        return '';
    },

    getFormPostString: function(ctl)
    {
        var sRet = '';
        if (ctl != null)
        {
            if (ctl.tagName && ctl.tagName.toLowerCase() == 'form')	//if form, faster to loop elements collection
            {
                for (var i = 0; i < ctl.elements.length; i++)
                    sRet += this.getElementPostString(ctl.elements[i]);
            }
            else
            {
                sRet = this.getElementPostString(ctl);
                for (var i = 0; i < ctl.childNodes.length; i++)
                    sRet += this.getFormPostString(ctl.childNodes[i]); //1.3 fix (calling self recursive insead of elementpoststring)
            }
        }
        return sRet;
    },

    getElementPostString: function(ctl)
    {
        var tagName;
        if (ctl.tagName)
            tagName = ctl.tagName.toLowerCase();

        if (tagName == 'input')
        {
            var type = ctl.type.toLowerCase();
            if (type == 'text' || type == 'password' || type == 'hidden' || ((type == 'checkbox' || type == 'radio') && ctl.checked))
                return ctl.name + '=' + dnn.encode(ctl.value, false) + '&';
        }
        else if (tagName == 'select')
        {
            for (var i = 0; i < ctl.options.length; i++)
            {
                if (ctl.options[i].selected)
                    return ctl.name + '=' + dnn.encode(ctl.options[i].value, false) + '&';
            }
        }
        else if (tagName == 'textarea')
            return ctl.name + '=' + dnn.encode(ctl.value, false) + '&';
        return '';
    },

    //OBSOLETE METHODS
    //devreplace
    //this method is obsolete, call nodeElement.appendChild directly
    appendChild: function(oParent, oChild)
    {
        return oParent.appendChild(oChild);
    },

    //this method is obsolete, call nodeElement.parentNode.removeChild directly
    removeChild: function(oChild)
    {
        return oChild.parentNode.removeChild(oChild);
    },

    //devreplace
    //this method is obsolete, call document.createElement directly
    createElement: function(tagName)
    {
        return document.createElement(tagName.toLowerCase());
    }

});   //dnn.dom end

		dnn.dom.leakEvt = function(name, ctl, oPtr)
		{
			this.name = name;
			this.ctl = ctl;
			this.ptr = oPtr;
		}
        dnn.dom.leakEvt.registerClass('dnn.dom.leakEvt');
        

		dnn.dom.eventObject = function(e, srcElement)
		{
			this.object = e;
			this.srcElement = srcElement;
		}
		dnn.dom.eventObject.registerClass('dnn.dom.eventObject');
		
		//--- dnn.dom.browser
		//Kept as is, Atlas detects smaller number of browsers
		dnn.dom.browserObject = function()
		{
			this.InternetExplorer = 'ie';
			this.Netscape = 'ns';
			this.Mozilla = 'mo';
			this.Opera = 'op';
			this.Safari = 'safari';
			this.Konqueror = 'kq';
			this.MacIE = 'macie';
			//this.Chrome = 'chrome';
			

			var type;
			var agt=navigator.userAgent.toLowerCase();

			if (agt.indexOf('konqueror') != -1) 
				type = this.Konqueror;
			else if (agt.indexOf('msie') != -1 && agt.indexOf('mac') != -1)
			    type = this.MacIE;
			//else if (agt.indexOf('chrome') != -1)
			//    type = this.Chrome;
			else if (Sys.Browser.agent === Sys.Browser.InternetExplorer)
			    type = this.InternetExplorer;
			else if (Sys.Browser.agent === Sys.Browser.FireFox)
			    type = this.Mozilla; //this.FireFox;
            else if (Sys.Browser.agent === Sys.Browser.Safari)
                type = this.Safari;
            else if (Sys.Browser.agent === Sys.Browser.Opera)
                type = this.Opera;
            else
                type = this.Mozilla;  
						
			this.type = type;
			this.version = Sys.Browser.version;
			var sAgent = navigator.userAgent.toLowerCase();
			if (this.type == this.InternetExplorer)
			{
				var temp=navigator.appVersion.split("MSIE");
				this.version=parseFloat(temp[1]);
			}
			if (this.type == this.Netscape)
			{
				var temp=sAgent.split("netscape");
				this.version=parseFloat(temp[1].split("/")[1]);	
			}
		}
		
dnn.dom.browserObject.prototype =
{
		toString: function()
		{
			return this.type + ' ' + this.version;
		},
		
		isType: function()
		{
			for (var i=0; i<arguments.length; i++)
			{
				if (dnn.dom.browser.type == arguments[i])
					return true;
			}
			return false;
		}
}	
dnn.dom.browserObject.registerClass('dnn.dom.browserObject');
dnn.dom.browser = new dnn.dom.browserObject();


//-- shorthand functions.  Only define if not already present
if (typeof($) == 'undefined')
{
eval("function $() {var ary = new Array(); for (var i=0; i<arguments.length; i++) {var arg = arguments[i]; var ctl; if (typeof arg == 'string') ctl = dnn.dom.getById(arg); else ctl = arg; if (ctl != null && typeof(Element) != 'undefined' && typeof(Element.extend) != 'undefined') Element.extend(ctl); if (arguments.length == 1) return ctl; ary[ary.length] = ctl;} return ary;}");
}

//image flickering
try {document.execCommand("BackgroundImageCache", false, true);} catch(err) {}

Sys.Application.add_load(dnn._onload); 
