5 Polymer('core-layout', {
9 * Controls if the element lays out vertically or not.
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
22 * @type string start|center|end|between
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
32 * @type string start|center|end
37 * Controls whether or not the items layout in reverse order.
44 layoutPrefix
: 'core-',
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');
56 fetchTemplate: function() {
60 attached: function() {
61 this.installScopeStyle(this.styles
[0]);
62 if (this.children
.length
) {
63 this.isContainer
= true;
65 var container
= this.isContainer
? this : this.parentNode
;
66 // detect if laying out a shadowRoot host.
67 var forHost
= container
instanceof ShadowRoot
;
69 this.installScopeStyle(this.styles
[1], 'host');
70 container
= container
.host
|| document
.body
;
72 this.layoutContainer
= container
;
75 detached: function() {
76 this.layoutContainer
= null;
79 layoutContainerChanged: function(old
) {
80 this.style
.display
= this.layoutContainer
=== this ? null : 'none';
81 this.verticalChanged();
83 this.justifyChanged();
86 setLayoutClass: function(prefix
, old
, newValue
) {
87 if (this.layoutContainer
) {
88 prefix
= this.layoutPrefix
+ prefix
;
90 this.layoutContainer
.classList
.remove(prefix
+ old
);
93 this.layoutContainer
.classList
.add(prefix
+ newValue
);
98 verticalChanged: function(old
) {
99 old
= old
? 'v' : 'h';
100 var vertical
= this.vertical
? 'v' : 'h';
101 this.setLayoutClass('', old
, vertical
);
104 alignChanged: function(old
) {
105 this.setLayoutClass('align-', old
, this.align
);
108 justifyChanged: function(old
) {
109 this.setLayoutClass('justify-', old
, this.justify
);
112 reverseChanged: function(old
) {
113 old
= old
? 'reverse' : '';
114 var newValue
= this.reverse
? 'reverse' : '';
115 this.setLayoutClass('', old
, newValue
);
126 var SKIP_ID
= 'meta';
127 var metaData
= {}, metaArray
= {};
129 Polymer('core-meta', {
132 * The type of meta-data. All meta-data with the same type with be
144 this.register(this.id
);
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);
171 unregister: function(meta
, id
) {
172 delete this.metaData
[id
|| meta
.id
];
173 var i
= this.metaArray
.indexOf(meta
);
175 this.metaArray
.splice(i
, 1);
180 * Returns a list of all meta-data elements with the same type.
187 return this.metaArray
;
191 * Retrieves meta-data by ID.
194 * @param {String} id The ID of the meta-data to be returned.
195 * @returns Returns meta-data.
198 return this.metaData
[id
];
208 Polymer('core-iconset', {
211 * The URL of the iconset image.
220 * The width of the iconset image. This must only be specified if the
221 * icons are arranged into separate rows inside the image.
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
241 * The size of an individual icon. Note that icons must be square.
243 * @attribute iconSize
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.
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.
271 created: function() {
278 // TODO(sorvell): ensure iconset's src is always relative to the main
280 if (this.src
&& (this.ownerDocument
!== document
)) {
281 this.src
= this.resolvePath(this.src
, this.ownerDocument
.baseURI
);
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
] = {
296 if (ox
+ this.iconSize
< this.width
) {
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
315 // TODO(ffu): support retrived by index e.g. getOffset(10);
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.
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.
331 getOffset: function(icon
, theme
) {
332 var i
= this.iconMap
[icon
];
334 var n
= this.iconNames
[Number(icon
)];
337 var t
= this.themes
[theme
];
340 offsetX
: i
.offsetX
+ t
.offsetX
,
341 offsetY
: i
.offsetY
+ t
.offsetY
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.
353 * @param {Element} element The element to which the background is
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.
360 applyIcon: function(element
, icon
, scale
) {
361 var offset
= this.getOffset(icon
);
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';
378 Polymer('core-iconset-svg', {
382 * The size of an individual icon. Note that icons must be square.
384 * @attribute iconSize
391 created: function() {
400 iconById: function(id
) {
401 return this._icons
[id
] || (this._icons
[id
] = this.querySelector('#' + id
));
404 cloneIcon: function(id
) {
405 var icon
= this.iconById(id
);
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
+ ' ' +
411 // NOTE(dfreedm): work around https://crbug.com/370136
412 svg
.style
.pointerEvents
= 'none';
413 svg
.appendChild(content
);
419 if (!this._iconNames
) {
420 this._iconNames
= this.findIconNames();
422 return this._iconNames
;
425 findIconNames: function() {
426 var icons
= this.querySelectorAll('[id]').array();
428 return icons
.map(function(n
){ return n
.id
});
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.
437 * @param {Element} element The element to which the icon is
439 * @param {String|Number} icon The name the icon to apply.
441 applyIcon: function(element
, icon
, scale
) {
442 var root
= element
.shadowRoot
|| element
;
444 var old
= root
.querySelector('svg');
449 var svg
= this.cloneIcon(icon
);
453 var size
= scale
* this.iconSize
;
455 svg
.style
.height
= svg
.style
.width
= size
+ 'px';
457 svg
.setAttribute('height', '100%');
458 svg
.setAttribute('width', '100%');
459 svg
.setAttribute('preserveAspectRatio', 'xMidYMid meet');
461 svg
.style
.display
= 'block';
462 root
.insertBefore(svg
, root
.firstElementChild
);
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'
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
++) {
497 Polymer('core-icon', {
500 * The URL of an image for the icon. If the src property is specified,
501 * the icon property should not be.
510 * Specifies the size of the icon in pixel units.
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.
530 'size icon': 'updateIcon'
533 defaultIconset
: 'icons',
537 meta
= document
.createElement('core-iconset');
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';
548 getIconset: function(name
) {
549 return meta
.byId(name
|| this.defaultIconset
);
552 updateIcon: function() {
554 this.style
.width
= this.style
.height
= this.size
+ 'px';
557 var parts
= String(this.icon
).split(':');
558 var icon
= parts
.pop();
560 var set = this.getIconset(parts
.pop());
562 set.applyIcon(this, icon
, this.size
/ set.iconSize
);
574 Polymer('core-icon-button', {
577 * The URL of an image for the icon. Should not use `icon` property
578 * if you are using this property.
587 * If true, border is placed around the button to indicate it's
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
607 activeChanged: function() {
608 this.classList
.toggle('selected', this.active
);
614 Polymer('core-toolbar');;
617 Polymer('core-header-panel', {
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`.
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
630 * `seamed`: The header is presented as seamed with the panel.
632 * `waterfall`: Similar to standard mode, but header is initially presented as
633 * seamed with panel, but then separates to form the step.
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).
639 * `scroll`: The header keeps its seam with the panel, and is pushed off screen.
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.
646 * core-header-panel[mode=cover]::shadow #mainContainer {
650 * margin: 60px 60px 60px 0;
654 * <core-header-panel mode="cover">
655 * <core-appbar class="tall">
656 * <core-icon-button icon="menu"></core-icon-button>
658 * <div class="content"></div>
659 * </core-header-panel>
665 mode
: {value
: '', reflect
: true},
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"
671 * @attribute tallClass
678 * If true, the drop-shadow is always shown no matter what mode is set to.
687 domReady: function() {
688 this.async('scroll');
691 modeChanged: function() {
696 return this.$.headerContent
.getDistributedNodes()[0];
700 var shadowMode
= {'waterfall': 1, 'waterfall-tall': 1};
701 var noShadow
= {'seamed': 1, 'cover': 1, 'scroll': 1};
702 var tallMode
= {'waterfall-tall': 1};
704 var main
= this.$.mainContainer
;
705 var header
= this.header
;
707 var sTop
= main
.scrollTop
;
708 var atTop
= sTop
=== 0;
711 this.$.dropShadow
.classList
.toggle('hidden', !this.shadow
&&
712 (atTop
&& shadowMode
[this.mode
] || noShadow
[this.mode
]));
714 if (tallMode
[this.mode
]) {
715 header
.classList
.toggle(this.tallClass
, atTop
);
718 header
.classList
.toggle('animate', tallMode
[this.mode
]);
726 * marked - a markdown parser
727 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
728 * https://github.com/chjj/marked
734 * Block-Level Grammar
739 code
: /^( {4}[^\n]+\n*)+/,
741 hr
: /^( *[-*_]){3,} *(?:\n+|$)/,
742 heading
: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
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+|$)/,
750 paragraph
: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
754 block
.bullet
= /(?:[*+-]|\d+\.)/;
755 block
.item
= /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
756 block
.item
= replace(block
.item
, 'gm')
757 (/bull/g, block
.bullet
)
760 block
.list
= replace(block
.list
)
761 (/bull/g, block
.bullet
)
762 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
763 ('def', '\\n+(?=' + block
.def
.source
+ ')')
766 block
.blockquote
= replace(block
.blockquote
)
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';
775 block
.html
= replace(block
.html
)
776 ('comment', /<!--[\s\S]*?-->/)
777 ('closed', /<(tag)[\s\S]+?<\/\1>/)
778 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
782 block
.paragraph
= replace(block
.paragraph
)
784 ('heading', block
.heading
)
785 ('lheading', block
.lheading
)
786 ('blockquote', block
.blockquote
)
787 ('tag', '<' + block
._tag
)
792 * Normal Block Grammar
795 block
.normal
= merge({}, block
);
801 block
.gfm
= merge({}, block
.normal
, {
802 fences
: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
806 block
.gfm
.paragraph
= replace(block
.paragraph
)
808 + block
.gfm
.fences
.source
.replace('\\1', '\\2') + '|'
809 + block
.list
.source
.replace('\\1', '\\3') + '|')
813 * GFM + Tables Block Grammar
816 block
.tables
= merge({}, block
.gfm
, {
817 nptable
: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
818 table
: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
825 function Lexer(options
) {
827 this.tokens
.links
= {};
828 this.options
= options
|| marked
.defaults
;
829 this.rules
= block
.normal
;
831 if (this.options
.gfm
) {
832 if (this.options
.tables
) {
833 this.rules
= block
.tables
;
835 this.rules
= block
.gfm
;
850 Lexer
.lex = function(src
, options
) {
851 var lexer
= new Lexer(options
);
852 return lexer
.lex(src
);
859 Lexer
.prototype.lex = function(src
) {
861 .replace(/\r\n|\r/g, '\n')
863 .replace(/\u00a0/g, ' ')
864 .replace(/\u2424/g, '\n');
866 return this.token(src
, true);
873 Lexer
.prototype.token = function(src
, top
, bq
) {
874 var src
= src
.replace(/^ +$/gm, '')
887 if (cap
= this.rules
.newline
.exec(src
)) {
888 src
= src
.substring(cap
[0].length
);
889 if (cap
[0].length
> 1) {
897 if (cap
= this.rules
.code
.exec(src
)) {
898 src
= src
.substring(cap
[0].length
);
899 cap
= cap
[0].replace(/^ {4}/gm, '');
902 text
: !this.options
.pedantic
903 ? cap
.replace(/\n+$/, '')
910 if (cap
= this.rules
.fences
.exec(src
)) {
911 src
= src
.substring(cap
[0].length
);
921 if (cap
= this.rules
.heading
.exec(src
)) {
922 src
= src
.substring(cap
[0].length
);
925 depth
: cap
[1].length
,
931 // table no leading pipe (gfm)
932 if (top
&& (cap
= this.rules
.nptable
.exec(src
))) {
933 src
= src
.substring(cap
[0].length
);
937 header
: cap
[1].replace(/^ *| *\| *$/g, '').split(/ *\| */
),
938 align
: cap
[2].replace(/^ *|\| *$/g, '').split(/ *\| */
),
939 cells
: cap
[3].replace(/\n$/, '').split('\n')
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';
950 item
.align
[i
] = null;
954 for (i
= 0; i
< item
.cells
.length
; i
++) {
955 item
.cells
[i
] = item
.cells
[i
].split(/ *\| */
);
958 this.tokens
.push(item
);
964 if (cap
= this.rules
.lheading
.exec(src
)) {
965 src
= src
.substring(cap
[0].length
);
968 depth
: cap
[2] === '=' ? 1 : 2,
975 if (cap
= this.rules
.hr
.exec(src
)) {
976 src
= src
.substring(cap
[0].length
);
984 if (cap
= this.rules
.blockquote
.exec(src
)) {
985 src
= src
.substring(cap
[0].length
);
988 type
: 'blockquote_start'
991 cap
= cap
[0].replace(/^ *> ?/gm, '');
993 // Pass `top` to keep the current
994 // "toplevel" state. This is exactly
995 // how markdown.pl works.
996 this.token(cap
, top
, true);
999 type
: 'blockquote_end'
1006 if (cap
= this.rules
.list
.exec(src
)) {
1007 src
= src
.substring(cap
[0].length
);
1012 ordered
: bull
.length
> 1
1015 // Get each top-level item.
1016 cap
= cap
[0].match(this.rules
.item
);
1022 for (; i
< l
; i
++) {
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+\.) +/, '');
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, '');
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
;
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
);
1054 next
= item
.charAt(item
.length
- 1) === '\n';
1055 if (!loose
) loose
= next
;
1060 ? 'loose_item_start'
1065 this.token(item
, false, bq
);
1068 type
: 'list_item_end'
1080 if (cap
= this.rules
.html
.exec(src
)) {
1081 src
= src
.substring(cap
[0].length
);
1083 type
: this.options
.sanitize
1086 pre
: cap
[1] === 'pre' || cap
[1] === 'script' || cap
[1] === 'style',
1093 if ((!bq
&& top
) && (cap
= this.rules
.def
.exec(src
))) {
1094 src
= src
.substring(cap
[0].length
);
1095 this.tokens
.links
[cap
[1].toLowerCase()] = {
1103 if (top
&& (cap
= this.rules
.table
.exec(src
))) {
1104 src
= src
.substring(cap
[0].length
);
1108 header
: cap
[1].replace(/^ *| *\| *$/g, '').split(/ *\| */
),
1109 align
: cap
[2].replace(/^ *|\| *$/g, '').split(/ *\| */
),
1110 cells
: cap
[3].replace(/(?: *\| *)?\n$/, '').split('\n')
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';
1121 item
.align
[i
] = null;
1125 for (i
= 0; i
< item
.cells
.length
; i
++) {
1126 item
.cells
[i
] = item
.cells
[i
]
1127 .replace(/^ *\| *| *\| *$/g, '')
1131 this.tokens
.push(item
);
1136 // top-level paragraph
1137 if (top
&& (cap
= this.rules
.paragraph
.exec(src
))) {
1138 src
= src
.substring(cap
[0].length
);
1141 text
: cap
[1].charAt(cap
[1].length
- 1) === '\n'
1142 ? cap
[1].slice(0, -1)
1149 if (cap
= this.rules
.text
.exec(src
)) {
1150 // Top-level should never reach here.
1151 src
= src
.substring(cap
[0].length
);
1161 Error('Infinite loop on byte: ' + src
.charCodeAt(0));
1169 * Inline-Level Grammar
1173 escape
: /^\\([\\`*{}\[\]()#+\-.!_>])/,
1174 autolink
: /^<([^ >]+(@|:\/)[^ >]+)>/,
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*$)/,
1185 text
: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
1188 inline
._inside
= /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
1189 inline
._href
= /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
1191 inline
.link
= replace(inline
.link
)
1192 ('inside', inline
._inside
)
1193 ('href', inline
._href
)
1196 inline
.reflink
= replace(inline
.reflink
)
1197 ('inside', inline
._inside
)
1201 * Normal Inline Grammar
1204 inline
.normal
= merge({}, inline
);
1207 * Pedantic Inline Grammar
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)\*(?!\*)/
1216 * GFM Inline Grammar
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
)
1225 ('|', '|https?://|')
1230 * GFM + Line Breaks Inline Grammar
1233 inline
.breaks
= merge({}, inline
.gfm
, {
1234 br
: replace(inline
.br
)('{2,}', '*')(),
1235 text
: replace(inline
.gfm
.text
)('{2,}', '*')()
1239 * Inline Lexer & Compiler
1242 function InlineLexer(links
, options
) {
1243 this.options
= options
|| marked
.defaults
;
1245 this.rules
= inline
.normal
;
1246 this.renderer
= this.options
.renderer
|| new Renderer
;
1247 this.renderer
.options
= this.options
;
1251 Error('Tokens array requires a `links` property.');
1254 if (this.options
.gfm
) {
1255 if (this.options
.breaks
) {
1256 this.rules
= inline
.breaks
;
1258 this.rules
= inline
.gfm
;
1260 } else if (this.options
.pedantic
) {
1261 this.rules
= inline
.pedantic
;
1266 * Expose Inline Rules
1269 InlineLexer
.rules
= inline
;
1272 * Static Lexing/Compiling Method
1275 InlineLexer
.output = function(src
, links
, options
) {
1276 var inline
= new InlineLexer(links
, options
);
1277 return inline
.output(src
);
1284 InlineLexer
.prototype.output = function(src
) {
1293 if (cap
= this.rules
.escape
.exec(src
)) {
1294 src
= src
.substring(cap
[0].length
);
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
;
1308 text
= escape(cap
[1]);
1311 out
+= this.renderer
.link(href
, null, text
);
1316 if (!this.inLink
&& (cap
= this.rules
.url
.exec(src
))) {
1317 src
= src
.substring(cap
[0].length
);
1318 text
= escape(cap
[1]);
1320 out
+= this.renderer
.link(href
, null, text
);
1325 if (cap
= this.rules
.tag
.exec(src
)) {
1326 if (!this.inLink
&& /^<a /i.test(cap
[0])) {
1328 } else if (this.inLink
&& /^<\/a>/i.test(cap
[0])) {
1329 this.inLink
= false;
1331 src
= src
.substring(cap
[0].length
);
1332 out
+= this.options
.sanitize
1339 if (cap
= this.rules
.link
.exec(src
)) {
1340 src
= src
.substring(cap
[0].length
);
1342 out
+= this.outputLink(cap
, {
1346 this.inLink
= false;
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
;
1362 out
+= this.outputLink(cap
, link
);
1363 this.inLink
= false;
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]));
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]));
1382 if (cap
= this.rules
.code
.exec(src
)) {
1383 src
= src
.substring(cap
[0].length
);
1384 out
+= this.renderer
.codespan(escape(cap
[2], true));
1389 if (cap
= this.rules
.br
.exec(src
)) {
1390 src
= src
.substring(cap
[0].length
);
1391 out
+= this.renderer
.br();
1396 if (cap
= this.rules
.del
.exec(src
)) {
1397 src
= src
.substring(cap
[0].length
);
1398 out
+= this.renderer
.del(this.output(cap
[1]));
1403 if (cap
= this.rules
.text
.exec(src
)) {
1404 src
= src
.substring(cap
[0].length
);
1405 out
+= escape(this.smartypants(cap
[0]));
1411 Error('Infinite loop on byte: ' + src
.charCodeAt(0));
1422 InlineLexer
.prototype.outputLink = function(cap
, link
) {
1423 var href
= escape(link
.href
)
1424 , title
= link
.title
? escape(link
.title
) : null;
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]));
1432 * Smartypants Transformations
1435 InlineLexer
.prototype.smartypants = function(text
) {
1436 if (!this.options
.smartypants
) return text
;
1439 .replace(/--/g, '\u2014')
1441 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
1442 // closing singles & apostrophes
1443 .replace(/'/g, '\u2019')
1445 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g
, '$1\u201c')
1447 .replace(/"/g, '\u201d')
1449 .replace(/\.{3}/g, '\u2026');
1456 InlineLexer.prototype.mangle = function(text) {
1462 for (; i < l; i++) {
1463 ch = text.charCodeAt(i);
1464 if (Math.random() > 0.5) {
1465 ch = 'x' + ch.toString(16);
1467 out += '&#' + ch + ';';
1477 function Renderer(options) {
1478 this.options = options || {};
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) {
1491 return '<pre><code>'
1492 + (escaped ? code : escape(code, true))
1493 + '\n</code></pre>';
1496 return '<pre><code class="'
1497 + this.options.langPrefix
1498 + escape(lang, true)
1500 + (escaped ? code : escape(code, true))
1501 + '\n</code></pre>\n';
1504 Renderer.prototype.blockquote = function(quote) {
1505 return '<blockquote>\n' + quote + '</blockquote>\n';
1508 Renderer.prototype.html = function(html) {
1512 Renderer.prototype.heading = function(text, level, raw) {
1516 + this.options.headerPrefix
1517 + raw.toLowerCase().replace(/[^\w]+/g, '-')
1525 Renderer.prototype.hr = function() {
1526 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
1529 Renderer.prototype.list = function(body, ordered) {
1530 var type = ordered ? 'ol' : 'ul';
1531 return '<' + type + '>\n' + body + '</' + type + '>\n';
1534 Renderer.prototype.listitem = function(text) {
1535 return '<li>' + text + '</li>\n';
1538 Renderer.prototype.paragraph = function(text) {
1539 return '<p>' + text + '</p>\n';
1542 Renderer.prototype.table = function(header, body) {
1553 Renderer.prototype.tablerow = function(content) {
1554 return '<tr>\n' + content + '</tr>\n';
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 + '">'
1562 return tag + content + '</' + type + '>\n';
1565 // span level renderer
1566 Renderer.prototype.strong = function(text) {
1567 return '<strong>' + text + '</strong>';
1570 Renderer.prototype.em = function(text) {
1571 return '<em>' + text + '</em>';
1574 Renderer.prototype.codespan = function(text) {
1575 return '<code>' + text + '</code>';
1578 Renderer.prototype.br = function() {
1579 return this.options.xhtml ? '<br/>' : '<br>';
1582 Renderer.prototype.del = function(text) {
1583 return '<del>' + text + '</del>';
1586 Renderer.prototype.link = function(href, title, text) {
1587 if (this.options.sanitize) {
1589 var prot = decodeURIComponent(unescape(href))
1590 .replace(/[^\w:]/g, '')
1595 if (prot.indexOf('javascript:') === 0) {
1599 var out = '<a href="' + href + '"';
1601 out += ' title="' + title + '"';
1603 out += '>' + text + '</a>';
1607 Renderer.prototype.image = function(href, title, text) {
1608 var out = '<img src="' + href + '" alt="' + text + '"';
1610 out += ' title="' + title + '"';
1612 out += this.options.xhtml ? '/>' : '>';
1617 * Parsing & Compiling
1620 function Parser(options) {
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;
1630 * Static Parse Method
1633 Parser.parse = function(src, options, renderer) {
1634 var parser = new Parser(options, renderer);
1635 return parser.parse(src);
1642 Parser.prototype.parse = function(src) {
1643 this.inline = new InlineLexer(src.links, this.options, this.renderer);
1644 this.tokens = src.reverse();
1647 while (this.next()) {
1658 Parser.prototype.next = function() {
1659 return this.token = this.tokens.pop();
1663 * Preview Next Token
1666 Parser.prototype.peek = function() {
1667 return this.tokens[this.tokens.length - 1] || 0;
1674 Parser.prototype.parseText = function() {
1675 var body = this.token.text;
1677 while (this.peek().type === 'text') {
1678 body += '\n' + this.next().text;
1681 return this.inline.output(body);
1685 * Parse Current Token
1688 Parser.prototype.tok = function() {
1689 switch (this.token.type) {
1694 return this.renderer.hr();
1697 return this.renderer.heading(
1698 this.inline.output(this.token.text),
1703 return this.renderer.code(this.token.text,
1705 this.token.escaped);
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] }
1725 header += this.renderer.tablerow(cell);
1727 for (i = 0; i < this.token.cells.length; i++) {
1728 row = this.token.cells[i];
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] }
1738 body += this.renderer.tablerow(cell);
1740 return this.renderer.table(header, body);
1742 case 'blockquote_start': {
1745 while (this.next().type !== 'blockquote_end') {
1749 return this.renderer.blockquote(body);
1751 case 'list_start': {
1753 , ordered = this.token.ordered;
1755 while (this.next().type !== 'list_end') {
1759 return this.renderer.list(body, ordered);
1761 case 'list_item_start': {
1764 while (this.next().type !== 'list_item_end') {
1765 body += this.token.type === 'text'
1770 return this.renderer.listitem(body);
1772 case 'loose_item_start': {
1775 while (this.next().type !== 'list_item_end') {
1779 return this.renderer.listitem(body);
1782 var html = !this.token.pre && !this.options.pedantic
1783 ? this.inline.output(this.token.text)
1785 return this.renderer.html(html);
1788 return this.renderer.paragraph(this.inline.output(this.token.text));
1791 return this.renderer.paragraph(this.parseText());
1800 function escape(html, encode) {
1802 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
1803 .replace(/</g, '<')
1804 .replace(/>/g, '>')
1805 .replace(/"/g
, '"')
1806 .replace(/'/g, ''');
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));
1822 function replace(regex, opt) {
1823 regex = regex.source;
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);
1837 function merge(obj) {
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];
1859 function marked(src, opt, callback) {
1860 if (callback || typeof opt === 'function') {
1866 opt = merge({}, marked.defaults, opt || {});
1868 var highlight = opt.highlight
1874 tokens = Lexer.lex(src, opt)
1879 pending = tokens.length;
1881 var done = function() {
1885 out = Parser.parse(tokens, opt);
1890 opt.highlight = highlight;
1894 : callback(null, out);
1897 if (!highlight || highlight.length < 3) {
1901 delete opt.highlight;
1903 if (!pending) return done();
1905 for (; i < tokens.length; i++) {
1907 if (token.type !== 'code
') {
1908 return --pending || done();
1910 return highlight(token.text, token.lang, function(err, code) {
1911 if (code == null || code === token.text) {
1912 return --pending || done();
1915 token.escaped = true;
1916 --pending || done();
1924 if (opt) opt = merge({}, marked.defaults, opt);
1925 return Parser.parse(Lexer.lex(src, opt), opt);
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)
1942 marked
.setOptions = function(opt
) {
1943 merge(marked
.defaults
, opt
);
1956 langPrefix
: 'lang-',
1959 renderer
: new Renderer
,
1967 marked
.Parser
= Parser
;
1968 marked
.parser
= Parser
.parse
;
1970 marked
.Renderer
= Renderer
;
1972 marked
.Lexer
= Lexer
;
1973 marked
.lexer
= Lexer
.lex
;
1975 marked
.InlineLexer
= InlineLexer
;
1976 marked
.inlineLexer
= InlineLexer
.output
;
1978 marked
.parse
= marked
;
1980 if (typeof exports
=== 'object') {
1981 module
.exports
= marked
;
1982 } else if (typeof define
=== 'function' && define
.amd
) {
1983 define(function() { return marked
; });
1985 this.marked
= marked
;
1988 }).call(function() {
1989 return this || (typeof window
!== 'undefined' ? window
: global
);
1994 Polymer('marked-element', {
1998 attached: function() {
2000 highlight
: this.highlight
.bind(this)
2003 this.text
= this.innerHTML
;
2007 textChanged: function () {
2008 this.innerHTML
= marked(this.text
);
2011 highlight: function(code
, lang
) {
2012 var event
= this.fire('marked-js-highlight', {code
: code
, lang
: lang
});
2013 return event
.detail
.code
|| code
;
2019 // Copyright (C) 2006 Google Inc.
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
2025 // http://www.apache.org/licenses/LICENSE-2.0
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.
2036 * some functions for browser-side pretty printing of code contained in html.
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.
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.
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.
2066 * cbeust, 2006/08/22
2068 * Java annotations (start with "@") are now captured as literals ("lit")
2073 // JSLint declarations
2074 /*global console, document, navigator, setTimeout, window, define */
2077 * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
2079 * If set to {@code false}, {@code prettyPrint()} is synchronous.
2081 window
['PR_SHOULD_USE_CONTINUATION'] = true;
2084 * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
2085 * {@code class=prettyprint} and prettify them.
2087 * @param {Function?} opt_whenDone if specified, called when the last entry
2088 * has been finished.
2092 * Pretty print a chunk of code.
2094 * @param {string} sourceCodeHtml code as html
2095 * @return {string} code as html, but prettier
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," +
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," +
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," +
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," +
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/;
2150 // token style names. correspond to css classes
2152 * token style for a string literal
2155 var PR_STRING
= 'str';
2157 * token style for a keyword
2160 var PR_KEYWORD
= 'kwd';
2162 * token style for a comment
2165 var PR_COMMENT
= 'com';
2167 * token style for a type
2170 var PR_TYPE
= 'typ';
2172 * token style for a literal value. e.g. 1, null, true.
2175 var PR_LITERAL
= 'lit';
2177 * token style for a punctuation string.
2180 var PR_PUNCTUATION
= 'pun';
2182 * token style for plain text.
2185 var PR_PLAIN
= 'pln';
2188 * token style for an sgml tag.
2193 * token style for a markup declaration such as a DOCTYPE.
2196 var PR_DECLARATION
= 'dec';
2198 * token style for embedded source.
2201 var PR_SOURCE
= 'src';
2203 * token style for an sgml attribute name.
2206 var PR_ATTRIB_NAME
= 'atn';
2208 * token style for an sgml attribute value.
2211 var PR_ATTRIB_VALUE
= 'atv';
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.
2218 var PR_NOCODE
= 'nocode';
2223 * A set of tokens that can precede a regular expression literal in
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.
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.
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.
2241 var REGEXP_PRECEDER_PATTERN
= '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
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.
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.
2258 function combinePrefixPatterns(regexs
) {
2259 var capturedGroupIndex
= 0;
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
) {
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;
2275 var escapeCharToCodeUnit
= {
2284 function decodeEscape(charsetPart
) {
2285 var cc0
= charsetPart
.charCodeAt(0);
2286 if (cc0
!== 92 /* \\ */) {
2289 var c1
= charsetPart
.charAt(1);
2290 cc0
= escapeCharToCodeUnit
[c1
];
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);
2298 return charsetPart
.charCodeAt(1);
2302 function encodeEscape(charCode
) {
2303 if (charCode
< 0x20) {
2304 return (charCode
< 0x10 ? '\\x0' : '\\x') + charCode
.toString(16);
2306 var ch
= String
.fromCharCode(charCode
);
2307 return (ch
=== '\\' || ch
=== '-' || ch
=== ']' || ch
=== '^')
2311 function caseFoldCharset(charSet
) {
2312 var charsetParts
= charSet
.substring(1, charSet
.length
- 1).match(
2314 '\\\\u[0-9A-Fa-f]{4}'
2315 + '|\\\\x[0-9A-Fa-f]{2}'
2316 + '|\\\\[0-3][0-7]{0,2}'
2323 var inverse
= charsetParts
[0] === '^';
2326 if (inverse
) { out
.push('^'); }
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.
2333 var start
= decodeEscape(p
);
2335 if (i
+ 2 < n
&& '-' === charsetParts
[i
+ 1]) {
2336 end
= decodeEscape(charsetParts
[i
+ 2]);
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]);
2350 if (!(end
< 97 || start
> 122)) {
2351 ranges
.push([Math
.max(97, start
) & ~32, Math
.min(end
, 122) & ~32]);
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
= [];
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]);
2367 consolidatedRanges
.push(lastRange
= range
);
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]));
2380 return out
.join('');
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(
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
2400 var n
= parts
.length
;
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
= [];
2407 // Walk over and identify back references to build the capturedGroups
2409 for (var i
= 0, groupIndex
= 0; i
< n
; ++i
) {
2412 // groups are 1-indexed, so max group index is count of '('
2414 } else if ('\\' === p
.charAt(0)) {
2415 var decimalValue
= +p
.substring(1);
2417 if (decimalValue
<= groupIndex
) {
2418 capturedGroups
[decimalValue
] = -1;
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
);
2429 // Renumber groups and reduce capturing groups to non-capturing groups
2431 for (var i
= 1; i
< capturedGroups
.length
; ++i
) {
2432 if (-1 === capturedGroups
[i
]) {
2433 capturedGroups
[i
] = ++capturedGroupIndex
;
2436 for (var i
= 0, groupIndex
= 0; i
< n
; ++i
) {
2440 if (!capturedGroups
[groupIndex
]) {
2443 } else if ('\\' === p
.charAt(0)) {
2444 var decimalValue
= +p
.substring(1);
2445 if (decimalValue
&& decimalValue
<= groupIndex
) {
2446 parts
[i
] = '\\' + capturedGroups
[decimalValue
];
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
] = ''; }
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
) {
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(
2470 var cc
= ch
.charCodeAt(0);
2471 return '[' + String
.fromCharCode(cc
& ~32, cc
| 32) + ']';
2477 return parts
.join('');
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
); }
2485 '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex
) + ')');
2488 return new RegExp(rewritten
.join('|'), ignoreCase
? 'gi' : 'g');
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.
2497 * The HTML DOM structure:</p>
2501 * (Text "print ")) ; #1
2502 * (Text "'Hello '") ; #2
2503 * (Element "br") ; #3
2504 * (Text " + 'World';")) ; #4
2507 * corresponds to the HTML
2508 * {@code <p><b>print </b>'Hello '<br> + 'World';</p>}.</p>
2511 * It will produce the output:</p>
2514 * sourceCode: "print 'Hello '\n + 'World';",
2516 * // 012345678901234 5678901234567
2517 * spans: [0, #1, 6, #2, 14, #3, 15, #4]
2521 * where #1 is a reference to the {@code "print "} text node above, and so
2522 * on for the other text nodes.
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.
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.
2537 function extractSourceSpans(node
, isPreformatted
) {
2538 var nocode
= /(?:^|\s)nocode(?:\s|$)/;
2545 function walk(node
) {
2546 switch (node
.nodeType
) {
2548 if (nocode
.test(node
.className
)) { return; }
2549 for (var child
= node
.firstChild
; child
; child
= child
.nextSibling
) {
2552 var nodeName
= node
.nodeName
.toLowerCase();
2553 if ('br' === nodeName
|| 'li' === nodeName
) {
2555 spans
[k
<< 1] = length
++;
2556 spans
[(k
++ << 1) | 1] = node
;
2559 case 3: case 4: // Text
2560 var text
= node
.nodeValue
;
2562 if (!isPreformatted
) {
2563 text
= text
.replace(/[ \t\r\n]+/g, ' ');
2565 text
= text
.replace(/\r\n?/g, '\n'); // Normalize newlines.
2567 // TODO: handle tabs here?
2569 spans
[k
<< 1] = length
;
2570 length
+= text
.length
;
2571 spans
[(k
++ << 1) | 1] = node
;
2580 sourceCode
: chunks
.join('').replace(/\n$/, ''),
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.
2592 function appendDecorations(basePos
, sourceCode
, langHandler
, out
) {
2593 if (!sourceCode
) { return; }
2595 sourceCode
: sourceCode
,
2599 out
.push
.apply(out
, job
.decorations
);
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.
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.
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
)
2625 return wrapper
=== element
? undefined : wrapper
;
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].
2636 * The stylePatterns is a list whose elements have the form
2637 * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
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.
2657 * Pattern must only match prefixes, and if it matches a prefix, then that
2658 * match is considered a token with the same style.
2660 * Context is applied to the last non-whitespace, non-comment token
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.
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.
2671 * @return {function (Object)} a
2672 * function that takes source code and returns a list of decorations.
2674 function createSimpleLexer(shortcutStylePatterns
, fallthroughStylePatterns
) {
2678 var allPatterns
= shortcutStylePatterns
.concat(fallthroughStylePatterns
);
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
;
2689 var regex
= patternParts
[1];
2691 if (!regexKeys
.hasOwnProperty(k
)) {
2692 allRegexs
.push(regex
);
2693 regexKeys
[k
] = null;
2696 allRegexs
.push(/[\0-\uffff]/);
2697 tokenizer
= combinePrefixPatterns(allRegexs
);
2700 var nPatterns
= fallthroughStylePatterns
.length
;
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.
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
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
2718 * @type {Array.<number|string>}
2720 var decorations
= [basePos
, PR_PLAIN
];
2721 var pos
= 0; // index into sourceCode
2722 var tokens
= sourceCode
.match(tokenizer
) || [];
2723 var styleCache
= {};
2725 for (var ti
= 0, nTokens
= tokens
.length
; ti
< nTokens
; ++ti
) {
2726 var token
= tokens
[ti
];
2727 var style
= styleCache
[token
];
2731 if (typeof style
=== 'string') {
2734 var patternParts
= shortcuts
[token
.charAt(0)];
2736 match
= token
.match(patternParts
[1]);
2737 style
= patternParts
[0];
2739 for (var i
= 0; i
< nPatterns
; ++i
) {
2740 patternParts
= fallthroughStylePatterns
[i
];
2741 match
= token
.match(patternParts
[1]);
2743 style
= patternParts
[0];
2748 if (!match
) { // make sure that we make progress
2753 isEmbedded
= style
.length
>= 5 && 'lang-' === style
.substring(0, 5);
2754 if (isEmbedded
&& !(match
&& typeof match
[1] === 'string')) {
2759 if (!isEmbedded
) { styleCache
[token
] = style
; }
2762 var tokenStart
= pos
;
2763 pos
+= token
.length
;
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
;
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
;
2778 var lang
= style
.substring(5);
2779 // Decorate the left of the embedded source
2781 basePos
+ tokenStart
,
2782 token
.substring(0, embeddedSourceStart
),
2783 decorate
, decorations
);
2784 // Decorate the embedded source
2786 basePos
+ tokenStart
+ embeddedSourceStart
,
2788 langHandlerForExtension(lang
, embeddedSource
),
2790 // Decorate the right of the embedded section
2792 basePos
+ tokenStart
+ embeddedSourceEnd
,
2793 token
.substring(embeddedSourceEnd
),
2794 decorate
, decorations
);
2797 job
.decorations
= decorations
;
2802 /** returns a function that produces a list of decorations from source text.
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.
2811 * It recognizes C, C++, and shell style comments.
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.
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])*(?:\"|$))/,
2824 } else if (options
['multiLineStrings']) {
2825 // 'multi-line-string', "multi-line-string"
2826 shortcutStylePatterns
.push(
2827 [PR_STRING
, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
2830 // 'single-line-string', "single-line-string"
2831 shortcutStylePatterns
.push(
2833 /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
2836 if (options
['verbatimStrings']) {
2837 // verbatim-string-literal production from the C# grammar. See issue 93.
2838 fallthroughStylePatterns
.push(
2839 [PR_STRING
, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
2841 var hc
= options
['hashComments'];
2843 if (options
['cStyleComments']) {
2844 if (hc
> 1) { // multiline hash comments
2845 shortcutStylePatterns
.push(
2846 [PR_COMMENT
, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
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]*)/,
2853 // #include <stdio.h>
2854 fallthroughStylePatterns
.push(
2856 /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
2859 shortcutStylePatterns
.push([PR_COMMENT
, /^#[^\r\n]*/, null, '#']);
2862 if (options
['cStyleComments']) {
2863 fallthroughStylePatterns
.push([PR_COMMENT
, /^\/\/[^\r\n]*/, null]);
2864 fallthroughStylePatterns
.push(
2865 [PR_COMMENT
, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
2867 if (options
['regexLiterals']) {
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
2876 // and then contains any number of raw characters,
2877 + '(?:[^/\\x5B\\x5C]'
2878 // escape sequences (\x5C),
2880 // or non-nesting character sets (\x5B\x5D);
2881 + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
2882 // finally closed by a /.
2884 fallthroughStylePatterns
.push(
2886 new RegExp('^' + REGEXP_PRECEDER_PATTERN
+ '(' + REGEX_LITERAL
+ ')')
2890 var types
= options
['types'];
2892 fallthroughStylePatterns
.push([PR_TYPE
, types
]);
2895 var keywords
= ("" + options
['keywords']).replace(/^ | $/g, '');
2896 if (keywords
.length
) {
2897 fallthroughStylePatterns
.push(
2899 new RegExp('^(?:' + keywords
.replace(/[\s,]+/g, '|') + ')\\b'),
2903 shortcutStylePatterns
.push([PR_PLAIN
, /^\s+/, null, ' \r\n\t\xA0']);
2906 // The Bash man page says
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: { }
2914 // A word beginning with # causes that word and all remaining
2915 // characters on that line to be ignored.
2917 // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
2918 // comment but empirically
2926 // so /(?:^|[|&;<>\s])/ is more appropriate.
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.
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\.$@\'\"\`\/\\]*/;
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],
2953 // or an octal or decimal number,
2954 + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
2955 // possibly in scientific notation
2956 + '(?:e[+\\-]?\\d+)?'
2958 // with an optional modifier like UL for unsigned long
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]);
2965 return createSimpleLexer(shortcutStylePatterns
, fallthroughStylePatterns
);
2968 var decorateSource
= sourceDecorator({
2969 'keywords': ALL_KEYWORDS
,
2970 'hashComments': true,
2971 'cStyleComments': true,
2972 'multiLineStrings': true,
2973 'regexLiterals': true
2977 * Given a DOM subtree, wraps it in a list, and puts each line into its own
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.
2987 function numberLines(node
, opt_startLineNum
, isPreformatted
) {
2988 var nocode
= /(?:^|\s)nocode(?:\s|$)/;
2989 var lineBreak
= /\r\n?|\n/;
2991 var document
= node
.ownerDocument
;
2993 var li
= document
.createElement('li');
2994 while (node
.firstChild
) {
2995 li
.appendChild(node
.firstChild
);
2997 // An array of lines. We split below, so this is initialized to one
2999 var listItems
= [li
];
3001 function walk(node
) {
3002 switch (node
.nodeType
) {
3004 if (nocode
.test(node
.className
)) { break; }
3005 if ('br' === node
.nodeName
) {
3007 // Discard the <BR> since it is now flush against a </LI>.
3008 if (node
.parentNode
) {
3009 node
.parentNode
.removeChild(node
);
3012 for (var child
= node
.firstChild
; child
; child
= child
.nextSibling
) {
3017 case 3: case 4: // Text
3018 if (isPreformatted
) {
3019 var text
= node
.nodeValue
;
3020 var match
= text
.match(lineBreak
);
3022 var firstLine
= text
.substring(0, match
.index
);
3023 node
.nodeValue
= firstLine
;
3024 var tail
= text
.substring(match
.index
+ match
[0].length
);
3026 var parent
= node
.parentNode
;
3027 parent
.insertBefore(
3028 document
.createTextNode(tail
), node
.nextSibling
);
3032 // Don't leave blank text nodes in the DOM.
3033 node
.parentNode
.removeChild(node
);
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; }
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
;
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
);
3073 var copiedListItem
= breakLeftOf(lineEndNode
.nextSibling
, 0);
3075 // Walk the parent chain until we reach an unattached LI.
3077 // Check nodeType since IE invents document fragments.
3078 (parent
= copiedListItem
.parentNode
) && parent
.nodeType
=== 1;) {
3079 copiedListItem
= parent
;
3081 // Put it on the list of lines for later processing.
3082 listItems
.push(copiedListItem
);
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.
3092 // Make sure numeric indices show correctly.
3093 if (opt_startLineNum
=== (opt_startLineNum
|0)) {
3094 listItems
[0].setAttribute('value', opt_startLineNum
);
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
) {
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'));
3112 node
.appendChild(ol
);
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
3123 * decorations: {Array.<number|string} an array of style classes preceded
3124 * by the position at which they start in job.sourceCode in order
3128 function recombineTagsAndDecorations(job
) {
3129 var isIE8OrEarlier
= /\bMSIE\s(\d+)/.exec(navigator
.userAgent
);
3130 isIE8OrEarlier
= isIE8OrEarlier
&& +isIE8OrEarlier
[1] <= 8;
3131 var newlineRe
= /\n/g;
3133 var source
= job
.sourceCode
;
3134 var sourceLength
= source
.length
;
3135 // Index into source after the last code-unit recombined.
3136 var sourceIndex
= 0;
3138 var spans
= job
.spans
;
3139 var nSpans
= spans
.length
;
3140 // Index into spans after the last span which ends at or before sourceIndex.
3143 var decorations
= job
.decorations
;
3144 var nDecorations
= decorations
.length
;
3145 // Index into decorations after the last decoration which ends at or before
3147 var decorationIndex
= 0;
3149 // Remove all zero-length decorations.
3150 decorations
[nDecorations
] = sourceLength
;
3152 for (i
= decPos
= 0; i
< nDecorations
;) {
3153 if (decorations
[i
] !== decorations
[i
+ 2]) {
3154 decorations
[decPos
++] = decorations
[i
++];
3155 decorations
[decPos
++] = decorations
[i
++];
3160 nDecorations
= decPos
;
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];
3168 while (end
+ 2 <= nDecorations
&& decorations
[end
+ 1] === startDec
) {
3171 decorations
[decPos
++] = startPos
;
3172 decorations
[decPos
++] = startDec
;
3176 nDecorations
= decorations
.length
= decPos
;
3178 var sourceNode
= job
.sourceNode
;
3181 oldDisplay
= sourceNode
.style
.display
;
3182 sourceNode
.style
.display
= 'none';
3185 var decoration
= null;
3186 while (spanIndex
< nSpans
) {
3187 var spanStart
= spans
[spanIndex
];
3188 var spanEnd
= spans
[spanIndex
+ 2] || sourceLength
;
3190 var decEnd
= decorations
[decorationIndex
+ 2] || sourceLength
;
3192 var end
= Math
.min(spanEnd
, decEnd
);
3194 var textNode
= spans
[spanIndex
+ 1];
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');
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
);
3224 if (sourceIndex
>= spanEnd
) {
3227 if (sourceIndex
>= decEnd
) {
3228 decorationIndex
+= 2;
3233 sourceNode
.style
.display
= oldDisplay
;
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
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.
3255 * @param {Array.<string>} fileExtensions
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
);
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
)
3275 return langHandlerRegistry
[extension
];
3277 registerLangHandler(decorateSource
, ['default-code']);
3278 registerLangHandler(
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]
3296 ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
3297 registerLangHandler(
3300 [PR_PLAIN
, /^[\s]+/, null, ' \t\r\n'],
3301 [PR_ATTRIB_VALUE
, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
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]
3316 registerLangHandler(
3317 createSimpleLexer([], [[PR_ATTRIB_VALUE
, /^[\s\S]+/]]), ['uq.val']);
3318 registerLangHandler(sourceDecorator({
3319 'keywords': CPP_KEYWORDS
,
3320 'hashComments': true,
3321 'cStyleComments': true,
3323 }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
3324 registerLangHandler(sourceDecorator({
3325 'keywords': 'null,true,false'
3327 registerLangHandler(sourceDecorator({
3328 'keywords': CSHARP_KEYWORDS
,
3329 'hashComments': true,
3330 'cStyleComments': true,
3331 'verbatimStrings': true,
3334 registerLangHandler(sourceDecorator({
3335 'keywords': JAVA_KEYWORDS
,
3336 'cStyleComments': true
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
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
3361 registerLangHandler(sourceDecorator({
3362 'keywords': JSCRIPT_KEYWORDS
,
3363 'cStyleComments': true,
3364 'regexLiterals': true
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
3374 registerLangHandler(
3375 createSimpleLexer([], [[PR_STRING
, /^[\s\S]+/]]), ['regex']);
3377 function applyDecorator(job
) {
3378 var opt_langExtension
= job
.langExtension
;
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
;
3389 // Apply the appropriate language handler
3390 langHandlerForExtension(opt_langExtension
, source
)(job
);
3392 // Integrate the decorations and tags back into the source code,
3393 // modifying the sourceNode in place.
3394 recombineTagsAndDecorations(job
);
3396 if (win
['console']) {
3397 console
['log'](e
&& e
['stack'] ? e
['stack'] : e
);
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.
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);
3420 langExtension
: opt_langExtension
,
3421 numberLines
: opt_numberLines
,
3422 sourceNode
: container
,
3425 applyDecorator(job
);
3426 return container
.innerHTML
;
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')];
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
]);
3439 codeSegments
= null;
3442 if (!clock
['now']) {
3443 clock
= { 'now': function () { return +(new Date
); } };
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.
3449 var prettyPrintingJob
;
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;
3459 var endTime
= (win
['PR_SHOULD_USE_CONTINUATION'] ?
3460 clock
['now']() + 250 /* ms */ :
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
)) {
3471 // make sure this is not nested in an already prettified element
3473 for (var p
= cs
.parentNode
; p
; p
= p
.parentNode
) {
3475 if (preCodeXmpRe
.test(tn
)
3476 && p
.className
&& prettyPrintRe
.test(p
.className
)) {
3482 // Mark done. If we fail to prettyprint for whatever reason,
3483 // we shouldn't try again.
3484 cs
.className
+= ' prettyprinted';
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">
3497 if (!langExtension
&& (wrapper
= childContentWrapper(cs
))
3498 && codeRe
.test(wrapper
.tagName
)) {
3499 langExtension
= wrapper
.className
.match(langExtensionRe
);
3502 if (langExtension
) { langExtension
= langExtension
[1]; }
3505 if (preformattedTagNameRe
.test(cs
.tagName
)) {
3508 var currentStyle
= cs
['currentStyle'];
3511 ? currentStyle
['whiteSpace']
3512 : (document
.defaultView
3513 && document
.defaultView
.getComputedStyle
)
3514 ? document
.defaultView
.getComputedStyle(cs
, null)
3515 .getPropertyValue('white-space')
3517 preformatted
= whitespace
3518 && 'pre' === whitespace
.substring(0, 3);
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+))?/);
3525 ? lineNums
[1] && lineNums
[1].length
? +lineNums
[1] : true
3527 if (lineNums
) { numberLines(cs
, lineNums
, preformatted
); }
3529 // do the pretty printing
3530 prettyPrintingJob
= {
3531 langExtension
: langExtension
,
3533 numberLines
: lineNums
,
3536 applyDecorator(prettyPrintingJob
);
3540 if (k
< elements
.length
) {
3541 // finish up in a continuation
3542 setTimeout(doWork
, 250);
3543 } else if (opt_whenDone
) {
3552 * Contains functions for creating and registering new language handlers.
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
,
3572 'prettyPrintOne': win
['prettyPrintOne'] = prettyPrintOne
,
3573 'prettyPrint': win
['prettyPrint'] = prettyPrint
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.
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 () {
3597 var ContextFreeParser
= {
3598 parse: function(text
) {
3602 var subCurrent
= {};
3604 var scriptDocCommentClause
= '\\/\\*\\*([\\s\\S]*?)\\*\\/';
3605 var htmlDocCommentClause
= '<!--([\\s\\S]*?)-->';
3607 // matches text between /** and */ inclusive and <!-- and --> inclusive
3608 var docCommentRegex
= new RegExp(scriptDocCommentClause
+ '|' + htmlDocCommentClause
, 'g');
3610 // acquire all script doc comments
3611 var docComments
= text
.match(docCommentRegex
) || [];
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');
3618 // pragmas (@-rules) must occur on a line by themselves
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-]*) (.*)/);
3630 // collect all other text into a single block
3631 var code
= lines
.join('\n');
3634 pragmas
.forEach(function(m
) {
3635 var pragma
= m
[1], content
= m
[2];
3638 // currently all entities are either @class or @element
3645 entities
.push(current
);
3648 // an entity may have these describable sub-features
3657 var label
= pragma
== 'property' ? 'properties' : pragma
+ 's';
3658 makePragma(current
, label
, subCurrent
);
3661 // sub-feature pragmas
3664 subCurrent
[pragma
] = content
;
3669 current
[pragma
] = content
;
3674 // utility function, yay hoisting
3675 function makePragma(object
, pragma
, content
) {
3679 p
$[pragma
] = p
= [];
3686 if (entities
.length
=== 0) {
3687 entities
.push({name
: 'Entity', description
: '**Undocumented**'});
3693 if (typeof module
!== 'undefined' && module
.exports
) {
3694 module
.exports
= ContextFreeParser
;
3696 scope
.ContextFreeParser
= ContextFreeParser
;
3702 Polymer('core-xhr', {
3705 * Sends a HTTP request to the server and returns the XHR object.
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.
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
;
3726 var params
= this.toQueryString(options
.params
);
3727 if (params
&& method
== 'GET') {
3728 url
+= (url
.indexOf('?') > 0 ? '&' : '?') + params
;
3730 var xhrParams
= this.isBodyMethod(method
) ? (options
.body
|| params
) : null;
3732 xhr
.open(method
, url
, async
);
3733 if (options
.responseType
) {
3734 xhr
.responseType
= options
.responseType
;
3736 if (options
.withCredentials
) {
3737 xhr
.withCredentials
= true;
3739 this.makeReadyStateHandler(xhr
, options
.callback
);
3740 this.setRequestHeaders(xhr
, options
.headers
);
3741 xhr
.send(xhrParams
);
3743 xhr
.onreadystatechange(xhr
);
3748 toQueryString: function(params
) {
3750 for (var n
in params
) {
3752 n
= encodeURIComponent(n
);
3753 r
.push(v
== null ? n
: (n
+ '=' + encodeURIComponent(v
)));
3758 isBodyMethod: function(method
) {
3759 return this.bodyMethods
[(method
|| '').toUpperCase()];
3768 makeReadyStateHandler: function(xhr
, callback
) {
3769 xhr
.onreadystatechange = function() {
3770 if (xhr
.readyState
== 4) {
3771 callback
&& callback
.call(null, xhr
.response
, xhr
);
3776 setRequestHeaders: function(xhr
, headers
) {
3778 for (var name
in headers
) {
3779 xhr
.setRequestHeader(name
, headers
[name
]);
3789 Polymer('core-ajax', {
3791 * Fired when a response is received.
3793 * @event core-response
3797 * Fired when an error is received.
3803 * Fired whenever a response or an error is received.
3805 * @event core-complete
3809 * The URL target of the request.
3818 * Specifies what data to store in the `response` property, and
3819 * to deliver as `event.response` in `response` events.
3823 * `text`: uses `XHR.responseText`.
3825 * `xml`: uses `XHR.responseXML`.
3827 * `json`: uses `XHR.responseText` parsed as JSON.
3829 * `arraybuffer`: uses `XHR.response`.
3831 * `blob`: uses `XHR.response`.
3833 * `document`: uses `XHR.response`.
3835 * @attribute handleAs
3842 * If true, automatically performs an Ajax request when either `url` or `params` changes.
3851 * Parameters to send to the specified URL, as JSON.
3854 * @type string (JSON)
3860 * Returns the response object.
3862 * @attribute response
3869 * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
3879 * HTTP request headers to send.
3885 * url="http://somesite.com"
3886 * headers='{"X-Requested-With": "XMLHttpRequest"}'
3888 * on-core-response="{{handleResponse}}"></core-ajax>
3890 * @attribute headers
3897 * Optional raw body content to send when method === "POST".
3901 * <core-ajax method="POST" auto url="http://somesite.com"
3902 * body='{"foo":1, "bar":2}'>
3912 * Content type to use when sending data.
3914 * @attribute contentType
3916 * @default 'application/x-www-form-urlencoded'
3918 contentType
: 'application/x-www-form-urlencoded',
3921 * Set the withCredentials flag on the request.
3923 * @attribute withCredentials
3927 withCredentials
: false,
3930 * Additional properties to send to core-xhr.
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.
3943 this.xhr
= document
.createElement('core-xhr');
3946 receive: function(response
, xhr
) {
3947 if (this.isSuccess(xhr
)) {
3948 this.processResponse(xhr
);
3955 isSuccess: function(xhr
) {
3956 var status
= xhr
.status
|| 0;
3957 return !status
|| (status
>= 200 && status
< 300);
3960 processResponse: function(xhr
) {
3961 var response
= this.evalResponse(xhr
);
3962 this.response
= response
;
3963 this.fire('core-response', {response
: response
, xhr
: xhr
});
3966 error: function(xhr
) {
3967 var response
= xhr
.status
+ ': ' + xhr
.responseText
;
3968 this.fire('core-error', {response
: response
, xhr
: xhr
});
3971 complete: function(xhr
) {
3972 this.fire('core-complete', {response
: xhr
.status
, xhr
: xhr
});
3975 evalResponse: function(xhr
) {
3976 return this[(this.handleAs
|| 'text') + 'Handler'](xhr
);
3979 xmlHandler: function(xhr
) {
3980 return xhr
.responseXML
;
3983 textHandler: function(xhr
) {
3984 return xhr
.responseText
;
3987 jsonHandler: function(xhr
) {
3988 var r
= xhr
.responseText
;
3990 return JSON
.parse(r
);
3996 documentHandler: function(xhr
) {
3997 return xhr
.response
;
4000 blobHandler: function(xhr
) {
4001 return xhr
.response
;
4004 arraybufferHandler: function(xhr
) {
4005 return xhr
.response
;
4008 urlChanged: function() {
4009 if (!this.handleAs
) {
4010 var ext
= String(this.url
).split('.').pop();
4013 this.handleAs
= 'json';
4020 paramsChanged: function() {
4024 autoChanged: function() {
4028 // TODO(sorvell): multiple side-effects could call autoGo
4029 // during one micro-task, use a job to have only one action
4031 autoGo: function() {
4033 this.goJob
= this.job(this.goJob
, this.go
, 0);
4038 * Performs an Ajax request to the specified URL.
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
);
4050 args
.headers
= this.headers
|| args
.headers
|| {};
4051 if (args
.headers
&& typeof(args
.headers
) == 'string') {
4052 args
.headers
= JSON
.parse(args
.headers
);
4054 if (this.contentType
) {
4055 args
.headers
['content-type'] = this.contentType
;
4057 if (this.handleAs
=== 'arraybuffer' || this.handleAs
=== 'blob' ||
4058 this.handleAs
=== 'document') {
4059 args
.responseType
= this.handleAs
;
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
);
4073 Polymer('context-free-parser', {
4077 textChanged: function() {
4079 var entities
= ContextFreeParser
.parse(this.text
);
4080 if (!entities
|| entities
.length
=== 0) {
4082 {name
: this.url
.split('/').pop(), description
: '**Undocumented**'}
4085 this.data
= { classes
: entities
};
4089 dataChanged: function() {
4090 this.fire('data-ready');
4098 Polymer('core-doc-page', {
4100 hilight: function(event
, detail
, sender
) {
4101 detail
.code
= prettyPrintOne((detail
.code
|| '').replace(/</g,'<').replace(/>/g
,'>'));
4104 homepageFilter: function(data
) {
4108 if (!data
.homepage
|| data
.homepage
=== 'github.io') {
4109 return '//polymer.github.io/' + data
.name
;
4111 return data
.homepage
;
4119 Polymer('core-selection', {
4121 * If true, multiple selections are allowed.
4132 this.selection
= [];
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.
4141 getSelection: function() {
4142 return this.multi
? this.selection
: this.selection
[0];
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.
4150 isSelected: function(item
) {
4151 return this.selection
.indexOf(item
) >= 0;
4153 setItemSelected: function(item
, isSelected
) {
4154 if (item
!== undefined && item
!== null) {
4156 this.selection
.push(item
);
4158 var i
= this.selection
.indexOf(item
);
4160 this.selection
.splice(i
, 1);
4163 this.fire("core-select", {isSelected
: isSelected
, item
: item
});
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.
4171 * @param {any} item: The item to select.
4173 select: function(item
) {
4176 } else if (this.getSelection() !== item
) {
4177 this.setItemSelected(this.getSelection(), false);
4178 this.setItemSelected(item
, true);
4182 * Toggles the selection state for `item`.
4184 * @param {any} item: The item to toggle.
4186 toggle: function(item
) {
4187 this.setItemSelected(item
, !this.isSelected(item
));
4193 Polymer('core-selector', {
4196 * Gets or sets the selected element. Default to use the index
4197 * of the item element.
4199 * If you want a specific attribute value of the element to be
4200 * used instead of index, set "valueattr" to that attribute name.
4204 * <core-selector valueattr="label" selected="foo">
4205 * <div label="foo"></div>
4206 * <div label="bar"></div>
4207 * <div label="zot"></div>
4210 * In multi-selection this should be an array of values.
4214 * <core-selector id="selector" valueattr="label" multi>
4215 * <div label="foo"></div>
4216 * <div label="bar"></div>
4217 * <div label="zot"></div>
4220 * this.$.selector.selected = ['foo', 'zot'];
4222 * @attribute selected
4229 * If true, multiple selections are allowed.
4238 * Specifies the attribute to be used for "selected" attribute.
4240 * @attribute valueattr
4247 * Specifies the CSS class to be used to add to the selected element.
4249 * @attribute selectedClass
4251 * @default 'core-selected'
4253 selectedClass
: 'core-selected',
4256 * Specifies the property to be used to set on the selected element
4257 * to indicate its active state.
4259 * @attribute selectedProperty
4263 selectedProperty
: '',
4266 * Specifies the attribute to set on the selected element to indicate
4269 * @attribute selectedAttribute
4273 selectedAttribute
: 'active',
4276 * Returns the currently selected element. In multi-selection this returns
4277 * an array of selected elements.
4279 * @attribute selectedItem
4286 * In single selection, this returns the model associated with the
4289 * @attribute selectedModel
4293 selectedModel
: null,
4296 * In single selection, this returns the selected index.
4298 * @attribute selectedIndex
4305 * The target element that contains items. If this is not set
4306 * core-selector is the container.
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.
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>
4328 * @attribute itemSelector
4335 * The event that would be fired from the item element to indicate
4336 * it is being selected.
4338 * @attribute activateEvent
4342 activateEvent
: 'tap',
4345 * Set this to true to disallow changing the selection via the
4355 this.activateListener
= this.activateHandler
.bind(this);
4356 this.observer
= new MutationObserver(this.updateSelected
.bind(this));
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';
4374 targetChanged: function(old
) {
4376 this.removeListener(old
);
4377 this.observer
.disconnect();
4378 this.clearSelection();
4381 this.addListener(this.target
);
4382 this.observer
.observe(this.target
, {childList
: true});
4383 this.updateSelected();
4387 addListener: function(node
) {
4388 node
.addEventListener(this.activateEvent
, this.activateListener
);
4391 removeListener: function(node
) {
4392 node
.removeEventListener(this.activateEvent
, this.activateListener
);
4396 return this.$.selection
.getSelection();
4399 selectedChanged: function() {
4400 this.updateSelected();
4403 updateSelected: function() {
4404 this.validateSelected();
4406 this.clearSelection();
4407 this.selected
&& this.selected
.forEach(function(s
) {
4408 this.valueToSelection(s
);
4411 this.valueToSelection(this.selected
);
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
];
4423 clearSelection: function() {
4425 this.selection
.slice().forEach(function(s
) {
4426 this.$.selection
.setItemSelected(s
, false);
4429 this.$.selection
.setItemSelected(this.selection
, false);
4431 this.selectedItem
= null;
4432 this.$.selection
.clear();
4435 valueToSelection: function(value
) {
4436 var item
= (value
=== null || value
=== undefined) ?
4437 null : this.items
[this.valueToIndex(value
)];
4438 this.$.selection
.select(item
);
4441 updateSelectedItem: function() {
4442 this.selectedItem
= this.selection
;
4445 selectedItemChanged: function() {
4446 if (this.selectedItem
) {
4447 var t
= this.selectedItem
.templateInstance
;
4448 this.selectedModel
= t
? t
.model
: undefined;
4450 this.selectedModel
= null;
4452 this.selectedIndex
= this.selectedItem
?
4453 parseInt(this.valueToIndex(this.selected
)) : -1;
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
) {
4463 // if no item found, the value itself is probably the index
4467 valueForNode: function(node
) {
4468 return node
[this.valueattr
] || node
.getAttribute(this.valueattr
);
4471 // events fired from <core-selection> object
4472 selectionSelect: function(e
, detail
) {
4473 this.updateSelectedItem();
4475 this.applySelection(detail
.item
, detail
.isSelected
);
4479 applySelection: function(item
, isSelected
) {
4480 if (this.selectedClass
) {
4481 item
.classList
.toggle(this.selectedClass
, isSelected
);
4483 if (this.selectedProperty
) {
4484 item
[this.selectedProperty
] = isSelected
;
4486 if (this.selectedAttribute
&& item
.setAttribute
) {
4488 item
.setAttribute(this.selectedAttribute
, '');
4490 item
.removeAttribute(this.selectedAttribute
);
4495 // event fired from host
4496 activateHandler: function(e
) {
4498 var i
= this.findDistributedTarget(e
.target
, this.items
);
4500 var item
= this.items
[i
];
4501 var s
= this.valueForNode(item
) || i
;
4503 if (this.selected
) {
4504 this.addRemoveSelected(s
);
4506 this.selected
= [s
];
4511 this.asyncFire('core-activate', {item
: item
});
4516 addRemoveSelected: function(value
) {
4517 var i
= this.selected
.indexOf(value
);
4519 this.selected
.splice(i
, 1);
4521 this.selected
.push(value
);
4523 this.valueToSelection(value
);
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
);
4534 target
= target
.parentNode
;
4540 Polymer('core-menu',{});
4544 Polymer('core-item', {
4547 * The URL of an image for the icon.
4555 * Specifies the icon from the Polymer icon set.
4563 * Specifies the label for the menu item.
4575 Polymer('core-doc-toc', {
4577 searchAction: function() {
4578 this.$.searchBar
.style
.opacity
= 1;
4579 this.$.searchBar
.style
.display
= '';
4582 closeSearchAction: function() {
4583 this.$.searchBar
.style
.opacity
= 0;
4584 this.$.searchBar
.style
.display
= 'none';
4592 Polymer('core-doc-viewer', {
4594 * A single file to parse for docs
4602 * Class documentation extracted from the parser
4611 * Files to parse for docs
4613 * @attribute sources
4620 window
.addEventListener('hashchange', this.parseLocationHash
.bind(this));
4621 this.parseLocationHash();
4624 parseLocationHash: function() {
4625 this.route
= window
.location
.hash
.slice(1);
4628 routeChanged: function() {
4629 this.validateRoute();
4632 validateRoute: function() {
4634 this.classes
.some(function(c
) {
4635 if (c
.name
=== this.route
) {
4644 selectedChanged: function() {
4645 this.data
= this.classes
[this.selected
];
4648 parserDataReady: function(event
) {
4649 this.assimilateData(event
.target
.data
);
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;
4658 if (!this.data
&& !this.route
&& this.classes
.length
) {
4659 this.data
= this.classes
[0];
4661 if (this.classes
.length
> 1) {
4662 this.$.toc
.style
.display
= 'block';
4664 this.validateRoute();
4672 Polymer('core-component-page', {
4675 // TODO(sjmiles): needed this to force Object type for deserialization
4679 this.moduleName
= this.moduleName
|| this.findModuleName();
4682 moduleNameChanged: function() {
4683 document
.title
= this.moduleName
;
4684 this.url
= !this.sources
.length
&& this.moduleName
? this.moduleName
+ '.html' : '';
4687 findModuleName: function() {
4688 var path
= location
.pathname
.split('/');
4689 var name
= path
.pop() || path
.pop();
4690 if (name
.indexOf('.html') >= 0) {