'use strict'; const urlFn = require('url'); const moment = require('moment'); const util = require('hexo-util'); const htmlTag = util.htmlTag; const stripHTML = util.stripHTML; const escapeHTML = util.escapeHTML; 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 page = this.page; const config = this.config; const content = page.content; 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('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(); } module.exports = openGraphHelper;