// HTML5 text/html .innerHTML getter function (with no optimizations)
// Also see <http://shadow2531.com/js/getInnerHTML5clean_faster.js>
(function() {

window.addEventListener("load", function() {
    var start = new Date().getTime();
    var markup = getInnerHTML(document);
    var end = new Date().getTime();
    alert(end - start + "ms");
    alert(markup);
}, false);

function escapeTextNodeValue(data) {
    return data.replace(/(<)|(>)|(&)|(\u00A0)/g, function(m, a, b, c) {
        if (a) return "&lt;";
        if (b) return "&gt;"
        if (c) return "&amp;";
        return "&nbsp;"
    });
}

function escapeAttrNodeValue(value) {
    return value.replace(/(&)|(")|(\u00A0)/g, function(m, a, b) {
        if (a) return "&amp;";
        if (b) return "&quot;";
        return "&nbsp;";
    });
}

function isVoidElement(element) {
     return (/^area$|^base$|^basefont$|^bgsound$|^br$|^col$|^embed$|^frame$|^hr$|^img$|^input$|^link$|^meta$|^param$|^spacer$|^wbr$/i).test(element.tagName);
}

function needsNewlinePrefix(element) {
    return (/^pre$|^textarea$|^listing$/i).test(element.tagName);
}

function isCDATA(node) {
    for (var p = node.parentNode; p; p = p.parentNode) {
        if ((/^style$|^script$|^xmp$|^iframe$|^noembed$|^noframes$|^noscript$|^plaintext$/i).test(p.tagName)) {
            return true;
        }
    }
    return false;
}

function getAttrHTML(attr) {
    var markup = attr.name.toLowerCase();
    markup += '="';
    markup += escapeAttrNodeValue(attr.value);
    markup += '"';
    return markup;
}

function getInnerHTML(node) {
    var markup = "";
    if (node.hasChildNodes()) {
        var current = node.firstChild;
        for ( ; ; ) {
            switch (current.nodeType) {
                case 1:
                    markup += "<";
                    markup += current.tagName.toLowerCase();
                    var attrs = current.attributes;
                    for (var i = 0; i < attrs.length; ++i) {
                        markup += " ";
                        markup += getAttrHTML(attrs[i]);
                    }
                    markup += ">";
                    if (needsNewlinePrefix(current)) {
                       markup += "\n";
                    }
                    break;
                case 3: case 4:
                    markup += isCDATA(current) ? current.data : escapeTextNodeValue(current.data);
                    break;
                case 7:
                    markup += "<!--?";
                    markup += current.target;
                    markup += current.data;
                    // text/html PIs don't have the ending ?
                    markup += "-->";
                    break;
                case 8:
                    markup += "<!--";
                    markup += current.data;
                    markup += "-->";
                    break;
                case 10:
                    markup += "<!DOCTYPE ";
                    markup += current.name.toLowerCase()
                    markup += ">";
                    break;
                default:
                    throw "INVALID_STATE_ERR";
            }
            if (current.firstChild) {
                current = current.firstChild;
            } else {
                var done = false;
                for ( ; ; ) {
                    if (current.nodeType === 1 && !isVoidElement(current)) {
                        markup += "</";
                        markup += current.tagName.toLowerCase();
                        markup += ">";
                    }
                    if (current === node.lastChild) {
                        done = true;
                        break;
                    } else if (current.nextSibling) {
                        current = current.nextSibling;
                        break;
                    } else {
                        current = current.parentNode;
                    }
                }
                if (done) {
                    break;
                }
            }
        }
    }
    return markup;
}
})();
