<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="xml.xsl"?>
<!--
 |
 | displaying xml source in various format (html, text)
 | frederic.glorieux@ajlsm.com
 |
 | fast test, apply this to itself
 |
 | usage :
 |   as a root xsl matching all elements
 |   as an import xsl to format some xml
 |     with <xsl:apply-templates select="node()" mode="xml:html"/>
 |     in this case you need to copy css and js somewhere to link with
 |
 | features :
 |   DOM compatible hide/show
 |   double click to expand all
 |   old browser compatible
 |   no extra characters to easy copy/paste code
 |   html formatting oriented for logical css
 |   commented to easier adaptation
 |   all xmlns:*="uri" attributes in root node
 |   text reformating ( xml entities )
 |
 | problems :
 |   <![CDATA[ node ]]> can't be shown (processed by xml parser before xsl transformation)
 |
 | TODOs
 |
 | - an edit mode
 |
 +-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
    <xsl:output indent="no" method="xml" cdata-section-elements="cdata"/>
    <xsl:param name="pre" select="false()"/>
    <xsl:param name="text-replace" select="true()"/>
    <xsl:param name="val-replace" select="true()"/>
    <xsl:param name="inlines" select="' emph hilite strong b i a '"/>
    <xsl:param name="pres" select="' pre script style logic documentation originalExpression '"/>
    <xsl:param name="blocks" select="' p para '"/>
    <xsl:param name="hides" select="' hide '"/>
    <xsl:param name="cdatas" select="concat(' cdata ', normalize-space(/*/*/@cdata-section-elements), ' ')"/>
    <xsl:param name="action"/>
    <xsl:param name="back"/>
    <xsl:variable name="xml:search-atts" select="document('')/*/xsl:template[@name='search-atts']/search"/>
    <!--
     |    specific formating for some nodes (local-name only)
     |-->
    <!-- style -->
    <xsl:template name="xml:style">
        <xsl:text xml:space="preserve">
            /*
            body css properties are applied to operators

            nodes have a class :
                processing-instruction, comment,
                text, attribute value
                tag name, attribute name
            tag+atts are included in el class
             */

            .xml_pi
                {color:#000080; font-weight: bold;}
            .xml_rem
                {  margin:0; padding:0; white-space:pre;
                 font-size:90%;
                font-family: monospace, sans-serif;
                color:#666666; background:#FFFFF0; font-weight: 100; }
            .xml_text, .xml_val, .xml_cdata
                { font-size:100%; font-weight:bold; color:#555555; 
                margin:0; padding:0;
                font-family:monospace, sans-serif; 
                background:#F5F5F5;
                }
            .xml_cdata {color:red}
            .xml_name
                {color:#CC3333; font-weight:900; font-family: Verdana, sans-serif; }
            .xml_att
                {color:#008000;font-weight:100; font-family: Verdana, sans-serif; }
            .xml_tag
                { white-space:nowrap;}
            .xml_ns
                { color:#000080; font-weight:100;}
            .xml_margin
                { display:block; margin-left:20px; padding-left:10px; border-left: 1px dotted;
                margin-top:0px; margin-bottom:0px; margin-right:0px; }

            /* 
             *  global formatting 
             * 
             *  those classes could be used inline or as block
             *  so formatting is decide by an xml class container
             */
            .xml
                { display:block; 
                font-family: "Lucida Console", monospace, sans-serif; font-size: 80%;
                color:Navy; margin-left:2em; }
            .xml pre { margin:0 }
            .xml a {text-decoration:none; }
            .xml a:hover {text-decoration: underline; }
            .xml a:active, .xml a:active *  {background:#CC3333;color:#FFFFFF;}
            .xml a {text-decoration:none; }
            .swap_hide { color:blue; text-decoration:underline; }

        </xsl:text>
    </xsl:template>
    <!--
     |    ROOT
     |-->
    <!-- low priority on root node in case of include -->
    <xsl:template name="xml:root" match="/" priority="-2">
        <xsl:param name="current" select="."/>
        <xsl:param name="title" select="'XML - source'"/>
        <html>
            <head>
                <title>
                    <xsl:value-of select="$title"/>
                </title>
                <script type="text/javascript">
                    <xsl:call-template name="xml:swap.js"/>
    var SWAP_CLASS='xml_margin';


    function save(text, fileName)
    {
        log="";
        if (document.execCommand)
        {
            if (!document.frames[0]) return log + "no frame to write in";
            win=document.frames[0];
            win.document.open("text/html", "replace");
            win.document.write(text);
            win.document.close();
            win.focus();
            win.document.execCommand('SaveAs', fileName);
        }
     /*
     
     function writeToFile(fileName,text) {
 
netscape.security.PrivilegeManager.enablePrivilege('UniversalFileAccess');
    var fileWriter = new java.io.FileWriter(fileName);
    fileWriter.write (text, 0, text.length);
    fileWriter.close();
}

if (document.layers) {
    writeToFile('file.txt','Hello World');
}
		*/
    }

    function dom(xml)
    {
        if (window.DOMParser) return (new DOMParser()).parseFromString(xml, "text/xml");
        else if (window.ActiveXObject)
        {
            var doc = new ActiveXObject("Microsoft.XMLDOM");
            doc.async = false;
            doc.loadXML(xml);
            return doc;
        }
        else
        {
            alert(NOXML);
            return null;
        }
    }
    
    function xml_save()
    {
        if (!document.getElementById) return false;
        o=document.getElementById('edit');
        if (!o.innerText) return false; 
        var edit=o.contentEditable; 
        o.contentEditable=false; 
        var xml=o.innerText; 
        o.contentEditable=edit; 
        var doc=dom(xml); 
        if (!doc || !doc.documentElement) if (!confirm('The document you want to save is not well-formed. Continue ?')) return false;  
        save(xml)
    }
    
    function xml_edit(button, edit)
    {
        if (!document.getElementById) return false;
        if (document.body.isContentEditable==null) return false;
        var o=document.getElementById('edit'); 
        if(!o) return false; 
        if (edit == null) edit=!o.isContentEditable; 
        if (edit) { 
            if (button) button.className='but_in'; 
            o.contentEditable=true;
            document.cookie="edit=true";
        } 
        else { 
            if (button) button.className=''; 
            o.contentEditable=false; 
            document.cookie="edit=false";
        }
    }

    function xml_submit(form)
    {
        if (!form) form=document.forms[0];
        if (!document.getElementById) return false;
        o=document.getElementById('edit'); 
        if (!o.innerText) return false; 
        var edit=o.contentEditable; 
        o.contentEditable=false; 
        var xml=o.innerText; 
        o.contentEditable=edit; 
        var doc=dom(xml); 
        if (!doc || !doc.documentElement) if (!confirm('The document you want to upload is not well-formed. Continue ?')) return false;  
        if (!form || !form.xml) return false;
        form.xml.value=xml;
        if(form.url) form.url.value=window.location.href;
        return true;
    }
    
    function edit_key(o)
    {
        if (!event) return;
        var key=event.keyCode;
        if (!o) o=document.getElementById('edit');
        if (!o.isContentEditable) return true;
        if (key==9) {
            if (event.shiftKey) document.execCommand("Outdent"); 
            else document.execCommand("Indent"); 
            window.event.cancelBubble = true;
            window.event.returnValue = false;
            return false;
        }
        if (key==83) {
            if (!event.ctrlKey) return;
            xml_save();
            window.event.cancelBubble = true;
            window.event.returnValue = false;
            return false;
        }
        if (key==85) {
            if (!event.ctrlKey) return;
            form=document.forms[0];
            if (!form) return;
            if (xml_submit(form)) form.submit();
            window.event.cancelBubble = true;
            window.event.returnValue = false;
            return false;
        }
    }
    
    function xml_load()
    {
        if (!document.getElementById || !document.execCommand || document.body.isContentEditable==null) return false; 
        var o=document.getElementById('bar'); 
        if (!o || !o.style) return; 
        o.style.display='';
        xml_edit(document.forms[0].butEdit, (document.cookie.search("edit=true") != -1));
    }

                </script>
                <style type="text/css">
                    <xsl:call-template name="xml:style"/>
                    .but_over {}
                    .but_in {border:inset 2px; color:red; }
                </style>
            </head>
            <body onload="xml_load()" ondblclick="swap_all(this)">
                <form id="bar" style="margin:0; display:none;" onsubmit="return xml_submit(this); ">
                    <xsl:if test="$action">
                        <xsl:attribute name="action">
                            <xsl:value-of select="$action"/>
                        </xsl:attribute>
                        <xsl:attribute name="method">post</xsl:attribute>
                    </xsl:if>
                    <iframe id="save" style="display:none">nothing</iframe>
                    <xsl:if test="$back">
                        <input type="hidden" name="back" value="{$back}"/>
                        <button accesskey="b" type="button" onclick="window.location.href=this.form.back.value">
                            <u>B</u>ack
                    </button>
                    </xsl:if>
                    <button name="butEdit" type="button" accesskey="e" onclick="xml_edit(this)">
                        <u>E</u>dit</button>
                    <button type="button" accesskey="s" onclick="xml_save()">
                        <u>S</u>ave</button>
                    <xsl:if test="$action">
                        <button type="submit" accesskey="u">
                            <u>U</u>pload</button>
                    </xsl:if>
                    <input type="hidden" name="url"/>
                    <textarea name="xml" cols="1" rows="1" style="width:1px; height:1px">nothing</textarea>
                </form>
                <div class="xml" id="edit" onkeydown="edit_key(this)">
                    <div class="xml_pi">&lt;?xml version="1.0"?&gt;</div>
                    <xsl:apply-templates select="$current" mode="xml:html"/>
                </div>
            </body>
        </html>
    </xsl:template>
    <!-- script -->
    <xsl:template name="xml:swap.js">
        /*<cdata>
            <xsl:text disable-output-escaping="yes"><![CDATA[  */
/*    
    if (!document.getElementById)
    {
        html='';
        html +='<style type="text/css">';
        html +='.'+ SWAP_CLASS + ' {display:block;}';
        html +='</style>';
        document.write(html);
    }
*/
    var SWAP_CLASS='swap';
    var SWAP_SRC_PREFIX='src';
    var SWAP_DEBUG=true;
    function swap_one(id, src, mode)
    {
        var log='swap_one ';
        if (!document.getElementById) return false; // log + 'no dom ';
        tgt=document.getElementById(id);
        if (!tgt) return false; // log+' not find ' ;
        log+=' #'+tgt.id+' ';
        if (!mode)
            if (tgt.store=='none') mode='show';
            else if (tgt.style.display=='none') mode='show';
            else mode='hide';
        log+=mode+' ';
        if (false) {}
        else if (mode=='show') log+=swap_show(tgt, src);
        else if (mode=='hide') log+=swap_hide(tgt, src);
        return log;
    }

    function swap_show(tgt, src, store)
    {
        var log='swap_show';
        if (!tgt) return log+" no tgt";
        if (!tgt.style) 
        {
            if (!document.getElementById) return log + ' no dom';
            tgt=document.getElementById(tgt);
            log+=' #'+tgt;
        }
        if (!tgt || !tgt.style) return log + ' not a node';
        tgt.style.display='';
        if (store==null || store != false) 
        {
            tgt.store=tgt.style.display;
            log+=" store"+tgt.store+" ";
        }
        // store user action
        if (!src) src=SWAP_SRC_PREFIX + tgt.id;
        if (!src.className) src=document.getElementById(src);
        if (!src) return log+" no src #"+src;
        if (src.value=='+') src.value='-';
        else if (src.className != null) 
        {
            style=src.className.replace(/ ?swap_hide ?/gi, '');
            // style=style+' swap_show';
            src.className=style;
        }
        else if (src.innerHTML) src.innerHTML.replace('+', '-');
        return log;
    }

    function swap_hide(tgt, src, restore)
    {
        var log='swap_hide';
        if (!tgt) return log+" no tgt";
        if (!tgt.style) 
        {
            if (!document.getElementById) return log + ' no dom';
            tgt=document.getElementById(tgt);
            log+=' #'+tgt;
        }
        if (!tgt || !tgt.style) return log + ' not a node';
        // store display
        tgt.display=tgt.style.display;
        if (!restore) 
        {
            tgt.style.display='none'; 
            tgt.store=tgt.style.display; 
        }
        else 
        {
            tgt.style.display=tgt.store;
            log+=' restore ';
        } 
        if (!src) src=SWAP_SRC_PREFIX + tgt.id;
        if (!src.className) src=document.getElementById(src);
        if (!src) return log+" no src #"+src;
        if (src.value=='-') {  src.value='+'; }
        else if (src.tagName != null) 
        {
            
            var style=src.className;
            // style=style.replace(/ ?swap_show ?/gi, '');
            src.className=style+' swap_hide';
        }
        else if (src.innerHTML) src.innerHTML.replace('+', '-');
        return log;
    }

    function swap_over(tgt, src)
    {
        return 'swap_over ' + swap_show(tgt, src, false);
    }
    
    function swap_out(tgt, src)
    {
        return 'swap_out ' + swap_hide(tgt, src, true);
    }
    

    function swap_hideAll(tgt, level, swap_class)
    {
        if (!level) level=0;
        if (!swap_class) swap_class=SWAP_CLASS;
        if (tgt.className) if (tgt.className.search(swap_class) != -1)
            {
                if (level != 0) level--; 
                if (level==0) swap_hide (tgt);
            }
        if (!tgt.childNodes) return;
        var i=0;
        while (tgt.childNodes[i]) 
            {swap_hideAll(tgt.childNodes[i], level, swap_class); i++; }
        return true;
    }    
    function swap_showAll(tgt, level, swap_class)
    {
        if (!level) level=0;
        if (!swap_class) swap_class=SWAP_CLASS;
        if (tgt.className) if (tgt.className.search(swap_class) != -1) 
            {
                if (level !=0) level--; 
                if (level == 0) swap_show(tgt);
            }
        if (!tgt.childNodes) return;
        var i=0;
        while (tgt.childNodes[i]) 
            {swap_showAll(tgt.childNodes[i], level, swap_class); i++; }
    }

    function swap_all(id, level, mode, swap_class)    
    {
        if (!document.getElementById) return false;
        var node=document.getElementById(id);
        if (!node) node=document.documentElement;
        if (mode=='hide') hide=true;
        else if (mode=='show') hide=false;
        else hide=node.swap;
        if (hide) {node.swap=false; return swap_hideAll(node, level, swap_class)}
        else {node.swap=true; return swap_showAll(node, level, swap_class)}
        return false;
    }
/*]]></xsl:text>
        </cdata>*/
    </xsl:template>
    <!-- PI -->
    <xsl:template match="processing-instruction()" mode="xml:html">
        <span class="xml_pi">
            <xsl:apply-templates select="." mode="xml:text"/>
        </span>
    </xsl:template>
    <!-- add xmlns declarations -->
    <xsl:template name="xml:ns">
        <xsl:variable name="ns" select="../namespace::*"/>
        <xsl:for-each select="namespace::*">
            <xsl:if test="
            name() != 'xml' 
            and (
                not(. = $ns) 
                or not($ns[name()=name(current())])
            )">
                <xsl:value-of select="' '"/>
                <span class="xml_att">
                    <xsl:text>xmlns</xsl:text>
                    <xsl:if test="normalize-space(name())!=''">
                        <xsl:text>:</xsl:text>
                        <span class="xml_ns">
                            <xsl:value-of select="name()"/>
                        </span>
                    </xsl:if>
                </span>
                <xsl:text>="</xsl:text>
                <span class="xml_ns">
                    <xsl:value-of select="."/>
                </span>
                <xsl:text>"</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    <!-- attribute -->
    <xsl:template name="xml:att" match="@*" mode="xml:html">
        <xsl:param name="inline"/>
        <xsl:value-of select="' '"/>
        <span class="xml_att">
            <xsl:call-template name="xml:name"/>
        </span>
        <xsl:text>=&quot;</xsl:text>
        <xsl:if test=". != ''">
            <span class="xml_val">
                <xsl:call-template name="replaces">
                    <xsl:with-param name="string" select="."/>
                    <xsl:with-param name="searches" select="
  document('')/*/xsl:template[@name='xml:ampgtlt']/search
| document('')/*/xsl:template[@name='xml:quot']/search
                            "/>
                </xsl:call-template>
            </span>
        </xsl:if>
        <xsl:text>"</xsl:text>
    </xsl:template>
    <!--  text -->
    <xsl:template match="text()[normalize-space(.)='']" mode="xml:html">
        <!-- only non empty text nodes are matched -->
    </xsl:template>
    <!--
    <xsl:template match="text()[normalize-space(.)='']" mode="xml:html"/>
-->
    <xsl:template match="*/text()" mode="xml:html" priority="1">
        <xsl:param name="text" select="."/>
        <xsl:param name="pre"/>
        <xsl:param name="cdata"/>
        <xsl:param name="inline"/>
        <xsl:variable name="name">
            <xsl:choose>
                <xsl:when test="$pre and ../*">pre</xsl:when>
                <xsl:otherwise>span</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:element name="{$name}">
            <xsl:choose>
                <xsl:when test="$cdata">
                    <xsl:attribute name="class">xml_cdata</xsl:attribute>
                    <xsl:value-of select="."/>
                </xsl:when>
                <xsl:when test="
                    (contains(., '&amp;') 
                    or contains(., '&lt;')
                    or contains(., '&gt;'))
                    and $text-replace
                    ">
                    <xsl:attribute name="class">xml_text</xsl:attribute>
                    <xsl:call-template name="replaces">
                        <xsl:with-param name="string" select="$text"/>
                        <xsl:with-param name="searches" select="
        document('')/*/xsl:template[@name='xml:ampgtlt']/search
                        "/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:attribute name="class">xml_text</xsl:attribute>
                    <xsl:value-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
    <!-- comment -->
    <xsl:template match="comment()" mode="xml:html">
        <xsl:if test=". != ''">
            <pre>
                <xsl:choose>
                    <xsl:when test="contains(., '&#xa;')">
                        <a href="#i" class="pointer" id="src{generate-id()}" onclick="swap_one('{generate-id()}'); return false;">&lt;!--</a>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>&lt;!--</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
                <span class="xml_rem" id="{generate-id()}">
                    <xsl:value-of select="."/>
                </span>
                <xsl:text>--&gt;</xsl:text>
            </pre>
        </xsl:if>
    </xsl:template>
    <!-- name -->
    <xsl:template name="xml:name">
        <xsl:param name="node" select="."/>
        <xsl:if test="contains(name($node), ':')">
            <span class="xml_ns">
                <xsl:value-of select="concat(normalize-space(substring-before(name($node), ':')), ':')"/>
            </span>
        </xsl:if>
        <xsl:value-of select="local-name($node)"/>
    </xsl:template>
    <!--
     |    ELEMENT
     |-->
    <xsl:template match="*" name="xml:element" mode="xml:html">
        <xsl:param name="element" select="."/>
        <xsl:param name="content" select="$element/* | $element/text() | $element/comment()"/>
        <xsl:param name="local-name" select="concat(' ', local-name($element), ' ')"/>
        <xsl:param name="hide" select="contains($hides, $local-name) or $element/@xml:swap[contains(., 'hide')]"/>
        <xsl:param name="inline" select="contains($inlines, $local-name)"/>
        <xsl:param name="id" select="generate-id($element)"/>
        <xsl:variable name="block" select="contains($blocks, $local-name)"/>
        <xsl:variable name="cdata" select="contains($cdatas, $local-name)"/>
        <xsl:variable name="pre" select="contains($pres, $local-name) or @xml:space='preserve'"/>
        <xsl:variable name="el">
            <xsl:choose>
                <xsl:when test="$cdata">pre</xsl:when>
                <xsl:when test="$pre and not($content/*[contains($pres, concat(' ',local-name())) or @xml:space='preserve'])">pre</xsl:when>
                <xsl:when test="$inline">span</xsl:when>
                <xsl:when test="$block">div</xsl:when>
                <xsl:otherwise>div</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:variable name="tag">
            <xsl:choose>
                <xsl:when test="$inline">span</xsl:when>
                <xsl:when test="$cdata">span</xsl:when>
                <xsl:when test="$pre">span</xsl:when>
                <xsl:when test="$block">span</xsl:when>
                <xsl:otherwise>span</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:variable name="cont">
            <xsl:choose>
                <xsl:when test="$inline">span</xsl:when>
                <xsl:when test="$block">span</xsl:when>
                <xsl:when test="$content/descendant-or-self::*[1]">blockquote</xsl:when>
                <xsl:when test="$cdata">span</xsl:when>
                <xsl:when test="$pre">span</xsl:when>
                <xsl:otherwise>span</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:variable name="swap">
            <xsl:choose>
                <xsl:when test="$hide">a</xsl:when>
                <xsl:when test="$cdata and text()">a</xsl:when>
                <xsl:when test="$pre and text()">a</xsl:when>
                <xsl:when test="$inline">span</xsl:when>
                <xsl:when test="$content/descendant-or-self::*[1]">a</xsl:when>
                <xsl:otherwise>span</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:element name="{$el}">
            <!-- element-open -->
            <xsl:element name="{$tag}">
                <xsl:attribute name="id">
                    <xsl:text>src</xsl:text>
                    <xsl:value-of select="$id"/>
                </xsl:attribute>
                <xsl:attribute name="class">xml_tag</xsl:attribute>
                <xsl:element name="{$swap}">
                    <xsl:if test="$swap='a'">
                        <xsl:attribute name="name">
                            <xsl:text>src</xsl:text>
                            <xsl:value-of select="$id"/>
                        </xsl:attribute>
                        <xsl:attribute name="href">#end<xsl:value-of select="$id"/>
                        </xsl:attribute>
                        <xsl:attribute name="onclick">if (window.swap_one) a=swap_one('<xsl:value-of select="$id"/>'); if (a) return false;</xsl:attribute>
                    </xsl:if>
                    <xsl:text>&lt;</xsl:text>
                    <span class="xml_name">
                        <xsl:if test="$hide">
                            <xsl:attribute name="class">xml_name swap_hide</xsl:attribute>
                        </xsl:if>
                        <xsl:call-template name="xml:name">
                            <xsl:with-param name="node" select="$element"/>
                        </xsl:call-template>
                    </span>
                    <xsl:call-template name="xml:ns"/>
                    <xsl:apply-templates select="$element/@*[name()!='xml:swap']" mode="xml:html">
                        <xsl:with-param name="inline" select="$inline"/>
                    </xsl:apply-templates>
                    <xsl:if test="not($content)">/</xsl:if>
                    <xsl:text>&gt;</xsl:text>
                    <xsl:if test="$cdata and $content">
                        <xsl:text>&lt;![CDATA[</xsl:text>
                    </xsl:if>
                </xsl:element>
            </xsl:element>
            <xsl:if test="$content">
                <!-- element-content -->
                <xsl:element name="{$cont}">
                    <xsl:attribute name="id">
                        <xsl:value-of select="$id"/>
                    </xsl:attribute>
                    <xsl:if test="$hide">
                        <xsl:attribute name="style">display:none; {};</xsl:attribute>
                    </xsl:if>
                    <xsl:attribute name="class">
                        <xsl:if test="$cont ='blockquote'">xml_margin</xsl:if>
                    </xsl:attribute>
                    <xsl:choose>
                        <xsl:when test="$block or $inline">
                            <xsl:apply-templates select="$content" mode="xml:html">
                                <xsl:with-param name="pre" select="$pre"/>
                                <xsl:with-param name="cdata" select="$cdata"/>
                                <xsl:with-param name="inline" select="true()"/>
                            </xsl:apply-templates>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="$content" mode="xml:html">
                                <xsl:with-param name="pre" select="$pre"/>
                                <xsl:with-param name="cdata" select="$cdata"/>
                            </xsl:apply-templates>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:element>
                <!-- element-close -->
                <xsl:element name="{$tag}">
                    <xsl:attribute name="class">xml_tag</xsl:attribute>
                    <xsl:if test="$cdata">
                        <xsl:text>]]&gt;</xsl:text>
                    </xsl:if>
                    <xsl:text>&lt;/</xsl:text>
                    <a class="xml_name" name="end{generate-id($element)}">
                        <xsl:if test="$cont ='blockquote'">
                            <xsl:attribute name="href">#src<xsl:value-of select="$id"/>
                            </xsl:attribute>
                        </xsl:if>
                        <xsl:call-template name="xml:name">
                            <xsl:with-param name="node" select="$element"/>
                        </xsl:call-template>
                    </a>
                    <xsl:text>&gt;</xsl:text>
                </xsl:element>
            </xsl:if>
        </xsl:element>
    </xsl:template>
    <!-- unplugged -->
    <!--
    <xsl:template match="*[local-name()='include' or local-name()='import']" mode="xml:html">
        <xsl:call-template name="xml:element">
            <xsl:with-param name="element" select="."/>
            <xsl:with-param name="content" select="document(@href, .)"/>
            <xsl:with-param name="hide" select="true()"/>
        </xsl:call-template>
    </xsl:template>
    -->
    <!--
     find/replace on a set of nodes
     thanks to jeni@jenitennison.com
     http://www.biglist.com/lists/xsl-list/archives/200110/msg01229.html
     fixed and adapted by frederic.glorieux@ajlsm.com -->
    <xsl:template name="replaces">
        <xsl:param name="string"/>
        <xsl:param name="searches" select="no-node"/>
        <xsl:choose>
            <!--
            <xsl:when xmlns:java="java" xmlns:xalan="http://xml.apache.org/xalan" test="function-available('xalan:distinct')">
                <xsl:value-of select="java:org.apache.xalan.xsltc.compiler.util.Util.replace($string, 'a', '__')"/>
            </xsl:when>
        -->
            <xsl:when test="false()">
                <!-- -->
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="first" select="$searches[1]"/>
                <xsl:variable name="rest" select="$searches[position() > 1]"/>
                <xsl:choose>
                    <xsl:when test="$first and contains($string, $first/find)">
                        <!-- replace with rest in before -->
                        <xsl:call-template name="replaces">
                            <xsl:with-param name="string" select="substring-before($string, $first/find)"/>
                            <xsl:with-param name="searches" select="$rest"/>
                        </xsl:call-template>
                        <!-- copy-of current replace -->
                        <xsl:copy-of select="$first/replace/node()"/>
                        <!-- replace with all in after -->
                        <xsl:call-template name="replaces">
                            <xsl:with-param name="string" select="substring-after($string, $first/find)"/>
                            <xsl:with-param name="searches" select="$searches"/>
                        </xsl:call-template>
                    </xsl:when>
                    <!-- empty the searches -->
                    <xsl:when test="$rest">
                        <xsl:call-template name="replaces">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="searches" select="$rest"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$string"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <!-- find/replace to search in attributes value -->
    <xsl:template name="xml:quot">
        <search>
            <find>&quot;</find>
            <replace>&amp;quot;</replace>
        </search>
        <!--
        <search>
            <find>&apos;</find>
            <replace>&amp;apos;</replace>
        </search>
        <search>
            <find>&#xA;</find>
            <replace>
                <br/>
            </replace>
        </search>
        -->
    </xsl:template>
    <xsl:template name="xml:br">
        <search>
            <find>&#xA;</find>
            <replace>
                <br/>
            </replace>
        </search>
    </xsl:template>
    <!--
        <search>
            <find>&#xA;</find>
            <replace>
                <br/>
            </replace>
        </search>
-->
    <!-- find/replace to search -->
    <xsl:template name="xml:ampgtlt">
        <!-- entities -->
        <search>
            <find>&amp;</find>
            <replace>&amp;amp;</replace>
        </search>
        <search>
            <find>&gt;</find>
            <replace>&amp;gt;</replace>
        </search>
        <search>
            <find>&lt;</find>
            <replace>&amp;lt;</replace>
        </search>
    </xsl:template>
    <!--
     |    text mode
     |-->
    <!-- PI -->
    <xsl:template match="processing-instruction()" mode="xml:text">
        <xsl:text>&lt;?</xsl:text>
        <xsl:value-of select="concat(name(.), ' ', .)"/>
        <xsl:text>?&gt;</xsl:text>
    </xsl:template>
    <!-- @* -->
    <xsl:template match="@*" mode="xml:text">
        <xsl:value-of select="concat(' ', name(), '=&quot;', ., '&quot;')"/>
    </xsl:template>
    <!-- comment -->
    <xsl:template match="comment()" mode="xml:text">
        <xsl:call-template name="xml:margin"/>
        <xsl:text>&lt;!--</xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>--&gt;</xsl:text>
    </xsl:template>
    <!-- * -->
    <xsl:template match="*" mode="xml:text">
        <xsl:call-template name="xml:margin"/>
        <xsl:text>&lt;</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:apply-templates select="@*" mode="xml:text"/>
        <xsl:call-template name="xml:ns-text"/>
        <xsl:if test="not(node() | comment())">
            <xsl:text>/</xsl:text>
        </xsl:if>
        <xsl:text>&gt;</xsl:text>
        <xsl:if test="comment() | node ()">
            <xsl:apply-templates select="comment() | node () | processing-instruction()" mode="xml:text"/>
            <xsl:if test="* | comment()">
                <xsl:call-template name="xml:margin"/>
            </xsl:if>
            <xsl:text>&lt;/</xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>&gt;</xsl:text>
        </xsl:if>
    </xsl:template>
    <!-- xmlns -->
    <xsl:template name="xml:ns-text">
        <xsl:variable name="ns" select="../namespace::*"/>
        <xsl:for-each select="namespace::*">
            <xsl:if test="
            name() != 'xml' 
            and (
                not(. = $ns) 
                or not($ns[name()=name(current())])
            )">
                <xsl:text> xmlns</xsl:text>
                <xsl:if test="normalize-space(name())!=''">
                    <xsl:text>:</xsl:text>
                    <xsl:value-of select="name()"/>
                </xsl:if>
                <xsl:text>="</xsl:text>
                <xsl:value-of select="."/>
                <xsl:text>"</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    <!-- "breakable" space margin -->
    <xsl:template name="xml:margin">
        <xsl:text>&#32;
</xsl:text>
        <xsl:for-each select="ancestor::*">
            <xsl:text>&#32;&#32;&#32;&#32;</xsl:text>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
