diff options
Diffstat (limited to 'vnext/src/utils/embed.js')
-rw-r--r-- | vnext/src/utils/embed.js | 260 |
1 files changed, 130 insertions, 130 deletions
diff --git a/vnext/src/utils/embed.js b/vnext/src/utils/embed.js index f16342cf..afec71b4 100644 --- a/vnext/src/utils/embed.js +++ b/vnext/src/utils/embed.js @@ -2,21 +2,21 @@ function htmlEscape(html) { return html.replace(/&/g, '&') .replace(/"/g, '"') .replace(/</g, '<') - .replace(/>/g, '>'); + .replace(/>/g, '>') } function insertAfter(newNode, referenceNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling) } function setContent(containerNode, ...newNodes) { - removeAllFrom(containerNode); - newNodes.forEach(n => containerNode.appendChild(n)); - return containerNode; + removeAllFrom(containerNode) + newNodes.forEach(n => containerNode.appendChild(n)) + return containerNode } function removeAllFrom(fromNode) { - fromNode.innerHTML = ''; + fromNode.innerHTML = '' } // rules :: [{pr: number, re: RegExp, with: string}] @@ -24,96 +24,96 @@ function removeAllFrom(fromNode) { // rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string]}] // rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string, Function]}] function formatText(txt, rules) { - let idCounter = 0; - function nextId() { return idCounter++; } + let idCounter = 0 + function nextId() { return idCounter++ } function ft(txt, rules) { - let matches = rules.map(r => { r.re.lastIndex = 0; return [r, r.re.exec(txt)]; }) + let matches = rules.map(r => { r.re.lastIndex = 0; return [r, r.re.exec(txt)] }) .filter(([, m]) => m !== null) - .sort(([r1, m1], [r2, m2]) => (r1.pr - r2.pr) || (m1.index - m2.index)); + .sort(([r1, m1], [r2, m2]) => (r1.pr - r2.pr) || (m1.index - m2.index)) if (matches && matches.length > 0) { - let [rule, match] = matches[0]; - let subsequentRules = rules.filter(r => r.pr >= rule.pr); - let idStr = `<>(${nextId()})<>`; - let outerStr = txt.substring(0, match.index) + idStr + txt.substring(rule.re.lastIndex); + let [rule, match] = matches[0] + let subsequentRules = rules.filter(r => r.pr >= rule.pr) + let idStr = `<>(${nextId()})<>` + let outerStr = txt.substring(0, match.index) + idStr + txt.substring(rule.re.lastIndex) let innerStr = (rule.brackets) - ? (() => { let [l, r, f] = rule.with; return l + ft((f ? f(match[1]) : match[1]), subsequentRules) + r; })() - : match[0].replace(rule.re, rule.with); - return ft(outerStr, subsequentRules).replace(idStr, innerStr); + ? (() => { let [l, r, f] = rule.with; return l + ft((f ? f(match[1]) : match[1]), subsequentRules) + r })() + : match[0].replace(rule.re, rule.with) + return ft(outerStr, subsequentRules).replace(idStr, innerStr) } - return txt; + return txt } - return ft(htmlEscape(txt), rules); // idStr above relies on the fact the text is escaped + return ft(htmlEscape(txt), rules) // idStr above relies on the fact the text is escaped } function fixWwwLink(url) { - return url.replace(/^(?!([a-z]+:)?\/\/)/i, '//'); + return url.replace(/^(?!([a-z]+:)?\/\/)/i, '//') } function makeNewNode(embedType, aNode, reResult) { const withClasses = el => { if (embedType.className) { - el.classList.add(...embedType.className.split(' ')); + el.classList.add(...embedType.className.split(' ')) } - return el; - }; - return embedType.makeNode(aNode, reResult, withClasses(document.createElement('div'))); + return el + } + return embedType.makeNode(aNode, reResult, withClasses(document.createElement('div'))) } function makeIframe(src, w, h, scrolling = 'no') { - let iframe = document.createElement('iframe'); - iframe.style.width = w; - iframe.style.height = h; - iframe.frameBorder = '0'; - iframe.scrolling = scrolling; - iframe.setAttribute('allowFullScreen', ''); - iframe.src = src; - iframe.innerHTML = 'Cannot show iframes.'; - return iframe; + let iframe = document.createElement('iframe') + iframe.style.width = w + iframe.style.height = h + iframe.frameBorder = '0' + iframe.scrolling = scrolling + iframe.setAttribute('allowFullScreen', '') + iframe.src = src + iframe.innerHTML = 'Cannot show iframes.' + return iframe } function makeResizableToRatio(element, ratio) { - element.setAttribute('data-ratio', ratio); - makeResizable(element, w => w * element.getAttribute('data-ratio')); + element.setAttribute('data-ratio', ratio) + makeResizable(element, w => w * element.getAttribute('data-ratio')) } // calcHeight :: Number -> Number -- calculate element height for a given width function makeResizable(element, calcHeight) { const setHeight = el => { if (document.body.contains(el) && (el.offsetWidth > 0)) { - el.style.height = (calcHeight(el.offsetWidth)).toFixed(2) + 'px'; + el.style.height = (calcHeight(el.offsetWidth)).toFixed(2) + 'px' } - }; - window.addEventListener('resize', () => setHeight(element)); - setHeight(element); + } + window.addEventListener('resize', () => setHeight(element)) + setHeight(element) } function extractDomain(url) { - const domainRe = /^(?:https?:\/\/)?(?:[^@/\n]+@)?(?:www\.)?([^:/\n]+)/i; - let result = domainRe.exec(url) || []; + const domainRe = /^(?:https?:\/\/)?(?:[^@/\n]+@)?(?:www\.)?([^:/\n]+)/i + let result = domainRe.exec(url) || [] if (result.length > 0) { - return result[1]; + return result[1] } } function urlReplace(match, p1, p2, p3) { - let isBrackets = (p1 !== undefined); + let isBrackets = (p1 !== undefined) return (isBrackets) ? `<a href="${fixWwwLink(p2 || p3)}">${p1}</a>` - : `<a href="${fixWwwLink(match)}">${extractDomain(match)}</a>`; + : `<a href="${fixWwwLink(match)}">${extractDomain(match)}</a>` } function urlReplaceInCode(match, p1, p2, p3) { - let isBrackets = (p1 !== undefined); + let isBrackets = (p1 !== undefined) return (isBrackets) ? `<a href="${fixWwwLink(p2 || p3)}">${match}</a>` - : `<a href="${fixWwwLink(match)}">${match}</a>`; + : `<a href="${fixWwwLink(match)}">${match}</a>` } function messageReplyReplace(messageId) { return function(match, mid, rid) { - let replyPart = (rid && rid != '0') ? '#' + rid : ''; - return `<a href="/m/${mid || messageId}${replyPart}">${match}</a>`; - }; + let replyPart = (rid && rid != '0') ? '#' + rid : '' + return `<a href="/m/${mid || messageId}${replyPart}">${match}</a>` + } } /** @@ -125,8 +125,8 @@ function messageReplyReplace(messageId) { * @returns {string} formatted message */ function juickFormat(txt, messageId, isCode) { - const urlRe = /(?:\[([^\][]+)\](?:\[([^\]]+)\]|\(((?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[-\S+*&@#/%=~|$?!:;,.])*(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[\S+*&@#/%=~|$]))\))|\b(?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[-\S+*&@#/%=~|$?!:;,.])*(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[\S+*&@#/%=~|$]))/gi; - const bqReplace = m => m.replace(/^(?:>|>)\s?/gmi, ''); + const urlRe = /(?:\[([^\][]+)\](?:\[([^\]]+)\]|\(((?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[-\S+*&@#/%=~|$?!:;,.])*(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[\S+*&@#/%=~|$]))\))|\b(?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[-\S+*&@#/%=~|$?!:;,.])*(?:\([-\S+*&@#/%=~|$?!:;,.]*\)|[\S+*&@#/%=~|$]))/gi + const bqReplace = m => m.replace(/^(?:>|>)\s?/gmi, '') return (isCode) ? formatText(txt, [ { pr: 1, re: urlRe, with: urlReplaceInCode }, @@ -142,7 +142,7 @@ function juickFormat(txt, messageId, isCode) { { pr: 2, re: /\B\/([^\n]+?)\/((?=\s)|(?=$)|(?=[!"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<i>', '</i>'] }, { pr: 2, re: /\b_([^\n]+?)_((?=\s)|(?=$)|(?=[!"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<u>', '</u>'] }, { pr: 3, re: /\n/g, with: '<br/>' }, - ]); + ]) } /** * @external RegExpExecArray @@ -179,9 +179,9 @@ function getEmbeddableLinkTypes() { re: /\.(jpe?g|png|svg)(:[a-zA-Z]+)?(?:\?[\w&;?=]*)?$/i, makeNode: function(aNode, reResult, div) { // dirty fix for dropbox urls - let url = aNode.href.endsWith('dl=0') ? aNode.href.replace('dl=0', 'raw=1') : aNode.href; - div.innerHTML = `<a href="${url}"><img src="${url}"></a>`; - return div; + let url = aNode.href.endsWith('dl=0') ? aNode.href.replace('dl=0', 'raw=1') : aNode.href + div.innerHTML = `<a href="${url}"><img src="${url}"></a>` + return div } }, { @@ -190,8 +190,8 @@ function getEmbeddableLinkTypes() { className: 'picture compact', re: /\.gif(:[a-zA-Z]+)?(?:\?[\w&;?=]*)?$/i, makeNode: function(aNode, reResult, div) { - div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`; - return div; + div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>` + return div } }, { @@ -200,8 +200,8 @@ function getEmbeddableLinkTypes() { className: 'video compact', re: /\.(webm|mp4|m4v|ogv)(?:\?[\w&;?=]*)?$/i, makeNode: function(aNode, reResult, div) { - div.innerHTML = `<video src="${aNode.href}" title="${aNode.href}" controls></video>`; - return div; + div.innerHTML = `<video src="${aNode.href}" title="${aNode.href}" controls></video>` + return div } }, { @@ -210,8 +210,8 @@ function getEmbeddableLinkTypes() { className: 'audio singleColumn', re: /\.(mp3|ogg|weba|opus|m4a|oga|wav)(?:\?[\w&;?=]*)?$/i, makeNode: function(aNode, reResult, div) { - div.innerHTML = `<audio src="${aNode.href}" title="${aNode.href}" controls></audio>`; - return div; + div.innerHTML = `<audio src="${aNode.href}" title="${aNode.href}" controls></audio>` + return div } }, { @@ -220,37 +220,37 @@ function getEmbeddableLinkTypes() { className: 'youtube resizableV singleColumn', re: /^(?:https?:)?\/\/(?:www\.|m\.|gaming\.)?(?:youtu(?:(?:\.be\/|be\.com\/(?:v|embed)\/)([-\w]+)|be\.com\/watch)((?:(?:\?|&(?:amp;)?)(?:\w+=[-.\w]*[-\w]))*)|youtube\.com\/playlist\?list=([-\w]*)(&(amp;)?[-\w?=]*)?)/i, makeNode: function(aNode, reResult, div) { - let [, v, args, plist] = reResult; - let iframeUrl; + let [, v, args, plist] = reResult + let iframeUrl if (plist) { - iframeUrl = '//www.youtube-nocookie.com/embed/videoseries?list=' + plist; + iframeUrl = '//www.youtube-nocookie.com/embed/videoseries?list=' + plist } else { let pp = {}; args.replace(/^\?/, '') .split('&') .map(s => s.split('=')) - .forEach(z => pp[z[0]] = z[1]); + .forEach(z => pp[z[0]] = z[1]) let embedArgs = { rel: '0', enablejsapi: '1', origin: `${window.location.protocol}//${window.location.host}` - }; + } if (pp.t) { - const tre = /^(?:(\d+)|(?:(\d+)h)?(?:(\d+)m)?(\d+)s|(?:(\d+)h)?(\d+)m|(\d+)h)$/i; - let [, t, h, m, s, h1, m1, h2] = tre.exec(pp.t); - embedArgs['start'] = (+t) || ((+(h || h1 || h2 || 0)) * 60 * 60 + (+(m || m1 || 0)) * 60 + (+(s || 0))); + const tre = /^(?:(\d+)|(?:(\d+)h)?(?:(\d+)m)?(\d+)s|(?:(\d+)h)?(\d+)m|(\d+)h)$/i + let [, t, h, m, s, h1, m1, h2] = tre.exec(pp.t) + embedArgs['start'] = (+t) || ((+(h || h1 || h2 || 0)) * 60 * 60 + (+(m || m1 || 0)) * 60 + (+(s || 0))) } if (pp.list) { - embedArgs['list'] = pp.list; + embedArgs['list'] = pp.list } - v = v || pp.v; + v = v || pp.v let argsStr = Object.keys(embedArgs) .map(k => `${k}=${embedArgs[k]}`) - .join('&'); - iframeUrl = `//www.youtube-nocookie.com/embed/${v}?${argsStr}`; + .join('&') + iframeUrl = `//www.youtube-nocookie.com/embed/${v}?${argsStr}` } - let iframe = makeIframe(iframeUrl, '100%', '360px'); - iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); - return setContent(div, iframe); + let iframe = makeIframe(iframeUrl, '100%', '360px') + iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0) + return setContent(div, iframe) } }, { @@ -259,9 +259,9 @@ function getEmbeddableLinkTypes() { className: 'vimeo resizableV', re: /^(?:https?:)?\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(?:video\/|album\/[\d]+\/video\/)?([\d]+)/i, makeNode: function(aNode, reResult, div) { - let iframe = makeIframe('//player.vimeo.com/video/' + reResult[1], '100%', '360px'); - iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); - return setContent(div, iframe); + let iframe = makeIframe('//player.vimeo.com/video/' + reResult[1], '100%', '360px') + iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0) + return setContent(div, iframe) } }, { @@ -273,9 +273,9 @@ function getEmbeddableLinkTypes() { fetch('https://beta.juick.com/api/oembed?url=' + reResult[0]) .then(response => response.json()) .then(json => { - div.innerHTML = json.html; - }).catch(console.log); - return div; + div.innerHTML = json.html + }).catch(console.log) + return div } }, { @@ -284,10 +284,10 @@ function getEmbeddableLinkTypes() { className: 'picture compact', re: /https?:\/\/www\.?instagram\.com(\/p\/\w+)\/?/i, makeNode: function(aNode, reResult, div) { - let [, postId] = reResult; - let mediaUrl = `https://instagr.am${postId}/media`; - div.innerHTML = `<a href="${aNode.href}"><img src="${mediaUrl}"></a>`; - return div; + let [, postId] = reResult + let mediaUrl = `https://instagr.am${postId}/media` + div.innerHTML = `<a href="${aNode.href}"><img src="${mediaUrl}"></a>` + return div } }, { @@ -296,19 +296,19 @@ function getEmbeddableLinkTypes() { className: 'tg compact', re: /https?:\/\/t\.me\/(\S+)/i, makeNode: function(aNode, reResult, div) { - let [, post] = reResult; + let [, post] = reResult // innerHTML cannot insert scripts, so... - let script = document.createElement('script'); - script.src = 'https://telegram.org/js/telegram-widget.js?18'; - script.setAttribute('data-telegram-post', post); - script.setAttribute('data-tme-mode', 'data-tme-mode'); - script.setAttribute('data-width', '100%'); - script.charset = 'utf-8'; - div.appendChild(script); - return div; + let script = document.createElement('script') + script.src = 'https://telegram.org/js/telegram-widget.js?18' + script.setAttribute('data-telegram-post', post) + script.setAttribute('data-tme-mode', 'data-tme-mode') + script.setAttribute('data-width', '100%') + script.charset = 'utf-8' + div.appendChild(script) + return div } }, - ]; + ] } /** @@ -320,38 +320,38 @@ function getEmbeddableLinkTypes() { * @returns { boolean } `true` when some link was embedded */ function embedLink(aNode, linkTypes, container, afterNode = false) { - let anyEmbed = false; - let linkId = (aNode.href.replace(/^https?:/i, '').replace(/'/gi, '')); - let sameEmbed = container.querySelector(`*[data-linkid='${linkId}']`); // do not embed the same thing twice + let anyEmbed = false + let linkId = (aNode.href.replace(/^https?:/i, '').replace(/'/gi, '')) + let sameEmbed = container.querySelector(`*[data-linkid='${linkId}']`) // do not embed the same thing twice if (!sameEmbed) { anyEmbed = linkTypes.some((linkType) => { - let reResult = linkType.re.exec(aNode.href); + let reResult = linkType.re.exec(aNode.href) if (reResult) { - if (linkType.match && (linkType.match(aNode, reResult) === false)) { return false; } - let newNode = makeNewNode(linkType, aNode, reResult); - if (!newNode) { return false; } - newNode.setAttribute('data-linkid', linkId); + if (linkType.match && (linkType.match(aNode, reResult) === false)) { return false } + let newNode = makeNewNode(linkType, aNode, reResult) + if (!newNode) { return false } + newNode.setAttribute('data-linkid', linkId) if (afterNode) { - insertAfter(newNode, afterNode); + insertAfter(newNode, afterNode) } else { - container.appendChild(newNode); + container.appendChild(newNode) } - aNode.classList.add('embedLink'); - return true; + aNode.classList.add('embedLink') + return true } - }); + }) } - return anyEmbed; + return anyEmbed } function embedLinks(aNodes, container) { - let anyEmbed = false; - let embeddableLinkTypes = getEmbeddableLinkTypes(); + let anyEmbed = false + let embeddableLinkTypes = getEmbeddableLinkTypes() Array.from(aNodes).forEach(aNode => { - let isEmbedded = embedLink(aNode, embeddableLinkTypes, container); - anyEmbed = anyEmbed || isEmbedded; - }); - return anyEmbed; + let isEmbedded = embedLink(aNode, embeddableLinkTypes, container) + anyEmbed = anyEmbed || isEmbedded + }) + return anyEmbed } /** @@ -364,19 +364,19 @@ function embedLinks(aNodes, container) { * @param {string} allLinksSelector */ export function embedLinksToX(x, beforeNodeSelector, allLinksSelector) { - let allLinks = x.querySelectorAll(allLinksSelector); + let allLinks = x.querySelectorAll(allLinksSelector) - let existingContainer = x.querySelector('div.embedContainer'); + let existingContainer = x.querySelector('div.embedContainer') if (existingContainer) { - embedLinks(allLinks, existingContainer); + embedLinks(allLinks, existingContainer) } else { - let embedContainer = document.createElement('div'); - embedContainer.className = 'embedContainer'; + let embedContainer = document.createElement('div') + embedContainer.className = 'embedContainer' - let anyEmbed = embedLinks(allLinks, embedContainer); + let anyEmbed = embedLinks(allLinks, embedContainer) if (anyEmbed) { - let beforeNode = x.querySelector(beforeNodeSelector); - x.insertBefore(embedContainer, beforeNode); + let beforeNode = x.querySelector(beforeNodeSelector) + x.insertBefore(embedContainer, beforeNode) } } } @@ -385,11 +385,11 @@ export function embedLinksToX(x, beforeNodeSelector, allLinksSelector) { * Embed all the links in all messages/replies on the page. */ export function embedAll() { - let beforeNodeSelector = '.msg-txt + *'; - let allLinksSelector = '.msg-txt a'; + let beforeNodeSelector = '.msg-txt + *' + let allLinksSelector = '.msg-txt a' Array.from(document.querySelectorAll('#content .msg-cont')).forEach(msg => { - embedLinksToX(msg, beforeNodeSelector, allLinksSelector); - }); + embedLinksToX(msg, beforeNodeSelector, allLinksSelector) + }) } /** * Embed URLs to container @@ -397,8 +397,8 @@ export function embedAll() { * @param {HTMLDivElement} embedContainer */ export function embedUrls(urls, embedContainer) { - embedLinks(urls, embedContainer); + embedLinks(urls, embedContainer) } -export const format = juickFormat; +export const format = juickFormat |