From 7ebcc03977a59fe32d188e4972e67f6504bbfc01 Mon Sep 17 00:00:00 2001 From: curben Date: Sat, 25 May 2019 22:21:44 +0930 Subject: [PATCH] fix(open_graph): use data-src attribute in img tag * compatibility with cloudinary (see scripts/cloudinary.js) * helper has to be registered via a function - https://github.com/hexojs/hexo/issues/1462#issuecomment-277474592 - https://github.com/hexojs/hexo/issues/743#issuecomment-168262852 - https://hexo.io/api/helper - https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/open_graph.js --- themes/typing/layout/_partial/head.ejs | 3 +- .../typing/scripts/open_graph_cloudinary.js | 153 ++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 themes/typing/scripts/open_graph_cloudinary.js diff --git a/themes/typing/layout/_partial/head.ejs b/themes/typing/layout/_partial/head.ejs index ae6c352..192b3b3 100644 --- a/themes/typing/layout/_partial/head.ejs +++ b/themes/typing/layout/_partial/head.ejs @@ -24,7 +24,8 @@ <% if (title) { %><%= title %> | <% } %><%= config.title %> <%/* Add Open Graph meta tags for easier sharing on social networking sites */%> - <%- open_graph() %> + <%/* Modified for cloudinary compatibility */%> + <%- open_graph_cloudinary() %> <% if (theme.rss) { %> <% } %> diff --git a/themes/typing/scripts/open_graph_cloudinary.js b/themes/typing/scripts/open_graph_cloudinary.js new file mode 100644 index 0000000..fc23316 --- /dev/null +++ b/themes/typing/scripts/open_graph_cloudinary.js @@ -0,0 +1,153 @@ +'use strict' + +const urlFn = require('url') +const moment = require('moment') +const { escapeHTML, htmlTag, stripHTML } = require('hexo-util') +let cheerio + +function meta (name, content, escape) { + if (escape !== false && typeof content === 'string') { + content = escapeHTML(content) + } + + return `${htmlTag('meta', { + name, + content + })}\n` +} + +function og (name, content, escape) { + if (escape !== false && typeof content === 'string') { + content = escapeHTML(content) + } + + return `${htmlTag('meta', { + property: name, + content + })}\n` +} + +function openGraphHelper (options = {}) { + if (!cheerio) cheerio = require('cheerio') + + const { config, page } = this + const { content } = page + let images = options.image || options.images || page.photos || [] + let description = options.description || page.description || page.excerpt || content || config.description + const keywords = page.keywords || (page.tags && page.tags.length ? page.tags : undefined) || config.keywords + const title = options.title || page.title || config.title + const type = options.type || (this.is_post() ? 'article' : 'website') + const url = options.url || this.url + const siteName = options.site_name || config.title + const twitterCard = options.twitter_card || 'summary' + const updated = options.updated !== false ? options.updated || page.updated : false + const language = options.language || page.lang || page.language || config.language + let result = '' + + if (!Array.isArray(images)) images = [images] + + if (description) { + description = stripHTML(description).substring(0, 200) + .trim() // Remove prefixing/trailing spaces + .replace(//g, '>') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/\n/g, ' ') // Replace new lines by spaces + } + + if (!images.length && content) { + images = images.slice() + + const $ = cheerio.load(content) + + $('img').each(function () { + const src = $(this).attr('data-src') + if (src) images.push(src) + }) + } + + if (description) { + result += meta('description', description, false) + } + + if (keywords) { + if (typeof keywords === 'string') { + result += meta('keywords', keywords) + } else if (keywords.length) { + result += meta('keywords', keywords.map(tag => { + return tag.name ? tag.name : tag + }).filter(keyword => !!keyword).join()) + } + } + + result += og('og:type', type) + result += og('og:title', title) + result += og('og:url', url, false) + result += og('og:site_name', siteName) + if (description) { + result += og('og:description', description, false) + } + + if (language) { + result += og('og:locale', language, false) + } + + images = images.map(path => { + if (!urlFn.parse(path).host) { + // resolve `path`'s absolute path relative to current page's url + // `path` can be both absolute (starts with `/`) or relative. + return urlFn.resolve(url || config.url, path) + } + + return path + }) + + images.forEach(path => { + result += og('og:image', path, false) + }) + + if (updated) { + if ((moment.isMoment(updated) || moment.isDate(updated)) && !isNaN(updated.valueOf())) { + result += og('og:updated_time', updated.toISOString()) + } + } + + result += meta('twitter:card', twitterCard) + result += meta('twitter:title', title) + if (description) { + result += meta('twitter:description', description, false) + } + + if (images.length) { + result += meta('twitter:image', images[0], false) + } + + if (options.twitter_id) { + let twitterId = options.twitter_id + if (twitterId[0] !== '@') twitterId = `@${twitterId}` + + result += meta('twitter:creator', twitterId) + } + + if (options.twitter_site) { + result += meta('twitter:site', options.twitter_site, false) + } + + if (options.google_plus) { + result += `${htmlTag('link', { rel: 'publisher', href: options.google_plus })}\n` + } + + if (options.fb_admins) { + result += og('fb:admins', options.fb_admins) + } + + if (options.fb_app_id) { + result += og('fb:app_id', options.fb_app_id) + } + + return result.trim() +} + +hexo.extend.helper.register('open_graph_cloudinary', openGraphHelper)