Home | History | Annotate | Download | only in core-component-page
      1 
      2   
      3   (function() {
      4   
      5     Polymer('core-layout', {
      6 
      7       isContainer: false,
      8       /**
      9        * Controls if the element lays out vertically or not.
     10        *
     11        * @attribute vertical
     12        * @type boolean
     13        * @default false
     14        */
     15       vertical: false,
     16       /**
     17        * Controls how the items are aligned in the main-axis direction. For 
     18        * example for a horizontal layout, this controls how each item is aligned
     19        * horizontally.
     20        *
     21        * @attribute justify
     22        * @type string start|center|end|between
     23        * @default ''
     24        */
     25       justify: '',
     26       /**
     27        * Controls how the items are aligned in cross-axis direction. For 
     28        * example for a horizontal layout, this controls how each item is aligned
     29        * vertically.
     30        *
     31        * @attribute align
     32        * @type string start|center|end
     33        * @default ''
     34        */
     35       align: '',
     36       /**
     37        * Controls whether or not the items layout in reverse order.
     38        *
     39        * @attribute reverse
     40        * @type boolean
     41        * @default false
     42        */
     43       reverse: false,
     44       layoutPrefix: 'core-',
     45   
     46       // NOTE: include template so that styles are loaded, but remove
     47       // so that we can decide dynamically what part to include
     48       registerCallback: function(polymerElement) {
     49         var template = polymerElement.querySelector('template');
     50         this.styles = template.content.querySelectorAll('style').array();
     51         this.styles.forEach(function(s) {
     52           s.removeAttribute('no-shim');
     53         })
     54       },
     55   
     56       fetchTemplate: function() {
     57         return null;
     58       },
     59   
     60       attached: function() {
     61         this.installScopeStyle(this.styles[0]);
     62         if (this.children.length) {
     63           this.isContainer = true;
     64         }
     65         var container = this.isContainer ? this : this.parentNode;  
     66         // detect if laying out a shadowRoot host.
     67         var forHost = container instanceof ShadowRoot;
     68         if (forHost) {
     69           this.installScopeStyle(this.styles[1], 'host');
     70           container = container.host || document.body;
     71         }
     72         this.layoutContainer = container;
     73       },
     74 
     75       detached: function() {
     76         this.layoutContainer = null;
     77       },
     78 
     79       layoutContainerChanged: function(old) {
     80         this.style.display = this.layoutContainer === this ? null : 'none';
     81         this.verticalChanged();
     82         this.alignChanged();
     83         this.justifyChanged();
     84       },
     85 
     86       setLayoutClass: function(prefix, old, newValue) {
     87         if (this.layoutContainer) {
     88           prefix = this.layoutPrefix + prefix;
     89           if (old) {
     90             this.layoutContainer.classList.remove(prefix + old);
     91           }
     92           if (newValue) {
     93             this.layoutContainer.classList.add(prefix + newValue);
     94           }
     95         }
     96       },
     97 
     98       verticalChanged: function(old) {
     99         old = old ? 'v' : 'h';
    100         var vertical = this.vertical ? 'v' : 'h';
    101         this.setLayoutClass('', old, vertical);
    102       },
    103 
    104       alignChanged: function(old) {
    105         this.setLayoutClass('align-', old, this.align);
    106       },
    107 
    108       justifyChanged: function(old) {
    109         this.setLayoutClass('justify-', old, this.justify);
    110       },
    111 
    112       reverseChanged: function(old) {
    113         old = old ? 'reverse' : '';
    114         var newValue = this.reverse ? 'reverse' : '';
    115         this.setLayoutClass('', old, newValue);
    116       }
    117 
    118     });
    119 
    120   })();
    121   ;
    122 
    123 
    124   (function() {
    125     
    126     var SKIP_ID = 'meta';
    127     var metaData = {}, metaArray = {};
    128 
    129     Polymer('core-meta', {
    130       
    131       /**
    132        * The type of meta-data.  All meta-data with the same type with be
    133        * stored together.
    134        * 
    135        * @attribute type
    136        * @type string
    137        * @default 'default'
    138        */
    139       type: 'default',
    140       
    141       alwaysPrepare: true,
    142       
    143       ready: function() {
    144         this.register(this.id);
    145       },
    146       
    147       get metaArray() {
    148         var t = this.type;
    149         if (!metaArray[t]) {
    150           metaArray[t] = [];
    151         }
    152         return metaArray[t];
    153       },
    154       
    155       get metaData() {
    156         var t = this.type;
    157         if (!metaData[t]) {
    158           metaData[t] = {};
    159         }
    160         return metaData[t];
    161       },
    162       
    163       register: function(id, old) {
    164         if (id && id !== SKIP_ID) {
    165           this.unregister(this, old);
    166           this.metaData[id] = this;
    167           this.metaArray.push(this);
    168         }
    169       },
    170       
    171       unregister: function(meta, id) {
    172         delete this.metaData[id || meta.id];
    173         var i = this.metaArray.indexOf(meta);
    174         if (i >= 0) {
    175           this.metaArray.splice(i, 1);
    176         }
    177       },
    178       
    179       /**
    180        * Returns a list of all meta-data elements with the same type.
    181        * 
    182        * @attribute list
    183        * @type array
    184        * @default []
    185        */
    186       get list() {
    187         return this.metaArray;
    188       },
    189       
    190       /**
    191        * Retrieves meta-data by ID.
    192        *
    193        * @method byId
    194        * @param {String} id The ID of the meta-data to be returned.
    195        * @returns Returns meta-data.
    196        */
    197       byId: function(id) {
    198         return this.metaData[id];
    199       }
    200       
    201     });
    202     
    203   })();
    204   
    205 ;
    206 
    207   
    208     Polymer('core-iconset', {
    209   
    210       /**
    211        * The URL of the iconset image.
    212        *
    213        * @attribute src
    214        * @type string
    215        * @default ''
    216        */
    217       src: '',
    218 
    219       /**
    220        * The width of the iconset image. This must only be specified if the
    221        * icons are arranged into separate rows inside the image.
    222        *
    223        * @attribute width
    224        * @type number
    225        * @default 0
    226        */
    227       width: 0,
    228 
    229       /**
    230        * A space separated list of names corresponding to icons in the iconset
    231        * image file. This list must be ordered the same as the icon images
    232        * in the image file.
    233        *
    234        * @attribute icons
    235        * @type string
    236        * @default ''
    237        */
    238       icons: '',
    239 
    240       /**
    241        * The size of an individual icon. Note that icons must be square.
    242        *
    243        * @attribute iconSize
    244        * @type number
    245        * @default 24
    246        */
    247       iconSize: 24,
    248 
    249       /**
    250        * The horizontal offset of the icon images in the inconset src image.
    251        * This is typically used if the image resource contains additional images
    252        * beside those intended for the iconset.
    253        *
    254        * @attribute offsetX
    255        * @type number
    256        * @default 0
    257        */
    258       offsetX: 0,
    259       /**
    260        * The vertical offset of the icon images in the inconset src image.
    261        * This is typically used if the image resource contains additional images
    262        * beside those intended for the iconset.
    263        *
    264        * @attribute offsetY
    265        * @type number
    266        * @default 0
    267        */
    268       offsetY: 0,
    269       type: 'iconset',
    270 
    271       created: function() {
    272         this.iconMap = {};
    273         this.iconNames = [];
    274         this.themes = {};
    275       },
    276   
    277       ready: function() {
    278         // TODO(sorvell): ensure iconset's src is always relative to the main
    279         // document
    280         if (this.src && (this.ownerDocument !== document)) {
    281           this.src = this.resolvePath(this.src, this.ownerDocument.baseURI);
    282         }
    283         this.super();
    284         this.updateThemes();
    285       },
    286 
    287       iconsChanged: function() {
    288         var ox = this.offsetX;
    289         var oy = this.offsetY;
    290         this.icons && this.icons.split(/\s+/g).forEach(function(name, i) {
    291           this.iconNames.push(name);
    292           this.iconMap[name] = {
    293             offsetX: ox,
    294             offsetY: oy
    295           }
    296           if (ox + this.iconSize < this.width) {
    297             ox += this.iconSize;
    298           } else {
    299             ox = this.offsetX;
    300             oy += this.iconSize;
    301           }
    302         }, this);
    303       },
    304 
    305       updateThemes: function() {
    306         var ts = this.querySelectorAll('property[theme]');
    307         ts && ts.array().forEach(function(t) {
    308           this.themes[t.getAttribute('theme')] = {
    309             offsetX: parseInt(t.getAttribute('offsetX')) || 0,
    310             offsetY: parseInt(t.getAttribute('offsetY')) || 0
    311           };
    312         }, this);
    313       },
    314 
    315       // TODO(ffu): support retrived by index e.g. getOffset(10);
    316       /**
    317        * Returns an object containing `offsetX` and `offsetY` properties which
    318        * specify the pixel locaion in the iconset's src file for the given
    319        * `icon` and `theme`. It's uncommon to call this method. It is useful,
    320        * for example, to manually position a css backgroundImage to the proper
    321        * offset. It's more common to use the `applyIcon` method.
    322        *
    323        * @method getOffset
    324        * @param {String|Number} icon The name of the icon or the index of the
    325        * icon within in the icon image.
    326        * @param {String} theme The name of the theme.
    327        * @returns {Object} An object specifying the offset of the given icon 
    328        * within the icon resource file; `offsetX` is the horizontal offset and
    329        * `offsetY` is the vertical offset. Both values are in pixel units.
    330        */
    331       getOffset: function(icon, theme) {
    332         var i = this.iconMap[icon];
    333         if (!i) {
    334           var n = this.iconNames[Number(icon)];
    335           i = this.iconMap[n];
    336         }
    337         var t = this.themes[theme];
    338         if (i && t) {
    339           return {
    340             offsetX: i.offsetX + t.offsetX,
    341             offsetY: i.offsetY + t.offsetY
    342           }
    343         }
    344         return i;
    345       },
    346 
    347       /**
    348        * Applies an icon to the given element as a css background image. This
    349        * method does not size the element, and it's often necessary to set 
    350        * the element's height and width so that the background image is visible.
    351        *
    352        * @method applyIcon
    353        * @param {Element} element The element to which the background is
    354        * applied.
    355        * @param {String|Number} icon The name or index of the icon to apply.
    356        * @param {String} theme (optional) The name of the theme for the icon.
    357        * @param {Number} scale (optional, defaults to 1) A scaling factor 
    358        * with which the icon can be magnified.
    359        */
    360       applyIcon: function(element, icon, scale) {
    361          var offset = this.getOffset(icon);
    362          scale = scale || 1;
    363          if (element && offset) {
    364            var style = element.style;
    365            style.backgroundImage = 'url(' + this.src + ')';
    366            style.backgroundPosition = (-offset.offsetX * scale + 'px') + 
    367               ' ' + (-offset.offsetY * scale + 'px');
    368            style.backgroundSize = scale === 1 ? 'auto' :
    369               this.width * scale + 'px';
    370          }
    371       }
    372 
    373     });
    374 
    375   ;
    376 
    377 
    378     Polymer('core-iconset-svg', {
    379 
    380 
    381       /**
    382        * The size of an individual icon. Note that icons must be square.
    383        *
    384        * @attribute iconSize
    385        * @type number
    386        * @default 24
    387        */
    388       iconSize: 24,
    389       type: 'iconset',
    390 
    391       created: function() {
    392         this._icons = {};
    393       },
    394 
    395       ready: function() {
    396         this.super();
    397         this.updateIcons();
    398       },
    399 
    400       iconById: function(id) {
    401         return this._icons[id] || (this._icons[id] = this.querySelector('#' + id));
    402       },
    403 
    404       cloneIcon: function(id) {
    405         var icon = this.iconById(id);
    406         if (icon) {
    407           var content = icon.cloneNode(true);
    408           var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    409           svg.setAttribute('viewBox', '0 0 ' + this.iconSize + ' ' +
    410               this.iconSize);
    411           // NOTE(dfreedm): work around https://crbug.com/370136
    412           svg.style.pointerEvents = 'none';
    413           svg.appendChild(content);
    414           return svg;
    415         }
    416       },
    417 
    418       get iconNames() {
    419         if (!this._iconNames) {
    420           this._iconNames = this.findIconNames();
    421         }
    422         return this._iconNames;
    423       },
    424 
    425       findIconNames: function() {
    426         var icons = this.querySelectorAll('[id]').array();
    427         if (icons.length) {
    428           return icons.map(function(n){ return n.id });
    429         }
    430       },
    431 
    432       /**
    433        * Applies an icon to the given element. The svg icon is added to the
    434        * element's shadowRoot if one exists or directly to itself.
    435        *
    436        * @method applyIcon
    437        * @param {Element} element The element to which the icon is
    438        * applied.
    439        * @param {String|Number} icon The name the icon to apply.
    440        */
    441       applyIcon: function(element, icon, scale) {
    442         var root = element.shadowRoot || element;
    443         // remove old
    444         var old = root.querySelector('svg');
    445         if (old) {
    446           old.remove();
    447         }
    448         // install new
    449         var svg = this.cloneIcon(icon);
    450         if (!svg) {
    451           return;
    452         }
    453         var size = scale * this.iconSize;
    454         if (size) {
    455           svg.style.height = svg.style.width = size + 'px';
    456         } else {
    457           svg.setAttribute('height', '100%');
    458           svg.setAttribute('width', '100%');
    459           svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
    460         }
    461         svg.style.display = 'block';
    462         root.insertBefore(svg, root.firstElementChild);
    463       },
    464       
    465       /**
    466        * Tell users of the iconset, that the set has loaded.
    467        * This finds all elements matching the selector argument and calls 
    468        * the method argument on them.
    469        * @method updateIcons
    470        * @param selector {string} css selector to identify iconset users, 
    471        * defaults to '[icon]'
    472        * @param method {string} method to call on found elements, 
    473        * defaults to 'updateIcon'
    474        */
    475       updateIcons: function(selector, method) {
    476         selector = selector || '[icon]';
    477         method = method || 'updateIcon';
    478         var deep = window.ShadowDOMPolyfill ? '' : 'html /deep/ ';
    479         var i$ = document.querySelectorAll(deep + selector);
    480         for (var i=0, e; e=i$[i]; i++) {
    481           if (e[method]) {
    482             e[method].call(e);
    483           }
    484         }
    485       }
    486       
    487 
    488     });
    489 
    490   ;
    491 
    492 (function() {
    493   
    494   // mono-state
    495   var meta;
    496   
    497   Polymer('core-icon', {
    498 
    499     /**
    500      * The URL of an image for the icon. If the src property is specified,
    501      * the icon property should not be.
    502      *
    503      * @attribute src
    504      * @type string
    505      * @default ''
    506      */
    507     src: '',
    508 
    509     /**
    510      * Specifies the size of the icon in pixel units.
    511      *
    512      * @attribute size
    513      * @type string
    514      * @default 24
    515      */
    516     size: 24,
    517 
    518     /**
    519      * Specifies the icon name or index in the set of icons available in
    520      * the icon's icon set. If the icon property is specified,
    521      * the src property should not be.
    522      *
    523      * @attribute icon
    524      * @type string
    525      * @default ''
    526      */
    527     icon: '',
    528 
    529     observe: {
    530       'size icon': 'updateIcon'
    531     },
    532 
    533     defaultIconset: 'icons',
    534 
    535     ready: function() {
    536       if (!meta) {
    537         meta = document.createElement('core-iconset');
    538       }
    539       this.updateIcon();
    540     },
    541 
    542     srcChanged: function() {
    543       this.style.backgroundImage = 'url(' + this.src + ')';
    544       this.style.backgroundPosition = 'center';
    545       this.style.backgroundSize = this.size + 'px ' + this.size + 'px';
    546     },
    547 
    548     getIconset: function(name) {
    549       return meta.byId(name || this.defaultIconset);
    550     },
    551 
    552     updateIcon: function() {
    553       if (this.size) {
    554         this.style.width = this.style.height = this.size + 'px';
    555       }
    556       if (this.icon) {
    557         var parts = String(this.icon).split(':');
    558         var icon = parts.pop();
    559         if (icon) {
    560           var set = this.getIconset(parts.pop());
    561           if (set) {
    562             set.applyIcon(this, icon, this.size / set.iconSize);
    563           }
    564         }
    565       }
    566     }
    567 
    568   });
    569   
    570 })();
    571 ;
    572 
    573 
    574     Polymer('core-icon-button', {
    575 
    576       /**
    577        * The URL of an image for the icon.  Should not use `icon` property
    578        * if you are using this property.
    579        *
    580        * @attribute src
    581        * @type string
    582        * @default ''
    583        */
    584       src: '',
    585 
    586       /**
    587        * If true, border is placed around the button to indicate it's
    588        * active state.
    589        *
    590        * @attribute active
    591        * @type boolean
    592        * @default false
    593        */
    594       active: false,
    595 
    596       /**
    597        * Specifies the icon name or index in the set of icons available in
    598        * the icon set.  Should not use `src` property if you are using this
    599        * property.
    600        *
    601        * @attribute icon
    602        * @type string
    603        * @default ''
    604        */
    605       icon: '',
    606 
    607       activeChanged: function() {
    608         this.classList.toggle('selected', this.active);
    609       }
    610 
    611     });
    612 
    613   ;
    614 Polymer('core-toolbar');;
    615 
    616 
    617   Polymer('core-header-panel', {
    618 
    619     publish: {
    620       /**
    621        * Controls header and scrolling behavior. Options are
    622        * `standard`, `seamed`, `waterfall`, `waterfall-tall`, 
    623        * `waterfall-medium-tall`, `scroll` and `cover`.
    624        * Default is `standard`.
    625        *
    626        * `standard`: The header is a step above the panel. The header will consume the 
    627        * panel at the point of entry, preventing it from passing through to the 
    628        * opposite side.
    629        *
    630        * `seamed`: The header is presented as seamed with the panel.
    631        *
    632        * `waterfall`: Similar to standard mode, but header is initially presented as 
    633        * seamed with panel, but then separates to form the step.
    634        *
    635        * `waterfall-tall`: The header is initially taller (`tall` class is added to 
    636        * the header).  As the user scrolls, the header separates (forming an edge)
    637        * while condensing (`tall` class is removed from the header).
    638        *
    639        * `scroll`: The header keeps its seam with the panel, and is pushed off screen.
    640        *
    641        * `cover`: The panel covers the whole `core-header-panel` including the
    642        * header. This allows user to style the panel in such a way that the panel is
    643        * partially covering the header.
    644        *
    645        *     <style>
    646        *       core-header-panel[mode=cover]::shadow #mainContainer {
    647        *         left: 80px;
    648        *       }
    649        *       .content {
    650        *         margin: 60px 60px 60px 0;
    651        *       }
    652        *     </style>
    653        * 
    654        *     <core-header-panel mode="cover">
    655        *       <core-appbar class="tall">
    656        *         <core-icon-button icon="menu"></core-icon-button>
    657        *       </core-appbar>
    658        *       <div class="content"></div>
    659        *     </core-header-panel>
    660        *
    661        * @attribute mode
    662        * @type string
    663        * @default ''
    664        */
    665       mode: {value: '', reflect: true},
    666       
    667       /**
    668        * The class used in waterfall-tall mode.  Change this if the header
    669        * accepts a different class for toggling height, e.g. "medium-tall"
    670        *
    671        * @attribute tallClass
    672        * @type string
    673        * @default 'tall'
    674        */
    675       tallClass: 'tall',
    676       
    677       /**
    678        * If true, the drop-shadow is always shown no matter what mode is set to.
    679        *
    680        * @attribute shadow
    681        * @type boolean
    682        * @default false
    683        */
    684       shadow: false,
    685     },
    686     
    687     domReady: function() {
    688       this.async('scroll');
    689     },
    690 
    691     modeChanged: function() {
    692       this.scroll();
    693     },
    694 
    695     get header() {
    696       return this.$.headerContent.getDistributedNodes()[0];
    697     },
    698     
    699     scroll: function() {
    700       var shadowMode = {'waterfall': 1, 'waterfall-tall': 1};
    701       var noShadow = {'seamed': 1, 'cover': 1, 'scroll': 1};
    702       var tallMode = {'waterfall-tall': 1};
    703       
    704       var main = this.$.mainContainer;
    705       var header = this.header;
    706       
    707       var sTop = main.scrollTop;
    708       var atTop = sTop === 0;
    709       
    710       if (header) {
    711         this.$.dropShadow.classList.toggle('hidden', !this.shadow &&
    712             (atTop && shadowMode[this.mode] || noShadow[this.mode]));
    713         
    714         if (tallMode[this.mode]) {
    715           header.classList.toggle(this.tallClass, atTop);
    716         }
    717         
    718         header.classList.toggle('animate', tallMode[this.mode]);
    719       }
    720     }
    721 
    722   });
    723 
    724 ;
    725 /**
    726  * marked - a markdown parser
    727  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
    728  * https://github.com/chjj/marked
    729  */
    730 
    731 ;(function() {
    732 
    733 /**
    734  * Block-Level Grammar
    735  */
    736 
    737 var block = {
    738   newline: /^\n+/,
    739   code: /^( {4}[^\n]+\n*)+/,
    740   fences: noop,
    741   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
    742   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
    743   nptable: noop,
    744   lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
    745   blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
    746   list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
    747   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
    748   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
    749   table: noop,
    750   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
    751   text: /^[^\n]+/
    752 };
    753 
    754 block.bullet = /(?:[*+-]|\d+\.)/;
    755 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
    756 block.item = replace(block.item, 'gm')
    757   (/bull/g, block.bullet)
    758   ();
    759 
    760 block.list = replace(block.list)
    761   (/bull/g, block.bullet)
    762   ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
    763   ('def', '\\n+(?=' + block.def.source + ')')
    764   ();
    765 
    766 block.blockquote = replace(block.blockquote)
    767   ('def', block.def)
    768   ();
    769 
    770 block._tag = '(?!(?:'
    771   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
    772   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
    773   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
    774 
    775 block.html = replace(block.html)
    776   ('comment', /<!--[\s\S]*?-->/)
    777   ('closed', /<(tag)[\s\S]+?<\/\1>/)
    778   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
    779   (/tag/g, block._tag)
    780   ();
    781 
    782 block.paragraph = replace(block.paragraph)
    783   ('hr', block.hr)
    784   ('heading', block.heading)
    785   ('lheading', block.lheading)
    786   ('blockquote', block.blockquote)
    787   ('tag', '<' + block._tag)
    788   ('def', block.def)
    789   ();
    790 
    791 /**
    792  * Normal Block Grammar
    793  */
    794 
    795 block.normal = merge({}, block);
    796 
    797 /**
    798  * GFM Block Grammar
    799  */
    800 
    801 block.gfm = merge({}, block.normal, {
    802   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
    803   paragraph: /^/
    804 });
    805 
    806 block.gfm.paragraph = replace(block.paragraph)
    807   ('(?!', '(?!'
    808     + block.gfm.fences.source.replace('\\1', '\\2') + '|'
    809     + block.list.source.replace('\\1', '\\3') + '|')
    810   ();
    811 
    812 /**
    813  * GFM + Tables Block Grammar
    814  */
    815 
    816 block.tables = merge({}, block.gfm, {
    817   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
    818   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
    819 });
    820 
    821 /**
    822  * Block Lexer
    823  */
    824 
    825 function Lexer(options) {
    826   this.tokens = [];
    827   this.tokens.links = {};
    828   this.options = options || marked.defaults;
    829   this.rules = block.normal;
    830 
    831   if (this.options.gfm) {
    832     if (this.options.tables) {
    833       this.rules = block.tables;
    834     } else {
    835       this.rules = block.gfm;
    836     }
    837   }
    838 }
    839 
    840 /**
    841  * Expose Block Rules
    842  */
    843 
    844 Lexer.rules = block;
    845 
    846 /**
    847  * Static Lex Method
    848  */
    849 
    850 Lexer.lex = function(src, options) {
    851   var lexer = new Lexer(options);
    852   return lexer.lex(src);
    853 };
    854 
    855 /**
    856  * Preprocessing
    857  */
    858 
    859 Lexer.prototype.lex = function(src) {
    860   src = src
    861     .replace(/\r\n|\r/g, '\n')
    862     .replace(/\t/g, '    ')
    863     .replace(/\u00a0/g, ' ')
    864     .replace(/\u2424/g, '\n');
    865 
    866   return this.token(src, true);
    867 };
    868 
    869 /**
    870  * Lexing
    871  */
    872 
    873 Lexer.prototype.token = function(src, top, bq) {
    874   var src = src.replace(/^ +$/gm, '')
    875     , next
    876     , loose
    877     , cap
    878     , bull
    879     , b
    880     , item
    881     , space
    882     , i
    883     , l;
    884 
    885   while (src) {
    886     // newline
    887     if (cap = this.rules.newline.exec(src)) {
    888       src = src.substring(cap[0].length);
    889       if (cap[0].length > 1) {
    890         this.tokens.push({
    891           type: 'space'
    892         });
    893       }
    894     }
    895 
    896     // code
    897     if (cap = this.rules.code.exec(src)) {
    898       src = src.substring(cap[0].length);
    899       cap = cap[0].replace(/^ {4}/gm, '');
    900       this.tokens.push({
    901         type: 'code',
    902         text: !this.options.pedantic
    903           ? cap.replace(/\n+$/, '')
    904           : cap
    905       });
    906       continue;
    907     }
    908 
    909     // fences (gfm)
    910     if (cap = this.rules.fences.exec(src)) {
    911       src = src.substring(cap[0].length);
    912       this.tokens.push({
    913         type: 'code',
    914         lang: cap[2],
    915         text: cap[3]
    916       });
    917       continue;
    918     }
    919 
    920     // heading
    921     if (cap = this.rules.heading.exec(src)) {
    922       src = src.substring(cap[0].length);
    923       this.tokens.push({
    924         type: 'heading',
    925         depth: cap[1].length,
    926         text: cap[2]
    927       });
    928       continue;
    929     }
    930 
    931     // table no leading pipe (gfm)
    932     if (top && (cap = this.rules.nptable.exec(src))) {
    933       src = src.substring(cap[0].length);
    934 
    935       item = {
    936         type: 'table',
    937         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
    938         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
    939         cells: cap[3].replace(/\n$/, '').split('\n')
    940       };
    941 
    942       for (i = 0; i < item.align.length; i++) {
    943         if (/^ *-+: *$/.test(item.align[i])) {
    944           item.align[i] = 'right';
    945         } else if (/^ *:-+: *$/.test(item.align[i])) {
    946           item.align[i] = 'center';
    947         } else if (/^ *:-+ *$/.test(item.align[i])) {
    948           item.align[i] = 'left';
    949         } else {
    950           item.align[i] = null;
    951         }
    952       }
    953 
    954       for (i = 0; i < item.cells.length; i++) {
    955         item.cells[i] = item.cells[i].split(/ *\| */);
    956       }
    957 
    958       this.tokens.push(item);
    959 
    960       continue;
    961     }
    962 
    963     // lheading
    964     if (cap = this.rules.lheading.exec(src)) {
    965       src = src.substring(cap[0].length);
    966       this.tokens.push({
    967         type: 'heading',
    968         depth: cap[2] === '=' ? 1 : 2,
    969         text: cap[1]
    970       });
    971       continue;
    972     }
    973 
    974     // hr
    975     if (cap = this.rules.hr.exec(src)) {
    976       src = src.substring(cap[0].length);
    977       this.tokens.push({
    978         type: 'hr'
    979       });
    980       continue;
    981     }
    982 
    983     // blockquote
    984     if (cap = this.rules.blockquote.exec(src)) {
    985       src = src.substring(cap[0].length);
    986 
    987       this.tokens.push({
    988         type: 'blockquote_start'
    989       });
    990 
    991       cap = cap[0].replace(/^ *> ?/gm, '');
    992 
    993       // Pass `top` to keep the current
    994       // "toplevel" state. This is exactly
    995       // how markdown.pl works.
    996       this.token(cap, top, true);
    997 
    998       this.tokens.push({
    999         type: 'blockquote_end'
   1000       });
   1001 
   1002       continue;
   1003     }
   1004 
   1005     // list
   1006     if (cap = this.rules.list.exec(src)) {
   1007       src = src.substring(cap[0].length);
   1008       bull = cap[2];
   1009 
   1010       this.tokens.push({
   1011         type: 'list_start',
   1012         ordered: bull.length > 1
   1013       });
   1014 
   1015       // Get each top-level item.
   1016       cap = cap[0].match(this.rules.item);
   1017 
   1018       next = false;
   1019       l = cap.length;
   1020       i = 0;
   1021 
   1022       for (; i < l; i++) {
   1023         item = cap[i];
   1024 
   1025         // Remove the list item's bullet
   1026         // so it is seen as the next token.
   1027         space = item.length;
   1028         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
   1029 
   1030         // Outdent whatever the
   1031         // list item contains. Hacky.
   1032         if (~item.indexOf('\n ')) {
   1033           space -= item.length;
   1034           item = !this.options.pedantic
   1035             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
   1036             : item.replace(/^ {1,4}/gm, '');
   1037         }
   1038 
   1039         // Determine whether the next list item belongs here.
   1040         // Backpedal if it does not belong in this list.
   1041         if (this.options.smartLists && i !== l - 1) {
   1042           b = block.bullet.exec(cap[i + 1])[0];
   1043           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
   1044             src = cap.slice(i + 1).join('\n') + src;
   1045             i = l - 1;
   1046           }
   1047         }
   1048 
   1049         // Determine whether item is loose or not.
   1050         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
   1051         // for discount behavior.
   1052         loose = next || /\n\n(?!\s*$)/.test(item);
   1053         if (i !== l - 1) {
   1054           next = item.charAt(item.length - 1) === '\n';
   1055           if (!loose) loose = next;
   1056         }
   1057 
   1058         this.tokens.push({
   1059           type: loose
   1060             ? 'loose_item_start'
   1061             : 'list_item_start'
   1062         });
   1063 
   1064         // Recurse.
   1065         this.token(item, false, bq);
   1066 
   1067         this.tokens.push({
   1068           type: 'list_item_end'
   1069         });
   1070       }
   1071 
   1072       this.tokens.push({
   1073         type: 'list_end'
   1074       });
   1075 
   1076       continue;
   1077     }
   1078 
   1079     // html
   1080     if (cap = this.rules.html.exec(src)) {
   1081       src = src.substring(cap[0].length);
   1082       this.tokens.push({
   1083         type: this.options.sanitize
   1084           ? 'paragraph'
   1085           : 'html',
   1086         pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
   1087         text: cap[0]
   1088       });
   1089       continue;
   1090     }
   1091 
   1092     // def
   1093     if ((!bq && top) && (cap = this.rules.def.exec(src))) {
   1094       src = src.substring(cap[0].length);
   1095       this.tokens.links[cap[1].toLowerCase()] = {
   1096         href: cap[2],
   1097         title: cap[3]
   1098       };
   1099       continue;
   1100     }
   1101 
   1102     // table (gfm)
   1103     if (top && (cap = this.rules.table.exec(src))) {
   1104       src = src.substring(cap[0].length);
   1105 
   1106       item = {
   1107         type: 'table',
   1108         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
   1109         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
   1110         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
   1111       };
   1112 
   1113       for (i = 0; i < item.align.length; i++) {
   1114         if (/^ *-+: *$/.test(item.align[i])) {
   1115           item.align[i] = 'right';
   1116         } else if (/^ *:-+: *$/.test(item.align[i])) {
   1117           item.align[i] = 'center';
   1118         } else if (/^ *:-+ *$/.test(item.align[i])) {
   1119           item.align[i] = 'left';
   1120         } else {
   1121           item.align[i] = null;
   1122         }
   1123       }
   1124 
   1125       for (i = 0; i < item.cells.length; i++) {
   1126         item.cells[i] = item.cells[i]
   1127           .replace(/^ *\| *| *\| *$/g, '')
   1128           .split(/ *\| */);
   1129       }
   1130 
   1131       this.tokens.push(item);
   1132 
   1133       continue;
   1134     }
   1135 
   1136     // top-level paragraph
   1137     if (top && (cap = this.rules.paragraph.exec(src))) {
   1138       src = src.substring(cap[0].length);
   1139       this.tokens.push({
   1140         type: 'paragraph',
   1141         text: cap[1].charAt(cap[1].length - 1) === '\n'
   1142           ? cap[1].slice(0, -1)
   1143           : cap[1]
   1144       });
   1145       continue;
   1146     }
   1147 
   1148     // text
   1149     if (cap = this.rules.text.exec(src)) {
   1150       // Top-level should never reach here.
   1151       src = src.substring(cap[0].length);
   1152       this.tokens.push({
   1153         type: 'text',
   1154         text: cap[0]
   1155       });
   1156       continue;
   1157     }
   1158 
   1159     if (src) {
   1160       throw new
   1161         Error('Infinite loop on byte: ' + src.charCodeAt(0));
   1162     }
   1163   }
   1164 
   1165   return this.tokens;
   1166 };
   1167 
   1168 /**
   1169  * Inline-Level Grammar
   1170  */
   1171 
   1172 var inline = {
   1173   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
   1174   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
   1175   url: noop,
   1176   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
   1177   link: /^!?\[(inside)\]\(href\)/,
   1178   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
   1179   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
   1180   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
   1181   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
   1182   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
   1183   br: /^ {2,}\n(?!\s*$)/,
   1184   del: noop,
   1185   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
   1186 };
   1187 
   1188 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
   1189 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
   1190 
   1191 inline.link = replace(inline.link)
   1192   ('inside', inline._inside)
   1193   ('href', inline._href)
   1194   ();
   1195 
   1196 inline.reflink = replace(inline.reflink)
   1197   ('inside', inline._inside)
   1198   ();
   1199 
   1200 /**
   1201  * Normal Inline Grammar
   1202  */
   1203 
   1204 inline.normal = merge({}, inline);
   1205 
   1206 /**
   1207  * Pedantic Inline Grammar
   1208  */
   1209 
   1210 inline.pedantic = merge({}, inline.normal, {
   1211   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
   1212   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
   1213 });
   1214 
   1215 /**
   1216  * GFM Inline Grammar
   1217  */
   1218 
   1219 inline.gfm = merge({}, inline.normal, {
   1220   escape: replace(inline.escape)('])', '~|])')(),
   1221   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
   1222   del: /^~~(?=\S)([\s\S]*?\S)~~/,
   1223   text: replace(inline.text)
   1224     (']|', '~]|')
   1225     ('|', '|https?://|')
   1226     ()
   1227 });
   1228 
   1229 /**
   1230  * GFM + Line Breaks Inline Grammar
   1231  */
   1232 
   1233 inline.breaks = merge({}, inline.gfm, {
   1234   br: replace(inline.br)('{2,}', '*')(),
   1235   text: replace(inline.gfm.text)('{2,}', '*')()
   1236 });
   1237 
   1238 /**
   1239  * Inline Lexer & Compiler
   1240  */
   1241 
   1242 function InlineLexer(links, options) {
   1243   this.options = options || marked.defaults;
   1244   this.links = links;
   1245   this.rules = inline.normal;
   1246   this.renderer = this.options.renderer || new Renderer;
   1247   this.renderer.options = this.options;
   1248 
   1249   if (!this.links) {
   1250     throw new
   1251       Error('Tokens array requires a `links` property.');
   1252   }
   1253 
   1254   if (this.options.gfm) {
   1255     if (this.options.breaks) {
   1256       this.rules = inline.breaks;
   1257     } else {
   1258       this.rules = inline.gfm;
   1259     }
   1260   } else if (this.options.pedantic) {
   1261     this.rules = inline.pedantic;
   1262   }
   1263 }
   1264 
   1265 /**
   1266  * Expose Inline Rules
   1267  */
   1268 
   1269 InlineLexer.rules = inline;
   1270 
   1271 /**
   1272  * Static Lexing/Compiling Method
   1273  */
   1274 
   1275 InlineLexer.output = function(src, links, options) {
   1276   var inline = new InlineLexer(links, options);
   1277   return inline.output(src);
   1278 };
   1279 
   1280 /**
   1281  * Lexing/Compiling
   1282  */
   1283 
   1284 InlineLexer.prototype.output = function(src) {
   1285   var out = ''
   1286     , link
   1287     , text
   1288     , href
   1289     , cap;
   1290 
   1291   while (src) {
   1292     // escape
   1293     if (cap = this.rules.escape.exec(src)) {
   1294       src = src.substring(cap[0].length);
   1295       out += cap[1];
   1296       continue;
   1297     }
   1298 
   1299     // autolink
   1300     if (cap = this.rules.autolink.exec(src)) {
   1301       src = src.substring(cap[0].length);
   1302       if (cap[2] === '@') {
   1303         text = cap[1].charAt(6) === ':'
   1304           ? this.mangle(cap[1].substring(7))
   1305           : this.mangle(cap[1]);
   1306         href = this.mangle('mailto:') + text;
   1307       } else {
   1308         text = escape(cap[1]);
   1309         href = text;
   1310       }
   1311       out += this.renderer.link(href, null, text);
   1312       continue;
   1313     }
   1314 
   1315     // url (gfm)
   1316     if (!this.inLink && (cap = this.rules.url.exec(src))) {
   1317       src = src.substring(cap[0].length);
   1318       text = escape(cap[1]);
   1319       href = text;
   1320       out += this.renderer.link(href, null, text);
   1321       continue;
   1322     }
   1323 
   1324     // tag
   1325     if (cap = this.rules.tag.exec(src)) {
   1326       if (!this.inLink && /^<a /i.test(cap[0])) {
   1327         this.inLink = true;
   1328       } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
   1329         this.inLink = false;
   1330       }
   1331       src = src.substring(cap[0].length);
   1332       out += this.options.sanitize
   1333         ? escape(cap[0])
   1334         : cap[0];
   1335       continue;
   1336     }
   1337 
   1338     // link
   1339     if (cap = this.rules.link.exec(src)) {
   1340       src = src.substring(cap[0].length);
   1341       this.inLink = true;
   1342       out += this.outputLink(cap, {
   1343         href: cap[2],
   1344         title: cap[3]
   1345       });
   1346       this.inLink = false;
   1347       continue;
   1348     }
   1349 
   1350     // reflink, nolink
   1351     if ((cap = this.rules.reflink.exec(src))
   1352         || (cap = this.rules.nolink.exec(src))) {
   1353       src = src.substring(cap[0].length);
   1354       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
   1355       link = this.links[link.toLowerCase()];
   1356       if (!link || !link.href) {
   1357         out += cap[0].charAt(0);
   1358         src = cap[0].substring(1) + src;
   1359         continue;
   1360       }
   1361       this.inLink = true;
   1362       out += this.outputLink(cap, link);
   1363       this.inLink = false;
   1364       continue;
   1365     }
   1366 
   1367     // strong
   1368     if (cap = this.rules.strong.exec(src)) {
   1369       src = src.substring(cap[0].length);
   1370       out += this.renderer.strong(this.output(cap[2] || cap[1]));
   1371       continue;
   1372     }
   1373 
   1374     // em
   1375     if (cap = this.rules.em.exec(src)) {
   1376       src = src.substring(cap[0].length);
   1377       out += this.renderer.em(this.output(cap[2] || cap[1]));
   1378       continue;
   1379     }
   1380 
   1381     // code
   1382     if (cap = this.rules.code.exec(src)) {
   1383       src = src.substring(cap[0].length);
   1384       out += this.renderer.codespan(escape(cap[2], true));
   1385       continue;
   1386     }
   1387 
   1388     // br
   1389     if (cap = this.rules.br.exec(src)) {
   1390       src = src.substring(cap[0].length);
   1391       out += this.renderer.br();
   1392       continue;
   1393     }
   1394 
   1395     // del (gfm)
   1396     if (cap = this.rules.del.exec(src)) {
   1397       src = src.substring(cap[0].length);
   1398       out += this.renderer.del(this.output(cap[1]));
   1399       continue;
   1400     }
   1401 
   1402     // text
   1403     if (cap = this.rules.text.exec(src)) {
   1404       src = src.substring(cap[0].length);
   1405       out += escape(this.smartypants(cap[0]));
   1406       continue;
   1407     }
   1408 
   1409     if (src) {
   1410       throw new
   1411         Error('Infinite loop on byte: ' + src.charCodeAt(0));
   1412     }
   1413   }
   1414 
   1415   return out;
   1416 };
   1417 
   1418 /**
   1419  * Compile Link
   1420  */
   1421 
   1422 InlineLexer.prototype.outputLink = function(cap, link) {
   1423   var href = escape(link.href)
   1424     , title = link.title ? escape(link.title) : null;
   1425 
   1426   return cap[0].charAt(0) !== '!'
   1427     ? this.renderer.link(href, title, this.output(cap[1]))
   1428     : this.renderer.image(href, title, escape(cap[1]));
   1429 };
   1430 
   1431 /**
   1432  * Smartypants Transformations
   1433  */
   1434 
   1435 InlineLexer.prototype.smartypants = function(text) {
   1436   if (!this.options.smartypants) return text;
   1437   return text
   1438     // em-dashes
   1439     .replace(/--/g, '\u2014')
   1440     // opening singles
   1441     .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
   1442     // closing singles & apostrophes
   1443     .replace(/'/g, '\u2019')
   1444     // opening doubles
   1445     .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
   1446     // closing doubles
   1447     .replace(/"/g, '\u201d')
   1448     // ellipses
   1449     .replace(/\.{3}/g, '\u2026');
   1450 };
   1451 
   1452 /**
   1453  * Mangle Links
   1454  */
   1455 
   1456 InlineLexer.prototype.mangle = function(text) {
   1457   var out = ''
   1458     , l = text.length
   1459     , i = 0
   1460     , ch;
   1461 
   1462   for (; i < l; i++) {
   1463     ch = text.charCodeAt(i);
   1464     if (Math.random() > 0.5) {
   1465       ch = 'x' + ch.toString(16);
   1466     }
   1467     out += '&#' + ch + ';';
   1468   }
   1469 
   1470   return out;
   1471 };
   1472 
   1473 /**
   1474  * Renderer
   1475  */
   1476 
   1477 function Renderer(options) {
   1478   this.options = options || {};
   1479 }
   1480 
   1481 Renderer.prototype.code = function(code, lang, escaped) {
   1482   if (this.options.highlight) {
   1483     var out = this.options.highlight(code, lang);
   1484     if (out != null && out !== code) {
   1485       escaped = true;
   1486       code = out;
   1487     }
   1488   }
   1489 
   1490   if (!lang) {
   1491     return '<pre><code>'
   1492       + (escaped ? code : escape(code, true))
   1493       + '\n</code></pre>';
   1494   }
   1495 
   1496   return '<pre><code class="'
   1497     + this.options.langPrefix
   1498     + escape(lang, true)
   1499     + '">'
   1500     + (escaped ? code : escape(code, true))
   1501     + '\n</code></pre>\n';
   1502 };
   1503 
   1504 Renderer.prototype.blockquote = function(quote) {
   1505   return '<blockquote>\n' + quote + '</blockquote>\n';
   1506 };
   1507 
   1508 Renderer.prototype.html = function(html) {
   1509   return html;
   1510 };
   1511 
   1512 Renderer.prototype.heading = function(text, level, raw) {
   1513   return '<h'
   1514     + level
   1515     + ' id="'
   1516     + this.options.headerPrefix
   1517     + raw.toLowerCase().replace(/[^\w]+/g, '-')
   1518     + '">'
   1519     + text
   1520     + '</h'
   1521     + level
   1522     + '>\n';
   1523 };
   1524 
   1525 Renderer.prototype.hr = function() {
   1526   return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
   1527 };
   1528 
   1529 Renderer.prototype.list = function(body, ordered) {
   1530   var type = ordered ? 'ol' : 'ul';
   1531   return '<' + type + '>\n' + body + '</' + type + '>\n';
   1532 };
   1533 
   1534 Renderer.prototype.listitem = function(text) {
   1535   return '<li>' + text + '</li>\n';
   1536 };
   1537 
   1538 Renderer.prototype.paragraph = function(text) {
   1539   return '<p>' + text + '</p>\n';
   1540 };
   1541 
   1542 Renderer.prototype.table = function(header, body) {
   1543   return '<table>\n'
   1544     + '<thead>\n'
   1545     + header
   1546     + '</thead>\n'
   1547     + '<tbody>\n'
   1548     + body
   1549     + '</tbody>\n'
   1550     + '</table>\n';
   1551 };
   1552 
   1553 Renderer.prototype.tablerow = function(content) {
   1554   return '<tr>\n' + content + '</tr>\n';
   1555 };
   1556 
   1557 Renderer.prototype.tablecell = function(content, flags) {
   1558   var type = flags.header ? 'th' : 'td';
   1559   var tag = flags.align
   1560     ? '<' + type + ' style="text-align:' + flags.align + '">'
   1561     : '<' + type + '>';
   1562   return tag + content + '</' + type + '>\n';
   1563 };
   1564 
   1565 // span level renderer
   1566 Renderer.prototype.strong = function(text) {
   1567   return '<strong>' + text + '</strong>';
   1568 };
   1569 
   1570 Renderer.prototype.em = function(text) {
   1571   return '<em>' + text + '</em>';
   1572 };
   1573 
   1574 Renderer.prototype.codespan = function(text) {
   1575   return '<code>' + text + '</code>';
   1576 };
   1577 
   1578 Renderer.prototype.br = function() {
   1579   return this.options.xhtml ? '<br/>' : '<br>';
   1580 };
   1581 
   1582 Renderer.prototype.del = function(text) {
   1583   return '<del>' + text + '</del>';
   1584 };
   1585 
   1586 Renderer.prototype.link = function(href, title, text) {
   1587   if (this.options.sanitize) {
   1588     try {
   1589       var prot = decodeURIComponent(unescape(href))
   1590         .replace(/[^\w:]/g, '')
   1591         .toLowerCase();
   1592     } catch (e) {
   1593       return '';
   1594     }
   1595     if (prot.indexOf('javascript:') === 0) {
   1596       return '';
   1597     }
   1598   }
   1599   var out = '<a href="' + href + '"';
   1600   if (title) {
   1601     out += ' title="' + title + '"';
   1602   }
   1603   out += '>' + text + '</a>';
   1604   return out;
   1605 };
   1606 
   1607 Renderer.prototype.image = function(href, title, text) {
   1608   var out = '<img src="' + href + '" alt="' + text + '"';
   1609   if (title) {
   1610     out += ' title="' + title + '"';
   1611   }
   1612   out += this.options.xhtml ? '/>' : '>';
   1613   return out;
   1614 };
   1615 
   1616 /**
   1617  * Parsing & Compiling
   1618  */
   1619 
   1620 function Parser(options) {
   1621   this.tokens = [];
   1622   this.token = null;
   1623   this.options = options || marked.defaults;
   1624   this.options.renderer = this.options.renderer || new Renderer;
   1625   this.renderer = this.options.renderer;
   1626   this.renderer.options = this.options;
   1627 }
   1628 
   1629 /**
   1630  * Static Parse Method
   1631  */
   1632 
   1633 Parser.parse = function(src, options, renderer) {
   1634   var parser = new Parser(options, renderer);
   1635   return parser.parse(src);
   1636 };
   1637 
   1638 /**
   1639  * Parse Loop
   1640  */
   1641 
   1642 Parser.prototype.parse = function(src) {
   1643   this.inline = new InlineLexer(src.links, this.options, this.renderer);
   1644   this.tokens = src.reverse();
   1645 
   1646   var out = '';
   1647   while (this.next()) {
   1648     out += this.tok();
   1649   }
   1650 
   1651   return out;
   1652 };
   1653 
   1654 /**
   1655  * Next Token
   1656  */
   1657 
   1658 Parser.prototype.next = function() {
   1659   return this.token = this.tokens.pop();
   1660 };
   1661 
   1662 /**
   1663  * Preview Next Token
   1664  */
   1665 
   1666 Parser.prototype.peek = function() {
   1667   return this.tokens[this.tokens.length - 1] || 0;
   1668 };
   1669 
   1670 /**
   1671  * Parse Text Tokens
   1672  */
   1673 
   1674 Parser.prototype.parseText = function() {
   1675   var body = this.token.text;
   1676 
   1677   while (this.peek().type === 'text') {
   1678     body += '\n' + this.next().text;
   1679   }
   1680 
   1681   return this.inline.output(body);
   1682 };
   1683 
   1684 /**
   1685  * Parse Current Token
   1686  */
   1687 
   1688 Parser.prototype.tok = function() {
   1689   switch (this.token.type) {
   1690     case 'space': {
   1691       return '';
   1692     }
   1693     case 'hr': {
   1694       return this.renderer.hr();
   1695     }
   1696     case 'heading': {
   1697       return this.renderer.heading(
   1698         this.inline.output(this.token.text),
   1699         this.token.depth,
   1700         this.token.text);
   1701     }
   1702     case 'code': {
   1703       return this.renderer.code(this.token.text,
   1704         this.token.lang,
   1705         this.token.escaped);
   1706     }
   1707     case 'table': {
   1708       var header = ''
   1709         , body = ''
   1710         , i
   1711         , row
   1712         , cell
   1713         , flags
   1714         , j;
   1715 
   1716       // header
   1717       cell = '';
   1718       for (i = 0; i < this.token.header.length; i++) {
   1719         flags = { header: true, align: this.token.align[i] };
   1720         cell += this.renderer.tablecell(
   1721           this.inline.output(this.token.header[i]),
   1722           { header: true, align: this.token.align[i] }
   1723         );
   1724       }
   1725       header += this.renderer.tablerow(cell);
   1726 
   1727       for (i = 0; i < this.token.cells.length; i++) {
   1728         row = this.token.cells[i];
   1729 
   1730         cell = '';
   1731         for (j = 0; j < row.length; j++) {
   1732           cell += this.renderer.tablecell(
   1733             this.inline.output(row[j]),
   1734             { header: false, align: this.token.align[j] }
   1735           );
   1736         }
   1737 
   1738         body += this.renderer.tablerow(cell);
   1739       }
   1740       return this.renderer.table(header, body);
   1741     }
   1742     case 'blockquote_start': {
   1743       var body = '';
   1744 
   1745       while (this.next().type !== 'blockquote_end') {
   1746         body += this.tok();
   1747       }
   1748 
   1749       return this.renderer.blockquote(body);
   1750     }
   1751     case 'list_start': {
   1752       var body = ''
   1753         , ordered = this.token.ordered;
   1754 
   1755       while (this.next().type !== 'list_end') {
   1756         body += this.tok();
   1757       }
   1758 
   1759       return this.renderer.list(body, ordered);
   1760     }
   1761     case 'list_item_start': {
   1762       var body = '';
   1763 
   1764       while (this.next().type !== 'list_item_end') {
   1765         body += this.token.type === 'text'
   1766           ? this.parseText()
   1767           : this.tok();
   1768       }
   1769 
   1770       return this.renderer.listitem(body);
   1771     }
   1772     case 'loose_item_start': {
   1773       var body = '';
   1774 
   1775       while (this.next().type !== 'list_item_end') {
   1776         body += this.tok();
   1777       }
   1778 
   1779       return this.renderer.listitem(body);
   1780     }
   1781     case 'html': {
   1782       var html = !this.token.pre && !this.options.pedantic
   1783         ? this.inline.output(this.token.text)
   1784         : this.token.text;
   1785       return this.renderer.html(html);
   1786     }
   1787     case 'paragraph': {
   1788       return this.renderer.paragraph(this.inline.output(this.token.text));
   1789     }
   1790     case 'text': {
   1791       return this.renderer.paragraph(this.parseText());
   1792     }
   1793   }
   1794 };
   1795 
   1796 /**
   1797  * Helpers
   1798  */
   1799 
   1800 function escape(html, encode) {
   1801   return html
   1802     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
   1803     .replace(/</g, '&lt;')
   1804     .replace(/>/g, '&gt;')
   1805     .replace(/"/g, '&quot;')
   1806     .replace(/'/g, '&#39;');
   1807 }
   1808 
   1809 function unescape(html) {
   1810   return html.replace(/&([#\w]+);/g, function(_, n) {
   1811     n = n.toLowerCase();
   1812     if (n === 'colon') return ':';
   1813     if (n.charAt(0) === '#') {
   1814       return n.charAt(1) === 'x'
   1815         ? String.fromCharCode(parseInt(n.substring(2), 16))
   1816         : String.fromCharCode(+n.substring(1));
   1817     }
   1818     return '';
   1819   });
   1820 }
   1821 
   1822 function replace(regex, opt) {
   1823   regex = regex.source;
   1824   opt = opt || '';
   1825   return function self(name, val) {
   1826     if (!name) return new RegExp(regex, opt);
   1827     val = val.source || val;
   1828     val = val.replace(/(^|[^\[])\^/g, '$1');
   1829     regex = regex.replace(name, val);
   1830     return self;
   1831   };
   1832 }
   1833 
   1834 function noop() {}
   1835 noop.exec = noop;
   1836 
   1837 function merge(obj) {
   1838   var i = 1
   1839     , target
   1840     , key;
   1841 
   1842   for (; i < arguments.length; i++) {
   1843     target = arguments[i];
   1844     for (key in target) {
   1845       if (Object.prototype.hasOwnProperty.call(target, key)) {
   1846         obj[key] = target[key];
   1847       }
   1848     }
   1849   }
   1850 
   1851   return obj;
   1852 }
   1853 
   1854 
   1855 /**
   1856  * Marked
   1857  */
   1858 
   1859 function marked(src, opt, callback) {
   1860   if (callback || typeof opt === 'function') {
   1861     if (!callback) {
   1862       callback = opt;
   1863       opt = null;
   1864     }
   1865 
   1866     opt = merge({}, marked.defaults, opt || {});
   1867 
   1868     var highlight = opt.highlight
   1869       , tokens
   1870       , pending
   1871       , i = 0;
   1872 
   1873     try {
   1874       tokens = Lexer.lex(src, opt)
   1875     } catch (e) {
   1876       return callback(e);
   1877     }
   1878 
   1879     pending = tokens.length;
   1880 
   1881     var done = function() {
   1882       var out, err;
   1883 
   1884       try {
   1885         out = Parser.parse(tokens, opt);
   1886       } catch (e) {
   1887         err = e;
   1888       }
   1889 
   1890       opt.highlight = highlight;
   1891 
   1892       return err
   1893         ? callback(err)
   1894         : callback(null, out);
   1895     };
   1896 
   1897     if (!highlight || highlight.length < 3) {
   1898       return done();
   1899     }
   1900 
   1901     delete opt.highlight;
   1902 
   1903     if (!pending) return done();
   1904 
   1905     for (; i < tokens.length; i++) {
   1906       (function(token) {
   1907         if (token.type !== 'code') {
   1908           return --pending || done();
   1909         }
   1910         return highlight(token.text, token.lang, function(err, code) {
   1911           if (code == null || code === token.text) {
   1912             return --pending || done();
   1913           }
   1914           token.text = code;
   1915           token.escaped = true;
   1916           --pending || done();
   1917         });
   1918       })(tokens[i]);
   1919     }
   1920 
   1921     return;
   1922   }
   1923   try {
   1924     if (opt) opt = merge({}, marked.defaults, opt);
   1925     return Parser.parse(Lexer.lex(src, opt), opt);
   1926   } catch (e) {
   1927     e.message += '\nPlease report this to https://github.com/chjj/marked.';
   1928     if ((opt || marked.defaults).silent) {
   1929       return '<p>An error occured:</p><pre>'
   1930         + escape(e.message + '', true)
   1931         + '</pre>';
   1932     }
   1933     throw e;
   1934   }
   1935 }
   1936 
   1937 /**
   1938  * Options
   1939  */
   1940 
   1941 marked.options =
   1942 marked.setOptions = function(opt) {
   1943   merge(marked.defaults, opt);
   1944   return marked;
   1945 };
   1946 
   1947 marked.defaults = {
   1948   gfm: true,
   1949   tables: true,
   1950   breaks: false,
   1951   pedantic: false,
   1952   sanitize: false,
   1953   smartLists: false,
   1954   silent: false,
   1955   highlight: null,
   1956   langPrefix: 'lang-',
   1957   smartypants: false,
   1958   headerPrefix: '',
   1959   renderer: new Renderer,
   1960   xhtml: false
   1961 };
   1962 
   1963 /**
   1964  * Expose
   1965  */
   1966 
   1967 marked.Parser = Parser;
   1968 marked.parser = Parser.parse;
   1969 
   1970 marked.Renderer = Renderer;
   1971 
   1972 marked.Lexer = Lexer;
   1973 marked.lexer = Lexer.lex;
   1974 
   1975 marked.InlineLexer = InlineLexer;
   1976 marked.inlineLexer = InlineLexer.output;
   1977 
   1978 marked.parse = marked;
   1979 
   1980 if (typeof exports === 'object') {
   1981   module.exports = marked;
   1982 } else if (typeof define === 'function' && define.amd) {
   1983   define(function() { return marked; });
   1984 } else {
   1985   this.marked = marked;
   1986 }
   1987 
   1988 }).call(function() {
   1989   return this || (typeof window !== 'undefined' ? window : global);
   1990 }());
   1991 ;
   1992 
   1993 
   1994   Polymer('marked-element', {
   1995 
   1996     text: '',
   1997 
   1998     attached: function() {
   1999       marked.setOptions({
   2000         highlight: this.highlight.bind(this)
   2001       });
   2002       if (!this.text) {
   2003         this.text = this.innerHTML;
   2004       }
   2005     },
   2006 
   2007     textChanged: function () {
   2008       this.innerHTML = marked(this.text);
   2009     },
   2010 
   2011     highlight: function(code, lang) {
   2012       var event = this.fire('marked-js-highlight', {code: code, lang: lang});
   2013       return event.detail.code || code;
   2014     }
   2015 
   2016   });
   2017 
   2018 ;
   2019 // Copyright (C) 2006 Google Inc.
   2020 //
   2021 // Licensed under the Apache License, Version 2.0 (the "License");
   2022 // you may not use this file except in compliance with the License.
   2023 // You may obtain a copy of the License at
   2024 //
   2025 //      http://www.apache.org/licenses/LICENSE-2.0
   2026 //
   2027 // Unless required by applicable law or agreed to in writing, software
   2028 // distributed under the License is distributed on an "AS IS" BASIS,
   2029 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   2030 // See the License for the specific language governing permissions and
   2031 // limitations under the License.
   2032 
   2033 
   2034 /**
   2035  * @fileoverview
   2036  * some functions for browser-side pretty printing of code contained in html.
   2037  *
   2038  * <p>
   2039  * For a fairly comprehensive set of languages see the
   2040  * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
   2041  * file that came with this source.  At a minimum, the lexer should work on a
   2042  * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
   2043  * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
   2044  * and a subset of Perl, but, because of commenting conventions, doesn't work on
   2045  * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
   2046  * <p>
   2047  * Usage: <ol>
   2048  * <li> include this source file in an html page via
   2049  *   {@code <script type="text/javascript" src="/path/to/prettify.js"><\/script>}
   2050  * <li> define style rules.  See the example page for examples.
   2051  * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
   2052  *    {@code class=prettyprint.}
   2053  *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
   2054  *    printer needs to do more substantial DOM manipulations to support that, so
   2055  *    some css styles may not be preserved.
   2056  * </ol>
   2057  * That's it.  I wanted to keep the API as simple as possible, so there's no
   2058  * need to specify which language the code is in, but if you wish, you can add
   2059  * another class to the {@code <pre>} or {@code <code>} element to specify the
   2060  * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
   2061  * starts with "lang-" followed by a file extension, specifies the file type.
   2062  * See the "lang-*.js" files in this directory for code that implements
   2063  * per-language file handlers.
   2064  * <p>
   2065  * Change log:<br>
   2066  * cbeust, 2006/08/22
   2067  * <blockquote>
   2068  *   Java annotations (start with "@") are now captured as literals ("lit")
   2069  * </blockquote>
   2070  * @requires console
   2071  */
   2072 
   2073 // JSLint declarations
   2074 /*global console, document, navigator, setTimeout, window, define */
   2075 
   2076 /**
   2077  * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
   2078  * UI events.
   2079  * If set to {@code false}, {@code prettyPrint()} is synchronous.
   2080  */
   2081 window['PR_SHOULD_USE_CONTINUATION'] = true;
   2082 
   2083 /**
   2084  * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
   2085  * {@code class=prettyprint} and prettify them.
   2086  *
   2087  * @param {Function?} opt_whenDone if specified, called when the last entry
   2088  *     has been finished.
   2089  */
   2090 var prettyPrintOne;
   2091 /**
   2092  * Pretty print a chunk of code.
   2093  *
   2094  * @param {string} sourceCodeHtml code as html
   2095  * @return {string} code as html, but prettier
   2096  */
   2097 var prettyPrint;
   2098 
   2099 
   2100 (function () {
   2101   var win = window;
   2102   // Keyword lists for various languages.
   2103   // We use things that coerce to strings to make them compact when minified
   2104   // and to defeat aggressive optimizers that fold large string constants.
   2105   var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
   2106   var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
   2107       "double,enum,extern,float,goto,int,long,register,short,signed,sizeof," +
   2108       "static,struct,switch,typedef,union,unsigned,void,volatile"];
   2109   var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
   2110       "new,operator,private,protected,public,this,throw,true,try,typeof"];
   2111   var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
   2112       "concept,concept_map,const_cast,constexpr,decltype," +
   2113       "dynamic_cast,explicit,export,friend,inline,late_check," +
   2114       "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast," +
   2115       "template,typeid,typename,using,virtual,where"];
   2116   var JAVA_KEYWORDS = [COMMON_KEYWORDS,
   2117       "abstract,boolean,byte,extends,final,finally,implements,import," +
   2118       "instanceof,null,native,package,strictfp,super,synchronized,throws," +
   2119       "transient"];
   2120   var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
   2121       "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
   2122       "fixed,foreach,from,group,implicit,in,interface,internal,into,is,let," +
   2123       "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
   2124       "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
   2125       "var,virtual,where"];
   2126   var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
   2127       "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
   2128       "throw,true,try,unless,until,when,while,yes";
   2129   var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
   2130       "debugger,eval,export,function,get,null,set,undefined,var,with," +
   2131       "Infinity,NaN"];
   2132   var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
   2133       "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
   2134       "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
   2135   var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
   2136       "elif,except,exec,finally,from,global,import,in,is,lambda," +
   2137       "nonlocal,not,or,pass,print,raise,try,with,yield," +
   2138       "False,True,None"];
   2139   var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
   2140       "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
   2141       "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
   2142       "BEGIN,END"];
   2143   var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
   2144       "function,in,local,set,then,until"];
   2145   var ALL_KEYWORDS = [
   2146       CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
   2147       PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
   2148   var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
   2149 
   2150   // token style names.  correspond to css classes
   2151   /**
   2152    * token style for a string literal
   2153    * @const
   2154    */
   2155   var PR_STRING = 'str';
   2156   /**
   2157    * token style for a keyword
   2158    * @const
   2159    */
   2160   var PR_KEYWORD = 'kwd';
   2161   /**
   2162    * token style for a comment
   2163    * @const
   2164    */
   2165   var PR_COMMENT = 'com';
   2166   /**
   2167    * token style for a type
   2168    * @const
   2169    */
   2170   var PR_TYPE = 'typ';
   2171   /**
   2172    * token style for a literal value.  e.g. 1, null, true.
   2173    * @const
   2174    */
   2175   var PR_LITERAL = 'lit';
   2176   /**
   2177    * token style for a punctuation string.
   2178    * @const
   2179    */
   2180   var PR_PUNCTUATION = 'pun';
   2181   /**
   2182    * token style for plain text.
   2183    * @const
   2184    */
   2185   var PR_PLAIN = 'pln';
   2186 
   2187   /**
   2188    * token style for an sgml tag.
   2189    * @const
   2190    */
   2191   var PR_TAG = 'tag';
   2192   /**
   2193    * token style for a markup declaration such as a DOCTYPE.
   2194    * @const
   2195    */
   2196   var PR_DECLARATION = 'dec';
   2197   /**
   2198    * token style for embedded source.
   2199    * @const
   2200    */
   2201   var PR_SOURCE = 'src';
   2202   /**
   2203    * token style for an sgml attribute name.
   2204    * @const
   2205    */
   2206   var PR_ATTRIB_NAME = 'atn';
   2207   /**
   2208    * token style for an sgml attribute value.
   2209    * @const
   2210    */
   2211   var PR_ATTRIB_VALUE = 'atv';
   2212 
   2213   /**
   2214    * A class that indicates a section of markup that is not code, e.g. to allow
   2215    * embedding of line numbers within code listings.
   2216    * @const
   2217    */
   2218   var PR_NOCODE = 'nocode';
   2219 
   2220 
   2221 
   2222 /**
   2223  * A set of tokens that can precede a regular expression literal in
   2224  * javascript
   2225  * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
   2226  * has the full list, but I've removed ones that might be problematic when
   2227  * seen in languages that don't support regular expression literals.
   2228  *
   2229  * <p>Specifically, I've removed any keywords that can't precede a regexp
   2230  * literal in a syntactically legal javascript program, and I've removed the
   2231  * "in" keyword since it's not a keyword in many languages, and might be used
   2232  * as a count of inches.
   2233  *
   2234  * <p>The link above does not accurately describe EcmaScript rules since
   2235  * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
   2236  * very well in practice.
   2237  *
   2238  * @private
   2239  * @const
   2240  */
   2241 var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
   2242 
   2243 // CAVEAT: this does not properly handle the case where a regular
   2244 // expression immediately follows another since a regular expression may
   2245 // have flags for case-sensitivity and the like.  Having regexp tokens
   2246 // adjacent is not valid in any language I'm aware of, so I'm punting.
   2247 // TODO: maybe style special characters inside a regexp as punctuation.
   2248 
   2249 
   2250   /**
   2251    * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
   2252    * matches the union of the sets of strings matched by the input RegExp.
   2253    * Since it matches globally, if the input strings have a start-of-input
   2254    * anchor (/^.../), it is ignored for the purposes of unioning.
   2255    * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
   2256    * @return {RegExp} a global regex.
   2257    */
   2258   function combinePrefixPatterns(regexs) {
   2259     var capturedGroupIndex = 0;
   2260   
   2261     var needToFoldCase = false;
   2262     var ignoreCase = false;
   2263     for (var i = 0, n = regexs.length; i < n; ++i) {
   2264       var regex = regexs[i];
   2265       if (regex.ignoreCase) {
   2266         ignoreCase = true;
   2267       } else if (/[a-z]/i.test(regex.source.replace(
   2268                      /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
   2269         needToFoldCase = true;
   2270         ignoreCase = false;
   2271         break;
   2272       }
   2273     }
   2274   
   2275     var escapeCharToCodeUnit = {
   2276       'b': 8,
   2277       't': 9,
   2278       'n': 0xa,
   2279       'v': 0xb,
   2280       'f': 0xc,
   2281       'r': 0xd
   2282     };
   2283   
   2284     function decodeEscape(charsetPart) {
   2285       var cc0 = charsetPart.charCodeAt(0);
   2286       if (cc0 !== 92 /* \\ */) {
   2287         return cc0;
   2288       }
   2289       var c1 = charsetPart.charAt(1);
   2290       cc0 = escapeCharToCodeUnit[c1];
   2291       if (cc0) {
   2292         return cc0;
   2293       } else if ('0' <= c1 && c1 <= '7') {
   2294         return parseInt(charsetPart.substring(1), 8);
   2295       } else if (c1 === 'u' || c1 === 'x') {
   2296         return parseInt(charsetPart.substring(2), 16);
   2297       } else {
   2298         return charsetPart.charCodeAt(1);
   2299       }
   2300     }
   2301   
   2302     function encodeEscape(charCode) {
   2303       if (charCode < 0x20) {
   2304         return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
   2305       }
   2306       var ch = String.fromCharCode(charCode);
   2307       return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
   2308           ? "\\" + ch : ch;
   2309     }
   2310   
   2311     function caseFoldCharset(charSet) {
   2312       var charsetParts = charSet.substring(1, charSet.length - 1).match(
   2313           new RegExp(
   2314               '\\\\u[0-9A-Fa-f]{4}'
   2315               + '|\\\\x[0-9A-Fa-f]{2}'
   2316               + '|\\\\[0-3][0-7]{0,2}'
   2317               + '|\\\\[0-7]{1,2}'
   2318               + '|\\\\[\\s\\S]'
   2319               + '|-'
   2320               + '|[^-\\\\]',
   2321               'g'));
   2322       var ranges = [];
   2323       var inverse = charsetParts[0] === '^';
   2324   
   2325       var out = ['['];
   2326       if (inverse) { out.push('^'); }
   2327   
   2328       for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
   2329         var p = charsetParts[i];
   2330         if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
   2331           out.push(p);
   2332         } else {
   2333           var start = decodeEscape(p);
   2334           var end;
   2335           if (i + 2 < n && '-' === charsetParts[i + 1]) {
   2336             end = decodeEscape(charsetParts[i + 2]);
   2337             i += 2;
   2338           } else {
   2339             end = start;
   2340           }
   2341           ranges.push([start, end]);
   2342           // If the range might intersect letters, then expand it.
   2343           // This case handling is too simplistic.
   2344           // It does not deal with non-latin case folding.
   2345           // It works for latin source code identifiers though.
   2346           if (!(end < 65 || start > 122)) {
   2347             if (!(end < 65 || start > 90)) {
   2348               ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
   2349             }
   2350             if (!(end < 97 || start > 122)) {
   2351               ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
   2352             }
   2353           }
   2354         }
   2355       }
   2356   
   2357       // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
   2358       // -> [[1, 12], [14, 14], [16, 17]]
   2359       ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
   2360       var consolidatedRanges = [];
   2361       var lastRange = [];
   2362       for (var i = 0; i < ranges.length; ++i) {
   2363         var range = ranges[i];
   2364         if (range[0] <= lastRange[1] + 1) {
   2365           lastRange[1] = Math.max(lastRange[1], range[1]);
   2366         } else {
   2367           consolidatedRanges.push(lastRange = range);
   2368         }
   2369       }
   2370   
   2371       for (var i = 0; i < consolidatedRanges.length; ++i) {
   2372         var range = consolidatedRanges[i];
   2373         out.push(encodeEscape(range[0]));
   2374         if (range[1] > range[0]) {
   2375           if (range[1] + 1 > range[0]) { out.push('-'); }
   2376           out.push(encodeEscape(range[1]));
   2377         }
   2378       }
   2379       out.push(']');
   2380       return out.join('');
   2381     }
   2382   
   2383     function allowAnywhereFoldCaseAndRenumberGroups(regex) {
   2384       // Split into character sets, escape sequences, punctuation strings
   2385       // like ('(', '(?:', ')', '^'), and runs of characters that do not
   2386       // include any of the above.
   2387       var parts = regex.source.match(
   2388           new RegExp(
   2389               '(?:'
   2390               + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
   2391               + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
   2392               + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
   2393               + '|\\\\[0-9]+'  // a back-reference or octal escape
   2394               + '|\\\\[^ux0-9]'  // other escape sequence
   2395               + '|\\(\\?[:!=]'  // start of a non-capturing group
   2396               + '|[\\(\\)\\^]'  // start/end of a group, or line start
   2397               + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
   2398               + ')',
   2399               'g'));
   2400       var n = parts.length;
   2401   
   2402       // Maps captured group numbers to the number they will occupy in
   2403       // the output or to -1 if that has not been determined, or to
   2404       // undefined if they need not be capturing in the output.
   2405       var capturedGroups = [];
   2406   
   2407       // Walk over and identify back references to build the capturedGroups
   2408       // mapping.
   2409       for (var i = 0, groupIndex = 0; i < n; ++i) {
   2410         var p = parts[i];
   2411         if (p === '(') {
   2412           // groups are 1-indexed, so max group index is count of '('
   2413           ++groupIndex;
   2414         } else if ('\\' === p.charAt(0)) {
   2415           var decimalValue = +p.substring(1);
   2416           if (decimalValue) {
   2417             if (decimalValue <= groupIndex) {
   2418               capturedGroups[decimalValue] = -1;
   2419             } else {
   2420               // Replace with an unambiguous escape sequence so that
   2421               // an octal escape sequence does not turn into a backreference
   2422               // to a capturing group from an earlier regex.
   2423               parts[i] = encodeEscape(decimalValue);
   2424             }
   2425           }
   2426         }
   2427       }
   2428   
   2429       // Renumber groups and reduce capturing groups to non-capturing groups
   2430       // where possible.
   2431       for (var i = 1; i < capturedGroups.length; ++i) {
   2432         if (-1 === capturedGroups[i]) {
   2433           capturedGroups[i] = ++capturedGroupIndex;
   2434         }
   2435       }
   2436       for (var i = 0, groupIndex = 0; i < n; ++i) {
   2437         var p = parts[i];
   2438         if (p === '(') {
   2439           ++groupIndex;
   2440           if (!capturedGroups[groupIndex]) {
   2441             parts[i] = '(?:';
   2442           }
   2443         } else if ('\\' === p.charAt(0)) {
   2444           var decimalValue = +p.substring(1);
   2445           if (decimalValue && decimalValue <= groupIndex) {
   2446             parts[i] = '\\' + capturedGroups[decimalValue];
   2447           }
   2448         }
   2449       }
   2450   
   2451       // Remove any prefix anchors so that the output will match anywhere.
   2452       // ^^ really does mean an anchored match though.
   2453       for (var i = 0; i < n; ++i) {
   2454         if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
   2455       }
   2456   
   2457       // Expand letters to groups to handle mixing of case-sensitive and
   2458       // case-insensitive patterns if necessary.
   2459       if (regex.ignoreCase && needToFoldCase) {
   2460         for (var i = 0; i < n; ++i) {
   2461           var p = parts[i];
   2462           var ch0 = p.charAt(0);
   2463           if (p.length >= 2 && ch0 === '[') {
   2464             parts[i] = caseFoldCharset(p);
   2465           } else if (ch0 !== '\\') {
   2466             // TODO: handle letters in numeric escapes.
   2467             parts[i] = p.replace(
   2468                 /[a-zA-Z]/g,
   2469                 function (ch) {
   2470                   var cc = ch.charCodeAt(0);
   2471                   return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
   2472                 });
   2473           }
   2474         }
   2475       }
   2476   
   2477       return parts.join('');
   2478     }
   2479   
   2480     var rewritten = [];
   2481     for (var i = 0, n = regexs.length; i < n; ++i) {
   2482       var regex = regexs[i];
   2483       if (regex.global || regex.multiline) { throw new Error('' + regex); }
   2484       rewritten.push(
   2485           '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
   2486     }
   2487   
   2488     return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
   2489   }
   2490 
   2491 
   2492   /**
   2493    * Split markup into a string of source code and an array mapping ranges in
   2494    * that string to the text nodes in which they appear.
   2495    *
   2496    * <p>
   2497    * The HTML DOM structure:</p>
   2498    * <pre>
   2499    * (Element   "p"
   2500    *   (Element "b"
   2501    *     (Text  "print "))       ; #1
   2502    *   (Text    "'Hello '")      ; #2
   2503    *   (Element "br")            ; #3
   2504    *   (Text    "  + 'World';")) ; #4
   2505    * </pre>
   2506    * <p>
   2507    * corresponds to the HTML
   2508    * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
   2509    *
   2510    * <p>
   2511    * It will produce the output:</p>
   2512    * <pre>
   2513    * {
   2514    *   sourceCode: "print 'Hello '\n  + 'World';",
   2515    *   //                     1          2
   2516    *   //           012345678901234 5678901234567
   2517    *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
   2518    * }
   2519    * </pre>
   2520    * <p>
   2521    * where #1 is a reference to the {@code "print "} text node above, and so
   2522    * on for the other text nodes.
   2523    * </p>
   2524    *
   2525    * <p>
   2526    * The {@code} spans array is an array of pairs.  Even elements are the start
   2527    * indices of substrings, and odd elements are the text nodes (or BR elements)
   2528    * that contain the text for those substrings.
   2529    * Substrings continue until the next index or the end of the source.
   2530    * </p>
   2531    *
   2532    * @param {Node} node an HTML DOM subtree containing source-code.
   2533    * @param {boolean} isPreformatted true if white-space in text nodes should
   2534    *    be considered significant.
   2535    * @return {Object} source code and the text nodes in which they occur.
   2536    */
   2537   function extractSourceSpans(node, isPreformatted) {
   2538     var nocode = /(?:^|\s)nocode(?:\s|$)/;
   2539   
   2540     var chunks = [];
   2541     var length = 0;
   2542     var spans = [];
   2543     var k = 0;
   2544   
   2545     function walk(node) {
   2546       switch (node.nodeType) {
   2547         case 1:  // Element
   2548           if (nocode.test(node.className)) { return; }
   2549           for (var child = node.firstChild; child; child = child.nextSibling) {
   2550             walk(child);
   2551           }
   2552           var nodeName = node.nodeName.toLowerCase();
   2553           if ('br' === nodeName || 'li' === nodeName) {
   2554             chunks[k] = '\n';
   2555             spans[k << 1] = length++;
   2556             spans[(k++ << 1) | 1] = node;
   2557           }
   2558           break;
   2559         case 3: case 4:  // Text
   2560           var text = node.nodeValue;
   2561           if (text.length) {
   2562             if (!isPreformatted) {
   2563               text = text.replace(/[ \t\r\n]+/g, ' ');
   2564             } else {
   2565               text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
   2566             }
   2567             // TODO: handle tabs here?
   2568             chunks[k] = text;
   2569             spans[k << 1] = length;
   2570             length += text.length;
   2571             spans[(k++ << 1) | 1] = node;
   2572           }
   2573           break;
   2574       }
   2575     }
   2576   
   2577     walk(node);
   2578   
   2579     return {
   2580       sourceCode: chunks.join('').replace(/\n$/, ''),
   2581       spans: spans
   2582     };
   2583   }
   2584 
   2585 
   2586   /**
   2587    * Apply the given language handler to sourceCode and add the resulting
   2588    * decorations to out.
   2589    * @param {number} basePos the index of sourceCode within the chunk of source
   2590    *    whose decorations are already present on out.
   2591    */
   2592   function appendDecorations(basePos, sourceCode, langHandler, out) {
   2593     if (!sourceCode) { return; }
   2594     var job = {
   2595       sourceCode: sourceCode,
   2596       basePos: basePos
   2597     };
   2598     langHandler(job);
   2599     out.push.apply(out, job.decorations);
   2600   }
   2601 
   2602   var notWs = /\S/;
   2603 
   2604   /**
   2605    * Given an element, if it contains only one child element and any text nodes
   2606    * it contains contain only space characters, return the sole child element.
   2607    * Otherwise returns undefined.
   2608    * <p>
   2609    * This is meant to return the CODE element in {@code <pre><code ...>} when
   2610    * there is a single child element that contains all the non-space textual
   2611    * content, but not to return anything where there are multiple child elements
   2612    * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
   2613    * is textual content.
   2614    */
   2615   function childContentWrapper(element) {
   2616     var wrapper = undefined;
   2617     for (var c = element.firstChild; c; c = c.nextSibling) {
   2618       var type = c.nodeType;
   2619       wrapper = (type === 1)  // Element Node
   2620           ? (wrapper ? element : c)
   2621           : (type === 3)  // Text Node
   2622           ? (notWs.test(c.nodeValue) ? element : wrapper)
   2623           : wrapper;
   2624     }
   2625     return wrapper === element ? undefined : wrapper;
   2626   }
   2627 
   2628   /** Given triples of [style, pattern, context] returns a lexing function,
   2629     * The lexing function interprets the patterns to find token boundaries and
   2630     * returns a decoration list of the form
   2631     * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
   2632     * where index_n is an index into the sourceCode, and style_n is a style
   2633     * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
   2634     * all characters in sourceCode[index_n-1:index_n].
   2635     *
   2636     * The stylePatterns is a list whose elements have the form
   2637     * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
   2638     *
   2639     * Style is a style constant like PR_PLAIN, or can be a string of the
   2640     * form 'lang-FOO', where FOO is a language extension describing the
   2641     * language of the portion of the token in $1 after pattern executes.
   2642     * E.g., if style is 'lang-lisp', and group 1 contains the text
   2643     * '(hello (world))', then that portion of the token will be passed to the
   2644     * registered lisp handler for formatting.
   2645     * The text before and after group 1 will be restyled using this decorator
   2646     * so decorators should take care that this doesn't result in infinite
   2647     * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
   2648     * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
   2649     * '<script>foo()<\/script>', which would cause the current decorator to
   2650     * be called with '<script>' which would not match the same rule since
   2651     * group 1 must not be empty, so it would be instead styled as PR_TAG by
   2652     * the generic tag rule.  The handler registered for the 'js' extension would
   2653     * then be called with 'foo()', and finally, the current decorator would
   2654     * be called with '<\/script>' which would not match the original rule and
   2655     * so the generic tag rule would identify it as a tag.
   2656     *
   2657     * Pattern must only match prefixes, and if it matches a prefix, then that
   2658     * match is considered a token with the same style.
   2659     *
   2660     * Context is applied to the last non-whitespace, non-comment token
   2661     * recognized.
   2662     *
   2663     * Shortcut is an optional string of characters, any of which, if the first
   2664     * character, gurantee that this pattern and only this pattern matches.
   2665     *
   2666     * @param {Array} shortcutStylePatterns patterns that always start with
   2667     *   a known character.  Must have a shortcut string.
   2668     * @param {Array} fallthroughStylePatterns patterns that will be tried in
   2669     *   order if the shortcut ones fail.  May have shortcuts.
   2670     *
   2671     * @return {function (Object)} a
   2672     *   function that takes source code and returns a list of decorations.
   2673     */
   2674   function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
   2675     var shortcuts = {};
   2676     var tokenizer;
   2677     (function () {
   2678       var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
   2679       var allRegexs = [];
   2680       var regexKeys = {};
   2681       for (var i = 0, n = allPatterns.length; i < n; ++i) {
   2682         var patternParts = allPatterns[i];
   2683         var shortcutChars = patternParts[3];
   2684         if (shortcutChars) {
   2685           for (var c = shortcutChars.length; --c >= 0;) {
   2686             shortcuts[shortcutChars.charAt(c)] = patternParts;
   2687           }
   2688         }
   2689         var regex = patternParts[1];
   2690         var k = '' + regex;
   2691         if (!regexKeys.hasOwnProperty(k)) {
   2692           allRegexs.push(regex);
   2693           regexKeys[k] = null;
   2694         }
   2695       }
   2696       allRegexs.push(/[\0-\uffff]/);
   2697       tokenizer = combinePrefixPatterns(allRegexs);
   2698     })();
   2699 
   2700     var nPatterns = fallthroughStylePatterns.length;
   2701 
   2702     /**
   2703      * Lexes job.sourceCode and produces an output array job.decorations of
   2704      * style classes preceded by the position at which they start in
   2705      * job.sourceCode in order.
   2706      *
   2707      * @param {Object} job an object like <pre>{
   2708      *    sourceCode: {string} sourceText plain text,
   2709      *    basePos: {int} position of job.sourceCode in the larger chunk of
   2710      *        sourceCode.
   2711      * }</pre>
   2712      */
   2713     var decorate = function (job) {
   2714       var sourceCode = job.sourceCode, basePos = job.basePos;
   2715       /** Even entries are positions in source in ascending order.  Odd enties
   2716         * are style markers (e.g., PR_COMMENT) that run from that position until
   2717         * the end.
   2718         * @type {Array.<number|string>}
   2719         */
   2720       var decorations = [basePos, PR_PLAIN];
   2721       var pos = 0;  // index into sourceCode
   2722       var tokens = sourceCode.match(tokenizer) || [];
   2723       var styleCache = {};
   2724 
   2725       for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
   2726         var token = tokens[ti];
   2727         var style = styleCache[token];
   2728         var match = void 0;
   2729 
   2730         var isEmbedded;
   2731         if (typeof style === 'string') {
   2732           isEmbedded = false;
   2733         } else {
   2734           var patternParts = shortcuts[token.charAt(0)];
   2735           if (patternParts) {
   2736             match = token.match(patternParts[1]);
   2737             style = patternParts[0];
   2738           } else {
   2739             for (var i = 0; i < nPatterns; ++i) {
   2740               patternParts = fallthroughStylePatterns[i];
   2741               match = token.match(patternParts[1]);
   2742               if (match) {
   2743                 style = patternParts[0];
   2744                 break;
   2745               }
   2746             }
   2747 
   2748             if (!match) {  // make sure that we make progress
   2749               style = PR_PLAIN;
   2750             }
   2751           }
   2752 
   2753           isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
   2754           if (isEmbedded && !(match && typeof match[1] === 'string')) {
   2755             isEmbedded = false;
   2756             style = PR_SOURCE;
   2757           }
   2758 
   2759           if (!isEmbedded) { styleCache[token] = style; }
   2760         }
   2761 
   2762         var tokenStart = pos;
   2763         pos += token.length;
   2764 
   2765         if (!isEmbedded) {
   2766           decorations.push(basePos + tokenStart, style);
   2767         } else {  // Treat group 1 as an embedded block of source code.
   2768           var embeddedSource = match[1];
   2769           var embeddedSourceStart = token.indexOf(embeddedSource);
   2770           var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
   2771           if (match[2]) {
   2772             // If embeddedSource can be blank, then it would match at the
   2773             // beginning which would cause us to infinitely recurse on the
   2774             // entire token, so we catch the right context in match[2].
   2775             embeddedSourceEnd = token.length - match[2].length;
   2776             embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
   2777           }
   2778           var lang = style.substring(5);
   2779           // Decorate the left of the embedded source
   2780           appendDecorations(
   2781               basePos + tokenStart,
   2782               token.substring(0, embeddedSourceStart),
   2783               decorate, decorations);
   2784           // Decorate the embedded source
   2785           appendDecorations(
   2786               basePos + tokenStart + embeddedSourceStart,
   2787               embeddedSource,
   2788               langHandlerForExtension(lang, embeddedSource),
   2789               decorations);
   2790           // Decorate the right of the embedded section
   2791           appendDecorations(
   2792               basePos + tokenStart + embeddedSourceEnd,
   2793               token.substring(embeddedSourceEnd),
   2794               decorate, decorations);
   2795         }
   2796       }
   2797       job.decorations = decorations;
   2798     };
   2799     return decorate;
   2800   }
   2801 
   2802   /** returns a function that produces a list of decorations from source text.
   2803     *
   2804     * This code treats ", ', and ` as string delimiters, and \ as a string
   2805     * escape.  It does not recognize perl's qq() style strings.
   2806     * It has no special handling for double delimiter escapes as in basic, or
   2807     * the tripled delimiters used in python, but should work on those regardless
   2808     * although in those cases a single string literal may be broken up into
   2809     * multiple adjacent string literals.
   2810     *
   2811     * It recognizes C, C++, and shell style comments.
   2812     *
   2813     * @param {Object} options a set of optional parameters.
   2814     * @return {function (Object)} a function that examines the source code
   2815     *     in the input job and builds the decoration list.
   2816     */
   2817   function sourceDecorator(options) {
   2818     var shortcutStylePatterns = [], fallthroughStylePatterns = [];
   2819     if (options['tripleQuotedStrings']) {
   2820       // '''multi-line-string''', 'single-line-string', and double-quoted
   2821       shortcutStylePatterns.push(
   2822           [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
   2823            null, '\'"']);
   2824     } else if (options['multiLineStrings']) {
   2825       // 'multi-line-string', "multi-line-string"
   2826       shortcutStylePatterns.push(
   2827           [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
   2828            null, '\'"`']);
   2829     } else {
   2830       // 'single-line-string', "single-line-string"
   2831       shortcutStylePatterns.push(
   2832           [PR_STRING,
   2833            /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
   2834            null, '"\'']);
   2835     }
   2836     if (options['verbatimStrings']) {
   2837       // verbatim-string-literal production from the C# grammar.  See issue 93.
   2838       fallthroughStylePatterns.push(
   2839           [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
   2840     }
   2841     var hc = options['hashComments'];
   2842     if (hc) {
   2843       if (options['cStyleComments']) {
   2844         if (hc > 1) {  // multiline hash comments
   2845           shortcutStylePatterns.push(
   2846               [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
   2847         } else {
   2848           // Stop C preprocessor declarations at an unclosed open comment
   2849           shortcutStylePatterns.push(
   2850               [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
   2851                null, '#']);
   2852         }
   2853         // #include <stdio.h>
   2854         fallthroughStylePatterns.push(
   2855             [PR_STRING,
   2856              /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
   2857              null]);
   2858       } else {
   2859         shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
   2860       }
   2861     }
   2862     if (options['cStyleComments']) {
   2863       fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
   2864       fallthroughStylePatterns.push(
   2865           [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
   2866     }
   2867     if (options['regexLiterals']) {
   2868       /**
   2869        * @const
   2870        */
   2871       var REGEX_LITERAL = (
   2872           // A regular expression literal starts with a slash that is
   2873           // not followed by * or / so that it is not confused with
   2874           // comments.
   2875           '/(?=[^/*])'
   2876           // and then contains any number of raw characters,
   2877           + '(?:[^/\\x5B\\x5C]'
   2878           // escape sequences (\x5C),
   2879           +    '|\\x5C[\\s\\S]'
   2880           // or non-nesting character sets (\x5B\x5D);
   2881           +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
   2882           // finally closed by a /.
   2883           + '/');
   2884       fallthroughStylePatterns.push(
   2885           ['lang-regex',
   2886            new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
   2887            ]);
   2888     }
   2889 
   2890     var types = options['types'];
   2891     if (types) {
   2892       fallthroughStylePatterns.push([PR_TYPE, types]);
   2893     }
   2894 
   2895     var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
   2896     if (keywords.length) {
   2897       fallthroughStylePatterns.push(
   2898           [PR_KEYWORD,
   2899            new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
   2900            null]);
   2901     }
   2902 
   2903     shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
   2904 
   2905     var punctuation =
   2906       // The Bash man page says
   2907 
   2908       // A word is a sequence of characters considered as a single
   2909       // unit by GRUB. Words are separated by metacharacters,
   2910       // which are the following plus space, tab, and newline: { }
   2911       // | & $ ; < >
   2912       // ...
   2913       
   2914       // A word beginning with # causes that word and all remaining
   2915       // characters on that line to be ignored.
   2916 
   2917       // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
   2918       // comment but empirically
   2919       // $ echo {#}
   2920       // {#}
   2921       // $ echo \$#
   2922       // $#
   2923       // $ echo }#
   2924       // }#
   2925 
   2926       // so /(?:^|[|&;<>\s])/ is more appropriate.
   2927 
   2928       // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
   2929       // suggests that this definition is compatible with a
   2930       // default mode that tries to use a single token definition
   2931       // to recognize both bash/python style comments and C
   2932       // preprocessor directives.
   2933 
   2934       // This definition of punctuation does not include # in the list of
   2935       // follow-on exclusions, so # will not be broken before if preceeded
   2936       // by a punctuation character.  We could try to exclude # after
   2937       // [|&;<>] but that doesn't seem to cause many major problems.
   2938       // If that does turn out to be a problem, we should change the below
   2939       // when hc is truthy to include # in the run of punctuation characters
   2940       // only when not followint [|&;<>].
   2941       /^.[^\s\w\.$@\'\"\`\/\\]*/;
   2942 
   2943     fallthroughStylePatterns.push(
   2944         // TODO(mikesamuel): recognize non-latin letters and numerals in idents
   2945         [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
   2946         [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
   2947         [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
   2948         [PR_LITERAL,
   2949          new RegExp(
   2950              '^(?:'
   2951              // A hex number
   2952              + '0x[a-f0-9]+'
   2953              // or an octal or decimal number,
   2954              + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
   2955              // possibly in scientific notation
   2956              + '(?:e[+\\-]?\\d+)?'
   2957              + ')'
   2958              // with an optional modifier like UL for unsigned long
   2959              + '[a-z]*', 'i'),
   2960          null, '0123456789'],
   2961         // Don't treat escaped quotes in bash as starting strings.  See issue 144.
   2962         [PR_PLAIN,       /^\\[\s\S]?/, null],
   2963         [PR_PUNCTUATION, punctuation, null]);
   2964 
   2965     return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
   2966   }
   2967 
   2968   var decorateSource = sourceDecorator({
   2969         'keywords': ALL_KEYWORDS,
   2970         'hashComments': true,
   2971         'cStyleComments': true,
   2972         'multiLineStrings': true,
   2973         'regexLiterals': true
   2974       });
   2975 
   2976   /**
   2977    * Given a DOM subtree, wraps it in a list, and puts each line into its own
   2978    * list item.
   2979    *
   2980    * @param {Node} node modified in place.  Its content is pulled into an
   2981    *     HTMLOListElement, and each line is moved into a separate list item.
   2982    *     This requires cloning elements, so the input might not have unique
   2983    *     IDs after numbering.
   2984    * @param {boolean} isPreformatted true iff white-space in text nodes should
   2985    *     be treated as significant.
   2986    */
   2987   function numberLines(node, opt_startLineNum, isPreformatted) {
   2988     var nocode = /(?:^|\s)nocode(?:\s|$)/;
   2989     var lineBreak = /\r\n?|\n/;
   2990   
   2991     var document = node.ownerDocument;
   2992   
   2993     var li = document.createElement('li');
   2994     while (node.firstChild) {
   2995       li.appendChild(node.firstChild);
   2996     }
   2997     // An array of lines.  We split below, so this is initialized to one
   2998     // un-split line.
   2999     var listItems = [li];
   3000   
   3001     function walk(node) {
   3002       switch (node.nodeType) {
   3003         case 1:  // Element
   3004           if (nocode.test(node.className)) { break; }
   3005           if ('br' === node.nodeName) {
   3006             breakAfter(node);
   3007             // Discard the <BR> since it is now flush against a </LI>.
   3008             if (node.parentNode) {
   3009               node.parentNode.removeChild(node);
   3010             }
   3011           } else {
   3012             for (var child = node.firstChild; child; child = child.nextSibling) {
   3013               walk(child);
   3014             }
   3015           }
   3016           break;
   3017         case 3: case 4:  // Text
   3018           if (isPreformatted) {
   3019             var text = node.nodeValue;
   3020             var match = text.match(lineBreak);
   3021             if (match) {
   3022               var firstLine = text.substring(0, match.index);
   3023               node.nodeValue = firstLine;
   3024               var tail = text.substring(match.index + match[0].length);
   3025               if (tail) {
   3026                 var parent = node.parentNode;
   3027                 parent.insertBefore(
   3028                     document.createTextNode(tail), node.nextSibling);
   3029               }
   3030               breakAfter(node);
   3031               if (!firstLine) {
   3032                 // Don't leave blank text nodes in the DOM.
   3033                 node.parentNode.removeChild(node);
   3034               }
   3035             }
   3036           }
   3037           break;
   3038       }
   3039     }
   3040   
   3041     // Split a line after the given node.
   3042     function breakAfter(lineEndNode) {
   3043       // If there's nothing to the right, then we can skip ending the line
   3044       // here, and move root-wards since splitting just before an end-tag
   3045       // would require us to create a bunch of empty copies.
   3046       while (!lineEndNode.nextSibling) {
   3047         lineEndNode = lineEndNode.parentNode;
   3048         if (!lineEndNode) { return; }
   3049       }
   3050   
   3051       function breakLeftOf(limit, copy) {
   3052         // Clone shallowly if this node needs to be on both sides of the break.
   3053         var rightSide = copy ? limit.cloneNode(false) : limit;
   3054         var parent = limit.parentNode;
   3055         if (parent) {
   3056           // We clone the parent chain.
   3057           // This helps us resurrect important styling elements that cross lines.
   3058           // E.g. in <i>Foo<br>Bar</i>
   3059           // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
   3060           var parentClone = breakLeftOf(parent, 1);
   3061           // Move the clone and everything to the right of the original
   3062           // onto the cloned parent.
   3063           var next = limit.nextSibling;
   3064           parentClone.appendChild(rightSide);
   3065           for (var sibling = next; sibling; sibling = next) {
   3066             next = sibling.nextSibling;
   3067             parentClone.appendChild(sibling);
   3068           }
   3069         }
   3070         return rightSide;
   3071       }
   3072   
   3073       var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
   3074   
   3075       // Walk the parent chain until we reach an unattached LI.
   3076       for (var parent;
   3077            // Check nodeType since IE invents document fragments.
   3078            (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
   3079         copiedListItem = parent;
   3080       }
   3081       // Put it on the list of lines for later processing.
   3082       listItems.push(copiedListItem);
   3083     }
   3084   
   3085     // Split lines while there are lines left to split.
   3086     for (var i = 0;  // Number of lines that have been split so far.
   3087          i < listItems.length;  // length updated by breakAfter calls.
   3088          ++i) {
   3089       walk(listItems[i]);
   3090     }
   3091   
   3092     // Make sure numeric indices show correctly.
   3093     if (opt_startLineNum === (opt_startLineNum|0)) {
   3094       listItems[0].setAttribute('value', opt_startLineNum);
   3095     }
   3096   
   3097     var ol = document.createElement('ol');
   3098     ol.className = 'linenums';
   3099     var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
   3100     for (var i = 0, n = listItems.length; i < n; ++i) {
   3101       li = listItems[i];
   3102       // Stick a class on the LIs so that stylesheets can
   3103       // color odd/even rows, or any other row pattern that
   3104       // is co-prime with 10.
   3105       li.className = 'L' + ((i + offset) % 10);
   3106       if (!li.firstChild) {
   3107         li.appendChild(document.createTextNode('\xA0'));
   3108       }
   3109       ol.appendChild(li);
   3110     }
   3111   
   3112     node.appendChild(ol);
   3113   }
   3114 
   3115   /**
   3116    * Breaks {@code job.sourceCode} around style boundaries in
   3117    * {@code job.decorations} and modifies {@code job.sourceNode} in place.
   3118    * @param {Object} job like <pre>{
   3119    *    sourceCode: {string} source as plain text,
   3120    *    spans: {Array.<number|Node>} alternating span start indices into source
   3121    *       and the text node or element (e.g. {@code <BR>}) corresponding to that
   3122    *       span.
   3123    *    decorations: {Array.<number|string} an array of style classes preceded
   3124    *       by the position at which they start in job.sourceCode in order
   3125    * }</pre>
   3126    * @private
   3127    */
   3128   function recombineTagsAndDecorations(job) {
   3129     var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
   3130     isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
   3131     var newlineRe = /\n/g;
   3132   
   3133     var source = job.sourceCode;
   3134     var sourceLength = source.length;
   3135     // Index into source after the last code-unit recombined.
   3136     var sourceIndex = 0;
   3137   
   3138     var spans = job.spans;
   3139     var nSpans = spans.length;
   3140     // Index into spans after the last span which ends at or before sourceIndex.
   3141     var spanIndex = 0;
   3142   
   3143     var decorations = job.decorations;
   3144     var nDecorations = decorations.length;
   3145     // Index into decorations after the last decoration which ends at or before
   3146     // sourceIndex.
   3147     var decorationIndex = 0;
   3148   
   3149     // Remove all zero-length decorations.
   3150     decorations[nDecorations] = sourceLength;
   3151     var decPos, i;
   3152     for (i = decPos = 0; i < nDecorations;) {
   3153       if (decorations[i] !== decorations[i + 2]) {
   3154         decorations[decPos++] = decorations[i++];
   3155         decorations[decPos++] = decorations[i++];
   3156       } else {
   3157         i += 2;
   3158       }
   3159     }
   3160     nDecorations = decPos;
   3161   
   3162     // Simplify decorations.
   3163     for (i = decPos = 0; i < nDecorations;) {
   3164       var startPos = decorations[i];
   3165       // Conflate all adjacent decorations that use the same style.
   3166       var startDec = decorations[i + 1];
   3167       var end = i + 2;
   3168       while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
   3169         end += 2;
   3170       }
   3171       decorations[decPos++] = startPos;
   3172       decorations[decPos++] = startDec;
   3173       i = end;
   3174     }
   3175   
   3176     nDecorations = decorations.length = decPos;
   3177   
   3178     var sourceNode = job.sourceNode;
   3179     var oldDisplay;
   3180     if (sourceNode) {
   3181       oldDisplay = sourceNode.style.display;
   3182       sourceNode.style.display = 'none';
   3183     }
   3184     try {
   3185       var decoration = null;
   3186       while (spanIndex < nSpans) {
   3187         var spanStart = spans[spanIndex];
   3188         var spanEnd = spans[spanIndex + 2] || sourceLength;
   3189   
   3190         var decEnd = decorations[decorationIndex + 2] || sourceLength;
   3191   
   3192         var end = Math.min(spanEnd, decEnd);
   3193   
   3194         var textNode = spans[spanIndex + 1];
   3195         var styledText;
   3196         if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
   3197             // Don't introduce spans around empty text nodes.
   3198             && (styledText = source.substring(sourceIndex, end))) {
   3199           // This may seem bizarre, and it is.  Emitting LF on IE causes the
   3200           // code to display with spaces instead of line breaks.
   3201           // Emitting Windows standard issue linebreaks (CRLF) causes a blank
   3202           // space to appear at the beginning of every line but the first.
   3203           // Emitting an old Mac OS 9 line separator makes everything spiffy.
   3204           if (isIE8OrEarlier) {
   3205             styledText = styledText.replace(newlineRe, '\r');
   3206           }
   3207           textNode.nodeValue = styledText;
   3208           var document = textNode.ownerDocument;
   3209           var span = document.createElement('span');
   3210           span.className = decorations[decorationIndex + 1];
   3211           var parentNode = textNode.parentNode;
   3212           parentNode.replaceChild(span, textNode);
   3213           span.appendChild(textNode);
   3214           if (sourceIndex < spanEnd) {  // Split off a text node.
   3215             spans[spanIndex + 1] = textNode
   3216                 // TODO: Possibly optimize by using '' if there's no flicker.
   3217                 = document.createTextNode(source.substring(end, spanEnd));
   3218             parentNode.insertBefore(textNode, span.nextSibling);
   3219           }
   3220         }
   3221   
   3222         sourceIndex = end;
   3223   
   3224         if (sourceIndex >= spanEnd) {
   3225           spanIndex += 2;
   3226         }
   3227         if (sourceIndex >= decEnd) {
   3228           decorationIndex += 2;
   3229         }
   3230       }
   3231     } finally {
   3232       if (sourceNode) {
   3233         sourceNode.style.display = oldDisplay;
   3234       }
   3235     }
   3236   }
   3237 
   3238 
   3239   /** Maps language-specific file extensions to handlers. */
   3240   var langHandlerRegistry = {};
   3241   /** Register a language handler for the given file extensions.
   3242     * @param {function (Object)} handler a function from source code to a list
   3243     *      of decorations.  Takes a single argument job which describes the
   3244     *      state of the computation.   The single parameter has the form
   3245     *      {@code {
   3246     *        sourceCode: {string} as plain text.
   3247     *        decorations: {Array.<number|string>} an array of style classes
   3248     *                     preceded by the position at which they start in
   3249     *                     job.sourceCode in order.
   3250     *                     The language handler should assigned this field.
   3251     *        basePos: {int} the position of source in the larger source chunk.
   3252     *                 All positions in the output decorations array are relative
   3253     *                 to the larger source chunk.
   3254     *      } }
   3255     * @param {Array.<string>} fileExtensions
   3256     */
   3257   function registerLangHandler(handler, fileExtensions) {
   3258     for (var i = fileExtensions.length; --i >= 0;) {
   3259       var ext = fileExtensions[i];
   3260       if (!langHandlerRegistry.hasOwnProperty(ext)) {
   3261         langHandlerRegistry[ext] = handler;
   3262       } else if (win['console']) {
   3263         console['warn']('cannot override language handler %s', ext);
   3264       }
   3265     }
   3266   }
   3267   function langHandlerForExtension(extension, source) {
   3268     if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
   3269       // Treat it as markup if the first non whitespace character is a < and
   3270       // the last non-whitespace character is a >.
   3271       extension = /^\s*</.test(source)
   3272           ? 'default-markup'
   3273           : 'default-code';
   3274     }
   3275     return langHandlerRegistry[extension];
   3276   }
   3277   registerLangHandler(decorateSource, ['default-code']);
   3278   registerLangHandler(
   3279       createSimpleLexer(
   3280           [],
   3281           [
   3282            [PR_PLAIN,       /^[^<?]+/],
   3283            [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
   3284            [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
   3285            // Unescaped content in an unknown language
   3286            ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
   3287            ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
   3288            [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
   3289            ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
   3290            // Unescaped content in javascript.  (Or possibly vbscript).
   3291            ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
   3292            // Contains unescaped stylesheet content
   3293            ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
   3294            ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
   3295           ]),
   3296       ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
   3297   registerLangHandler(
   3298       createSimpleLexer(
   3299           [
   3300            [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
   3301            [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
   3302            ],
   3303           [
   3304            [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
   3305            [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
   3306            ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
   3307            [PR_PUNCTUATION,  /^[=<>\/]+/],
   3308            ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
   3309            ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
   3310            ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
   3311            ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
   3312            ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
   3313            ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
   3314            ]),
   3315       ['in.tag']);
   3316   registerLangHandler(
   3317       createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
   3318   registerLangHandler(sourceDecorator({
   3319           'keywords': CPP_KEYWORDS,
   3320           'hashComments': true,
   3321           'cStyleComments': true,
   3322           'types': C_TYPES
   3323         }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
   3324   registerLangHandler(sourceDecorator({
   3325           'keywords': 'null,true,false'
   3326         }), ['json']);
   3327   registerLangHandler(sourceDecorator({
   3328           'keywords': CSHARP_KEYWORDS,
   3329           'hashComments': true,
   3330           'cStyleComments': true,
   3331           'verbatimStrings': true,
   3332           'types': C_TYPES
   3333         }), ['cs']);
   3334   registerLangHandler(sourceDecorator({
   3335           'keywords': JAVA_KEYWORDS,
   3336           'cStyleComments': true
   3337         }), ['java']);
   3338   registerLangHandler(sourceDecorator({
   3339           'keywords': SH_KEYWORDS,
   3340           'hashComments': true,
   3341           'multiLineStrings': true
   3342         }), ['bsh', 'csh', 'sh']);
   3343   registerLangHandler(sourceDecorator({
   3344           'keywords': PYTHON_KEYWORDS,
   3345           'hashComments': true,
   3346           'multiLineStrings': true,
   3347           'tripleQuotedStrings': true
   3348         }), ['cv', 'py']);
   3349   registerLangHandler(sourceDecorator({
   3350           'keywords': PERL_KEYWORDS,
   3351           'hashComments': true,
   3352           'multiLineStrings': true,
   3353           'regexLiterals': true
   3354         }), ['perl', 'pl', 'pm']);
   3355   registerLangHandler(sourceDecorator({
   3356           'keywords': RUBY_KEYWORDS,
   3357           'hashComments': true,
   3358           'multiLineStrings': true,
   3359           'regexLiterals': true
   3360         }), ['rb']);
   3361   registerLangHandler(sourceDecorator({
   3362           'keywords': JSCRIPT_KEYWORDS,
   3363           'cStyleComments': true,
   3364           'regexLiterals': true
   3365         }), ['js']);
   3366   registerLangHandler(sourceDecorator({
   3367           'keywords': COFFEE_KEYWORDS,
   3368           'hashComments': 3,  // ### style block comments
   3369           'cStyleComments': true,
   3370           'multilineStrings': true,
   3371           'tripleQuotedStrings': true,
   3372           'regexLiterals': true
   3373         }), ['coffee']);
   3374   registerLangHandler(
   3375       createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
   3376 
   3377   function applyDecorator(job) {
   3378     var opt_langExtension = job.langExtension;
   3379 
   3380     try {
   3381       // Extract tags, and convert the source code to plain text.
   3382       var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
   3383       /** Plain text. @type {string} */
   3384       var source = sourceAndSpans.sourceCode;
   3385       job.sourceCode = source;
   3386       job.spans = sourceAndSpans.spans;
   3387       job.basePos = 0;
   3388 
   3389       // Apply the appropriate language handler
   3390       langHandlerForExtension(opt_langExtension, source)(job);
   3391 
   3392       // Integrate the decorations and tags back into the source code,
   3393       // modifying the sourceNode in place.
   3394       recombineTagsAndDecorations(job);
   3395     } catch (e) {
   3396       if (win['console']) {
   3397         console['log'](e && e['stack'] ? e['stack'] : e);
   3398       }
   3399     }
   3400   }
   3401 
   3402   /**
   3403    * @param sourceCodeHtml {string} The HTML to pretty print.
   3404    * @param opt_langExtension {string} The language name to use.
   3405    *     Typically, a filename extension like 'cpp' or 'java'.
   3406    * @param opt_numberLines {number|boolean} True to number lines,
   3407    *     or the 1-indexed number of the first line in sourceCodeHtml.
   3408    */
   3409   function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
   3410     var container = document.createElement('pre');
   3411     // This could cause images to load and onload listeners to fire.
   3412     // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
   3413     // We assume that the inner HTML is from a trusted source.
   3414     container.innerHTML = sourceCodeHtml;
   3415     if (opt_numberLines) {
   3416       numberLines(container, opt_numberLines, true);
   3417     }
   3418 
   3419     var job = {
   3420       langExtension: opt_langExtension,
   3421       numberLines: opt_numberLines,
   3422       sourceNode: container,
   3423       pre: 1
   3424     };
   3425     applyDecorator(job);
   3426     return container.innerHTML;
   3427   }
   3428 
   3429   function prettyPrint(opt_whenDone) {
   3430     function byTagName(tn) { return document.getElementsByTagName(tn); }
   3431     // fetch a list of nodes to rewrite
   3432     var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
   3433     var elements = [];
   3434     for (var i = 0; i < codeSegments.length; ++i) {
   3435       for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
   3436         elements.push(codeSegments[i][j]);
   3437       }
   3438     }
   3439     codeSegments = null;
   3440 
   3441     var clock = Date;
   3442     if (!clock['now']) {
   3443       clock = { 'now': function () { return +(new Date); } };
   3444     }
   3445 
   3446     // The loop is broken into a series of continuations to make sure that we
   3447     // don't make the browser unresponsive when rewriting a large page.
   3448     var k = 0;
   3449     var prettyPrintingJob;
   3450 
   3451     var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
   3452     var prettyPrintRe = /\bprettyprint\b/;
   3453     var prettyPrintedRe = /\bprettyprinted\b/;
   3454     var preformattedTagNameRe = /pre|xmp/i;
   3455     var codeRe = /^code$/i;
   3456     var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
   3457 
   3458     function doWork() {
   3459       var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
   3460                      clock['now']() + 250 /* ms */ :
   3461                      Infinity);
   3462       for (; k < elements.length && clock['now']() < endTime; k++) {
   3463         var cs = elements[k];
   3464         var className = cs.className;
   3465         if (prettyPrintRe.test(className)
   3466             // Don't redo this if we've already done it.
   3467             // This allows recalling pretty print to just prettyprint elements
   3468             // that have been added to the page since last call.
   3469             && !prettyPrintedRe.test(className)) {
   3470 
   3471           // make sure this is not nested in an already prettified element
   3472           var nested = false;
   3473           for (var p = cs.parentNode; p; p = p.parentNode) {
   3474             var tn = p.tagName;
   3475             if (preCodeXmpRe.test(tn)
   3476                 && p.className && prettyPrintRe.test(p.className)) {
   3477               nested = true;
   3478               break;
   3479             }
   3480           }
   3481           if (!nested) {
   3482             // Mark done.  If we fail to prettyprint for whatever reason,
   3483             // we shouldn't try again.
   3484             cs.className += ' prettyprinted';
   3485 
   3486             // If the classes includes a language extensions, use it.
   3487             // Language extensions can be specified like
   3488             //     <pre class="prettyprint lang-cpp">
   3489             // the language extension "cpp" is used to find a language handler
   3490             // as passed to PR.registerLangHandler.
   3491             // HTML5 recommends that a language be specified using "language-"
   3492             // as the prefix instead.  Google Code Prettify supports both.
   3493             // http://dev.w3.org/html5/spec-author-view/the-code-element.html
   3494             var langExtension = className.match(langExtensionRe);
   3495             // Support <pre class="prettyprint"><code class="language-c">
   3496             var wrapper;
   3497             if (!langExtension && (wrapper = childContentWrapper(cs))
   3498                 && codeRe.test(wrapper.tagName)) {
   3499               langExtension = wrapper.className.match(langExtensionRe);
   3500             }
   3501 
   3502             if (langExtension) { langExtension = langExtension[1]; }
   3503 
   3504             var preformatted;
   3505             if (preformattedTagNameRe.test(cs.tagName)) {
   3506               preformatted = 1;
   3507             } else {
   3508               var currentStyle = cs['currentStyle'];
   3509               var whitespace = (
   3510                   currentStyle
   3511                   ? currentStyle['whiteSpace']
   3512                   : (document.defaultView
   3513                      && document.defaultView.getComputedStyle)
   3514                   ? document.defaultView.getComputedStyle(cs, null)
   3515                   .getPropertyValue('white-space')
   3516                   : 0);
   3517               preformatted = whitespace
   3518                   && 'pre' === whitespace.substring(0, 3);
   3519             }
   3520 
   3521             // Look for a class like linenums or linenums:<n> where <n> is the
   3522             // 1-indexed number of the first line.
   3523             var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
   3524             lineNums = lineNums
   3525                 ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
   3526                 : false;
   3527             if (lineNums) { numberLines(cs, lineNums, preformatted); }
   3528 
   3529             // do the pretty printing
   3530             prettyPrintingJob = {
   3531               langExtension: langExtension,
   3532               sourceNode: cs,
   3533               numberLines: lineNums,
   3534               pre: preformatted
   3535             };
   3536             applyDecorator(prettyPrintingJob);
   3537           }
   3538         }
   3539       }
   3540       if (k < elements.length) {
   3541         // finish up in a continuation
   3542         setTimeout(doWork, 250);
   3543       } else if (opt_whenDone) {
   3544         opt_whenDone();
   3545       }
   3546     }
   3547 
   3548     doWork();
   3549   }
   3550 
   3551   /**
   3552    * Contains functions for creating and registering new language handlers.
   3553    * @type {Object}
   3554    */
   3555   var PR = win['PR'] = {
   3556         'createSimpleLexer': createSimpleLexer,
   3557         'registerLangHandler': registerLangHandler,
   3558         'sourceDecorator': sourceDecorator,
   3559         'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
   3560         'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
   3561         'PR_COMMENT': PR_COMMENT,
   3562         'PR_DECLARATION': PR_DECLARATION,
   3563         'PR_KEYWORD': PR_KEYWORD,
   3564         'PR_LITERAL': PR_LITERAL,
   3565         'PR_NOCODE': PR_NOCODE,
   3566         'PR_PLAIN': PR_PLAIN,
   3567         'PR_PUNCTUATION': PR_PUNCTUATION,
   3568         'PR_SOURCE': PR_SOURCE,
   3569         'PR_STRING': PR_STRING,
   3570         'PR_TAG': PR_TAG,
   3571         'PR_TYPE': PR_TYPE,
   3572         'prettyPrintOne': win['prettyPrintOne'] = prettyPrintOne,
   3573         'prettyPrint': win['prettyPrint'] = prettyPrint
   3574       };
   3575 
   3576   // Make PR available via the Asynchronous Module Definition (AMD) API.
   3577   // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
   3578   // The Asynchronous Module Definition (AMD) API specifies a
   3579   // mechanism for defining modules such that the module and its
   3580   // dependencies can be asynchronously loaded.
   3581   // ...
   3582   // To allow a clear indicator that a global define function (as
   3583   // needed for script src browser loading) conforms to the AMD API,
   3584   // any global define function SHOULD have a property called "amd"
   3585   // whose value is an object. This helps avoid conflict with any
   3586   // other existing JavaScript code that could have defined a define()
   3587   // function that does not conform to the AMD API.
   3588   if (typeof define === "function" && define['amd']) {
   3589     define(function () {
   3590       return PR; 
   3591     });
   3592   }
   3593 })();
   3594 ;
   3595 (function(scope) {
   3596   
   3597   var ContextFreeParser = {
   3598     parse: function(text) {
   3599       var top = {};
   3600       var entities = [];
   3601       var current = top;
   3602       var subCurrent = {};
   3603   
   3604       var scriptDocCommentClause = '\\/\\*\\*([\\s\\S]*?)\\*\\/';
   3605       var htmlDocCommentClause = '<!--([\\s\\S]*?)-->';
   3606   
   3607       // matches text between /** and */ inclusive and <!-- and --> inclusive
   3608       var docCommentRegex = new RegExp(scriptDocCommentClause + '|' + htmlDocCommentClause, 'g');
   3609   
   3610       // acquire all script doc comments
   3611       var docComments = text.match(docCommentRegex) || [];
   3612   
   3613       // each match represents a single block of doc comments
   3614       docComments.forEach(function(m) {
   3615         // unify line ends, remove all comment characters, split into individual lines
   3616         var lines = m.replace(/\r\n/g, '\n').replace(/^\s*\/\*\*|^\s*\*\/|^\s*\* ?|^\s*\<\!-\-|^s*\-\-\>/gm, '').split('\n');
   3617   
   3618         // pragmas (@-rules) must occur on a line by themselves
   3619         var pragmas = [];
   3620         // filter lines whose first non-whitespace character is @ into the pragma list
   3621         // (and out of the `lines` array)
   3622         lines = lines.filter(function(l) {
   3623           var m = l.match(/\s*@([\w-]*) (.*)/);
   3624           if (!m) {
   3625             return true;
   3626           }
   3627           pragmas.push(m);
   3628         });
   3629   
   3630         // collect all other text into a single block
   3631         var code = lines.join('\n');
   3632         
   3633         // process pragmas
   3634         pragmas.forEach(function(m) {
   3635           var pragma = m[1], content = m[2];
   3636           switch (pragma) {
   3637   
   3638             // currently all entities are either @class or @element
   3639             case 'class':
   3640             case 'element':
   3641               current = {
   3642                 name: content,
   3643                 description: code
   3644               };
   3645               entities.push(current);
   3646               break;
   3647             
   3648             // an entity may have these describable sub-features
   3649             case 'attribute':
   3650             case 'property':
   3651             case 'method':
   3652             case 'event':
   3653               subCurrent = {
   3654                 name: content,
   3655                 description: code
   3656               };
   3657               var label = pragma == 'property' ? 'properties' : pragma + 's';
   3658               makePragma(current, label, subCurrent);
   3659               break;
   3660   
   3661             // sub-feature pragmas
   3662             case 'default':
   3663             case 'type':
   3664               subCurrent[pragma] = content;
   3665               break;
   3666   
   3667             // everything else
   3668             default:
   3669               current[pragma] = content;
   3670               break;
   3671           }
   3672         });
   3673   
   3674         // utility function, yay hoisting
   3675         function makePragma(object, pragma, content) {
   3676           var p$ = object;
   3677           var p = p$[pragma];
   3678           if (!p) {
   3679             p$[pragma] = p = [];
   3680           }
   3681           p.push(content);
   3682         }
   3683   
   3684       });
   3685   
   3686       if (entities.length === 0) {
   3687         entities.push({name: 'Entity', description: '**Undocumented**'});
   3688       }
   3689       return entities;
   3690     }
   3691   };
   3692   
   3693   if (typeof module !== 'undefined' && module.exports) {
   3694     module.exports = ContextFreeParser;
   3695   } else {
   3696     scope.ContextFreeParser = ContextFreeParser;
   3697   }
   3698   
   3699 })(this);;
   3700 
   3701 
   3702     Polymer('core-xhr', {
   3703 
   3704       /**
   3705        * Sends a HTTP request to the server and returns the XHR object.
   3706        *
   3707        * @method request
   3708        * @param {Object} inOptions
   3709        *    @param {String} inOptions.url The url to which the request is sent.
   3710        *    @param {String} inOptions.method The HTTP method to use, default is GET.
   3711        *    @param {boolean} inOptions.sync By default, all requests are sent asynchronously. To send synchronous requests, set to true.
   3712        *    @param {Object} inOptions.params Data to be sent to the server.
   3713        *    @param {Object} inOptions.body The content for the request body for POST method.
   3714        *    @param {Object} inOptions.headers HTTP request headers.
   3715        *    @param {String} inOptions.responseType The response type. Default is 'text'.
   3716        *    @param {boolean} inOptions.withCredentials Whether or not to send credentials on the request. Default is false.
   3717        *    @param {Object} inOptions.callback Called when request is completed.
   3718        * @returns {Object} XHR object.
   3719        */
   3720       request: function(options) {
   3721         var xhr = new XMLHttpRequest();
   3722         var url = options.url;
   3723         var method = options.method || 'GET';
   3724         var async = !options.sync;
   3725         //
   3726         var params = this.toQueryString(options.params);
   3727         if (params && method == 'GET') {
   3728           url += (url.indexOf('?') > 0 ? '&' : '?') + params;
   3729         }
   3730         var xhrParams = this.isBodyMethod(method) ? (options.body || params) : null;
   3731         //
   3732         xhr.open(method, url, async);
   3733         if (options.responseType) {
   3734           xhr.responseType = options.responseType;
   3735         }
   3736         if (options.withCredentials) {
   3737           xhr.withCredentials = true;
   3738         }
   3739         this.makeReadyStateHandler(xhr, options.callback);
   3740         this.setRequestHeaders(xhr, options.headers);
   3741         xhr.send(xhrParams);
   3742         if (!async) {
   3743           xhr.onreadystatechange(xhr);
   3744         }
   3745         return xhr;
   3746       },
   3747     
   3748       toQueryString: function(params) {
   3749         var r = [];
   3750         for (var n in params) {
   3751           var v = params[n];
   3752           n = encodeURIComponent(n);
   3753           r.push(v == null ? n : (n + '=' + encodeURIComponent(v)));
   3754         }
   3755         return r.join('&');
   3756       },
   3757 
   3758       isBodyMethod: function(method) {
   3759         return this.bodyMethods[(method || '').toUpperCase()];
   3760       },
   3761       
   3762       bodyMethods: {
   3763         POST: 1,
   3764         PUT: 1,
   3765         DELETE: 1
   3766       },
   3767 
   3768       makeReadyStateHandler: function(xhr, callback) {
   3769         xhr.onreadystatechange = function() {
   3770           if (xhr.readyState == 4) {
   3771             callback && callback.call(null, xhr.response, xhr);
   3772           }
   3773         };
   3774       },
   3775 
   3776       setRequestHeaders: function(xhr, headers) {
   3777         if (headers) {
   3778           for (var name in headers) {
   3779             xhr.setRequestHeader(name, headers[name]);
   3780           }
   3781         }
   3782       }
   3783 
   3784     });
   3785 
   3786   ;
   3787 
   3788 
   3789   Polymer('core-ajax', {
   3790     /**
   3791      * Fired when a response is received.
   3792      * 
   3793      * @event core-response
   3794      */
   3795 
   3796     /**
   3797      * Fired when an error is received.
   3798      * 
   3799      * @event core-error
   3800      */
   3801 
   3802     /**
   3803      * Fired whenever a response or an error is received.
   3804      *
   3805      * @event core-complete
   3806      */
   3807 
   3808     /**
   3809      * The URL target of the request.
   3810      * 
   3811      * @attribute url
   3812      * @type string
   3813      * @default ''
   3814      */
   3815     url: '',
   3816 
   3817     /**
   3818      * Specifies what data to store in the `response` property, and
   3819      * to deliver as `event.response` in `response` events.
   3820      * 
   3821      * One of:
   3822      * 
   3823      *    `text`: uses `XHR.responseText`.
   3824      *    
   3825      *    `xml`: uses `XHR.responseXML`.
   3826      *    
   3827      *    `json`: uses `XHR.responseText` parsed as JSON.
   3828      *
   3829      *    `arraybuffer`: uses `XHR.response`.
   3830      *
   3831      *    `blob`: uses `XHR.response`.
   3832      *
   3833      *    `document`: uses `XHR.response`.
   3834      *  
   3835      * @attribute handleAs
   3836      * @type string
   3837      * @default 'text'
   3838      */
   3839     handleAs: '',
   3840 
   3841     /**
   3842      * If true, automatically performs an Ajax request when either `url` or `params` changes.
   3843      *
   3844      * @attribute auto
   3845      * @type boolean
   3846      * @default false
   3847      */
   3848     auto: false,
   3849 
   3850     /**
   3851      * Parameters to send to the specified URL, as JSON.
   3852      *  
   3853      * @attribute params
   3854      * @type string (JSON)
   3855      * @default ''
   3856      */
   3857     params: '',
   3858 
   3859     /**
   3860      * Returns the response object.
   3861      *
   3862      * @attribute response
   3863      * @type Object
   3864      * @default null
   3865      */
   3866     response: null,
   3867 
   3868     /**
   3869      * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
   3870      * Default is 'GET'.
   3871      *
   3872      * @attribute method
   3873      * @type string
   3874      * @default ''
   3875      */
   3876     method: '',
   3877 
   3878     /**
   3879      * HTTP request headers to send.
   3880      *
   3881      * Example:
   3882      *
   3883      *     <core-ajax 
   3884      *         auto
   3885      *         url="http://somesite.com"
   3886      *         headers='{"X-Requested-With": "XMLHttpRequest"}'
   3887      *         handleAs="json"
   3888      *         on-core-response="{{handleResponse}}"></core-ajax>
   3889      *  
   3890      * @attribute headers
   3891      * @type Object
   3892      * @default null
   3893      */
   3894     headers: null,
   3895 
   3896     /**
   3897      * Optional raw body content to send when method === "POST".
   3898      *
   3899      * Example:
   3900      *
   3901      *     <core-ajax method="POST" auto url="http://somesite.com"
   3902      *         body='{"foo":1, "bar":2}'>
   3903      *     </core-ajax>
   3904      *  
   3905      * @attribute body
   3906      * @type Object
   3907      * @default null
   3908      */
   3909     body: null,
   3910 
   3911     /**
   3912      * Content type to use when sending data.
   3913      *
   3914      * @attribute contentType
   3915      * @type string
   3916      * @default 'application/x-www-form-urlencoded'
   3917      */
   3918     contentType: 'application/x-www-form-urlencoded',
   3919 
   3920     /**
   3921      * Set the withCredentials flag on the request.
   3922      * 
   3923      * @attribute withCredentials
   3924      * @type boolean
   3925      * @default false
   3926      */
   3927     withCredentials: false,
   3928     
   3929     /**
   3930      * Additional properties to send to core-xhr.
   3931      *
   3932      * Can be set to an object containing default properties
   3933      * to send as arguments to the `core-xhr.request()` method
   3934      * which implements the low-level communication.
   3935      * 
   3936      * @property xhrArgs
   3937      * @type Object
   3938      * @default null
   3939      */
   3940     xhrArgs: null,
   3941      
   3942     ready: function() {
   3943       this.xhr = document.createElement('core-xhr');
   3944     },
   3945 
   3946     receive: function(response, xhr) {
   3947       if (this.isSuccess(xhr)) {
   3948         this.processResponse(xhr);
   3949       } else {
   3950         this.error(xhr);
   3951       }
   3952       this.complete(xhr);
   3953     },
   3954 
   3955     isSuccess: function(xhr) {
   3956       var status = xhr.status || 0;
   3957       return !status || (status >= 200 && status < 300);
   3958     },
   3959 
   3960     processResponse: function(xhr) {
   3961       var response = this.evalResponse(xhr);
   3962       this.response = response;
   3963       this.fire('core-response', {response: response, xhr: xhr});
   3964     },
   3965 
   3966     error: function(xhr) {
   3967       var response = xhr.status + ': ' + xhr.responseText;
   3968       this.fire('core-error', {response: response, xhr: xhr});
   3969     },
   3970 
   3971     complete: function(xhr) {
   3972       this.fire('core-complete', {response: xhr.status, xhr: xhr});
   3973     },
   3974 
   3975     evalResponse: function(xhr) {
   3976       return this[(this.handleAs || 'text') + 'Handler'](xhr);
   3977     },
   3978 
   3979     xmlHandler: function(xhr) {
   3980       return xhr.responseXML;
   3981     },
   3982 
   3983     textHandler: function(xhr) {
   3984       return xhr.responseText;
   3985     },
   3986 
   3987     jsonHandler: function(xhr) {
   3988       var r = xhr.responseText;
   3989       try {
   3990         return JSON.parse(r);
   3991       } catch (x) {
   3992         return r;
   3993       }
   3994     },
   3995 
   3996     documentHandler: function(xhr) {
   3997       return xhr.response;
   3998     },
   3999 
   4000     blobHandler: function(xhr) {
   4001       return xhr.response;
   4002     },
   4003 
   4004     arraybufferHandler: function(xhr) {
   4005       return xhr.response;
   4006     },
   4007 
   4008     urlChanged: function() {
   4009       if (!this.handleAs) {
   4010         var ext = String(this.url).split('.').pop();
   4011         switch (ext) {
   4012           case 'json':
   4013             this.handleAs = 'json';
   4014             break;
   4015         }
   4016       }
   4017       this.autoGo();
   4018     },
   4019 
   4020     paramsChanged: function() {
   4021       this.autoGo();
   4022     },
   4023 
   4024     autoChanged: function() {
   4025       this.autoGo();
   4026     },
   4027 
   4028     // TODO(sorvell): multiple side-effects could call autoGo 
   4029     // during one micro-task, use a job to have only one action 
   4030     // occur
   4031     autoGo: function() {
   4032       if (this.auto) {
   4033         this.goJob = this.job(this.goJob, this.go, 0);
   4034       }
   4035     },
   4036 
   4037     /**
   4038      * Performs an Ajax request to the specified URL.
   4039      *
   4040      * @method go
   4041      */
   4042     go: function() {
   4043       var args = this.xhrArgs || {};
   4044       // TODO(sjmiles): we may want XHR to default to POST if body is set
   4045       args.body = this.body || args.body;
   4046       args.params = this.params || args.params;
   4047       if (args.params && typeof(args.params) == 'string') {
   4048         args.params = JSON.parse(args.params);
   4049       }
   4050       args.headers = this.headers || args.headers || {};
   4051       if (args.headers && typeof(args.headers) == 'string') {
   4052         args.headers = JSON.parse(args.headers);
   4053       }
   4054       if (this.contentType) {
   4055         args.headers['content-type'] = this.contentType;
   4056       }
   4057       if (this.handleAs === 'arraybuffer' || this.handleAs === 'blob' ||
   4058           this.handleAs === 'document') {
   4059         args.responseType = this.handleAs;
   4060       }
   4061       args.withCredentials = this.withCredentials;
   4062       args.callback = this.receive.bind(this);
   4063       args.url = this.url;
   4064       args.method = this.method;
   4065       return args.url && this.xhr.request(args);
   4066     }
   4067 
   4068   });
   4069 
   4070 ;
   4071 
   4072 
   4073   Polymer('context-free-parser', {
   4074 
   4075     text: null,
   4076 
   4077     textChanged: function() {
   4078       if (this.text) {
   4079         var entities = ContextFreeParser.parse(this.text);
   4080         if (!entities || entities.length === 0) {
   4081           entities = [
   4082             {name: this.url.split('/').pop(), description: '**Undocumented**'}
   4083           ];
   4084         }
   4085         this.data = { classes: entities };
   4086       }
   4087     },
   4088 
   4089     dataChanged: function() {
   4090       this.fire('data-ready');
   4091     }
   4092 
   4093   });
   4094 
   4095 ;
   4096 
   4097 
   4098     Polymer('core-doc-page', {
   4099 
   4100       hilight: function(event, detail, sender) {
   4101         detail.code = prettyPrintOne((detail.code || '').replace(/</g,'&lt;').replace(/>/g,'&gt;'));
   4102       },
   4103 
   4104       homepageFilter: function(data) {
   4105         if (!data) {
   4106           return '';
   4107         }
   4108         if (!data.homepage || data.homepage === 'github.io') {
   4109           return '//polymer.github.io/' + data.name;
   4110         } else {
   4111           return data.homepage;
   4112         }
   4113       }
   4114 
   4115     });
   4116 
   4117   ;
   4118 
   4119     Polymer('core-selection', {
   4120       /**
   4121        * If true, multiple selections are allowed.
   4122        *
   4123        * @attribute multi
   4124        * @type boolean
   4125        * @default false
   4126        */
   4127       multi: false,
   4128       ready: function() {
   4129         this.clear();
   4130       },
   4131       clear: function() {
   4132         this.selection = [];
   4133       },
   4134       /**
   4135        * Retrieves the selected item(s).
   4136        * @method getSelection
   4137        * @returns Returns the selected item(s). If the multi property is true,
   4138        * getSelection will return an array, otherwise it will return 
   4139        * the selected item or undefined if there is no selection.
   4140       */
   4141       getSelection: function() {
   4142         return this.multi ? this.selection : this.selection[0];
   4143       },
   4144       /**
   4145        * Indicates if a given item is selected.
   4146        * @method isSelected
   4147        * @param {any} item The item whose selection state should be checked.
   4148        * @returns Returns true if `item` is selected.
   4149       */
   4150       isSelected: function(item) {
   4151         return this.selection.indexOf(item) >= 0;
   4152       },
   4153       setItemSelected: function(item, isSelected) {
   4154         if (item !== undefined && item !== null) {
   4155           if (isSelected) {
   4156             this.selection.push(item);
   4157           } else {
   4158             var i = this.selection.indexOf(item);
   4159             if (i >= 0) {
   4160               this.selection.splice(i, 1);
   4161             }
   4162           }
   4163           this.fire("core-select", {isSelected: isSelected, item: item});
   4164         }
   4165       },
   4166       /**
   4167        * Set the selection state for a given `item`. If the multi property
   4168        * is true, then the selected state of `item` will be toggled; otherwise
   4169        * the `item` will be selected.
   4170        * @method select
   4171        * @param {any} item: The item to select.
   4172       */
   4173       select: function(item) {
   4174         if (this.multi) {
   4175           this.toggle(item);
   4176         } else if (this.getSelection() !== item) {
   4177           this.setItemSelected(this.getSelection(), false);
   4178           this.setItemSelected(item, true);
   4179         }
   4180       },
   4181       /**
   4182        * Toggles the selection state for `item`.
   4183        * @method toggle
   4184        * @param {any} item: The item to toggle.
   4185       */
   4186       toggle: function(item) {
   4187         this.setItemSelected(item, !this.isSelected(item));
   4188       }
   4189     });
   4190   ;
   4191 
   4192 
   4193     Polymer('core-selector', {
   4194 
   4195       /**
   4196        * Gets or sets the selected element.  Default to use the index
   4197        * of the item element.
   4198        *
   4199        * If you want a specific attribute value of the element to be
   4200        * used instead of index, set "valueattr" to that attribute name.
   4201        *
   4202        * Example:
   4203        *
   4204        *     <core-selector valueattr="label" selected="foo">
   4205        *       <div label="foo"></div>
   4206        *       <div label="bar"></div>
   4207        *       <div label="zot"></div>
   4208        *     </core-selector>
   4209        *
   4210        * In multi-selection this should be an array of values.
   4211        *
   4212        * Example:
   4213        *
   4214        *     <core-selector id="selector" valueattr="label" multi>
   4215        *       <div label="foo"></div>
   4216        *       <div label="bar"></div>
   4217        *       <div label="zot"></div>
   4218        *     </core-selector>
   4219        *
   4220        *     this.$.selector.selected = ['foo', 'zot'];
   4221        *
   4222        * @attribute selected
   4223        * @type Object
   4224        * @default null
   4225        */
   4226       selected: null,
   4227 
   4228       /**
   4229        * If true, multiple selections are allowed.
   4230        *
   4231        * @attribute multi
   4232        * @type boolean
   4233        * @default false
   4234        */
   4235       multi: false,
   4236 
   4237       /**
   4238        * Specifies the attribute to be used for "selected" attribute.
   4239        *
   4240        * @attribute valueattr
   4241        * @type string
   4242        * @default 'name'
   4243        */
   4244       valueattr: 'name',
   4245 
   4246       /**
   4247        * Specifies the CSS class to be used to add to the selected element.
   4248        * 
   4249        * @attribute selectedClass
   4250        * @type string
   4251        * @default 'core-selected'
   4252        */
   4253       selectedClass: 'core-selected',
   4254 
   4255       /**
   4256        * Specifies the property to be used to set on the selected element
   4257        * to indicate its active state.
   4258        *
   4259        * @attribute selectedProperty
   4260        * @type string
   4261        * @default ''
   4262        */
   4263       selectedProperty: '',
   4264 
   4265       /**
   4266        * Specifies the attribute to set on the selected element to indicate
   4267        * its active state.
   4268        *
   4269        * @attribute selectedAttribute
   4270        * @type string
   4271        * @default 'active'
   4272        */
   4273       selectedAttribute: 'active',
   4274 
   4275       /**
   4276        * Returns the currently selected element. In multi-selection this returns
   4277        * an array of selected elements.
   4278        * 
   4279        * @attribute selectedItem
   4280        * @type Object
   4281        * @default null
   4282        */
   4283       selectedItem: null,
   4284 
   4285       /**
   4286        * In single selection, this returns the model associated with the
   4287        * selected element.
   4288        * 
   4289        * @attribute selectedModel
   4290        * @type Object
   4291        * @default null
   4292        */
   4293       selectedModel: null,
   4294 
   4295       /**
   4296        * In single selection, this returns the selected index.
   4297        *
   4298        * @attribute selectedIndex
   4299        * @type number
   4300        * @default -1
   4301        */
   4302       selectedIndex: -1,
   4303 
   4304       /**
   4305        * The target element that contains items.  If this is not set 
   4306        * core-selector is the container.
   4307        * 
   4308        * @attribute target
   4309        * @type Object
   4310        * @default null
   4311        */
   4312       target: null,
   4313 
   4314       /**
   4315        * This can be used to query nodes from the target node to be used for 
   4316        * selection items.  Note this only works if the 'target' property is set.
   4317        *
   4318        * Example:
   4319        *
   4320        *     <core-selector target="{{$.myForm}}" itemsSelector="input[type=radio]"></core-selector>
   4321        *     <form id="myForm">
   4322        *       <label><input type="radio" name="color" value="red"> Red</label> <br>
   4323        *       <label><input type="radio" name="color" value="green"> Green</label> <br>
   4324        *       <label><input type="radio" name="color" value="blue"> Blue</label> <br>
   4325        *       <p>color = {{color}}</p>
   4326        *     </form>
   4327        * 
   4328        * @attribute itemSelector
   4329        * @type string
   4330        * @default ''
   4331        */
   4332       itemsSelector: '',
   4333 
   4334       /**
   4335        * The event that would be fired from the item element to indicate
   4336        * it is being selected.
   4337        *
   4338        * @attribute activateEvent
   4339        * @type string
   4340        * @default 'tap'
   4341        */
   4342       activateEvent: 'tap',
   4343 
   4344       /**
   4345        * Set this to true to disallow changing the selection via the
   4346        * `activateEvent`.
   4347        *
   4348        * @attribute notap
   4349        * @type boolean
   4350        * @default false
   4351        */
   4352       notap: false,
   4353 
   4354       ready: function() {
   4355         this.activateListener = this.activateHandler.bind(this);
   4356         this.observer = new MutationObserver(this.updateSelected.bind(this));
   4357         if (!this.target) {
   4358           this.target = this;
   4359         }
   4360       },
   4361 
   4362       get items() {
   4363         if (!this.target) {
   4364           return [];
   4365         }
   4366         var nodes = this.target !== this ? (this.itemsSelector ? 
   4367             this.target.querySelectorAll(this.itemsSelector) : 
   4368                 this.target.children) : this.$.items.getDistributedNodes();
   4369         return Array.prototype.filter.call(nodes || [], function(n) {
   4370           return n && n.localName !== 'template';
   4371         });
   4372       },
   4373 
   4374       targetChanged: function(old) {
   4375         if (old) {
   4376           this.removeListener(old);
   4377           this.observer.disconnect();
   4378           this.clearSelection();
   4379         }
   4380         if (this.target) {
   4381           this.addListener(this.target);
   4382           this.observer.observe(this.target, {childList: true});
   4383           this.updateSelected();
   4384         }
   4385       },
   4386 
   4387       addListener: function(node) {
   4388         node.addEventListener(this.activateEvent, this.activateListener);
   4389       },
   4390 
   4391       removeListener: function(node) {
   4392         node.removeEventListener(this.activateEvent, this.activateListener);
   4393       },
   4394 
   4395       get selection() {
   4396         return this.$.selection.getSelection();
   4397       },
   4398 
   4399       selectedChanged: function() {
   4400         this.updateSelected();
   4401       },
   4402 
   4403       updateSelected: function() {
   4404         this.validateSelected();
   4405         if (this.multi) {
   4406           this.clearSelection();
   4407           this.selected && this.selected.forEach(function(s) {
   4408             this.valueToSelection(s);
   4409           }, this);
   4410         } else {
   4411           this.valueToSelection(this.selected);
   4412         }
   4413       },
   4414 
   4415       validateSelected: function() {
   4416         // convert to an array for multi-selection
   4417         if (this.multi && !Array.isArray(this.selected) && 
   4418             this.selected !== null && this.selected !== undefined) {
   4419           this.selected = [this.selected];
   4420         }
   4421       },
   4422 
   4423       clearSelection: function() {
   4424         if (this.multi) {
   4425           this.selection.slice().forEach(function(s) {
   4426             this.$.selection.setItemSelected(s, false);
   4427           }, this);
   4428         } else {
   4429           this.$.selection.setItemSelected(this.selection, false);
   4430         }
   4431         this.selectedItem = null;
   4432         this.$.selection.clear();
   4433       },
   4434 
   4435       valueToSelection: function(value) {
   4436         var item = (value === null || value === undefined) ? 
   4437             null : this.items[this.valueToIndex(value)];
   4438         this.$.selection.select(item);
   4439       },
   4440 
   4441       updateSelectedItem: function() {
   4442         this.selectedItem = this.selection;
   4443       },
   4444 
   4445       selectedItemChanged: function() {
   4446         if (this.selectedItem) {
   4447           var t = this.selectedItem.templateInstance;
   4448           this.selectedModel = t ? t.model : undefined;
   4449         } else {
   4450           this.selectedModel = null;
   4451         }
   4452         this.selectedIndex = this.selectedItem ? 
   4453             parseInt(this.valueToIndex(this.selected)) : -1;
   4454       },
   4455 
   4456       valueToIndex: function(value) {
   4457         // find an item with value == value and return it's index
   4458         for (var i=0, items=this.items, c; (c=items[i]); i++) {
   4459           if (this.valueForNode(c) == value) {
   4460             return i;
   4461           }
   4462         }
   4463         // if no item found, the value itself is probably the index
   4464         return value;
   4465       },
   4466 
   4467       valueForNode: function(node) {
   4468         return node[this.valueattr] || node.getAttribute(this.valueattr);
   4469       },
   4470 
   4471       // events fired from <core-selection> object
   4472       selectionSelect: function(e, detail) {
   4473         this.updateSelectedItem();
   4474         if (detail.item) {
   4475           this.applySelection(detail.item, detail.isSelected);
   4476         }
   4477       },
   4478 
   4479       applySelection: function(item, isSelected) {
   4480         if (this.selectedClass) {
   4481           item.classList.toggle(this.selectedClass, isSelected);
   4482         }
   4483         if (this.selectedProperty) {
   4484           item[this.selectedProperty] = isSelected;
   4485         }
   4486         if (this.selectedAttribute && item.setAttribute) {
   4487           if (isSelected) {
   4488             item.setAttribute(this.selectedAttribute, '');
   4489           } else {
   4490             item.removeAttribute(this.selectedAttribute);
   4491           }
   4492         }
   4493       },
   4494 
   4495       // event fired from host
   4496       activateHandler: function(e) {
   4497         if (!this.notap) {
   4498           var i = this.findDistributedTarget(e.target, this.items);
   4499           if (i >= 0) {
   4500             var item = this.items[i];
   4501             var s = this.valueForNode(item) || i;
   4502             if (this.multi) {
   4503               if (this.selected) {
   4504                 this.addRemoveSelected(s);
   4505               } else {
   4506                 this.selected = [s];
   4507               }
   4508             } else {
   4509               this.selected = s;
   4510             }
   4511             this.asyncFire('core-activate', {item: item});
   4512           }
   4513         }
   4514       },
   4515 
   4516       addRemoveSelected: function(value) {
   4517         var i = this.selected.indexOf(value);
   4518         if (i >= 0) {
   4519           this.selected.splice(i, 1);
   4520         } else {
   4521           this.selected.push(value);
   4522         }
   4523         this.valueToSelection(value);
   4524       },
   4525 
   4526       findDistributedTarget: function(target, nodes) {
   4527         // find first ancestor of target (including itself) that
   4528         // is in nodes, if any
   4529         while (target && target != this) {
   4530           var i = Array.prototype.indexOf.call(nodes, target);
   4531           if (i >= 0) {
   4532             return i;
   4533           }
   4534           target = target.parentNode;
   4535         }
   4536       }
   4537     });
   4538   ;
   4539 
   4540   Polymer('core-menu',{});
   4541 ;
   4542 
   4543 
   4544   Polymer('core-item', {
   4545     
   4546     /**
   4547      * The URL of an image for the icon.
   4548      *
   4549      * @attribute src
   4550      * @type string
   4551      * @default ''
   4552      */
   4553 
   4554     /**
   4555      * Specifies the icon from the Polymer icon set.
   4556      *
   4557      * @attribute icon
   4558      * @type string
   4559      * @default ''
   4560      */
   4561 
   4562     /**
   4563      * Specifies the label for the menu item.
   4564      *
   4565      * @attribute label
   4566      * @type string
   4567      * @default ''
   4568      */
   4569 
   4570   });
   4571 
   4572 ;
   4573 
   4574 
   4575     Polymer('core-doc-toc', {
   4576 
   4577       searchAction: function() {
   4578         this.$.searchBar.style.opacity = 1;
   4579         this.$.searchBar.style.display = '';
   4580       },
   4581 
   4582       closeSearchAction: function() {
   4583         this.$.searchBar.style.opacity = 0;
   4584         this.$.searchBar.style.display = 'none';
   4585       }
   4586 
   4587     });
   4588 
   4589   ;
   4590 
   4591 
   4592     Polymer('core-doc-viewer', {
   4593       /**
   4594        * A single file to parse for docs
   4595        *
   4596        * @attribute url
   4597        * @type String
   4598        * @default ''
   4599        */
   4600 
   4601       /**
   4602        * Class documentation extracted from the parser
   4603        *
   4604        * @property classes
   4605        * @type Array
   4606        * @default []
   4607        */
   4608       classes: [],
   4609 
   4610       /**
   4611        * Files to parse for docs
   4612        *
   4613        * @attribute sources
   4614        * @type Array
   4615        * @default []
   4616        */
   4617       sources: [],
   4618 
   4619       ready: function() {
   4620         window.addEventListener('hashchange', this.parseLocationHash.bind(this));
   4621         this.parseLocationHash();
   4622       },
   4623 
   4624       parseLocationHash: function() {
   4625         this.route = window.location.hash.slice(1);
   4626       },
   4627 
   4628       routeChanged: function() {
   4629         this.validateRoute();
   4630       },
   4631 
   4632       validateRoute: function() {
   4633         if (this.route) {
   4634           this.classes.some(function(c) {
   4635             if (c.name === this.route) {
   4636               this.data = c;
   4637               this.route = '';
   4638               return;
   4639             }
   4640           }, this);
   4641         }
   4642       },
   4643 
   4644       selectedChanged: function() { 
   4645         this.data = this.classes[this.selected];
   4646       },
   4647 
   4648       parserDataReady: function(event) {
   4649         this.assimilateData(event.target.data);
   4650       },
   4651 
   4652       assimilateData: function(data) {
   4653         this.classes = this.classes.concat(data.classes);
   4654         this.classes.sort(function(a, b) {
   4655           var na = a && a.name.toLowerCase(), nb = b && b.name.toLowerCase();
   4656           return (na < nb) ? -1 : (na == nb) ? 0 : 1;
   4657         });
   4658         if (!this.data && !this.route && this.classes.length) {
   4659           this.data = this.classes[0];
   4660         }
   4661         if (this.classes.length > 1) {
   4662           this.$.toc.style.display = 'block';
   4663         }
   4664         this.validateRoute();
   4665       }
   4666 
   4667     });
   4668 
   4669   ;
   4670 
   4671 
   4672     Polymer('core-component-page', {
   4673 
   4674       moduleName: '',
   4675       // TODO(sjmiles): needed this to force Object type for deserialization
   4676       sources: [],
   4677 
   4678       ready: function() {
   4679         this.moduleName = this.moduleName || this.findModuleName();
   4680       },
   4681 
   4682       moduleNameChanged: function() {
   4683         document.title = this.moduleName;
   4684         this.url = !this.sources.length && this.moduleName ? this.moduleName + '.html' : '';
   4685       },
   4686 
   4687       findModuleName: function() {
   4688         var path = location.pathname.split('/');
   4689         var name = path.pop() || path.pop();
   4690         if (name.indexOf('.html') >= 0) {
   4691           name = path.pop();
   4692         }
   4693         return name || '';
   4694       }
   4695 
   4696     });
   4697 
   4698