0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Improving the showdown extensions

fixes #2381

- renamed the ghost extensions
- added new html tests
This commit is contained in:
Hannah Wolfe 2014-03-20 13:52:16 +00:00
parent 3ad4c27968
commit c02fd70c63
8 changed files with 93 additions and 67 deletions

View file

@ -460,8 +460,8 @@ var path = require('path'),
'bower_components/showdown/src/showdown.js', 'bower_components/showdown/src/showdown.js',
'bower_components/validator-js/validator.js', 'bower_components/validator-js/validator.js',
'core/client/assets/lib/showdown/extensions/ghostdown.js', 'core/shared/lib/showdown/extensions/ghostimagepreview.js',
'core/shared/lib/showdown/extensions/github.js', 'core/shared/lib/showdown/extensions/ghostgfm.js',
// ToDo: Remove or replace // ToDo: Remove or replace
'core/client/assets/vendor/shortcuts.js', 'core/client/assets/vendor/shortcuts.js',
@ -522,8 +522,8 @@ var path = require('path'),
'bower_components/showdown/src/showdown.js', 'bower_components/showdown/src/showdown.js',
'bower_components/validator-js/validator.js', 'bower_components/validator-js/validator.js',
'core/client/assets/lib/showdown/extensions/ghostdown.js', 'core/shared/lib/showdown/extensions/ghostimagepreview.js',
'core/shared/lib/showdown/extensions/github.js', 'core/shared/lib/showdown/extensions/ghostgfm.js',
// ToDo: Remove or replace // ToDo: Remove or replace
'core/client/assets/vendor/shortcuts.js', 'core/client/assets/vendor/shortcuts.js',

View file

@ -9,7 +9,7 @@
'use strict'; 'use strict';
var HTMLPreview = function (markdown, uploadMgr) { var HTMLPreview = function (markdown, uploadMgr) {
var converter = new Showdown.converter({extensions: ['ghostdown', 'github']}), var converter = new Showdown.converter({extensions: ['ghostimagepreview', 'ghostgfm']}),
preview = document.getElementsByClassName('rendered-markdown')[0], preview = document.getElementsByClassName('rendered-markdown')[0],
update; update;

View file

@ -3,8 +3,8 @@ var _ = require('lodash'),
when = require('when'), when = require('when'),
errors = require('../errorHandling'), errors = require('../errorHandling'),
Showdown = require('showdown'), Showdown = require('showdown'),
github = require('../../shared/lib/showdown/extensions/github'), ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'),
converter = new Showdown.converter({extensions: [github]}), converter = new Showdown.converter({extensions: [ghostgfm]}),
User = require('./user').User, User = require('./user').User,
Tag = require('./tag').Tag, Tag = require('./tag').Tag,
Tags = require('./tag').Tags, Tags = require('./tag').Tags,

View file

@ -1,10 +1,16 @@
/* jshint node:true, browser:true */
// Ghost GFM
// Taken and extended from the Showdown Github Extension (WIP)
// Makes a number of pre and post-processing changes to the way markdown is handled
// //
// Github Extension (WIP) // ~~strike-through~~ -> <del>strike-through</del> (Pre)
// ~~strike-through~~ -> <del>strike-through</del> // GFM newlines & underscores (Pre)
// // 4 or more underscores (Pre)
// autolinking / custom image handling (Post)
(function () { (function () {
var github = function (converter) { var ghostgfm = function () {
return [ return [
{ {
// strike-through // strike-through
@ -73,6 +79,17 @@
return text; return text;
} }
}, },
// 4 or more inline underscores e.g. Ghost rocks my _____!
{
type: 'lang',
filter: function (text) {
return text.replace(/([^_\n\r])(_{4,})/g, function (match, prefix, underscores) {
return prefix + underscores.replace(/_/g, '&#95;');
});
}
},
{ {
// GFM autolinking & custom image handling, happens AFTER showdown // GFM autolinking & custom image handling, happens AFTER showdown
type : 'html', type : 'html',
@ -131,7 +148,11 @@
}; };
// Client-side export // Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.github = github; } if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) {
window.Showdown.extensions.ghostgfm = ghostgfm;
}
// Server-side export // Server-side export
if (typeof module !== 'undefined') module.exports = github; if (typeof module !== 'undefined') {
module.exports = ghostgfm;
}
}()); }());

View file

@ -1,7 +1,15 @@
/* jshint node:true, browser:true */ /* jshint node:true, browser:true */
// Ghost Image Preview
//
// Manages the conversion of image markdown `![]()` from markdown into the HTML image preview
// This provides a dropzone and other interface elements for adding images
// Is only used in the admin client.
var Ghost = Ghost || {}; var Ghost = Ghost || {};
(function () { (function () {
var ghostdown = function () { var ghostimagepreview = function () {
return [ return [
// ![] image syntax // ![] image syntax
{ {
@ -33,26 +41,16 @@ var Ghost = Ghost || {};
return output; return output;
}); });
} }
},
// 4 or more inline underscores e.g. Ghost rocks my _____!
{
type: 'lang',
filter: function (text) {
return text.replace(/([^_\n\r])(_{4,})/g, function (match, prefix, underscores) {
return prefix + underscores.replace(/_/g, '&#95;');
});
}
} }
]; ];
}; };
// Client-side export // Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) {
window.Showdown.extensions.ghostdown = ghostdown; window.Showdown.extensions.ghostimagepreview = ghostimagepreview;
} }
// Server-side export // Server-side export
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = ghostdown; module.exports = ghostimagepreview;
} }
}()); }());

View file

@ -5,14 +5,15 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('../utils'), var testUtils = require('../utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing
Showdown = require('showdown'), Showdown = require('showdown'),
github = require('../../shared/lib/showdown/extensions/github'), ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'),
ghostdown = require('../../client/assets/lib/showdown/extensions/ghostdown'), ghostimagepreview = require('../../shared/lib/showdown/extensions/ghostimagepreview'),
converter = new Showdown.converter({extensions: [ghostdown, github]});
converter = new Showdown.converter({extensions: [ghostimagepreview, ghostgfm]});
describe("Showdown client side converter", function () { describe("Showdown client side converter", function () {
/*jslint regexp: true */ /*jslint regexp: true */
@ -107,7 +108,7 @@ describe("Showdown client side converter", function () {
it("should turn newlines into br tags in simple cases", function () { it("should turn newlines into br tags in simple cases", function () {
var testPhrases = [ var testPhrases = [
{input: "fizz\nbuzz", output: /^<p>fizz <br \/>\nbuzz<\/p>$/}, {input: "fizz\nbuzz", output: /^<p>fizz <br \/>\nbuzz<\/p>$/},
{input: "Hello world\nIt's a fine day", output: /^<p>Hello world <br \/>\nIt\'s a fine day<\/p>$/}, {input: "Hello world\nIt is a fine day", output: /^<p>Hello world <br \/>\nIt is a fine day<\/p>$/},
{input: "\"first\nsecond", output: /^<p>\"first <br \/>\nsecond<\/p>$/}, {input: "\"first\nsecond", output: /^<p>\"first <br \/>\nsecond<\/p>$/},
{input: "\'first\nsecond", output: /^<p>\'first <br \/>\nsecond<\/p>$/} {input: "\'first\nsecond", output: /^<p>\'first <br \/>\nsecond<\/p>$/}
], ],
@ -122,7 +123,7 @@ describe("Showdown client side converter", function () {
it("should convert newlines in all groups", function () { it("should convert newlines in all groups", function () {
var testPhrases = [ var testPhrases = [
{input: "ruby\npython\nerlang", output: /^<p>ruby <br \/>\npython <br \/>\nerlang<\/p>$/}, {input: "ruby\npython\nerlang", output: /^<p>ruby <br \/>\npython <br \/>\nerlang<\/p>$/},
{input: "Hello world\nIt's a fine day\nout", output: /^<p>Hello world <br \/>\nIt\'s a fine day <br \/>\nout<\/p>$/} {input: "Hello world\nIt is a fine day\nout", output: /^<p>Hello world <br \/>\nIt is a fine day <br \/>\nout<\/p>$/}
], ],
processedMarkup; processedMarkup;
@ -134,10 +135,13 @@ describe("Showdown client side converter", function () {
it("should convert newlines in even long groups", function () { it("should convert newlines in even long groups", function () {
var testPhrases = [ var testPhrases = [
{input: "ruby\npython\nerlang\ngo", output: /^<p>ruby <br \/>\npython <br \/>\nerlang <br \/>\ngo<\/p>$/},
{ {
input: "Hello world\nIt's a fine day\noutside\nthe window", input: "ruby\npython\nerlang\ngo",
output: /^<p>Hello world <br \/>\nIt\'s a fine day <br \/>\noutside <br \/>\nthe window<\/p>$/ output: /^<p>ruby <br \/>\npython <br \/>\nerlang <br \/>\ngo<\/p>$/
},
{
input: "Hello world\nIt is a fine day\noutside\nthe window",
output: /^<p>Hello world <br \/>\nIt is a fine day <br \/>\noutside <br \/>\nthe window<\/p>$/
} }
], ],
processedMarkup; processedMarkup;
@ -150,8 +154,14 @@ describe("Showdown client side converter", function () {
it("should not convert newlines in lists", function () { it("should not convert newlines in lists", function () {
var testPhrases = [ var testPhrases = [
{input: "#fizz\n# buzz\n### baz", output: /^<h1 id="fizz">fizz<\/h1>\n\n<h1 id="buzz">buzz<\/h1>\n\n<h3 id="baz">baz<\/h3>$/}, {
{input: "* foo\n* bar", output: /^<ul>\n<li>foo<\/li>\n<li>bar<\/li>\n<\/ul>$/} input: "#fizz\n# buzz\n### baz",
output: /^<h1 id="fizz">fizz<\/h1>\n\n<h1 id="buzz">buzz<\/h1>\n\n<h3 id="baz">baz<\/h3>$/
},
{
input: "* foo\n* bar",
output: /^<ul>\n<li>foo<\/li>\n<li>bar<\/li>\n<\/ul>$/
}
], ],
processedMarkup; processedMarkup;

View file

@ -4,12 +4,11 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('../utils'), var testUtils = require('../utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing
ghPath = "../../shared/lib/showdown/extensions/github.js", ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm');
github = require(ghPath);
function _ExecuteExtension(ext, text) { function _ExecuteExtension(ext, text) {
if (ext.regex) { if (ext.regex) {
@ -21,20 +20,20 @@ function _ExecuteExtension(ext, text) {
} }
function _ConvertPhrase(testPhrase) { function _ConvertPhrase(testPhrase) {
return github().reduce(function (text, ext) { return ghostgfm().reduce(function (text, ext) {
return _ExecuteExtension(ext, text); return _ExecuteExtension(ext, text);
}, testPhrase); }, testPhrase);
} }
describe("Github showdown extensions", function () { describe("Ghost GFM showdown extension", function () {
/*jslint regexp: true */ /*jslint regexp: true */
it("should export an array of methods for processing", function () { it("should export an array of methods for processing", function () {
github.should.be.a.function; ghostgfm.should.be.a.function;
github().should.be.an.Array; ghostgfm().should.be.an.Array;
github().forEach(function (processor) { ghostgfm().forEach(function (processor) {
processor.should.be.an.Object; processor.should.be.an.Object;
processor.should.have.property("type"); processor.should.have.property("type");
processor.type.should.be.a.String; processor.type.should.be.a.String;
@ -50,6 +49,14 @@ describe("Github showdown extensions", function () {
processedMarkup.should.match(testPhrase.output); processedMarkup.should.match(testPhrase.output);
}); });
it("should allow 4 underscores", function () {
var testPhrase = {input: 'Ghost ____', output: /Ghost\s(?:&#95;){4}$/},
processedMarkup = _ConvertPhrase(testPhrase.input);
processedMarkup.should.match(testPhrase.output);
});
it("should auto-link URL in text with markdown syntax", function () { it("should auto-link URL in text with markdown syntax", function () {
var testPhrases = [ var testPhrases = [
{ {

View file

@ -6,21 +6,20 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('../utils'), var testUtils = require('../utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing
gdPath = "../../client/assets/lib/showdown/extensions/ghostdown.js", ghostimagepreview = require('../../shared/lib/showdown/extensions/ghostimagepreview');
ghostdown = require(gdPath);
describe("Ghostdown showdown extensions", function () { describe("Ghost Image Preview showdown extension", function () {
it("should export an array of methods for processing", function () { it("should export an array of methods for processing", function () {
ghostdown.should.be.a.function; ghostimagepreview.should.be.a.function;
ghostdown().should.be.an.instanceof(Array); ghostimagepreview().should.be.an.instanceof(Array);
ghostdown().forEach(function (processor) { ghostimagepreview().forEach(function (processor) {
processor.should.be.an.Object; processor.should.be.an.Object;
processor.should.have.property("type"); processor.should.have.property("type");
processor.should.have.property("filter"); processor.should.have.property("filter");
@ -46,7 +45,7 @@ describe("Ghostdown showdown extensions", function () {
] ]
.forEach(function (imageMarkup) { .forEach(function (imageMarkup) {
var processedMarkup = var processedMarkup =
ghostdown().reduce(function (prev, processor) { ghostimagepreview().reduce(function (prev, processor) {
return processor.filter(prev); return processor.filter(prev);
}, imageMarkup); }, imageMarkup);
@ -55,15 +54,6 @@ describe("Ghostdown showdown extensions", function () {
}); });
}); });
it("should allow 4 underscores", function () {
var processedMarkup =
ghostdown().reduce(function (prev, processor) {
return processor.filter(prev);
}, "Ghost ____");
processedMarkup.should.match(/Ghost\s(?:&#95;){4}$/);
});
it("should correctly include an image", function () { it("should correctly include an image", function () {
[ [
"![image and another,/ image](http://dsurl.stuff)", "![image and another,/ image](http://dsurl.stuff)",
@ -75,7 +65,7 @@ describe("Ghostdown showdown extensions", function () {
] ]
.forEach(function (imageMarkup) { .forEach(function (imageMarkup) {
var processedMarkup = var processedMarkup =
ghostdown().reduce(function (prev, processor) { ghostimagepreview().reduce(function (prev, processor) {
return processor.filter(prev); return processor.filter(prev);
}, imageMarkup); }, imageMarkup);