'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 `