Project reorganization, add editorconfig and linter

This commit is contained in:
Ludwig Schubert
2017-08-09 16:47:35 -07:00
parent 07c7527734
commit 11eda0fc49
75 changed files with 1120 additions and 17117 deletions
+42
View File
@@ -0,0 +1,42 @@
/* Static styles and other modules */
import './styles/styles';
/* Components */
import { Abstract } from './components/d-abstract';
import { Acknowledgements } from './components/d-acknowledgements';
import { Appendix } from './components/d-appendix';
import { Article } from './components/d-article';
import { Bibliography } from './components/d-bibliography';
import { Byline } from './components/d-byline';
import { Cite } from './components/d-cite';
import { Code } from './components/d-code';
import { Footnote } from './components/d-footnote';
import { FootnoteList } from './components/d-footnote-list';
import { FrontMatter } from './components/d-front-matter';
import { DMath } from './components/d-math';
import { References } from './components/d-references';
import { Title } from './components/d-title';
import { TOC } from './components/d-toc';
const components = [
Abstract, Acknowledgements, Appendix, Article, Bibliography,
Byline, Cite, Code, Footnote, FootnoteList, FrontMatter, DMath,
References, Title, TOC,
];
/* Distill website specific components */
import { DistillHeader } from './distill-components/distill-header';
import { DistillAppendix } from './distill-components/distill-appendix';
const distillComponents = [
DistillHeader, DistillAppendix,
];
function defineComponents() {
const allComponents = components.concat(distillComponents);
for (const component of allComponents) {
customElements.define(component.is, component);
}
}
defineComponents();
+18
View File
@@ -0,0 +1,18 @@
import { Template } from "../mixins/template";
import { body } from "../helpers/layout";
const T = Template("d-abstract", `
<style>
d-abstract {
display: block;
font-size: 23px;
line-height: 1.7em;
margin-bottom: 140px;
}
${body("d-abstract")}
</style>
`, false);
export class Abstract extends T(HTMLElement) {
}
+23
View File
@@ -0,0 +1,23 @@
import { Template } from "../mixins/template";
const T = Template('d-acknowledgements', `
<style>
::slotted(h3) {
font-size: 15px;
font-weight: 500;
margin-top: 20px;
margin-bottom: 0;
color: rgba(0,0,0,0.65);
line-height: 1em;
}
::slotted(*) a {
color: rgba(0, 0, 0, 0.6);
}
</style>
<slot></slot>
`);
export class Acknowledgements extends T(HTMLElement) {
}
+30
View File
@@ -0,0 +1,30 @@
import { Template } from "../mixins/template";
import { page } from "../helpers/layout";
const T = Template("d-appendix", `
<style>
:host {
display: block;
font-size: 13px;
line-height: 1.7em;
margin-bottom: 0;
border-top: 1px solid rgba(0,0,0,0.1);
color: rgba(0,0,0,0.5);
background: rgb(250, 250, 250);
padding-top: 36px;
padding-bottom: 48px;
}
${page(".l-body")}
</style>
<div class="l-body">
<slot></slot>
</div>
`);
export class Appendix extends T(HTMLElement) {
}
+26
View File
@@ -0,0 +1,26 @@
import { Template } from '../mixins/template';
import { Controller } from '../controller';
const T = Template('d-article', `
<style></style>
`, false);
export class Article extends T(HTMLElement) {
// constructor() {
// super();
// }
connectedCallback() {
for (const [functionName, callback] of Object.entries(Controller.listeners)) {
if (typeof callback === 'function') {
document.addEventListener(functionName, callback);
} else {
console.error('Controller listeners need to be functions!')
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
import { Template } from "../mixins/template";
import { Mutating } from "../mixins/mutating";
import { collectCitations } from './d-cite'
import bibtexParse from "bibtex-parse-js";
import { bibliography_cite } from "../helpers/citation";
const T = Template('d-bibliography', `
<style>
.references {
font-size: 12px;
line-height: 20px;
}
.title {
font-weight: 600;
}
ol {
padding: 0 0 0 18px;
}
li {
margin-bottom: 12px;
}
h3 {
font-size: 15px;
font-weight: 500;
margin-top: 20px;
margin-bottom: 0;
color: rgba(0,0,0,0.65);
line-height: 1em;
}
a {
color: rgba(0, 0, 0, 0.6);
}
</style>
<h3>References</h3>
<ol></ol>
`);
export function parseBibtex(bibtex) {
const bibliography = new Map();
const parsedEntries = bibtexParse.toJSON(bibtex);
for (const entry of parsedEntries) {
// normalize tags; note entryTags is an object, not Map
for (const tag in entry.entryTags) {
let value = entry.entryTags[tag];
value = value.replace(/[\t\n ]+/g, " ");
value = value.replace(/{\\["^`\.'acu~Hvs]( )?([a-zA-Z])}/g,
(full, x, char) => char);
value = value.replace(/{\\([a-zA-Z])}/g,
(full, char) => char);
entry.entryTags[tag] = value;
}
entry.entryTags.type = entry.entryType;
// add to bibliography
bibliography.set(entry.citationKey, entry.entryTags);
}
return bibliography;
}
export class Bibliography extends T(HTMLElement) {
constructor() {
super()
// set up mutation observer
const options = {childList: true, subtree: true};
const observer = new MutationObserver( (mutations) => {
observer.disconnect();
this.parseIfPossible();
observer.observe(this, options);
});
// ...and listen for changes
observer.observe(this, options);
}
parseIfPossible() {
if (this.firstElementChild && this.firstElementChild.tagName === 'SCRIPT') {
const newBibtex = this.firstElementChild.textContent;
if (this.bibtex !== newBibtex) {
this.bibtex = newBibtex;
const bibliography = parseBibtex(this.bibtex);
this.notify(bibliography);
}
}
};
connectedCallback() {
this.list = this.root.querySelector('ol');
// bibliography is initially hidden
this.root.host.style.display = 'none';
// this.parseIfPossible();
// Store.subscribeTo('citations', (citations) => {
// // ensure citations list is visible
// this.root.host.style.display = 'initial';
// this.list.innerHTML = '';
// for (const key of citations) {
// const bibliography = Store.get('bibliography');
// const entry = bibliography.get(key);
// // construct and append list item to show citation
// const listItem = document.createElement('li');
// listItem.id = key;
// listItem.innerHTML = bibliography_cite(entry);
// this.list.appendChild(listItem);
// }
// });
}
notify(bibliography) {
const options = { detail: bibliography, bubbles: true };
const event = new CustomEvent('onBibliographyChanged', options);
this.dispatchEvent(event);
}
set entries(newEntries) {
this.root.host.style.display = 'initial';
this.list.innerHTML = '';
for (const [key, entry] of newEntries) {
const listItem = document.createElement('li');
listItem.id = key;
listItem.innerHTML = bibliography_cite(entry);
this.list.appendChild(listItem);
}
}
renderContent() {
// compute and store bibliography
// FrontMatter.bibliography = parseBibtex(this.bibtex);
// this.notify();
// Store.set('bibliography', bibliography);
// compute and store citations
// const citations = collectCitations();
// Store.set('citations', citations);
}
}
+149
View File
@@ -0,0 +1,149 @@
import { Template } from "../mixins/template";
import { page } from "../helpers/layout";
const T = Template("d-byline", `
<style>
d-byline {
box-sizing: border-box;
font-size: 13px;
line-height: 20px;
display: block;
/* border-top: 1px solid rgba(0, 0, 0, 0.1);*/
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1);*/
color: rgba(0, 0, 0, 0.6);
padding-top: 20px;
padding-bottom: 20px;
}
${page(".byline")}
d-article.centered {
text-align: center;
}
a,
d-article a {
color: rgba(0, 0, 0, 0.8);
text-decoration: none;
border-bottom: none;
}
d-article a:hover {
text-decoration: underline;
border-bottom: none;
}
.authors {
text-align: left;
}
.name {
font-weight: 600;
display: inline;
text-transform: uppercase;
}
.affiliation {
display: inline;
}
.date {
display: block;
text-align: left;
}
.year, .month {
display: inline;
}
.citation {
display: block;
text-align: left;
}
.citation div {
display: inline;
}
@media(min-width: 1080px) {
d-byline {
border-bottom: none;
}
a:hover {
color: rgba(0, 0, 0, 0.9);
}
.authors {
display: inline-block;
}
.author {
display: inline-block;
margin-right: 12px;
/*padding-left: 20px;*/
/*border-left: 1px solid #ddd;*/
}
.affiliation {
display: block;
}
.author:last-child {
margin-right: 0;
}
.name {
display: block;
}
.date {
border-left: 1px solid rgba(0, 0, 0, 0.1);
padding-left: 15px;
margin-left: 15px;
display: inline-block;
}
.year, .month {
display: block;
}
.citation {
border-left: 1px solid rgba(0, 0, 0, 0.15);
padding-left: 15px;
margin-left: 15px;
display: inline-block;
}
.citation div {
display: block;
}
}
</style>
<div class='byline'>
</div>
`, false);
export function bylineTemplate(frontMatter) {
return `
<div class="authors">
${frontMatter.authors.map( author => `<div class="author">
${author.personalURL ?
`<a class="name" href="${author.personalURL}">${author.name}</a>`
:
`<div class="name">${author.name}</div>`
}
${author.affiliationURL ?
`<a class="affiliation" href="${author.affiliationURL}">${author.affiliation}</a>`
:
`<div class="affiliation">${author.affiliation}</div>`
}
</div>`).join('\n')}
</div>
<div class="date">
<div class="month">${frontMatter.publishedMonth}. ${frontMatter.publishedDay}</div>
<div class="year">${frontMatter.publishedYear}</div>
</div>
<a class="citation" href="#citation">
<div>Citation:</div>
<div>${frontMatter.concatenatedAuthors}, ${frontMatter.publishedYear}</div>
</a>
`;
}
export class Byline extends T(HTMLElement) {
set frontMatter(frontMatter) {
const container = this.querySelector('.byline');
container.innerHTML = bylineTemplate(frontMatter);
}
}
+156
View File
@@ -0,0 +1,156 @@
import { Template } from "../mixins/template";
import { hover_cite } from "../helpers/citation";
import { HoverBox } from "../helpers/hover-box";
const T = Template('d-cite', `
<style>
.citation {
color: hsla(206, 90%, 20%, 0.7);
}
.citation-number {
cursor: default;
white-space: nowrap;
font-family: -apple-system, BlinkMacSystemFont, "Roboto", Helvetica, sans-serif;
font-size: 75%;
color: hsla(206, 90%, 20%, 0.7);
display: inline-block;
line-height: 1.1em;
text-align: center;
position: relative;
top: -2px;
margin: 0 2px;
}
figcaption .citation-number {
font-size: 11px;
font-weight: normal;
top: -2px;
line-height: 1em;
}
</style>
<div style="display: none;" id="hover-box" class="dt-hover-box">
</div>
<span id="citation-" class="citation">
<slot></slot>
<span class="citation-number"></span>
</span>
`);
export function collectCitations() {
const citations = new Set();
const citeTags = document.querySelectorAll('d-cite');
for (const tag of citeTags) {
const keys = tag.getAttribute('key').split(',');
for (const key of keys) {
citations.add(key);
}
}
return [...citations];
}
export class Cite extends T(HTMLElement) {
/* Lifecycle */
constructor() {
super()
// Cite.currentId += 1;
// this.citeId = Cite.currentId;
}
connectedCallback() {
// this.notify();
this.hoverDiv = this.root.querySelector('.dt-hover-box');
this.outerSpan = this.root.querySelector('#citation-');
this.innerSpan = this.root.querySelector('.citation-number');
// this.outerSpan.id = `citation-${this.citeId}`;
// this.hoverDiv.id = `dt-cite-hover-box-${this.citeId}`;
HoverBox.get_box(this.hoverDiv).bind(this.outerSpan);
}
/* Observed Attributes */
// renderCitationNumbers(citations) {
// const numbers = this._keys.map( (key) => {
// const index = citations.indexOf(key);
// return index == -1 ? '?' : index + 1 + '';
// });
// const text = "[" + numbers.join(", ") + "]";
// this.innerSpan.textContent = text;
// }
/* observe 'key' attribute */
static get observedAttributes() {
return ['key'];
}
attributeChangedCallback(name, oldValue, newValue) {
const eventName = oldValue ? 'onCiteKeyChanged' : 'onCiteKeyCreated';
const keys = newValue.split(',');
const options = { detail: [this, keys], bubbles: true };
const event = new CustomEvent(eventName, options);
document.dispatchEvent(event);
}
set key(value) {
this.setAttribute('key', value);
}
get key() {
return this.getAttribute('key');
}
get keys() {
return this.getAttribute('key').split(',');
}
/* Setters & Rendering */
set numbers(numbers) {
const numberStrings = numbers.map( index => {
return index == -1 ? '?' : index + 1 + '';
});
const textContent = "[" + numberStrings.join(", ") + "]";
const innerSpan = this.root.querySelector('.citation-number');
innerSpan.textContent = textContent;
}
set entries(entries) {
const div = this.root.querySelector('#hover-box');
div.innerHTML = entries.map(hover_cite).join('<br><br>');
}
// renderContent() {
// const bibliography = document.querySelector('d-bibliography');
// if (bibliography && bibliography.finishedLoading) {
// customElements.whenDefined('d-bibliography').then( () => {
// const keys = this.key.split(",");
//
// // set up hidden hover box
// const div = this.root.querySelector('.dt-hover-box');
// div.innerHTML = keys.map( (key) => {
// return bibliography.getEntry(key);
// }).map(hover_cite).join('<br><br>');
// div.id ='dt-cite-hover-box-' + this.citeId;
//
// // set up visible citation marker
// const outerSpan = this.root.querySelector('#citation-');
// outerSpan.id = `citation-${this.citeId}`;
// // outerSpan.setAttribute('data-hover', dataHoverString); // directly tell HoverBox instead?
// const innerSpan = this.root.querySelector('.citation-number');
// innerSpan.textContent = inline_cite_short(keys, bibliography);
//
// HoverBox.get_box(div).bind(outerSpan);
// });
// } else {
// console.error(`You used a d-cite tag (${key}) without including a d-bibliography tag in your article. We can't lookup your citation this way.`)
// }
// }
}
+74
View File
@@ -0,0 +1,74 @@
import Prism from "prismjs";
import "prismjs/components/prism-python";
import "prismjs/components/prism-clike";
import css from "prismjs/themes/prism.css";
import { Template } from "../mixins/template.js"
import { Mutating } from "../mixins/mutating.js"
const T = Template("d-code", `
<style>
code {
white-space: nowrap;
background: rgba(0, 0, 0, 0.04);
border-radius: 2px;
padding: 4px 7px;
font-size: 15px;
color: rgba(0, 0, 0, 0.6);
}
pre code {
display: block;
background: white;
border-left: 3px solid rgba(0, 0, 0, 0.05);
padding: 0 0 0 24px;
}
${css}
</style>
<code id="code-container"></code>
`);
export class Code extends Mutating(T(HTMLElement)) {
renderContent() {
// check if language can be highlighted
this.languageName = this.getAttribute('language');
if (!this.languageName) {
console.warn(`You need to provide a language attribute to your <d-code> block to let us know how to highlight your code; e.g.:\n <d-code language="python">zeros = np.zeros(shape)</d-code>.`);
return;
}
const language = Prism.languages[this.languageName];
if (language == undefined) {
console.warn(`Distill does not yet support highlighting your code block in "${this.languageName}".`);
return;
}
let content = this.textContent;
const codeTag = this.shadowRoot.querySelector("#code-container");
if (this.hasAttribute("block")) {
// normalize the tab indents
content = content.replace(/\n/, "");
const tabs = content.match(/\s*/);
content = content.replace(new RegExp("\n" + tabs, "g"), "\n");
content = content.trim();
// wrap code block in pre tag if needed
if (codeTag.parentNode instanceof ShadowRoot) {
const preTag = document.createElement("pre");
this.shadowRoot.removeChild(codeTag)
preTag.appendChild(codeTag);
this.shadowRoot.appendChild(preTag);
}
}
codeTag.className = `language-${this.languageName}`;
codeTag.innerHTML = Prism.highlight(content, language);
}
}
+74
View File
@@ -0,0 +1,74 @@
import { Template } from '../mixins/template';
const T = Template('d-footnote-list', `
<style>
ol {
padding: 0 0 0 18px;
}
li {
margin-bottom: 12px;
}
h3 {
font-size: 15px;
font-weight: 500;
margin-top: 20px;
margin-bottom: 0;
color: rgba(0,0,0,0.65);
line-height: 1em;
}
a {
color: rgba(0, 0, 0, 0.6);
}
a.footnote-backlink {
color: rgba(0,0,0,0.3);
padding-left: 0.5em;
}
</style>
<h3>Footnotes</h3>
<ol></ol>
`);
export class FootnoteList extends T(HTMLElement) {
connectedCallback() {
this.list = this.root.querySelector('ol');
// footnotes list is initially hidden
this.root.host.style.display = 'none';
// look through document and register existing footnotes
// Store.subscribeTo('footnotes', (footnote) => {
// this.renderFootnote(footnote);
// });
}
// TODO: could optimize this to accept individual footnotes?
set footnotes(footnotes) {
this.list.innerHTML = '';
if (footnotes.length) {
// ensure footnote list is visible
this.root.host.style.display = 'initial';
for (const footnote of footnotes) {
// construct and append list item to show footnote
const listItem = document.createElement('li');
listItem.id = footnote.id + '-listing';
listItem.innerHTML = footnote.innerHTML;
const backlink = document.createElement('a');
backlink.setAttribute('class', 'footnote-backlink');
backlink.textContent = '[↩]';
backlink.href = '#' + footnote.id;
listItem.appendChild(backlink);
this.list.appendChild(listItem);
}
} else {
// ensure footnote list is invisible
this.shadowRoot.host.style.display = 'none';
}
}
}
+63
View File
@@ -0,0 +1,63 @@
import { Template } from "../mixins/template.js";
import { HoverBox } from "../helpers/hover-box";
// import { Store } from './store';
const T = Template("d-footnote", `
<style>
d-math[block] {
display: block;
}
</style>
<div style="display: none;" class="dt-hover-box">
<slot id='slot'></slot>
</div>
<sup><span id="fn-" data-hover-ref="" style="cursor:pointer"></span></sup>
`);
export class Footnote extends T(HTMLElement) {
constructor() {
super();
const options = {childList: true, characterData: true, subtree: true};
const observer = new MutationObserver(this.notify);
observer.observe(this, options);
}
notify() {
const options = { detail: this, bubbles: true };
const event = new CustomEvent('onFootnoteChanged', options);
document.dispatchEvent(event);
}
connectedCallback() {
// listen and notify about changes to slotted content
// const slot = this.shadowRoot.querySelector('#slot');
// slot.addEventListener('slotchange', this.notify);
// create numeric ID
Footnote.currentFootnoteId += 1;
const IdString = Footnote.currentFootnoteId.toString();
this.root.host.id = 'd-footnote-' + IdString;
// set up hidden hover box
const div = this.root.querySelector('.dt-hover-box');
div.id = 'dt-fn-hover-box-' + IdString;
// set up visible footnote marker
const span = this.root.querySelector('#fn-');
span.setAttribute('id', 'fn-' + IdString);
span.setAttribute('data-hover-ref', div.id);
span.textContent = IdString;
HoverBox.get_box(div).bind(span);
}
}
Footnote.currentFootnoteId = 0;
+36
View File
@@ -0,0 +1,36 @@
import ymlParse from "js-yaml";
export class FrontMatter extends HTMLElement {
static get is() { return "d-front-matter"; }
constructor() {
super();
const options = {childList: true, characterData: true, subtree: true};
const observer = new MutationObserver( (mutation) => {
const data = this.parse();
this.notify(data);
});
observer.observe(this, options);
}
parse(){
const scriptTag = this.querySelector("script");
if (scriptTag) {
const yml = scriptTag.textContent;
const data = ymlParse.safeLoad(yml);
return data;
} else {
console.error('You added a frontmatter tag but did not provide a script tag with front matter data in it. Please take a look at our templates.')
return {};
}
}
notify(data) {
const options = { detail: data, bubbles: true };
const event = new CustomEvent('onFrontMatterChanged', options);
document.dispatchEvent(event);
}
}
+28
View File
@@ -0,0 +1,28 @@
import katex from "katex";
import { Mutating } from "../mixins/mutating.js"
import { Template } from "../mixins/template.js"
import katexCSS from "../../node_modules/katex/dist/katex.min.css"
const T = Template("d-math", `
<style>
d-math[block] {
display: block;
}
${katexCSS}
</style>
<span id="katex-container"></span>
`);
export class DMath extends Mutating(T(HTMLElement)) {
renderContent() {
const options = { displayMode: this.hasAttribute("block") };
const container = this.root.querySelector("#katex-container");
katex.render(this.textContent, container, options);
}
}
+18
View File
@@ -0,0 +1,18 @@
import { Template } from "../mixins/template";
import { body } from "../helpers/layout";
const T = Template("d-references", `
<style>
d-references {
display: block;
}
</style>
`, false);
export class References extends T(HTMLElement) {
connectedCallback() {
super.connectedCallback();
}
}
+36
View File
@@ -0,0 +1,36 @@
import { Template } from "../mixins/template";
import { page } from "../helpers/layout";
const T = Template("d-title", `
<style>
:host {
box-sizing: border-box;
display: block;
width: 100%;
margin-bottom: 100px;
}
::slotted(h1) {
padding-top: 140px;
padding-bottom: 24px;
margin: 0;
line-height: 1em;
font-size: 48px;
font-weight: 600;
}
d-byline {
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
${page("::slotted(h1), ::slotted(h2)")}
</style>
<slot></slot>
`);
export class Title extends T(HTMLElement) {
}
+13
View File
@@ -0,0 +1,13 @@
import {Template} from "../mixins/template";
const T = Template("d-toc", `
<style>
d-toc {
display: block;
}
</style>
`, false);
export class TOC extends T(HTMLElement) {
}
+134
View File
@@ -0,0 +1,134 @@
import { FrontMatter } from './front-matter';
import { collectCitations } from './components/d-cite';
const frontMatter = new FrontMatter();
export const Controller = {
frontMatter: frontMatter,
waitingOn: {
bibliography: [],
citations: [],
},
listeners: {
onCiteKeyCreated(event) {
const [citeTag, keys] = event.detail;
// ensure we have citations
if (frontMatter.citations.length === 0) {
// console.debug('onCiteKeyCreated, but unresolved dependency ("citations"). Enqueing.');
Controller.waitingOn.citations.push(() => Controller.listeners.onCiteKeyCreated(event));
return;
}
// ensure we have a loaded bibliography
if (frontMatter.bibliography.size === 0) {
// console.debug('onCiteKeyCreated, but unresolved dependency ("bibliography"). Enqueing.');
Controller.waitingOn.bibliography.push(() => Controller.listeners.onCiteKeyCreated(event));
return;
}
const numbers = keys.map( key => frontMatter.citations.indexOf(key) );
citeTag.numbers = numbers;
const entries = keys.map( key => frontMatter.bibliography.get(key) );
citeTag.entries = entries;
},
onCiteKeyChanged(event) {
const [citeTag, keys] = event.detail;
// update citations
frontMatter.citations = collectCitations();
for (const waitingCallback of Controller.waitingOn.citations) {
waitingCallback();
}
// update bibliography
const bibliographyTag = document.querySelector('d-bibliography');
const bibliographyEntries = new Map(frontMatter.citations.map( citationKey => {
return [citationKey, frontMatter.bibliography.get(citationKey)];
}));
bibliographyTag.entries = bibliographyEntries;
const citeTags = document.querySelectorAll('d-cite');
for (const citeTag of citeTags) {
const keys = citeTag.keys;
const numbers = keys.map( key => frontMatter.citations.indexOf(key) );
citeTag.numbers = numbers;
const entries = keys.map( key => frontMatter.bibliography.get(key) );
citeTag.entries = entries;
}
const numbers = keys.map( key => frontMatter.citations.indexOf(key) );
citeTag.numbers = numbers;
const entries = keys.map( key => frontMatter.bibliography.get(key) );
citeTag.entries = entries;
},
onBibliographyChanged(event) {
const bibliographyTag = event.target;
const bibliography = event.detail;
frontMatter.bibliography = bibliography;
for (const waitingCallback of Controller.waitingOn.bibliography) {
waitingCallback();
}
// ensure we have citations
if (frontMatter.citations.length === 0) {
// console.debug('onBibliographyChanged, but unresolved dependency ("citations"). Enqueing.');
Controller.waitingOn.citations.push(() => Controller.listeners.onBibliographyChanged(event));
return;
}
const entries = new Map(frontMatter.citations.map( citationKey => {
return [citationKey, frontMatter.bibliography.get(citationKey)];
}));
bibliographyTag.entries = entries;
},
onFootnoteChanged() {
// const footnote = event.detail;
//TODO: optimize to only update current footnote
const footnotesList = document.querySelector('d-footnote-list');
if (footnotesList) {
const footnotes = document.querySelectorAll('d-footnote');
footnotesList.footnotes = footnotes;
}
},
onFrontMatterChanged(event) {
const data = event.detail;
frontMatter.mergeFromYMLFrontmatter(data);
const appendix = document.querySelector('distill-appendix');
appendix.frontMatter = frontMatter;
const byline = document.querySelector('d-byline');
byline.frontMatter = frontMatter;
},
DOMContentLoaded() {
// console.debug('DOMContentLoaded.');
const frontMatterTag = document.querySelector('d-front-matter');
const data = frontMatterTag.parse();
Controller.listeners.onFrontMatterChanged({detail: data});
// console.debug('Resolving "citations" dependency due to initial DOM load.');
frontMatter.citations = collectCitations();
for (const waitingCallback of Controller.waitingOn.citations) {
waitingCallback();
}
const footnotesList = document.querySelector('d-footnote-list');
if (footnotesList) {
const footnotes = document.querySelectorAll('d-footnote');
footnotesList.footnotes = footnotes;
}
}
}, // listeners
}; // Controller
@@ -0,0 +1,66 @@
const styles = `
<style>
distill-appendix h3 {
font-size: 15px;
font-weight: 500;
margin-top: 20px;
margin-bottom: 0;
color: rgba(0,0,0,0.65);
line-height: 1em;
}
distill-appendix a {
color: rgba(0, 0, 0, 0.6);
}
distill-appendix ol,
distill-appendix ul {
padding-left: 24px;
}
distill-appendix .citation {
font-size: 11px;
line-height: 15px;
border-left: 1px solid rgba(0, 0, 0, 0.1);
padding-left: 18px;
border: 1px solid rgba(0,0,0,0.1);
background: rgba(0, 0, 0, 0.02);
padding: 10px 18px;
border-radius: 3px;
color: rgba(150, 150, 150, 1);
overflow: hidden;
margin-top: -12px;
}
</style>
`
export function appendixTemplate(frontMatter) {
return `
${styles}
<h3 id="updates-and-corrections">Updates and Corrections</h3>
<p><a href="">View all changes</a> to this article since it was first published. If you see mistakes or want to suggest changes, please <a href="${frontMatter.githubUrl + '/issues/new'}">create an issue on GitHub</a>. </p>
<h3 id="reuse">Reuse</h3>
<p>Diagrams and text are licensed under Creative Commons Attribution <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY 2.0</a> with the <a class="github" href="${frontMatter.githubUrl}">source available on GitHub</a>, unless noted otherwise. The figures that have been reused from other sources dont fall under this license and can be recognized by a note in their caption: “Figure from …”.</p>
<h3 id="citation">Citation</h3>
<p>For attribution in academic contexts, please cite this work as</p>
<pre class="citation short">${frontMatter.concatenatedAuthors}, "${frontMatter.title}", Distill, ${frontMatter.publishedYear}.</pre>
<p>BibTeX citation</p>
<pre class="citation long">@article{${frontMatter.slug},
author = {${frontMatter.bibtexAuthors}},
title = {${frontMatter.title}},
journal = {Distill},
year = {${frontMatter.publishedYear}},
note = {${frontMatter.url}}
}</pre>
`;
}
export class DistillAppendix extends HTMLElement {
static get is() { return "distill-appendix"; }
set frontMatter(frontMatter) {
this.innerHTML = appendixTemplate(frontMatter);
}
}
+91
View File
@@ -0,0 +1,91 @@
import {Template} from "../mixins/template";
// import logo from "./distill-logo.svg";
var logo = "";
const T = Template("distill-header", `
<style>
:host {
box-sizing: border-box;
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
background-color: hsl(200, 60%, 15%);
z-index: ${1e6};
color: rgba(0, 0, 0, 0.8);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
}
.content {
height: 70px;
}
a {
font-size: 16px;
height: 60px;
line-height: 60px;
text-decoration: none;
color: rgba(255, 255, 255, 0.8);
padding: 22px 0;
}
a:hover {
color: rgba(255, 255, 255, 1);
}
svg {
width: 24px;
position: relative;
top: 4px;
margin-right: 2px;
}
@media(min-width: 1080px) {
:host {
height: 70px;
}
a {
height: 70px;
line-height: 70px;
padding: 28px 0;
}
.logo {
}
}
svg path {
fill: none;
stroke: rgba(255, 255, 255, 0.8);
stroke-width: 3px;
}
.logo {
font-size: 17px;
font-weight: 200;
}
.nav {
float: right;
font-weight: 300;
}
.nav a {
font-size: 12px;
margin-left: 24px;
text-transform: uppercase;
}
</style>
<div class="content l-page">
<a href="/" class="logo">
${logo}
Distill
</a>
<div class="nav">
<a href="/faq">About</a>
<a href="https://github.com/distillpub">GitHub</a>
<!-- https://twitter.com/distillpub -->
</div>
</div>
`);
export class DistillHeader extends T(HTMLElement) {
static get is() {
return "distill-header";
}
}
+220
View File
@@ -0,0 +1,220 @@
import {timeFormat} from "d3-time-format";
const zeroPad = n => n < 10 ? "0" + n : n;
const RFC = timeFormat("%a, %d %b %Y %H:%M:%S %Z");
const months = ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"];
class Author {
constructor(name='', personalURL='', affiliation='', affiliationURL='') {
this.name = name; // "Chris Olah"
this.personalURL = personalURL; // "https://colah.github.io"
this.affiliation = affiliation; // "Google Brain"
this.affiliationURL = affiliationURL; // "https://g.co/brain"
}
// "Chris"
get firstName() {
const names = this.name.split(" ");
return names.slice(0, names.length - 1).join(" ");
}
// "Olah"
get lastName() {
const names = this.name.split(" ");
return names[names.length -1];
}
}
export class FrontMatter {
constructor() {
this.title = ""; // "Attention and Augmented Recurrent Neural Networks"
this.description = ""; // "A visual overview of neural attention..."
this.authors = []; // Array of Author(s)
this.bibliography = new Map();
// {
// "gregor2015draw": {
// "title": "DRAW: A recurrent neural network for image generation",
// "author": "Gregor, Karol and Danihelka, Ivo and Graves, Alex and Rezende, Danilo Jimenez and Wierstra, Daan",
// "journal": "arXiv preprint arXiv:1502.04623",
// "year": "2015",
// "url": "https://arxiv.org/pdf/1502.04623.pdf",
// "type": "article"
// },
// }
// Citation keys should be listed in the order that they are appear in the document.
// Each key refers to a key in the bibliography dictionary.
this.citations = []; // [ "gregor2015draw", "mercier2011humans" ]
//
// Assigned from posts.csv
//
// publishedDate: 2016-09-08T07:00:00.000Z,
// tags: [ 'rnn' ],
// distillPath: '2016/augmented-rnns',
// githubPath: 'distillpub/post--augmented-rnns',
// doiSuffix: 1,
//
// Assigned from journal
//
this.journal = {};
// journal: {
// "title": "Distill",
// "full_title": "Distill",
// "abbrev_title": "Distill",
// "url": "http://distill.pub",
// "doi": "10.23915/distill",
// "publisherName": "Distill Working Group",
// "publisherEmail": "admin@distill.pub",
// "issn": "2476-0757",
// "editors": [...],
// "committee": [...]
// }
// volume: 1,
// issue: 9,
//
// Assigned from publishing process
//
// githubCompareUpdatesUrl: 'https://github.com/distillpub/post--augmented-rnns/compare/1596e094d8943d2dc0ea445d92071129c6419c59...3bd9209e0c24d020f87cf6152dcecc6017cbc193',
// updatedDate: 2017-03-21T07:13:16.000Z,
// doi: '10.23915/distill.00001',
}
// Example:
// title: Demo Title Attention and Augmented Recurrent Neural Networks
// published: Jan 10, 2017
// authors:
// - Chris Olah:
// - Shan Carter: http://shancarter.com
// affiliations:
// - Google Brain:
// - Google Brain: http://g.co/brain
mergeFromYMLFrontmatter(data) {
this.title = data.title;
this.publishedDate = new Date(data.published);
this.description = data.description;
const zipped = data.authors.map( (author, index) => [author, data.affiliations[index]]);
this.authors = zipped.map( ([authorEntry, affiliationEntry]) => {
const name = Object.keys(authorEntry)[0];
const author = new Author(name);
if (typeof authorEntry === 'object') {
author.personalURL = authorEntry[name];
}
author.affiliation = Object.keys(affiliationEntry)[0];
if (typeof affiliationEntry === 'object') {
author.affiliationURL = affiliationEntry[author.affiliation]
}
return author;
});
}
//
// Computed Properties
//
// 'http://distill.pub/2016/augmented-rnns',
set url(value) {
this._url = value;
}
get url() {
if (this._url) {
return this._url
} else if (this.distillPath && this.journal.url) {
return this.journal.url + "/" + this.distillPath;
} else if (this.journal.url) {
return this.journal.url;
}
}
// 'https://github.com/distillpub/post--augmented-rnns',
get githubUrl() {
return "https://github.com/" + this.githubPath;
}
// TODO resolve differences in naming of URL/Url/url.
// 'http://distill.pub/2016/augmented-rnns/thumbnail.jpg',
set previewURL(value) {
this._previewURL = value;
}
get previewURL() {
return this._previewURL ? this._previewURL : this.url + "/thumbnail.jpg";
}
// 'Thu, 08 Sep 2016 00:00:00 -0700',
get publishedDateRFC() {
return RFC(this.publishedDate);
}
// 'Thu, 08 Sep 2016 00:00:00 -0700',
get updatedDateRFC() {
return RFC(this.updatedDate);
}
// 2016,
get publishedYear() {
return this.publishedDate.getFullYear();
}
// 'Sept',
get publishedMonth() {
return months[this.publishedDate.getMonth()];
}
// 8,
get publishedDay() {
return this.publishedDate.getDate();
}
// '09',
get publishedMonthPadded() {
return zeroPad(this.publishedDate.getMonth() + 1);
}
// '08',
get publishedDayPadded() {
return zeroPad(this.publishedDate.getDate());
}
// 'Tue, 21 Mar 2017 00:13:16 -0700',
get updatedDateRFC() {
}
// 'Olah & Carter',
get concatenatedAuthors() {
if (this.authors.length > 2) {
return this.authors[0].lastName + ", et al.";
} else if (this.authors.length === 2) {
return this.authors[0].lastName + " & " + this.authors[1].lastName;
} else if (this.authors.length === 1) {
return this.authors[0].lastName;
}
}
// 'Olah, Chris and Carter, Shan',
get bibtexAuthors() {
return this.authors.map(author => {
return author.lastName + ", " + author.firstName
}).join(" and ");
}
// 'olah2016attention'
get slug() {
let slug = '';
if (this.authors.length) {
slug += this.authors[0].lastName.toLowerCase();
slug += this.publishedYear;
slug += this.title.split(' ')[0].toLowerCase();
}
return slug || 'Untitled';
}
}
+165
View File
@@ -0,0 +1,165 @@
export function inline_cite_short(keys){
function cite_string(key){
if (key in data.bibliography){
var n = data.citations.indexOf(key)+1;
return ""+n;
} else {
return "?";
}
}
return "["+keys.map(cite_string).join(", ")+"]";
}
export function inline_cite_long(keys){
function cite_string(key){
if (key in data.bibliography){
var ent = data.bibliography[key];
var names = ent.author.split(" and ");
names = names.map(name => name.split(",")[0].trim())
var year = ent.year;
if (names.length == 1) return names[0] + ", " + year;
if (names.length == 2) return names[0] + " & " + names[1] + ", " + year;
if (names.length > 2) return names[0] + ", et al., " + year;
} else {
return "?";
}
}
return keys.map(cite_string).join(", ");
}
function author_string(ent, template, sep, finalSep){
var names = ent.author.split(" and ");
let name_strings = names.map(name => {
name = name.trim();
if (name.indexOf(",") != -1){
var last = name.split(",")[0].trim();
var firsts = name.split(",")[1];
} else {
var last = name.split(" ").slice(-1)[0].trim();
var firsts = name.split(" ").slice(0,-1).join(" ");
}
var initials = "";
if (firsts != undefined) {
initials = firsts.trim().split(" ").map(s => s.trim()[0]);
initials = initials.join(".")+".";
}
return template.replace("${F}", firsts)
.replace("${L}", last)
.replace("${I}", initials);
});
if (names.length > 1) {
var str = name_strings.slice(0, names.length-1).join(sep);
str += (finalSep || sep) + name_strings[names.length-1];
return str;
} else {
return name_strings[0];
}
}
function venue_string(ent) {
var cite = (ent.journal || ent.booktitle || "")
if ("volume" in ent){
var issue = ent.issue || ent.number;
issue = (issue != undefined)? "("+issue+")" : "";
cite += ", Vol " + ent.volume + issue;
}
if ("pages" in ent){
cite += ", pp. " + ent.pages
}
if (cite != "") cite += ". "
if ("publisher" in ent){
cite += ent.publisher;
if (cite[cite.length-1] != ".") cite += ".";
}
return cite;
}
function link_string(ent){
if ("url" in ent){
var url = ent.url;
var arxiv_match = (/arxiv\.org\/abs\/([0-9\.]*)/).exec(url);
if (arxiv_match != null){
url = `http://arxiv.org/pdf/${arxiv_match[1]}.pdf`;
}
if (url.slice(-4) == ".pdf"){
var label = "PDF";
} else if (url.slice(-5) == ".html") {
var label = "HTML";
}
return ` &ensp;<a href="${url}">[${label||"link"}]</a>`;
}/* else if ("doi" in ent){
return ` &ensp;<a href="https://doi.org/${ent.doi}" >[DOI]</a>`;
}*/ else {
return "";
}
}
function doi_string(ent, new_line){
if ("doi" in ent) {
return `${new_line?"<br>":""} <a href="https://doi.org/${ent.doi}" style="text-decoration:inherit;">DOI: ${ent.doi}</a>`;
} else {
return "";
}
}
export function bibliography_cite(ent, fancy){
if (ent){
var cite = "<b>" + ent.title + "</b> "
cite += link_string(ent) + "<br>";
cite += author_string(ent, "${L}, ${I}", ", ", " and ");
if (ent.year || ent.date){
cite += ", " + (ent.year || ent.date) + ". "
} else {
cite += ". "
}
cite += venue_string(ent);
cite += doi_string(ent);
return cite
/*var cite = author_string(ent, "${L}, ${I}", ", ", " and ");
if (ent.year || ent.date){
cite += ", " + (ent.year || ent.date) + ". "
} else {
cite += ". "
}
cite += "<b>" + ent.title + "</b>. ";
cite += venue_string(ent);
cite += doi_string(ent);
cite += link_string(ent);
return cite*/
} else {
return "?";
}
}
export function hover_cite(ent){
if (ent){
var cite = "";
cite += "<b>" + ent.title + "</b>";
cite += link_string(ent);
cite += "<br>"
var a_str = author_string(ent, "${I} ${L}", ", ") + ".";
var v_str = venue_string(ent).trim() + " " + ent.year + ". " + doi_string(ent, true);
if ((a_str+v_str).length < Math.min(40, ent.title.length)) {
cite += a_str + " " + v_str;
} else {
cite += a_str + "<br>" + v_str;
}
return cite;
} else {
return "?";
}
}
//https://scholar.google.com/scholar?q=allintitle%3ADocument+author%3Aolah
function get_GS_URL(ent){
if (ent){
var names = ent.author.split(" and ");
names = names.map(name => name.split(",")[0].trim())
var title = ent.title.split(" ")//.replace(/[,:]/, "")
var url = "http://search.labs.crossref.org/dois?"//""https://scholar.google.com/scholar?"
url += uris({q: names.join(" ") + " " + title.join(" ")})
}
}
+112
View File
@@ -0,0 +1,112 @@
function make_hover_css(pos) {
var pretty = window.innerWidth > 600;
var padding = pretty? 18 : 12;
var outer_padding = pretty ? 18 : 0;
var bbox = document.querySelector("body").getBoundingClientRect();
var left = pos[0] - bbox.left, top = pos[1] - bbox.top;
var width = Math.min(window.innerWidth-2*outer_padding, 648);
left = Math.min(left, window.innerWidth-width-outer_padding);
width = width - 2*padding;
return (`position: absolute;
background-color: #FFF;
opacity: 0.95;
max-width: ${width}px;
top: ${top}px;
left: ${left}px;
border: 1px solid rgba(0, 0, 0, 0.25);
padding: ${padding}px;
border-radius: ${pretty? 3 : 0}px;
box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.2);
z-index: ${1e6};`);
}
export class HoverBox {
constructor(div) {
this.div = div;
this.visible = false;
this.bindDivEvents(div);
HoverBox.box_map[div.id] = this;
}
}
HoverBox.box_map = {};
HoverBox.get_box = function get_box(div_id) {
if (div_id in HoverBox.box_map) {
return HoverBox.box_map[div_id];
} else {
return new HoverBox(div_id);
}
}
HoverBox.prototype.show = function show(pos){
this.visible = true;
this.div.setAttribute("style", make_hover_css(pos) );
for (var box_id in HoverBox.box_map) {
var box = HoverBox.box_map[box_id];
if (box != this) box.hide();
}
}
HoverBox.prototype.showAtNode = function showAtNode(node){
var bbox = node.getBoundingClientRect();
this.show([bbox.right, bbox.bottom]);
}
HoverBox.prototype.hide = function hide(){
this.visible = false;
if (this.div) this.div.setAttribute("style", "display:none");
if (this.timeout) clearTimeout(this.timeout);
}
HoverBox.prototype.stopTimeout = function stopTimeout() {
if (this.timeout) clearTimeout(this.timeout);
}
HoverBox.prototype.extendTimeout = function extendTimeout(T) {
//console.log("extend", T)
var this_ = this;
this.stopTimeout();
this.timeout = setTimeout(function(){this_.hide();}.bind(this), T);
}
// Bind events to a link to open this box
HoverBox.prototype.bind = function bind(node) {
if (typeof node == "string"){
node = document.querySelector(node);
}
node.addEventListener("mouseover", function(){
if (!this.visible) this.showAtNode(node);
this.stopTimeout();
}.bind(this));
node.addEventListener("mouseout", function(){this.extendTimeout(250);}.bind(this));
node.addEventListener("touchstart", function(e) {
if (this.visible) {
this.hide();
} else {
this.showAtNode(node);
}
// Don't trigger body touchstart event when touching link
e.stopPropagation();
}.bind(this), {passive: true});
}
HoverBox.prototype.bindDivEvents = function bindDivEvents(node){
// For mice, same behavior as hovering on links
this.div.addEventListener("mouseover", function(){
if (!this.visible) this.showAtNode(node);
this.stopTimeout();
}.bind(this));
this.div.addEventListener("mouseout", function(){this.extendTimeout(250);}.bind(this));
// Don't trigger body touchstart event when touching within box
this.div.addEventListener("touchstart", function(e){e.stopPropagation();}, {passive: true});
// Close box when touching outside box
document.body.addEventListener("touchstart", function(){this.hide();}.bind(this), {passive: true});
}
+68
View File
@@ -0,0 +1,68 @@
export function body(selector) {
return `${selector} {
width: auto;
margin-left: 24px;
margin-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
${selector} {
margin-left: 72px;
margin-right: 72px;
}
}
@media(min-width: 1080px) {
${selector} {
margin-left: calc(50% - 984px / 2);
width: 648px;
}
}
`;
}
export function page(selector) {
return `${selector} {
width: auto;
margin-left: 24px;
margin-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
${selector} {
margin-left: 72px;
margin-right: 72px;
}
}
@media(min-width: 1080px) {
${selector} {
width: 984px;
margin-left: auto;
margin-right: auto;
}
}
`;
}
export function screen(selector) {
return `${selector} {
width: auto;
margin-left: 24px;
margin-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
${selector} {
margin-left: 72px;
margin-right: 72px;
}
}
@media(min-width: 1080px) {
${selector} {
margin-left: auto;
margin-right: auto;
width: auto;
}
}
`;
}
+39
View File
@@ -0,0 +1,39 @@
export const Mutating = (superclass) => {
return class extends superclass {
constructor() {
super();
// set up mutation observer
const options = {childList: true, characterData: true, subtree: true};
const observer = new MutationObserver( (mutations) => {
observer.disconnect();
this.renderIfPossible();
observer.observe(this, options);
});
// ...and listen for changes
observer.observe(this, options);
}
connectedCallback() {
super.connectedCallback();
this.renderIfPossible();
}
// potential TODO: check if this is enough for all our usecases
// maybe provide a custom function to tell if we have enough information to render
renderIfPossible() {
if (this.textContent && this.root) {
this.renderContent();
}
};
renderContent() {
console.error(`Your class ${this.constructor.name} must provide a custom renderContent() method!` );
}
} // end class
}; // end mixin function
+86
View File
@@ -0,0 +1,86 @@
export function propName(attr) {
return attr.replace(/(\-[a-z])/g, (s) => s.toUpperCase().replace('-', ''));
}
export function attrName(prop) {
return prop.replace(/([A-Z])/g, (s) => '-' + s.toLowerCase());
}
export function deserializeAttribute(value, type) {
switch (type) {
case String:
break;
case Array:
case Object:
try {
value = JSON.parse(value);
} catch (e) {
}
break;
case Boolean:
value = value != 'false' && value != '0' && value;
break;
default:
}
return value;
}
const immediately = window.setImmediate || function(fn, args) {
window.setTimeout(function() {
fn.apply(this, args);
}, 0);
};
export const Properties = (properties) => {
const keys = Object.keys(properties);
const attrs = keys.map((k) => attrName(k));
return (superclass) => {
const cls = class extends superclass {
static get observedAttributes() {
return attrs;
}
attributeChangedCallback(attr, oldValue, newValue) {
const prop = propName(attr);
const value = deserializeAttribute(newValue, properties[prop].type);
this[prop] = value;
}
_propertiesChanged() {
if (!this.propertiesChangedCallback) {
return;
}
clearTimeout(this._propertiesChangedTimeout);
this._propertiesChangedTimeout = immediately(() => {
this.propertiesChangedCallback(this);
});
}
}
keys.forEach(function(k) {
const secret = `_${k}`;
const callback = `${k}Changed`;
const defaultValue = properties[k].value;
Object.defineProperty(cls.prototype, k, {
get: function() {
let value = this[secret];
if (value) {
return value;
}
if (defaultValue && typeof defaultValue == 'function') {
this[secret] = defaultValue();
} else {
this[secret] = defaultValue;
}
return this[secret];
},
set: function(value) {
const oldValue = this[secret];
this[secret] = value;
if (this[callback]) {
this[callback](value, oldValue);
}
this._propertiesChanged();
}
});
});
return cls;
};
};
+53
View File
@@ -0,0 +1,53 @@
export const Template = (name, templateString, useShadow = true) => {
const template = document.createElement('template');
template.innerHTML = templateString;
if (useShadow && 'ShadyCSS' in window) {
ShadyCSS.prepareTemplate(template, name);
}
return (superclass) => {
return class extends superclass {
static get is() { return name; }
constructor() {
super();
this.clone = document.importNode(template.content, true);
if (useShadow) {
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(this.clone);
}
}
connectedCallback() {
if (useShadow) {
if ('ShadyCSS' in window) {
ShadyCSS.styleElement(this);
}
} else {
this.insertBefore(this.clone, this.firstChild);
}
}
get root() {
if (useShadow) {
return this.shadowRoot;
} else {
return this;
}
}
/* TODO: Are we using these? Should we even? */
$(query) {
return this.root.querySelector(query);
}
$$(query) {
return this.root.querySelectorAll(query);
}
}
}
};
+197
View File
@@ -0,0 +1,197 @@
d-article {
display: block;
color: rgba(0, 0, 0, 0.8);
padding-top: 36px;
padding-bottom: 72px;
overflow: hidden;
font-size: 16px;
line-height: 1.6em;
border-top: 1px solid rgba(0, 0, 0, 0.2);
}
@media(min-width: 1024px) {
d-article {
font-size: 18px;
}
}
/* H2 */
d-article h2 {
font-weight: 400;
font-size: 26px;
line-height: 1.25em;
margin-top: 36px;
margin-bottom: 24px;
padding-bottom: 24px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
@media(min-width: 1024px) {
d-article h2 {
margin-top: 2em;
font-size: 32px;
}
}
d-article h1 + h2 {
font-weight: 300;
font-size: 20px;
line-height: 1.4em;
margin-top: 8px;
font-style: normal;
}
@media(min-width: 1080px) {
.centered h1 + h2 {
text-align: center;
}
d-article h1 + h2 {
margin-top: 12px;
font-size: 32px;
}
}
/* H3 */
d-article h3 {
font-weight: 400;
font-size: 20px;
line-height: 1.4em;
margin-top: 36px;
margin-bottom: 18px;
font-style: italic;
}
d-article h1 + h3 {
margin-top: 48px;
}
@media(min-width: 1024px) {
d-article h3 {
font-size: 26px;
}
}
/* H4 */
d-article h4 {
font-weight: 600;
text-transform: uppercase;
font-size: 14px;
line-height: 1.4em;
}
d-article a {
color: inherit;
}
d-article p,
d-article ul,
d-article ol {
margin-bottom: 24px;
}
d-article p b,
d-article ul b,
d-article ol b {
-webkit-font-smoothing: antialiased;
}
d-article a {
border-bottom: 1px solid rgba(0, 0, 0, 0.4);
text-decoration: none;
}
d-article a:hover {
border-bottom: 1px solid rgba(0, 0, 0, 0.8);
}
d-article .link {
text-decoration: underline;
cursor: pointer;
}
d-article ul,
d-article ol {
padding-left: 24px;
}
d-article li {
margin-bottom: 24px;
margin-left: 0;
padding-left: 0;
}
d-article pre {
font-size: 14px;
margin-bottom: 20px;
}
d-article hr {
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
margin-top: 60px;
margin-bottom: 60px;
}
d-article section {
margin-top: 60px;
margin-bottom: 60px;
}
/* Figure */
d-article figure {
position: relative;
margin-top: 30px;
margin-bottom: 30px;
}
@media(min-width: 1024px) {
d-article figure {
margin-top: 48px;
margin-bottom: 48px;
}
}
d-article figure img {
width: 100%;
}
d-article figure svg text,
d-article figure svg tspan {
}
d-article figure figcaption {
color: rgba(0, 0, 0, 0.6);
font-size: 12px;
line-height: 1.5em;
}
@media(min-width: 1024px) {
d-article figure figcaption {
font-size: 13px;
}
}
d-article figure.external img {
background: white;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.1);
padding: 18px;
box-sizing: border-box;
}
d-article figure figcaption a {
color: rgba(0, 0, 0, 0.6);
}
/*d-article figure figcaption::before {
position: relative;
display: block;
top: -20px;
content: "";
width: 25px;
border-top: 1px solid rgba(0, 0, 0, 0.3);
}*/
d-article span.equation-mimic {
font-family: georgia;
font-size: 115%;
font-style: italic;
}
d-article figure figcaption b {
font-weight: 600;
color: rgba(0, 0, 0, 1.0);
}
d-article > d-code,
d-article section > d-code {
display: block;
}
d-article > d-math[block],
d-article section > d-math[block] {
display: block;
}
d-article .citation {
color: #668;
cursor: pointer;
}
d-include {
width: auto;
display: block;
}
+85
View File
@@ -0,0 +1,85 @@
html {
font-size: 20px;
line-height: 1rem;
/*font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", sans-serif;*/
font-family: "Libre Franklin", "Helvetica Neue", sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
body {
margin: 0;
/*background-color: hsl(223, 9%, 25%);*/
}
a {
color: #004276;
}
figure {
margin: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
table th {
text-align: left;
}
table thead {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
table thead th {
padding-bottom: 0.5em;
}
table tbody :first-child td {
padding-top: 0.5em;
}
/*
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}*/
+239
View File
@@ -0,0 +1,239 @@
/*
Column: 60px
Gutter: 24px
Body: 648px
- 8 columns
- 7 gutters
Middle: 816px
Page: 984px
- 12 columns
- 11 gutters
*/
.l-body,
.l-body-outset,
.l-page,
.l-page-outset,
.l-middle,
.l-middle-outset,
d-article > div,
d-article > p,
d-article > h1,
d-article > h2,
d-article > h3,
d-article > h4,
d-article > figure,
d-article > ul,
d-article > d-abstract,
d-article > d-code,
d-article > d-math,
d-article > table,
d-article section > div,
d-article section > p,
d-article section > h1,
d-article section > h2,
d-article section > h3,
d-article section > h4,
d-article section > figure,
d-article section > ul,
d-article section > d-abstract,
d-article section > d-code,
d-article section > d-math,
d-article section > table, {
width: auto;
margin-left: 24px;
margin-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
.l-body,
.l-body-outset,
.l-page,
.l-page-outset,
.l-middle,
.l-middle-outset,
d-article > div,
d-article > p,
d-article > h1,
d-article > h2,
d-article > h3,
d-article > h4,
d-article > figure,
d-article > ul,
d-article > d-abstract,
d-article > d-code,
d-article > d-math,
d-article > d-math,
d-article section > div,
d-article section > p,
d-article section > h1,
d-article section > h2,
d-article section > h3,
d-article section > h4,
d-article section > figure,
d-article section > ul,
d-article section > d-abstract,
d-article section > d-code,
d-article section > d-math,
d-article section > table {
margin-left: 72px;
margin-right: 72px;
}
}
@media(min-width: 1080px) {
.l-body,
d-article > div,
d-article > p,
d-article > h2,
d-article > h3,
d-article > h4,
d-article > figure,
d-article > ul,
d-article > d-abstract,
d-article > d-code,
d-article > d-math,
d-article > table,
d-article section > div,
d-article section > p,
d-article section > h2,
d-article section > h3,
d-article section > h4,
d-article section > figure,
d-article section > ul,
d-article section > d-abstract,
d-article section > d-code,
d-article section > d-math,
d-article section > table {
margin-left: calc(50% - 984px / 2);
width: 648px;
}
.l-body-outset,
d-article .l-body-outset {
margin-left: calc(50% - 984px / 2 - 96px/2);
width: calc(648px + 96px);
}
.l-middle,
d-article .l-middle {
width: 816px;
margin-left: calc(50% - 984px / 2);
margin-right: auto;
}
.l-middle-outset,
d-article .l-middle-outset {
width: calc(816px + 96px);
margin-left: calc(50% - 984px / 2 - 48px);
margin-right: auto;
}
d-article > h1,
d-article section > h1,
.l-page,
d-article .l-page,
d-article.centered .l-page {
width: 984px;
margin-left: auto;
margin-right: auto;
}
.l-page-outset,
d-article .l-page-outset,
d-article.centered .l-page-outset {
width: 1080px;
margin-left: auto;
margin-right: auto;
}
.l-screen,
d-article .l-screen,
d-article.centered .l-screen {
margin-left: auto;
margin-right: auto;
width: auto;
}
.l-screen-inset,
d-article .l-screen-inset,
d-article.centered .l-screen-inset {
margin-left: 24px;
margin-right: 24px;
width: auto;
}
.l-gutter,
d-article .l-gutter {
clear: both;
float: right;
margin-top: 0;
margin-left: 24px;
margin-right: calc((100vw - 984px) / 2 + 168px);
width: calc((984px - 648px) / 2 - 24px);
}
/* Side */
.side.l-body,
d-article .side.l-body {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 984px + 648px) / 2);
width: calc(648px / 2 - 24px - 84px);
}
.side.l-body-outset,
d-article .side.l-body-outset {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 984px + 648px - 48px) / 2);
width: calc(648px / 2 - 48px + 24px);
}
.side.l-middle,
d-article .side.l-middle {
clear: both;
float: right;
width: calc(456px - 84px);
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px);
}
.side.l-middle-outset,
d-article .side.l-middle-outset {
clear: both;
float: right;
width: 456px;
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px);
}
.side.l-page,
d-article .side.l-page {
clear: both;
float: right;
margin-left: 48px;
width: calc(624px - 84px);
margin-right: calc((100vw - 984px) / 2);
}
.side.l-page-outset,
d-article .side.l-page-outset {
clear: both;
float: right;
width: 624px;
margin-right: calc((100vw - 984px) / 2);
}
}
/* Rows and Columns */
.row {
display: flex;
}
.column {
flex: 1;
box-sizing: border-box;
margin-right: 24px;
margin-left: 24px;
}
.row > .column:first-of-type {
margin-left: 0;
}
.row > .column:last-of-type {
margin-right: 0;
}
+20
View File
@@ -0,0 +1,20 @@
@media print {
@page {
size: 8in 11in;
}
html {
}
p, code {
page-break-inside: avoid;
}
h2, h3 {
page-break-after: avoid;
}
d-header {
visibility: hidden;
}
d-footer {
display: none!important;
}
}
+10
View File
@@ -0,0 +1,10 @@
import base from "./styles-base.css";
import layout from "./styles-layout.css";
import article from "./styles-article.css";
import print from "./styles-print.css";
import katex from '../../node_modules/katex/dist/katex.min.css'
let s = document.createElement("style");
s.textContent = base + layout + print + article + katex;
document.querySelector("head").appendChild(s);
export default s;
View File
+1
View File
@@ -0,0 +1 @@
node_modules/
+89
View File
@@ -0,0 +1,89 @@
const html = `
<style>
dt-appendix {
display: block;
font-size: 14px;
line-height: 24px;
margin-bottom: 0;
border-top: 1px solid rgba(0,0,0,0.1);
color: rgba(0,0,0,0.5);
background: rgb(250, 250, 250);
padding-top: 36px;
padding-bottom: 60px;
}
dt-appendix h3 {
font-size: 16px;
font-weight: 500;
margin-top: 18px;
margin-bottom: 18px;
color: rgba(0,0,0,0.65);
}
dt-appendix .citation {
font-size: 11px;
line-height: 15px;
border-left: 1px solid rgba(0, 0, 0, 0.1);
padding-left: 18px;
border: 1px solid rgba(0,0,0,0.1);
background: rgba(0, 0, 0, 0.02);
padding: 10px 18px;
border-radius: 3px;
color: rgba(150, 150, 150, 1);
overflow: hidden;
margin-top: -12px;
}
dt-appendix .references {
font-size: 12px;
line-height: 20px;
}
dt-appendix a {
color: rgba(0, 0, 0, 0.6);
}
dt-appendix ol,
dt-appendix ul {
padding-left: 24px;
}
</style>
<div class="l-body">
<h3>References</h3>
<dt-bibliography></dt-bibliography>
<h3 id="citation">Errors, Reuse, and Citation</h3>
<p>If you see mistakes or want to suggest changes, please <a class="github-issue">create an issue on GitHub</a>.</p>
<p>Diagrams and text are licensed under Creative Commons Attribution <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY 2.0</a>, unless noted otherwise, with the <a class="github">source available on GitHub</a>. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: “Figure from …”.</p>
<p>For attribution in academic contexts, please cite this work as</p>
<pre class="citation short"></pre>
<p>BibTeX citation</p>
<pre class="citation long"></pre>
</div>
`;
export default function(dom, data) {
let el = dom.querySelector('dt-appendix')
if (el) {
let oldHtml = el.innerHTML;
el.innerHTML = html;
let div = el.querySelector("div.l-body")
if (dom.querySelector("dt-fn")) {
div.innerHTML = `<h3>Footnotes</h3><dt-fn-list></dt-fn-list>` + div.innerHTML;
}
div.innerHTML = oldHtml + div.innerHTML;
if (data.githubCompareUpdatesUrl) {
div.innerHTML = `<h3>Updates</h3><p><a href="${data.githubCompareUpdatesUrl}">View all changes</a> to this article since it was first published.</p>` + div.innerHTML;
}
el.querySelector("a.github").setAttribute("href", data.githubUrl);
el.querySelector("a.github-issue").setAttribute("href", data.githubUrl + "/issues/new");
el.querySelector(".citation.short").textContent = data.concatenatedAuthors + ", " + '"' + data.title + '", Distill, ' + data.publishedYear + ".";
var bibtex = "@article{" + data.slug + ",\n";
bibtex += " author = {" + data.bibtexAuthors + "},\n";
bibtex += " title = {" + data.title + "},\n";
bibtex += " journal = {Distill},\n";
bibtex += " year = {" + data.publishedYear + "},\n";
bibtex += " note = {" + data.url + "}\n";
bibtex += "}";
el.querySelector(".citation.long").textContent = bibtex;
}
}
+23
View File
@@ -0,0 +1,23 @@
const html = `
<style>
dt-banner {
background: #FFF59D;
display: block;
text-align: center;
color: black;
height: 70px;
line-height: 70px;
}
</style>
<div>This article is a draft, awaiting review for publication in Distill</div>
`;
export default function(dom, data) {
let banner = dom.createElement("dt-banner");
banner.innerHTML = html;
let b = dom.querySelector("body");
b.insertBefore(banner, b.firstChild);
banner.addEventListener("click", function() {
banner.style.display = "none";
});
}
+46
View File
@@ -0,0 +1,46 @@
import bibtexParse from "bibtex-parse-js";
export default function(dom, data) {
let el = dom.querySelector('script[type="text/bibliography"]');
let citations = [];
let bibliography = {};
//TODO If we don't have a local element, make a request for the document.
if (el) {
let rawBib = el.textContent;
let parsed = bibtexParse.toJSON(rawBib);
if(parsed) {
parsed.forEach(e => {
for (var k in e.entryTags){
var val = e.entryTags[k];
val = val.replace(/[\t\n ]+/g, " ");
val = val.replace(/{\\["^`\.'acu~Hvs]( )?([a-zA-Z])}/g,
(full, x, char) => char);
val = val.replace(/{\\([a-zA-Z])}/g,
(full, char) => char);
e.entryTags[k] = val;
}
bibliography[e.citationKey] = e.entryTags;
bibliography[e.citationKey].type = e.entryType;
});
}
var citeTags = [].slice.apply(dom.querySelectorAll("dt-cite"));
citeTags.forEach(el => {
let key = el.getAttribute("key");
if (key) {
let citationKeys = key.split(",");
citationKeys.forEach(key => {
if (citations.indexOf(key) == -1){
citations.push(key);
if (!(key in bibliography)){
console.warn("No bibliography entry found for: " + key);
}
}
});
}
});
}
data.bibliography = bibliography;
data.citations = citations;
}
+155
View File
@@ -0,0 +1,155 @@
import mustache from "mustache";
const html = `
<style>
dt-byline {
font-size: 12px;
line-height: 18px;
display: block;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
color: rgba(0, 0, 0, 0.5);
padding-top: 12px;
padding-bottom: 12px;
min-height: 90px;
}
dt-article.centered dt-byline {
text-align: center;
}
dt-byline a,
dt-article dt-byline a {
text-decoration: none;
border-bottom: none;
}
dt-article dt-byline a:hover {
text-decoration: underline;
border-bottom: none;
}
dt-byline .authors {
text-align: left;
}
dt-byline .name {
display: inline;
text-transform: uppercase;
}
dt-byline .affiliation {
display: inline;
}
dt-byline .date {
display: block;
text-align: left;
}
dt-byline .year, dt-byline .month {
display: inline;
}
dt-byline .citation {
display: block;
text-align: left;
}
dt-byline .citation div {
display: inline;
}
@media(min-width: 768px) {
dt-byline {
}
}
@media(min-width: 1080px) {
dt-byline {
border-bottom: none;
margin-bottom: 70px;
}
dt-byline a:hover {
color: rgba(0, 0, 0, 0.9);
}
dt-byline .authors {
display: inline-block;
}
dt-byline .author {
display: inline-block;
margin-right: 12px;
/*padding-left: 20px;*/
/*border-left: 1px solid #ddd;*/
}
dt-byline .affiliation {
display: block;
}
dt-byline .author:last-child {
margin-right: 0;
}
dt-byline .name {
display: block;
}
dt-byline .date {
border-left: 1px solid rgba(0, 0, 0, 0.1);
padding-left: 15px;
margin-left: 15px;
display: inline-block;
}
dt-byline .year, dt-byline .month {
display: block;
}
dt-byline .citation {
border-left: 1px solid rgba(0, 0, 0, 0.15);
padding-left: 15px;
margin-left: 15px;
display: inline-block;
}
dt-byline .citation div {
display: block;
}
}
</style>
`;
const template = `
<div class="byline">
<div class="authors">
{{#authors}}
<div class="author">
{{#personalURL}}
<a class="name" href="{{personalURL}}">{{name}}</a>
{{/personalURL}}
{{^personalURL}}
<div class="name">{{name}}</div>
{{/personalURL}}
{{#affiliation}}
{{#affiliationURL}}
<a class="affiliation" href="{{affiliationURL}}">{{affiliation}}</a>
{{/affiliationURL}}
{{^affiliationURL}}
<div class="affiliation">{{affiliation}}</div>
{{/affiliationURL}}
{{/affiliation}}
</div>
{{/authors}}
</div>
<div class="date">
<div class="month">{{publishedMonth}}. {{publishedDay}}</div>
<div class="year">{{publishedYear}}</div>
</div>
<a class="citation" href="#citation">
<div>Citation:</div>
<div>{{concatenatedAuthors}}, {{publishedYear}}</div>
</a>
</div>
`
export default function(dom, data) {
let el = dom.querySelector('dt-byline');
if (el) {
el.innerHTML = html + mustache.render(template, data);
}
}
+234
View File
@@ -0,0 +1,234 @@
export default function(dom, data) {
let css = `
dt-cite {
color: hsla(206, 90%, 20%, 0.7);
}
dt-cite .citation-number {
cursor: default;
white-space: nowrap;
font-family: -apple-system, BlinkMacSystemFont, "Roboto", Helvetica, sans-serif;
font-size: 75%;
color: hsla(206, 90%, 20%, 0.7);
display: inline-block;
line-height: 1.1em;
text-align: center;
position: relative;
top: -2px;
margin: 0 2px;
}
figcaption dt-cite .citation-number {
font-size: 11px;
font-weight: normal;
top: -2px;
line-height: 1em;
}
`;
let style = dom.createElement("style");
style.textContent = css;
dom.querySelector("body").appendChild(style);
let citations = data.citations;
/*if (data.citations) {
citations = Object.keys(data.citations).map(c => data.citations[c]);
citations.sort((a, b) => {
return a.author.localeCompare(b.author);
});
}*/
var citeTags = [].slice.apply(dom.querySelectorAll("dt-cite"));
citeTags.forEach((el,n) => {
var key = el.getAttribute("key");
if (key) {
var keys = key.split(",");
var cite_string = inline_cite_short(keys);
var cite_hover_str = "";
keys.map((key,n) => {
if (n>0) cite_hover_str += "<br><br>";
cite_hover_str += hover_cite(data.bibliography[key]);
});
cite_hover_str = cite_hover_str.replace(/"/g, "&#39;")
var orig_string = el.innerHTML;
if (orig_string != "") orig_string += " ";
el.innerHTML = `<span id="citation-${n}" data-hover="${cite_hover_str}">${orig_string}<span class="citation-number">${cite_string}</span></span>`;
}
});
let bibEl = dom.querySelector("dt-bibliography");
if (bibEl) {
let ol = dom.createElement("ol");
citations.forEach(key => {
let el = dom.createElement("li");
el.innerHTML = bibliography_cite(data.bibliography[key]);
ol.appendChild(el);
})
bibEl.appendChild(ol);
}
function inline_cite_short(keys){
function cite_string(key){
if (key in data.bibliography){
var n = data.citations.indexOf(key)+1;
return ""+n;
} else {
return "?";
}
}
return "["+keys.map(cite_string).join(", ")+"]";
}
function inline_cite_long(keys){
function cite_string(key){
if (key in data.bibliography){
var ent = data.bibliography[key];
var names = ent.author.split(" and ");
names = names.map(name => name.split(",")[0].trim())
var year = ent.year;
if (names.length == 1) return names[0] + ", " + year;
if (names.length == 2) return names[0] + " & " + names[1] + ", " + year;
if (names.length > 2) return names[0] + ", et al., " + year;
} else {
return "?";
}
}
return keys.map(cite_string).join(", ");
}
function author_string(ent, template, sep, finalSep){
var names = ent.author.split(" and ");
let name_strings = names.map(name => {
name = name.trim();
if (name.indexOf(",") != -1){
var last = name.split(",")[0].trim();
var firsts = name.split(",")[1];
} else {
var last = name.split(" ").slice(-1)[0].trim();
var firsts = name.split(" ").slice(0,-1).join(" ");
}
var initials = "";
if (firsts != undefined) {
initials = firsts.trim().split(" ").map(s => s.trim()[0]);
initials = initials.join(".")+".";
}
return template.replace("${F}", firsts)
.replace("${L}", last)
.replace("${I}", initials);
});
if (names.length > 1) {
var str = name_strings.slice(0, names.length-1).join(sep);
str += (finalSep || sep) + name_strings[names.length-1];
return str;
} else {
return name_strings[0];
}
}
function venue_string(ent) {
var cite = (ent.journal || ent.booktitle || "")
if ("volume" in ent){
var issue = ent.issue || ent.number;
issue = (issue != undefined)? "("+issue+")" : "";
cite += ", Vol " + ent.volume + issue;
}
if ("pages" in ent){
cite += ", pp. " + ent.pages
}
if (cite != "") cite += ". "
if ("publisher" in ent){
cite += ent.publisher;
if (cite[cite.length-1] != ".") cite += ".";
}
return cite;
}
function link_string(ent){
if ("url" in ent){
var url = ent.url;
var arxiv_match = (/arxiv\.org\/abs\/([0-9\.]*)/).exec(url);
if (arxiv_match != null){
url = `http://arxiv.org/pdf/${arxiv_match[1]}.pdf`;
}
if (url.slice(-4) == ".pdf"){
var label = "PDF";
} else if (url.slice(-5) == ".html") {
var label = "HTML";
}
return ` &ensp;<a href="${url}">[${label||"link"}]</a>`;
}/* else if ("doi" in ent){
return ` &ensp;<a href="https://doi.org/${ent.doi}" >[DOI]</a>`;
}*/ else {
return "";
}
}
function doi_string(ent, new_line){
if ("doi" in ent) {
return `${new_line?"<br>":""} <a href="https://doi.org/${ent.doi}" style="text-decoration:inherit;">DOI: ${ent.doi}</a>`;
} else {
return "";
}
}
function bibliography_cite(ent, fancy){
if (ent){
var cite = "<b>" + ent.title + "</b> "
cite += link_string(ent) + "<br>";
cite += author_string(ent, "${L}, ${I}", ", ", " and ");
if (ent.year || ent.date){
cite += ", " + (ent.year || ent.date) + ". "
} else {
cite += ". "
}
cite += venue_string(ent);
cite += doi_string(ent);
return cite
/*var cite = author_string(ent, "${L}, ${I}", ", ", " and ");
if (ent.year || ent.date){
cite += ", " + (ent.year || ent.date) + ". "
} else {
cite += ". "
}
cite += "<b>" + ent.title + "</b>. ";
cite += venue_string(ent);
cite += doi_string(ent);
cite += link_string(ent);
return cite*/
} else {
return "?";
}
}
function hover_cite(ent){
if (ent){
var cite = "";
cite += "<b>" + ent.title + "</b>";
cite += link_string(ent);
cite += "<br>"
var a_str = author_string(ent, "${I} ${L}", ", ") + ".";
var v_str = venue_string(ent).trim() + " " + ent.year + ". " + doi_string(ent, true);
if ((a_str+v_str).length < Math.min(40, ent.title.length)) {
cite += a_str + " " + v_str;
} else {
cite += a_str + "<br>" + v_str;
}
return cite;
} else {
return "?";
}
}
//https://scholar.google.com/scholar?q=allintitle%3ADocument+author%3Aolah
function get_GS_URL(ent){
if (ent){
var names = ent.author.split(" and ");
names = names.map(name => name.split(",")[0].trim())
var title = ent.title.split(" ")//.replace(/[,:]/, "")
var url = "http://search.labs.crossref.org/dois?"//""https://scholar.google.com/scholar?"
url += uris({q: names.join(" ") + " " + title.join(" ")})
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import Prism from "prismjs";
export default function(dom, data) {
let codeElements = [].slice.call(dom.querySelectorAll("dt-code"));
codeElements.forEach(el => {
let content = el.textContent;
el.innerHTML = "";
let language = el.getAttribute("language");
let c = dom.createElement("code");
if (el.getAttribute("block") === "") {
// Let's normalize the tab indents
content = content.replace(/\n/, "");
let tabs = content.match(/\s*/);
content = content.replace(new RegExp("\n" + tabs, "g"), "\n");
content = content.trim();
let p = dom.createElement("pre");
p.appendChild(c);
el.appendChild(p);
} else {
el.appendChild(c);
}
let highlighted = content;
if (Prism.languages[language]) {
c.setAttribute("class", "language-" + language);
highlighted = Prism.highlight(content, Prism.languages[language]);
}
c.innerHTML = highlighted;
});
}
+1
View File
@@ -0,0 +1 @@
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA99JREFUeNrsG4t1ozDMzQSM4A2ODUonKBucN2hugtIJ6E1AboLcBiQTkJsANiAb9OCd/OpzMWBJBl5TvaeXPiiyJetry0J8wW3D3QpjRh3GjneXDq+fSQA9s2mH9x3KDhN4foJfCb8N/Jrv+2fnDn8vLRQOplWHVYdvHZYdZsBcZP1vBmh/n8DzEmhUQDPaOuP9pFuY+JwJHwHnCLQE2tnWBGEyXozY9xCUgHMhhjE2I4heVWtgIkZ83wL6Qgxj1obfWBxymPwe+b00BCCRNPbwfb60yleAkkBHGT5AEehIYz7eJrFDMF9CvH4wwhcGHiHMneFvLDQwlwvMLQq58trRcYBWfYn0A0OgHWQUSu25mE+BnoYKnnEJoeIWAifzOv7vLWd2ZKRfWAIme3tOiUaQ3UnLkb0xj1FxRIeEGKaGIHOs9nEgLaaA9i0JRYo1Ic67wJW86KSKE/ZAM8KuVMk8ITVhmxUxJ3Cl2xlm9Vtkeju1+mpCQNxaEGNCY8bs9X2YqwNoQeGjBWut/ma0QAWy/TqAsHx9wSya3I5IRxOfTC+leG+kA/4vSeEcGBtNUN6byhu3+keEZCQJUNh8MAO7HL6H8pQLnsW/Hd4T4lv93TPjfM7A46iEEqbB5EDOvwYNW6tGNZzT/o+CZ6sqZ6wUtR/wf7mi/VL8iNciT6rHih48Y55b4nKCHJCCzb4y0nwFmin3ZEMIoLfZF8F7nncFmvnWBaBj7CGAYA/WGJsUwHdYqVDwAmNsUgAx4CGgAA7GOOxADYOFWOaIKifuVYzmOpREqA21Mo7aPsgiY1PhOMAmxtR+AUbYH3Id2wc0SAFIQTsn9IUGWR8k9jx3vtXSiAacFxTAGakBk9UudkNECd6jLe+6HrshshvIuC6IlLMRy7er+JpcKma24SlE4cFZSZJDGVVrsNvitQhQrDhW0jfiOLfFd47C42eHT56D/BK0To+58Ahj+cAT8HT1UWlfLZCCd/uKawzU0Rh2EyIX/Icqth3niG8ybNroezwe6khdCNxRN+l4XGdOLVLlOOt2hTRJlr1ETIuMAltVTMz70mJrkdGAaZLSmnBEqmAE32JCMmuTlCnRgsBENtOUpHhvvsYIL0ibnBkaC6QvKcR7738GKp0AKnim7xgUSNv1bpS8QwhBt8r+EP47v/oyRK/S34yJ9nT+AN0Tkm4OdB9E4BsmXM3SnMlRFUrtp6IDpV2eKzdYvF3etm3KhQksbOLChGkSmcBdmcEwvqkrMy5BzL00NZeu3qPYJOOuCc+5NjcWKXQxFvTa3NoXJ4d8in7fiAUuTt781dkvuHX4K8AA2Usy7yNKLy0AAAAASUVORK5CYII=
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="64px" height="64px" viewBox="-1214 838 64 64" style="enable-background:new -1214 838 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;}
</style>
<g>
<g>
<path class="st0" d="M-1182,841.2c-15.9,0-28.8,12.9-28.8,28.8s12.9,28.8,28.8,28.8s28.8-12.9,28.8-28.8S-1166.1,841.2-1182,841.2
z M-1182,891.2c-7.7,0-13.9-6.1-13.9-13.8s13.9-22.2,13.9-36.3c0,14.2,13.9,28.7,13.9,36.3S-1174.3,891.2-1182,891.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 740 B

+3
View File
@@ -0,0 +1,3 @@
<svg viewBox="-607 419 64 64">
<path d="M-573.4,478.9c-8,0-14.6-6.4-14.6-14.5s14.6-25.9,14.6-40.8c0,14.9,14.6,32.8,14.6,40.8S-565.4,478.9-573.4,478.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 163 B

+52
View File
@@ -0,0 +1,52 @@
import logo from "./distill-logo.svg";
let html = `
<style>
dt-footer {
display: block;
color: rgba(255, 255, 255, 0.4);
font-weight: 300;
padding: 40px 0;
border-top: 1px solid rgba(0, 0, 0, 0.1);
background-color: hsl(200, 60%, 15%);
text-align: center;
}
dt-footer .logo svg {
width: 24px;
position: relative;
top: 4px;
margin-right: 2px;
}
dt-footer .logo svg path {
fill: none;
stroke: rgba(255, 255, 255, 0.8);
stroke-width: 3px;
}
dt-footer .logo {
font-size: 17px;
font-weight: 200;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
margin-right: 6px;
}
</style>
<div class="l-screen-inset">
<a href="/" class="logo">
${logo}
Distill
</a> is dedicated to clear explanations of machine learning
</div>
`;
export default function(dom, data) {
let el = dom.querySelector("dt-footer");
if(el) {
el.innerHTML = html;
} else {
let footer = dom.createElement("dt-footer");
footer.innerHTML = html;
let b = dom.querySelector("body");
b.appendChild(footer);
}
}
+25
View File
@@ -0,0 +1,25 @@
export default function(dom, data) {
var fnTags = [].slice.apply(dom.querySelectorAll("dt-fn"));
var fnContent = [];
fnTags.forEach((el,n) => {
var content = el.innerHTML;
fnContent.push(content);
n = (n+1)+"";
var key = "fn-"+n;
var escaped_content = content.replace(/"/g, "&#39;");
el.innerHTML = `<sup><span id="${key}" data-hover="${escaped_content}" style="cursor:pointer">${n}</span></sup>`;
});
let fnList = dom.querySelector("dt-fn-list");
if (fnList) {
let ol = dom.createElement("ol");
fnContent.forEach(content => {
let el = dom.createElement("li");
el.innerHTML = content;
ol.appendChild(el);
})
fnList.appendChild(ol);
}
}
+41
View File
@@ -0,0 +1,41 @@
import ymlParse from "js-yaml";
export default function(dom, data) {
let localData = {};
let el = dom.querySelector('script[type="text/front-matter"]');
if (el) {
let text = el.textContent;
localData = ymlParse.safeLoad(text);
}
data.title = localData.title ? localData.title : "Untitled";
data.description = localData.description ? localData.description : "No description.";
data.authors = localData.authors ? localData.authors : [];
data.authors = data.authors.map((author, i) =>{
let a = {};
let name = Object.keys(author)[0];
if ((typeof author) === "string") {
name = author;
} else {
a.personalURL = author[name];
}
let names = name.split(" ");
a.name = name;
a.firstName = names.slice(0, names.length - 1).join(" ");
a.lastName = names[names.length -1];
if(localData.affiliations[i]) {
let affiliation = Object.keys(localData.affiliations[i])[0];
if ((typeof localData.affiliations[i]) === "string") {
affiliation = localData.affiliations[i]
} else {
a.affiliationURL = localData.affiliations[i][affiliation];
}
a.affiliation = affiliation;
}
return a;
});
}
+167
View File
@@ -0,0 +1,167 @@
//import xml from "xml";
export default function(data) {
var date = data.publishedDate;
var batch_timestamp = Math.floor(Date.now() / 1000);
var batch_id = data.authors.length ? data.authors[0].lastName.toLowerCase().slice(0,20) : "Anonymous";
batch_id += "_" + date.getFullYear();
batch_id += "_" + data.title.split(" ")[0].toLowerCase().slice(0,20) + "_" + batch_timestamp;
// generate XML
var crf_data =
{doi_batch : [
{ _attr: {
version: "4.3.7",
xmlns: "http://www.crossref.org/schema/4.3.7",
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"xsi:schemaLocation": "http://www.crossref.org/schema/4.3.7 http://www.crossref.org/schemas/crossref4.3.7.xsd",
}},
{ head: [
{doi_batch_id: batch_id},
{timestamp: batch_timestamp},
{depositor: [
{depositor_name: data.journal.publisherName},
{email_address: data.journal.publisherEmail},
]},
{registrant: data.journal.publisherName},
]},
{body: [
{journal: [
{journal_metadata: [
{full_title: data.journal.full_title || data.journal.title},
{abbrev_title: data.journal.abbrev_title || data.journal.title || data.journal.full_title},
{issn: data.journal.issn},
{doi_data: [
{doi: data.journal.doi},
{resource: data.journal.url},
]},
]},
{journal_issue: [
{publication_date: [
{month: date.getMonth()+1},
{year: date.getFullYear()},
]},
{journal_volume: [
{volume: data.volume},
]},
{issue: data.issue},
]},
{journal_article: [
{titles: [
{title: data.title},
]},
{ contributors:
data.authors.map((author, ind) => (
{person_name: [
{ _attr: {
contributor_role: "author",
sequence: (ind == 0)? "first" : "additional"
}},
{given_name: author.firstName},
{surname: author.lastName},
{affiliation: author.affiliation}
// TODO: ORCID?
]}
))
},
{publication_date: [
{month: date.getMonth()+1},
{day: date.getDate()},
{year: date.getFullYear()}
]},
{ publisher_item: [
{item_number: data.doi}
]},
{doi_data: [
{doi: data.doi},
//{timestamp: ""},
{resource: data.url},
]},
{citation_list:
data.citations.map(key =>
citation_xml(key, data.bibliography[key]))
}
]},
]},
]},
]};
return xml(crf_data);
}
function citation_xml(key, ent){
if (ent == undefined) return {};
var info = [];
info.push({_attr: {key: key}});
if ("title" in ent)
info.push({article_title: ent.title});
if ("author" in ent)
info.push({author: ent.author.split(" and ")[0].split(",")[0].trim()});
if ("journal" in ent)
info.push({journal_title: ent.journal});
if ("booktitle" in ent)
info.push({volume_title: ent.booktitle});
if ("volume" in ent)
info.push({volume: ent.volume});
if ("issue" in ent)
info.push({issue: ent.issue});
if ("doi" in ent)
info.push({doi: ent.doi});
return {citation: info}
}
function xml(obj) {
//console.log(typeof(obj), obj)
if (typeof obj === 'string') return obj;
if (typeof obj === 'number') return ""+obj;
let keys = Object.keys(obj);
if (keys.length != 1) console.error("can't interpret ", obj, "as xml");
let name = keys[0];
var full_content = obj[name];
var attr = {};
if (Array.isArray(full_content)){
var content = [];
for (var i in full_content) {
var obj = full_content[i];
var obj_name = Object.keys(obj)[0];
if ("_attr" == obj_name) {
attr = obj["_attr"];
} else {
//console.log(Object.keys(obj)[0])
content.push(obj);
}
}
} else {
content = full_content;
}
if (content == undefined){
content = "undefined"
}
let attr_string = "";
for (var k in attr) {
attr_string += ` ${k}=\"${attr[k]}\"`
}
//console.log(typeof content, Array.isArray(content), content instanceof String, content)
if (Array.isArray(content)){
content = content.map(xml);
content = content.join("\n").split("\n");
content = content.map(s => " " + s).join("\n")
var result = `<${name}${attr_string}>\n${content}\n</${name}>`;
} else {
content = xml(content);
var result = `<${name}${attr_string}>${content}</${name}>`;
}
return result;
}
+92
View File
@@ -0,0 +1,92 @@
import logo from "./distill-logo.svg";
const html = `
<style>
dt-header {
display: block;
position: relative;
height: 60px;
background-color: hsl(200, 60%, 15%);
width: 100%;
box-sizing: border-box;
z-index: 2;
color: rgba(0, 0, 0, 0.8);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
}
dt-header .content {
height: 70px;
}
dt-header a {
font-size: 16px;
height: 60px;
line-height: 60px;
text-decoration: none;
color: rgba(255, 255, 255, 0.8);
padding: 22px 0;
}
dt-header a:hover {
color: rgba(255, 255, 255, 1);
}
dt-header svg {
width: 24px;
position: relative;
top: 4px;
margin-right: 2px;
}
@media(min-width: 1080px) {
dt-header {
height: 70px;
}
dt-header a {
height: 70px;
line-height: 70px;
padding: 28px 0;
}
dt-header .logo {
}
}
dt-header svg path {
fill: none;
stroke: rgba(255, 255, 255, 0.8);
stroke-width: 3px;
}
dt-header .logo {
font-size: 17px;
font-weight: 200;
}
dt-header .nav {
float: right;
font-weight: 300;
}
dt-header .nav a {
font-size: 12px;
margin-left: 24px;
text-transform: uppercase;
}
</style>
<div class="content l-page">
<a href="/" class="logo">
${logo}
Distill
</a>
<div class="nav">
<a href="/faq">About</a>
<a href="https://github.com/distillpub">GitHub</a>
<!-- https://twitter.com/distillpub -->
</div>
</div>
`
export default function(dom, data) {
let el = dom.querySelector("dt-header");
if(el) {
el.innerHTML = html;
} else {
let header = dom.createElement("dt-header");
header.innerHTML = html;
let b = dom.querySelector("body");
b.insertBefore(header, b.firstChild);
}
}
+7
View File
@@ -0,0 +1,7 @@
import code from './hover-box.txt';
export default function(dom) {
let s = dom.createElement("script");
s.textContent = code;
dom.querySelector("body").appendChild(s);
}
+108
View File
@@ -0,0 +1,108 @@
// DistillHoverBox
//=====================================
function DistillHoverBox(key, pos){
if (!(key in DistillHoverBox.contentMap)){
console.error("No DistillHoverBox content registered for key", key);
}
if (key in DistillHoverBox.liveBoxes) {
console.error("There already exists a DistillHoverBox for key", key);
} else {
for (var k in DistillHoverBox.liveBoxes)
DistillHoverBox.liveBoxes[k].remove();
DistillHoverBox.liveBoxes[key] = this;
}
this.key = key;
var pretty = window.innerWidth > 600;
var padding = pretty? 18 : 12;
var outer_padding = pretty ? 18 : 0;
var bbox = document.querySelector("body").getBoundingClientRect();
var left = pos[0] - bbox.left, top = pos[1] - bbox.top;
var width = Math.min(window.innerWidth-2*outer_padding, 648);
left = Math.min(left, window.innerWidth-width-outer_padding);
width = width - 2*padding;
var str = `<div style="position: absolute;
background-color: #FFF;
white-s
opacity: 0.95;
max-width: ${width}px;
top: ${top}px;
left: ${left}px;
border: 1px solid rgba(0, 0, 0, 0.25);
padding: ${padding}px;
border-radius: ${pretty? 3 : 0}px;
box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.2);
z-index: ${1e6}" >
${DistillHoverBox.contentMap[key]}
</div>`;
this.div = appendBody(str);
DistillHoverBox.bind (this.div, key);
}
DistillHoverBox.prototype.remove = function remove(){
if (this.div) this.div.remove();
if (this.timeout) clearTimeout(this.timeout);
delete DistillHoverBox.liveBoxes[this.key];
}
DistillHoverBox.prototype.stopTimeout = function stopTimeout() {
if (this.timeout) clearTimeout(this.timeout);
}
DistillHoverBox.prototype.extendTimeout = function extendTimeout(T) {
//console.log("extend", T)
var this_ = this;
this.stopTimeout();
this.timeout = setTimeout(() => this_.remove(), T);
}
DistillHoverBox.liveBoxes = {};
DistillHoverBox.contentMap = {};
DistillHoverBox.bind = function bind(node, key) {
if (typeof node == "string"){
node = document.querySelector(node);
}
node.addEventListener("mouseover", () => {
var bbox = node.getBoundingClientRect();
if (!(key in DistillHoverBox.liveBoxes)){
new DistillHoverBox(key, [bbox.right, bbox.bottom]);
}
DistillHoverBox.liveBoxes[key].stopTimeout();
});
node.addEventListener("mouseout", () => {
if (key in DistillHoverBox.liveBoxes){
DistillHoverBox.liveBoxes[key].extendTimeout(250);
}
});
}
function appendBody(str){
var node = nodeFromString(str);
var body = document.querySelector("body");
body.appendChild(node);
return node;
}
function nodeFromString(str) {
var div = document.createElement("div");
div.innerHTML = str;
return div.firstChild;
}
var hover_es = document.querySelectorAll("span[data-hover]");
hover_es = [].slice.apply(hover_es);
hover_es.forEach((e,n) => {
var key = "hover-"+n;
var content = e.getAttribute("data-hover");
DistillHoverBox.contentMap[key] = content;
DistillHoverBox.bind(e, key);
});
+19
View File
@@ -0,0 +1,19 @@
export default function(dom) {
if (!dom.querySelector("html").getAttribute("lang")) {
dom.querySelector("html").setAttribute("lang", "en")
}
let head = dom.querySelector("head");
if (!dom.querySelector("meta[charset]")) {
let meta = dom.createElement("meta");
meta.setAttribute("charset", "utf-8");
head.appendChild(meta);
}
if (!dom.querySelector("meta[name=viewport]")) {
let meta = dom.createElement("meta");
meta.setAttribute("name", "viewport");
meta.setAttribute("content", "width=device-width, initial-scale=1");
head.appendChild(meta);
}
}
+18
View File
@@ -0,0 +1,18 @@
import fetch from "fetch";
let fetchUrl = fetch.fetchUrl
export default function(dom, data) {
var includeTags = [].slice.apply(dom.querySelectorAll("dt-include"));
includeTags.forEach(el => {
let src = el.getAttribute("src");
fetchUrl(src, (err, meta, body) => {
console.log(err, meta, body);
el.innerHTML = body.toString();
})
});
data.bibliography = bibliography;
data.citations = citations;
}
+20
View File
@@ -0,0 +1,20 @@
import marked from 'marked';
marked.setOptions({
gfm: true,
smartypants: true
});
export default function(dom, data) {
let markdownElements = [].slice.call(dom.querySelectorAll('[markdown]'));
markdownElements.forEach(el => {
let content = el.innerHTML;
// Set default indents
content = content.replace(/\n/, "");
let tabs = content.match(/\s*/);
content = content.replace(new RegExp("\n" + tabs, "g"), "\n");
content = content.trim();
el.innerHTML = marked(content);
});
}
+114
View File
@@ -0,0 +1,114 @@
import favicon from './distill-favicon.base64';
export default function(dom, data) {
let head = dom.querySelector("head");
let appendHead = html => appendHtml(head, html);
function meta(name, content) {
if (content)
appendHead(` <meta name="${name}" content="${content}" >\n`);
}
appendHead(`
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="icon" type="image/png" href="data:image/png;base64,${favicon}">
<link href="/rss.xml" rel="alternate" type="application/rss+xml" title="Articles from Distill">
<link rel="canonical" href="${data.url}">
<title>${data.title}</title>
`);
appendHead(`
<!-- https://schema.org/Article -->
<meta property="article:published" itemprop="datePublished" content="${data.publishedYear}-${data.publishedMonthPadded}-${data.publishedDayPadded}" />
<meta property="article:modified" itemprop="dateModified" content="${data.updatedDate}" />
`);
data.authors.forEach((a) => {
appendHtml(head, `
<meta property="article:author" content="${a.firstName} ${a.lastName}" />`)
});
appendHead(`
<!-- https://developers.facebook.com/docs/sharing/webmasters#markup -->
<meta property="og:type" content="article"/>
<meta property="og:title" content="${data.title}"/>
<meta property="og:description" content="${data.description}">
<meta property="og:url" content="${data.url}"/>
<meta property="og:image" content="${data.url}/thumbnail.jpg"/>
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Distill" />
`);
appendHead(`
<!-- https://dev.twitter.com/cards/types/summary -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="${data.title}">
<meta name="twitter:description" content="${data.description}">
<meta name="twitter:url" content="${data.url}">
<meta name="twitter:image" content="${data.url}/thumbnail.jpg">
<meta name="twitter:image:width" content="560">
<meta name="twitter:image:height" content="295">
`);
// if this is a proprer article, generate Google Scholar meta data
if (data.doiSuffix){
appendHead(`
<!-- https://scholar.google.com/intl/en/scholar/inclusion.html#indexing -->\n`);
meta("citation_title", data.title);
meta("citation_fulltext_html_url", data.url);
meta("citation_volume", data.volume);
meta("citation_issue", data.issue);
meta("citation_firstpage", data.doiSuffix? `e${data.doiSuffix}` : undefined);
meta("citation_doi", data.doi);
let journal = data.journal || {};
meta("citation_journal_title", journal.name);
meta("citation_journal_abbrev", journal.nameAbbrev);
meta("citation_issn", journal.issn);
meta("citation_publisher", journal.publisher);
if (data.publishedDate){
let zeroPad = (n) => { return n < 10 ? "0" + n : n; };
meta("citation_publication_date", `${data.publishedYear}/${data.publishedMonthPadded}/${data.publishedDayPadded}`);
}
(data.authors || []).forEach((a) => {
meta("citation_author", `${a.lastName}, ${a.firstName}`);
meta("citation_author_institution", a.affiliation);
});
if (data.citations) {
data.citations.forEach(key => {
let d = data.bibliography[key];
if(!d) {
console.warn("No bibliography data fround for " + key)
} else {
meta("citation_reference", citation_meta_content(data.bibliography[key]) )
};
});
}
}
}
function appendHtml(el, html) {
el.innerHTML += html;
}
function citation_meta_content(ref){
var content = `citation_title=${ref.title};`;
ref.author.split(" and ").forEach(author => {
content += `citation_author=${author.trim()};`;
});
if ("journal" in ref){
content += `citation_journal_title=${ref.journal};`;
}
if ("volume" in ref) {
content += `citation_volume=${ref.volume};`;
}
if ("issue" in ref || "number" in ref){
content += `citation_number=${ref.issue || ref.number};`;
}
/*content += `citation_first_page=${};`;
content += `citation_publication_date=${};`;*/
return content;
}
+269
View File
@@ -0,0 +1,269 @@
dt-article {
display: block;
color: rgba(0, 0, 0, 0.8);
font: 17px/1.55em -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
padding-bottom: 72px;
overflow: hidden;
background: white;
min-height: calc(100vh - 70px - 182px);
}
@media(min-width: 1024px) {
dt-article {
font-size: 20px;
}
}
/* H1 */
dt-article h1 {
margin-top: 18px;
font-weight: 400;
font-size: 40px;
line-height: 1em;
font-family: HoeflerText-Regular, Cochin, Georgia, serif;
}
@media(min-width: 768px) {
dt-article h1 {
font-size: 46px;
margin-top: 48px;
margin-bottom: 12px;
}
}
@media(min-width: 1080px) {
.centered h1 {
text-align: center;
}
dt-article h1 {
font-size: 50px;
letter-spacing: -0.02em;
}
dt-article > h1:first-of-type,
dt-article section > h1:first-of-type {
margin-top: 80px;
}
}
@media(min-width: 1200px) {
dt-article h1 {
font-size: 56px;
}
dt-article > h1:first-of-type {
margin-top: 100px;
}
}
/* H2 */
dt-article h2 {
font-family: HoeflerText-Regular, Cochin, Georgia, serif;
font-weight: 400;
font-size: 26px;
line-height: 1.25em;
margin-top: 36px;
margin-bottom: 24px;
}
@media(min-width: 1024px) {
dt-article h2 {
margin-top: 48px;
font-size: 30px;
}
}
dt-article h1 + h2 {
font-weight: 300;
font-size: 20px;
line-height: 1.4em;
margin-top: 8px;
font-style: normal;
}
@media(min-width: 1080px) {
.centered h1 + h2 {
text-align: center;
}
dt-article h1 + h2 {
margin-top: 12px;
font-size: 24px;
}
}
/* H3 */
dt-article h3 {
font-family: HoeflerText-Regular, Georgia, serif;
font-weight: 400;
font-size: 20px;
line-height: 1.4em;
margin-top: 36px;
margin-bottom: 18px;
font-style: italic;
}
dt-article h1 + h3 {
margin-top: 48px;
}
@media(min-width: 1024px) {
dt-article h3 {
font-size: 26px;
}
}
/* H4 */
dt-article h4 {
font-weight: 600;
text-transform: uppercase;
font-size: 14px;
line-height: 1.4em;
}
dt-article a {
color: inherit;
}
dt-article p,
dt-article ul,
dt-article ol {
margin-bottom: 24px;
font-family: Georgia, serif;
}
dt-article p b,
dt-article ul b,
dt-article ol b {
-webkit-font-smoothing: antialiased;
}
dt-article a {
border-bottom: 1px solid rgba(0, 0, 0, 0.4);
text-decoration: none;
}
dt-article a:hover {
border-bottom: 1px solid rgba(0, 0, 0, 0.8);
}
dt-article .link {
text-decoration: underline;
cursor: pointer;
}
dt-article ul,
dt-article ol {
padding-left: 24px;
}
dt-article li {
margin-bottom: 24px;
margin-left: 0;
padding-left: 0;
}
dt-article pre {
font-size: 14px;
margin-bottom: 20px;
}
dt-article hr {
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
margin-top: 60px;
margin-bottom: 60px;
}
dt-article section {
margin-top: 60px;
margin-bottom: 60px;
}
/* Figure */
dt-article figure {
position: relative;
margin-top: 30px;
margin-bottom: 30px;
}
@media(min-width: 1024px) {
dt-article figure {
margin-top: 48px;
margin-bottom: 48px;
}
}
dt-article figure img {
width: 100%;
}
dt-article figure svg text,
dt-article figure svg tspan {
}
dt-article figure figcaption {
color: rgba(0, 0, 0, 0.6);
font-size: 12px;
line-height: 1.5em;
}
@media(min-width: 1024px) {
dt-article figure figcaption {
font-size: 13px;
}
}
dt-article figure.external img {
background: white;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.1);
padding: 18px;
box-sizing: border-box;
}
dt-article figure figcaption a {
color: rgba(0, 0, 0, 0.6);
}
/*dt-article figure figcaption::before {
position: relative;
display: block;
top: -20px;
content: "";
width: 25px;
border-top: 1px solid rgba(0, 0, 0, 0.3);
}*/
dt-article span.equation-mimic {
font-family: georgia;
font-size: 115%;
font-style: italic;
}
dt-article figure figcaption b {
font-weight: 600;
color: rgba(0, 0, 0, 1.0);
}
dt-article > dt-code,
dt-article section > dt-code {
display: block;
}
dt-article .citation {
color: #668;
cursor: pointer;
}
dt-include {
width: auto;
display: block;
}
+67
View File
@@ -0,0 +1,67 @@
html {
font: 400 16px/1.55em -apple-system, BlinkMacSystemFont, "Roboto", Helvetica, sans-serif;
/*background-color: hsl(223, 9%, 25%);*/
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
body {
margin: 0;
/*background-color: hsl(223, 9%, 25%);*/
}
a {
color: #004276;
}
figure {
margin: 0;
}
h1 {
font-family: Cochin, Georgia, serif;
}
/*
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}*/
+147
View File
@@ -0,0 +1,147 @@
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code {
white-space: nowrap;
background: rgba(0, 0, 0, 0.04);
border-radius: 2px;
padding: 4px 7px;
font-size: 15px;
color: rgba(0, 0, 0, 0.6);
}
pre code {
display: block;
background: white;
border-left: 3px solid rgba(0, 0, 0, 0.05);
padding: 0 0 0 24px;
}
code[class*="language-"],
pre[class*="language-"] {
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
}
/* Inline code */
:not(pre) > code[class*="language-"] {
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
+353
View File
@@ -0,0 +1,353 @@
/*
Column: 60px
Gutter: 24px
Body: 648px
- 8 columns
- 7 gutters
Middle: 816px
Page: 984px
- 12 columns
- 11 gutters
*/
.l-body,
.l-body-outset,
.l-page,
.l-page-outset,
.l-middle,
.l-middle-outset,
dt-article > div,
dt-article > p,
dt-article > h1,
dt-article > h2,
dt-article > h3,
dt-article > h4,
dt-article > figure,
dt-article > ul,
dt-article > dt-byline,
dt-article > dt-code,
dt-article section > div,
dt-article section > p,
dt-article section > h1,
dt-article section > h2,
dt-article section > h3,
dt-article section > h4,
dt-article section > figure,
dt-article section > ul,
dt-article section > dt-byline,
dt-article section > dt-code {
width: auto;
margin-left: 24px;
margin-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
.l-body,
.l-body-outset,
.l-page,
.l-page-outset,
.l-middle,
.l-middle-outset,
dt-article > div,
dt-article > p,
dt-article > h1,
dt-article > h2,
dt-article > h3,
dt-article > h4,
dt-article > figure,
dt-article > ul,
dt-article > dt-byline,
dt-article > dt-code,
dt-article section > div,
dt-article section > p,
dt-article section > h1,
dt-article section > h2,
dt-article section > h3,
dt-article section > h4,
dt-article section > figure,
dt-article section > ul,
dt-article section > dt-byline,
dt-article section > dt-code {
margin-left: 72px;
margin-right: 72px;
}
}
@media(min-width: 1080px) {
.l-body,
dt-article > div,
dt-article > p,
dt-article > h2,
dt-article > h3,
dt-article > h4,
dt-article > figure,
dt-article > ul,
dt-article > dt-byline,
dt-article > dt-code,
dt-article section > div,
dt-article section > p,
dt-article section > h2,
dt-article section > h3,
dt-article section > h4,
dt-article section > figure,
dt-article section > ul,
dt-article section > dt-byline,
dt-article section > dt-code {
margin-left: calc(50% - 984px / 2);
width: 648px;
}
.l-body-outset,
dt-article .l-body-outset {
margin-left: calc(50% - 984px / 2 - 96px/2);
width: calc(648px + 96px);
}
.l-middle,
dt-article .l-middle {
width: 816px;
margin-left: calc(50% - 984px / 2);
margin-right: auto;
}
.l-middle-outset,
dt-article .l-middle-outset {
width: calc(816px + 96px);
margin-left: calc(50% - 984px / 2 - 48px);
margin-right: auto;
}
dt-article > h1,
dt-article section > h1,
.l-page,
dt-article .l-page,
dt-article.centered .l-page {
width: 984px;
margin-left: auto;
margin-right: auto;
}
.l-page-outset,
dt-article .l-page-outset,
dt-article.centered .l-page-outset {
width: 1080px;
margin-left: auto;
margin-right: auto;
}
.l-screen,
dt-article .l-screen,
dt-article.centered .l-screen {
margin-left: auto;
margin-right: auto;
width: auto;
}
.l-screen-inset,
dt-article .l-screen-inset,
dt-article.centered .l-screen-inset {
margin-left: 24px;
margin-right: 24px;
width: auto;
}
.l-gutter,
dt-article .l-gutter {
clear: both;
float: right;
margin-top: 0;
margin-left: 24px;
margin-right: calc((100vw - 984px) / 2 + 168px);
width: calc((984px - 648px) / 2 - 24px);
}
/* Side */
.side.l-body,
dt-article .side.l-body {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 984px + 648px) / 2);
width: calc(648px / 2 - 24px - 84px);
}
.side.l-body-outset,
dt-article .side.l-body-outset {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 984px + 648px - 48px) / 2);
width: calc(648px / 2 - 48px + 24px);
}
.side.l-middle,
dt-article .side.l-middle {
clear: both;
float: right;
width: calc(456px - 84px);
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px);
}
.side.l-middle-outset,
dt-article .side.l-middle-outset {
clear: both;
float: right;
width: 456px;
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px);
}
.side.l-page,
dt-article .side.l-page {
clear: both;
float: right;
margin-left: 48px;
width: calc(624px - 84px);
margin-right: calc((100vw - 984px) / 2);
}
.side.l-page-outset,
dt-article .side.l-page-outset {
clear: both;
float: right;
width: 624px;
margin-right: calc((100vw - 984px) / 2);
}
}
/* Centered */
@media(min-width: 1080px) {
.centered .l-body,
.centered.l-body,
dt-article.centered > div,
dt-article.centered > p,
dt-article.centered > h2,
dt-article.centered > h3,
dt-article.centered > h4,
dt-article.centered > figure,
dt-article.centered > ul,
dt-article.centered > dt-byline,
dt-article.centered > dt-code,
dt-article.centered section > div,
dt-article.centered section > p,
dt-article.centered section > h2,
dt-article.centered section > h3,
dt-article.centered section > h4,
dt-article.centered section > figure,
dt-article.centered section > ul,
dt-article.centered section > dt-byline,
dt-article.centered section > dt-code,
dt-article section.centered > div,
dt-article section.centered > p,
dt-article section.centered > h2,
dt-article section.centered > h3,
dt-article section.centered > h4,
dt-article section.centered > figure,
dt-article section.centered > ul,
dt-article section.centered > dt-byline,
dt-article section.centered > dt-code {
margin-left: auto;
margin-right: auto;
width: 648px;
}
.centered .l-body-outset,
.centered.l-body-outset,
dt-article.centered .l-body-outset {
margin-left: auto;
margin-right: auto;
width: calc(648px + 96px);
}
dt-article.centered > h1,
dt-article.centered section > h1,
dt-article section.centered > h1,
.centered .l-middle,
.centered.l-middle,
dt-article.centered .l-middle {
width: 816px;
margin-left: auto;
margin-right: auto;
}
.centered .l-middle-outset,
.centered.l-middle-outset,
dt-article.centered .l-middle-outset {
width: calc(816px + 96px);
margin-left: auto;
margin-right: auto;
}
/* page and screen are already centered */
/* Side */
.centered .side.l-body,
.centered dt-article .side.l-body {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 648px) / 2);
width: calc(4 * 60px + 3 * 24px);
}
.centered .side.l-body-outset,
.centered dt-article .side.l-body-outset {
clear: both;
float: right;
margin-top: 0;
margin-left: 48px;
margin-right: calc((100vw - 648px) / 2);
width: calc(4 * 60px + 3 * 24px);
}
.centered .side.l-middle,
.centered dt-article .side.l-middle {
clear: both;
float: right;
width: 396px;
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px / 2);
}
.centered .side.l-middle-outset,
.centered dt-article .side.l-middle-outset {
clear: both;
float: right;
width: 456px;
margin-left: 48px;
margin-right: calc((100vw - 984px) / 2 + 168px);
}
.centered .side.l-page,
.centered dt-article .side.l-page {
clear: both;
float: right;
width: 480px;
margin-right: calc((100vw - 984px) / 2);
}
.centered .side.l-page-outset,
.centered dt-article .side.l-page-outset {
clear: both;
float: right;
width: 480px;
margin-right: calc((100vw - 984px) / 2);
}
.centered .l-gutter,
.centered.l-gutter,
dt-article.centered .l-gutter {
clear: both;
float: right;
margin-top: 0;
margin-left: 24px;
margin-right: calc((100vw - 984px) / 2);
width: calc((984px - 648px) / 2 - 24px);
}
}
/* Rows and Columns */
.row {
display: flex;
}
.column {
flex: 1;
box-sizing: border-box;
margin-right: 24px;
margin-left: 24px;
}
.row > .column:first-of-type {
margin-left: 0;
}
.row > .column:last-of-type {
margin-right: 0;
}
+20
View File
@@ -0,0 +1,20 @@
@media print {
@page {
size: 8in 11in;
}
html {
}
p, code {
page-break-inside: avoid;
}
h2, h3 {
page-break-after: avoid;
}
dt-header {
visibility: hidden;
}
dt-footer {
display: none!important;
}
}
+11
View File
@@ -0,0 +1,11 @@
import base from './styles-base.css';
import layout from './styles-layout.css';
import article from './styles-article.css';
import code from './styles-code.css';
import print from './styles-print.css';
export default function(dom) {
let s = dom.createElement("style");
s.textContent = base + layout + article + code + print;
dom.querySelector("head").appendChild(s);
}
+98
View File
@@ -0,0 +1,98 @@
export default function(dom, data) {
var textNodes = dom.createTreeWalker(
dom.body,
dom.defaultView.NodeFilter.SHOW_TEXT
);
while (textNodes.nextNode()) {
var n = textNodes.currentNode,
text = n.nodeValue;
if (text && acceptNode(n)) {
text = quotes(text);
text = punctuation(text);
n.nodeValue = text;
}
}
}
function acceptNode(node) {
var parent = node.parentElement;
var isMath = (parent && parent.getAttribute && parent.getAttribute("class")) ? parent.getAttribute("class").includes("katex") || parent.getAttribute("class").includes("MathJax") : false;
return parent &&
parent.nodeName !== "SCRIPT" &&
parent.nodeName !== "STYLE" &&
parent.nodeName !== "CODE" &&
parent.nodeName !== "PRE" &&
parent.nodeName !== "SPAN" &&
parent.nodeName !== "D-HEADER" &&
parent.nodeName !== "D-BYLINE" &&
parent.nodeName !== "D-MATH" &&
parent.nodeName !== "D-CODE" &&
parent.nodeName !== "D-BIBLIOGRAPHY" &&
parent.nodeName !== "D-FOOTER" &&
parent.nodeType !== 8 && //comment nodes
!isMath;
}
/*!
* typeset - Typesetting for the web
* @version v0.1.6
* @link https://github.com/davidmerfield/Typeset.js
* @author David Merfield
*/
// which has a CC0 license
// http://creativecommons.org/publicdomain/zero/1.0/
function punctuation(text){
// Dashes
text = text.replace(/--/g, '\u2014');
text = text.replace(/ \u2014 /g,"\u2009\u2014\u2009"); //this has thin spaces
// Elipses
text = text.replace(/\.\.\./g,'…');
// Nbsp for punc with spaces
var NBSP = "\u00a0";
var NBSP_PUNCTUATION_START = /([«¿¡]) /g;
var NBSP_PUNCTUATION_END = / ([\!\?:;\.,‽»])/g;
text = text.replace(NBSP_PUNCTUATION_START, '$1' + NBSP);
text = text.replace(NBSP_PUNCTUATION_END, NBSP + '$1');
return text;
}
function quotes(text) {
text = text
.replace(/(\W|^)"([^\s\!\?:;\.,‽»])/g, '$1\u201c$2') // beginning "
.replace(/(\u201c[^"]*)"([^"]*$|[^\u201c"]*\u201c)/g, '$1\u201d$2') // ending "
.replace(/([^0-9])"/g,'$1\u201d') // remaining " at end of word
.replace(/(\W|^)'(\S)/g, '$1\u2018$2') // beginning '
.replace(/([a-z])'([a-z])/ig, '$1\u2019$2') // conjunction's possession
.replace(/((\u2018[^']*)|[a-z])'([^0-9]|$)/ig, '$1\u2019$3') // ending '
.replace(/(\u2018)([0-9]{2}[^\u2019]*)(\u2018([^0-9]|$)|$|\u2019[a-z])/ig, '\u2019$2$3') // abbrev. years like '93
.replace(/(\B|^)\u2018(?=([^\u2019]*\u2019\b)*([^\u2019\u2018]*\W[\u2019\u2018]\b|[^\u2019\u2018]*$))/ig, '$1\u2019') // backwards apostrophe
.replace(/'''/g, '\u2034') // triple prime
.replace(/("|'')/g, '\u2033') // double prime
.replace(/'/g, '\u2032');
// Allow escaped quotes
text = text.replace(/\\“/, '\"');
text = text.replace(/\\”/, '\"');
text = text.replace(/\\/, '\'');
text = text.replace(/\\/, '\'');
return text;
}
function ligatures(text){
text = text.replace(/fi/g, 'fi');
text = text.replace(/fl/g, 'fl');
return text;
};