Implement OCSP stapling in Windows BoringSSL port.
[chromium-blink-merge.git] / third_party / polymer / components-chromium / core-component-page / core-component-page-extracted.js
blobfe36fb744a631d657baefb8657f8b6e4d4ef4c8d
3 (function() {
5 Polymer('core-layout', {
7 isContainer: false,
8 /**
9 * Controls if the element lays out vertically or not.
11 * @attribute vertical
12 * @type boolean
13 * @default false
15 vertical: false,
16 /**
17 * Controls how the items are aligned in the main-axis direction. For
18 * example for a horizontal layout, this controls how each item is aligned
19 * horizontally.
21 * @attribute justify
22 * @type string start|center|end|between
23 * @default ''
25 justify: '',
26 /**
27 * Controls how the items are aligned in cross-axis direction. For
28 * example for a horizontal layout, this controls how each item is aligned
29 * vertically.
31 * @attribute align
32 * @type string start|center|end
33 * @default ''
35 align: '',
36 /**
37 * Controls whether or not the items layout in reverse order.
39 * @attribute reverse
40 * @type boolean
41 * @default false
43 reverse: false,
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() {
57 return null;
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;
68 if (forHost) {
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();
82 this.alignChanged();
83 this.justifyChanged();
86 setLayoutClass: function(prefix, old, newValue) {
87 if (this.layoutContainer) {
88 prefix = this.layoutPrefix + prefix;
89 if (old) {
90 this.layoutContainer.classList.remove(prefix + old);
92 if (newValue) {
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);
120 })();
124 (function() {
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
133 * stored together.
135 * @attribute type
136 * @type string
137 * @default 'default'
139 type: 'default',
141 alwaysPrepare: true,
143 ready: function() {
144 this.register(this.id);
147 get metaArray() {
148 var t = this.type;
149 if (!metaArray[t]) {
150 metaArray[t] = [];
152 return metaArray[t];
155 get metaData() {
156 var t = this.type;
157 if (!metaData[t]) {
158 metaData[t] = {};
160 return metaData[t];
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);
174 if (i >= 0) {
175 this.metaArray.splice(i, 1);
180 * Returns a list of all meta-data elements with the same type.
182 * @attribute list
183 * @type array
184 * @default []
186 get list() {
187 return this.metaArray;
191 * Retrieves meta-data by ID.
193 * @method byId
194 * @param {String} id The ID of the meta-data to be returned.
195 * @returns Returns meta-data.
197 byId: function(id) {
198 return this.metaData[id];
203 })();
208 Polymer('core-iconset', {
211 * The URL of the iconset image.
213 * @attribute src
214 * @type string
215 * @default ''
217 src: '',
220 * The width of the iconset image. This must only be specified if the
221 * icons are arranged into separate rows inside the image.
223 * @attribute width
224 * @type number
225 * @default 0
227 width: 0,
230 * A space separated list of names corresponding to icons in the iconset
231 * image file. This list must be ordered the same as the icon images
232 * in the image file.
234 * @attribute icons
235 * @type string
236 * @default ''
238 icons: '',
241 * The size of an individual icon. Note that icons must be square.
243 * @attribute iconSize
244 * @type number
245 * @default 24
247 iconSize: 24,
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.
254 * @attribute offsetX
255 * @type number
256 * @default 0
258 offsetX: 0,
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.
264 * @attribute offsetY
265 * @type number
266 * @default 0
268 offsetY: 0,
269 type: 'iconset',
271 created: function() {
272 this.iconMap = {};
273 this.iconNames = [];
274 this.themes = {};
277 ready: function() {
278 // TODO(sorvell): ensure iconset's src is always relative to the main
279 // document
280 if (this.src && (this.ownerDocument !== document)) {
281 this.src = this.resolvePath(this.src, this.ownerDocument.baseURI);
283 this.super();
284 this.updateThemes();
287 iconsChanged: function() {
288 var ox = this.offsetX;
289 var oy = this.offsetY;
290 this.icons && this.icons.split(/\s+/g).forEach(function(name, i) {
291 this.iconNames.push(name);
292 this.iconMap[name] = {
293 offsetX: ox,
294 offsetY: oy
296 if (ox + this.iconSize < this.width) {
297 ox += this.iconSize;
298 } else {
299 ox = this.offsetX;
300 oy += this.iconSize;
302 }, this);
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
312 }, this);
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.
323 * @method getOffset
324 * @param {String|Number} icon The name of the icon or the index of the
325 * icon within in the icon image.
326 * @param {String} theme The name of the theme.
327 * @returns {Object} An object specifying the offset of the given icon
328 * within the icon resource file; `offsetX` is the horizontal offset and
329 * `offsetY` is the vertical offset. Both values are in pixel units.
331 getOffset: function(icon, theme) {
332 var i = this.iconMap[icon];
333 if (!i) {
334 var n = this.iconNames[Number(icon)];
335 i = this.iconMap[n];
337 var t = this.themes[theme];
338 if (i && t) {
339 return {
340 offsetX: i.offsetX + t.offsetX,
341 offsetY: i.offsetY + t.offsetY
344 return i;
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.
352 * @method applyIcon
353 * @param {Element} element The element to which the background is
354 * applied.
355 * @param {String|Number} icon The name or index of the icon to apply.
356 * @param {String} theme (optional) The name of the theme for the icon.
357 * @param {Number} scale (optional, defaults to 1) A scaling factor
358 * with which the icon can be magnified.
360 applyIcon: function(element, icon, scale) {
361 var offset = this.getOffset(icon);
362 scale = scale || 1;
363 if (element && offset) {
364 var style = element.style;
365 style.backgroundImage = 'url(' + this.src + ')';
366 style.backgroundPosition = (-offset.offsetX * scale + 'px') +
367 ' ' + (-offset.offsetY * scale + 'px');
368 style.backgroundSize = scale === 1 ? 'auto' :
369 this.width * scale + 'px';
378 Polymer('core-iconset-svg', {
382 * The size of an individual icon. Note that icons must be square.
384 * @attribute iconSize
385 * @type number
386 * @default 24
388 iconSize: 24,
389 type: 'iconset',
391 created: function() {
392 this._icons = {};
395 ready: function() {
396 this.super();
397 this.updateIcons();
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);
406 if (icon) {
407 var content = icon.cloneNode(true);
408 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
409 svg.setAttribute('viewBox', '0 0 ' + this.iconSize + ' ' +
410 this.iconSize);
411 // NOTE(dfreedm): work around https://crbug.com/370136
412 svg.style.pointerEvents = 'none';
413 svg.appendChild(content);
414 return svg;
418 get iconNames() {
419 if (!this._iconNames) {
420 this._iconNames = this.findIconNames();
422 return this._iconNames;
425 findIconNames: function() {
426 var icons = this.querySelectorAll('[id]').array();
427 if (icons.length) {
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.
436 * @method applyIcon
437 * @param {Element} element The element to which the icon is
438 * applied.
439 * @param {String|Number} icon The name the icon to apply.
441 applyIcon: function(element, icon, scale) {
442 var root = element.shadowRoot || element;
443 // remove old
444 var old = root.querySelector('svg');
445 if (old) {
446 old.remove();
448 // install new
449 var svg = this.cloneIcon(icon);
450 if (!svg) {
451 return;
453 var size = scale * this.iconSize;
454 if (size) {
455 svg.style.height = svg.style.width = size + 'px';
456 } else {
457 svg.setAttribute('height', '100%');
458 svg.setAttribute('width', '100%');
459 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
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++) {
481 if (e[method]) {
482 e[method].call(e);
492 (function() {
494 // mono-state
495 var meta;
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.
503 * @attribute src
504 * @type string
505 * @default ''
507 src: '',
510 * Specifies the size of the icon in pixel units.
512 * @attribute size
513 * @type string
514 * @default 24
516 size: 24,
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.
523 * @attribute icon
524 * @type string
525 * @default ''
527 icon: '',
529 observe: {
530 'size icon': 'updateIcon'
533 defaultIconset: 'icons',
535 ready: function() {
536 if (!meta) {
537 meta = document.createElement('core-iconset');
539 this.updateIcon();
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() {
553 if (this.size) {
554 this.style.width = this.style.height = this.size + 'px';
556 if (this.icon) {
557 var parts = String(this.icon).split(':');
558 var icon = parts.pop();
559 if (icon) {
560 var set = this.getIconset(parts.pop());
561 if (set) {
562 set.applyIcon(this, icon, this.size / set.iconSize);
570 })();
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.
580 * @attribute src
581 * @type string
582 * @default ''
584 src: '',
587 * If true, border is placed around the button to indicate it's
588 * active state.
590 * @attribute active
591 * @type boolean
592 * @default false
594 active: false,
597 * Specifies the icon name or index in the set of icons available in
598 * the icon set. Should not use `src` property if you are using this
599 * property.
601 * @attribute icon
602 * @type string
603 * @default ''
605 icon: '',
607 activeChanged: function() {
608 this.classList.toggle('selected', this.active);
614 Polymer('core-toolbar');;
617 Polymer('core-header-panel', {
619 publish: {
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
628 * opposite side.
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.
645 * <style>
646 * core-header-panel[mode=cover]::shadow #mainContainer {
647 * left: 80px;
649 * .content {
650 * margin: 60px 60px 60px 0;
652 * </style>
654 * <core-header-panel mode="cover">
655 * <core-appbar class="tall">
656 * <core-icon-button icon="menu"></core-icon-button>
657 * </core-appbar>
658 * <div class="content"></div>
659 * </core-header-panel>
661 * @attribute mode
662 * @type string
663 * @default ''
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
672 * @type string
673 * @default 'tall'
675 tallClass: 'tall',
678 * If true, the drop-shadow is always shown no matter what mode is set to.
680 * @attribute shadow
681 * @type boolean
682 * @default false
684 shadow: false,
687 domReady: function() {
688 this.async('scroll');
691 modeChanged: function() {
692 this.scroll();
695 get header() {
696 return this.$.headerContent.getDistributedNodes()[0];
699 scroll: function() {
700 var shadowMode = {'waterfall': 1, 'waterfall-tall': 1};
701 var noShadow = {'seamed': 1, 'cover': 1, 'scroll': 1};
702 var tallMode = {'waterfall-tall': 1};
704 var main = this.$.mainContainer;
705 var header = this.header;
707 var sTop = main.scrollTop;
708 var atTop = sTop === 0;
710 if (header) {
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
731 ;(function() {
734 * Block-Level Grammar
737 var block = {
738 newline: /^\n+/,
739 code: /^( {4}[^\n]+\n*)+/,
740 fences: noop,
741 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
742 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
743 nptable: noop,
744 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
745 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
746 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
747 html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
748 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
749 table: noop,
750 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
751 text: /^[^\n]+/
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)
767 ('def', block.def)
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(?:"[^"]*"|'[^']*'|[^'">])*?>/)
779 (/tag/g, block._tag)
782 block.paragraph = replace(block.paragraph)
783 ('hr', block.hr)
784 ('heading', block.heading)
785 ('lheading', block.lheading)
786 ('blockquote', block.blockquote)
787 ('tag', '<' + block._tag)
788 ('def', block.def)
792 * Normal Block Grammar
795 block.normal = merge({}, block);
798 * GFM Block Grammar
801 block.gfm = merge({}, block.normal, {
802 fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
803 paragraph: /^/
806 block.gfm.paragraph = replace(block.paragraph)
807 ('(?!', '(?!'
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*/
822 * Block Lexer
825 function Lexer(options) {
826 this.tokens = [];
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;
834 } else {
835 this.rules = block.gfm;
841 * Expose Block Rules
844 Lexer.rules = block;
847 * Static Lex Method
850 Lexer.lex = function(src, options) {
851 var lexer = new Lexer(options);
852 return lexer.lex(src);
856 * Preprocessing
859 Lexer.prototype.lex = function(src) {
860 src = src
861 .replace(/\r\n|\r/g, '\n')
862 .replace(/\t/g, ' ')
863 .replace(/\u00a0/g, ' ')
864 .replace(/\u2424/g, '\n');
866 return this.token(src, true);
870 * Lexing
873 Lexer.prototype.token = function(src, top, bq) {
874 var src = src.replace(/^ +$/gm, '')
875 , next
876 , loose
877 , cap
878 , bull
880 , item
881 , space
883 , l;
885 while (src) {
886 // newline
887 if (cap = this.rules.newline.exec(src)) {
888 src = src.substring(cap[0].length);
889 if (cap[0].length > 1) {
890 this.tokens.push({
891 type: 'space'
896 // code
897 if (cap = this.rules.code.exec(src)) {
898 src = src.substring(cap[0].length);
899 cap = cap[0].replace(/^ {4}/gm, '');
900 this.tokens.push({
901 type: 'code',
902 text: !this.options.pedantic
903 ? cap.replace(/\n+$/, '')
904 : cap
906 continue;
909 // fences (gfm)
910 if (cap = this.rules.fences.exec(src)) {
911 src = src.substring(cap[0].length);
912 this.tokens.push({
913 type: 'code',
914 lang: cap[2],
915 text: cap[3]
917 continue;
920 // heading
921 if (cap = this.rules.heading.exec(src)) {
922 src = src.substring(cap[0].length);
923 this.tokens.push({
924 type: 'heading',
925 depth: cap[1].length,
926 text: cap[2]
928 continue;
931 // table no leading pipe (gfm)
932 if (top && (cap = this.rules.nptable.exec(src))) {
933 src = src.substring(cap[0].length);
935 item = {
936 type: 'table',
937 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
938 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
939 cells: cap[3].replace(/\n$/, '').split('\n')
942 for (i = 0; i < item.align.length; i++) {
943 if (/^ *-+: *$/.test(item.align[i])) {
944 item.align[i] = 'right';
945 } else if (/^ *:-+: *$/.test(item.align[i])) {
946 item.align[i] = 'center';
947 } else if (/^ *:-+ *$/.test(item.align[i])) {
948 item.align[i] = 'left';
949 } else {
950 item.align[i] = null;
954 for (i = 0; i < item.cells.length; i++) {
955 item.cells[i] = item.cells[i].split(/ *\| */);
958 this.tokens.push(item);
960 continue;
963 // lheading
964 if (cap = this.rules.lheading.exec(src)) {
965 src = src.substring(cap[0].length);
966 this.tokens.push({
967 type: 'heading',
968 depth: cap[2] === '=' ? 1 : 2,
969 text: cap[1]
971 continue;
974 // hr
975 if (cap = this.rules.hr.exec(src)) {
976 src = src.substring(cap[0].length);
977 this.tokens.push({
978 type: 'hr'
980 continue;
983 // blockquote
984 if (cap = this.rules.blockquote.exec(src)) {
985 src = src.substring(cap[0].length);
987 this.tokens.push({
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);
998 this.tokens.push({
999 type: 'blockquote_end'
1002 continue;
1005 // list
1006 if (cap = this.rules.list.exec(src)) {
1007 src = src.substring(cap[0].length);
1008 bull = cap[2];
1010 this.tokens.push({
1011 type: 'list_start',
1012 ordered: bull.length > 1
1015 // Get each top-level item.
1016 cap = cap[0].match(this.rules.item);
1018 next = false;
1019 l = cap.length;
1020 i = 0;
1022 for (; i < l; i++) {
1023 item = cap[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;
1045 i = l - 1;
1049 // Determine whether item is loose or not.
1050 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
1051 // for discount behavior.
1052 loose = next || /\n\n(?!\s*$)/.test(item);
1053 if (i !== l - 1) {
1054 next = item.charAt(item.length - 1) === '\n';
1055 if (!loose) loose = next;
1058 this.tokens.push({
1059 type: loose
1060 ? 'loose_item_start'
1061 : 'list_item_start'
1064 // Recurse.
1065 this.token(item, false, bq);
1067 this.tokens.push({
1068 type: 'list_item_end'
1072 this.tokens.push({
1073 type: 'list_end'
1076 continue;
1079 // html
1080 if (cap = this.rules.html.exec(src)) {
1081 src = src.substring(cap[0].length);
1082 this.tokens.push({
1083 type: this.options.sanitize
1084 ? 'paragraph'
1085 : 'html',
1086 pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
1087 text: cap[0]
1089 continue;
1092 // def
1093 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
1094 src = src.substring(cap[0].length);
1095 this.tokens.links[cap[1].toLowerCase()] = {
1096 href: cap[2],
1097 title: cap[3]
1099 continue;
1102 // table (gfm)
1103 if (top && (cap = this.rules.table.exec(src))) {
1104 src = src.substring(cap[0].length);
1106 item = {
1107 type: 'table',
1108 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
1109 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
1110 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
1113 for (i = 0; i < item.align.length; i++) {
1114 if (/^ *-+: *$/.test(item.align[i])) {
1115 item.align[i] = 'right';
1116 } else if (/^ *:-+: *$/.test(item.align[i])) {
1117 item.align[i] = 'center';
1118 } else if (/^ *:-+ *$/.test(item.align[i])) {
1119 item.align[i] = 'left';
1120 } else {
1121 item.align[i] = null;
1125 for (i = 0; i < item.cells.length; i++) {
1126 item.cells[i] = item.cells[i]
1127 .replace(/^ *\| *| *\| *$/g, '')
1128 .split(/ *\| */);
1131 this.tokens.push(item);
1133 continue;
1136 // top-level paragraph
1137 if (top && (cap = this.rules.paragraph.exec(src))) {
1138 src = src.substring(cap[0].length);
1139 this.tokens.push({
1140 type: 'paragraph',
1141 text: cap[1].charAt(cap[1].length - 1) === '\n'
1142 ? cap[1].slice(0, -1)
1143 : cap[1]
1145 continue;
1148 // text
1149 if (cap = this.rules.text.exec(src)) {
1150 // Top-level should never reach here.
1151 src = src.substring(cap[0].length);
1152 this.tokens.push({
1153 type: 'text',
1154 text: cap[0]
1156 continue;
1159 if (src) {
1160 throw new
1161 Error('Infinite loop on byte: ' + src.charCodeAt(0));
1165 return this.tokens;
1169 * Inline-Level Grammar
1172 var inline = {
1173 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
1174 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
1175 url: noop,
1176 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
1177 link: /^!?\[(inside)\]\(href\)/,
1178 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
1179 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
1180 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
1181 em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
1182 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
1183 br: /^ {2,}\n(?!\s*$)/,
1184 del: noop,
1185 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
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)
1224 (']|', '~]|')
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;
1244 this.links = links;
1245 this.rules = inline.normal;
1246 this.renderer = this.options.renderer || new Renderer;
1247 this.renderer.options = this.options;
1249 if (!this.links) {
1250 throw new
1251 Error('Tokens array requires a `links` property.');
1254 if (this.options.gfm) {
1255 if (this.options.breaks) {
1256 this.rules = inline.breaks;
1257 } else {
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);
1281 * Lexing/Compiling
1284 InlineLexer.prototype.output = function(src) {
1285 var out = ''
1286 , link
1287 , text
1288 , href
1289 , cap;
1291 while (src) {
1292 // escape
1293 if (cap = this.rules.escape.exec(src)) {
1294 src = src.substring(cap[0].length);
1295 out += cap[1];
1296 continue;
1299 // autolink
1300 if (cap = this.rules.autolink.exec(src)) {
1301 src = src.substring(cap[0].length);
1302 if (cap[2] === '@') {
1303 text = cap[1].charAt(6) === ':'
1304 ? this.mangle(cap[1].substring(7))
1305 : this.mangle(cap[1]);
1306 href = this.mangle('mailto:') + text;
1307 } else {
1308 text = escape(cap[1]);
1309 href = text;
1311 out += this.renderer.link(href, null, text);
1312 continue;
1315 // url (gfm)
1316 if (!this.inLink && (cap = this.rules.url.exec(src))) {
1317 src = src.substring(cap[0].length);
1318 text = escape(cap[1]);
1319 href = text;
1320 out += this.renderer.link(href, null, text);
1321 continue;
1324 // tag
1325 if (cap = this.rules.tag.exec(src)) {
1326 if (!this.inLink && /^<a /i.test(cap[0])) {
1327 this.inLink = true;
1328 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
1329 this.inLink = false;
1331 src = src.substring(cap[0].length);
1332 out += this.options.sanitize
1333 ? escape(cap[0])
1334 : cap[0];
1335 continue;
1338 // link
1339 if (cap = this.rules.link.exec(src)) {
1340 src = src.substring(cap[0].length);
1341 this.inLink = true;
1342 out += this.outputLink(cap, {
1343 href: cap[2],
1344 title: cap[3]
1346 this.inLink = false;
1347 continue;
1350 // reflink, nolink
1351 if ((cap = this.rules.reflink.exec(src))
1352 || (cap = this.rules.nolink.exec(src))) {
1353 src = src.substring(cap[0].length);
1354 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
1355 link = this.links[link.toLowerCase()];
1356 if (!link || !link.href) {
1357 out += cap[0].charAt(0);
1358 src = cap[0].substring(1) + src;
1359 continue;
1361 this.inLink = true;
1362 out += this.outputLink(cap, link);
1363 this.inLink = false;
1364 continue;
1367 // strong
1368 if (cap = this.rules.strong.exec(src)) {
1369 src = src.substring(cap[0].length);
1370 out += this.renderer.strong(this.output(cap[2] || cap[1]));
1371 continue;
1374 // em
1375 if (cap = this.rules.em.exec(src)) {
1376 src = src.substring(cap[0].length);
1377 out += this.renderer.em(this.output(cap[2] || cap[1]));
1378 continue;
1381 // code
1382 if (cap = this.rules.code.exec(src)) {
1383 src = src.substring(cap[0].length);
1384 out += this.renderer.codespan(escape(cap[2], true));
1385 continue;
1388 // br
1389 if (cap = this.rules.br.exec(src)) {
1390 src = src.substring(cap[0].length);
1391 out += this.renderer.br();
1392 continue;
1395 // del (gfm)
1396 if (cap = this.rules.del.exec(src)) {
1397 src = src.substring(cap[0].length);
1398 out += this.renderer.del(this.output(cap[1]));
1399 continue;
1402 // text
1403 if (cap = this.rules.text.exec(src)) {
1404 src = src.substring(cap[0].length);
1405 out += escape(this.smartypants(cap[0]));
1406 continue;
1409 if (src) {
1410 throw new
1411 Error('Infinite loop on byte: ' + src.charCodeAt(0));
1415 return out;
1419 * Compile Link
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;
1437 return text
1438 // em-dashes
1439 .replace(/--/g, '\u2014')
1440 // opening singles
1441 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
1442 // closing singles & apostrophes
1443 .replace(/'/g, '\u2019')
1444 // opening doubles
1445 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
1446 // closing doubles
1447 .replace(/"/g, '\u201d')
1448 // ellipses
1449 .replace(/\.{3}/g, '\u2026');
1453 * Mangle Links
1456 InlineLexer.prototype.mangle = function(text) {
1457 var out = ''
1458 , l = text.length
1459 , i = 0
1460 , ch;
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 + ';';
1470 return out;
1474 * Renderer
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) {
1485 escaped = true;
1486 code = out;
1490 if (!lang) {
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)
1499 + '">'
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) {
1509 return html;
1512 Renderer.prototype.heading = function(text, level, raw) {
1513 return '<h'
1514 + level
1515 + ' id="'
1516 + this.options.headerPrefix
1517 + raw.toLowerCase().replace(/[^\w]+/g, '-')
1518 + '">'
1519 + text
1520 + '</h'
1521 + level
1522 + '>\n';
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) {
1543 return '<table>\n'
1544 + '<thead>\n'
1545 + header
1546 + '</thead>\n'
1547 + '<tbody>\n'
1548 + body
1549 + '</tbody>\n'
1550 + '</table>\n';
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 + '">'
1561 : '<' + type + '>';
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) {
1588 try {
1589 var prot = decodeURIComponent(unescape(href))
1590 .replace(/[^\w:]/g, '')
1591 .toLowerCase();
1592 } catch (e) {
1593 return '';
1595 if (prot.indexOf('javascript:') === 0) {
1596 return '';
1599 var out = '<a href="' + href + '"';
1600 if (title) {
1601 out += ' title="' + title + '"';
1603 out += '>' + text + '</a>';
1604 return out;
1607 Renderer.prototype.image = function(href, title, text) {
1608 var out = '<img src="' + href + '" alt="' + text + '"';
1609 if (title) {
1610 out += ' title="' + title + '"';
1612 out += this.options.xhtml ? '/>' : '>';
1613 return out;
1617 * Parsing & Compiling
1620 function Parser(options) {
1621 this.tokens = [];
1622 this.token = null;
1623 this.options = options || marked.defaults;
1624 this.options.renderer = this.options.renderer || new Renderer;
1625 this.renderer = this.options.renderer;
1626 this.renderer.options = this.options;
1630 * Static Parse Method
1633 Parser.parse = function(src, options, renderer) {
1634 var parser = new Parser(options, renderer);
1635 return parser.parse(src);
1639 * Parse Loop
1642 Parser.prototype.parse = function(src) {
1643 this.inline = new InlineLexer(src.links, this.options, this.renderer);
1644 this.tokens = src.reverse();
1646 var out = '';
1647 while (this.next()) {
1648 out += this.tok();
1651 return out;
1655 * Next Token
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;
1671 * Parse Text Tokens
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) {
1690 case 'space': {
1691 return '';
1693 case 'hr': {
1694 return this.renderer.hr();
1696 case 'heading': {
1697 return this.renderer.heading(
1698 this.inline.output(this.token.text),
1699 this.token.depth,
1700 this.token.text);
1702 case 'code': {
1703 return this.renderer.code(this.token.text,
1704 this.token.lang,
1705 this.token.escaped);
1707 case 'table': {
1708 var header = ''
1709 , body = ''
1711 , row
1712 , cell
1713 , flags
1714 , j;
1716 // header
1717 cell = '';
1718 for (i = 0; i < this.token.header.length; i++) {
1719 flags = { header: true, align: this.token.align[i] };
1720 cell += this.renderer.tablecell(
1721 this.inline.output(this.token.header[i]),
1722 { header: true, align: this.token.align[i] }
1725 header += this.renderer.tablerow(cell);
1727 for (i = 0; i < this.token.cells.length; i++) {
1728 row = this.token.cells[i];
1730 cell = '';
1731 for (j = 0; j < row.length; j++) {
1732 cell += this.renderer.tablecell(
1733 this.inline.output(row[j]),
1734 { header: false, align: this.token.align[j] }
1738 body += this.renderer.tablerow(cell);
1740 return this.renderer.table(header, body);
1742 case 'blockquote_start': {
1743 var body = '';
1745 while (this.next().type !== 'blockquote_end') {
1746 body += this.tok();
1749 return this.renderer.blockquote(body);
1751 case 'list_start': {
1752 var body = ''
1753 , ordered = this.token.ordered;
1755 while (this.next().type !== 'list_end') {
1756 body += this.tok();
1759 return this.renderer.list(body, ordered);
1761 case 'list_item_start': {
1762 var body = '';
1764 while (this.next().type !== 'list_item_end') {
1765 body += this.token.type === 'text'
1766 ? this.parseText()
1767 : this.tok();
1770 return this.renderer.listitem(body);
1772 case 'loose_item_start': {
1773 var body = '';
1775 while (this.next().type !== 'list_item_end') {
1776 body += this.tok();
1779 return this.renderer.listitem(body);
1781 case 'html': {
1782 var html = !this.token.pre && !this.options.pedantic
1783 ? this.inline.output(this.token.text)
1784 : this.token.text;
1785 return this.renderer.html(html);
1787 case 'paragraph': {
1788 return this.renderer.paragraph(this.inline.output(this.token.text));
1790 case 'text': {
1791 return this.renderer.paragraph(this.parseText());
1797 * Helpers
1800 function escape(html, encode) {
1801 return html
1802 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
1803 .replace(/</g, '&lt;')
1804 .replace(/>/g, '&gt;')
1805 .replace(/"/g, '&quot;')
1806 .replace(/'/g, '&#39;');
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));
1818 return '';
1822 function replace(regex, opt) {
1823 regex = regex.source;
1824 opt = opt || '';
1825 return function self(name, val) {
1826 if (!name) return new RegExp(regex, opt);
1827 val = val.source || val;
1828 val = val.replace(/(^|[^\[])\^/g, '$1');
1829 regex = regex.replace(name, val);
1830 return self;
1834 function noop() {}
1835 noop.exec = noop;
1837 function merge(obj) {
1838 var i = 1
1839 , target
1840 , key;
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];
1851 return obj;
1856 * Marked
1859 function marked(src, opt, callback) {
1860 if (callback || typeof opt === 'function') {
1861 if (!callback) {
1862 callback = opt;
1863 opt = null;
1866 opt = merge({}, marked.defaults, opt || {});
1868 var highlight = opt.highlight
1869 , tokens
1870 , pending
1871 , i = 0;
1873 try {
1874 tokens = Lexer.lex(src, opt)
1875 } catch (e) {
1876 return callback(e);
1879 pending = tokens.length;
1881 var done = function() {
1882 var out, err;
1884 try {
1885 out = Parser.parse(tokens, opt);
1886 } catch (e) {
1887 err = e;
1890 opt.highlight = highlight;
1892 return err
1893 ? callback(err)
1894 : callback(null, out);
1897 if (!highlight || highlight.length < 3) {
1898 return done();
1901 delete opt.highlight;
1903 if (!pending) return done();
1905 for (; i < tokens.length; i++) {
1906 (function(token) {
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();
1914 token.text = code;
1915 token.escaped = true;
1916 --pending || done();
1918 })(tokens[i]);
1921 return;
1923 try {
1924 if (opt) opt = merge({}, marked.defaults, opt);
1925 return Parser.parse(Lexer.lex(src, opt), opt);
1926 } catch (e) {
1927 e.message += '\nPlease report this to https://github.com/chjj/marked.';
1928 if ((opt || marked.defaults).silent) {
1929 return '<p>An error occured:</p><pre>'
1930 + escape(e.message + '', true)
1931 + '</pre>';
1933 throw e;
1938 * Options
1941 marked.options =
1942 marked.setOptions = function(opt) {
1943 merge(marked.defaults, opt);
1944 return marked;
1947 marked.defaults = {
1948 gfm: true,
1949 tables: true,
1950 breaks: false,
1951 pedantic: false,
1952 sanitize: false,
1953 smartLists: false,
1954 silent: false,
1955 highlight: null,
1956 langPrefix: 'lang-',
1957 smartypants: false,
1958 headerPrefix: '',
1959 renderer: new Renderer,
1960 xhtml: false
1964 * Expose
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; });
1984 } else {
1985 this.marked = marked;
1988 }).call(function() {
1989 return this || (typeof window !== 'undefined' ? window : global);
1990 }());
1994 Polymer('marked-element', {
1996 text: '',
1998 attached: function() {
1999 marked.setOptions({
2000 highlight: this.highlight.bind(this)
2002 if (!this.text) {
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.
2035 * @fileoverview
2036 * some functions for browser-side pretty printing of code contained in html.
2038 * <p>
2039 * For a fairly comprehensive set of languages see the
2040 * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
2041 * file that came with this source. At a minimum, the lexer should work on a
2042 * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
2043 * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk
2044 * and a subset of Perl, but, because of commenting conventions, doesn't work on
2045 * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
2046 * <p>
2047 * Usage: <ol>
2048 * <li> include this source file in an html page via
2049 * {@code <script type="text/javascript" src="/path/to/prettify.js"><\/script>}
2050 * <li> define style rules. See the example page for examples.
2051 * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
2052 * {@code class=prettyprint.}
2053 * You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
2054 * printer needs to do more substantial DOM manipulations to support that, so
2055 * some css styles may not be preserved.
2056 * </ol>
2057 * That's it. I wanted to keep the API as simple as possible, so there's no
2058 * need to specify which language the code is in, but if you wish, you can add
2059 * another class to the {@code <pre>} or {@code <code>} element to specify the
2060 * language, as in {@code <pre class="prettyprint lang-java">}. Any class that
2061 * starts with "lang-" followed by a file extension, specifies the file type.
2062 * See the "lang-*.js" files in this directory for code that implements
2063 * per-language file handlers.
2064 * <p>
2065 * Change log:<br>
2066 * cbeust, 2006/08/22
2067 * <blockquote>
2068 * Java annotations (start with "@") are now captured as literals ("lit")
2069 * </blockquote>
2070 * @requires console
2073 // JSLint declarations
2074 /*global console, document, navigator, setTimeout, window, define */
2077 * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
2078 * UI events.
2079 * If set to {@code false}, {@code prettyPrint()} is synchronous.
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.
2090 var prettyPrintOne;
2092 * Pretty print a chunk of code.
2094 * @param {string} sourceCodeHtml code as html
2095 * @return {string} code as html, but prettier
2097 var prettyPrint;
2100 (function () {
2101 var win = window;
2102 // Keyword lists for various languages.
2103 // We use things that coerce to strings to make them compact when minified
2104 // and to defeat aggressive optimizers that fold large string constants.
2105 var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
2106 var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
2107 "double,enum,extern,float,goto,int,long,register,short,signed,sizeof," +
2108 "static,struct,switch,typedef,union,unsigned,void,volatile"];
2109 var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
2110 "new,operator,private,protected,public,this,throw,true,try,typeof"];
2111 var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
2112 "concept,concept_map,const_cast,constexpr,decltype," +
2113 "dynamic_cast,explicit,export,friend,inline,late_check," +
2114 "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast," +
2115 "template,typeid,typename,using,virtual,where"];
2116 var JAVA_KEYWORDS = [COMMON_KEYWORDS,
2117 "abstract,boolean,byte,extends,final,finally,implements,import," +
2118 "instanceof,null,native,package,strictfp,super,synchronized,throws," +
2119 "transient"];
2120 var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
2121 "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
2122 "fixed,foreach,from,group,implicit,in,interface,internal,into,is,let," +
2123 "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
2124 "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
2125 "var,virtual,where"];
2126 var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
2127 "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
2128 "throw,true,try,unless,until,when,while,yes";
2129 var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
2130 "debugger,eval,export,function,get,null,set,undefined,var,with," +
2131 "Infinity,NaN"];
2132 var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
2133 "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
2134 "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
2135 var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
2136 "elif,except,exec,finally,from,global,import,in,is,lambda," +
2137 "nonlocal,not,or,pass,print,raise,try,with,yield," +
2138 "False,True,None"];
2139 var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
2140 "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
2141 "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
2142 "BEGIN,END"];
2143 var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
2144 "function,in,local,set,then,until"];
2145 var ALL_KEYWORDS = [
2146 CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
2147 PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
2148 var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
2150 // token style names. correspond to css classes
2152 * token style for a string literal
2153 * @const
2155 var PR_STRING = 'str';
2157 * token style for a keyword
2158 * @const
2160 var PR_KEYWORD = 'kwd';
2162 * token style for a comment
2163 * @const
2165 var PR_COMMENT = 'com';
2167 * token style for a type
2168 * @const
2170 var PR_TYPE = 'typ';
2172 * token style for a literal value. e.g. 1, null, true.
2173 * @const
2175 var PR_LITERAL = 'lit';
2177 * token style for a punctuation string.
2178 * @const
2180 var PR_PUNCTUATION = 'pun';
2182 * token style for plain text.
2183 * @const
2185 var PR_PLAIN = 'pln';
2188 * token style for an sgml tag.
2189 * @const
2191 var PR_TAG = 'tag';
2193 * token style for a markup declaration such as a DOCTYPE.
2194 * @const
2196 var PR_DECLARATION = 'dec';
2198 * token style for embedded source.
2199 * @const
2201 var PR_SOURCE = 'src';
2203 * token style for an sgml attribute name.
2204 * @const
2206 var PR_ATTRIB_NAME = 'atn';
2208 * token style for an sgml attribute value.
2209 * @const
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.
2216 * @const
2218 var PR_NOCODE = 'nocode';
2223 * A set of tokens that can precede a regular expression literal in
2224 * javascript
2225 * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
2226 * has the full list, but I've removed ones that might be problematic when
2227 * seen in languages that don't support regular expression literals.
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.
2238 * @private
2239 * @const
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) {
2266 ignoreCase = true;
2267 } else if (/[a-z]/i.test(regex.source.replace(
2268 /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
2269 needToFoldCase = true;
2270 ignoreCase = false;
2271 break;
2275 var escapeCharToCodeUnit = {
2276 'b': 8,
2277 't': 9,
2278 'n': 0xa,
2279 'v': 0xb,
2280 'f': 0xc,
2281 'r': 0xd
2284 function decodeEscape(charsetPart) {
2285 var cc0 = charsetPart.charCodeAt(0);
2286 if (cc0 !== 92 /* \\ */) {
2287 return cc0;
2289 var c1 = charsetPart.charAt(1);
2290 cc0 = escapeCharToCodeUnit[c1];
2291 if (cc0) {
2292 return cc0;
2293 } else if ('0' <= c1 && c1 <= '7') {
2294 return parseInt(charsetPart.substring(1), 8);
2295 } else if (c1 === 'u' || c1 === 'x') {
2296 return parseInt(charsetPart.substring(2), 16);
2297 } else {
2298 return charsetPart.charCodeAt(1);
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 === '^')
2308 ? "\\" + ch : ch;
2311 function caseFoldCharset(charSet) {
2312 var charsetParts = charSet.substring(1, charSet.length - 1).match(
2313 new RegExp(
2314 '\\\\u[0-9A-Fa-f]{4}'
2315 + '|\\\\x[0-9A-Fa-f]{2}'
2316 + '|\\\\[0-3][0-7]{0,2}'
2317 + '|\\\\[0-7]{1,2}'
2318 + '|\\\\[\\s\\S]'
2319 + '|-'
2320 + '|[^-\\\\]',
2321 'g'));
2322 var ranges = [];
2323 var inverse = charsetParts[0] === '^';
2325 var out = ['['];
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.
2331 out.push(p);
2332 } else {
2333 var start = decodeEscape(p);
2334 var end;
2335 if (i + 2 < n && '-' === charsetParts[i + 1]) {
2336 end = decodeEscape(charsetParts[i + 2]);
2337 i += 2;
2338 } else {
2339 end = start;
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 = [];
2361 var lastRange = [];
2362 for (var i = 0; i < ranges.length; ++i) {
2363 var range = ranges[i];
2364 if (range[0] <= lastRange[1] + 1) {
2365 lastRange[1] = Math.max(lastRange[1], range[1]);
2366 } else {
2367 consolidatedRanges.push(lastRange = range);
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]));
2379 out.push(']');
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(
2388 new RegExp(
2389 '(?:'
2390 + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
2391 + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
2392 + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
2393 + '|\\\\[0-9]+' // a back-reference or octal escape
2394 + '|\\\\[^ux0-9]' // other escape sequence
2395 + '|\\(\\?[:!=]' // start of a non-capturing group
2396 + '|[\\(\\)\\^]' // start/end of a group, or line start
2397 + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
2398 + ')',
2399 'g'));
2400 var n = parts.length;
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
2408 // mapping.
2409 for (var i = 0, groupIndex = 0; i < n; ++i) {
2410 var p = parts[i];
2411 if (p === '(') {
2412 // groups are 1-indexed, so max group index is count of '('
2413 ++groupIndex;
2414 } else if ('\\' === p.charAt(0)) {
2415 var decimalValue = +p.substring(1);
2416 if (decimalValue) {
2417 if (decimalValue <= groupIndex) {
2418 capturedGroups[decimalValue] = -1;
2419 } else {
2420 // Replace with an unambiguous escape sequence so that
2421 // an octal escape sequence does not turn into a backreference
2422 // to a capturing group from an earlier regex.
2423 parts[i] = encodeEscape(decimalValue);
2429 // Renumber groups and reduce capturing groups to non-capturing groups
2430 // where possible.
2431 for (var i = 1; i < capturedGroups.length; ++i) {
2432 if (-1 === capturedGroups[i]) {
2433 capturedGroups[i] = ++capturedGroupIndex;
2436 for (var i = 0, groupIndex = 0; i < n; ++i) {
2437 var p = parts[i];
2438 if (p === '(') {
2439 ++groupIndex;
2440 if (!capturedGroups[groupIndex]) {
2441 parts[i] = '(?:';
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) {
2461 var p = parts[i];
2462 var ch0 = p.charAt(0);
2463 if (p.length >= 2 && ch0 === '[') {
2464 parts[i] = caseFoldCharset(p);
2465 } else if (ch0 !== '\\') {
2466 // TODO: handle letters in numeric escapes.
2467 parts[i] = p.replace(
2468 /[a-zA-Z]/g,
2469 function (ch) {
2470 var cc = ch.charCodeAt(0);
2471 return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
2477 return parts.join('');
2480 var rewritten = [];
2481 for (var i = 0, n = regexs.length; i < n; ++i) {
2482 var regex = regexs[i];
2483 if (regex.global || regex.multiline) { throw new Error('' + regex); }
2484 rewritten.push(
2485 '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
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.
2496 * <p>
2497 * The HTML DOM structure:</p>
2498 * <pre>
2499 * (Element "p"
2500 * (Element "b"
2501 * (Text "print ")) ; #1
2502 * (Text "'Hello '") ; #2
2503 * (Element "br") ; #3
2504 * (Text " + 'World';")) ; #4
2505 * </pre>
2506 * <p>
2507 * corresponds to the HTML
2508 * {@code <p><b>print </b>'Hello '<br> + 'World';</p>}.</p>
2510 * <p>
2511 * It will produce the output:</p>
2512 * <pre>
2514 * sourceCode: "print 'Hello '\n + 'World';",
2515 * // 1 2
2516 * // 012345678901234 5678901234567
2517 * spans: [0, #1, 6, #2, 14, #3, 15, #4]
2519 * </pre>
2520 * <p>
2521 * where #1 is a reference to the {@code "print "} text node above, and so
2522 * on for the other text nodes.
2523 * </p>
2525 * <p>
2526 * The {@code} spans array is an array of pairs. Even elements are the start
2527 * indices of substrings, and odd elements are the text nodes (or BR elements)
2528 * that contain the text for those substrings.
2529 * Substrings continue until the next index or the end of the source.
2530 * </p>
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|$)/;
2540 var chunks = [];
2541 var length = 0;
2542 var spans = [];
2543 var k = 0;
2545 function walk(node) {
2546 switch (node.nodeType) {
2547 case 1: // Element
2548 if (nocode.test(node.className)) { return; }
2549 for (var child = node.firstChild; child; child = child.nextSibling) {
2550 walk(child);
2552 var nodeName = node.nodeName.toLowerCase();
2553 if ('br' === nodeName || 'li' === nodeName) {
2554 chunks[k] = '\n';
2555 spans[k << 1] = length++;
2556 spans[(k++ << 1) | 1] = node;
2558 break;
2559 case 3: case 4: // Text
2560 var text = node.nodeValue;
2561 if (text.length) {
2562 if (!isPreformatted) {
2563 text = text.replace(/[ \t\r\n]+/g, ' ');
2564 } else {
2565 text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
2567 // TODO: handle tabs here?
2568 chunks[k] = text;
2569 spans[k << 1] = length;
2570 length += text.length;
2571 spans[(k++ << 1) | 1] = node;
2573 break;
2577 walk(node);
2579 return {
2580 sourceCode: chunks.join('').replace(/\n$/, ''),
2581 spans: spans
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; }
2594 var job = {
2595 sourceCode: sourceCode,
2596 basePos: basePos
2598 langHandler(job);
2599 out.push.apply(out, job.decorations);
2602 var notWs = /\S/;
2605 * Given an element, if it contains only one child element and any text nodes
2606 * it contains contain only space characters, return the sole child element.
2607 * Otherwise returns undefined.
2608 * <p>
2609 * This is meant to return the CODE element in {@code <pre><code ...>} when
2610 * there is a single child element that contains all the non-space textual
2611 * content, but not to return anything where there are multiple child elements
2612 * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
2613 * is textual content.
2615 function childContentWrapper(element) {
2616 var wrapper = undefined;
2617 for (var c = element.firstChild; c; c = c.nextSibling) {
2618 var type = c.nodeType;
2619 wrapper = (type === 1) // Element Node
2620 ? (wrapper ? element : c)
2621 : (type === 3) // Text Node
2622 ? (notWs.test(c.nodeValue) ? element : wrapper)
2623 : wrapper;
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
2661 * recognized.
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) {
2675 var shortcuts = {};
2676 var tokenizer;
2677 (function () {
2678 var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
2679 var allRegexs = [];
2680 var regexKeys = {};
2681 for (var i = 0, n = allPatterns.length; i < n; ++i) {
2682 var patternParts = allPatterns[i];
2683 var shortcutChars = patternParts[3];
2684 if (shortcutChars) {
2685 for (var c = shortcutChars.length; --c >= 0;) {
2686 shortcuts[shortcutChars.charAt(c)] = patternParts;
2689 var regex = patternParts[1];
2690 var k = '' + regex;
2691 if (!regexKeys.hasOwnProperty(k)) {
2692 allRegexs.push(regex);
2693 regexKeys[k] = null;
2696 allRegexs.push(/[\0-\uffff]/);
2697 tokenizer = combinePrefixPatterns(allRegexs);
2698 })();
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
2710 * sourceCode.
2711 * }</pre>
2713 var decorate = function (job) {
2714 var sourceCode = job.sourceCode, basePos = job.basePos;
2715 /** Even entries are positions in source in ascending order. Odd enties
2716 * are style markers (e.g., PR_COMMENT) that run from that position until
2717 * the end.
2718 * @type {Array.<number|string>}
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];
2728 var match = void 0;
2730 var isEmbedded;
2731 if (typeof style === 'string') {
2732 isEmbedded = false;
2733 } else {
2734 var patternParts = shortcuts[token.charAt(0)];
2735 if (patternParts) {
2736 match = token.match(patternParts[1]);
2737 style = patternParts[0];
2738 } else {
2739 for (var i = 0; i < nPatterns; ++i) {
2740 patternParts = fallthroughStylePatterns[i];
2741 match = token.match(patternParts[1]);
2742 if (match) {
2743 style = patternParts[0];
2744 break;
2748 if (!match) { // make sure that we make progress
2749 style = PR_PLAIN;
2753 isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
2754 if (isEmbedded && !(match && typeof match[1] === 'string')) {
2755 isEmbedded = false;
2756 style = PR_SOURCE;
2759 if (!isEmbedded) { styleCache[token] = style; }
2762 var tokenStart = pos;
2763 pos += token.length;
2765 if (!isEmbedded) {
2766 decorations.push(basePos + tokenStart, style);
2767 } else { // Treat group 1 as an embedded block of source code.
2768 var embeddedSource = match[1];
2769 var embeddedSourceStart = token.indexOf(embeddedSource);
2770 var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
2771 if (match[2]) {
2772 // If embeddedSource can be blank, then it would match at the
2773 // beginning which would cause us to infinitely recurse on the
2774 // entire token, so we catch the right context in match[2].
2775 embeddedSourceEnd = token.length - match[2].length;
2776 embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
2778 var lang = style.substring(5);
2779 // Decorate the left of the embedded source
2780 appendDecorations(
2781 basePos + tokenStart,
2782 token.substring(0, embeddedSourceStart),
2783 decorate, decorations);
2784 // Decorate the embedded source
2785 appendDecorations(
2786 basePos + tokenStart + embeddedSourceStart,
2787 embeddedSource,
2788 langHandlerForExtension(lang, embeddedSource),
2789 decorations);
2790 // Decorate the right of the embedded section
2791 appendDecorations(
2792 basePos + tokenStart + embeddedSourceEnd,
2793 token.substring(embeddedSourceEnd),
2794 decorate, decorations);
2797 job.decorations = decorations;
2799 return decorate;
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])*(?:\"|$))/,
2823 null, '\'"']);
2824 } else if (options['multiLineStrings']) {
2825 // 'multi-line-string', "multi-line-string"
2826 shortcutStylePatterns.push(
2827 [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
2828 null, '\'"`']);
2829 } else {
2830 // 'single-line-string', "single-line-string"
2831 shortcutStylePatterns.push(
2832 [PR_STRING,
2833 /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
2834 null, '"\'']);
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'];
2842 if (hc) {
2843 if (options['cStyleComments']) {
2844 if (hc > 1) { // multiline hash comments
2845 shortcutStylePatterns.push(
2846 [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
2847 } else {
2848 // Stop C preprocessor declarations at an unclosed open comment
2849 shortcutStylePatterns.push(
2850 [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
2851 null, '#']);
2853 // #include <stdio.h>
2854 fallthroughStylePatterns.push(
2855 [PR_STRING,
2856 /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
2857 null]);
2858 } else {
2859 shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
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']) {
2869 * @const
2871 var REGEX_LITERAL = (
2872 // A regular expression literal starts with a slash that is
2873 // not followed by * or / so that it is not confused with
2874 // comments.
2875 '/(?=[^/*])'
2876 // and then contains any number of raw characters,
2877 + '(?:[^/\\x5B\\x5C]'
2878 // escape sequences (\x5C),
2879 + '|\\x5C[\\s\\S]'
2880 // or non-nesting character sets (\x5B\x5D);
2881 + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
2882 // finally closed by a /.
2883 + '/');
2884 fallthroughStylePatterns.push(
2885 ['lang-regex',
2886 new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
2890 var types = options['types'];
2891 if (types) {
2892 fallthroughStylePatterns.push([PR_TYPE, types]);
2895 var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
2896 if (keywords.length) {
2897 fallthroughStylePatterns.push(
2898 [PR_KEYWORD,
2899 new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
2900 null]);
2903 shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']);
2905 var punctuation =
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: { }
2911 // | & $ ; < >
2912 // ...
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
2919 // $ echo {#}
2920 // {#}
2921 // $ echo \$#
2922 // $#
2923 // $ echo }#
2924 // }#
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],
2948 [PR_LITERAL,
2949 new RegExp(
2950 '^(?:'
2951 // A hex number
2952 + '0x[a-f0-9]+'
2953 // or an octal or decimal number,
2954 + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
2955 // possibly in scientific notation
2956 + '(?:e[+\\-]?\\d+)?'
2957 + ')'
2958 // with an optional modifier like UL for unsigned long
2959 + '[a-z]*', 'i'),
2960 null, '0123456789'],
2961 // Don't treat escaped quotes in bash as starting strings. See issue 144.
2962 [PR_PLAIN, /^\\[\s\S]?/, null],
2963 [PR_PUNCTUATION, punctuation, null]);
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
2978 * list item.
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
2998 // un-split line.
2999 var listItems = [li];
3001 function walk(node) {
3002 switch (node.nodeType) {
3003 case 1: // Element
3004 if (nocode.test(node.className)) { break; }
3005 if ('br' === node.nodeName) {
3006 breakAfter(node);
3007 // Discard the <BR> since it is now flush against a </LI>.
3008 if (node.parentNode) {
3009 node.parentNode.removeChild(node);
3011 } else {
3012 for (var child = node.firstChild; child; child = child.nextSibling) {
3013 walk(child);
3016 break;
3017 case 3: case 4: // Text
3018 if (isPreformatted) {
3019 var text = node.nodeValue;
3020 var match = text.match(lineBreak);
3021 if (match) {
3022 var firstLine = text.substring(0, match.index);
3023 node.nodeValue = firstLine;
3024 var tail = text.substring(match.index + match[0].length);
3025 if (tail) {
3026 var parent = node.parentNode;
3027 parent.insertBefore(
3028 document.createTextNode(tail), node.nextSibling);
3030 breakAfter(node);
3031 if (!firstLine) {
3032 // Don't leave blank text nodes in the DOM.
3033 node.parentNode.removeChild(node);
3037 break;
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;
3055 if (parent) {
3056 // We clone the parent chain.
3057 // This helps us resurrect important styling elements that cross lines.
3058 // E.g. in <i>Foo<br>Bar</i>
3059 // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
3060 var parentClone = breakLeftOf(parent, 1);
3061 // Move the clone and everything to the right of the original
3062 // onto the cloned parent.
3063 var next = limit.nextSibling;
3064 parentClone.appendChild(rightSide);
3065 for (var sibling = next; sibling; sibling = next) {
3066 next = sibling.nextSibling;
3067 parentClone.appendChild(sibling);
3070 return rightSide;
3073 var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
3075 // Walk the parent chain until we reach an unattached LI.
3076 for (var parent;
3077 // Check nodeType since IE invents document fragments.
3078 (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
3079 copiedListItem = parent;
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.
3088 ++i) {
3089 walk(listItems[i]);
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) {
3101 li = listItems[i];
3102 // Stick a class on the LIs so that stylesheets can
3103 // color odd/even rows, or any other row pattern that
3104 // is co-prime with 10.
3105 li.className = 'L' + ((i + offset) % 10);
3106 if (!li.firstChild) {
3107 li.appendChild(document.createTextNode('\xA0'));
3109 ol.appendChild(li);
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
3122 * span.
3123 * decorations: {Array.<number|string} an array of style classes preceded
3124 * by the position at which they start in job.sourceCode in order
3125 * }</pre>
3126 * @private
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.
3141 var spanIndex = 0;
3143 var decorations = job.decorations;
3144 var nDecorations = decorations.length;
3145 // Index into decorations after the last decoration which ends at or before
3146 // sourceIndex.
3147 var decorationIndex = 0;
3149 // Remove all zero-length decorations.
3150 decorations[nDecorations] = sourceLength;
3151 var decPos, i;
3152 for (i = decPos = 0; i < nDecorations;) {
3153 if (decorations[i] !== decorations[i + 2]) {
3154 decorations[decPos++] = decorations[i++];
3155 decorations[decPos++] = decorations[i++];
3156 } else {
3157 i += 2;
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];
3167 var end = i + 2;
3168 while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
3169 end += 2;
3171 decorations[decPos++] = startPos;
3172 decorations[decPos++] = startDec;
3173 i = end;
3176 nDecorations = decorations.length = decPos;
3178 var sourceNode = job.sourceNode;
3179 var oldDisplay;
3180 if (sourceNode) {
3181 oldDisplay = sourceNode.style.display;
3182 sourceNode.style.display = 'none';
3184 try {
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];
3195 var styledText;
3196 if (textNode.nodeType !== 1 // Don't muck with <BR>s or <LI>s
3197 // Don't introduce spans around empty text nodes.
3198 && (styledText = source.substring(sourceIndex, end))) {
3199 // This may seem bizarre, and it is. Emitting LF on IE causes the
3200 // code to display with spaces instead of line breaks.
3201 // Emitting Windows standard issue linebreaks (CRLF) causes a blank
3202 // space to appear at the beginning of every line but the first.
3203 // Emitting an old Mac OS 9 line separator makes everything spiffy.
3204 if (isIE8OrEarlier) {
3205 styledText = styledText.replace(newlineRe, '\r');
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);
3222 sourceIndex = end;
3224 if (sourceIndex >= spanEnd) {
3225 spanIndex += 2;
3227 if (sourceIndex >= decEnd) {
3228 decorationIndex += 2;
3231 } finally {
3232 if (sourceNode) {
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
3245 * {@code {
3246 * sourceCode: {string} as plain text.
3247 * decorations: {Array.<number|string>} an array of style classes
3248 * preceded by the position at which they start in
3249 * job.sourceCode in order.
3250 * The language handler should assigned this field.
3251 * basePos: {int} the position of source in the larger source chunk.
3252 * All positions in the output decorations array are relative
3253 * to the larger source chunk.
3254 * } }
3255 * @param {Array.<string>} fileExtensions
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)
3272 ? 'default-markup'
3273 : 'default-code';
3275 return langHandlerRegistry[extension];
3277 registerLangHandler(decorateSource, ['default-code']);
3278 registerLangHandler(
3279 createSimpleLexer(
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(
3298 createSimpleLexer(
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]
3315 ['in.tag']);
3316 registerLangHandler(
3317 createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
3318 registerLangHandler(sourceDecorator({
3319 'keywords': CPP_KEYWORDS,
3320 'hashComments': true,
3321 'cStyleComments': true,
3322 'types': C_TYPES
3323 }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
3324 registerLangHandler(sourceDecorator({
3325 'keywords': 'null,true,false'
3326 }), ['json']);
3327 registerLangHandler(sourceDecorator({
3328 'keywords': CSHARP_KEYWORDS,
3329 'hashComments': true,
3330 'cStyleComments': true,
3331 'verbatimStrings': true,
3332 'types': C_TYPES
3333 }), ['cs']);
3334 registerLangHandler(sourceDecorator({
3335 'keywords': JAVA_KEYWORDS,
3336 'cStyleComments': true
3337 }), ['java']);
3338 registerLangHandler(sourceDecorator({
3339 'keywords': SH_KEYWORDS,
3340 'hashComments': true,
3341 'multiLineStrings': true
3342 }), ['bsh', 'csh', 'sh']);
3343 registerLangHandler(sourceDecorator({
3344 'keywords': PYTHON_KEYWORDS,
3345 'hashComments': true,
3346 'multiLineStrings': true,
3347 'tripleQuotedStrings': true
3348 }), ['cv', 'py']);
3349 registerLangHandler(sourceDecorator({
3350 'keywords': PERL_KEYWORDS,
3351 'hashComments': true,
3352 'multiLineStrings': true,
3353 'regexLiterals': true
3354 }), ['perl', 'pl', 'pm']);
3355 registerLangHandler(sourceDecorator({
3356 'keywords': RUBY_KEYWORDS,
3357 'hashComments': true,
3358 'multiLineStrings': true,
3359 'regexLiterals': true
3360 }), ['rb']);
3361 registerLangHandler(sourceDecorator({
3362 'keywords': JSCRIPT_KEYWORDS,
3363 'cStyleComments': true,
3364 'regexLiterals': true
3365 }), ['js']);
3366 registerLangHandler(sourceDecorator({
3367 'keywords': COFFEE_KEYWORDS,
3368 'hashComments': 3, // ### style block comments
3369 'cStyleComments': true,
3370 'multilineStrings': true,
3371 'tripleQuotedStrings': true,
3372 'regexLiterals': true
3373 }), ['coffee']);
3374 registerLangHandler(
3375 createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
3377 function applyDecorator(job) {
3378 var opt_langExtension = job.langExtension;
3380 try {
3381 // Extract tags, and convert the source code to plain text.
3382 var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
3383 /** Plain text. @type {string} */
3384 var source = sourceAndSpans.sourceCode;
3385 job.sourceCode = source;
3386 job.spans = sourceAndSpans.spans;
3387 job.basePos = 0;
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);
3395 } catch (e) {
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);
3419 var job = {
3420 langExtension: opt_langExtension,
3421 numberLines: opt_numberLines,
3422 sourceNode: container,
3423 pre: 1
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')];
3433 var elements = [];
3434 for (var i = 0; i < codeSegments.length; ++i) {
3435 for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
3436 elements.push(codeSegments[i][j]);
3439 codeSegments = null;
3441 var clock = Date;
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.
3448 var k = 0;
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;
3458 function doWork() {
3459 var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
3460 clock['now']() + 250 /* ms */ :
3461 Infinity);
3462 for (; k < elements.length && clock['now']() < endTime; k++) {
3463 var cs = elements[k];
3464 var className = cs.className;
3465 if (prettyPrintRe.test(className)
3466 // Don't redo this if we've already done it.
3467 // This allows recalling pretty print to just prettyprint elements
3468 // that have been added to the page since last call.
3469 && !prettyPrintedRe.test(className)) {
3471 // make sure this is not nested in an already prettified element
3472 var nested = false;
3473 for (var p = cs.parentNode; p; p = p.parentNode) {
3474 var tn = p.tagName;
3475 if (preCodeXmpRe.test(tn)
3476 && p.className && prettyPrintRe.test(p.className)) {
3477 nested = true;
3478 break;
3481 if (!nested) {
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">
3496 var wrapper;
3497 if (!langExtension && (wrapper = childContentWrapper(cs))
3498 && codeRe.test(wrapper.tagName)) {
3499 langExtension = wrapper.className.match(langExtensionRe);
3502 if (langExtension) { langExtension = langExtension[1]; }
3504 var preformatted;
3505 if (preformattedTagNameRe.test(cs.tagName)) {
3506 preformatted = 1;
3507 } else {
3508 var currentStyle = cs['currentStyle'];
3509 var whitespace = (
3510 currentStyle
3511 ? currentStyle['whiteSpace']
3512 : (document.defaultView
3513 && document.defaultView.getComputedStyle)
3514 ? document.defaultView.getComputedStyle(cs, null)
3515 .getPropertyValue('white-space')
3516 : 0);
3517 preformatted = whitespace
3518 && 'pre' === whitespace.substring(0, 3);
3521 // Look for a class like linenums or linenums:<n> where <n> is the
3522 // 1-indexed number of the first line.
3523 var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
3524 lineNums = lineNums
3525 ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
3526 : false;
3527 if (lineNums) { numberLines(cs, lineNums, preformatted); }
3529 // do the pretty printing
3530 prettyPrintingJob = {
3531 langExtension: langExtension,
3532 sourceNode: cs,
3533 numberLines: lineNums,
3534 pre: preformatted
3536 applyDecorator(prettyPrintingJob);
3540 if (k < elements.length) {
3541 // finish up in a continuation
3542 setTimeout(doWork, 250);
3543 } else if (opt_whenDone) {
3544 opt_whenDone();
3548 doWork();
3552 * Contains functions for creating and registering new language handlers.
3553 * @type {Object}
3555 var PR = win['PR'] = {
3556 'createSimpleLexer': createSimpleLexer,
3557 'registerLangHandler': registerLangHandler,
3558 'sourceDecorator': sourceDecorator,
3559 'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
3560 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
3561 'PR_COMMENT': PR_COMMENT,
3562 'PR_DECLARATION': PR_DECLARATION,
3563 'PR_KEYWORD': PR_KEYWORD,
3564 'PR_LITERAL': PR_LITERAL,
3565 'PR_NOCODE': PR_NOCODE,
3566 'PR_PLAIN': PR_PLAIN,
3567 'PR_PUNCTUATION': PR_PUNCTUATION,
3568 'PR_SOURCE': PR_SOURCE,
3569 'PR_STRING': PR_STRING,
3570 'PR_TAG': PR_TAG,
3571 'PR_TYPE': PR_TYPE,
3572 'prettyPrintOne': win['prettyPrintOne'] = prettyPrintOne,
3573 'prettyPrint': win['prettyPrint'] = prettyPrint
3576 // Make PR available via the Asynchronous Module Definition (AMD) API.
3577 // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
3578 // The Asynchronous Module Definition (AMD) API specifies a
3579 // mechanism for defining modules such that the module and its
3580 // dependencies can be asynchronously loaded.
3581 // ...
3582 // To allow a clear indicator that a global define function (as
3583 // needed for script src browser loading) conforms to the AMD API,
3584 // any global define function SHOULD have a property called "amd"
3585 // whose value is an object. This helps avoid conflict with any
3586 // other existing JavaScript code that could have defined a define()
3587 // function that does not conform to the AMD API.
3588 if (typeof define === "function" && define['amd']) {
3589 define(function () {
3590 return PR;
3593 })();
3595 (function(scope) {
3597 var ContextFreeParser = {
3598 parse: function(text) {
3599 var top = {};
3600 var entities = [];
3601 var current = top;
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
3619 var pragmas = [];
3620 // filter lines whose first non-whitespace character is @ into the pragma list
3621 // (and out of the `lines` array)
3622 lines = lines.filter(function(l) {
3623 var m = l.match(/\s*@([\w-]*) (.*)/);
3624 if (!m) {
3625 return true;
3627 pragmas.push(m);
3630 // collect all other text into a single block
3631 var code = lines.join('\n');
3633 // process pragmas
3634 pragmas.forEach(function(m) {
3635 var pragma = m[1], content = m[2];
3636 switch (pragma) {
3638 // currently all entities are either @class or @element
3639 case 'class':
3640 case 'element':
3641 current = {
3642 name: content,
3643 description: code
3645 entities.push(current);
3646 break;
3648 // an entity may have these describable sub-features
3649 case 'attribute':
3650 case 'property':
3651 case 'method':
3652 case 'event':
3653 subCurrent = {
3654 name: content,
3655 description: code
3657 var label = pragma == 'property' ? 'properties' : pragma + 's';
3658 makePragma(current, label, subCurrent);
3659 break;
3661 // sub-feature pragmas
3662 case 'default':
3663 case 'type':
3664 subCurrent[pragma] = content;
3665 break;
3667 // everything else
3668 default:
3669 current[pragma] = content;
3670 break;
3674 // utility function, yay hoisting
3675 function makePragma(object, pragma, content) {
3676 var p$ = object;
3677 var p = p$[pragma];
3678 if (!p) {
3679 p$[pragma] = p = [];
3681 p.push(content);
3686 if (entities.length === 0) {
3687 entities.push({name: 'Entity', description: '**Undocumented**'});
3689 return entities;
3693 if (typeof module !== 'undefined' && module.exports) {
3694 module.exports = ContextFreeParser;
3695 } else {
3696 scope.ContextFreeParser = ContextFreeParser;
3699 })(this);;
3702 Polymer('core-xhr', {
3705 * Sends a HTTP request to the server and returns the XHR object.
3707 * @method request
3708 * @param {Object} inOptions
3709 * @param {String} inOptions.url The url to which the request is sent.
3710 * @param {String} inOptions.method The HTTP method to use, default is GET.
3711 * @param {boolean} inOptions.sync By default, all requests are sent asynchronously. To send synchronous requests, set to true.
3712 * @param {Object} inOptions.params Data to be sent to the server.
3713 * @param {Object} inOptions.body The content for the request body for POST method.
3714 * @param {Object} inOptions.headers HTTP request headers.
3715 * @param {String} inOptions.responseType The response type. Default is 'text'.
3716 * @param {boolean} inOptions.withCredentials Whether or not to send credentials on the request. Default is false.
3717 * @param {Object} inOptions.callback Called when request is completed.
3718 * @returns {Object} XHR object.
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);
3742 if (!async) {
3743 xhr.onreadystatechange(xhr);
3745 return xhr;
3748 toQueryString: function(params) {
3749 var r = [];
3750 for (var n in params) {
3751 var v = params[n];
3752 n = encodeURIComponent(n);
3753 r.push(v == null ? n : (n + '=' + encodeURIComponent(v)));
3755 return r.join('&');
3758 isBodyMethod: function(method) {
3759 return this.bodyMethods[(method || '').toUpperCase()];
3762 bodyMethods: {
3763 POST: 1,
3764 PUT: 1,
3765 DELETE: 1
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) {
3777 if (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.
3799 * @event core-error
3803 * Fired whenever a response or an error is received.
3805 * @event core-complete
3809 * The URL target of the request.
3811 * @attribute url
3812 * @type string
3813 * @default ''
3815 url: '',
3818 * Specifies what data to store in the `response` property, and
3819 * to deliver as `event.response` in `response` events.
3821 * One of:
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
3836 * @type string
3837 * @default 'text'
3839 handleAs: '',
3842 * If true, automatically performs an Ajax request when either `url` or `params` changes.
3844 * @attribute auto
3845 * @type boolean
3846 * @default false
3848 auto: false,
3851 * Parameters to send to the specified URL, as JSON.
3853 * @attribute params
3854 * @type string (JSON)
3855 * @default ''
3857 params: '',
3860 * Returns the response object.
3862 * @attribute response
3863 * @type Object
3864 * @default null
3866 response: null,
3869 * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
3870 * Default is 'GET'.
3872 * @attribute method
3873 * @type string
3874 * @default ''
3876 method: '',
3879 * HTTP request headers to send.
3881 * Example:
3883 * <core-ajax
3884 * auto
3885 * url="http://somesite.com"
3886 * headers='{"X-Requested-With": "XMLHttpRequest"}'
3887 * handleAs="json"
3888 * on-core-response="{{handleResponse}}"></core-ajax>
3890 * @attribute headers
3891 * @type Object
3892 * @default null
3894 headers: null,
3897 * Optional raw body content to send when method === "POST".
3899 * Example:
3901 * <core-ajax method="POST" auto url="http://somesite.com"
3902 * body='{"foo":1, "bar":2}'>
3903 * </core-ajax>
3905 * @attribute body
3906 * @type Object
3907 * @default null
3909 body: null,
3912 * Content type to use when sending data.
3914 * @attribute contentType
3915 * @type string
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
3924 * @type boolean
3925 * @default false
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.
3936 * @property xhrArgs
3937 * @type Object
3938 * @default null
3940 xhrArgs: null,
3942 ready: function() {
3943 this.xhr = document.createElement('core-xhr');
3946 receive: function(response, xhr) {
3947 if (this.isSuccess(xhr)) {
3948 this.processResponse(xhr);
3949 } else {
3950 this.error(xhr);
3952 this.complete(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;
3989 try {
3990 return JSON.parse(r);
3991 } catch (x) {
3992 return 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();
4011 switch (ext) {
4012 case 'json':
4013 this.handleAs = 'json';
4014 break;
4017 this.autoGo();
4020 paramsChanged: function() {
4021 this.autoGo();
4024 autoChanged: function() {
4025 this.autoGo();
4028 // TODO(sorvell): multiple side-effects could call autoGo
4029 // during one micro-task, use a job to have only one action
4030 // occur
4031 autoGo: function() {
4032 if (this.auto) {
4033 this.goJob = this.job(this.goJob, this.go, 0);
4038 * Performs an Ajax request to the specified URL.
4040 * @method go
4042 go: function() {
4043 var args = this.xhrArgs || {};
4044 // TODO(sjmiles): we may want XHR to default to POST if body is set
4045 args.body = this.body || args.body;
4046 args.params = this.params || args.params;
4047 if (args.params && typeof(args.params) == 'string') {
4048 args.params = JSON.parse(args.params);
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', {
4075 text: null,
4077 textChanged: function() {
4078 if (this.text) {
4079 var entities = ContextFreeParser.parse(this.text);
4080 if (!entities || entities.length === 0) {
4081 entities = [
4082 {name: this.url.split('/').pop(), description: '**Undocumented**'}
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,'&lt;').replace(/>/g,'&gt;'));
4104 homepageFilter: function(data) {
4105 if (!data) {
4106 return '';
4108 if (!data.homepage || data.homepage === 'github.io') {
4109 return '//polymer.github.io/' + data.name;
4110 } else {
4111 return data.homepage;
4119 Polymer('core-selection', {
4121 * If true, multiple selections are allowed.
4123 * @attribute multi
4124 * @type boolean
4125 * @default false
4127 multi: false,
4128 ready: function() {
4129 this.clear();
4131 clear: function() {
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) {
4155 if (isSelected) {
4156 this.selection.push(item);
4157 } else {
4158 var i = this.selection.indexOf(item);
4159 if (i >= 0) {
4160 this.selection.splice(i, 1);
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.
4170 * @method select
4171 * @param {any} item: The item to select.
4173 select: function(item) {
4174 if (this.multi) {
4175 this.toggle(item);
4176 } else if (this.getSelection() !== item) {
4177 this.setItemSelected(this.getSelection(), false);
4178 this.setItemSelected(item, true);
4182 * Toggles the selection state for `item`.
4183 * @method toggle
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.
4202 * Example:
4204 * <core-selector valueattr="label" selected="foo">
4205 * <div label="foo"></div>
4206 * <div label="bar"></div>
4207 * <div label="zot"></div>
4208 * </core-selector>
4210 * In multi-selection this should be an array of values.
4212 * Example:
4214 * <core-selector id="selector" valueattr="label" multi>
4215 * <div label="foo"></div>
4216 * <div label="bar"></div>
4217 * <div label="zot"></div>
4218 * </core-selector>
4220 * this.$.selector.selected = ['foo', 'zot'];
4222 * @attribute selected
4223 * @type Object
4224 * @default null
4226 selected: null,
4229 * If true, multiple selections are allowed.
4231 * @attribute multi
4232 * @type boolean
4233 * @default false
4235 multi: false,
4238 * Specifies the attribute to be used for "selected" attribute.
4240 * @attribute valueattr
4241 * @type string
4242 * @default 'name'
4244 valueattr: 'name',
4247 * Specifies the CSS class to be used to add to the selected element.
4249 * @attribute selectedClass
4250 * @type string
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
4260 * @type string
4261 * @default ''
4263 selectedProperty: '',
4266 * Specifies the attribute to set on the selected element to indicate
4267 * its active state.
4269 * @attribute selectedAttribute
4270 * @type string
4271 * @default 'active'
4273 selectedAttribute: 'active',
4276 * Returns the currently selected element. In multi-selection this returns
4277 * an array of selected elements.
4279 * @attribute selectedItem
4280 * @type Object
4281 * @default null
4283 selectedItem: null,
4286 * In single selection, this returns the model associated with the
4287 * selected element.
4289 * @attribute selectedModel
4290 * @type Object
4291 * @default null
4293 selectedModel: null,
4296 * In single selection, this returns the selected index.
4298 * @attribute selectedIndex
4299 * @type number
4300 * @default -1
4302 selectedIndex: -1,
4305 * The target element that contains items. If this is not set
4306 * core-selector is the container.
4308 * @attribute target
4309 * @type Object
4310 * @default null
4312 target: null,
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.
4318 * Example:
4320 * <core-selector target="{{$.myForm}}" itemsSelector="input[type=radio]"></core-selector>
4321 * <form id="myForm">
4322 * <label><input type="radio" name="color" value="red"> Red</label> <br>
4323 * <label><input type="radio" name="color" value="green"> Green</label> <br>
4324 * <label><input type="radio" name="color" value="blue"> Blue</label> <br>
4325 * <p>color = {{color}}</p>
4326 * </form>
4328 * @attribute itemSelector
4329 * @type string
4330 * @default ''
4332 itemsSelector: '',
4335 * The event that would be fired from the item element to indicate
4336 * it is being selected.
4338 * @attribute activateEvent
4339 * @type string
4340 * @default 'tap'
4342 activateEvent: 'tap',
4345 * Set this to true to disallow changing the selection via the
4346 * `activateEvent`.
4348 * @attribute notap
4349 * @type boolean
4350 * @default false
4352 notap: false,
4354 ready: function() {
4355 this.activateListener = this.activateHandler.bind(this);
4356 this.observer = new MutationObserver(this.updateSelected.bind(this));
4357 if (!this.target) {
4358 this.target = this;
4362 get items() {
4363 if (!this.target) {
4364 return [];
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) {
4375 if (old) {
4376 this.removeListener(old);
4377 this.observer.disconnect();
4378 this.clearSelection();
4380 if (this.target) {
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);
4395 get selection() {
4396 return this.$.selection.getSelection();
4399 selectedChanged: function() {
4400 this.updateSelected();
4403 updateSelected: function() {
4404 this.validateSelected();
4405 if (this.multi) {
4406 this.clearSelection();
4407 this.selected && this.selected.forEach(function(s) {
4408 this.valueToSelection(s);
4409 }, this);
4410 } else {
4411 this.valueToSelection(this.selected);
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() {
4424 if (this.multi) {
4425 this.selection.slice().forEach(function(s) {
4426 this.$.selection.setItemSelected(s, false);
4427 }, this);
4428 } else {
4429 this.$.selection.setItemSelected(this.selection, false);
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;
4449 } else {
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) {
4460 return i;
4463 // if no item found, the value itself is probably the index
4464 return value;
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();
4474 if (detail.item) {
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) {
4487 if (isSelected) {
4488 item.setAttribute(this.selectedAttribute, '');
4489 } else {
4490 item.removeAttribute(this.selectedAttribute);
4495 // event fired from host
4496 activateHandler: function(e) {
4497 if (!this.notap) {
4498 var i = this.findDistributedTarget(e.target, this.items);
4499 if (i >= 0) {
4500 var item = this.items[i];
4501 var s = this.valueForNode(item) || i;
4502 if (this.multi) {
4503 if (this.selected) {
4504 this.addRemoveSelected(s);
4505 } else {
4506 this.selected = [s];
4508 } else {
4509 this.selected = s;
4511 this.asyncFire('core-activate', {item: item});
4516 addRemoveSelected: function(value) {
4517 var i = this.selected.indexOf(value);
4518 if (i >= 0) {
4519 this.selected.splice(i, 1);
4520 } else {
4521 this.selected.push(value);
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);
4531 if (i >= 0) {
4532 return i;
4534 target = target.parentNode;
4540 Polymer('core-menu',{});
4544 Polymer('core-item', {
4547 * The URL of an image for the icon.
4549 * @attribute src
4550 * @type string
4551 * @default ''
4555 * Specifies the icon from the Polymer icon set.
4557 * @attribute icon
4558 * @type string
4559 * @default ''
4563 * Specifies the label for the menu item.
4565 * @attribute label
4566 * @type string
4567 * @default ''
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
4596 * @attribute url
4597 * @type String
4598 * @default ''
4602 * Class documentation extracted from the parser
4604 * @property classes
4605 * @type Array
4606 * @default []
4608 classes: [],
4611 * Files to parse for docs
4613 * @attribute sources
4614 * @type Array
4615 * @default []
4617 sources: [],
4619 ready: function() {
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() {
4633 if (this.route) {
4634 this.classes.some(function(c) {
4635 if (c.name === this.route) {
4636 this.data = c;
4637 this.route = '';
4638 return;
4640 }, this);
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', {
4674 moduleName: '',
4675 // TODO(sjmiles): needed this to force Object type for deserialization
4676 sources: [],
4678 ready: function() {
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) {
4691 name = path.pop();
4693 return name || '';