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)