mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-17 23:44:39 -05:00
Add support for typographically-correct punctuation
Closes #1795 - Added typography.js Showdown extension - Updated RSS test to support new typographic quotes
This commit is contained in:
parent
ccaceb7310
commit
b49f10c33d
5 changed files with 121 additions and 4 deletions
|
@ -382,6 +382,7 @@ var path = require('path'),
|
||||||
'core/client/assets/vendor/codemirror/mode/gfm/gfm.js',
|
'core/client/assets/vendor/codemirror/mode/gfm/gfm.js',
|
||||||
'core/client/assets/vendor/showdown/showdown.js',
|
'core/client/assets/vendor/showdown/showdown.js',
|
||||||
'core/client/assets/vendor/showdown/extensions/ghostdown.js',
|
'core/client/assets/vendor/showdown/extensions/ghostdown.js',
|
||||||
|
'core/shared/vendor/showdown/extensions/typography.js',
|
||||||
'core/shared/vendor/showdown/extensions/github.js',
|
'core/shared/vendor/showdown/extensions/github.js',
|
||||||
'core/client/assets/vendor/shortcuts.js',
|
'core/client/assets/vendor/shortcuts.js',
|
||||||
'core/client/assets/vendor/validator-client.js',
|
'core/client/assets/vendor/validator-client.js',
|
||||||
|
@ -437,6 +438,7 @@ var path = require('path'),
|
||||||
'core/client/assets/vendor/codemirror/mode/gfm/gfm.js',
|
'core/client/assets/vendor/codemirror/mode/gfm/gfm.js',
|
||||||
'core/client/assets/vendor/showdown/showdown.js',
|
'core/client/assets/vendor/showdown/showdown.js',
|
||||||
'core/client/assets/vendor/showdown/extensions/ghostdown.js',
|
'core/client/assets/vendor/showdown/extensions/ghostdown.js',
|
||||||
|
'core/shared/vendor/showdown/extensions/typography.js',
|
||||||
'core/shared/vendor/showdown/extensions/github.js',
|
'core/shared/vendor/showdown/extensions/github.js',
|
||||||
'core/client/assets/vendor/shortcuts.js',
|
'core/client/assets/vendor/shortcuts.js',
|
||||||
'core/client/assets/vendor/validator-client.js',
|
'core/client/assets/vendor/validator-client.js',
|
||||||
|
|
|
@ -449,7 +449,7 @@
|
||||||
initMarkdown: function () {
|
initMarkdown: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.converter = new Showdown.converter({extensions: ['ghostdown', 'github']});
|
this.converter = new Showdown.converter({extensions: ['typography', 'ghostdown', 'github']});
|
||||||
this.editor = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
|
this.editor = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
|
||||||
mode: 'gfm',
|
mode: 'gfm',
|
||||||
tabMode: 'indent',
|
tabMode: 'indent',
|
||||||
|
|
|
@ -4,7 +4,8 @@ var _ = require('lodash'),
|
||||||
errors = require('../errorHandling'),
|
errors = require('../errorHandling'),
|
||||||
Showdown = require('showdown'),
|
Showdown = require('showdown'),
|
||||||
github = require('../../shared/vendor/showdown/extensions/github'),
|
github = require('../../shared/vendor/showdown/extensions/github'),
|
||||||
converter = new Showdown.converter({extensions: [github]}),
|
typography = require('../../shared/vendor/showdown/extensions/typography'),
|
||||||
|
converter = new Showdown.converter({extensions: [typography, github]}),
|
||||||
User = require('./user').User,
|
User = require('./user').User,
|
||||||
Tag = require('./tag').Tag,
|
Tag = require('./tag').Tag,
|
||||||
Tags = require('./tag').Tags,
|
Tags = require('./tag').Tags,
|
||||||
|
@ -453,4 +454,4 @@ Posts = ghostBookshelf.Collection.extend({
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Post: Post,
|
Post: Post,
|
||||||
Posts: Posts
|
Posts: Posts
|
||||||
};
|
};
|
||||||
|
|
114
core/shared/vendor/showdown/extensions/typography.js
vendored
Normal file
114
core/shared/vendor/showdown/extensions/typography.js
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*global module */
|
||||||
|
//
|
||||||
|
// Replaces straight quotes with curly ones, -- and --- with en dash and em
|
||||||
|
// dash respectively, and ... with horizontal ellipses.
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var typography = function () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: "lang",
|
||||||
|
filter: function (text) {
|
||||||
|
var fCodeblocks = {}, nCodeblocks = {}, iCodeblocks = {},
|
||||||
|
e = {
|
||||||
|
endash: '\u2009\u2013\u2009', // U+2009 = thin space
|
||||||
|
emdash: '\u2014',
|
||||||
|
lsquo: '\u2018',
|
||||||
|
rsquo: '\u2019',
|
||||||
|
ldquo: '\u201c',
|
||||||
|
rdquo: '\u201d',
|
||||||
|
hellip: '\u2026'
|
||||||
|
},
|
||||||
|
|
||||||
|
i;
|
||||||
|
|
||||||
|
// Extract fenced code blocks.
|
||||||
|
i = -1;
|
||||||
|
text = text.replace(/```((?:.|\n)+?)```/g,
|
||||||
|
function (match, code) {
|
||||||
|
i += 1;
|
||||||
|
fCodeblocks[i] = "```" + code + "```";
|
||||||
|
return "{typog-fcb-" + i + "}";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract indented code blocks.
|
||||||
|
i = -1;
|
||||||
|
text = text.replace(/((\n+([ ]{4}|\t).+)+)/g,
|
||||||
|
function (match, code) {
|
||||||
|
i += 1;
|
||||||
|
nCodeblocks[i] = " " + code;
|
||||||
|
return "{typog-ncb-" + i + "}";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract inline code blocks
|
||||||
|
i = -1;
|
||||||
|
text = text.replace(/`(.+)`/g, function (match, code) {
|
||||||
|
i += 1;
|
||||||
|
iCodeblocks[i] = "`" + code + "`";
|
||||||
|
return "{typog-icb-" + i + "}";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Perform typographic symbol replacement.
|
||||||
|
|
||||||
|
// Double quotes. There might be a reason this doesn't use
|
||||||
|
// the same \b matching style as the single quotes, but I
|
||||||
|
// can't remember what it is :(
|
||||||
|
text = text.
|
||||||
|
// Opening quotes
|
||||||
|
replace(/"([\w'])/g, e.ldquo + "$1").
|
||||||
|
// All the rest
|
||||||
|
replace(/"/g, e.rdquo);
|
||||||
|
|
||||||
|
// Single quotes/apostrophes
|
||||||
|
text = text.
|
||||||
|
// Apostrophes first
|
||||||
|
replace(/\b'\b/g, e.rsquo).
|
||||||
|
// Opening quotes
|
||||||
|
replace(/'\b/g, e.lsquo).
|
||||||
|
// All the rest
|
||||||
|
replace(/'/g, e.rsquo);
|
||||||
|
|
||||||
|
// Dashes
|
||||||
|
text = text.
|
||||||
|
// Don't replace lines containing only hyphens
|
||||||
|
replace(/^-+$/gm, "{typog-hr}").
|
||||||
|
replace(/---/g, e.emdash).
|
||||||
|
replace(/ -- /g, e.endash).
|
||||||
|
replace(/{typog-hr}/g, "----");
|
||||||
|
|
||||||
|
// Ellipses.
|
||||||
|
text = text.replace(/\.{3}/g, e.hellip);
|
||||||
|
|
||||||
|
|
||||||
|
// Restore fenced code blocks.
|
||||||
|
text = text.replace(/{typog-fcb-([0-9]+)}/g, function (x, y) {
|
||||||
|
return fCodeblocks[y];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore indented code blocks.
|
||||||
|
text = text.replace(/{typog-ncb-([0-9]+)}/g, function (x, y) {
|
||||||
|
return nCodeblocks[y];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore inline code blocks.
|
||||||
|
text = text.replace(/{typog-icb-([0-9]+)}/g, function (x, y) {
|
||||||
|
return iCodeblocks[y];
|
||||||
|
});
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Client-side export
|
||||||
|
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) {
|
||||||
|
window.Showdown.extensions.typography = typography;
|
||||||
|
}
|
||||||
|
// Server-side export
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = typography;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
|
@ -10,7 +10,7 @@ CasperTest.begin('Ensure that RSS is available', 11, function suite(test) {
|
||||||
siteDescription = '<description><![CDATA[Just a blogging platform.]]></description>',
|
siteDescription = '<description><![CDATA[Just a blogging platform.]]></description>',
|
||||||
siteUrl = '<link>http://127.0.0.1:2369/</link>',
|
siteUrl = '<link>http://127.0.0.1:2369/</link>',
|
||||||
postTitle = '<![CDATA[Welcome to Ghost]]>',
|
postTitle = '<![CDATA[Welcome to Ghost]]>',
|
||||||
postStart = '<description><![CDATA[<p>You\'re live!',
|
postStart = '<description><![CDATA[<p>You’re live!',
|
||||||
postEnd = 'you think :)</p>]]></description>',
|
postEnd = 'you think :)</p>]]></description>',
|
||||||
postLink = '<link>http://127.0.0.1:2369/welcome-to-ghost/</link>',
|
postLink = '<link>http://127.0.0.1:2369/welcome-to-ghost/</link>',
|
||||||
postCreator = '<dc:creator><![CDATA[Test User]]>';
|
postCreator = '<dc:creator><![CDATA[Test User]]>';
|
||||||
|
|
Loading…
Add table
Reference in a new issue