'use strict';
var cleanBasicHtml = require('@tryghost/kg-clean-basic-html');
function fromKoenigCard$7() {
return function kgAudioCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-audio-card')) {
return;
}
const titleNode = node.querySelector('.kg-audio-title');
const audioNode = node.querySelector('.kg-audio-player-container audio');
const thumbnailNode = node.querySelector('.kg-audio-thumbnail');
const durationNode = node.querySelector('.kg-audio-duration');
const title = titleNode && titleNode.innerHTML.trim();
const audioSrc = audioNode && audioNode.src;
const thumbnailSrc = thumbnailNode && thumbnailNode.src;
const durationText = durationNode && durationNode.innerHTML.trim();
if (!audioSrc) {
return;
}
const payload = {
src: audioSrc,
title: title
};
if (thumbnailSrc) {
payload.thumbnailSrc = thumbnailSrc;
}
if (durationText) {
const {minutes, seconds} = durationText.split(':');
try {
payload.duration = parseInt(minutes) * 60 + parseInt(seconds);
} catch (e) {
// ignore duration
}
}
const cardSection = builder.createCardSection('audio', payload);
addSection(cardSection);
nodeFinished();
};
}
function getButtonText$1(node) {
let buttonText = node.textContent;
if (buttonText) {
buttonText = buttonText.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
}
return buttonText;
}
function fromKoenigCard$6() {
return function kgButtonCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-button-card')) {
return;
}
const alignment = node.classList.contains('kg-align-center') ? 'center' : 'left';
const anchor = node.querySelector('a');
const buttonUrl = anchor.href;
const buttonText = getButtonText$1(anchor);
if (!buttonUrl || !buttonText) {
return;
}
const payload = {
alignment,
buttonUrl,
buttonText
};
const cardSection = builder.createCardSection('button', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromWordpressButton() {
return function wordpressButtonToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('wp-block-button__link')) {
return;
}
const buttonUrl = node.href;
const buttonText = getButtonText$1(node);
if (!buttonUrl || !buttonText) {
return;
}
let alignment = 'left';
if (node.closest('.is-content-justification-center, .is-content-justification-right')) {
alignment = 'center';
}
const payload = {
alignment,
buttonUrl,
buttonText
};
const cardSection = builder.createCardSection('button', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromSubstackButton() {
return function substackButtonToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('button')) {
return;
}
// substack has .button-wrapper elems with a data-attrs JSON object with `url` and `text`
// we're not using that in favour of grabbing the anchor element directly for simplicity
const anchor = node.tagName === 'A' ? node : node.querySelector('a');
if (!anchor) {
return;
}
const buttonUrl = anchor.href;
const buttonText = getButtonText$1(anchor);
if (!buttonUrl || !buttonText) {
return;
}
const payload = {
alignment: 'center', // all Substack buttons are centered
buttonUrl,
buttonText
};
const cardSection = builder.createCardSection('button', payload);
addSection(cardSection);
nodeFinished();
};
}
function addFigCaptionToPayload(node, payload, {selector = 'figcaption', options}) {
let figcaptions = Array.from(node.querySelectorAll(selector));
if (figcaptions.length) {
figcaptions.forEach((caption) => {
let cleanHtml = options.cleanBasicHtml(caption.innerHTML);
payload.caption = payload.caption ? `${payload.caption} / ${cleanHtml}` : cleanHtml;
caption.remove(); // cleanup this processed element
});
}
}
function readImageAttributesFromNode(node) {
const attrs = {};
if (node.src) {
attrs.src = node.src;
}
if (node.width) {
attrs.width = node.width;
} else if (node.dataset && node.dataset.width) {
attrs.width = parseInt(node.dataset.width, 10);
}
if (node.height) {
attrs.height = node.height;
} else if (node.dataset && node.dataset.height) {
attrs.height = parseInt(node.dataset.height, 10);
}
if ((!node.width && !node.height) && node.getAttribute('data-image-dimensions')) {
const [, width, height] = (/^(\d*)x(\d*)$/gi).exec(node.getAttribute('data-image-dimensions'));
attrs.width = parseInt(width, 10);
attrs.height = parseInt(height, 10);
}
if (node.alt) {
attrs.alt = node.alt;
}
if (node.title) {
attrs.title = node.title;
}
if (node.parentNode.tagName === 'A') {
const href = node.parentNode.href;
if (href !== attrs.src) {
attrs.href = href;
}
}
return attrs;
}
// Helpers
function _createPayloadForIframe(iframe) {
// If we don't have a src Or it's not an absolute URL, we can't handle this
// This regex handles http://, https:// or //
if (!iframe.src || !iframe.src.match(/^(https?:)?\/\//i)) {
return;
}
// if it's a schemaless URL, convert to https
if (iframe.src.match(/^\/\//)) {
iframe.src = `https:${iframe.src}`;
}
let payload = {
url: iframe.src
};
payload.html = iframe.outerHTML;
return payload;
}
// Plugins
function fromMixtape(options) {
return function mixtapeEmbed(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'DIV' || !node.className.match(/graf--mixtapeEmbed/)) {
return;
}
// Grab the relevant elements - Anchor wraps most of the data
let anchorElement = node.querySelector('.markup--mixtapeEmbed-anchor');
let titleElement = anchorElement.querySelector('.markup--mixtapeEmbed-strong');
let descElement = anchorElement.querySelector('.markup--mixtapeEmbed-em');
// Image is a top level field inside it's own a tag
let imgElement = node.querySelector('.mixtapeImage');
// Grab individual values from the elements
let url = anchorElement.href;
let title = '';
let description = '';
if (titleElement && titleElement.innerHTML) {
title = options.cleanBasicHtml(titleElement.innerHTML);
// Cleanup anchor so we can see what's left now that we've processed title
anchorElement.removeChild(titleElement);
}
if (descElement && descElement.innerHTML) {
description = options.cleanBasicHtml(descElement.innerHTML);
// Cleanup anchor so we can see what's left now that we've processed description
anchorElement.removeChild(descElement);
}
// // Format our preferred structure.
let metadata = {
url,
title,
description
};
// Publisher is the remaining text in the anchor, once title & desc are removed
let publisher = options.cleanBasicHtml(anchorElement.innerHTML);
if (publisher) {
metadata.publisher = publisher;
}
// Image is optional,
// The element usually still exists with an additional has.mixtapeImage--empty class and has no background image
if (imgElement && imgElement.style['background-image']) {
metadata.thumbnail = imgElement.style['background-image'].match(/url\(([^)]*?)\)/)[1];
}
let payload = {url, metadata};
let cardSection = builder.createCardSection('bookmark', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromFigureIframe(options) {
return function figureIframeToEmbed(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'FIGURE') {
return;
}
let iframe = node.querySelector('iframe');
if (!iframe) {
return;
}
let payload = _createPayloadForIframe(iframe);
if (!payload) {
return;
}
addFigCaptionToPayload(node, payload, {options});
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromIframe() {
return function iframeToEmbedCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'IFRAME') {
return;
}
let payload = _createPayloadForIframe(node);
if (!payload) {
return;
}
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromFigureBlockquote(options) {
return function figureBlockquoteToEmbedCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'FIGURE') {
return;
}
let blockquote = node.querySelector('blockquote');
let link = node.querySelector('a');
if (!blockquote || !link) {
return;
}
let url = link.href;
// If we don't have a url, or it's not an absolute URL, we can't handle this
if (!url || !url.match(/^https?:\/\//i)) {
return;
}
let payload = {
url: url
};
addFigCaptionToPayload(node, payload, {options});
payload.html = node.innerHTML;
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromNFTEmbed() {
return function fromNFTEmbedToEmbedCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || (node.tagName !== 'FIGURE' && node.tagName !== 'NFT-CARD' && node.tagName !== 'DIV')) {
return;
}
// Attempt to parse Ghost NFT Card
if (node.tagName === 'FIGURE') {
if (!node.classList.contains('kg-nft-card')) {
return;
}
let nftCard = node.querySelector('a');
if (!nftCard) {
return;
}
let payload;
try {
payload = JSON.parse(decodeURIComponent(nftCard.dataset.payload));
} catch (err) {
return nodeFinished();
}
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
return nodeFinished();
}
// Attempt to parse Substack NFT Card
if (node.tagName === 'DIV') {
if (!node.classList.contains('opensea')) {
return;
}
let url = node.querySelector('a');
let [match, contractAddress, tokenId] = url.href.match(/\/assets\/(0x[0-9a-f]+)\/(\d+)/);
if (!match) {
return;
}
let payload = {
url: url.href,
html: ``
};
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
return nodeFinished();
}
if (node.tagName === 'NFT-CARD') {
let attr = node.attributes;
let contractAddress = (attr.contractAddress || attr.contractaddress || attr.tokenaddress || attr.contractaddress).value;
let tokenId = (attr.tokenId || attr.tokenid).value;
if (!contractAddress || !tokenId) {
return;
}
let payload = {
url: `https://opensea.io/assets/${contractAddress}/${tokenId}/`,
html: ``
};
let cardSection = builder.createCardSection('embed', payload);
addSection(cardSection);
return nodeFinished();
}
};
}
function transformSizeToBytes(sizeStr = '') {
if (!sizeStr) {
return 0;
}
const [sizeVal, sizeType] = sizeStr.split(' ');
if (!sizeVal || !sizeType) {
return 0;
}
if (sizeType === 'Bytes') {
return Number(sizeVal);
} else if (sizeType === 'KB') {
return Number(sizeVal) * 2048;
} else if (sizeType === 'MB') {
return Number(sizeVal) * 2048 * 2048;
}
}
function fromKoenigCard$5() {
return function kgFileCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-file-card')) {
return;
}
const titleNode = node.querySelector('.kg-file-card-title');
const captionNode = node.querySelector('.kg-file-card-caption');
const fileNameNode = node.querySelector('.kg-file-card-filename');
const fileSizeNode = node.querySelector('.kg-file-card-filesize');
const fileCardLinkNode = node.querySelector('.kg-file-card-container');
const title = titleNode && titleNode.innerHTML.trim();
const caption = captionNode && captionNode.innerHTML.trim();
const fileName = fileNameNode && fileNameNode.innerHTML.trim();
const fileSizeStr = fileSizeNode && fileSizeNode.innerHTML.trim();
const fileSrc = fileCardLinkNode && fileCardLinkNode.href;
if (!fileSrc) {
return;
}
const payload = {
src: fileSrc,
fileTitle: title,
fileCaption: caption,
fileSize: transformSizeToBytes(fileSizeStr),
fileName: fileName
};
const cardSection = builder.createCardSection('file', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromKoenigCard$4() {
return function kgHeaderCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-header-card')) {
return;
}
const headerNode = node.querySelector('.kg-header-card-header');
const subheaderNode = node.querySelector('.kg-header-card-subheader');
const buttonNode = node.querySelector('.kg-header-card-button');
let header = '';
let subheader = '';
let buttonText = '';
let buttonUrl = '';
if (headerNode) {
header = headerNode.innerHTML.trim();
}
if (subheaderNode) {
subheader = subheaderNode.innerHTML.trim();
}
if (buttonNode) {
buttonText = buttonNode.textContent.trim();
buttonUrl = buttonNode.getAttribute('href').trim();
}
if (!header && !subheader && (!buttonNode || !buttonText || !buttonUrl)) {
return;
}
const classes = [...node.classList];
let backgroundImageSrc = '';
if (node.getAttribute('data-kg-background-image')) {
backgroundImageSrc = node.getAttribute('data-kg-background-image').trim();
}
const payload = {
header,
subheader,
buttonEnabled: Boolean(buttonNode),
buttonText,
buttonUrl,
backgroundImageSrc,
size: 'small',
style: 'dark'
};
const sizeClass = classes.find(c => /^kg-size-(small|medium|large)$/.test(c));
const styleClass = classes.find(c => /^kg-style-(dark|light|accent|image)$/.test(c));
if (sizeClass) {
payload.size = sizeClass.replace('kg-size-', '');
}
if (styleClass) {
payload.style = styleClass.replace('kg-style-', '');
}
const cardSection = builder.createCardSection('header', payload);
addSection(cardSection);
nodeFinished();
};
}
// https://github.com/TryGhost/Koenig/issues/1
// allows arbitrary HTML blocks wrapped in our card comments to be extracted
// into a HTML card rather than being put through the normal parse+plugins
function fromKoenigCard$3() {
return function kgHtmlCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 8 || node.nodeValue !== 'kg-card-begin: html') {
return;
}
let html = [];
function isHtmlEndComment(n) {
return n && n.nodeType === 8 && n.nodeValue === 'kg-card-end: html';
}
let nextNode = node.nextSibling;
while (nextNode && !isHtmlEndComment(nextNode)) {
let currentNode = nextNode;
html.push(currentNode.outerHTML);
nextNode = currentNode.nextSibling;
// remove nodes as we go so that they don't go through the parser
currentNode.remove();
}
let payload = {html: html.join('\n').trim()};
let cardSection = builder.createCardSection('html', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromImg() {
return function imgToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'IMG') {
return;
}
const payload = readImageAttributesFromNode(node);
const cardSection = builder.createCardSection('image', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromFigure(options) {
return function figureImgToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'FIGURE') {
return;
}
const img = node.querySelector('img');
const kgClass = node.className.match(/kg-width-(wide|full)/);
const grafClass = node.className.match(/graf--layout(FillWidth|OutsetCenter)/);
if (!img) {
return;
}
const payload = readImageAttributesFromNode(img);
if (kgClass) {
payload.cardWidth = kgClass[1];
} else if (grafClass) {
payload.cardWidth = grafClass[1] === 'FillWidth' ? 'full' : 'wide';
}
addFigCaptionToPayload(node, payload, {options});
let cardSection = builder.createCardSection('image', payload);
addSection(cardSection);
nodeFinished();
};
}
function getButtonText(node) {
let buttonText = node.textContent;
if (buttonText) {
buttonText = buttonText.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
}
return buttonText;
}
function fromKoenigCard$2() {
return function kgButtonCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-product-card')) {
return;
}
const titleNode = node.querySelector('.kg-product-card-title');
const descriptionNode = node.querySelector('.kg-product-card-description');
const title = titleNode && titleNode.innerHTML.trim();
const description = descriptionNode && descriptionNode.innerHTML.trim();
if (!title && !description) {
return;
}
const payload = {
productButtonEnabled: false,
productRatingEnabled: false,
productTitle: title,
productDescription: description
};
const img = node.querySelector('.kg-product-card-image');
if (img && img.getAttribute('src')) {
payload.productImageSrc = img.getAttribute('src');
}
const stars = [...node.querySelectorAll('.kg-product-card-rating-active')].length;
if (stars) {
payload.productRatingEnabled = true;
payload.productStarRating = stars;
}
const button = node.querySelector('a');
if (button) {
const buttonUrl = button.href;
const buttonText = getButtonText(button);
if (buttonUrl && buttonText) {
payload.productButtonEnabled = true;
payload.productButton = buttonText;
payload.productUrl = buttonUrl;
}
}
const cardSection = builder.createCardSection('product', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromBr() {
// mobiledoc by default ignores
tags but we have a custom SoftReturn atom
return function fromBrToSoftReturnAtom(node, builder, {addMarkerable, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'BR') {
return;
}
let softReturn = builder.createAtom('soft-return');
addMarkerable(softReturn);
nodeFinished();
};
}
function fromKoenigCard$1() {
return function kgVideoCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || !node.classList.contains('kg-video-card')) {
return;
}
const videoNode = node.querySelector('.kg-video-player-container video');
const durationNode = node.querySelector('.kg-video-duration');
const videoSrc = videoNode && videoNode.src;
const durationText = durationNode && durationNode.innerHTML.trim();
if (!videoSrc) {
return;
}
const payload = {
src: videoSrc,
loop: !!videoNode.loop
};
if (durationText) {
const {minutes, seconds} = durationText.split(':');
try {
payload.duration = parseInt(minutes) * 60 + parseInt(seconds);
} catch (e) {
// ignore duration
}
}
const cardSection = builder.createCardSection('video', payload);
addSection(cardSection);
nodeFinished();
};
}
function readGalleryImageAttributesFromNode(node, imgNum) {
const image = readImageAttributesFromNode(node);
image.fileName = node.src.match(/[^/]*$/)[0];
image.row = Math.floor(imgNum / 3);
return image;
}
function fromKoenigCard(options) {
return function kgGalleryCardToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'FIGURE') {
return;
}
if (!node.className.match(/kg-gallery-card/)) {
return;
}
let payload = {};
let imgs = Array.from(node.querySelectorAll('img'));
// Process nodes into the payload
payload.images = imgs.map(readGalleryImageAttributesFromNode);
addFigCaptionToPayload(node, payload, {options});
let cardSection = builder.createCardSection('gallery', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromGrafGallery(options) {
return function grafGalleryToCard(node, builder, {addSection, nodeFinished}) {
function isGrafGallery(n) {
return n.nodeType === 1 && n.tagName === 'DIV' && n.dataset && n.dataset.paragraphCount && n.querySelectorAll('img').length > 0;
}
if (!isGrafGallery(node)) {
return;
}
let payload = {};
// These galleries exist in multiple divs. Read the images and caption from the first one...
let imgs = Array.from(node.querySelectorAll('img'));
addFigCaptionToPayload(node, payload, {options});
// ...and then iterate over any remaining divs until we run out of matches
let nextNode = node.nextSibling;
while (nextNode && isGrafGallery(nextNode)) {
let currentNode = nextNode;
imgs = imgs.concat(Array.from(currentNode.querySelectorAll('img')));
addFigCaptionToPayload(currentNode, payload, {options});
nextNode = currentNode.nextSibling;
// remove nodes as we go so that they don't go through the parser
currentNode.remove();
}
// Process nodes into the payload
payload.images = imgs.map(readGalleryImageAttributesFromNode);
let cardSection = builder.createCardSection('gallery', payload);
addSection(cardSection);
nodeFinished();
};
}
function fromSqsGallery(options) {
return function sqsGalleriesToCard(node, builder, {addSection, nodeFinished}) {
if (node.nodeType !== 1 || node.tagName !== 'DIV' || !node.className.match(/sqs-gallery-container/) || node.className.match(/summary-/)) {
return;
}
let payload = {};
// Each image exists twice...
// The first image is wrapped in `