Home | History | Annotate | Download | only in google-styleguide
      1 <xsl:stylesheet version="1.0"
      2 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      3 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      4 xmlns:dc="http://purl.org/dc/elements/1.1/"
      5 xmlns:dcq="http://purl.org/dc/qualifiers/1.0/"
      6 xmlns:fo="http://www.w3.org/1999/XSL/Format"
      7 xmlns:fn="http://www.w3.org/2005/xpath-functions">
      8   <xsl:output method="html"/>
      9   <!-- Set to 1 to show explanations by default.  Set to 0 to hide them -->
     10   <xsl:variable name="show_explanation_default" select="0" />
     11   <!-- The characters within the Webdings font that show the triangles -->
     12   <xsl:variable name="show_button_text" select="'&#x25B6;'" />
     13   <xsl:variable name="hide_button_text" select="'&#x25BD;'" />
     14   <!-- The suffix for names -->
     15   <xsl:variable name="button_suffix" select="'__button'"/>
     16   <xsl:variable name="body_suffix" select="'__body'"/>
     17   <!-- For easy reference, the name of the button -->
     18   <xsl:variable name="show_hide_all_button" select="'show_hide_all_button'"/>
     19 
     20   <!-- The top-level element -->
     21   <xsl:template match="GUIDE">
     22       <HTML>
     23           <HEAD>
     24               <TITLE><xsl:value-of select="@title"/></TITLE>
     25               <META http-equiv="Content-Type" content="text/html; charset=utf-8"/>
     26               <LINK HREF="https://www.google.com/favicon.ico" type="image/x-icon"
     27                     rel="shortcut icon"/>
     28               <LINK HREF="styleguide.css"
     29                     type="text/css" rel="stylesheet"/>
     30 
     31               <SCRIPT language="javascript" type="text/javascript">
     32 
     33                 function GetElementsByName(name) {
     34                   // Workaround a bug on old versions of opera.
     35                   if (document.getElementsByName) {
     36                     return document.getElementsByName(name);
     37                   } else {
     38                     return [document.getElementById(name)];
     39                   }
     40                 }
     41 
     42                 /**
     43                  * @param {string} namePrefix The prefix of the body name.
     44                  * @param {function(boolean): boolean} getVisibility Computes the new
     45                  *     visibility state, given the current one.
     46                  */
     47                 function ChangeVisibility(namePrefix, getVisibility) {
     48                   var bodyName = namePrefix + '<xsl:value-of select="$body_suffix"/>';
     49                   var buttonName = namePrefix + '<xsl:value-of select="$button_suffix"/>';
     50                   var bodyElements = GetElementsByName(bodyName);
     51                   var linkElement = GetElementsByName('link-' + buttonName)[0];
     52                   if (bodyElements.length != 1) {
     53                     throw Error('ShowHideByName() got the wrong number of bodyElements:  ' + 
     54                         bodyElements.length);
     55                   } else {
     56                     var bodyElement = bodyElements[0];
     57                     var buttonElement = GetElementsByName(buttonName)[0];
     58                     var isVisible = bodyElement.style.display != "none";
     59                     if (getVisibility(isVisible)) {
     60                       bodyElement.style.display = "inline";
     61                       linkElement.style.display = "block";
     62                       buttonElement.innerHTML = '<xsl:value-of select="$hide_button_text"/>';
     63                     } else {
     64                       bodyElement.style.display = "none";
     65                       linkElement.style.display = "none";
     66                       buttonElement.innerHTML = '<xsl:value-of select="$show_button_text"/>';
     67                     }
     68                   }
     69                 }
     70 
     71                 function ShowHideByName(namePrefix) {
     72                   ChangeVisibility(namePrefix, function(old) { return !old; });
     73                 }
     74 
     75                 function ShowByName(namePrefix) {
     76                   ChangeVisibility(namePrefix, function() { return true; });
     77                 }
     78 
     79                 function ShowHideAll() {
     80                   var allButton = GetElementsByName("show_hide_all_button")[0];
     81                   if (allButton.innerHTML == '<xsl:value-of select="$hide_button_text"/>') {
     82                     allButton.innerHTML = '<xsl:value-of select="$show_button_text"/>';
     83                     SetHiddenState(document.getElementsByTagName("body")[0].childNodes, "none", '<xsl:value-of select="$show_button_text"/>');
     84                   } else {
     85                     allButton.innerHTML = '<xsl:value-of select="$hide_button_text"/>';
     86                     SetHiddenState(document.getElementsByTagName("body")[0].childNodes, "inline", '<xsl:value-of select="$hide_button_text"/>');
     87                   }
     88                 }
     89 
     90                 // Recursively sets state of all children
     91                 // of a particular node.
     92                 function SetHiddenState(root, newState, newButton) {
     93                   for (var i = 0; i != root.length; i++) {
     94                     SetHiddenState(root[i].childNodes, newState, newButton);
     95                     if (root[i].className == 'showhide_button')  {
     96                       root[i].innerHTML = newButton;
     97                     }
     98                     if (root[i].className == 'stylepoint_body' ||
     99                         root[i].className == 'link_button')  {
    100                       root[i].style.display = newState;
    101                     }
    102                   }
    103                 }
    104 
    105 
    106                 function EndsWith(str, suffix) {
    107                   var l = str.length - suffix.length;
    108                   return l >= 0 &amp;&amp; str.indexOf(suffix, l) == l;
    109                 }
    110 
    111                 function RefreshVisibilityFromHashParam() {
    112                   var hashRegexp = new RegExp('#([^&amp;#]*)$');
    113                   var hashMatch = hashRegexp.exec(window.location.href);
    114                   var anchor = hashMatch &amp;&amp; GetElementsByName(hashMatch[1])[0];
    115                   var node = anchor;
    116                   var suffix = '<xsl:value-of select="$body_suffix"/>';
    117                   while (node) {
    118                     var id = node.id;
    119                     var matched = id &amp;&amp; EndsWith(id, suffix);
    120                     if (matched) {
    121                       var len = id.length - suffix.length;
    122                       ShowByName(id.substring(0, len));
    123                       if (anchor.scrollIntoView) {
    124                         anchor.scrollIntoView();
    125                       }
    126 
    127                       return;
    128                     }
    129                     node = node.parentNode;
    130                   }
    131                 }
    132 
    133                 window.onhashchange = RefreshVisibilityFromHashParam;
    134 
    135                 window.onload = function() {
    136                   // if the URL contains "?showall=y", expand the details of all children
    137                   var showHideAllRegex = new RegExp("[\\?&amp;](showall)=([^&amp;#]*)");
    138                   var showHideAllValue = showHideAllRegex.exec(window.location.href);
    139                   if (showHideAllValue != null) {
    140                     if (showHideAllValue[2] == "y") {
    141                       SetHiddenState(document.getElementsByTagName("body")[0].childNodes, 
    142                           "inline", '<xsl:value-of select="$hide_button_text"/>');
    143                     } else {
    144                       SetHiddenState(document.getElementsByTagName("body")[0].childNodes, 
    145                           "none", '<xsl:value-of select="$show_button_text"/>');
    146                     }
    147                   }
    148                   var showOneRegex = new RegExp("[\\?&amp;](showone)=([^&amp;#]*)");
    149                   var showOneValue = showOneRegex.exec(window.location.href);
    150                   if (showOneValue) {
    151                     ShowHideByName(showOneValue[2]);
    152                   }
    153 
    154 
    155                   RefreshVisibilityFromHashParam();
    156                 }
    157               </SCRIPT>
    158           </HEAD>
    159           <BODY>
    160             <H1><xsl:value-of select="@title"/></H1>
    161               <xsl:apply-templates/>
    162           </BODY>
    163       </HTML>
    164   </xsl:template>
    165 
    166   <xsl:template match="OVERVIEW">
    167     <xsl:variable name="button_text">
    168       <xsl:choose>
    169         <xsl:when test="$show_explanation_default">
    170           <xsl:value-of select="$hide_button_text"/>
    171         </xsl:when>
    172         <xsl:otherwise>
    173           <xsl:value-of select="$show_button_text"/>
    174         </xsl:otherwise>
    175       </xsl:choose>
    176     </xsl:variable>
    177     <DIV style="margin-left: 50%; font-size: 75%;">
    178       <P>
    179         Each style point has a summary for which additional information is available
    180         by toggling the accompanying arrow button that looks this way:
    181         <SPAN class="showhide_button" style="margin-left: 0; float: none">
    182           <xsl:value-of select="$show_button_text"/></SPAN>.
    183         You may toggle all summaries with the big arrow button:
    184       </P>
    185       <DIV style=" font-size: larger; margin-left: +2em;">
    186         <SPAN class="showhide_button" style="font-size: 180%; float: none">
    187           <xsl:attribute name="onclick"><xsl:value-of select="'javascript:ShowHideAll()'"/></xsl:attribute>
    188           <xsl:attribute name="name"><xsl:value-of select="$show_hide_all_button"/></xsl:attribute>
    189           <xsl:attribute name="id"><xsl:value-of select="$show_hide_all_button"/></xsl:attribute>
    190           <xsl:value-of select="$button_text"/>
    191         </SPAN>
    192         Toggle all summaries
    193       </DIV>
    194     </DIV>
    195     <xsl:call-template name="TOC">
    196       <xsl:with-param name="root" select=".."/>
    197     </xsl:call-template>
    198     <xsl:apply-templates/>
    199   </xsl:template>
    200 
    201   <xsl:template match="PARTING_WORDS">
    202     <H2>Parting Words</H2>
    203     <xsl:apply-templates/>
    204   </xsl:template>
    205 
    206   <xsl:template match="CATEGORY">
    207     <DIV>
    208       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    209       <H2>
    210         <xsl:variable name="category_name">
    211           <xsl:call-template name="anchorname">
    212             <xsl:with-param name="sectionname" select="@title"/>
    213           </xsl:call-template>
    214         </xsl:variable>
    215         <xsl:attribute name="name"><xsl:value-of select="$category_name"/></xsl:attribute>
    216         <xsl:attribute name="id"><xsl:value-of select="$category_name"/></xsl:attribute>
    217         <xsl:value-of select="@title"/>
    218       </H2>
    219       <xsl:apply-templates/>
    220     </DIV>
    221   </xsl:template>
    222 
    223   <xsl:template match="STYLEPOINT">
    224     <DIV>
    225       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    226       <xsl:variable name="stylepoint_name">
    227         <xsl:call-template name="anchorname">
    228           <xsl:with-param name="sectionname" select="@title"/>
    229         </xsl:call-template>
    230       </xsl:variable>
    231       <xsl:variable name="button_text">
    232         <xsl:choose>
    233           <xsl:when test="$show_explanation_default">
    234             <xsl:value-of select="$hide_button_text"/>
    235           </xsl:when>
    236           <xsl:otherwise>
    237             <xsl:value-of select="$show_button_text"/>
    238           </xsl:otherwise>
    239         </xsl:choose>
    240       </xsl:variable>
    241       <H3>
    242         <A>
    243           <xsl:attribute name="name"><xsl:value-of select="$stylepoint_name"/></xsl:attribute>
    244           <xsl:attribute name="id"><xsl:value-of select="$stylepoint_name"/></xsl:attribute>
    245           <xsl:value-of select="@title"/>
    246         </A>
    247       </H3>
    248       <xsl:variable name="buttonName">
    249         <xsl:value-of select="$stylepoint_name"/><xsl:value-of select="$button_suffix"/>
    250       </xsl:variable>
    251       <xsl:variable name="onclick_definition">
    252         <xsl:text>javascript:ShowHideByName('</xsl:text>
    253         <xsl:value-of select="$stylepoint_name"/>
    254         <xsl:text>')</xsl:text>
    255       </xsl:variable>
    256       <SPAN class="link_button" id="link-{$buttonName}" name="link-{$buttonName}">
    257         <A>
    258           <xsl:attribute name="href">?showone=<xsl:value-of select="$stylepoint_name"/>#<xsl:value-of select="$stylepoint_name"/></xsl:attribute>
    259           link
    260         </A>
    261       </SPAN>
    262       <SPAN class="showhide_button">
    263         <xsl:attribute name="onclick"><xsl:value-of select="$onclick_definition"/></xsl:attribute>
    264         <xsl:attribute name="name"><xsl:value-of select="$buttonName"/></xsl:attribute>
    265         <xsl:attribute name="id"><xsl:value-of select="$buttonName"/></xsl:attribute>
    266         <xsl:value-of select="$button_text"/>
    267       </SPAN>
    268       <xsl:apply-templates>
    269         <xsl:with-param name="anchor_prefix" select="$stylepoint_name" />
    270       </xsl:apply-templates>
    271     </DIV>
    272   </xsl:template>
    273 
    274   <xsl:template match="SUMMARY">
    275     <xsl:param name="anchor_prefix" />
    276     <DIV style="display:inline;">
    277       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    278       <xsl:apply-templates/>
    279     </DIV>
    280   </xsl:template>
    281 
    282   <xsl:template match="BODY">
    283     <xsl:param name="anchor_prefix" />
    284     <DIV>
    285       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    286       <DIV class="stylepoint_body">
    287         <xsl:attribute name="name"><xsl:value-of select="$anchor_prefix"/><xsl:value-of select="$body_suffix"/></xsl:attribute>
    288         <xsl:attribute name="id"><xsl:value-of select="$anchor_prefix"/><xsl:value-of select="$body_suffix"/></xsl:attribute>
    289         <xsl:attribute name="style">
    290           <xsl:choose>
    291             <xsl:when test="$show_explanation_default">display: inline</xsl:when>
    292             <xsl:otherwise>display: none</xsl:otherwise>
    293           </xsl:choose>
    294         </xsl:attribute>
    295         <xsl:apply-templates/>
    296       </DIV>
    297     </DIV>
    298   </xsl:template>
    299 
    300   <xsl:template match="DEFINITION">
    301     <P>
    302       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    303       <SPAN class="stylepoint_section">Definition:  </SPAN>
    304       <xsl:apply-templates/>
    305     </P>
    306   </xsl:template>
    307 
    308   <xsl:template match="PROS">
    309     <P>
    310       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    311       <SPAN class="stylepoint_section">Pros:  </SPAN>
    312       <xsl:apply-templates/>
    313     </P>
    314   </xsl:template>
    315 
    316   <xsl:template match="CONS">
    317     <P>
    318       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    319       <SPAN class="stylepoint_section">Cons: </SPAN>
    320       <xsl:apply-templates/>
    321     </P>
    322   </xsl:template>
    323 
    324   <xsl:template match="DECISION">
    325     <P>
    326       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    327       <SPAN class="stylepoint_section">Decision:  </SPAN>
    328       <xsl:apply-templates/>
    329     </P>
    330   </xsl:template>
    331 
    332   <xsl:template match="TODO">
    333     <P>
    334       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    335       <DIV style="font-size: 150%;">TODO:
    336         <xsl:apply-templates/>
    337       </DIV>
    338     </P>
    339   </xsl:template>
    340 
    341   <xsl:template match="SUBSECTION">
    342     <P>
    343       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    344       <SPAN class="stylepoint_subsection"><xsl:value-of select="@title"/>  </SPAN>
    345       <xsl:apply-templates/>
    346     </P>
    347   </xsl:template>
    348 
    349   <xsl:template match="SUBSUBSECTION">
    350     <P>
    351       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    352       <SPAN class="stylepoint_subsubsection"><xsl:value-of select="@title"/>  </SPAN>
    353       <xsl:apply-templates/>
    354     </P>
    355   </xsl:template>
    356 
    357   <xsl:template match="CODE_SNIPPET">
    358     <DIV>
    359       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    360       <PRE><xsl:call-template name="print_without_leading_chars">
    361            <xsl:with-param name="text" select="."/>
    362            <xsl:with-param name="strip" select="1"/>
    363            <xsl:with-param name="is_firstline" select="1"/>
    364            <xsl:with-param name="trim_count">
    365              <xsl:call-template name="num_leading_spaces">
    366                <xsl:with-param name="text" select="."/>
    367                <xsl:with-param name="max_so_far" select="1000"/>
    368              </xsl:call-template>
    369            </xsl:with-param>
    370          </xsl:call-template></PRE>
    371     </DIV>
    372   </xsl:template>
    373 
    374   <xsl:template match="BAD_CODE_SNIPPET">
    375     <DIV>
    376       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    377       <PRE class="badcode"><xsl:call-template name="print_without_leading_chars">
    378            <xsl:with-param name="text" select="."/>
    379            <xsl:with-param name="strip" select="1"/>
    380            <xsl:with-param name="is_firstline" select="1"/>
    381            <xsl:with-param name="trim_count">
    382              <xsl:call-template name="num_leading_spaces">
    383                <xsl:with-param name="text" select="."/>
    384                <xsl:with-param name="max_so_far" select="1000"/>
    385              </xsl:call-template>
    386            </xsl:with-param>
    387          </xsl:call-template></PRE>
    388     </DIV>
    389   </xsl:template>
    390 
    391   <xsl:template match="PY_CODE_SNIPPET">
    392     <DIV>
    393       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    394       <PRE><xsl:call-template name="print_python_code">
    395              <xsl:with-param name="text" select="."/>
    396            </xsl:call-template></PRE>
    397     </DIV>
    398   </xsl:template>
    399 
    400   <xsl:template match="BAD_PY_CODE_SNIPPET">
    401     <DIV>
    402       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    403       <PRE class="badcode"><xsl:call-template name="print_python_code">
    404                              <xsl:with-param name="text" select="."/>
    405                            </xsl:call-template></PRE>
    406     </DIV>
    407   </xsl:template>
    408 
    409   <xsl:template match="FUNCTION">
    410     <xsl:call-template name="print_function_name">
    411       <xsl:with-param name="text" select="."/>
    412     </xsl:call-template>
    413   </xsl:template>
    414 
    415   <xsl:template match="SYNTAX">
    416     <I>
    417       <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    418       <xsl:apply-templates/>
    419     </I>
    420   </xsl:template>
    421 
    422 
    423   <!-- This passes through any HTML elements that the
    424     XML doc uses for minor formatting -->
    425   <xsl:template match="a|address|blockquote|br|center|cite|code|dd|div|dl|dt|em|hr|i|img|li|ol|p|pre|span|table|td|th|tr|ul|var|A|ADDRESS|BLOCKQUOTE|BR|CENTER|CITE|CODE|DD|DIV|DL|DT|EM|HR|I|LI|OL|P|PRE|SPAN|TABLE|TD|TH|TR|UL|VAR">
    426       <xsl:element name="{local-name()}">
    427           <xsl:copy-of select="@*"/>
    428           <xsl:apply-templates/>
    429       </xsl:element>
    430   </xsl:template>
    431 
    432     <!-- Builds the table of contents -->
    433   <xsl:template name="TOC">
    434     <xsl:param name="root"/>
    435     <DIV class="toc">
    436       <DIV class="toc_title">Table of Contents</DIV>
    437       <TABLE>
    438       <xsl:for-each select="$root/CATEGORY">
    439         <TR valign="top">
    440           <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    441           <TD>
    442           <DIV class="toc_category">
    443             <A>
    444               <xsl:attribute name="href">
    445                 <xsl:text>#</xsl:text>
    446                 <xsl:call-template name="anchorname">
    447                   <xsl:with-param name="sectionname" select="@title"/>
    448                 </xsl:call-template>
    449               </xsl:attribute>
    450               <xsl:value-of select="@title"/>
    451             </A>
    452           </DIV>
    453           </TD><TD>
    454             <DIV class="toc_stylepoint">
    455               <xsl:for-each select="./STYLEPOINT">
    456                 <SPAN style="padding-right: 1em; white-space:nowrap;">
    457                   <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
    458                   <A>
    459                     <xsl:attribute name="href">
    460                       <xsl:text>#</xsl:text>
    461                       <xsl:call-template name="anchorname">
    462                         <xsl:with-param name="sectionname" select="@title"/>
    463                       </xsl:call-template>
    464                     </xsl:attribute>
    465                     <xsl:value-of select="@title"/>
    466                   </A>
    467                 </SPAN>
    468                 <xsl:text> </xsl:text>
    469               </xsl:for-each>
    470             </DIV>
    471           </TD>
    472         </TR>
    473       </xsl:for-each>
    474       </TABLE>
    475     </DIV>
    476   </xsl:template>
    477 
    478   <xsl:template name="TOC_one_stylepoint">
    479     <xsl:param name="stylepoint"/>
    480   </xsl:template>
    481 
    482   <!-- Creates a standard anchor given any text.
    483        Substitutes underscore for characters unsuitable for URLs  -->
    484   <xsl:template name="anchorname">
    485     <xsl:param name="sectionname"/>
    486     <!-- strange quoting necessary to strip apostrophes -->
    487     <xsl:variable name="bad_characters" select="&quot; ()#'&quot;"/>
    488     <xsl:value-of select="translate($sectionname,$bad_characters,'_____')"/>
    489   </xsl:template>
    490 
    491   <!-- Given text, evaluates to the number of leading spaces. -->
    492   <!-- TODO(csilvers): deal well with leading tabs (treat as 8 spaces?) -->
    493   <xsl:template name="num_leading_spaces_one_line">
    494     <xsl:param name="text"/>
    495     <xsl:param name="current_count"/>
    496     <xsl:choose>
    497       <xsl:when test="starts-with($text, ' ')">
    498         <xsl:call-template name="num_leading_spaces_one_line">
    499           <xsl:with-param name="text" select="substring($text, 2)"/>
    500           <xsl:with-param name="current_count" select="$current_count + 1"/>
    501         </xsl:call-template>
    502       </xsl:when>
    503       <xsl:otherwise>
    504         <xsl:value-of select="$current_count"/>
    505       </xsl:otherwise>
    506     </xsl:choose>
    507   </xsl:template>
    508 
    509   <!-- Given a block of text, each line terminated by \n, evaluates to
    510        the indentation-level of that text; that is, the largest number
    511        n such that every non-blank line starts with at least n spaces. -->
    512   <xsl:template name="num_leading_spaces">
    513     <xsl:param name="text"/>
    514     <xsl:param name="max_so_far"/>
    515     <!-- TODO(csilvers): deal with case text doesn't end in a newline -->
    516     <xsl:variable name="line" select="substring-before($text, '&#xA;')"/>
    517     <xsl:variable name="rest" select="substring-after($text, '&#xA;')"/>
    518     <xsl:variable name="num_spaces_this_line">
    519       <xsl:choose>
    520         <xsl:when test="$line=''">
    521            <xsl:value-of select="$max_so_far"/>
    522         </xsl:when>
    523         <xsl:otherwise>
    524           <xsl:call-template name="num_leading_spaces_one_line">
    525             <xsl:with-param name="text" select="$line"/>
    526             <xsl:with-param name="current_count" select="0"/>
    527           </xsl:call-template>
    528         </xsl:otherwise>
    529       </xsl:choose>
    530     </xsl:variable>
    531     <xsl:variable name="new_max_so_far">
    532        <xsl:choose>
    533          <xsl:when test="$num_spaces_this_line &lt; $max_so_far">
    534            <xsl:value-of select="$num_spaces_this_line"/>
    535          </xsl:when>
    536          <xsl:otherwise>
    537            <xsl:value-of select="$max_so_far"/>
    538          </xsl:otherwise>
    539        </xsl:choose>
    540     </xsl:variable>
    541     <!-- now check if we're on the last line, and if not, recurse -->
    542     <xsl:if test="$rest=''">
    543       <xsl:value-of select="$new_max_so_far"/>
    544     </xsl:if>
    545     <xsl:if test="not($rest='')">
    546       <xsl:call-template name="num_leading_spaces">
    547         <xsl:with-param name="text" select="$rest"/>
    548         <xsl:with-param name="max_so_far" select="$new_max_so_far"/>
    549       </xsl:call-template>
    550     </xsl:if>
    551   </xsl:template>
    552 
    553   <!-- Given text, determine the starting position of code.
    554        This similar to num_leading_spaces_one_line but treats "Yes:" and "No:" 
    555        as spaces. Also, if there is no code on the first line, it searches 
    556        subsequent lines until a non-empty line is found.
    557        Used to find the start of code in snippets like:
    558        Yes: if(foo):
    559        No : if(foo):
    560        As well as:
    561        Yes:
    562          if (foo):
    563   -->
    564   <xsl:template name="code_start_index">
    565     <xsl:param name="text"/>
    566     <xsl:param name="current_count"/>
    567     <xsl:choose>
    568       <xsl:when test="starts-with($text, ' ')">
    569         <xsl:call-template name="code_start_index">
    570           <xsl:with-param name="text" select="substring($text, 2)"/>
    571           <xsl:with-param name="current_count" select="$current_count + 1"/>
    572         </xsl:call-template>
    573       </xsl:when>
    574       <xsl:when test="starts-with($text, 'Yes:')">
    575         <xsl:call-template name="code_start_index">
    576           <xsl:with-param name="text" select="substring($text, 5)"/>
    577           <xsl:with-param name="current_count" select="$current_count + 4"/>
    578         </xsl:call-template>
    579       </xsl:when>
    580       <xsl:when test="starts-with($text, 'No:')">
    581         <xsl:call-template name="code_start_index">
    582           <xsl:with-param name="text" select="substring($text, 4)"/>
    583           <xsl:with-param name="current_count" select="$current_count + 3"/>
    584         </xsl:call-template>
    585       </xsl:when>
    586       <!-- This is only reached if the first line is entirely whitespace or 
    587            contains nothing but "Yes:" or "No:"-->
    588       <xsl:when test="starts-with($text, '&#xA;')">
    589         <xsl:call-template name="code_start_index">
    590           <xsl:with-param name="text" select="substring($text, 2)"/>
    591           <xsl:with-param name="current_count" select="0"/>
    592         </xsl:call-template>
    593       </xsl:when>
    594       <xsl:otherwise>
    595         <xsl:value-of select="$current_count"/>
    596       </xsl:otherwise>
    597     </xsl:choose>
    598   </xsl:template>
    599 
    600   <!-- Helper for ends_with_colon. Determine whether the given line is nothing
    601        but spaces and python-style comments. -->
    602   <xsl:template name="is_blank_or_comment">
    603     <xsl:param name="line"/>
    604     <xsl:choose>
    605       <xsl:when test="$line = ''">
    606         <xsl:value-of select="1"/>
    607       </xsl:when>
    608       <xsl:when test="starts-with($line, '&#xA;')">
    609         <xsl:value-of select="1"/>
    610       </xsl:when>
    611       <xsl:when test="starts-with($line, '#')">
    612         <xsl:value-of select="1"/>
    613       </xsl:when>
    614       <xsl:when test="starts-with($line, ' ')">
    615         <xsl:call-template name="is_blank_or_comment">
    616           <xsl:with-param name="line" select="substring($line, 2)"/>
    617         </xsl:call-template>
    618       </xsl:when>
    619       <xsl:otherwise>
    620         <xsl:value-of select="0"/>
    621       </xsl:otherwise>
    622     </xsl:choose>
    623   </xsl:template>
    624 
    625   <!-- Determine whether the given line ends with a colon. Note that Python
    626        style comments are ignored so the following lines return True:
    627        - def foo():
    628        - def foo():  # Bar
    629        - if(foo):
    630 
    631        But some code may confuse this function. For example the following are
    632        also consider to "end_with_colon" even though they don't for Python
    633        - foo(":  #")
    634        - foo() # No need for :
    635   -->
    636   <xsl:template name="ends_with_colon">
    637     <xsl:param name="line"/>
    638     <xsl:param name="found_colon"/>
    639     <xsl:choose>
    640       <xsl:when test="$line = ''">
    641         <xsl:value-of select="$found_colon"/>
    642       </xsl:when>
    643       <xsl:when test="starts-with($line, '&#xA;')">
    644         <xsl:value-of select="$found_colon"/>
    645       </xsl:when>
    646       <xsl:when test="starts-with($line, ' ')">
    647         <xsl:call-template name="ends_with_colon">
    648           <xsl:with-param name="line" select="substring($line, 2)"/>
    649           <xsl:with-param name="found_colon" select="$found_colon"/>
    650         </xsl:call-template>
    651       </xsl:when>
    652       <xsl:when test="starts-with($line, ':')">
    653         <xsl:variable name="rest_is_comment">
    654           <xsl:call-template name="is_blank_or_comment">
    655             <xsl:with-param name="line" select="substring($line, 2)"/>
    656           </xsl:call-template>
    657         </xsl:variable>
    658         <xsl:choose>
    659           <xsl:when test="$rest_is_comment = '1'">
    660             <xsl:value-of select="1"/>
    661           </xsl:when>
    662           <xsl:otherwise>
    663             <xsl:call-template name="ends_with_colon">
    664               <xsl:with-param name="line" select="substring($line, 2)"/>
    665               <xsl:with-param name="found_colon" select="0"/>
    666             </xsl:call-template>
    667           </xsl:otherwise>
    668         </xsl:choose>
    669       </xsl:when>
    670       <xsl:otherwise>
    671         <xsl:call-template name="ends_with_colon">
    672           <xsl:with-param name="line" select="substring($line, 2)"/>
    673           <xsl:with-param name="found_colon" select="0"/>
    674         </xsl:call-template>
    675       </xsl:otherwise>
    676     </xsl:choose>
    677   </xsl:template>
    678 
    679   <!-- Prints one line of python code with proper indent and calls itself
    680        recursively for the rest of the text.
    681        This template uses "a", "b", "c" and "d" to refer to four key column
    682        numbers. They are:
    683        - a: the indentation common to all lines in a code snippet. This is
    684             stripped out to allow for cleaner code in the xml.
    685        - b: the indentation of the most out-dented line of code. This is
    686             different from "a" when code is labelled with "Yes:" or "No:"
    687        - c: the indentation of the current python block, in other words, the
    688             indentation of the first line of this block, which is the
    689             indentation of the last line we saw that ended with a colon.
    690        - d: the "total" indentation of the line, ignorng possible "Yes:" or
    691             "No:" text on the line.
    692 
    693        For example, for the last line of the following code snippet, the
    694        positions of a, b, c and d are indicated below:
    695            Yes: def Foo():
    696                   if bar():
    697                     a += 1
    698                     baz()
    699            a    b c d
    700 
    701        The algorithm is:
    702        1) Split the text into first line and the rest. Note that the
    703           substring-before function is supposed to handle the case where the
    704           character is not present in the string but does not so we
    705           automatically ignore the last line of the snippet which is always
    706           empty (the closing snippet tag). This is consistent with the
    707           behavior or print_without_leading_chars.
    708        2) If the current is empty (only whitespace), print newline and call
    709           itself recursively on the rest of the text with the rest of the
    710           parameters unchanged.
    711        3) Otherwise, measure "d"
    712        4) Measure "c" by taking:
    713           - the value of "d" if the previous line ended with a colon or the
    714             current line is outdented compare to the previous line
    715           - the indent of the previous line otherwise
    716        5) Print line[a:c] (Note that we ignore line[0:a])
    717        6) Print line[b:c] in an external span (in order to double the block
    718           indent in external code).
    719        7) Print line[c:<end>] with function names processed to produce both 
    720           internal and external names.
    721        8) If there are more lines, recurse.
    722   -->
    723   <xsl:template name="print_python_line_recursively">
    724     <xsl:param name="text"/>
    725     <xsl:param name="a"/>
    726     <xsl:param name="b"/>
    727     <xsl:param name="previous_indent"/>
    728     <xsl:param name="previous_ends_with_colon"/>
    729     <xsl:param name="is_first_line"/>
    730     <xsl:variable name="line" select="substring-before($text, '&#xA;')"/>
    731     <xsl:variable name="rest" select="substring-after($text, '&#xA;')"/>
    732     <xsl:choose>
    733       <xsl:when test="substring($line, $b) = '' and not($rest = '')">
    734         <xsl:if test="not($is_first_line = '1')">
    735           <xsl:text>&#xA;</xsl:text>
    736         </xsl:if>
    737         <xsl:call-template name="print_python_line_recursively">
    738           <xsl:with-param name="text" select="$rest"/>
    739           <xsl:with-param name="a" select="$a"/>
    740           <xsl:with-param name="b" select="$b"/>
    741           <xsl:with-param name="previous_indent" select="$previous_indent"/>
    742           <xsl:with-param name="previous_ends_with_colon"
    743                           select="$previous_ends_with_colon"/>
    744           <xsl:with-param name="is_first_line" select="0"/>
    745         </xsl:call-template>
    746       </xsl:when>
    747       <xsl:otherwise>
    748         <xsl:variable name="indent_after_b">
    749           <xsl:call-template name="num_leading_spaces_one_line">
    750             <xsl:with-param name="text" select="substring($line, $b + 1)"/>
    751             <xsl:with-param name="current_count" select="0"/>
    752           </xsl:call-template>
    753         </xsl:variable>
    754         <xsl:variable name="d" select="$b + $indent_after_b"/>
    755         <xsl:variable name="c">
    756            <xsl:choose>
    757              <xsl:when test="$previous_ends_with_colon = '1' or
    758                              $previous_indent > $d">
    759                <xsl:value-of select="$d"/>
    760              </xsl:when>
    761              <xsl:otherwise>
    762                <xsl:value-of select="$previous_indent"/>
    763              </xsl:otherwise>
    764            </xsl:choose>
    765         </xsl:variable>
    766 
    767         <xsl:value-of select="substring($line, $a + 1, $c - $a)"/>
    768         <span class="external">
    769            <xsl:value-of select="substring($line, $b + 1, $c - $b)"/>
    770         </span>
    771         <xsl:call-template name="munge_function_names_in_text">
    772           <xsl:with-param name="stripped_line"
    773              select="substring($line, $c + 1)"/>
    774         </xsl:call-template>
    775         <xsl:if test="not(substring($rest, $a) = '')">
    776           <xsl:text>&#xA;</xsl:text>
    777           <xsl:call-template name="print_python_line_recursively">
    778             <xsl:with-param name="text" select="$rest"/>
    779             <xsl:with-param name="a" select="$a"/>
    780             <xsl:with-param name="b" select="$b"/>
    781             <xsl:with-param name="previous_indent" select="$c"/>
    782             <xsl:with-param name="previous_ends_with_colon">
    783               <xsl:call-template name="ends_with_colon">
    784                 <xsl:with-param name="line" select="$line"/>
    785                 <xsl:with-param name="found_colon" select="0"/>
    786               </xsl:call-template>
    787             </xsl:with-param>
    788             <xsl:with-param name="is_first_line" select="0"/>
    789           </xsl:call-template>
    790         </xsl:if>
    791       </xsl:otherwise>
    792     </xsl:choose>
    793   </xsl:template>
    794 
    795   <!-- Print python code with internal and external styles.
    796        In order to conform with PEP-8 externally, we identify 2-space indents
    797        and an external-only 4-space indent.
    798        Function names that are marked with $$FunctionName/$$ have an external
    799        lower_with_underscore version added. -->
    800   <xsl:template name="print_python_code">
    801     <xsl:param name="text"/>
    802 
    803     <xsl:variable name="a">
    804        <xsl:call-template name="num_leading_spaces">
    805          <xsl:with-param name="text" select="."/>
    806          <xsl:with-param name="max_so_far" select="1000"/>
    807        </xsl:call-template>
    808     </xsl:variable>
    809 
    810     <xsl:variable name="b">
    811       <xsl:call-template name="code_start_index">
    812         <xsl:with-param name="text" select="$text"/>
    813         <xsl:with-param name="current_count" select="0"/>
    814       </xsl:call-template>
    815     </xsl:variable>
    816 
    817     <xsl:call-template name="print_python_line_recursively">
    818       <xsl:with-param name="text" select="$text"/>
    819       <xsl:with-param name="a" select="$a"/>
    820       <xsl:with-param name="b" select="$b"/>
    821       <xsl:with-param name="previous_indent" select="$b"/>
    822       <xsl:with-param name="previous_ends_with_colon" select="0"/>
    823       <xsl:with-param name="is_first_line" select="1"/> 
    824     </xsl:call-template>
    825   </xsl:template>
    826 
    827   <!-- Given a block of text, each line terminated by \n, and a number n,
    828        emits the text with the first n characters of each line
    829        deleted.  If strip==1, then we omit blank lines at the beginning
    830        and end of the text (but not the middle!) -->
    831   <!-- TODO(csilvers): deal well with leading tabs (treat as 8 spaces?) -->
    832   <xsl:template name="print_without_leading_chars">
    833     <xsl:param name="text"/>
    834     <xsl:param name="trim_count"/>
    835     <xsl:param name="strip"/>
    836     <xsl:param name="is_firstline"/>
    837     <!-- TODO(csilvers): deal with case text doesn't end in a newline -->
    838     <xsl:variable name="line" select="substring-before($text, '&#xA;')"/>
    839     <xsl:variable name="rest" select="substring-after($text, '&#xA;')"/>
    840     <xsl:variable name="stripped_line" select="substring($line, $trim_count+1)"/>
    841     <xsl:choose>
    842       <!-- $line (or $rest) is considered empty if we'd trim the entire line -->
    843       <xsl:when test="($strip = '1') and ($is_firstline = '1') and
    844                       (string-length($line) &lt;= $trim_count)">
    845       </xsl:when>
    846       <xsl:when test="($strip = '1') and
    847                       (string-length($rest) &lt;= $trim_count)">
    848         <xsl:value-of select="$stripped_line"/>
    849       </xsl:when>
    850       <xsl:otherwise>
    851         <xsl:value-of select="$stripped_line"/>
    852         <xsl:text>&#xA;</xsl:text>
    853       </xsl:otherwise>
    854     </xsl:choose>
    855     <xsl:if test="not($rest='')">
    856       <xsl:call-template name="print_without_leading_chars">
    857         <xsl:with-param name="text" select="$rest"/>
    858         <xsl:with-param name="trim_count" select="$trim_count"/>
    859         <xsl:with-param name="strip" select="$strip"/>
    860         <xsl:with-param name="is_firstline" select="0"/>
    861       </xsl:call-template>
    862     </xsl:if>
    863   </xsl:template>
    864 
    865   <!-- Given a line of code, find function names that are marked with $$ /$$ and
    866        print out the line with the internal and external versions of the
    867        function names.-->
    868   <xsl:template name="munge_function_names_in_text">
    869     <xsl:param name="stripped_line"/>
    870     <xsl:choose>
    871       <xsl:when test="contains($stripped_line, '$$')">
    872         <xsl:value-of select="substring-before($stripped_line, '$$')"/>
    873         <xsl:call-template name="print_function_name">
    874           <xsl:with-param name="text" select="substring-after(substring-before($stripped_line, '/$$'), '$$')"/>
    875         </xsl:call-template>
    876         <xsl:call-template name="munge_function_names_in_text">
    877           <xsl:with-param name="stripped_line" select="substring-after($stripped_line, '/$$')"/>
    878         </xsl:call-template>
    879       </xsl:when>
    880       <xsl:otherwise>
    881         <xsl:value-of select="$stripped_line"/>
    882      </xsl:otherwise>
    883    </xsl:choose>
    884   </xsl:template>
    885 
    886   <!-- Given a function name, print out both the internal and external version
    887        of the function name in their respective spans.-->
    888   <xsl:template name="print_function_name">
    889     <xsl:param name="text"/>
    890       <xsl:call-template name="convert_camel_case_to_lowercase_with_under">
    891         <xsl:with-param name="text" select="$text"/>
    892       </xsl:call-template>
    893   </xsl:template>
    894 
    895   <!-- Given a single word of text convert it from CamelCase to
    896        lower_with_under.
    897        This means replacing each uppercase character with _ followed by the
    898        lowercase version except for the first character which is replaced 
    899        without adding the _.-->
    900   <xsl:template name="convert_camel_case_to_lowercase_with_under">
    901     <xsl:param name="text"/>
    902     <xsl:param name="is_recursive_call"/>
    903     <xsl:variable name="first_char" select="substring($text, 1, 1)"/>
    904     <xsl:variable name="rest" select="substring($text, 2)"/>
    905     <xsl:choose>
    906       <xsl:when test="contains('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $first_char)">
    907         <xsl:if test="$is_recursive_call='1'">
    908            <xsl:text>_</xsl:text>
    909         </xsl:if>
    910         <xsl:value-of select="translate($first_char, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
    911       </xsl:when>
    912       <xsl:otherwise>
    913         <xsl:value-of select="$first_char" />
    914       </xsl:otherwise>
    915     </xsl:choose>
    916     <xsl:if test="not($rest='')">
    917       <xsl:call-template name="convert_camel_case_to_lowercase_with_under">
    918         <xsl:with-param name="text" select="$rest"/>
    919         <xsl:with-param name="is_recursive_call" select="1"/>
    920       </xsl:call-template>
    921     </xsl:if>
    922   </xsl:template>
    923 </xsl:stylesheet>
    924 
    925