Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: third_party/polymer/components-chromium/core-component-page/core-component-page-extracted.js

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

Powered by Google App Engine
This is Rietveld 408576698