diff --git a/core/client/Brocfile.js b/core/client/Brocfile.js index 1b71645cb2..410dbed13e 100644 --- a/core/client/Brocfile.js +++ b/core/client/Brocfile.js @@ -36,6 +36,10 @@ app.import('bower_components/ember-load-initializers/ember-load-initializers.js' app.import('bower_components/validator-js/validator.js'); app.import('bower_components/rangyinputs/rangyinputs-jquery-src.js'); app.import('bower_components/showdown-ghost/src/showdown.js'); +app.import('bower_components/showdown-ghost/src/extensions/ghostgfm.js'); +app.import('bower_components/showdown-ghost/src/extensions/ghostimagepreview.js'); +app.import('bower_components/showdown-ghost/src/extensions/footnotes.js'); +app.import('bower_components/showdown-ghost/src/extensions/highlight.js'); app.import('bower_components/moment/moment.js'); app.import('bower_components/keymaster/keymaster.js'); app.import('bower_components/device/lib/device.js'); @@ -49,9 +53,4 @@ app.import('bower_components/google-caja/html-css-sanitizer-bundle.js'); app.import('bower_components/nanoscroller/bin/javascripts/jquery.nanoscroller.js'); app.import('bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.js'); -app.import('vendor/showdown/extensions/ghostgfm.js'); -app.import('vendor/showdown/extensions/ghostimagepreview.js'); -app.import('vendor/showdown/extensions/ghostfootnotes.js'); -app.import('vendor/showdown/extensions/ghosthighlight.js'); - module.exports = app.toTree(); diff --git a/core/client/bower.json b/core/client/bower.json index 05eafa2360..15e86d4226 100644 --- a/core/client/bower.json +++ b/core/client/bower.json @@ -23,7 +23,7 @@ "normalize-scss": "~3.0.1", "nprogress": "0.1.2", "rangyinputs": "1.2.0", - "showdown-ghost": "0.3.4", + "showdown-ghost": "0.3.6", "validator-js": "3.28.0" }, "devDependencies": { diff --git a/core/client/vendor/showdown/extensions/ghostfootnotes.js b/core/client/vendor/showdown/extensions/ghostfootnotes.js deleted file mode 100644 index b491504451..0000000000 --- a/core/client/vendor/showdown/extensions/ghostfootnotes.js +++ /dev/null @@ -1,110 +0,0 @@ -/* jshint node:true, browser:true */ - -// Adds footnote syntax as per Markdown Extra: -// -// https://michelf.ca/projects/php-markdown/extra/#footnotes -// -// That's some text with a footnote.[^1] -// -// [^1]: And that's the footnote. -// -// That's the second paragraph. -// -// Also supports [^n] if you don't want to worry about preserving -// the footnote order yourself. - -function replaceInlineFootnotes(text) { - // Inline footnotes e.g. "foo[^1]" - var inlineRegex = /(?!^)\[\^(\d+|n)\]/gim, - i = 0; - - return text.replace(inlineRegex, function (match, n) { - // We allow both automatic and manual footnote numbering - if (n === 'n') { - n = i + 1; - } - - var s = '' + - '' + n + '' + - ''; - i += 1; - return s; - }); -} - -function replaceEndFootnotes(text, converter) { - // Expanded footnotes at the end e.g. "[^1]: cool stuff" - var endRegex = /\[\^(\d+|n)\]: ([\s\S]*?)$(?! )/gim, - m = text.match(endRegex), - total = m ? m.length : 0, - i = 0; - - return text.replace(endRegex, function (match, n, content) { - if (n === 'n') { - n = i + 1; - } - - content = content.replace(/\n /g, '
'); - content = converter.makeHtml(content); - content = content.replace(/<\/p>$/, ''); - var s = '
  • ' + - content + ' ' + - '

  • '; - - if (i === 0) { - s = '
      ' + s; - } - - if (i === total - 1) { - s = s + '
    '; - } - - i += 1; - return s; - }); -} - -(function () { - var footnotes = function (converter) { - return [ - { - type: 'lang', - filter: function (text) { - var preExtractions = {}, - hashID = 0; - - function hashId() { - return hashID += 1; - } - - // Extract pre blocks - text = text.replace(/```[\s\S]*?\n```/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - text = replaceInlineFootnotes(text); - text = replaceEndFootnotes(text, converter); - - // replace extractions - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return preExtractions[y]; - }); - - return text; - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.footnotes = footnotes; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = footnotes; - } -}()); diff --git a/core/client/vendor/showdown/extensions/ghostgfm.js b/core/client/vendor/showdown/extensions/ghostgfm.js deleted file mode 100644 index bcc9626d96..0000000000 --- a/core/client/vendor/showdown/extensions/ghostgfm.js +++ /dev/null @@ -1,171 +0,0 @@ -/* 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 -// -// ~~strike-through~~ -> strike-through (Pre) -// GFM newlines & underscores (Pre) -// 4 or more underscores (Pre) -// autolinking / custom image handling (Post) - -(function () { - var ghostgfm = function () { - return [ - { - // strike-through - // NOTE: showdown already replaced "~" with "~T", so we need to adjust accordingly. - type: 'lang', - regex: '(~T){2}([^~]+)(~T){2}', - replace: function (match, prefix, content) { - return '' + content + ''; - } - }, - { - // Escaped tildes - // NOTE: showdown already replaced "~" with "~T", and this char doesn't get escaped properly. - type: 'lang', - regex: '\\\\(~T)', - replace: function (match, content) { - return content; - } - }, - { - // GFM newline and underscore modifications, happen BEFORE showdown - type: 'lang', - filter: function (text) { - var extractions = {}, - imageMarkdownRegex = /^(?:\{(.*?)\})?!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim, - hashID = 0; - - function hashId() { - /*jshint plusplus:false*/ - return hashID++; - } - - // Extract pre blocks - text = text.replace(/
    [\s\S]*?<\/pre>/gim, function (x) {
    -                        var hash = hashId();
    -                        extractions[hash] = x;
    -                        return '{gfm-js-extract-pre-' + hash + '}';
    -                    }, 'm');
    -
    -                    // Extract code blocks
    -                    text = text.replace(/```[\s\S]*```/gim, function (x) {
    -                        var hash = hashId();
    -                        extractions[hash] = x;
    -                        return '{gfm-js-extract-code-' + hash + '}';
    -                    }, 'm');
    -
    -                    // prevent foo_bar and foo_bar_baz from ending up with an italic word in the middle
    -                    text = text.replace(/(^(?! {4}|\t)(?!__)\w+_\w+_\w[\w_]*)/gm, function (x) {
    -                        return x.replace(/_/gm, '\\_');
    -                    });
    -
    -                    text = text.replace(/\{gfm-js-extract-code-([0-9]+)\}/gm, function (x, y) {
    -                        return extractions[y];
    -                    });
    -
    -                    // in very clear cases, let newlines become 
    tags - /*jshint -W049 */ - text = text.replace(/^[\w\<\'\'][^\n]*\n+/gm, function (x) { - return x.match(/\n{2}/) ? x : x.trim() + ' \n'; - }); - /*jshint +W049 */ - - // better URL support, but no title support - text = text.replace(imageMarkdownRegex, function (match, key, alt, src) { - if (src) { - return '' + alt + ''; - } - - return ''; - }); - - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return '\n\n' + extractions[y]; - }); - - 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, '_'); - }); - } - }, - - { - // GFM autolinking & custom image handling, happens AFTER showdown - type: 'html', - filter: function (text) { - var refExtractions = {}, - preExtractions = {}, - hashID = 0; - - function hashId() { - /*jshint plusplus:false*/ - return hashID++; - } - - // Extract pre blocks - text = text.replace(/<(pre|code)>[\s\S]*?<\/(\1)>/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - // filter out def urls - // from Marked https://github.com/chjj/marked/blob/master/lib/marked.js#L24 - text = text.replace(/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/gmi, - function (x) { - var hash = hashId(); - refExtractions[hash] = x; - return '{gfm-js-extract-ref-url-' + hash + '}'; - }); - - // match a URL - // adapted from https://gist.github.com/jorilallo/1283095#L158 - // and http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript - /*jshint -W049 */ - text = text.replace(/(\]\(|\]|\[|]*?\>)?https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/gmi, - function (wholeMatch, lookBehind, matchIndex) { - // Check we are not inside an HTML tag - var left = text.slice(0, matchIndex), right = text.slice(matchIndex); - if ((left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) || lookBehind) { - return wholeMatch; - } - // If we have a matching lookBehind, this is a failure, else wrap the match in tag - return lookBehind ? wholeMatch : '' + wholeMatch + ''; - }); - /*jshint +W049 */ - - // replace extractions - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return preExtractions[y]; - }); - - text = text.replace(/\{gfm-js-extract-ref-url-([0-9]+)\}/gi, function (x, y) { - return '\n\n' + refExtractions[y]; - }); - - return text; - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.ghostgfm = ghostgfm; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = ghostgfm; - } -}()); diff --git a/core/client/vendor/showdown/extensions/ghosthighlight.js b/core/client/vendor/showdown/extensions/ghosthighlight.js deleted file mode 100644 index b867b0183b..0000000000 --- a/core/client/vendor/showdown/extensions/ghosthighlight.js +++ /dev/null @@ -1,71 +0,0 @@ -/* jshint node:true, browser:true, -W044 */ - -// Adds highlight syntax as per RedCarpet: -// -// https://github.com/vmg/redcarpet -// -// This is ==highlighted==. It looks like this: highlighted - -(function () { - var highlight = function () { - return [ - { - type: 'html', - filter: function (text) { - var highlightRegex = /(=){2}([\s\S]+?)(=){2}/gim, - preExtractions = {}, - codeExtractions = {}, - hashID = 0; - - function hashId() { - return hashID += 1; - } - - // Extract pre blocks - text = text.replace(/
    [\s\S]*?<\/pre>/gim, function (x) {
    -                        var hash = hashId();
    -                        preExtractions[hash] = x;
    -                        return '{gfm-js-extract-pre-' + hash + '}';
    -                    }, 'm');
    -
    -                    // Extract code blocks
    -                    text = text.replace(/[\s\S]*?<\/code>/gim, function (x) {
    -                        var hash = hashId();
    -                        codeExtractions[hash] = x;
    -                        return '{gfm-js-extract-code-' + hash + '}';
    -                    }, 'm');
    -
    -                    text = text.replace(highlightRegex, function (match, n, content) {
    -                        // Check the content isn't just an `=`
    -                        if (!/^=+$/.test(content)) {
    -                            return '' + content + '';
    -                        }
    -
    -                        return match;
    -                    });
    -
    -                    // replace pre extractions
    -                    text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) {
    -                        return preExtractions[y];
    -                    });
    -
    -                     // replace code extractions
    -                    text = text.replace(/\{gfm-js-extract-code-([0-9]+)\}/gm, function (x, y) {
    -                        return codeExtractions[y];
    -                    });
    -
    -                    return text;
    -                }
    -            }
    -        ];
    -    };
    -
    -    // Client-side export
    -    if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) {
    -        window.Showdown.extensions.highlight = highlight;
    -    }
    -    // Server-side export
    -    if (typeof module !== 'undefined') {
    -        module.exports = highlight;
    -    }
    -}());
    diff --git a/core/client/vendor/showdown/extensions/ghostimagepreview.js b/core/client/vendor/showdown/extensions/ghostimagepreview.js
    deleted file mode 100644
    index 5974c4e5b7..0000000000
    --- a/core/client/vendor/showdown/extensions/ghostimagepreview.js
    +++ /dev/null
    @@ -1,51 +0,0 @@
    -/* 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 || {};
    -(function () {
    -    var ghostimagepreview = function () {
    -        return [
    -            // ![] image syntax
    -            {
    -                type: 'lang',
    -                filter: function (text) {
    -                    var imageMarkdownRegex = /^!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim,
    -                    /* regex from isURL in node-validator. Yum! */
    -                        uriRegex = /^(?!mailto:)(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:\/[^\s]*)?$/i,
    -                        pathRegex = /^(\/)?([^\/\0]+(\/)?)+$/i;
    -
    -                    return text.replace(imageMarkdownRegex, function (match, alt, src) {
    -                        var result = '',
    -                            output;
    -
    -                        if (src && (src.match(uriRegex) || src.match(pathRegex))) {
    -                            result = '';
    -                        }
    -
    -                        output = '
    ' + - result + - '
    Add image of ' + alt + '
    ' + - '' + - '
    '; - - return output; - }); - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.ghostimagepreview = ghostimagepreview; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = ghostimagepreview; - } -}()); diff --git a/core/server/models/post.js b/core/server/models/post.js index 29e20ee5f8..705714ae2f 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -5,10 +5,7 @@ var _ = require('lodash'), sequence = require('../utils/sequence'), errors = require('../errors'), Showdown = require('showdown-ghost'), - ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'), - footnotes = require('../../shared/lib/showdown/extensions/ghostfootnotes'), - highlight = require('../../shared/lib/showdown/extensions/ghosthighlight'), - converter = new Showdown.converter({extensions: [ghostgfm, footnotes, highlight]}), + converter = new Showdown.converter({extensions: ['ghostgfm', 'footnotes', 'highlight']}), ghostBookshelf = require('./base'), xmlrpc = require('../xmlrpc'), sitemap = require('../data/sitemap'), diff --git a/core/shared/lib/showdown/extensions/ghostfootnotes.js b/core/shared/lib/showdown/extensions/ghostfootnotes.js deleted file mode 100644 index 24d4c9611a..0000000000 --- a/core/shared/lib/showdown/extensions/ghostfootnotes.js +++ /dev/null @@ -1,123 +0,0 @@ -/* jshint node:true, browser:true */ - -// Adds footnote syntax as per Markdown Extra: -// -// https://michelf.ca/projects/php-markdown/extra/#footnotes -// -// That's some text with a footnote.[^1] -// -// [^1]: And that's the footnote. -// -// That's the second paragraph. -// -// Also supports [^n] if you don't want to worry about preserving -// the footnote order yourself. - -function replaceInlineFootnotes(text) { - // Inline footnotes e.g. "foo[^1]" - var inlineRegex = /(?!^)\[\^(\d+|n)\]/gim, - i = 0; - - return text.replace(inlineRegex, function (match, n) { - // We allow both automatic and manual footnote numbering - if (n === 'n') { - n = i + 1; - } - - var s = '' + - '' + n + '' + - ''; - i += 1; - return s; - }); -} - -function replaceEndFootnotes(text, converter) { - // Expanded footnotes at the end e.g. "[^1]: cool stuff" - var endRegex = /\[\^(\d+|n)\]: ([\s\S]*?)$(?! )/gim, - m = text.match(endRegex), - total = m ? m.length : 0, - i = 0; - - return text.replace(endRegex, function (match, n, content) { - if (n === 'n') { - n = i + 1; - } - - content = content.replace(/\n /g, '
    '); - content = converter.makeHtml(content); - content = content.replace(/<\/p>$/, ''); - var s = '
  • ' + - content + ' ' + - '

  • '; - - if (i === 0) { - s = '
      ' + s; - } - - if (i === total - 1) { - s = s + '
    '; - } - - i += 1; - return s; - }); -} - -(function () { - var footnotes = function (converter) { - return [ - { - type: 'lang', - filter: function (text) { - var preExtractions = {}, - hashID = 0; - - function hashId() { - return hashID += 1; - } - - // Extract pre blocks - text = text.replace(/<(pre|code)>[\s\S]*?<\/(\1)>/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - text = text.replace(/```[\s\S]*?\n```/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - // Extract code blocks - text = text.replace(/`[\s\S]*?`/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - text = replaceInlineFootnotes(text); - text = replaceEndFootnotes(text, converter); - - // replace extractions - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return preExtractions[y]; - }); - - return text; - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.footnotes = footnotes; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = footnotes; - } -}()); diff --git a/core/shared/lib/showdown/extensions/ghostgfm.js b/core/shared/lib/showdown/extensions/ghostgfm.js deleted file mode 100644 index 6847d8d1c9..0000000000 --- a/core/shared/lib/showdown/extensions/ghostgfm.js +++ /dev/null @@ -1,171 +0,0 @@ -/* 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 -// -// ~~strike-through~~ -> strike-through (Pre) -// GFM newlines & underscores (Pre) -// 4 or more underscores (Pre) -// autolinking / custom image handling (Post) - -(function () { - var ghostgfm = function () { - return [ - { - // strike-through - // NOTE: showdown already replaced "~" with "~T", so we need to adjust accordingly. - type: 'lang', - regex: '(~T){2}([^~]+)(~T){2}', - replace: function (match, prefix, content) { - return '' + content + ''; - } - }, - { - // Escaped tildes - // NOTE: showdown already replaced "~" with "~T", and this char doesn't get escaped properly. - type: 'lang', - regex: '\\\\(~T)', - replace: function (match, content) { - return content; - } - }, - { - // GFM newline and underscore modifications, happen BEFORE showdown - type: 'lang', - filter: function (text) { - var extractions = {}, - imageMarkdownRegex = /^(?:\{(.*?)\})?!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim, - hashID = 0; - - function hashId() { - /*jshint plusplus:false*/ - return hashID++; - } - - // Extract pre blocks - text = text.replace(/
    [\s\S]*?<\/pre>/gim, function (x) {
    -                        var hash = hashId();
    -                        extractions[hash] = x;
    -                        return '{gfm-js-extract-pre-' + hash + '}';
    -                    }, 'm');
    -
    -                    // Extract code blocks
    -                    text = text.replace(/```[\s\S]*```/gim, function (x) {
    -                        var hash = hashId();
    -                        extractions[hash] = x;
    -                        return '{gfm-js-extract-code-' + hash + '}';
    -                    }, 'm');
    -
    -                    // prevent foo_bar and foo_bar_baz from ending up with an italic word in the middle
    -                    text = text.replace(/(^(?! {4}|\t)(?!__)\w+_\w+_\w[\w_]*)/gm, function (x) {
    -                        return x.replace(/_/gm, '\\_');
    -                    });
    -
    -                    text = text.replace(/\{gfm-js-extract-code-([0-9]+)\}/gm, function (x, y) {
    -                        return extractions[y];
    -                    });
    -
    -                    // in very clear cases, let newlines become 
    tags - /*jshint -W049 */ - text = text.replace(/^[\w\<\'\'][^\n]*\n+/gm, function (x) { - return x.match(/\n{2}/) ? x : x.trim() + ' \n'; - }); - /*jshint +W049 */ - - // better URL support, but no title support - text = text.replace(imageMarkdownRegex, function (match, key, alt, src) { - if (src) { - return '' + alt + ''; - } - - return ''; - }); - - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return '\n\n' + extractions[y]; - }); - - 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, '_'); - }); - } - }, - - { - // GFM autolinking & custom image handling, happens AFTER showdown - type: 'html', - filter: function (text) { - var refExtractions = {}, - preExtractions = {}, - hashID = 0; - - function hashId() { - /*jshint plusplus:false*/ - return hashID++; - } - - // Extract pre blocks - text = text.replace(/<(pre|code|script)>[\s\S]*?<\/(\1)>/gim, function (x) { - var hash = hashId(); - preExtractions[hash] = x; - return '{gfm-js-extract-pre-' + hash + '}'; - }, 'm'); - - // filter out def urls - // from Marked https://github.com/chjj/marked/blob/master/lib/marked.js#L24 - text = text.replace(/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/gmi, - function (x) { - var hash = hashId(); - refExtractions[hash] = x; - return '{gfm-js-extract-ref-url-' + hash + '}'; - }); - - // match a URL - // adapted from https://gist.github.com/jorilallo/1283095#L158 - // and http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript - /*jshint -W049 */ - text = text.replace(/(\]\(|\]|\[|]*?\>)?https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/gmi, - function (wholeMatch, lookBehind, matchIndex) { - // Check we are not inside an HTML tag - var left = text.slice(0, matchIndex), right = text.slice(matchIndex); - if ((left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) || lookBehind) { - return wholeMatch; - } - // If we have a matching lookBehind, this is a failure, else wrap the match in tag - return lookBehind ? wholeMatch : '' + wholeMatch + ''; - }); - /*jshint +W049 */ - - // replace extractions - text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) { - return preExtractions[y]; - }); - - text = text.replace(/\{gfm-js-extract-ref-url-([0-9]+)\}/gi, function (x, y) { - return '\n\n' + refExtractions[y]; - }); - - return text; - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.ghostgfm = ghostgfm; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = ghostgfm; - } -}()); diff --git a/core/shared/lib/showdown/extensions/ghosthighlight.js b/core/shared/lib/showdown/extensions/ghosthighlight.js deleted file mode 100644 index b867b0183b..0000000000 --- a/core/shared/lib/showdown/extensions/ghosthighlight.js +++ /dev/null @@ -1,71 +0,0 @@ -/* jshint node:true, browser:true, -W044 */ - -// Adds highlight syntax as per RedCarpet: -// -// https://github.com/vmg/redcarpet -// -// This is ==highlighted==. It looks like this: highlighted - -(function () { - var highlight = function () { - return [ - { - type: 'html', - filter: function (text) { - var highlightRegex = /(=){2}([\s\S]+?)(=){2}/gim, - preExtractions = {}, - codeExtractions = {}, - hashID = 0; - - function hashId() { - return hashID += 1; - } - - // Extract pre blocks - text = text.replace(/
    [\s\S]*?<\/pre>/gim, function (x) {
    -                        var hash = hashId();
    -                        preExtractions[hash] = x;
    -                        return '{gfm-js-extract-pre-' + hash + '}';
    -                    }, 'm');
    -
    -                    // Extract code blocks
    -                    text = text.replace(/[\s\S]*?<\/code>/gim, function (x) {
    -                        var hash = hashId();
    -                        codeExtractions[hash] = x;
    -                        return '{gfm-js-extract-code-' + hash + '}';
    -                    }, 'm');
    -
    -                    text = text.replace(highlightRegex, function (match, n, content) {
    -                        // Check the content isn't just an `=`
    -                        if (!/^=+$/.test(content)) {
    -                            return '' + content + '';
    -                        }
    -
    -                        return match;
    -                    });
    -
    -                    // replace pre extractions
    -                    text = text.replace(/\{gfm-js-extract-pre-([0-9]+)\}/gm, function (x, y) {
    -                        return preExtractions[y];
    -                    });
    -
    -                     // replace code extractions
    -                    text = text.replace(/\{gfm-js-extract-code-([0-9]+)\}/gm, function (x, y) {
    -                        return codeExtractions[y];
    -                    });
    -
    -                    return text;
    -                }
    -            }
    -        ];
    -    };
    -
    -    // Client-side export
    -    if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) {
    -        window.Showdown.extensions.highlight = highlight;
    -    }
    -    // Server-side export
    -    if (typeof module !== 'undefined') {
    -        module.exports = highlight;
    -    }
    -}());
    diff --git a/core/shared/lib/showdown/extensions/ghostimagepreview.js b/core/shared/lib/showdown/extensions/ghostimagepreview.js
    deleted file mode 100644
    index 5974c4e5b7..0000000000
    --- a/core/shared/lib/showdown/extensions/ghostimagepreview.js
    +++ /dev/null
    @@ -1,51 +0,0 @@
    -/* 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 || {};
    -(function () {
    -    var ghostimagepreview = function () {
    -        return [
    -            // ![] image syntax
    -            {
    -                type: 'lang',
    -                filter: function (text) {
    -                    var imageMarkdownRegex = /^!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim,
    -                    /* regex from isURL in node-validator. Yum! */
    -                        uriRegex = /^(?!mailto:)(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:\/[^\s]*)?$/i,
    -                        pathRegex = /^(\/)?([^\/\0]+(\/)?)+$/i;
    -
    -                    return text.replace(imageMarkdownRegex, function (match, alt, src) {
    -                        var result = '',
    -                            output;
    -
    -                        if (src && (src.match(uriRegex) || src.match(pathRegex))) {
    -                            result = '';
    -                        }
    -
    -                        output = '
    ' + - result + - '
    Add image of ' + alt + '
    ' + - '' + - '
    '; - - return output; - }); - } - } - ]; - }; - - // Client-side export - if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { - window.Showdown.extensions.ghostimagepreview = ghostimagepreview; - } - // Server-side export - if (typeof module !== 'undefined') { - module.exports = ghostimagepreview; - } -}()); diff --git a/core/test/unit/showdown_client_integrated_spec.js b/core/test/unit/showdown_client_integrated_spec.js index 40c6ac3ba2..2348f1fefd 100644 --- a/core/test/unit/showdown_client_integrated_spec.js +++ b/core/test/unit/showdown_client_integrated_spec.js @@ -10,12 +10,7 @@ var should = require('should'), // Stuff we are testing Showdown = require('showdown-ghost'), - ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'), - ghostimagepreview = require('../../shared/lib/showdown/extensions/ghostimagepreview'), - footnotes = require('../../shared/lib/showdown/extensions/ghostfootnotes'), - highlight = require('../../shared/lib/showdown/extensions/ghosthighlight'), - - converter = new Showdown.converter({extensions: [ghostimagepreview, ghostgfm, footnotes, highlight]}); + converter = new Showdown.converter({extensions: ['ghostimagepreview', 'ghostgfm', 'footnotes', 'highlight']}); // To stop jshint complaining should.equal(true, true); diff --git a/core/test/unit/showdown_footnotes_spec.js b/core/test/unit/showdown_footnotes_spec.js deleted file mode 100644 index 079edf067f..0000000000 --- a/core/test/unit/showdown_footnotes_spec.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Tests the footnotes extension for showdown - * - */ - -/*globals describe, it */ -/*jshint expr:true*/ -var should = require('should'), - - // Stuff we are testing - ghostfootnotes = require('../../shared/lib/showdown/extensions/ghostfootnotes'), - Showdown = require('showdown-ghost'), - converter = new Showdown.converter({extensions: []}); - -// To stop jshint complaining -should.equal(true, true); - -function _ExecuteExtension(ext, text) { - if (ext.regex) { - var re = new RegExp(ext.regex, 'g'); - return text.replace(re, ext.replace); - } else if (ext.filter) { - return ext.filter(text); - } -} - -function _ConvertPhrase(testPhrase) { - return ghostfootnotes(converter).reduce(function (text, ext) { - return _ExecuteExtension(ext, text); - }, testPhrase); -} - -describe('Ghost footnotes showdown extension', function () { - /*jslint regexp: true */ - - it('should export an array of methods for processing', function () { - ghostfootnotes.should.be.a.function; - ghostfootnotes().should.be.an.Array; - - ghostfootnotes().forEach(function (processor) { - processor.should.be.an.Object; - processor.should.have.property('type'); - processor.type.should.be.a.String; - }); - }); - - it('should replace inline footnotes with the right html', function () { - var testPhrase = { - input: 'foo_bar[^1]', - output: /1<\/a><\/sup>/ - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should handle n-digit footnotes', function () { - var testPhrase = { - input: 'foo_bar[^42]', - output: /42<\/a><\/sup>/ - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should replace end footnotes with the right html', function () { - var testPhrase = { - input: '[^1]: foo bar', - output: /
    1. foo bar ↩<\/a><\/p><\/li><\/ol><\/div>/ - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should expand Markdown inside footnotes', function () { - var testPhrase = { - input: '[^1]: *foo*', - output: /foo<\/em>/ - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should number multiple footnotes correctly', function () { - var testPhrase = { - input: 'foo[^1] bar[^n] etc[^2]', - output: /foo1<\/a><\/sup> bar2<\/a><\/sup> etc2<\/a><\/sup>/ - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should put everything together', function () { - // Tests for some interaction bugs between components e.g. - // confusing the end form and the inline form - var testPhrase = { - input: 'foo bar[^1] is a very[^n] foo bar[^1]\n' + - '[^n]: a metasyntactic variable\n' + - '[^n]: this is hard to measure', - output: 'foo bar1 is a very2 foo bar1\n' + - '

      1. a metasyntactic variable

      2. \n' + - '
      3. this is hard to measure

      ' - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should show markdown inside code block', function () { - var testPhrase = { - input: '[^n]<\/code>', - output: '[^n]<\/code>' - }, processedMarkup = _ConvertPhrase(testPhrase.input); - - processedMarkup.should.match(testPhrase.output); - }); - - it('should show markdown inside pre block', function () { - var testPhrase = { - input: '
      [^n]<\/pre>',
      -            output: '
      [^n]<\/pre>'
      -        }, processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -
      -    it('should show markdown inside single tick', function () {
      -        var testPhrase = {
      -            input: '`[^n]`',
      -            output: '`[^n]`'
      -        }, processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -
      -    it('should show markdown inside triple tick', function () {
      -        var testPhrase = {
      -            input: '```[^n]```',
      -            output: '```[^n]```'
      -        }, processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -});
      diff --git a/core/test/unit/showdown_ghostGFM_spec.js b/core/test/unit/showdown_ghostGFM_spec.js
      deleted file mode 100644
      index 43093beb80..0000000000
      --- a/core/test/unit/showdown_ghostGFM_spec.js
      +++ /dev/null
      @@ -1,285 +0,0 @@
      -/**
      - * Tests the github extension for showdown
      - *
      - */
      -
      -/*globals describe, it */
      -/*jshint expr:true*/
      -var should      = require('should'),
      -
      -    // Stuff we are testing
      -    ghostgfm    = require('../../shared/lib/showdown/extensions/ghostgfm');
      -
      -// To stop jshint complaining
      -should.equal(true, true);
      -
      -function _ExecuteExtension(ext, text) {
      -    if (ext.regex) {
      -        var re = new RegExp(ext.regex, 'g');
      -        return text.replace(re, ext.replace);
      -    } else if (ext.filter) {
      -        return ext.filter(text);
      -    }
      -}
      -
      -function _ConvertPhrase(testPhrase) {
      -    return ghostgfm().reduce(function (text, ext) {
      -        return _ExecuteExtension(ext, text);
      -    }, testPhrase);
      -}
      -
      -describe('Ghost GFM showdown extension', function () {
      -    /*jslint regexp: true */
      -
      -    it('should export an array of methods for processing', function () {
      -        ghostgfm.should.be.a.function;
      -        ghostgfm().should.be.an.Array;
      -
      -        ghostgfm().forEach(function (processor) {
      -            processor.should.be.an.Object;
      -            processor.should.have.property('type');
      -            processor.type.should.be.a.String;
      -        });
      -    });
      -
      -    it('should replace showdown strike through with html', function () {
      -        var testPhrase = {input: '~T~Tfoo_bar~T~T', output: /foo_bar<\/del>/},
      -            processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        // The image is the entire markup, so the image box should be too
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -
      -    it('should honour escaped tildes', function () {
      -        /*jshint -W044 */
      -        var testPhrase = {input: '\\~T\\~Tfoo_bar\\~T\\~T', output: /~T~Tfoo_bar~T~T/},
      -        /*jshint +W044 */
      -            processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        // The image is the entire markup, so the image box should be too
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -
      -    it('should allow 4 underscores', function () {
      -        var testPhrase = {input: 'Ghost ____', output: /Ghost\s(?:_){4}$/},
      -            processedMarkup = _ConvertPhrase(testPhrase.input);
      -
      -        processedMarkup.should.match(testPhrase.output);
      -    });
      -
      -    it('should auto-link URL in text with markdown syntax', function () {
      -        var testPhrases = [
      -            {
      -                input: 'http://google.co.uk',
      -                output: /^http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: 'https://atest.com/fizz/buzz?baz=fizzbuzz',
      -                output: /^https:\/\/atest.com\/fizz\/buzz\?baz=fizzbuzz<\/a>$/
      -            },
      -            {
      -                input: 'Some text http://www.google.co.uk some other text',
      -                output: /^Some text http:\/\/www.google.co.uk<\/a> some other text$/
      -            },
      -            {
      -                input: 'Some [ text http://www.google.co.uk some other text',
      -                output: /^Some \[ text http:\/\/www.google.co.uk<\/a> some other text$/
      -            },
      -            {
      -                input: 'Some [ text (http://www.google.co.uk) some other text',
      -                output: /^Some \[ text \(http:\/\/www.google.co.uk<\/a>\) some other text$/
      -            },
      -            {
      -                input: '  http://google.co.uk  ',
      -                output: /^  http:\/\/google.co.uk<\/a>  $/
      -            },
      -            {
      -                input: '>http://google.co.uk',
      -                output: /^>http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '> http://google.co.uk',
      -                output: /^> http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '<>>> http://google.co.uk',
      -                output: /^<>>> http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '<>>>http://google.co.uk',
      -                output: /^<>>>http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '>>http://google.co.uk',
      -                output: /^>>http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: 'http://google.co.uk',
      -                output: /^http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '# http://google.co.uk',
      -                output: /^# http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '#http://google.co.uk',
      -                output: /^#http:\/\/google.co.uk<\/a>$/
      -            },
      -            {
      -                input: '* http://google.co.uk',
      -                output: /^\* http:\/\/google.co.uk<\/a>$/
      -            }
      -        ],
      -        processedMarkup;
      -
      -        testPhrases.forEach(function (testPhrase) {
      -            processedMarkup = _ConvertPhrase(testPhrase.input);
      -            processedMarkup.should.match(testPhrase.output);
      -        });
      -    });
      -
      -    it('should NOT auto-link URL in HTML', function () {
      -        var testPhrases = [
      -            {
      -                input: '',
      -                output: /^$/
      -            },
      -            {
      -                input: '',
      -                output: /^$/
      -            },
      -            {
      -                input: '',
      -                output: /^',
      -                output: /^