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 &&