function csiExec(el, recursive) {
    var attribs = el.attributes;
    var i;
    if(attribs) {
        switch(el.tagName.toLowerCase()) {
            
            
            case "span": 
            case "div": 
            case "textarea": 
            case "input": 
            // use injectProp="value" for this one
            //case .. : add more tags here as you please
            
            var j,
            d;
            for(i = 0; i < attribs.length; i ++ ) {
                if(attribs[i].name == "src") {
                    d = false;
                    for(j = 0; j < attribs.length; j ++ ) {
                        if(attribs[j].name == "csiLoadStatus" && (attribs[j].value == "loading" || attribs[j].value == "loaded")) {
                            
                            d = true;
                            break;
                            // from nested loop
                            
                        }
                    }
                    if( ! d) {
                        csiLoad(el, attribs[i].value);
                    }
                }
            }
            break;
            // from switch..case
            
        }
    }
    if(recursive) {
        for(i = 0; i < el.childNodes.length; i ++ ) {
            csiExec(el.childNodes[i], recursive);
        }
    }
}
function csiFindElementByCsiId(rootEl, csiId) {
    return findElementByAttributePair(rootEl, "csiid", csiId);
}
function findElementByAttributePair(rootEl, attribName, attribValue) {
    var attribs = el.attributes;
    var i;
    if(attribs) {
        for(i = 0; i < attribs.length; i ++ ) {
            if(attribs[i].name == attribName && attribs[i].value == attribValue) {
                return el;
            }
        }
    }
    if(el.childNodes) {
        for(i = 0; i < el.childNodes.length; i ++ ) {
            var ret = findElementByAttributePair(el.childNodes[i], attribName, attribValue);
            if(ret)
                return ret;
        }
    }
    return null;
}
function csiLoad(el, src) {
    el.attributes.setNamedItem(csiMakeAttribute("csiLoadStatus", "loading"));
    var httpRequest;
    var antiCacheMethod = csiGetAntiCacheMethod(el);
    if(antiCacheMethod == "querystring") {
        var dt = new Date();
        var dts = "" + dt.getFullYear() + dt.getMonth() + dt.getDate() + dt.getHours() + dt.getMinutes() + dt.getSeconds();
        if(src.indexOf("?") == -1)
            src += "?nocachetimestamp=" + dts;
        else src += "&nocachetimestamp=" + dts;
    }
    if(window.XMLHttpRequest) {
        // Mozilla, Safari, ...
        httpRequest = new XMLHttpRequest();
        if(httpRequest.overrideMimeType) {
            httpRequest.overrideMimeType("text/plain");
        }
    }  else
    if(window.ActiveXObject) {
        // IE
        try {
            httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e) {
            try {
                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(e) {}
        }
    }
    
    if( ! httpRequest) {}  else {
        httpRequest.onreadystatechange = function() {
            if(httpRequest.readyState == 4 && httpRequest.status == 200) {
                
                var newHtml = httpRequest.responseText;
                
                // This might be a full-blown HTML document; 
                // we should never pull in anything more than what's inside 
                // the <body> tag
                
                var cropBody = csiGetCropBody(el);
                if(cropBody)
                    newHtml = csiGetInnerHtmlOfTag(newHtml, "body");
                
                var injProp = csiGetInjectProp(el);
                
                switch(injProp) {
                    case "innerHTML": 
                    case "inner": 
                    case "contents": 
                    case "content": el.innerHTML = newHtml;
                    //csiReplaceHtml(el, newHtml);
                    
                    break;
                    
                    case "value": el.value = newHtml;
                    break;
                    
                    // this kind of replace really only works differently from innerHTML 
                    // when using Internet Explorer which allows you to swap out the
                    // entire tag's HTML
                    
                    case "outerHTML": 
                    case "outer": 
                    case "replace": 
                    //if (el.outerHTML) { // MSIE
                    //    el.outerHTML = newHtml;
                    //}
                    //else {
                    el = csiReplaceHtml(el, newHtml);
                    //}
                    
                    break;
                    
                }
                //Check for more src=".."
                //Only supported on innerHTML inserts, not outerHTML
                csiExec(el, true);
            }
        };
    }
    httpRequest.open("GET", src, true);
    httpRequest.send("");
    
    el.attributes.setNamedItem(csiMakeAttribute("csiLoadStatus", "loaded"));
}
function csiGetCropBody(el) {
    var defaultSetting = "on";
    var a;
    var attribs = el.attributes;
    if( ! attribs)
        return false;
    for(a = 0; a < attribs.length; a ++ ) {
        if(attribs[a].name.toLowerCase() == "cropbody") {
            return attribs[a].value.toLowerCase() == "on" || attribs[a].value.toLowerCase() == "yes" || attribs[a].value.toLowerCase() == "true" || attribs[a].value.toLowerCase() == "1";
        }
    }
    return defaultSetting == "on";
}
function csiGetAntiCacheMethod(el) {
    var defaultSetting = "disabled";
    if(el.tagName.toLowerCase() == "input")
        defaultSetting = "querystring";
    var a;
    var attribs = el.attributes;
    if( ! attribs)
        return false;
    for(a = 0; a < attribs.length; a ++ ) {
        if(attribs[a].name.toLowerCase() == "anticache") {
            return attribs[a].value.toLowerCase();
        }
    }
    return defaultSetting;
}
function csiGetInjectProp(el) {
    var defaultProp = "innerHTML";
    // Defaulting this causes problems!
    // if (el.tagName.toLowerCase() == "input") defaultProp = "value";   
    
    if(el.tagName.toLowerCase() == "input")
        defaultProp = "none";
    
    var a;
    var attribs = el.attributes;
    if( ! attribs)
        return "outerHTML";
    for(a = 0; a < attribs.length; a ++ ) {
        if(attribs[a].name.toLowerCase() == "apply") {
            return attribs[a].value;
        }
    }
    return defaultProp;
}
function csiGetInnerHtmlOfTag(str, tagName) {
    
    if(str.toLowerCase().indexOf("<" + tagName) > -1 && str.toLowerCase().indexOf("</" + tagName + ">", str.toLowerCase().indexOf("<" + tagName)) > -1) {
        
        var startTagStart = str.toLowerCase().indexOf("<" + tagName);
        var startTagEnd = str.indexOf(">", startTagStart);
        var endTagStart = str.toLowerCase().indexOf("</" + tagName, startTagEnd);
        
        var ret = str.substring(startTagEnd + 1, endTagStart - 1);
        return ret;
        
    }  else return str;
}
function csiMakeAttribute(attName, attValue) {
    var objAttribute = document.createAttribute(attName);
    objAttribute.value = attValue;
    return objAttribute;
}
function dtdExtensionsCleanup() {
    // tested on MSIE 6 & 7, Safari 3, Firefox 2
    
    if((document.body.innerHTML.replace(/ /g, '').replace(/\n/g, "").substr(0, 5) == "]&gt;") || (document.body.innerHTML.substr(0, 11) == "<!--ATTLIST" || document.body.innerHTML.substr(0, 11) == "<!--ELEMENT")) {
        var subStrStartIndex = document.body.innerHTML.indexOf("&gt;", document.body.innerHTML.indexOf("]"));
        var subStrHtml = document.body.innerHTML.substring(subStrStartIndex + 4);
        document.body.innerHTML = subStrHtml;
    }  else {
        // Opera 9.23 "just works"
        
    }
}

function csiAutoExec() {
    dtdExtensionsCleanup();
    csiExec(document.documentElement, true);
}
//--------------------------------------------------------------------------------------------------
function csiReplaceHtml(el, html, wrap) {
    if( ! wrap)
        wrap = false;
    var oldEl = (typeof el === "string" ? document.getElementById(el): el);
    var tagName = oldEl.nodeName;
    if(el.attributes && el.attributes["wraptag"])
        tagName = el.attributes["wraptag"].value;
    var newEl = document.createElement(tagName);
    // Preserve the element's id and class (other properties are lost)
    newEl.id = oldEl.id;
    newEl.className = oldEl.className;
    // Replace the old with the new
    newEl.innerHTML = html;
    oldEl.parentNode.replaceChild(newEl, oldEl);
    //        if (newEl.childNodes.length == 1) {
    //        	newEl = newEl.childNodes[0];
    //        	oldEl.parentNode.replaceChild(newEl, oldEl);
    //        } else {
    //        	var i;
    //        	var par = oldEl.parentNode;
    //        	var prev;
    //        		debugger;
    //        	for (i=newEl.childNodes.length-1; i>=0; i--) {
    //        		if (!prev) {
    //        			prev = par.replaceChild(newEl.childNodes[i], oldEl);
    //        		} else {
    //        			par.insertBefore(newEl.childNodes[i], prev); // doesn't work, causes weird error
    //        		}
    //        	}
    //        }
    
    /* Since we just removed the old element from the DOM, return a reference
        to the new element, which can be used to restore variable references. */
    return newEl.parentNode;
};

function csiAttachEvent(obj, evt, fnc, useCapture) {
    if( ! useCapture)
        useCapture = false;
    if(obj.addEventListener) {
        obj.addEventListener(evt, fnc, useCapture);
        return true;
    }  else
    if(obj.attachEvent)
        return obj.attachEvent("on" + evt, fnc);
    else {
        csiNsAttachEvent(obj, evt, fnc);
        obj['on' + evt] = function() {
            csiFireEvent(obj, evt)
            };
    }
}
//The following are for browsers like NS4 or IE5Mac which don't support either
//attachEvent or addEventListener
function csiNsAttachEvent(obj, evt, fnc) {
    if( ! obj.myEvents)
        obj.myEvents = {};
    if( ! obj.myEvents[evt])
        obj.myEvents[evt] = [];
    var evts = obj.myEvents[evt];
    evts[evts.length] = fnc;
}
function csiFireEvent(obj, evt) {
    if( ! obj || !obj.myEvents || !obj.myEvents[evt])
        return;
    var evts = obj.myEvents[evt];
    for(
    var i = 0, len = evts.length; i < len; i ++ )
        evts[i]
    ();
}
//--------------------------------------------------------------------------------------------------

// Bind to window load.
csiAttachEvent(window, "load", csiAutoExec, false);
