UNPKG

api-console-assets

Version:

This repo only exists to publish api console components to npm

367 lines (313 loc) 10.3 kB
<!-- @license Copyright (c) 2015 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="marked-import.html"> <!-- Element wrapper for the [marked](https://github.com/chjj/marked) library. `<marked-element>` accepts Markdown source and renders it to a child element with the class `markdown-html`. This child element can be styled as you would a normal DOM element. If you do not provide a child element with the `markdown-html` class, the Markdown source will still be rendered, but to a shadow DOM child that cannot be styled. ### Markdown Content The Markdown source can be specified several ways: #### Use the `markdown` attribute to bind markdown <marked-element markdown="`Markdown` is _awesome_!"> <div class="markdown-html"></div> </marked-element> #### Use `<script type="text/markdown">` element child to inline markdown <marked-element> <div class="markdown-html"></div> <script type="text/markdown"> Check out my markdown! We can even embed elements without fear of the HTML parser mucking up their textual representation: ```html <awesome-sauce> <div>Oops, I'm about to forget to close this div. </awesome-sauce> ``` </script> </marked-element> #### Use `<script type="text/markdown" src="URL">` element child to specify remote markdown <marked-element> <div class="markdown-html"></div> <script type="text/markdown" src="../guidelines.md"></script> </marked-element> Note that the `<script type="text/markdown">` approach is *static*. Changes to the script content will *not* update the rendered markdown! Though, you can data bind to the `src` attribute to change the markdown. ```html <marked-element> <div class="markdown-html"></div> <script type="text/markdown" src$="[[source]]"></script> </marked-element> ... <script> ... this.source = '../guidelines.md'; </script> ``` ### Styling If you are using a child with the `markdown-html` class, you can style it as you would a regular DOM element: .markdown-html p { color: red; } .markdown-html td:first-child { padding-left: 24px; } @element marked-element @group Molecules @hero hero.svg @demo demo/index.html --> <dom-module id="marked-element"> <template> <style> :host { display: block; } /* Thanks IE 10. */ .hidden { display: none !important; } </style> <content select=".markdown-html"></content> <div id="content" class="hidden"></div> </template> </dom-module> <script> 'use strict'; Polymer({ is: 'marked-element', properties: { /** * The markdown source that should be rendered by this element. */ markdown: { observer: 'render', type: String, value: null }, /** * Enable GFM line breaks (regular newlines instead of two spaces for breaks) */ breaks: { observer: 'render', type: Boolean, value: false }, /** * Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior. */ pedantic: { observer: 'render', type: Boolean, value: false }, /** * Function used to customize a renderer based on the [API specified in the Marked * library](https://github.com/chjj/marked#overriding-renderer-methods). * It takes one argument: a marked renderer object, which is mutated by the function. */ renderer: { observer: 'render', type: Function, value: null }, /** * Sanitize the output. Ignore any HTML that has been input. */ sanitize: { observer: 'render', type: Boolean, value: false }, /** * Use "smart" typographic punctuation for things like quotes and dashes. */ smartypants: { observer: 'render', type: Boolean, value: false }, /** * Callback function invoked by Marked after HTML has been rendered. * It must take two arguments: err and text and must return the resulting text. */ callback: { observer: 'render', type: Function, value: null }, /** * A reference to the XMLHttpRequest instance used to generate the * network request. * * @type {XMLHttpRequest} */ xhr: { type: Object, notify: true, readOnly: true } }, ready: function() { if (this.markdown) { return; } // Use the Markdown from the first `<script>` descendant whose MIME type starts with // "text/markdown". Script elements beyond the first are ignored. this._markdownElement = Polymer.dom(this).querySelector('[type="text/markdown"]'); if (!this._markdownElement) { return; } this.markdown = this._unindent(this._markdownElement.textContent); if (this._markdownElement.src) { this._request(this._markdownElement.src); } else { var observer = new MutationObserver(this._onScriptAttributeChanged .bind(this)); observer.observe(this._markdownElement, { attributes: true }) } }, /** * Renders `markdown` to HTML when the element is attached. * * This serves a dual purpose: * * * Prevents unnecessary work (no need to render when not visible). * * * `attached` fires top-down, so we can give ancestors a chance to * register listeners for the `syntax-highlight` event _before_ we render * any markdown. * */ attached: function() { this._attached = true; this._outputElement = this.outputElement; this.render(); }, detached: function() { this._attached = false; }, /** * Unindents the markdown source that will be rendered. */ unindent: function(text) { return this._unindent(text); }, get outputElement () { var child = Polymer.dom(this).queryDistributedElements('.markdown-html')[0]; if (child) return child; this.toggleClass('hidden', false, this.$.content); return this.$.content; }, /** * The `marked-render-complete` event is fired once Markdown to HTML * conversion has finished, and the DOM has been populated via the resulting * HTML. * * @event marked-render-complete */ /** * Renders `markdown` into this element's DOM. * * This is automatically called whenever the `markdown` property is changed. * * The only case where you should be calling this is if you are providing * markdown via `<script type="text/markdown">` after this element has been * constructed (or updating that markdown). */ render: function() { if (!this._attached) return; if (!this.markdown) { Polymer.dom(this._outputElement).innerHTML = ''; return; } var renderer = new marked.Renderer(); if (this.renderer) { this.renderer(renderer); } var opts = { renderer: renderer, highlight: this._highlight.bind(this), breaks: this.breaks, sanitize: this.sanitize, pedantic: this.pedantic, smartypants: this.smartypants }; Polymer.dom(this._outputElement).innerHTML = marked(this.markdown, opts, this.callback); this.fire('marked-render-complete'); }, _highlight: function(code, lang) { var event = this.fire('syntax-highlight', {code: code, lang: lang}); return event.detail.code || code; }, _unindent: function(text) { if (!text) return text; var lines = text.replace(/\t/g, ' ').split('\n'); var indent = lines.reduce(function(prev, line) { if (/^\s*$/.test(line)) return prev; // Completely ignore blank lines. var lineIndent = line.match(/^(\s*)/)[0].length; if (prev === null) return lineIndent; return lineIndent < prev ? lineIndent : prev; }, null); return lines.map(function(l) { return l.substr(indent); }).join('\n'); }, /** * Fired when the XHR finishes loading * * @event marked-loadend */ _request: function(url) { this._setXhr(new XMLHttpRequest()); var xhr = this.xhr; if (xhr.readyState > 0) { return null; } xhr.addEventListener('error', this._handleError.bind(this)); xhr.addEventListener('loadend', function(e) { var status = this.xhr.status || 0; // Note: if we are using the file:// protocol, the status code will be 0 // for all outcomes (successful or otherwise). if (status === 0 || (status >= 200 && status < 300)) { this.sanitize = true; this.markdown = e.target.response; } else { this._handleError(e); } this.fire('marked-loadend', e); }.bind(this)); xhr.open('GET', url); xhr.setRequestHeader('Accept', 'text/markdown'); xhr.send(); }, /** * Fired when an error is received while fetching remote markdown content. * * @event marked-request-error */ _handleError: function(e) { var evt = this.fire('marked-request-error', e, {cancelable: true}); if (!evt.defaultPrevented) { this.markdown = 'Failed loading markdown source'; } }, _onScriptAttributeChanged: function(mutation) { if (mutation[0].attributeName !== 'src') { return; } this._request(this._markdownElement.src); } }); </script>