Last active
October 16, 2020 08:47
-
-
Save M1nhNV/c7413906ec98bcbe34a759d9c5e32898 to your computer and use it in GitHub Desktop.
Open Graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import openGraph from './open-graph' | |
async addOpenGraphInfoToContent (content) { | |
return await openGraph(content) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author @phong.nhh | |
*/ | |
var ready = callback => { | |
if (document.readyState != "loading") callback(); | |
else document.addEventListener("DOMContentLoaded", callback); | |
}; | |
ready(() => { | |
var allATags = document.querySelectorAll('a') | |
for (let i = 0; i < allATags.length; i++) { | |
const aTag = allATags[i]; | |
aTag.setAttribute('id', 'aTag' + i); | |
aTag.setAttribute('class', 'open-graph'); | |
const url = aTag.getAttribute('href') | |
getContentRaw(apiUrl + url).then(data => { | |
if (data && data.data) { | |
const info = data.data; | |
const isEmptyData = Object.keys(info).findIndex(key => key !== 'image' && info[key] === '') !== -1 | |
if (!isEmptyData) { | |
aTag.innerHTML = ` | |
<span class="left"> | |
<span class="title">${info.title}</span> | |
<span class="description">${info.description}</span> | |
<span class="fa fa-link">${parseDomainName(info.url)}</span> | |
</span> | |
<img src="${info.image || thumbnailErrorUrl}" alt="${info.title}"> | |
` | |
} | |
} | |
}) | |
} | |
}); | |
function parseDomainName(url) { | |
const urlRegex = /^(?:https?:\/\/)?(?:www\.)?([^/]+)/; | |
const siteNameMatch = url.match(urlRegex); | |
if (siteNameMatch !== null && siteNameMatch[1] !== '') { | |
return siteNameMatch[1]; | |
} | |
return url; | |
} | |
async function getContentRaw(url) { | |
const options = { | |
headers: new Headers({ | |
'content-type': 'application/json', | |
'Accept': 'application/json' | |
}) | |
}; | |
const response = await fetch(url, options); | |
return response.json() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
* @author @phong.nhh | |
* Example for mobile web browser | |
--> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Document</title> | |
<script> | |
var apiUrl = 'https://api.dev.k250.bla-one.net/v1/data/storage/content?path=' | |
var thumbnailErrorUrl = 'img.jpg' | |
</script> | |
<script src="index.js"></script> | |
</head> | |
<body> | |
<p><a target="_blank" href="https://www.24h.com.vn/tin-tuc-trong-ngay/ben-xe-lon-nhat-nuoc-dep-nhu-san-bay-quoc-te-va-hinh-anh-sau-khai-truong-c46a1190355.html">https://www.24h.com.vn/tin-tuc-trong-ngay/ben-xe-lon-nhat-nuoc-dep-nhu-san-bay-quoc-te-va-hinh-anh-sau-khai-truong-c46a1190355.html</a></p><p><a target="_blank" href="https://nld.com.vn/thoi-su/nhieu-ky-vong-vao-dai-hoi-dang-bo-tp-hcm-nhiem-ky-2020-2025-20201013230535416.htm">https://nld.com.vn/thoi-su/nhieu-ky-vong-vao-dai-hoi-dang-bo-tp-hcm-nhiem-ky-2020-2025-20201013230535416.htm</a></p><p><a target="_blank" href="https://www.instagram.com/giant_japan/">https://www.instagram.com/giant_japan/</a></p><p><a target="_blank" href="https://www3.nhk.or.jp/news/html/20201014/k10012662481000.html?utm_int=error_contents_news-main_003">https://www3.nhk.or.jp/news/html/20201014/k10012662481000.html?utm_int=error_contents_news-main_003</a><a target="_blank" href="https://www.olympicchannel.com/ja/stories/features/detail/%E6%9C%80%E5%A4%A7%E6%99%82%E9%80%9F70km-h%E3%82%92%E8%B6%85%E3%81%88%E3%82%8B%E8%87%AA%E8%BB%A2%E8%BB%8A%E3%83%88%E3%83%A9%E3%83%83%E3%82%AF%E7%AB%B6%E6%8A%80-%E5%80%8B%E4%BA%BA%E3%81%8B%E3%82%89%E3%83%81-%E3%83%A0%E3%81%AB%E3%82%88%E3%82%8B%E7%99%BD%E7%86%B1%E3%81%AE6%E7%A8%AE%E7%9B%AE%E3%82%92%E8%A7%A3%E8%AA%AC/">https://www.olympicchannel.com/ja/stories/features/detail/%E6%9C%80%E5%A4%A7%E6%99%82%E9%80%9F70km-h%E3%82%92%E8%B6%85%E3%81%88%E3%82%8B%E8%87%AA%E8%BB%A2%E8%BB%8A%E3%83%88%E3%83%A9%E3%83%83%E3%82%AF%E7%AB%B6%E6%8A%80-%E5%80%8B%E4%BA%BA%E3%81%8B%E3%82%89%E3%83%81-%E3%83%A0%E3%81%AB%E3%82%88%E3%82%8B%E7%99%BD%E7%86%B1%E3%81%AE6%E7%A8%AE%E7%9B%AE%E3%82%92%E8%A7%A3%E8%AA%AC/</a></p><p><a target="_blank" href="https://www3.nhk.or.jp/news/html/20201013/k10012661771000.html?utm_int=all_side_ranking-social_004">https://www3.nhk.or.jp/news/html/20201013/k10012661771000.html?utm_int=all_side_ranking-social_004</a></p> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.open-graph { | |
display: flex; | |
width: 100%; | |
max-width: 600px; | |
padding: 10px; | |
border: 1px solid #ddd; | |
border-radius: 7px; | |
cursor: pointer; | |
margin-bottom: 10px; | |
margin-top: 10px; | |
text-decoration: none; | |
box-sizing: border-box; | |
} | |
.open-graph img { | |
padding-left: 10px; | |
margin-bottom: 0; | |
text-align: right; | |
height: 80px; | |
} | |
.open-graph .left { | |
width: 60%; | |
color: #999; | |
flex: 1; | |
display: flex; | |
flex-direction: column; | |
} | |
.open-graph .left .title { | |
color: #666; | |
display: block; | |
font-weight: bold; | |
margin-bottom: 10px; | |
height: 17px; | |
font-size: 14px; | |
white-space: pre; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
.open-graph .left .description { | |
margin-bottom: 10px; | |
display: block; | |
height: 35px; | |
font-size: 13px; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
.open-graph .left .fa { | |
display: block; | |
font-weight: bold; | |
color: #666; | |
margin-top: auto; | |
} | |
.open-graph .left .fa:before { | |
margin-right: 7px; | |
} | |
@media (max-width: 767px) { | |
.open-graph img { | |
height: 60px; | |
} | |
.open-graph .left .title { | |
font-size: 12px; | |
} | |
.open-graph .left .description { | |
font-size: 11px; | |
height: 17px; | |
white-space: pre; | |
} | |
.open-graph .left .fa { | |
font-size: 11px; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Using Axios call api get data | |
// global object | |
const result = {} | |
import thumbnailErrorUrl from '@/assets/images/thumbnail-error.png' | |
import AxiosClient from '@/utils/axios'; | |
export default async function openGraph (bodyString, callBack) { | |
const data = await detectATag(bodyString).then( | |
newContent => { | |
return newContent | |
} | |
) | |
return callBack(data) | |
} | |
function detectURL (string) { | |
const regexUrl = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/s | |
const match = string.match(regexUrl) | |
return match !== null && match[0] !== null ? match[0] : '' | |
} | |
async function detectATag (content) { | |
const aTagRegex = /<\s*a[^>]*>(.*?)<\s*\/\s*a>/g | |
const resultATag = content.match(aTagRegex) | |
if (resultATag === null) { | |
return content | |
} | |
try { | |
let newContent = String(content) | |
const mappingUrlAndATag = {} | |
for (const i in resultATag) { | |
// get urls | |
const url = detectURL(resultATag[i]) | |
mappingUrlAndATag[url] = resultATag[i] | |
// check has result | |
if (result.hasOwnProperty(url)) { | |
newContent = makeHtml(newContent, url, mappingUrlAndATag) | |
continue; | |
} | |
// call api | |
await AxiosClient.get('data/storage/content?path='+ url).then(res => { | |
if (res.data && res.data.data ) { | |
result[url] = res.data.data | |
newContent = makeHtml(newContent, url, mappingUrlAndATag) | |
} else { | |
result[url] = '' | |
} | |
}).catch(e => '') | |
} | |
return newContent | |
} catch (e) { | |
return content | |
} | |
} | |
function makeHtml (newContent, url, mappingUrlAndATag) { | |
if (result[url] && result[url].title !== '' && result[url].url !== '' && result[url].description !== '' && result[url].site_name !== '') { | |
const html = generateHTML(url, result[url]) | |
for (const i in mappingUrlAndATag) { | |
if (i === url) { | |
newContent = newContent.replace(mappingUrlAndATag[url], html) | |
} | |
} | |
} | |
return newContent | |
} | |
function generateHTML (url, info) { | |
return `<a href="${url}" target="_blank" class="open-graph"> | |
<span class="left"> | |
<span class="title">${info.title}</span> | |
<span class="description">${info.description}</span> | |
<span class="fa fa-link">${parseDomainName(info.url)}</span> | |
</span> | |
<img src="${ info.image === '' ? thumbnailErrorUrl : info.image }" alt="${info.title}"> | |
</a>` | |
} | |
function parseDomainName (url) { | |
const urlRegex = /^(?:https?:\/\/)?(?:www\.)?([^/]+)/ | |
const siteNameMatch = url.match(urlRegex) | |
if (siteNameMatch !== null && siteNameMatch[1] !== '') { | |
return siteNameMatch[1] | |
} | |
return url | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// global object | |
const result = {} | |
export default async function openGraph (bodyString) { | |
return await detectATag(bodyString).then( | |
newContent => { | |
return newContent | |
} | |
) | |
} | |
function detectURL (string) { | |
const regexUrl = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g | |
const result = string.match(regexUrl) | |
const urls = [] | |
for (const i in result) { | |
if (!urls.includes(result[i])) { | |
urls.push(result[i]) | |
} | |
} | |
return urls | |
} | |
function checkHasResult (url) { | |
return result && result.hasOwnProperty(url) | |
} | |
function saveToResult (url, info) { | |
result[url] = info | |
} | |
async function detectATag (content) { | |
const aTagRegex = /<\s*a[^>]*>(.*?)<\s*\/\s*a>/g | |
const resultATag = content.match(aTagRegex) | |
if (resultATag === null) { | |
return content | |
} | |
try { | |
let newContent = String(content) | |
const htmlContentRaws = {} | |
const promiseContents = {} | |
const mappingUrlAndATag = {} | |
for (const i in resultATag) { | |
// get urls | |
const url = detectURL(resultATag[i]) | |
mappingUrlAndATag[url] = resultATag[i] | |
promiseContents[url] = getContentRaw(url).then ( res => { | |
return res | |
}).catch(e => '') | |
} | |
for (const i in promiseContents) { | |
htmlContentRaws[i] = await promiseContents[i] | |
} | |
//get info | |
for (const i in htmlContentRaws) { | |
if (htmlContentRaws[i] !== '') { | |
//get og html string | |
const ogTags = parseOgTag(htmlContentRaws[i]) | |
// get open graph info | |
const openGraphInfo = parseInfo(ogTags) | |
// save to result | |
saveToResult(i, openGraphInfo) | |
// generate html | |
const html = generateHTML(i, openGraphInfo) | |
for (const url in mappingUrlAndATag) { | |
if (url === i) { | |
newContent = newContent.replace(mappingUrlAndATag[i], html) | |
} | |
} | |
} | |
} | |
return newContent | |
} catch (e) { | |
return content | |
} | |
} | |
async function getContentRaw (url) { | |
const response = await fetch(url) | |
return await _getTextFromStream(response.body) | |
} | |
function parseOgTag (rawHtml) { | |
const re = /<meta\sproperty="og:.+\/>/gm; | |
return rawHtml.match(re) | |
} | |
function parseInfo (tags) { | |
const info = { | |
title: '', | |
site_name: '', | |
url: '', | |
image: '', | |
description: '' | |
} | |
for (const i in tags) { | |
const regContent = /<meta\sproperty="og:([a-z_]+)".+content="(.+)+"\s\/>/; | |
const match = tags[i].match(regContent) | |
if (match !== null && info.hasOwnProperty(match[1])) { | |
info[match[1]] = match[2] | |
const urlRegex = /^(?:https?:\/\/)?(?:www\.)?([^/]+)/ | |
const siteNameMatch = info.url.match(urlRegex) | |
if (siteNameMatch !== null && siteNameMatch[1] !== '') { | |
info.site_name = siteNameMatch[1] | |
} | |
} | |
} | |
return info | |
} | |
function generateHTML (url, info) { | |
return `<a href="${url}" target="_blank" class="open-graph"> | |
<span class="left"> | |
<span class="title">${info.title}</span> | |
<span class="description">${info.description}</span> | |
<span class="fa fa-link">${info.site_name}</span> | |
</span> | |
<img src="${info.image}" alt="${info.title}"> | |
</a>` | |
} | |
async function _getTextFromStream (readableStream) { | |
const reader = readableStream.getReader() | |
const utf8Decoder = new TextDecoder() | |
let nextChunk | |
let resultStr = '' | |
while (!(nextChunk = await reader.read()).done) { | |
const partialData = nextChunk.value | |
resultStr += utf8Decoder.decode(partialData) | |
} | |
return resultStr | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment