diff --git a/src/components.js b/src/components.js
index cdf8518..877e195 100644
--- a/src/components.js
+++ b/src/components.js
@@ -1,6 +1,5 @@
/* Static styles and other modules */
import './styles/styles';
-// import katex from 'katex';
/* Components */
import { Abstract } from './components/d-abstract';
@@ -43,5 +42,3 @@ function defineComponents() {
}
defineComponents();
-
-window.DMath = DMath;
diff --git a/src/components/d-math.js b/src/components/d-math.js
index 904a367..db3342a 100644
--- a/src/components/d-math.js
+++ b/src/components/d-math.js
@@ -1,7 +1,9 @@
-import katex from 'katex';
+/*global katex */
import { Mutating } from '../mixins/mutating.js';
import { Template } from '../mixins/template.js';
-import katexCSS from '../../node_modules/katex/dist/katex.min.css';
+
+const katexJSURL = 'https://distill.pub/third-party/katex/katex.min.js';
+const katexCSSTag = '';
const T = Template('d-math', `
+${katexCSSTag}
+
`);
+
+// DMath, not Math, because that's a JS built-in
export class DMath extends Mutating(T(HTMLElement)) {
+ static katexLoadedCallback() {
+ const mathTags = document.querySelectorAll('d-math');
+ for (const mathTag of mathTags) {
+ mathTag.renderContent();
+ }
+ }
+
+ connectedCallback() {
+ if (!DMath.katexAdded) {
+ // script tag has to be created to work properly
+ const scriptTag = document.createElement('script');
+ scriptTag.src = katexJSURL;
+ scriptTag.async = true;
+ scriptTag.onload = DMath.katexLoadedCallback;
+ scriptTag.crossorigin = 'anonymous';
+ document.head.appendChild(scriptTag);
+ // css tag can use this convenience function
+ document.head.insertAdjacentHTML('beforeend', katexCSSTag);
+
+ DMath.katexAdded = true;
+ }
+ }
+
renderContent() {
- const options = { displayMode: this.hasAttribute('block') };
- const container = this.root.querySelector('#katex-container');
- katex.render(this.textContent, container, options);
+ if (typeof katex !== 'undefined') {
+ const options = { displayMode: this.hasAttribute('block') };
+ const container = this.root.querySelector('#katex-container');
+ katex.render(this.textContent, container, options);
+ }
}
}
+
+DMath.katexAdded = false;
+window.DMath = DMath; // TODO: check if this can be removed, or if we should expose a distill global
diff --git a/src/styles/styles.js b/src/styles/styles.js
index cb838f4..96e3ffa 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -2,9 +2,8 @@ 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;
+s.textContent = base + layout + print + article;
document.querySelector('head').appendChild(s);
export default s;
diff --git a/src/transforms/helpers/bibtex.js b/src/transforms/helpers/bibtex.js
new file mode 100644
index 0000000..786e83a
--- /dev/null
+++ b/src/transforms/helpers/bibtex.js
@@ -0,0 +1,33 @@
+import bibtexParse from 'bibtex-parse-js';
+
+function normalizeTag(string) {
+ return string
+ .replace(/[\t\n ]+/g, ' ')
+ .replace(/{\\["^`.'acu~Hvs]( )?([a-zA-Z])}/g, (full, x, char) => char)
+ .replace(/{\\([a-zA-Z])}/g, (full, char) => char);
+}
+
+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 [key, value] of Object.entries(entry.entryTags)) {
+ entry.entryTags[key] = normalizeTag(value);
+ }
+ entry.entryTags.type = entry.entryType;
+ // add to bibliography
+ bibliography.set(entry.citationKey, entry.entryTags);
+ }
+ return bibliography;
+}
+
+export function serializeFrontmatterToBibtex(frontMatter) {
+ return `@article{${frontMatter.slug},
+ author = {${frontMatter.bibtexAuthors}},
+ title = {${frontMatter.title}},
+ journal = {${frontMatter.journal.title}},
+ year = {${frontMatter.publishedYear}},
+ note = {${frontMatter.url}}
+}`;
+}
diff --git a/src/transforms/helpers/citation.js b/src/transforms/helpers/citation.js
new file mode 100644
index 0000000..8062b6f
--- /dev/null
+++ b/src/transforms/helpers/citation.js
@@ -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 ` [${label||'link'}]`;
+ }/* else if ("doi" in ent){
+ return ` [DOI]`;
+ }*/ else {
+ return '';
+ }
+}
+function doi_string(ent, new_line){
+ if ('doi' in ent) {
+ return `${new_line?'
':''} DOI: ${ent.doi}`;
+ } else {
+ return '';
+ }
+}
+
+export function bibliography_cite(ent, fancy){
+ if (ent){
+ var cite = '' + ent.title + ' ';
+ cite += link_string(ent) + '
';
+ 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 += "" + ent.title + ". ";
+ 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 += '' + ent.title + '';
+ cite += link_string(ent);
+ cite += '
';
+
+ 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 + '
' + 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(' ')});
+ }
+}
diff --git a/src/transforms/helpers/hover-box.js b/src/transforms/helpers/hover-box.js
new file mode 100644
index 0000000..be19f57
--- /dev/null
+++ b/src/transforms/helpers/hover-box.js
@@ -0,0 +1,108 @@
+function make_hover_css(pos) {
+ const pretty = window.innerWidth > 600;
+ const padding = pretty? 18 : 12;
+ const outer_padding = pretty ? 18 : 0;
+ const bbox = document.querySelector('body').getBoundingClientRect();
+ let left = pos[0] - bbox.left, top = pos[1] - bbox.top;
+ let 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(contentHTML, triggerElement) {
+ this.visible = false;
+ // div hold teh contents of the box that will become visible
+ this.div = contentHTML;
+ this.bindDivEvents(this.div);
+ // triggerElement holds the element that needs to be hovered etc to show contents
+ this.triggerElement = triggerElement;
+ this.bindTriggerEvents(this.triggerElement);
+ this.hide();
+ }
+
+ bindDivEvents(node) {
+ // For mice, same behavior as hovering on links
+ this.div.addEventListener('mouseover', () => {
+ if (!this.visible) this.showAtNode(node);
+ this.stopTimeout();
+ });
+ this.div.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+ // Don't trigger body touchstart event when touching within box
+ this.div.addEventListener('touchstart', (event) => {
+ event.stopPropagation();
+ }, {passive: true});
+ // Close box when touching outside box
+ document.body.addEventListener('touchstart', () => {
+ this.hide();
+ }, {passive: true});
+ }
+
+ bindTriggerEvents(node) {
+ node.addEventListener('mouseover', () => {
+ if (!this.visible) {
+ this.showAtNode(node);
+ }
+ this.stopTimeout();
+ });
+
+ node.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+
+ node.addEventListener('touchstart', (event) => {
+ if (this.visible) {
+ this.hide();
+ } else {
+ this.showAtNode(node);
+ }
+ // Don't trigger body touchstart event when touching link
+ event.stopPropagation();
+ }, {passive: true});
+ }
+
+ show(position) {
+ this.visible = true;
+ const css = make_hover_css(position);
+ this.div.setAttribute('style', css );
+ }
+
+ showAtNode(node) {
+ const bbox = node.getBoundingClientRect();
+ this.show([bbox.right, bbox.bottom]);
+ }
+
+ hide() {
+ this.visible = false;
+ this.div.setAttribute('style', 'display:none');
+ this.stopTimeout();
+ }
+
+ stopTimeout() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ extendTimeout(time) {
+ this.stopTimeout();
+ this.timeout = setTimeout(() => {
+ this.hide();
+ }, time);
+ }
+
+}
diff --git a/src/transforms/helpers/layout.js b/src/transforms/helpers/layout.js
new file mode 100644
index 0000000..f2dc8be
--- /dev/null
+++ b/src/transforms/helpers/layout.js
@@ -0,0 +1,33 @@
+// const marginSmall = 16;
+// const marginLarge = 3 * marginSmall;
+// const margin = marginSmall + marginLarge;
+// const gutter = marginSmall;
+// const outsetAmount = margin / 2;
+// const numCols = 4;
+// const numGutters = numCols - 1;
+// const columnWidth = (768 - 2 * marginLarge - numGutters * gutter) / numCols;
+//
+// const screenwidth = 768;
+// const pageWidth = screenwidth - 2 * marginLarge;
+// const bodyWidth = pageWidth - columnWidth - gutter;
+
+export function body(selector) {
+ return `${selector} {
+ grid-column: margin-left / body;
+ }
+ `;
+}
+
+export function page(selector) {
+ return `${selector} {
+ grid-column: margin-left / page;
+ }
+ `;
+}
+
+export function screen(selector) {
+ return `${selector} {
+ grid-column: start / end;
+ }
+ `;
+}
diff --git a/src/transforms/helpers0/bibtex.js b/src/transforms/helpers0/bibtex.js
new file mode 100644
index 0000000..786e83a
--- /dev/null
+++ b/src/transforms/helpers0/bibtex.js
@@ -0,0 +1,33 @@
+import bibtexParse from 'bibtex-parse-js';
+
+function normalizeTag(string) {
+ return string
+ .replace(/[\t\n ]+/g, ' ')
+ .replace(/{\\["^`.'acu~Hvs]( )?([a-zA-Z])}/g, (full, x, char) => char)
+ .replace(/{\\([a-zA-Z])}/g, (full, char) => char);
+}
+
+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 [key, value] of Object.entries(entry.entryTags)) {
+ entry.entryTags[key] = normalizeTag(value);
+ }
+ entry.entryTags.type = entry.entryType;
+ // add to bibliography
+ bibliography.set(entry.citationKey, entry.entryTags);
+ }
+ return bibliography;
+}
+
+export function serializeFrontmatterToBibtex(frontMatter) {
+ return `@article{${frontMatter.slug},
+ author = {${frontMatter.bibtexAuthors}},
+ title = {${frontMatter.title}},
+ journal = {${frontMatter.journal.title}},
+ year = {${frontMatter.publishedYear}},
+ note = {${frontMatter.url}}
+}`;
+}
diff --git a/src/transforms/helpers0/citation.js b/src/transforms/helpers0/citation.js
new file mode 100644
index 0000000..8062b6f
--- /dev/null
+++ b/src/transforms/helpers0/citation.js
@@ -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 ` [${label||'link'}]`;
+ }/* else if ("doi" in ent){
+ return ` [DOI]`;
+ }*/ else {
+ return '';
+ }
+}
+function doi_string(ent, new_line){
+ if ('doi' in ent) {
+ return `${new_line?'
':''} DOI: ${ent.doi}`;
+ } else {
+ return '';
+ }
+}
+
+export function bibliography_cite(ent, fancy){
+ if (ent){
+ var cite = '' + ent.title + ' ';
+ cite += link_string(ent) + '
';
+ 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 += "" + ent.title + ". ";
+ 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 += '' + ent.title + '';
+ cite += link_string(ent);
+ cite += '
';
+
+ 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 + '
' + 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(' ')});
+ }
+}
diff --git a/src/transforms/helpers0/hover-box.js b/src/transforms/helpers0/hover-box.js
new file mode 100644
index 0000000..be19f57
--- /dev/null
+++ b/src/transforms/helpers0/hover-box.js
@@ -0,0 +1,108 @@
+function make_hover_css(pos) {
+ const pretty = window.innerWidth > 600;
+ const padding = pretty? 18 : 12;
+ const outer_padding = pretty ? 18 : 0;
+ const bbox = document.querySelector('body').getBoundingClientRect();
+ let left = pos[0] - bbox.left, top = pos[1] - bbox.top;
+ let 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(contentHTML, triggerElement) {
+ this.visible = false;
+ // div hold teh contents of the box that will become visible
+ this.div = contentHTML;
+ this.bindDivEvents(this.div);
+ // triggerElement holds the element that needs to be hovered etc to show contents
+ this.triggerElement = triggerElement;
+ this.bindTriggerEvents(this.triggerElement);
+ this.hide();
+ }
+
+ bindDivEvents(node) {
+ // For mice, same behavior as hovering on links
+ this.div.addEventListener('mouseover', () => {
+ if (!this.visible) this.showAtNode(node);
+ this.stopTimeout();
+ });
+ this.div.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+ // Don't trigger body touchstart event when touching within box
+ this.div.addEventListener('touchstart', (event) => {
+ event.stopPropagation();
+ }, {passive: true});
+ // Close box when touching outside box
+ document.body.addEventListener('touchstart', () => {
+ this.hide();
+ }, {passive: true});
+ }
+
+ bindTriggerEvents(node) {
+ node.addEventListener('mouseover', () => {
+ if (!this.visible) {
+ this.showAtNode(node);
+ }
+ this.stopTimeout();
+ });
+
+ node.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+
+ node.addEventListener('touchstart', (event) => {
+ if (this.visible) {
+ this.hide();
+ } else {
+ this.showAtNode(node);
+ }
+ // Don't trigger body touchstart event when touching link
+ event.stopPropagation();
+ }, {passive: true});
+ }
+
+ show(position) {
+ this.visible = true;
+ const css = make_hover_css(position);
+ this.div.setAttribute('style', css );
+ }
+
+ showAtNode(node) {
+ const bbox = node.getBoundingClientRect();
+ this.show([bbox.right, bbox.bottom]);
+ }
+
+ hide() {
+ this.visible = false;
+ this.div.setAttribute('style', 'display:none');
+ this.stopTimeout();
+ }
+
+ stopTimeout() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ extendTimeout(time) {
+ this.stopTimeout();
+ this.timeout = setTimeout(() => {
+ this.hide();
+ }, time);
+ }
+
+}
diff --git a/src/transforms/helpers0/layout.js b/src/transforms/helpers0/layout.js
new file mode 100644
index 0000000..f2dc8be
--- /dev/null
+++ b/src/transforms/helpers0/layout.js
@@ -0,0 +1,33 @@
+// const marginSmall = 16;
+// const marginLarge = 3 * marginSmall;
+// const margin = marginSmall + marginLarge;
+// const gutter = marginSmall;
+// const outsetAmount = margin / 2;
+// const numCols = 4;
+// const numGutters = numCols - 1;
+// const columnWidth = (768 - 2 * marginLarge - numGutters * gutter) / numCols;
+//
+// const screenwidth = 768;
+// const pageWidth = screenwidth - 2 * marginLarge;
+// const bodyWidth = pageWidth - columnWidth - gutter;
+
+export function body(selector) {
+ return `${selector} {
+ grid-column: margin-left / body;
+ }
+ `;
+}
+
+export function page(selector) {
+ return `${selector} {
+ grid-column: margin-left / page;
+ }
+ `;
+}
+
+export function screen(selector) {
+ return `${selector} {
+ grid-column: start / end;
+ }
+ `;
+}
diff --git a/src/transforms/helpers1/bibtex.js b/src/transforms/helpers1/bibtex.js
new file mode 100644
index 0000000..786e83a
--- /dev/null
+++ b/src/transforms/helpers1/bibtex.js
@@ -0,0 +1,33 @@
+import bibtexParse from 'bibtex-parse-js';
+
+function normalizeTag(string) {
+ return string
+ .replace(/[\t\n ]+/g, ' ')
+ .replace(/{\\["^`.'acu~Hvs]( )?([a-zA-Z])}/g, (full, x, char) => char)
+ .replace(/{\\([a-zA-Z])}/g, (full, char) => char);
+}
+
+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 [key, value] of Object.entries(entry.entryTags)) {
+ entry.entryTags[key] = normalizeTag(value);
+ }
+ entry.entryTags.type = entry.entryType;
+ // add to bibliography
+ bibliography.set(entry.citationKey, entry.entryTags);
+ }
+ return bibliography;
+}
+
+export function serializeFrontmatterToBibtex(frontMatter) {
+ return `@article{${frontMatter.slug},
+ author = {${frontMatter.bibtexAuthors}},
+ title = {${frontMatter.title}},
+ journal = {${frontMatter.journal.title}},
+ year = {${frontMatter.publishedYear}},
+ note = {${frontMatter.url}}
+}`;
+}
diff --git a/src/transforms/helpers1/citation.js b/src/transforms/helpers1/citation.js
new file mode 100644
index 0000000..8062b6f
--- /dev/null
+++ b/src/transforms/helpers1/citation.js
@@ -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 ` [${label||'link'}]`;
+ }/* else if ("doi" in ent){
+ return ` [DOI]`;
+ }*/ else {
+ return '';
+ }
+}
+function doi_string(ent, new_line){
+ if ('doi' in ent) {
+ return `${new_line?'
':''} DOI: ${ent.doi}`;
+ } else {
+ return '';
+ }
+}
+
+export function bibliography_cite(ent, fancy){
+ if (ent){
+ var cite = '' + ent.title + ' ';
+ cite += link_string(ent) + '
';
+ 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 += "" + ent.title + ". ";
+ 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 += '' + ent.title + '';
+ cite += link_string(ent);
+ cite += '
';
+
+ 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 + '
' + 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(' ')});
+ }
+}
diff --git a/src/transforms/helpers1/hover-box.js b/src/transforms/helpers1/hover-box.js
new file mode 100644
index 0000000..be19f57
--- /dev/null
+++ b/src/transforms/helpers1/hover-box.js
@@ -0,0 +1,108 @@
+function make_hover_css(pos) {
+ const pretty = window.innerWidth > 600;
+ const padding = pretty? 18 : 12;
+ const outer_padding = pretty ? 18 : 0;
+ const bbox = document.querySelector('body').getBoundingClientRect();
+ let left = pos[0] - bbox.left, top = pos[1] - bbox.top;
+ let 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(contentHTML, triggerElement) {
+ this.visible = false;
+ // div hold teh contents of the box that will become visible
+ this.div = contentHTML;
+ this.bindDivEvents(this.div);
+ // triggerElement holds the element that needs to be hovered etc to show contents
+ this.triggerElement = triggerElement;
+ this.bindTriggerEvents(this.triggerElement);
+ this.hide();
+ }
+
+ bindDivEvents(node) {
+ // For mice, same behavior as hovering on links
+ this.div.addEventListener('mouseover', () => {
+ if (!this.visible) this.showAtNode(node);
+ this.stopTimeout();
+ });
+ this.div.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+ // Don't trigger body touchstart event when touching within box
+ this.div.addEventListener('touchstart', (event) => {
+ event.stopPropagation();
+ }, {passive: true});
+ // Close box when touching outside box
+ document.body.addEventListener('touchstart', () => {
+ this.hide();
+ }, {passive: true});
+ }
+
+ bindTriggerEvents(node) {
+ node.addEventListener('mouseover', () => {
+ if (!this.visible) {
+ this.showAtNode(node);
+ }
+ this.stopTimeout();
+ });
+
+ node.addEventListener('mouseout', () => {
+ this.extendTimeout(250);
+ });
+
+ node.addEventListener('touchstart', (event) => {
+ if (this.visible) {
+ this.hide();
+ } else {
+ this.showAtNode(node);
+ }
+ // Don't trigger body touchstart event when touching link
+ event.stopPropagation();
+ }, {passive: true});
+ }
+
+ show(position) {
+ this.visible = true;
+ const css = make_hover_css(position);
+ this.div.setAttribute('style', css );
+ }
+
+ showAtNode(node) {
+ const bbox = node.getBoundingClientRect();
+ this.show([bbox.right, bbox.bottom]);
+ }
+
+ hide() {
+ this.visible = false;
+ this.div.setAttribute('style', 'display:none');
+ this.stopTimeout();
+ }
+
+ stopTimeout() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ }
+
+ extendTimeout(time) {
+ this.stopTimeout();
+ this.timeout = setTimeout(() => {
+ this.hide();
+ }, time);
+ }
+
+}
diff --git a/src/transforms/helpers1/layout.js b/src/transforms/helpers1/layout.js
new file mode 100644
index 0000000..f2dc8be
--- /dev/null
+++ b/src/transforms/helpers1/layout.js
@@ -0,0 +1,33 @@
+// const marginSmall = 16;
+// const marginLarge = 3 * marginSmall;
+// const margin = marginSmall + marginLarge;
+// const gutter = marginSmall;
+// const outsetAmount = margin / 2;
+// const numCols = 4;
+// const numGutters = numCols - 1;
+// const columnWidth = (768 - 2 * marginLarge - numGutters * gutter) / numCols;
+//
+// const screenwidth = 768;
+// const pageWidth = screenwidth - 2 * marginLarge;
+// const bodyWidth = pageWidth - columnWidth - gutter;
+
+export function body(selector) {
+ return `${selector} {
+ grid-column: margin-left / body;
+ }
+ `;
+}
+
+export function page(selector) {
+ return `${selector} {
+ grid-column: margin-left / page;
+ }
+ `;
+}
+
+export function screen(selector) {
+ return `${selector} {
+ grid-column: start / end;
+ }
+ `;
+}
diff --git a/src/transforms/polyfills.js b/src/transforms/polyfills.js
index d479c15..35ef476 100644
--- a/src/transforms/polyfills.js
+++ b/src/transforms/polyfills.js
@@ -1,5 +1,5 @@
-const webcomponentPath = '../dist/webcomponents-lite.js';
-const intersectionObserverPath = '../dist/intersection-observer.js';
+const webcomponentPath = 'https://distill.pub/third-party/polyfills/webcomponents-lite.js';
+const intersectionObserverPath = 'https://distill.pub/third-party/polyfills/intersection-observer.js';
const template = `
if ('IntersectionObserver' in window &&