This commit is contained in:
Shan Carter
2017-10-23 11:10:44 -07:00
33 changed files with 549 additions and 238 deletions
-1
View File
@@ -126,7 +126,6 @@
<p>Some text with links describing who reviewed the article.</p>
<d-bibliography src="bibliography.bib"></d-bibliography>
</d-appendix>
<distill-footer></distill-footer>
+79 -36
View File
@@ -1,48 +1,91 @@
/* Static styles and other modules */
import { makeStyleTag } from './styles/styles';
makeStyleTag(document);
import { Controller } from './controller';
/* Transforms */
import { makeStyleTag } from './styles/styles';
import { Polyfills, polyfills } from './helpers/polyfills';
/* Components */
import { Abstract } from './components/d-abstract';
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 { CitationList } from './components/d-citation-list';
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 { Title } from './components/d-title';
import { DMath } from './components/d-math';
import { References } from './components/d-references';
import { TOC } from './components/d-toc';
import { Figure } from './components/d-figure';
import { Interstitial } from './components/d-interstitial';
import { Slider } from './ui/d-slider';
const components = [
Abstract, Appendix, Article, Bibliography, Byline, Cite, CitationList, Code,
Footnote, FootnoteList, FrontMatter, Title, DMath, References, TOC, Figure,
Slider, Interstitial
];
import { Abstract } from './components/d-abstract';
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 { CitationList } from './components/d-citation-list';
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 { Title } from './components/d-title';
import { DMath } from './components/d-math';
import { References } from './components/d-references';
import { TOC } from './components/d-toc';
import { Figure } from './components/d-figure';
import { Interstitial } from './components/d-interstitial';
import { Slider } from './ui/d-slider';
/* Distill website specific components */
import { DistillHeader } from './distill-components/distill-header';
import { DistillHeader } from './distill-components/distill-header';
import { DistillAppendix } from './distill-components/distill-appendix';
import { DistillFooter } from './distill-components/distill-footer';
import { DistillFooter } from './distill-components/distill-footer';
const distillComponents = [
DistillHeader, DistillAppendix, DistillFooter,
];
const distillMain = function() {
function defineComponents() {
if (window.distillRunlevel < 1) {
throw new Error('Insufficient Runlevel for Distill Template!');
}
/* 1. Flag that we're being loaded */
if ('distillTemplateIsLoading' in window && window.distillTemplateIsLoading) {
throw new Error('Runlevel 1: Distill Template is getting loaded more than once, aborting!');
} else {
window.distillTemplateIsLoading = true;
console.info('Runlevel 1: Distill Template has started loading.');
}
/* 2. Add styles if they weren't added during prerendering */
makeStyleTag(document);
console.info('Runlevel 1: Static Distill styles have been added.');
console.info('Runlevel 1->2.');
window.distillRunlevel = 2;
/* 3. Register components */
/* Article will register controller which takes control from there */
const components = [
Abstract, Appendix, Article, Bibliography, Byline, Cite, CitationList, Code,
Footnote, FootnoteList, FrontMatter, Title, DMath, References, TOC, Figure,
Slider, Interstitial
];
const distillComponents = [
DistillHeader, DistillAppendix, DistillFooter,
];
if (window.distillRunlevel < 2) {
throw new Error('Insufficient Runlevel for adding custom elements!');
}
const allComponents = components.concat(distillComponents);
for (const component of allComponents) {
console.info('Runlevel 2: Registering custom element: ' + component.is);
customElements.define(component.is, component);
}
}
defineComponents();
console.info('Runlevel 2: Distill Template finished registering custom elements.');
console.info('Runlevel 2->3.');
window.distillRunlevel = 3;
console.info('Distill Template initialisation complete.');
Controller.listeners.DOMContentLoaded();
};
window.distillRunlevel = 0;
/* 0. Check browser feature support; synchronously polyfill if needed */
if (Polyfills.browserSupportsAllFeatures()) {
console.info('Runlevel 0: No need for polyfills.');
console.info('Runlevel 0->1.');
window.distillRunlevel = 1;
distillMain();
} else {
console.info('Runlevel 0: Distill Template is loading polyfills.');
Polyfills.load(distillMain);
}
-1
View File
@@ -4,7 +4,6 @@ import { body } from '../helpers/layout';
const T = Template('d-abstract', `
<style>
:host {
display: block;
font-size: 1.25rem;
line-height: 1.6em;
color: rgba(0, 0, 0, 0.7);
+11 -4
View File
@@ -8,10 +8,11 @@ d-appendix {
contain: content;
font-size: 0.8em;
line-height: 1.7em;
margin-top: 60px;
margin-bottom: 0;
border-top: 1px solid rgba(0,0,0,0.1);
border-top: 1px solid rgba(0, 0, 0, 0.1);
color: rgba(0,0,0,0.5);
padding-top: 36px;
padding-top: 60px;
padding-bottom: 48px;
}
@@ -29,8 +30,14 @@ d-appendix h3 + * {
}
d-appendix ol {
padding: 0 0 0 30px;
margin-left: -30px;
padding: 0 0 0 15px;
}
@media (min-width: 768px) {
d-appendix ol {
padding: 0 0 0 30px;
margin-left: -30px;
}
}
d-appendix li {
+6 -3
View File
@@ -14,9 +14,6 @@ export class Article extends HTMLElement {
for (const mutation of mutations) {
for (const addedNode of mutation.addedNodes) {
switch (addedNode.nodeName) {
// case 'HR':
// console.warn('Use of <hr> tags in distill articles is discouraged as they interfere with layout! To separate sections, please just use h2 or h3 tags.');
// break;
case '#text': { // usually text nodes are only linebreaks.
const text = addedNode.nodeValue;
if (!isOnlyWhitespace.test(text)) {
@@ -34,6 +31,12 @@ export class Article extends HTMLElement {
}
connectedCallback() {
document.onreadystatechange = function () {
console.log("onreadystatechange:");
console.log(document.readyState);
};
console.info('Article tag connected, we can now listen to controller events.');
console.info('Runlevel 3->4.');
for (const [functionName, callback] of Object.entries(Controller.listeners)) {
if (typeof callback === 'function') {
document.addEventListener(functionName, callback);
+12 -3
View File
@@ -1,9 +1,18 @@
import { parseBibtex } from '../helpers/bibtex';
export function parseBibliography(element) {
if (element.firstElementChild && element.firstElementChild.tagName === 'SCRIPT') {
const bibtex = element.firstElementChild.textContent;
return parseBibtex(bibtex);
const scriptTag = element.firstElementChild;
if (scriptTag && scriptTag.tagName === 'SCRIPT') {
if (scriptTag.type == 'text/bibtex') {
const bibtex = element.firstElementChild.textContent;
return parseBibtex(bibtex);
} else if (scriptTag.type == 'text/json') {
return new Map(JSON.parse(scriptTag.textContent));
} else {
console.warn('Unsupported bibliography script tag type: ' + scriptTag.type);
}
} else {
console.warn('Bibliography did not have any script tag.');
}
}
+13 -15
View File
@@ -1,4 +1,4 @@
import style from '../styles/d-byline.css';
// import style from '../styles/d-byline.css';
export function bylineTemplate(frontMatter) {
return `
@@ -7,31 +7,29 @@ export function bylineTemplate(frontMatter) {
<h3>Authors</h3>
<h3>Affiliations</h3>
${frontMatter.authors.map(author => `
<p>
${author.personalURL
? `<a class="name" href="${author.personalURL}">${author.name}</a>`
: `<div class="name">${author.name}</div>`
}
<p class="author">
${author.personalURL ? `
<a class="name" href="${author.personalURL}">${author.name}</a>` : `
<div class="name">${author.name}</div>`}
</p>
<p>
${author.affiliationURL
? `<a class="affiliation" href="${author.affiliationURL}">${author.affiliation}</a>`
: `<div class="affiliation">${author.affiliation}</div>`
}
<p class="affiliation">
${author.affiliationURL ? `
<a class="affiliation" href="${author.affiliationURL}">${author.affiliation}</a>` : `
<div class="affiliation">${author.affiliation}</div>`}
</p>
`).join("")}
`).join('')}
</div>
<div>
<h3>Published</h3>
${frontMatter.published ? `
${frontMatter.publishedDate ? `
<p>${frontMatter.publishedMonth}. ${frontMatter.publishedDay} ${frontMatter.publishedYear}</p> ` : `
<p><em>Not yet published</em></p>`}
<p><em>Not published yet.</em></p>`}
</div>
<div>
<h3>DOI</h3>
${frontMatter.doi ? `
<p>${frontMatter.doi}</p>` : `
<p><em>No DOI yet</em></p>`}
<p><em>No DOI yet.</em></p>`}
</div>
</div>
`;
+27 -19
View File
@@ -1,9 +1,6 @@
import { Template } from '../mixins/template';
import { bibliography_cite } from '../helpers/citation';
const T = Template('d-citation-list', `
<style>
const styles = `
d-citation-list {
contain: content;
overflow: hidden;
@@ -16,21 +13,32 @@ d-citation-list .references {
d-citation-list .references .title {
font-weight: 500;
}
`;
</style>
<h3>References</h3>
<ol class='references' id='references-list'></ol>
`, false);
export function renderCitationList(element, entries) {
export function renderCitationList(element, entries, dom=document) {
if (entries.size > 0) {
element.style.display = '';
const list = document.getElementById('references-list');
list.innerHTML = '';
let list = element.querySelector('.references');
if (list) {
list.innerHTML = '';
} else {
const stylesTag = dom.createElement('style');
stylesTag.innerHTML = styles;
element.appendChild(stylesTag);
const heading = dom.createElement('h3');
heading.id = 'references';
heading.textContent = 'References';
element.appendChild(heading);
list = dom.createElement('ol');
list.id = 'references-list';
list.className = 'references';
element.appendChild(list);
}
for (const [key, entry] of entries) {
const listItem = document.createElement('li');
const listItem = dom.createElement('li');
listItem.id = key;
listItem.innerHTML = bibliography_cite(entry);
list.appendChild(listItem);
@@ -40,16 +48,16 @@ export function renderCitationList(element, entries) {
}
}
export class CitationList extends T(HTMLElement) {
export class CitationList extends HTMLElement {
static get is() { return 'd-citation-list'; }
connectedCallback() {
super.connectedCallback();
this.root.style.display = 'none';
this.style.display = 'none';
}
set citations(citations) {
renderCitationList(this.root, citations);
renderCitationList(this, citations);
}
}
+3 -1
View File
@@ -10,6 +10,7 @@ const T = Template('d-cite', `
}
.citation {
display: inline-block;
color: hsla(206, 90%, 20%, 0.7);
}
@@ -39,6 +40,7 @@ figcaption .citation-number {
width: 100%;
left: 0;
z-index: 10000;
margin-top: 2em;
}
.dt-hover-box {
@@ -59,7 +61,7 @@ figcaption .citation-number {
<div id="hover-box" class="dt-hover-box"></div>
</div>
<span id="citation-" class="citation"><slot></slot><span class="citation-number"></span></span>
<div id="citation-" class="citation"><slot></slot><span class="citation-number"></span></div>
`);
export class Cite extends T(HTMLElement) {
+9 -7
View File
@@ -33,8 +33,7 @@ export class Figure extends HTMLElement {
static runReadyQueue() {
// console.log("Checking to run readyQueue, length: " + Figure.readyQueue.length + ", scrolling: " + Figure.isScrolling);
if (Figure.isScrolling) return;
// if (Figure.isScrolling) return;
// console.log("Running ready Queue");
const figure = Figure.readyQueue
.sort((a,b) => a._seenOnScreen - b._seenOnScreen )
@@ -56,6 +55,7 @@ export class Figure extends HTMLElement {
}
connectedCallback() {
this.loadsWhileScrolling = this.hasAttribute('loadsWhileScrolling');
Figure.marginObserver.observe(this);
Figure.directObserver.observe(this);
}
@@ -71,12 +71,15 @@ export class Figure extends HTMLElement {
static get marginObserver() {
if (!Figure._marginObserver) {
// if (!('IntersectionObserver' in window)) {
// throw new Error('no interscetionobbserver!');
// }
const viewportHeight = window.innerHeight;
const margin = Math.floor(2 * viewportHeight);
Figure._marginObserver = new IntersectionObserver(
Figure.didObserveMarginIntersection, {
rootMargin: margin + 'px 0px ' + margin + 'px 0px', threshold: 0.01,
});
const options = {rootMargin: margin + 'px 0px ' + margin + 'px 0px', threshold: 0.01};
const callback = Figure.didObserveMarginIntersection;
const observer = new IntersectionObserver(callback, options);
Figure._marginObserver = observer;
}
return Figure._marginObserver;
}
@@ -166,7 +169,6 @@ if (typeof window !== 'undefined') {
clearTimeout(timeout);
timeout = setTimeout(() => {
Figure.isScrolling = false;
console.log('Stopped Scrolling')
Figure.runReadyQueue();
}, 500);
};
+36 -4
View File
@@ -26,14 +26,18 @@ const T = Template('d-interstitial', `
.container {
position: relative;
left: 25%;
width: 50%;
margin-left: auto;
margin-right: auto;
max-width: 420px;
padding: 2em;
}
h1 {
text-decoration: underline;
text-decoration-color: hsl(0,100%,40%);
-webkit-text-decoration-color: hsl(0,100%,40%);
margin-bottom: 1em;
line-height: 1.5em;
}
input[type="password"] {
@@ -71,12 +75,31 @@ p small {
color: #888;
}
.logo {
position: relative;
font-size: 1.5em;
margin-bottom: 3em;
}
.logo svg {
width: 36px;
position: relative;
top: 6px;
margin-right: 2px;
}
.logo svg path {
fill: none;
stroke: black;
stroke-width: 2px;
}
</style>
<div class="overlay">
<div class="container">
<h1>This article is in review.</h1>
<p>Do not share this URL, or the contents of this article. Thank you!</p>
<p>Do not share this URL or the contents of this article. Thank you!</p>
<input id="interstitial-password-input" type="password" name="password" autofocus/>
<p><small>Enter the password we shared with you as part of the review process to view the article.</small></p>
</div>
@@ -88,14 +111,23 @@ export class Interstitial extends T(HTMLElement) {
connectedCallback() {
const passwordInput = this.root.querySelector('#interstitial-password-input');
passwordInput.oninput = (event) => this.passwordChanged(event);
if (typeof(Storage) !== 'undefined') {
if (localStorage.getItem('distill-interstitial-password-correct') === 'true') {
console.log('Loaded that correct password was entered before; skipping interstitial.');
this.parentElement.removeChild(this);
}
}
}
passwordChanged(event) {
const entered = event.target.value;
if (entered === this.password) {
console.log('Correct password entered.');
event.target.classList.add('correct');
this.parentElement.removeChild(this);
if (typeof(Storage) !== 'undefined') {
console.log('Saved that correct password was entered.');
localStorage.setItem('distill-interstitial-password-correct', 'true');
}
}
}
+7 -3
View File
@@ -33,15 +33,19 @@ export class DMath extends Mutating(T(HTMLElement)) {
static set katexOptions(options) {
DMath._katexOptions = options;
if (DMath.katexOptions.delimiters && !DMath.katexAdded) {
DMath.addKatex();
if (DMath.katexOptions.delimiters) {
if (!DMath.katexAdded) {
DMath.addKatex();
} else {
DMath.katexLoadedCallback();
}
}
}
static get katexOptions() {
if (!DMath._katexOptions) {
DMath._katexOptions = {
delimiters: [ { 'left':'$', 'right':'$', 'display':true } ]
delimiters: [ { 'left':'$$', 'right':'$$', 'display': false } ]
};
}
return DMath._katexOptions;
+13 -1
View File
@@ -71,6 +71,7 @@ export const Controller = {
},
onBibliographyChanged(event) {
console.info('BibliographyChanged');
const citationListTag = document.querySelector('d-citation-list');
const bibliography = event.detail;
@@ -136,7 +137,12 @@ export const Controller = {
},
DOMContentLoaded() {
// console.debug('DOMContentLoaded.');
if (Controller.loaded || ['interactive', 'complete'].indexOf(document.readyState) === -1) {
return;
} else {
Controller.loaded = true;
console.log('Controller running DOMContentLoaded')
}
const frontMatterTag = document.querySelector('d-front-matter');
const data = parseFrontmatter(frontMatterTag);
@@ -149,6 +155,12 @@ export const Controller = {
waitingCallback();
}
if (frontMatter.bibliographyParsed) {
for (const waitingCallback of Controller.waitingOn.bibliography.slice()) {
waitingCallback();
}
}
const footnotesList = document.querySelector('d-footnote-list');
if (footnotesList) {
const footnotes = document.querySelectorAll('d-footnote');
+30 -12
View File
@@ -30,21 +30,39 @@ const styles = `
`;
export function appendixTemplate(frontMatter) {
return `
${styles}
let html = 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>
if (typeof frontMatter.githubUrl !== 'undefined') {
html += `
<h3 id="updates-and-corrections">Updates and Corrections</h3>
<p>`;
if (frontMatter.githubCompareUpdatesUrl) {
html += `<a href="${frontMatter.githubCompareUpdatesUrl}">View all changes</a> to this article since it was first published.`;
}
html += `
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/4.0/">CC-BY 4.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>
const journal = frontMatter.journal;
if (typeof journal !== 'undefined' && journal.title === 'Distill') {
html += `
<h3 id="reuse">Reuse</h3>
<p>Diagrams and text are licensed under Creative Commons Attribution <a href="https://creativecommons.org/licenses/by/4.0/">CC-BY 4.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">${serializeFrontmatterToBibtex(frontMatter)}</pre>
`;
if (typeof frontMatter.publishedDate !== 'undefined') {
html += `
<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">${serializeFrontmatterToBibtex(frontMatter)}</pre>
`;
}
return html;
}
export class DistillAppendix extends HTMLElement {
+2 -3
View File
@@ -5,10 +5,9 @@ const T = Template('distill-footer', `
<style>
:host {
display: block;
color: rgba(255, 255, 255, 0.4);
color: rgba(255, 255, 255, 0.5);
font-weight: 300;
padding: 40px 0;
padding: 60px 0;
border-top: 1px solid rgba(0, 0, 0, 0.1);
background-color: hsl(180, 5%, 15%); /*hsl(200, 60%, 15%);*/
text-align: left;
+53 -52
View File
@@ -4,80 +4,81 @@ import logo from '../assets/distill-logo.svg';
const T = Template('distill-header', `
<style>
:host {
box-sizing: border-box;
display: block;
top: 0;
left: 0;
distill-header {
position: relative;
height: 60px;
background-color: hsl(200, 60%, 15%);
width: 100%;
z-index: ${1e6};
box-sizing: border-box;
z-index: 2;
color: rgba(0, 0, 0, 0.8);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
contain: content;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
}
.content > * {
line-height: 30px;
font-size: 14px;
padding: 3px 7px;
margin: 8px 0;
distill-header .content {
height: 70px;
grid-column: page;
}
.name {
grid-column-end: span 8;
font-weight: 500;
border-radius: 3px;
font-size: 18px;
letter-spacing: -0.05em;
}
.content a {
font-size: 12px;
distill-header a {
font-size: 16px;
height: 60px;
line-height: 60px;
text-decoration: none;
color: black;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.8);
padding: 22px 0;
}
svg {
display: none;
background-color: hsl(0, 0%, 15%);
padding: 8px;
border-radius: 6px;
distill-header a:hover {
color: rgba(255, 255, 255, 1);
}
distill-header svg {
width: 24px;
position: relative;
top: 4px;
margin-right: 2px;
}
svg path {
fill: white;
stroke: rgba(255, 255, 255, 1.0);
@media(min-width: 1080px) {
distill-header {
height: 70px;
}
distill-header a {
height: 70px;
line-height: 70px;
padding: 28px 0;
}
distill-header .logo {
}
}
distill-header svg path {
fill: none;
stroke: rgba(255, 255, 255, 0.8);
stroke-width: 3px;
}
.content {
grid-column: page;
grid-template-columns: repeat(12, 1fr);
display: grid;
grid-column-gap: 40px;
}
.logo {
display: none;
distill-header .logo {
font-size: 17px;
font-weight: 200;
}
distill-header .nav {
float: right;
font-weight: 300;
}
distill-header .nav a {
font-size: 12px;
margin-left: 24px;
text-transform: uppercase;
}
</style>
<div class="content grid">
<div class="content">
<a href="/" class="logo">
${logo}
</a>
<div class='name'>
Distill
</a>
<div class="nav">
<a href="/about/">About</a>
<a href="/prize/">Prize</a>
<a href="/journal/">Submit</a>
</div>
<a href="/faq">Latest</a>
<a href="/faq">About</a>
<a href="/faq">Prize</a>
<a href="/faq">Submit</a>
</div>
`);
`, false);
// <div class="nav">
// <a href="https://github.com/distillpub">GitHub</a>
+4 -1
View File
@@ -1,4 +1,6 @@
export default function(dom) {
import { appendixTemplate } from '../distill-components/distill-appendix';
export default function(dom, data) {
const appendixTag = dom.querySelector('d-appendix');
if (!appendixTag) {
@@ -9,6 +11,7 @@ export default function(dom) {
if (!distillAppendixTag) {
const distillAppendix = dom.createElement('distill-appendix');
appendixTag.appendChild(distillAppendix);
distillAppendix.innerHTML = appendixTemplate(data);
}
}
+15
View File
@@ -1,3 +1,5 @@
import { parseBibtex } from '../helpers/bibtex';
import fs from 'fs';
import { parseBibliography } from '../components/d-bibliography';
export default function(dom, data) {
@@ -6,5 +8,18 @@ export default function(dom, data) {
console.warn('No bibliography tag found!');
return;
}
const src = bibliographyTag.getAttribute('src');
if (src) {
const path = data.inputDirectory + '/' + src;
const text = fs.readFileSync(path, 'utf-8');
const bibliography = parseBibtex(text);
const scriptTag = dom.createElement('script');
scriptTag.type = 'text/json';
scriptTag.textContent = JSON.stringify([...bibliography]);
bibliographyTag.appendChild(scriptTag);
bibliographyTag.removeAttribute('src');
}
data.bibliography = parseBibliography(bibliographyTag);
}
+75 -4
View File
@@ -13,6 +13,23 @@ const RFC = function(date) {
return `${day}, ${paddedDate} ${month} ${year} ${hours}:${minutes}:${seconds} Z`;
};
const objectFromMap = function(map) {
const object = Array.from(map).reduce((object, [key, value]) => (
Object.assign(object, { [key]: value }) // Be careful! Maps can have non-String keys; object literals can't.
), {});
return object;
};
const mapFromObject = function(object) {
const map = new Map();
for (var property in object) {
if (object.hasOwnProperty(property)) {
map.set(property, object[property]);
}
}
return map;
};
class Author {
// constructor(name='', personalURL='', affiliation='', affiliationURL='') {
@@ -44,7 +61,20 @@ class Author {
export function mergeFromYMLFrontmatter(target, source) {
target.title = source.title;
target.publishedDate = new Date(source.published);
if (source.published) {
if (source.published instanceof Date) {
target.publishedDate = source.published;
} else if (source.published.constructor === String) {
target.publishedDate = new Date(source.published);
}
}
if (source.publishedDate) {
if (source.publishedDate instanceof Date) {
target.publishedDate = source.publishedDate;
} else if (source.publishedDate.constructor === String) {
target.publishedDate = new Date(source.publishedDate);
}
}
target.description = source.description;
target.authors = source.authors.map( (authorObject) => new Author(authorObject));
target.katex = source.katex;
@@ -103,7 +133,6 @@ export class FrontMatter {
// }
// volume: 1,
// issue: 9,
this.publishedDate = new Date();
this.katex = {};
@@ -114,7 +143,7 @@ export class FrontMatter {
// githubCompareUpdatesUrl: 'https://github.com/distillpub/post--augmented-rnns/compare/1596e094d8943d2dc0ea445d92071129c6419c59...3bd9209e0c24d020f87cf6152dcecc6017cbc193',
// updatedDate: 2017-03-21T07:13:16.000Z,
// doi: '10.23915/distill.00001',
this.publishedDate = undefined;
}
// Example:
@@ -147,7 +176,11 @@ export class FrontMatter {
// 'https://github.com/distillpub/post--augmented-rnns',
get githubUrl() {
return 'https://github.com/' + this.githubPath;
if (this.githubPath) {
return 'https://github.com/' + this.githubPath;
} else {
return undefined;
}
}
// TODO resolve differences in naming of URL/Url/url.
@@ -230,4 +263,42 @@ export class FrontMatter {
}));
}
set bibliography(bibliography) {
if (bibliography instanceof Map) {
this._bibliography = bibliography;
} else if (typeof bibliography === 'object') {
this._bibliography = mapFromObject(bibliography);
}
}
get bibliography() {
return this._bibliography;
}
static fromObject(source) {
const frontMatter = new FrontMatter();
Object.assign(frontMatter, source);
return frontMatter;
}
assignToObject(target) {
Object.assign(target, this);
target.bibliography = objectFromMap(this.bibliographyEntries);
target.url = this.url;
target.githubUrl = this.githubUrl;
target.previewURL = this.previewURL;
target.volume = this.volume;
target.issue = this.issue;
target.publishedDateRFC = this.publishedDateRFC;
target.publishedYear = this.publishedYear;
target.publishedMonth = this.publishedMonth;
target.publishedDay = this.publishedDay;
target.publishedMonthPadded = this.publishedMonthPadded;
target.publishedDayPadded = this.publishedDayPadded;
target.updatedDateRFC = this.updatedDateRFC;
target.concatenatedAuthors = this.concatenatedAuthors;
target.bibtexAuthors = this.bibtexAuthors;
target.slug = this.slug;
}
}
+2 -2
View File
@@ -41,7 +41,7 @@ export class HoverBox {
this.stopTimeout();
});
this.div.addEventListener('mouseout', () => {
this.extendTimeout(250);
this.extendTimeout(500);
});
// Don't trigger body touchstart event when touching within box
this.div.addEventListener('touchstart', (event) => {
@@ -62,7 +62,7 @@ export class HoverBox {
});
node.addEventListener('mouseout', () => {
this.extendTimeout(250);
this.extendTimeout(500);
});
node.addEventListener('touchstart', (event) => {
+68
View File
@@ -0,0 +1,68 @@
export function addPolyfill(polyfill, polyfillLoadedCallback) {
console.info('Runlevel 0: Polyfill required: ' + polyfill.name);
const script = document.createElement('script');
script.src = polyfill.url;
script.async = false;
if (polyfillLoadedCallback) {
script.onload = function() { polyfillLoadedCallback(polyfill); };
}
script.onerror = function() {
new Error('Runlevel 0: Polyfills failed to load script ' + polyfill.name);
};
document.head.appendChild(script);
}
export const polyfills = [
{
name: 'WebComponents',
support: function() {
return 'customElements' in window &&
'attachShadow' in Element.prototype &&
'getRootNode' in Element.prototype &&
'content' in document.createElement('template') &&
'Promise' in window &&
'from' in Array;
},
url: 'https://distill.pub/third-party/polyfills/webcomponents-lite.js'
}, {
name: 'IntersectionObserver',
support: function() {
return 'IntersectionObserver' in window &&
'IntersectionObserverEntry' in window;
},
url: 'https://distill.pub/third-party/polyfills/intersection-observer.js'
},
];
export class Polyfills {
static browserSupportsAllFeatures() {
return polyfills.every((poly) => poly.support());
}
static load(callback) {
// Define an intermediate callback that checks if all is loaded.
const polyfillLoaded = function(polyfill) {
polyfill.loaded = true;
console.info('Runlevel 0: Polyfill has finished loading: ' + polyfill.name);
// console.info(window[polyfill.name]);
if (Polyfills.neededPolyfills.every((poly) => poly.loaded)) {
console.info('Runlevel 0: All required polyfills have finished loading.');
console.info('Runlevel 0->1.');
window.distillRunlevel = 1;
callback();
}
};
// Add polyfill script tags
for (const polyfill of Polyfills.neededPolyfills) {
addPolyfill(polyfill, polyfillLoaded);
}
}
static get neededPolyfills() {
if (!Polyfills._neededPolyfills) {
Polyfills._neededPolyfills = polyfills.filter((poly) => !poly.support());
}
return Polyfills._neededPolyfills;
}
}
+3 -3
View File
@@ -71,8 +71,8 @@ d-article h3 {
font-weight: 700;
font-size: 18px;
line-height: 1.4em;
margin-bottom: 24px;
margin-top: 0;
margin-bottom: 1em;
margin-top: 2em;
}
@media(min-width: 1024px) {
@@ -146,7 +146,7 @@ d-article hr {
grid-column: screen;
width: 100%;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin-top: 60px;
margin-bottom: 60px;
}
+3 -2
View File
@@ -41,9 +41,10 @@ d-article d-byline a:hover {
border-bottom: none;
}
d-byline .authors p {
font-weight: 600;
d-byline p.author {
font-weight: 500;
}
d-byline .affiliations {
}
+1 -1
View File
@@ -1,7 +1,7 @@
span.katex-display {
text-align: left;
padding: 8px 0 8px 0;
margin: 0 0 1.7em 1em;
margin: 0.5em 0 0.5em 1em;
}
span.katex {
+17 -1
View File
@@ -52,6 +52,17 @@ p {
margin-bottom: 1em;
}
sup, sub {
vertical-align: baseline;
position: relative;
top: -0.4em;
line-height: 1em;
}
sub {
top: 0.4em;
}
.kicker,
.marker {
font-size: 15px;
@@ -72,7 +83,12 @@ p {
figure {
position: relative;
margin-bottom: 1.5rem;
margin-bottom: 2.5em;
margin-top: 2.5em;
}
figcaption+figure {
}
figure img {
+2 -1
View File
@@ -17,7 +17,8 @@ export function makeStyleTag(dom) {
styleTag.type = 'text/css';
const cssTextTag = dom.createTextNode(styles);
styleTag.appendChild(cssTextTag);
dom.head.insertBefore(styleTag, dom.head.firstChild);
const firstScriptTag = dom.head.querySelector('script');
dom.head.insertBefore(styleTag, firstScriptTag);
}
}
+17 -6
View File
@@ -16,26 +16,26 @@ const extractors = new Map([
/* Transforms */
import HTML from './transforms/html';
import Byline from './transforms/byline';
import Polyfills from './transforms/polyfills';
import OptionalComponents from './transforms/optional-components';
import Mathematics from './transforms/mathematics';
import Meta from './transforms/meta';
import { makeStyleTag } from './styles/styles';
import TOC from './transforms/toc';
import Typeset from './transforms/typeset';
import Bibliography from './transforms/bibliography';
import Polyfills from './transforms/polyfills';
import CitationList from './transforms/citation-list';
const transforms = new Map([
['HTML', HTML],
['makeStyleTag', makeStyleTag],
['Polyfills', Polyfills],
['OptionalComponents', OptionalComponents],
['TOC', TOC],
['Byline', Byline],
['Mathematics', Mathematics],
['Meta', Meta],
['Typeset', Typeset],
['Bibliography', Bibliography],
['Polyfills', Polyfills],
['CitationList', CitationList],
]);
/* Distill Transforms */
@@ -52,19 +52,30 @@ const distillTransforms = new Map([
/* Exported functions */
export function render(dom, data, verbose=true) {
let frontMatter;
if (data instanceof FrontMatter) {
frontMatter = data;
} else {
frontMatter = FrontMatter.fromObject(data);
}
// first, we collect static data from the dom
for (const [name, extract] of extractors.entries()) {
if (verbose) console.warn('Running extractor: ' + name);
extract(dom, data, verbose);
extract(dom, frontMatter, verbose);
}
// secondly we use it to transform parts of the dom
for (const [name, transform] of transforms.entries()) {
if (verbose) console.warn('Running transform: ' + name);
// console.warn('Running transform: ', transform);
transform(dom, data, verbose);
transform(dom, frontMatter, verbose);
}
dom.body.setAttribute('distill-prerendered', '');
// the function calling us can now use the transformed dom and filled data object
if (data instanceof FrontMatter) {
// frontMatter will already have needed properties
} else {
frontMatter.assignToObject(data);
}
}
export function distillify(dom, data, verbose=true) {
-39
View File
@@ -1,39 +0,0 @@
// import { renderBibliography, templateString } from '../components/d-bibliography';
import { parseBibtex } from '../helpers/bibtex';
import fs from 'fs';
export default function(dom, data) {
const bibliographyTag = dom.querySelector('d-bibliography');
if (!bibliographyTag) {
console.warn('No bibliography tag present!');
return;
}
const src = bibliographyTag.getAttribute('src');
if (src) {
const path = data.inputDirectory + '/' + src;
const text = fs.readFileSync(path, 'utf-8');
const bibliography = parseBibtex(text);
const scriptTag = dom.createElement('script');
scriptTag.type = 'text/json';
scriptTag.textContent = JSON.stringify([...bibliography]);
bibliographyTag.appendChild(scriptTag);
bibliographyTag.removeAttribute('src');
}
// const bibliographyEntries = new Map(data.citations.map( citationKey => {
// const entry = data.bibliography.get(citationKey);
// return [citationKey, entry];
// }));
//
// const prerenderedBibliography = dom.createElement('d-bibliography-prerendered');
//
// const template = dom.createElement('template');
// template.innerHTML = templateString;
// const clone = dom.importNode(template.content, true);
// prerenderedBibliography.innerHTML = template.content;
// renderBibliography(prerenderedBibliography, bibliographyEntries, dom);
//
// bibliographyTag.parentElement.insertBefore(bibliographyTag, prerenderedBibliography);
// bibliographyTag.parentElement.removeChild(bibliographyTag);
}
+11
View File
@@ -0,0 +1,11 @@
import { renderCitationList } from '../components/d-citation-list'; // (element, entries)
export default function(dom, data) {
const citationListTag = dom.querySelector('d-citation-list');
if (citationListTag) {
const entries = new Map(data.citations.map( citationKey => {
return [citationKey, data.bibliography.get(citationKey)];
}));
renderCitationList(citationListTag, entries, dom);
}
}
+16 -4
View File
@@ -14,16 +14,28 @@ export default function(dom, data) {
<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(`
if (data.url) {
appendHead(`
<link rel="canonical" href="${data.url}">
`);
}
if (data.title) {
appendHead(`
<title>${data.title}</title>
`);
}
if (data.publishedDate){
appendHead(`
<!-- https://schema.org/Article -->
<meta property="article:published" itemprop="datePublished" content="${data.publishedYear}-${data.publishedMonthPadded}-${data.publishedDayPadded}" />
<meta property="article:created" itemprop="dateCreated" content="${data.publishedDate}" />
<meta property="article:modified" itemprop="dateModified" content="${data.updatedDate}" />
`);
`);
}
(data.authors || []).forEach((a) => {
appendHtml(head, `
+10 -6
View File
@@ -9,17 +9,21 @@
export default function(dom, data) {
const article = dom.querySelector('d-article');
if (!article) {
console.warn('No d-article tag found!');
console.warn('No d-article tag found; skipping adding optional components!');
return;
}
const hasPassword = typeof data.password !== 'undefined';
let interstitial = dom.querySelector('d-interstitial');
if (!interstitial && data.password) {
interstitial = dom.createElement('d-interstitial');
interstitial.password = data.password;
dom.body.insertBefore(interstitial, dom.body.firstChild);
if (hasPassword && !interstitial) {
const inBrowser = typeof window !== 'undefined';
const onLocalhost = inBrowser && window.location.hostname.includes('localhost');
if (!inBrowser || !onLocalhost) {
interstitial = dom.createElement('d-interstitial');
interstitial.password = data.password;
dom.body.insertBefore(interstitial, dom.body.firstChild);
}
}
// let h1 = dom.querySelector('h1');
+2 -1
View File
@@ -42,5 +42,6 @@ export default function render(dom) {
polyfillScriptTag.id = 'polyfills';
// insert at appropriate position--before any other script tag
dom.head.insertBefore(polyfillScriptTag, dom.head.firstChild);
const firstScriptTag = dom.head.querySelector('script');
dom.head.insertBefore(polyfillScriptTag, firstScriptTag);
}
+2 -2
View File
@@ -196,7 +196,7 @@ describe('Distill V2 (transforms)', function() {
expect(content).to.not.include('journal');
});
it('given only a DOM, it should add Google scholar references information', function() {
it('given only a DOM (and publish data), it should add Google scholar references information', function() {
const dom = new JSDOM(`
<d-cite key="mercier2011humans">sth</d-cite>
<d-bibliography>
@@ -215,7 +215,7 @@ describe('Distill V2 (transforms)', function() {
</script>
</d-bibliography>
`, options);
const data = {};
const data = { publishedDate: new Date(), updatedDate: new Date() };
distill.render(dom.window.document, data, false);
const metaTags = [].slice.call(dom.window.document.querySelectorAll('meta[name="citation_reference"]'));
expect(metaTags).to.not.be.empty;