// XHTML library
// Adds functions innerXHTML(el) and outerXHTML(el),
// and, where supported (Firefox, Safari), adds
// .innerXHTML() and .outerXHTML() to element prototypes
//
// Version: 0.2
// 
// Author: Jon Davis <jon@jondavis.net>
// Please contact the author for feedback.
//

if (!window.xhtmljs) {
    window.xhtmljs = {

        innerXHTML: function (el, strict) {
            if (!el) return "";
            var e;
            var ret = '';
            for (e=0; e<el.childNodes.length; e++) {
                var childNode = el.childNodes.item(e);
                ret += window.xhtmljs.outerXHTML(childNode, strict);
                if (xhtmlFormatting == "formatted" &&
                    !childNode.tagName && 
                  e < el.childNodes.length - 1 && 
                  el.childNodes[e+1].tagName && 
                  window.xhtmljs.tagIsLineBreaking(el.childNodes[e+1].tagName)) {
                    ret += "\n";
                }
            }
            return ret.replace(/\s*\n\s*\n/g, '\n');
        },

        outerXHTML: function (el, strict, tab) {
            var tagName = el.tagName;
            var attributes = el.attributes;
            var childNodes = el.childNodes;
            var supportsInlineTerminator = true;
            
            if (tagName) {
                switch(tagName.toLowerCase()) {
                    case "script":
                    case "link":
                        supportsInlineTerminator = false;
                        break;
                    default: 
                        supportsInlineTerminator = true;
                        break;
                }
            }
            
            var ret = "";
            var t;
            if (!tab) tab = 0;
            for (t=0; t<tab; t++) {
                ret += "\t";
            }
            ret += "<";
            if (tagName && (!strict || window.xhtmljs.tagInXhtml(tagName.toLowerCase()))) {
                var i;
                ret += tagName.toLowerCase();
                //debugger;
                if (attributes) {
                    var styleTagUsed = false;
                    for (i=0; i<attributes.length; i++) {
                        var attribName = attributes.item(i).nodeName;
                        var attribValue = attributes.item(i).value;
                        if (attribName == "href") {
                            //debugger;
                        }
                        if (attribValue != null &&
                            attribValue != "null" &&
                            attribValue.trim() != "" &&
                            attribValue != "inherit" &&
                            (!strict || window.xhtmljs.attribInXhtml(tagName, attribName, attribValue))) {
                            if (attribName.toLowerCase() == "style") {
                                styleTagUsed = true;
                            }
                            ret += " " + attribName.toLowerCase()
                                + "=\"" + window.xhtmljs.xmlEncode(attribValue)
                                + "\"";
                        }
                    }
                    if (!styleTagUsed && window.xhtmljs.attribInXhtml(tagName, "style") &&
                        el.style.cssText) {
                        var styleText = el.style.cssText;
                        var altStyleText = "";
                        var styleElements = styleText.split(/\;/);
                        for (s=0; s<styleElements.length; s++) {
                            var styleElement = styleElements[s].split(/:/);
                            altStyleText += styleElement[0].toLowerCase()
                                + ":" 
                                + styleElement[1]
                                + ";";                  
                        }
                        //ret += " style=\"" + styleText + "\"";
                        ret += " style=\"" + altStyleText + "\"";
                    }
                }
                if (childNodes && childNodes.length > 0) {
                    ret += ">";
                    var childTags = false;
                    var prevWasTag = false;
                    for (i=0; i<childNodes.length; i++) {
                        var tabv = tab+1;
                        var cv = "";
                        if (!childNodes.item(i).tagName) {
                            tabv = null;
                        } else {
                            if (xhtmlFormatting == "formatted" &&
                                childNodes.item(i).childNodes && 
                                childNodes.item(i).childNodes.length > 0){
                                cv = "\n";
                                childTags = true;
                            } else {
                                tabv = null;
                            }
                        }
                        cv += window.xhtmljs.outerXHTML(childNodes.item(i), strict, tabv);
                        if (xhtmlFormatting == "formatted" &&
                          cv.substr(0, 1) == "<" && prevWasTag) {
                            ret += "\n";
                        } 
                        ret += cv;
                        if (xhtmlFormatting == "formatted" && (
                          childNodes.item(i).tagName != undefined &&
                          childNodes.item(i).tagName.toString() != "") && 
                          window.xhtmljs.tagIsLineBreaking(childNodes.item(i).tagName)) {
                            ret += "\n";
                            var t;
                            for (t=0; t<tab; t++) {
                                ret += "\t";
                            }
                        }
                        prevWasTag = (childNodes.item(i).tagName != undefined);
                    }
                    if (xhtmlFormatting == "formatted" && childTags) {
                        ret += "\n";
                        for (i=0; i<tab;i++) {
                            ret += "\t";
                        }
                    } 
                    ret += "</" + tagName.toLowerCase() + ">";
                } else {
                    if (supportsInlineTerminator) {
                        ret += " />";
                    } else {
                        ret += ">";
                        if (xhtmlFormatting == "formatted") {
					        ret += "\n";
					        for (i=0; i<tab;i++) {
						        ret += "\t";
					        }
				        }
                        ret += "</" + tagName.toLowerCase() + ">";
                    }
                }
                if (xhtmlFormatting == "formatted" && tagName && 
                  tagName.toLowerCase() != "br" && window.xhtmljs.tagIsLineBreaking(tagName)) {
                    ret += "\n";
                    for (i=0; i<tab;i++) {
                        ret += "\t";
                    }
                }
                return ret.replace(/\s*\n(\t*)\s*\n/g, '\n$1');
            } else {
                // text
                if (el.nodeValue) {
                    return el.nodeValue.trim(); 
                } else {
                    //debugger;
                    if (el.toString().indexOf("[object") != 0) {
                        return el.toString().trim(); 
                    } else {
                        return "";
                    }
                }
            }    
        },

        xmlAttributeEncode: function (str) { // todo
            return xmlEncode(str);
        },

        xmlEncode: function (str) { // todo
            return str.replace(/&/g, "&amp;").replace(/\"/g, "&quot;");
        },

        tagIsLineBreaking: function (tagName) {
            if (!tagName) return false;
            switch (tagName.toLowerCase()) {
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                case "br":
                case "p":
                case "div":
                case "table":
                case "blockquote":
                case "ul":
                case "ol":
                    return true;
            }
        },

        tagInXhtml: function (tag) {
            switch(tag.toLowerCase()) {
                case "html":
                case "head":
                case "base":
                case "meta":
                case "title":
                case "script":
                case "style":
                case "link":
                case "body":
                
                case "iframe":
                case "frameset":
                case "frame":
                case "a":
                case "area":
                case "b":
                case "big":
                case "blockquote":
                case "br":
                case "center":
                case "cite":
                case "code":
                case "col":
                case "colgroup":
                case "dd":
                case "del":
                case "div":
                case "dl":
                case "em":
                case "font":
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                case "hr":
                case "i":
                case "iframe":
                case "img":
                case "ins":
                case "li":
                case "map":
                case "ol":
                case "p":
                case "pre":
                case "q":
                case "span":
                case "small":
                case "strong":
                case "sub":
                case "sup":
                case "table":
                case "tbody":
                case "td":
                case "tfoot":
                case "th":
                case "thead":
                case "tr":
                case "u":
                case "ul":
                    return true;
                default:
                    return false;
            }
        },

        attribInXhtml: function (tag, attrib, value) {
            //if (attrib != attrib.toLowerCase()) return false;
            attrib = attrib.toLowerCase();
            if (attrib == "disabled" && value == "false") return false;
            
            switch (attrib) {
            
                // core attributes
                case "id":
                case "class":
                case "style":
                case "title":
                    return window.xhtmljs.tagInXhtml(tag);
            
                // event attributes
                case "onclick":
                case "ondblclick":
                case "onmousedown":
                case "onmouseup":
                case "onmouseover":
                case "onmousemove":
                case "onmouseout":
                case "onkeydown":
                case "onkeypress":
                case "onkeyup":
                    return window.xhtmljs.tagInXhtml(tag);
            
                // language attributes
                case "lang":
                    return window.xhtmljs.tagInXhtml(tag);
                case "dir":
                    switch (value) {
                        case "ltr":
                        case "rtl":
                            return window.xhtmljs.tagInXhtml(tag);
                        default:
                            return false;
                    }
                    break;
                    
                // keyboard attributes
                case "accesskey":
                case "tabindex":
                    if (value == "0") return false;
                    switch (tag.toLowerCase()) {
                        case "br":
                            return false;
                        default:
                            return window.xhtmljs.tagInXhtml(tag);
                    }
            }
            switch(tag.toLowerCase()) {
                case "a":
                    switch (attrib) {
                        case "accesskey":
                        case "charset":
                        case "coords":
                        case "href":
                        case "hreflang":
                        case "name":
                        case "rel":
                        case "rev":
                        case "shape":
                        case "tabindex":
                        case "target":
                        case "type":
                            return true;
                        default: 
                            return false;
                    }
                case "area":
                    switch (attrib) {
                        case "alt":
                        case "coords":
                        case "href":
                        case "nohref":
                        case "shape":
                        case "target":
                            return true;
                        default: 
                            return false;
                    }
                case "b":
                case "big":
                case "br":
                case "center":
                case "cite":
                case "code":
                case "dd":
                case "dl":
                case "em":
                case "i":
                case "small":
                case "strong":
                case "sub":
                case "sup":
                case "u":
                    return false;
                case "blockquote":
                    switch (attrib) {
                        case "cite":
                            return true;
                        default: 
                            return false;
                    }
                case "col":
                case "colgroup":
                    switch (attrib) {
                        case "align":
                        case "char":
                        case "charoff":
                        case "span":
                        case "valign":
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "del":
                    switch (attrib) {
                        case "cite":
                        case "datetime":
                            return true;
                        default: 
                            return false;
                    }
                case "font":
                    switch (attrib) {
                        case "color":
                        case "face":
                        case "size":
                            return true;
                        default: 
                            return false;
                    }
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                    switch (attrib) {
                        case "align":
                            return true;
                        default: 
                            return false;
                    }
                case "hr":
                    switch (attrib) {
                        case "align":
                        case "noshade":
                        case "size":
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "iframe": // todo
                    return true;
                case "img":
                    switch (attrib) {
                        case "align":
                        case "alt":
                        case "border":
                        case "height":
                        case "hspace":
                        case "ismap":
                        case "longdesc":
                        case "src":
                        case "usemap":
                        case "vspace":
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "ins":
                    switch (attrib) {
                        case "cite":
                        case "datetime":
                            return true;
                        default: 
                            return false;
                    }
                case "li":
                    switch (attrib) {
                        case "type":
                        case "value":
                            return true;
                        default: 
                            return false;
                    }
                case "map":
                    switch (attrib) {
                        case "name":
                            return true;
                        default: 
                            return false;
                    }
                case "ol":
                    switch (attrib) {
                        case "compact":
                        case "start":
                        case "type":
                            return true;
                        default: 
                            return false;
                    }
                case "p":
                    switch (attrib) {
                        case "align":
                            return true;
                        default: 
                            return false;
                    }
                case "pre":
                    switch (attrib) {
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "q":
                    switch (attrib) {
                        case "cite":
                            return true;
                        default: 
                            return false;
                    }
                case "table":
                    switch (attrib) {
                        case "align":
                        case "bgcolor":
                        case "border":
                        case "cellpadding":
                        case "cellspacing":
                        case "frame":
                        case "rules":
                        case "summary":
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "thead":
                case "tbody":
                case "tfoot":
                    switch (attrib) {
                        case "align":
                        case "char":
                        case "charoff":
                        case "valign":
                            return true;
                        default: 
                            return false;
                    }
                case "td":
                case "th":
                    switch (attrib) {
                        case "abbr":
                        case "align":
                        case "axis":
                        case "bgcolor":
                        case "char":
                        case "charoff":
                        case "colspan":
                        case "headers":
                        case "height":
                        case "nowrap":
                        case "rowspan":
                        case "scope":
                        case "valign":
                        case "width":
                            return true;
                        default: 
                            return false;
                    }
                case "tr":
                    switch (attrib) {
                        case "align":
                        case "bgcolor":
                        case "char":
                        case "charoff":
                        case "valign":
                            return true;
                        default: 
                            return false;
                    }
                case "ul":
                    switch (attrib) {
                        case "compact":
                        case "type":
                            return true;
                        default: 
                            return false;
                    }
                default:
                    return false;
            }
        }
    }
}

if (!window.xhtmlFormatting) {
	window.xhtmlFormatting = "formatted";
}

if (String.prototype && !String.prototype.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    }
}

if (!window.outerXHTML) {
    window.outerXHTML = window.xhtmljs.outerXHTML;
}

if (!window.innerXHTML) {
    window.innerXHTML = window.xhtmljs.innerXHTML;
}

// =======================================================================

// add to HTMLElement prototype (not supported in IE)
if (!document.all && HTMLElement && HTMLElement.prototype) {
    HTMLElement.prototype.innerXHTML = function() {
        return window.xhtmljs.innerXHTML(this, true);
    }
    HTMLElement.prototype.outerXHTML = function() {
        return window.xhtmljs.outerXHTML(this, true);
    }
}

// add to jQuery prototype
if (window.jQuery) {
    window.jQuery.fn.xhtml = function(newXhtml) {
        if (newXhtml) {
            return this.html(newXhtml);
        } else {
            var i;
            var ret = '';
            for (i=0; i<this.length; i++) {
                if (i>0) i += "\n";
                ret += window.xhtmljs.innerXHTML(this[i], true);
            }
            return ret;
        }
    }
}



