import {
BITCHUTE_REGEX,
CUSTOM_COMMUNITY_REGEX,
INTERNAL_POST_REGEX,
INTERNAL_POST_TAG_REGEX,
D_TUBE_REGEX,
D_TUBE_REGEX2,
IMG_REGEX,
IPFS_REGEX,
MENTION_REGEX,
INTERNAL_MENTION_REGEX,
TOPIC_REGEX,
INTERNAL_TOPIC_REGEX,
POST_REGEX,
CCC_REGEX,
SPEAK_REGEX,
TWITCH_REGEX,
TWITTER_REGEX,
VIMEO_REGEX,
WHITE_LIST,
YOUTUBE_REGEX,
SPOTIFY_REGEX,
RUMBLE_REGEX,
BRIGHTEON_REGEX,
DOMParser,
LOOM_REGEX,
SECTION_REGEX,
SECTION_LIST
} from '../consts'
import { getSerializedInnerHTML } from './get-inner-html.method'
import { proxifyImageSrc } from '../proxify-image-src'
import { removeChildNodes } from './remove-child-nodes.method'
import { extractYtStartTime } from '../helper'
export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
let href = el.getAttribute('href')
// Continue if href has no value
if (!href) {
return
}
const className = el.getAttribute('class')
// Don't touch user and hashtag links
if (['markdown-author-link', 'markdown-tag-link'].indexOf(className) !== -1) {
return
}
// Do not allow js hrefs
if (href.startsWith('javascript')) {
el.removeAttribute('href')
return
}
// if href is an image url and innerHTML same with href then mark it as image
// & => & can break equality
if (
href.match(IMG_REGEX) &&
href.trim().replace(/&/g, '&') ===
getSerializedInnerHTML(el).trim().replace(/&/g, '&')
) {
const attrs = forApp ? `data-href="${href}" class="markdown-img-link" src="${proxifyImageSrc(href, 0, 0, webp ? 'webp' : 'match')}"` : `class="markdown-img-link" src="${proxifyImageSrc(href, 0, 0, webp ? 'webp' : 'match')}"`
const replaceNode = DOMParser.parseFromString(
``
)
el.parentNode.replaceChild(replaceNode, el)
return
}
if (
href.match(IPFS_REGEX) &&
href.trim().replace(/&/g, '&') ===
getSerializedInnerHTML(el).trim().replace(/&/g, '&') &&
href.indexOf('#') === -1
) {
if (forApp) {
el.setAttribute('data-href', href)
el.removeAttribute('href')
}
el.setAttribute('class', 'markdown-img-link')
removeChildNodes(el)
const img = el.ownerDocument.createElement('img')
img.setAttribute('src', href)
el.appendChild(img)
return
}
// If a hive post
const postMatch = href.match(POST_REGEX)
if (postMatch && WHITE_LIST.includes(postMatch[1].replace(/www./,''))) {
el.setAttribute('class', 'markdown-post-link')
const tag = postMatch[2]
const author = postMatch[3].replace('@', '')
const permlink = postMatch[4]
if (el.textContent === href) {
el.textContent = `@${author}/${permlink}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-tag', tag)
el.setAttribute('data-author', author)
el.setAttribute('data-permlink', permlink)
} else {
const h = `/${tag}/@${author}/${permlink}`
el.setAttribute('href', h)
}
return
}
// If a hive user with url
const mentionMatch = href.match(MENTION_REGEX)
if (mentionMatch && WHITE_LIST.includes(mentionMatch[1].replace(/www./,'')) && mentionMatch.length === 3) {
el.setAttribute('class', 'markdown-author-link')
const author = mentionMatch[2].replace('@', '').toLowerCase()
if (author.indexOf('/')===-1) {
if (el.textContent === href) {
el.textContent = `@${author}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-author', author)
} else {
const h = `/@${author}`
el.setAttribute('href', h)
}
}
return
}
// If a tagged post and profile section links
const tpostMatch = href.match(INTERNAL_POST_TAG_REGEX)
if (
(tpostMatch && tpostMatch.length === 4 && WHITE_LIST.some(v => tpostMatch[1].includes(v))) || (tpostMatch && tpostMatch.length === 4 && tpostMatch[1].indexOf('/') == 0)
) {
// check if permlink is section or section with params ?q=xyz
if (SECTION_LIST.some(v => tpostMatch[3].includes(v))) {
el.setAttribute('class', 'markdown-profile-link')
const author = tpostMatch[2].replace('@', '').toLowerCase()
const section = tpostMatch[3]
if (el.textContent === href) {
el.textContent = `@${author}/${section}`
}
if (forApp) {
const ha = `https://ecency.com/@${author}/${section}`
el.setAttribute('href', ha)
} else {
const h = `/@${author}/${section}`
el.setAttribute('href', h)
}
return
} else {
// check if domain is not whitelist and does contain dot (not tag e.g. `/ecency`)
if (tpostMatch[1] && tpostMatch[1].includes('.') && !WHITE_LIST.some(v => tpostMatch[1].includes(v))) {
return
}
let tag = 'post'
// check if tag does exist and doesn't include dot likely word/tag
if (tpostMatch[1] && !tpostMatch[1].includes('.')) {
[, tag] = tpostMatch
tag = tag.replace('/', '')
}
el.setAttribute('class', 'markdown-post-link')
const author = tpostMatch[2].replace('@', '')
const permlink = tpostMatch[3]
if (el.textContent === href) {
el.textContent = `@${author}/${permlink}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-tag', tag)
el.setAttribute('data-author', author)
el.setAttribute('data-permlink', permlink)
} else {
const h = `/${tag}/@${author}/${permlink}`
el.setAttribute('href', h)
}
return
}
}
// If a hive user with internal url
const imentionMatch = href.match(INTERNAL_MENTION_REGEX)
if (imentionMatch) {
el.setAttribute('class', 'markdown-author-link')
const author = imentionMatch[0].replace('/@', '').toLowerCase()
if (author.indexOf('/')===-1) {
if (el.textContent === href) {
el.textContent = `@${author}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-author', author)
} else {
const h = `/@${author}`
el.setAttribute('href', h)
}
}
return
}
// If a copied post and profile section links
const cpostMatch = href.match(INTERNAL_POST_REGEX)
if (
(cpostMatch && cpostMatch.length === 3 && cpostMatch[1].indexOf('@') === 0)
) {
if (SECTION_LIST.some(v => cpostMatch[2].includes(v))) {
el.setAttribute('class', 'markdown-profile-link')
const author = cpostMatch[1].replace('@', '').toLowerCase()
const section = cpostMatch[2]
if (el.textContent === href) {
el.textContent = `@${author}/${section}`
}
if (forApp) {
const ha = `https://ecency.com/@${author}/${section}`
el.setAttribute('href', ha)
} else {
const h = `/@${author}/${section}`
el.setAttribute('href', h)
}
return
} else {
el.setAttribute('class', 'markdown-post-link')
const tag = 'post'
const author = cpostMatch[1].replace('@', '')
const permlink = cpostMatch[2]
if (el.textContent === href) {
el.textContent = `@${author}/${permlink}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-tag', tag)
el.setAttribute('data-author', author)
el.setAttribute('data-permlink', permlink)
} else {
const h = `/${tag}/@${author}/${permlink}`
el.setAttribute('href', h)
}
return
}
}
// If topic with filters url
const topicMatch = href.match(TOPIC_REGEX)
if (topicMatch && WHITE_LIST.includes(topicMatch[1].replace(/www./,'')) && topicMatch.length === 4) {
el.setAttribute('class', 'markdown-tag-link')
const filter = topicMatch[2]
const tag = topicMatch[3]
if (el.textContent === href) {
el.textContent = `/${filter}/${tag}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-filter', filter)
el.setAttribute('data-tag', tag)
} else {
const h = `/${filter}/${tag}`
el.setAttribute('href', h)
}
return
}
// If topic with filters internal url
const itopicMatch = href.match(INTERNAL_TOPIC_REGEX)
if (itopicMatch && itopicMatch.length === 3) {
el.setAttribute('class', 'markdown-tag-link')
const filter = itopicMatch[1]
const tag = itopicMatch[2]
if (el.textContent === href) {
el.textContent = `/${filter}/${tag}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-filter', filter)
el.setAttribute('data-tag', tag)
} else {
const h = `/${filter}/${tag}`
el.setAttribute('href', h)
}
return
}
// If a custom hive community link
const comMatch = href.match(CUSTOM_COMMUNITY_REGEX)
if (comMatch && WHITE_LIST.includes(comMatch[1])) {
el.setAttribute('class', 'markdown-community-link')
const community = comMatch[2]
let filter = comMatch[3].substring(1)
if (!filter) filter = 'created'
if (filter === 'about' || filter === 'discord') {
filter = 'created'
}
if (el.textContent === href) {
el.textContent = `${filter}/${community}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-community', community)
el.setAttribute('data-filter', filter)
} else {
const h = `/${filter}/${community}`
el.setAttribute('href', h)
}
return
}
// If a collections post
const cccMatch = href.match(CCC_REGEX)
if (cccMatch && WHITE_LIST.includes(cccMatch[1])) {
el.setAttribute('class', 'markdown-post-link')
const tag = 'ccc'
const author = cccMatch[2].replace('@', '')
const permlink = cccMatch[3]
if (el.textContent === href) {
el.textContent = `@${author}/${permlink}`
}
if (forApp) {
el.removeAttribute('href')
el.setAttribute('data-tag', tag)
el.setAttribute('data-author', author)
el.setAttribute('data-permlink', permlink)
} else {
const h = `/${tag}/@${author}/${permlink}`
el.setAttribute('href', h)
}
return
}
const BCmatch = href.match(BITCHUTE_REGEX)
if (BCmatch && el.textContent.trim() === href) {
const e = BITCHUTE_REGEX.exec(href)
const vid = e[1]
el.setAttribute('class', 'markdown-video-link')
el.removeAttribute('href')
const embedSrc = `https://www.bitchute.com/embed/${vid}/`
el.textContent = ''
el.setAttribute('data-embed-src', embedSrc)
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
const RBmatch = href.match(RUMBLE_REGEX)
if (RBmatch && el.textContent.trim() === href) {
const e = RUMBLE_REGEX.exec(href)
if (e[1]) {
const vid = e[1]
const embedSrc = `https://www.rumble.com/embed/${vid}/?pub=4`
el.setAttribute('class', 'markdown-video-link')
el.removeAttribute('href')
el.textContent = ''
el.setAttribute('data-embed-src', embedSrc)
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
}
const BNmatch = href.match(BRIGHTEON_REGEX)
if (BNmatch && el.textContent.trim() === href) {
const e = BRIGHTEON_REGEX.exec(href)
if (e[2]) {
const vid = e[2]
const embedSrc = `https://www.brighteon.com/embed/${vid}`
el.setAttribute('class', 'markdown-video-link')
el.removeAttribute('href')
el.textContent = ''
el.setAttribute('data-embed-src', embedSrc)
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
}
// If a youtube video
let match = href.match(YOUTUBE_REGEX)
if (match && el.textContent.trim() === href) {
const e = YOUTUBE_REGEX.exec(href)
if (e[1]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-youtube')
el.removeAttribute('href')
const vid = e[1]
const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split('?')[0]}/hqdefault.jpg`, 0, 0, webp ? 'webp' : 'match')
const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`
el.textContent = ''
el.setAttribute('data-embed-src', embedSrc);
el.setAttribute('data-youtube', vid);
//extract start time if available
const startTime = extractYtStartTime(href);
if(startTime){
el.setAttribute('data-start-time', startTime);
}
const thumbImg = el.ownerDocument.createElement('img')
thumbImg.setAttribute('class', 'no-replace video-thumbnail')
thumbImg.setAttribute('itemprop', 'thumbnailUrl')
thumbImg.setAttribute('src', thumbnail)
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(thumbImg)
el.appendChild(play)
return
}
}
// If vimeo video
match = href.match(VIMEO_REGEX)
if (match && href === el.textContent) {
const e = VIMEO_REGEX.exec(href)
if (e[3]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-vimeo')
el.removeAttribute('href')
const embedSrc = `https://player.vimeo.com/video/${e[3]}`
el.textContent = ''
const ifr = el.ownerDocument.createElement('iframe')
ifr.setAttribute('frameborder', '0')
ifr.setAttribute('allowfullscreen', 'true')
ifr.setAttribute('src', embedSrc)
el.appendChild(ifr)
return
}
}
// If twitch video
match = href.match(TWITCH_REGEX)
if (match && href === el.textContent) {
const e = TWITCH_REGEX.exec(href)
if (e[2]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-twitch')
el.removeAttribute('href')
let embedSrc = ''
if (e[1] === undefined) {
embedSrc = `https://player.twitch.tv/?channel=${e[2]}`
} else {
embedSrc = `https://player.twitch.tv/?video=${e[1]}`
}
el.textContent = ''
const ifr = el.ownerDocument.createElement('iframe')
ifr.setAttribute('frameborder', '0')
ifr.setAttribute('allowfullscreen', 'true')
ifr.setAttribute('src', embedSrc)
el.appendChild(ifr)
return
}
}
// If a spotify audio
match = href.match(SPOTIFY_REGEX)
if (match && el.textContent.trim() === href) {
const e = SPOTIFY_REGEX.exec(href)
if (e[1]) {
el.setAttribute('class', 'markdown-audio-link markdown-audio-link-spotify')
el.removeAttribute('href')
const embedSrc = `https://open.spotify.com/embed/playlist/${e[1]}`
el.textContent = ''
const ifr = el.ownerDocument.createElement('iframe')
ifr.setAttribute('frameborder', '0')
ifr.setAttribute('allowfullscreen', 'true')
ifr.setAttribute('src', embedSrc)
ifr.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups')
el.appendChild(ifr)
return
}
}
// If a spotify audio
match = href.match(LOOM_REGEX)
if (match && el.textContent.trim() === href) {
const e = LOOM_REGEX.exec(href)
if (e[2]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-loom')
el.removeAttribute('href')
const embedSrc = `https://www.loom.com/embed/${e[2]}`
el.textContent = ''
const ifr = el.ownerDocument.createElement('iframe')
ifr.setAttribute('frameborder', '0')
ifr.setAttribute('allowfullscreen', 'true')
ifr.setAttribute('src', embedSrc)
ifr.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups')
el.appendChild(ifr)
return
}
}
// If a d.tube video
match = href.match(D_TUBE_REGEX)
if (match) {
// Only d.tube links contains an image
const imgEls = el.getElementsByTagName('img')
if (imgEls.length === 1 || el.textContent.trim() === href) {
const e = D_TUBE_REGEX.exec(href)
// e[2] = username, e[3] object id
if (e[2] && e[3]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-dtube')
el.removeAttribute('href')
const videoHref = `https://emb.d.tube/#!/${e[2]}/${e[3]}`
// el.setAttribute('data-video-href', videoHref)
el.setAttribute('data-embed-src', videoHref)
//process thumb img element
if (imgEls.length === 1) {
const thumbnail = proxifyImageSrc(imgEls[0].getAttribute('src').replace(/\s+/g, ''), 0, 0, webp ? 'webp' : 'match')
const thumbImg = el.ownerDocument.createElement('img')
thumbImg.setAttribute('class', 'no-replace video-thumbnail')
thumbImg.setAttribute('itemprop', 'thumbnailUrl')
thumbImg.setAttribute('src', thumbnail)
el.appendChild(thumbImg)
// Remove image.
el.removeChild(imgEls[0])
} else {
el.textContent = '';
}
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
}
}
match = href.match(D_TUBE_REGEX2)
if (match) {
const e = D_TUBE_REGEX2.exec(href)
// e[2] = username, e[3] object id
if (e[2] && e[3]) {
el.setAttribute('class', 'markdown-video-link markdown-video-link-dtube')
el.removeAttribute('href')
el.textContent = '';
const videoHref = `https://emb.d.tube/#!/${e[2]}/${e[3]}`
// el.setAttribute('data-video-href', videoHref);
el.setAttribute('data-embed-src', videoHref)
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
}
// Detect 3Speak
match = href.match(SPEAK_REGEX)
if (match) {
const imgEls = el.getElementsByTagName('img')
if (imgEls.length === 1 || el.textContent.trim() === href) {
const e = SPEAK_REGEX.exec(href)
// e[1] = tld , e[3] = embed address
if (e[1] && e[3]) {
const videoHref = `https://3speak.${e[1]}/embed?v=${e[3]}`
el.setAttribute('class', 'markdown-video-link markdown-video-link-speak')
el.removeAttribute('href')
el.setAttribute('data-embed-src', videoHref)
if (el.textContent.trim() === href) {
el.textContent = ''
}
if (imgEls.length === 1) {
const thumbnail = proxifyImageSrc(imgEls[0].getAttribute('src').replace(/\s+/g, ''), 0, 0, webp ? 'webp' : 'match')
const thumbImg = el.ownerDocument.createElement('img')
thumbImg.setAttribute('class', 'no-replace video-thumbnail')
thumbImg.setAttribute('itemprop', 'thumbnailUrl')
thumbImg.setAttribute('src', thumbnail)
el.appendChild(thumbImg)
// Remove image.
el.removeChild(imgEls[0])
}
const play = el.ownerDocument.createElement('span')
play.setAttribute('class', 'markdown-video-play')
el.appendChild(play)
return
}
}
}
// If tweets
const matchT = href.match(TWITTER_REGEX)
if (matchT && el.textContent.trim() === href) {
const e = TWITTER_REGEX.exec(href)
if (e) {
const url = e[0].replace(/(<([^>]+)>)/gi, '')
const author = e[1].replace(/(<([^>]+)>)/gi, '')
const twitterCode = `
` const replaceNode = DOMParser.parseFromString(twitterCode) el.parentNode.replaceChild(replaceNode, el) return } } if (href.indexOf('https://hivesigner.com/sign/account-witness-vote?witness=') === 0 && forApp) { el.setAttribute('class', 'markdown-witnesses-link') el.setAttribute('data-href', href) el.removeAttribute('href') return } if (href.indexOf('hivesigner.com/sign/update-proposal-votes?proposal_ids') > 0 && forApp) { const m = decodeURI(href).match(/proposal_ids=\[(\d+)]/) if (m) { el.setAttribute('class', 'markdown-proposal-link') el.setAttribute('data-href', href) el.setAttribute('data-proposal', m[1]) el.removeAttribute('href') return } } // If nothing matched element as external link so it will be opened in external window el.setAttribute('class', 'markdown-external-link') // Prepend https if no scheme provided if (!(/^((#)|(mailto:)|(\/(?!\/))|(((steem|hive|esteem|ecency|https?):)?\/\/))/.test(href))) { href = `https://${href}` } if (forApp) { el.setAttribute('data-href', href) const match = href.match(YOUTUBE_REGEX) if (match) { const e = YOUTUBE_REGEX.exec(href) if (e[1]) { const vid = e[1] el.setAttribute('data-youtube', vid); //extract start time if available const startTime = extractYtStartTime(href); if(startTime){ el.setAttribute('data-start-time', startTime); } } } el.removeAttribute('href') } else { const matchS = href.match(SECTION_REGEX) if(matchS) { el.setAttribute('class', 'markdown-internal-link'); } else { el.setAttribute('target', '_blank'); el.setAttribute('rel', 'noopener'); } } }${url}
- ${author}