Switch to central controller for distributed control flow

This commit is contained in:
Ludwig Schubert
2017-08-09 16:47:35 -07:00
parent 929b159aaa
commit 9fe826447b
37 changed files with 5358 additions and 4778 deletions
+145
View File
@@ -0,0 +1,145 @@
import { FrontMatter } from '../transforms/data';
import { collectCitations } from './d-cite';
const frontMatter = new FrontMatter();
// set up global controller object
/* functions whose names start with 'on' will be registered as listeners on d-article */
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(event) {
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;
},
DOMContentLoaded(event) {
console.debug('DOMContentLoaded.');
const frontMatterTag = document.querySelector('d-front-matter');
const data = frontMatterTag.parse();
frontMatter.mergeFromYMLFrontmatter(data);
const appendix = document.querySelector('distill-appendix');
appendix.frontMatter = frontMatter;
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
update: {
cite(element) {
},
footnoteList(element) {
}
}
} // Controller
+4 -6
View File
@@ -1,5 +1,5 @@
import {Template} from "../mixins/template";
import {body} from "./layout";
import { Template } from "../mixins/template";
import { body } from "./layout";
const T = Template("d-abstract", `
<style>
@@ -13,8 +13,6 @@ const T = Template("d-abstract", `
</style>
`, false);
export default class Abstract extends T(HTMLElement) {
static get is() { return "d-abstract"; }
}
export class Abstract extends T(HTMLElement) {
customElements.define(Abstract.is, Abstract);
}
+2 -8
View File
@@ -1,8 +1,6 @@
import { Template } from "../mixins/template";
const name = 'd-acknowledgements';
const T = Template(name, `
const T = Template('d-acknowledgements', `
<style>
::slotted(h3) {
font-size: 15px;
@@ -20,10 +18,6 @@ const T = Template(name, `
<slot></slot>
`);
export default class Acknowledgements extends T(HTMLElement) {
static get is() { return name; }
export class Acknowledgements extends T(HTMLElement) {
}
customElements.define(name, Acknowledgements);
+1 -5
View File
@@ -25,10 +25,6 @@ ${page(".l-body")}
</div>
`);
export default class Appendix extends T(HTMLElement) {
static get is() { return "d-appendix"; }
export class Appendix extends T(HTMLElement) {
}
customElements.define(Appendix.is, Appendix);
+22 -205
View File
@@ -1,209 +1,26 @@
import {Template} from "../mixins/template";
import { Template } from '../mixins/template';
import { Controller } from './controller';
const T = Template("d-article", `
<style>
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;
}
</style>
const T = Template('d-article', `
<style></style>
`, false);
export default class Article extends T(HTMLElement) {
static get is() { return "d-article"; }
}
customElements.define(Article.is, Article);
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!')
}
}
}
}
+66 -51
View File
@@ -1,9 +1,10 @@
import bibtexParse from "bibtex-parse-js";
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 "./citation";
const name = 'd-bibliography';
const T = Template(name, `
const T = Template('d-bibliography', `
<style>
.references {
font-size: 12px;
@@ -35,7 +36,7 @@ const T = Template(name, `
<ol></ol>
`);
function parseBibtex(bibtex) {
export function parseBibtex(bibtex) {
const bibliography = new Map();
const parsedEntries = bibtexParse.toJSON(bibtex);
for (const entry of parsedEntries) {
@@ -56,66 +57,80 @@ function parseBibtex(bibtex) {
return bibliography;
}
export default class Bibliography extends T(HTMLElement) {
export class Bibliography extends T(HTMLElement) {
constructor() {
super()
this.citations = new Array();
this.finishedLoading = false;
// 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';
// parse bibliography
const scriptTag = this.querySelector("script");
if (scriptTag) {
this.bibliography = parseBibtex(scriptTag.textContent);
this.finishedLoading = true;
// look through document and register existing citations
document.querySelectorAll('d-cite')
.forEach(citation => this.registerCitation(citation));
} else {
console.error("No script tag with bibtex found in d-bibliography tag!")
// 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);
}
}
getEntry(key) {
return this.bibliography.get(key);
}
hasEntry(key) {
return this.bibliography.has(key);
}
getIndex(key) {
return this.citations.indexOf(key);
}
registerCitation(citation) {
// a d-cite element may cite multiple sources
const keyString = citation.getAttribute("key");
const keys = keyString ? keyString.split(",") : [];
for (const key of keys) {
if (!this.bibliography.has(key)) {
console.error("Citation key '" + key + "' is not present in bibliography!")
} else if (this.citations.indexOf(key) === -1) {
this.citations.push(key);
const entry = this.getEntry(key);
// ensure citations list is visible
this.root.host.style.display = 'initial';
// 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);
}
}
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);
}
}
customElements.define(Bibliography.is, Bibliography);
+6 -15
View File
@@ -55,13 +55,8 @@ const T = Template("d-byline", `
display: inline;
}
@media(min-width: 768px) {
{
}
}
@media(min-width: 1080px) {
{
:host {
border-bottom: none;
}
@@ -152,17 +147,13 @@ const mustacheTemplate = `
{{/citation}}
`;
export default class Byline extends T(HTMLElement) {
static get is() { return "d-byline"; }
export class Byline extends T(HTMLElement) {
connectedCallback() {
const frontmatter = document.querySelector('d-front-matter');
const container = this.root.querySelector('.byline');
container.innerHTML = mustache.render(mustacheTemplate, frontmatter.data);
console.log(frontmatter.data)
// const frontmatter = document.querySelector('d-front-matter');
// const container = this.root.querySelector('.byline');
// container.innerHTML = mustache.render(mustacheTemplate, frontmatter.data);
// // console.log(frontmatter.data)
}
}
customElements.define(Byline.is, Byline);
+94 -50
View File
@@ -28,7 +28,7 @@ const T = Template('d-cite', `
}
</style>
<div style="display: none;" class="dt-hover-box">
<div style="display: none;" id="hover-box" class="dt-hover-box">
</div>
<span id="citation-" class="citation">
@@ -37,76 +37,120 @@ const T = Template('d-cite', `
</span>
`);
function inline_cite_short(keys, bibliography) {
function cite_string(key) {
if (bibliography.hasEntry(key)){
return (bibliography.getIndex(key)+1).toString();
} else {
return "?";
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 "[" + keys.map(cite_string).join(", ") + "]";
return [...citations];
}
export class Cite extends T(HTMLElement) {
export default class Cite extends T(HTMLElement) {
/* Lifecycle */
constructor() {
super()
this._key = null;
Cite.currentId += 1;
this.citeId = Cite.currentId;
// 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) {
// name will always be "key" due to observedAttributes
this._key = newValue;
this.renderContent();
}
get key() {
return this._key;
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);
}
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.`)
}
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.`)
// }
// }
}
Cite.currentId = 0;
customElements.define(Cite.is, Cite);
+3 -9
View File
@@ -6,9 +6,7 @@ import css from "prismjs/themes/prism.css";
import { Template } from "../mixins/template.js"
import { Mutating } from "../mixins/mutating.js"
const templateString = `
const T = Template("d-code", `
<style>
code {
@@ -32,11 +30,9 @@ ${css}
<code id="code-container"></code>
`
`);
const Templated = Template("d-code", templateString);
export class Code extends Mutating(Templated(HTMLElement)) {
export class Code extends Mutating(T(HTMLElement)) {
renderContent() {
@@ -76,5 +72,3 @@ export class Code extends Mutating(Templated(HTMLElement)) {
}
}
customElements.define("d-code", Code);
+33 -26
View File
@@ -1,7 +1,7 @@
import { Template } from "../mixins/template";
// import { Store } from './store';
const name = 'd-footnote-list';
const T = Template(name, `
const T = Template('d-footnote-list', `
<style>
ol {
padding: 0 0 0 18px;
@@ -33,40 +33,47 @@ a.footnote-backlink {
<ol></ol>
`);
export default class FootnoteList extends T(HTMLElement) {
static get is() { return name; }
export class FootnoteList extends T(HTMLElement) {
connectedCallback() {
this.list = this.root.querySelector('ol');
this.footnotes = new Map();
// footnotes list is initially hidden
this.root.host.style.display = 'none';
// look through document and register existing footnotes
document.querySelectorAll('d-footnote')
.forEach(footnote => this.registerFootnote(footnote));
// Store.subscribeTo('footnotes', (footnote) => {
// this.renderFootnote(footnote);
// });
}
registerFootnote(element) {
// check if we already know about this footnote
if (!this.footnotes.has(element.id)) {
this.footnotes.set(element.id, element);
// 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'
// construct and append list item to show footnote
const listItem = document.createElement('li');
listItem.innerHTML = element.innerHTML;
const backlink = document.createElement('a');
backlink.setAttribute('class', 'footnote-backlink');
backlink.textContent = '[↩]';
backlink.href = `#${element.id}`;
listItem.appendChild(backlink);
this.list.appendChild(listItem);
} /*else {
console.debug('Had already registered footnote ' + element.id + '!')
}*/
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';
}
}
renderFootnote(element) {
}
}
customElements.define(name, FootnoteList);
+23 -16
View File
@@ -1,7 +1,8 @@
import { Template } from "../mixins/template.js"
import { HoverBox } from "./hover-box.js"
// import { Store } from './store';
const templateString = `
const T = Template("d-footnote", `
<style>
d-math[block] {
@@ -11,18 +12,34 @@ d-math[block] {
</style>
<div style="display: none;" class="dt-hover-box">
<slot></slot>
<slot id='slot'></slot>
</div>
<sup><span id="fn-" data-hover-ref="" style="cursor:pointer"></span></sup>
`;
`);
const TemplatedFootnote = Template("d-footnote", templateString);
export class Footnote extends T(HTMLElement) {
export class Footnote extends TemplatedFootnote(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();
@@ -37,20 +54,10 @@ export class Footnote extends TemplatedFootnote(HTMLElement) {
span.setAttribute('id', 'fn-' + IdString);
span.setAttribute('data-hover-ref', div.id);
span.textContent = IdString;
HoverBox.get_box(div).bind(span);
// register with footnote list should there be one
const footnoteList = document.querySelector('d-footnote-list');
if (footnoteList) {
customElements.whenDefined('d-footnote-list').then(() => {
footnoteList.registerFootnote(this);
});
}
HoverBox.get_box(div).bind(span);
}
}
Footnote.currentFootnoteId = 0;
customElements.define("d-footnote", Footnote);
+25 -41
View File
@@ -1,52 +1,36 @@
import ymlParse from "js-yaml";
import expandData from "../transforms/expand-data"
export default class FrontMatter extends HTMLElement {
export class FrontMatter extends HTMLElement {
static get is() { return "d-front-matter"; }
constructor() {
super();
this.data = {};
const options = {childList: true, characterData: true, subtree: true};
const observer = new MutationObserver( (mutation) => {
const data = this.parse();
this.notify(data);
});
observer.observe(this, options);
}
connectedCallback() {
const el = this.querySelector("script");
if (el) {
const text = el.textContent;
this.parse(ymlParse.safeLoad(text));
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 {};
}
}
parse(localData) {
this.data.title = localData.title ? localData.title : "Untitled";
this.data.description = localData.description ? localData.description : "No description.";
this.data.publishedDate = localData.published ? new Date(localData.published) : false;
this.data.authors = localData.authors ? localData.authors : [];
this.data.authors = this.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;
});
expandData(null, this.data);
notify(data) {
const options = { detail: data, bubbles: true };
const event = new CustomEvent('onFrontMatterChanged', options);
document.dispatchEvent(event);
}
}
customElements.define(FrontMatter.is, FrontMatter);
}
+8 -10
View File
@@ -1,30 +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 templateString = `
const T = Template("d-math", `
<style>
d-math[block] {
display: block;
}
${katexCSS}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css">
<span id="katex-container"></span>
`;
`);
const TemplatedMath = Template("d-math", templateString);
export class Math extends Mutating(TemplatedMath(HTMLElement)) {
export class DMath extends Mutating(T(HTMLElement)) {
renderContent() {
const options = { displayMode: this.hasAttribute("block") };
const container = this.shadowRoot.querySelector("#katex-container")
const container = this.root.querySelector("#katex-container");
katex.render(this.textContent, container, options);
}
}
customElements.define("d-math", Math);
}
+5 -6
View File
@@ -1,5 +1,5 @@
import {Template} from "../mixins/template";
import {body} from "./layout";
import { Template } from "../mixins/template";
import { body } from "./layout";
const T = Template("d-references", `
<style>
@@ -9,11 +9,10 @@ d-references {
</style>
`, false);
export default class References extends T(HTMLElement) {
static get is() { return "d-references"; }
export class References extends T(HTMLElement) {
connectedCallback() {
super.connectedCallback();
}
}
customElements.define(References.is, References);
}
+3 -7
View File
@@ -1,5 +1,5 @@
import {Template} from "../mixins/template";
import {page} from "./layout";
import { Template } from "../mixins/template";
import { page } from "./layout";
const T = Template("d-title", `
<style>
@@ -32,9 +32,7 @@ ${page("::slotted(h1), ::slotted(h2)")}
<d-byline></d-byline>
`);
export default class Title extends T(HTMLElement) {
static get is() { return "d-title"; }
export class Title extends T(HTMLElement) {
connectedCallback() {
super.connectedCallback();
@@ -44,5 +42,3 @@ export default class Title extends T(HTMLElement) {
}
}
customElements.define(Title.is, Title);
+2 -6
View File
@@ -8,10 +8,6 @@ d-toc {
</style>
`, false);
export default class Toc extends T(HTMLElement) {
static get is() {
return "d-toc";
}
}
export class TOC extends T(HTMLElement) {
customElements.define(Toc.is, Toc);
}
-62
View File
@@ -1,62 +0,0 @@
import {page} from "./layout";
import mustache from "mustache";
let mustacheTemplate = `
<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>
<h3>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="">create an issue on GitHub</a>. </p>
<h3>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="https://github.com/distillpub/post--augmented-rnns">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>Citation</h3>
<p>For attribution in academic contexts, please cite this work as</p>
<pre class="citation short">Olah &amp; Carter, "Attention and Augmented Recurrent Neural Networks", Distill, 2016.</pre>
<p>BibTeX citation</p>
<pre class="citation long">@article{olah2016attention,
author = {Olah, Chris and Carter, Shan},
title = {Attention and Augmented Recurrent Neural Networks},
journal = {Distill},
year = {2016},
note = {http://distill.pub/2016/augmented-rnns}
}</pre>
`;
export default class DistillAppendix extends HTMLElement {
static get is() { return "distill-appendix"; }
connectedCallback(data) {
this.innerHTML = mustache.render(mustacheTemplate, data);
}
}
customElements.define(DistillAppendix.is, DistillAppendix);
+3 -3
View File
@@ -94,7 +94,7 @@ HoverBox.prototype.bind = function bind(node) {
}
// Don't trigger body touchstart event when touching link
e.stopPropagation();
}.bind(this));
}.bind(this), {passive: true});
}
HoverBox.prototype.bindDivEvents = function bindDivEvents(node){
@@ -106,7 +106,7 @@ HoverBox.prototype.bindDivEvents = function bindDivEvents(node){
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();});
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));
document.body.addEventListener("touchstart", function(){this.hide();}.bind(this), {passive: true});
}
-23
View File
@@ -44,29 +44,6 @@ export function page(selector) {
`;
}
export function pagePadding(selector) {
return `${selector} {
width: auto;
padding-left: 24px;
padding-right: 24px;
box-sizing: border-box;
}
@media(min-width: 768px) {
${selector} {
padding-left: 72px;
padding-right: 72px;
}
}
@media(min-width: 1080px) {
${selector} {
width: 984px;
padding-left: auto;
padding-right: auto;
}
}
`;
}
export function screen(selector) {
return `${selector} {
width: auto;
+60
View File
@@ -0,0 +1,60 @@
class DataStore {
constructor() {
this.collections = new Map();
this.subscribers = new Map();
}
get(collectionName) {
return this.collections.get(collectionName);
}
set(collectionName, collection) {
this.collections.set(collectionName, collection);
// notify subscribers
if (this.subscribers.has(collectionName)) {
const subscribers = this.subscribers.get(collectionName);
for (const subscriber of subscribers) {
subscriber(collection);
}
}
}
register(collectionName, element) {
// create collection if it doesn't yet exist
if (!this.collections.has(collectionName)) {
this.collections.set(collectionName, []);
}
// add new element to collection
const collection = this.collections.get(collectionName);
collection.push(element);
// notify subscribers
if (this.subscribers.has(collectionName)) {
const subscribers = this.subscribers.get(collectionName);
for (const subscriber of subscribers) {
subscriber(element, collection);
}
}
}
subscribeTo(collectionName, callback) {
// create subscriber collection if it doesn't yet exist
if (!this.subscribers.has(collectionName)) {
this.subscribers.set(collectionName, []);
}
// add new callback to list of subscribers
const subscribers = this.subscribers.get(collectionName);
subscribers.push(callback);
// notify subscriber about existing collection entries
if (this.collections.has(collectionName)) {
const collection = this.collections.get(collectionName);
for (const entry of collection) {
callback(entry, collection);
}
}
}
}
// set up store
// export const Store = new DataStore();
+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;
}
+2 -1
View File
@@ -2,8 +2,9 @@ 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;
s.textContent = base + layout + print + article + katex;
document.querySelector("head").appendChild(s);
export default s;