api-console-assets
Version:
This repo only exists to publish api console components to npm
367 lines (313 loc) • 10.3 kB
HTML
<!--
@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 ;
}
</style>
<content select=".markdown-html"></content>
<div id="content" class="hidden"></div>
</template>
</dom-module>
<script>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>