Merge branch 'master' into develop

# Conflicts:
#	Gruntfile.js
#	dist/showdown.js
#	dist/showdown.js.map
#	dist/showdown.min.js
#	dist/showdown.min.js.map
#	package-lock.json
#	package.json
#	src/converter.js
#	src/subParsers/makehtml/tables.js
This commit is contained in:
Estevao Soares dos Santos 2022-02-23 19:15:46 +00:00
commit e24d06e265
48 changed files with 7913 additions and 5828 deletions

View File

@ -1,41 +0,0 @@
# branches to build
#branches:
# whitelist
#only:
# - master
# What combinations to test
environment:
matrix:
- nodejs_version: "6"
platform: x64
- nodejs_version: "6"
platform: x86
- nodejs_version: "8"
platform: x86
- nodejs_version: "8"
platform: x64
- nodejs_version: "10"
platform: x64
cache:
- node_modules
install:
# Use version based on tag
- ps: $env:package_version = (Get-Content -Raw -Path package.json | ConvertFrom-Json).version
- ps: Update-AppveyorBuild -Version "$env:package_version-$env:APPVEYOR_BUILD_NUMBER"
# install node
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install grunt-cli globally
- npm install -g grunt-cli
# install modules
- npm install
test_script:
# Output useful info for debugging
- node --version && npm --version
- ps: grunt test
# Don't actually build.
build: off

30
.github/workflows/node.linux.yml vendored Normal file
View File

@ -0,0 +1,30 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node Linux CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x, 17.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test

30
.github/workflows/node.win.yml vendored Normal file
View File

@ -0,0 +1,30 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node Windows CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x, 17.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test

View File

@ -1,26 +0,0 @@
language: node_js
node_js:
- "6"
- "8"
- "10"
before_install:
- npm install -g grunt-cli
# travis build speed up
sudo: false
cache:
directories:
- node_modules
# scripts
script: grunt test
# hooks
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e369617839852624aa69
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@ -3,6 +3,7 @@ Credits
- Showdown v2 - Showdown v2
* [Estevão Santos](https://github.com/tivie) * [Estevão Santos](https://github.com/tivie)
* [SyntaxRules](https://github.com/SyntaxRules)
- Showdown v1 - Showdown v1
* [Estevão Santos](https://github.com/tivie) * [Estevão Santos](https://github.com/tivie)

View File

@ -75,7 +75,7 @@ module.exports = function (grunt) {
eslint: { eslint: {
options: { options: {
useEslintrc: true overrideConfigFile: '.eslintrc.json'
}, },
target: [ target: [
'Gruntfile.js', 'Gruntfile.js',

20
LICENSE
View File

@ -1,21 +1,9 @@
MIT License MIT License
Copyright (c) 2018 ShowdownJS Copyright (c) 2018,2021 ShowdownJS
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,7 +1,7 @@
![Showdown][sd-logo] ![Showdown][sd-logo]
[![Build Status: Linux](https://travis-ci.org/showdownjs/showdown.svg?branch=master)](https://travis-ci.org/showdownjs/showdown) ![Build Status: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.linux.yml/badge.svg)
[![Build Status: Windows](https://ci.appveyor.com/api/projects/status/github/showdownjs/showdown?branch=master&svg=true)](https://ci.appveyor.com/project/tivie/showdown/branch/master) ![Build Status: Windows](https://github.com/showdownjs/showdown/actions/workflows/node.win.yml/badge.svg)
[![npm version](https://badge.fury.io/js/showdown.svg)](http://badge.fury.io/js/showdown) [![npm version](https://badge.fury.io/js/showdown.svg)](http://badge.fury.io/js/showdown)
[![Bower version](https://badge.fury.io/bo/showdown.svg)](http://badge.fury.io/bo/showdown) [![Bower version](https://badge.fury.io/bo/showdown.svg)](http://badge.fury.io/bo/showdown)
[![Join the chat at https://gitter.im/showdownjs/showdown](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/showdownjs/showdown?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/showdownjs/showdown](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/showdownjs/showdown?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@ -93,7 +93,7 @@ The converter itself might even work in things that aren't web browsers, like Ac
## Node compatibility ## Node compatibility
Showdown has been tested with node 0.8 and 0.10. However, it should work with previous versions, such as node 0.6. Showdown is intended to work on any supported node.js version (see the [node.js releases schedule](https://nodejs.org/en/about/releases/). The code may work with previous versions of node.js, but no accomidations are made to ensure it does.
## Legacy version ## Legacy version

16
SECURITY.md Normal file
View File

@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
Security fixes are addressed for the following versions of Showdown.
| Version | Supported |
| ------- | ------------------ |
| 2.0.x | :white_check_mark: |
| 1.x.x | :x: (Known security issue with yargs dependecy) |
Showdown targets the node.js versions targeted in the [node.js release schedule](https://nodejs.org/en/about/releases/). Our test suite follows this release schedule. Consequently, older versions of node may become unusable.
## Reporting a Vulnerability
To report a vulnerability, please add an issue to our main github page: https://github.com/showdownjs/showdown/issues

BIN
dist/showdown.js vendored

Binary file not shown.

BIN
dist/showdown.js.map vendored

Binary file not shown.

BIN
dist/showdown.min.js vendored

Binary file not shown.

Binary file not shown.

10016
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@
"url": "https://github.com/showdownjs/showdown.git", "url": "https://github.com/showdownjs/showdown.git",
"web": "https://github.com/showdownjs/showdown" "web": "https://github.com/showdownjs/showdown"
}, },
"license": "BSD-3-Clause", "license": "MIT",
"main": "./dist/showdown.js", "main": "./dist/showdown.js",
"scripts": { "scripts": {
"test": "grunt test" "test": "grunt test"
@ -43,27 +43,27 @@
"dist" "dist"
], ],
"devDependencies": { "devDependencies": {
"chai": "4.1.x", "chai": "^4.3.4",
"grunt": "^1.0.3", "grunt": "^1.4.1",
"grunt-contrib-clean": "1.1.x", "grunt-contrib-clean": "^2.0.0",
"grunt-contrib-concat": "1.0.x", "grunt-contrib-concat": "^2.0.0",
"grunt-contrib-jshint": "3.2.x", "grunt-contrib-jshint": "^3.1.0",
"grunt-contrib-uglify": "~3.3.0", "grunt-contrib-uglify": "^5.0.1",
"grunt-conventional-changelog": "6.1.x", "grunt-conventional-changelog": "^6.1.0",
"grunt-conventional-github-releaser": "1.0.x", "grunt-conventional-github-releaser": "^1.0.0",
"grunt-endline": "0.6.x", "grunt-endline": "^0.7.0",
"grunt-eslint": "^24.0.0", "grunt-eslint": "^24.0.0",
"grunt-simple-mocha": "0.4.x", "grunt-simple-mocha": "^0.4.0",
"load-grunt-tasks": "3.5.x", "load-grunt-tasks": "^5.1.0",
"performance-now": "2.0.x", "performance-now": "^2.1.0",
"quiet-grunt": "0.2.x", "quiet-grunt": "^0.2.0",
"semver": "5.4.x", "semver": "^7.3.0",
"semver-sort": "0.0.x", "semver-sort": "^0.0.4",
"sinon": "^5.1.1", "sinon": "^12.0.1",
"source-map-support": "^0.5.9" "source-map-support": "^0.5.20"
}, },
"dependencies": { "dependencies": {
"jsdom": "^11.0.0", "jsdom": "^18.0.1",
"yargs": "14.2.0" "yargs": "^17.2.1"
} }
} }

View File

@ -393,7 +393,7 @@ showdown.Converter = function (converterOptions) {
for (var n = 0; n < node.childNodes.length; ++n) { for (var n = 0; n < node.childNodes.length; ++n) {
var child = node.childNodes[n]; var child = node.childNodes[n];
if (child.nodeType === 3) { if (child.nodeType === 3) {
if (!/\S/.test(child.nodeValue)) { if (!/\S/.test(child.nodeValue) && !/^[ ]+$/.test(child.nodeValue)) {
node.removeChild(child); node.removeChild(child);
--n; --n;
} else { } else {
@ -532,12 +532,12 @@ showdown.Converter = function (converterOptions) {
var ext = extension[a]; var ext = extension[a];
for (var i = 0; i < langExtensions.length; ++i) { for (var i = 0; i < langExtensions.length; ++i) {
if (langExtensions[i] === ext) { if (langExtensions[i] === ext) {
langExtensions[i].splice(i, 1); langExtensions.splice(i, 1);
} }
} }
for (var ii = 0; ii < outputModifiers.length; ++i) { for (var ii = 0; ii < outputModifiers.length; ++ii) {
if (outputModifiers[ii] === ext) { if (outputModifiers[ii] === ext) {
outputModifiers[ii].splice(i, 1); outputModifiers.splice(ii, 1);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -165,7 +165,12 @@ function getDefaultOpts (simple) {
defaultValue: false, defaultValue: false,
description: 'Split adjacent blockquote blocks', description: 'Split adjacent blockquote blocks',
type: 'boolean' type: 'boolean'
} },
relativePathBaseUrl: {
defaultValue: false,
describe: 'Prepends a base URL to relative paths',
type: 'string'
},
}; };
if (simple === false) { if (simple === false) {
return JSON.parse(JSON.stringify(defaultOptions)); return JSON.parse(JSON.stringify(defaultOptions));

View File

@ -36,6 +36,7 @@ showdown.subParser('makehtml.hashHTMLBlocks', function (text, options, globals)
'hgroup', 'hgroup',
'output', 'output',
'video', 'video',
'details',
'p' 'p'
], ],
repFunc = function (wholeMatch, match, left, right) { repFunc = function (wholeMatch, match, left, right) {

View File

@ -17,6 +17,12 @@ showdown.subParser('makehtml.images', function (text, options, globals) {
return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title); return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
} }
function writeImageTagBaseUrl (wholeMatch, altText, linkId, url, width, height, m5, title) {
url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
}
function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
var gUrls = globals.gUrls, var gUrls = globals.gUrls,
@ -91,10 +97,10 @@ showdown.subParser('makehtml.images', function (text, options, globals) {
text = text.replace(base64RegExp, writeImageTagBase64); text = text.replace(base64RegExp, writeImageTagBase64);
// cases with crazy urls like ./image/cat1).png // cases with crazy urls like ./image/cat1).png
text = text.replace(crazyRegExp, writeImageTag); text = text.replace(crazyRegExp, writeImageTagBaseUrl);
// normal cases // normal cases
text = text.replace(inlineRegExp, writeImageTag); text = text.replace(inlineRegExp, writeImageTagBaseUrl);
// handle reference-style shortcuts: ![img text] // handle reference-style shortcuts: ![img text]
text = text.replace(refShortcutRegExp, writeImageTag); text = text.replace(refShortcutRegExp, writeImageTag);

View File

@ -23,7 +23,7 @@
* @param {{}} globals * @param {{}} globals
* @returns {Function} * @returns {Function}
*/ */
function replaceAnchorTag (rgx, evtRootName, options, globals, emptyCase) { function replaceAnchorTagReference (rgx, evtRootName, options, globals, emptyCase) {
emptyCase = !!emptyCase; emptyCase = !!emptyCase;
return function (wholeMatch, text, id, url, m5, m6, title) { return function (wholeMatch, text, id, url, m5, m6, title) {
// bail we we find 2 newlines somewhere // bail we we find 2 newlines somewhere
@ -36,6 +36,15 @@
}; };
} }
function replaceAnchorTagBaseUrl (rgx, evtRootName, options, globals, emptyCase) {
return function (wholeMatch, text, id, url, m5, m6, title) {
url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
return writeAnchorTag(evt, options, globals, emptyCase);
};
}
/** /**
* TODO Normalize this * TODO Normalize this
* Helper function: Create a capture event * Helper function: Create a capture event
@ -192,21 +201,21 @@
// 1. Look for empty cases: []() and [empty]() and []("title") // 1. Look for empty cases: []() and [empty]() and []("title")
var rgxEmpty = /\[(.*?)]()()()()\(<? ?>? ?(?:["'](.*)["'])?\)/g; var rgxEmpty = /\[(.*?)]()()()()\(<? ?>? ?(?:["'](.*)["'])?\)/g;
text = text.replace(rgxEmpty, replaceAnchorTag(rgxEmpty, evtRootName, options, globals, true)); text = text.replace(rgxEmpty, replaceAnchorTagBaseUrl(rgxEmpty, evtRootName, options, globals, true));
// 2. Look for cases with crazy urls like ./image/cat1).png // 2. Look for cases with crazy urls like ./image/cat1).png
var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g; var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
text = text.replace(rgxCrazy, replaceAnchorTag(rgxCrazy, evtRootName, options, globals)); text = text.replace(rgxCrazy, replaceAnchorTagBaseUrl(rgxCrazy, evtRootName, options, globals));
// 3. inline links with no title or titles wrapped in ' or ": // 3. inline links with no title or titles wrapped in ' or ":
// [text](url.com) || [text](<url.com>) || [text](url.com "title") || [text](<url.com> "title") // [text](url.com) || [text](<url.com>) || [text](url.com "title") || [text](<url.com> "title")
//var rgx2 = /\[[ ]*[\s]?[ ]*([^\n\[\]]*?)[ ]*[\s]?[ ]*] ?()\(<?[ ]*[\s]?[ ]*([^\s'"]*)>?(?:[ ]*[\n]?[ ]*()(['"])(.*?)\5)?[ ]*[\s]?[ ]*\)/; // this regex is too slow!!! //var rgx2 = /\[[ ]*[\s]?[ ]*([^\n\[\]]*?)[ ]*[\s]?[ ]*] ?()\(<?[ ]*[\s]?[ ]*([^\s'"]*)>?(?:[ ]*[\n]?[ ]*()(['"])(.*?)\5)?[ ]*[\s]?[ ]*\)/; // this regex is too slow!!!
var rgx2 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s*(?:()(['"])(.*?)\5)? *\)/g; var rgx2 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s*(?:()(['"])(.*?)\5)? *\)/g;
text = text.replace(rgx2, replaceAnchorTag(rgx2, evtRootName, options, globals)); text = text.replace(rgx2, replaceAnchorTagBaseUrl(rgx2, evtRootName, options, globals));
// 4. inline links with titles wrapped in (): [foo](bar.com (title)) // 4. inline links with titles wrapped in (): [foo](bar.com (title))
var rgx3 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s+()()\((.*?)\) *\)/g; var rgx3 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s+()()\((.*?)\) *\)/g;
text = text.replace(rgx3, replaceAnchorTag(rgx3, evtRootName, options, globals)); text = text.replace(rgx3, replaceAnchorTagBaseUrl(rgx3, evtRootName, options, globals));
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
@ -222,7 +231,7 @@
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g; var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
text = text.replace(rgx, replaceAnchorTag(rgx, evtRootName, options, globals)); text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
@ -238,7 +247,7 @@
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
var rgx = /\[([^\[\]]+)]()()()()()/g; var rgx = /\[([^\[\]]+)]()()()()()/g;
text = text.replace(rgx, replaceAnchorTag(rgx, evtRootName, options, globals)); text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();

View File

@ -40,7 +40,7 @@ showdown.subParser('makehtml.lists', function (text, options, globals) {
// attacklab: add sentinel to emulate \z // attacklab: add sentinel to emulate \z
listStr += '¨0'; listStr += '¨0';
var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm, var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr)); isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
// Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation, // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
@ -48,7 +48,7 @@ showdown.subParser('makehtml.lists', function (text, options, globals) {
// activating this option reverts to old behavior // activating this option reverts to old behavior
// This will be removed in version 2.0 // This will be removed in version 2.0
if (options.disableForced4SpacesIndentedSublists) { if (options.disableForced4SpacesIndentedSublists) {
rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm; rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
} }
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {

View File

@ -6,18 +6,25 @@
showdown.subParser('makehtml.stripLinkDefinitions', function (text, options, globals) { showdown.subParser('makehtml.stripLinkDefinitions', function (text, options, globals) {
'use strict'; 'use strict';
var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm, var regex = /^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm; base64Regex = /^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += '¨0'; text += '¨0';
var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) { var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
// if there aren't two instances of linkId it must not be a reference link so back out
linkId = linkId.toLowerCase(); linkId = linkId.toLowerCase();
if (text.toLowerCase().split(linkId).length - 1 < 2) {
return wholeMatch;
}
if (url.match(/^data:.+?\/.+?;base64,/)) { if (url.match(/^data:.+?\/.+?;base64,/)) {
// remove newlines // remove newlines
globals.gUrls[linkId] = url.replace(/\s/g, ''); globals.gUrls[linkId] = url.replace(/\s/g, '');
} else { } else {
url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
globals.gUrls[linkId] = showdown.subParser('makehtml.encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive globals.gUrls[linkId] = showdown.subParser('makehtml.encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive
} }

View File

@ -1,8 +1,9 @@
<h1 id="mythings">my things</h1> <h1 id="mythings">my things</h1>
<ul> <ul>
<li>foo</li> <li>foo</li>
<li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"> bar</li> <li>[] bar</li>
<li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"> baz</li> <li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"> baz</li>
<li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;" checked> bazinga</li> <li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;" checked> bazinga</li>
<li class="task-list-item" style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;" checked> bazinga 2</li>
</ul> </ul>
<p>otherthings</p> <p>otherthings</p>

View File

@ -4,5 +4,6 @@
- [] bar - [] bar
- [ ] baz - [ ] baz
- [x] bazinga - [x] bazinga
- [X] bazinga 2
otherthings otherthings

View File

@ -1,2 +1,3 @@
<p>🍎 and 💋</p> <p>🍎 and 💋</p>
<p>💋my🍎</p> <p>💋my🍎</p>
<p>👩‍❤️‍💋‍👨</p>

View File

@ -1,3 +1,5 @@
:apple: and :kiss: :apple: and :kiss:
:kiss:my:apple: :kiss:my:apple:
:couplekiss_man_woman:

View File

@ -1,2 +1,2 @@
<p>this is showdown's emoji <img width="20" height="20" align="absmiddle" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAS1BMVEX///8jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS3b1q3b1q3b1q3b1q3b1q3b1q3b1q3b1q0565CIAAAAGXRSTlMAQHCAYCCw/+DQwPCQUBAwoHCAEP+wwFBgS2fvBgAAAUZJREFUeAHs1cGy7BAUheFFsEDw/k97VTq3T6ge2EmdM+pvrP6Iwd74XV9Kb52xuMU4/uc1YNgZLFOeV8FGdhGrNk5SEgUyPxAEdj4LlMRDyhVAMVEa2M7TBSeVZAFPdqHgzSZJwPKgcLFLAooHDJo4EDCw4gAtBoJA5UFj4Ng5LOGLwVXZuoIlji/jeQHFk7+baHxrCjeUwB9+s88KndvlhcyBN5BSkYNQIVVb4pV+Npm7hhuKDs/uMP5KxT3WzSNNLIuuoDpMmuAVMruMSeDyQBi24DTr43LAY7ILA1QYaWkgfHzFthYYzg67SQsCbB8GhJUEGCtO9n0rSaCLxgJQjS/JSgMTg2eBDEHAJ+H350AsjYNYscrErgI2e/l+mdR967TCX/v6N0EhPECYCP0i+IAoYQOE8BogNhQMEMdrgAQWHaMAAGi5I5euoY9NAAAAAElFTkSuQmCC"></p> <p>this is showdown's emoji <img width="20" height="20" align="absmiddle" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAS1BMVEX///8jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS3b1q3b1q3b1q3b1q3b1q3b1q3b1q3b1q0565CIAAAAGXRSTlMAQHCAYCCw/+DQwPCQUBAwoHCAEP+wwFBgS2fvBgAAAUZJREFUeAHs1cGy7BAUheFFsEDw/k97VTq3T6ge2EmdM+pvrP6Iwd74XV9Kb52xuMU4/uc1YNgZLFOeV8FGdhGrNk5SEgUyPxAEdj4LlMRDyhVAMVEa2M7TBSeVZAFPdqHgzSZJwPKgcLFLAooHDJo4EDCw4gAtBoJA5UFj4Ng5LOGLwVXZuoIlji/jeQHFk7+baHxrCjeUwB9+s88KndvlhcyBN5BSkYNQIVVb4pV+Npm7hhuKDs/uMP5KxT3WzSNNLIuuoDpMmuAVMruMSeDyQBi24DTr43LAY7ILA1QYaWkgfHzFthYYzg67SQsCbB8GhJUEGCtO9n0rSaCLxgJQjS/JSgMTg2eBDEHAJ+H350AsjYNYscrErgI2e/l+mdR967TCX/v6N0EhPECYCP0i+IAoYQOE8BogNhQMEMdrgAQWHaMAAGi5I5euoY9NAAAAAElFTkSuQmCC"></p>
<p>and this is github's emoji <img width="20" height="20" align="absmiddle" src="https://assets-cdn.github.com/images/icons/emoji/octocat.png"></p> <p>and this is github's emoji <img width="20" height="20" align="absmiddle" src="https://github.githubassets.com/images/icons/emoji/octocat.png?v8"></p>

View File

@ -0,0 +1,9 @@
<p><a href="http://my.site.com/that_dude_mike.js">inline relative linky</a></p>
<p><a href="ftp://wikis.com/micky.txt">inline absolute linky</a></p>
<p><a href="http://my.site.com/painters/Michelangelo.html">global relative linky</a></p>
<p><a href="https://www.my-wikis-site.com/peeps/Michelangelo.html">global absolute linky</a></p>
<p><img src="http://my.site.com/mona-lisa.png" alt="inline relative image" /></p>
<p><img src="http://images.com/mona-lisa.png" alt="inline absolute image" /></p>
<p><img src="http://my.site.com/mona-lisa.png" alt="global relative image" /></p>
<p><img src="https://www.my-photo-site.com/mona-lisa.png" alt="global absolute image" /></p>
<p><a href="#holdin_it_down">just an anchor</a></p>

View File

@ -0,0 +1,22 @@
[inline relative linky](that_dude_mike.js)
[inline absolute linky](ftp://wikis.com/micky.txt)
[global relative linky][relative_linky]
[global absolute linky][absolute_linky]
![inline relative image](mona-lisa.png)
![inline absolute image](http://images.com/mona-lisa.png)
![global relative image][relative_image]
![global absolute image][absolute_image]
[just an anchor](#holdin_it_down)
[relative_linky]: painters/Michelangelo.html
[relative_image]: ./mona-lisa.png
[absolute_linky]: https://www.my-wikis-site.com/peeps/Michelangelo.html
[absolute_image]: https://www.my-photo-site.com/mona-lisa.png

View File

@ -0,0 +1 @@
<p><em>one</em> <em>two</em> <em>three</em></p>

View File

@ -0,0 +1 @@
*one* *two* *three*

View File

@ -0,0 +1,3 @@
<p>[We] are going to show [you]: sunshine!</p>
<p>[x]: take out the garbage<br />
[ ]: bring up the coal</p>

View File

@ -0,0 +1,4 @@
[We] are going to show [you]: sunshine!
[x]: take out the garbage
[ ]: bring up the coal

View File

@ -0,0 +1,5 @@
<p><a href="#Declare">Declare options</a></p>
<p><a href="#Declare%20current%20operation%20options">Declare options</a></p>
<p><a href="#Declare current operation options">Declare options</a></p>
<p><a href="https://spec.commonmark.org/0.30/#example-500">Common Mark Example</a></p>
<p><a href="spec.commonmark.org/0.30/#example-500">Common Mark Example</a></p>

View File

@ -0,0 +1,9 @@
[Declare options](#Declare)
[Declare options](#Declare%20current%20operation%20options)
[Declare options](<#Declare current operation options>)
[Common Mark Example](https://spec.commonmark.org/0.30/#example-500)
[Common Mark Example](spec.commonmark.org/0.30/#example-500)

View File

@ -0,0 +1,67 @@
<p>Reserved Keywords found at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar</p>
<p><a href="">break</a></p>
<p><a href="">case</a></p>
<p><a href="">catch</a></p>
<p><a href="">class</a></p>
<p><a href="">const</a></p>
<p><a href="">continue</a></p>
<p><a href="">debugger</a></p>
<p><a href="">default</a></p>
<p><a href="">delete</a></p>
<p><a href="">do</a></p>
<p><a href="">else</a></p>
<p><a href="">export</a></p>
<p><a href="">extends</a></p>
<p><a href="">finally</a></p>
<p><a href="">for</a></p>
<p><a href="">function</a></p>
<p><a href="">if</a></p>
<p><a href="">import</a></p>
<p><a href="">in</a></p>
<p><a href="">instanceof</a></p>
<p><a href="">new</a></p>
<p><a href="">return</a></p>
<p><a href="">super</a></p>
<p><a href="">switch</a></p>
<p><a href="">this</a></p>
<p><a href="">throw</a></p>
<p><a href="">try</a></p>
<p><a href="">typeof</a></p>
<p><a href="">var</a></p>
<p><a href="">void</a></p>
<p><a href="">while</a></p>
<p><a href="">with</a></p>
<p><a href="">yield</a></p>
<p><a href="">enum</a></p>
<p><a href="">implements</a></p>
<p><a href="">interface</a></p>
<p><a href="">let</a></p>
<p><a href="">package</a></p>
<p><a href="">private</a></p>
<p><a href="">protected</a></p>
<p><a href="">public</a></p>
<p><a href="">static</a></p>
<p><a href="">yield</a></p>
<p><a href="">await</a></p>
<p><a href="">abstract</a></p>
<p><a href="">boolean</a></p>
<p><a href="">byte</a></p>
<p><a href="">char</a></p>
<p><a href="">double</a></p>
<p><a href="">final</a></p>
<p><a href="">float</a></p>
<p><a href="">goto</a></p>
<p><a href="">int</a></p>
<p><a href="">long</a></p>
<p><a href="">native</a></p>
<p><a href="">short</a></p>
<p><a href="">synchronized</a></p>
<p><a href="">throws</a></p>
<p><a href="">transient</a></p>
<p><a href="">volatile</a></p>
<p><a href="">null</a></p>
<p><a href="">true</a></p>
<p><a href="">false</a></p>
<p><a href="">arguments</a></p>
<p><a href="">get</a></p>
<p><a href="">set</a></p>

View File

@ -0,0 +1,133 @@
Reserved Keywords found at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar
[break]()
[case]()
[catch]()
[class]()
[const]()
[continue]()
[debugger]()
[default]()
[delete]()
[do]()
[else]()
[export]()
[extends]()
[finally]()
[for]()
[function]()
[if]()
[import]()
[in]()
[instanceof]()
[new]()
[return]()
[super]()
[switch]()
[this]()
[throw]()
[try]()
[typeof]()
[var]()
[void]()
[while]()
[with]()
[yield]()
[enum]()
[implements]()
[interface]()
[let]()
[package]()
[private]()
[protected]()
[public]()
[static]()
[yield]()
[await]()
[abstract]()
[boolean]()
[byte]()
[char]()
[double]()
[final]()
[float]()
[goto]()
[int]()
[long]()
[native]()
[short]()
[synchronized]()
[throws]()
[transient]()
[volatile]()
[null]()
[true]()
[false]()
[arguments]()
[get]()
[set]()

View File

@ -0,0 +1,12 @@
<p><a href="/uri">link</a></p>
<p><a href="http://example.com/">link</a></p>
<p><a href="http://example.com">link</a></p>
<p><a href="https://example.com">link</a></p>
<p><a href="https://example.com/">link</a></p>
<p><a href="example.com">link</a></p>
<p><a href="www.example.com">link</a></p>
<p><a href="file://example.com">link</a></p>
<p><a href="file://www.example.com">link</a></p>
<p><a href="example.jpg">link</a></p>
<p><a href="example.io">link</a></p>
<p><a href="http://baidu.com" title="百度">百度</a></p>

View File

@ -0,0 +1,23 @@
[link](/uri)
[link](http://example.com/)
[link](http://example.com)
[link](https://example.com)
[link](https://example.com/)
[link](example.com)
[link](www.example.com)
[link](file://example.com)
[link](file://www.example.com)
[link](example.jpg)
[link](example.io)
[百度](http://baidu.com "百度")

View File

@ -0,0 +1,46 @@
<p>Object property names found at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object</p>
<p><a href="">assign</a></p>
<p><a href="">create</a></p>
<p><a href="">defineProperty</a></p>
<p><a href="">defineProperties</a></p>
<p><a href="">entries</a></p>
<p><a href="">freeze</a></p>
<p><a href="">fromEntries</a></p>
<p><a href="">getOwnPropertyDescriptor</a></p>
<p><a href="">getOwnPropertyDescriptors</a></p>
<p><a href="">getOwnPropertyNames</a></p>
<p><a href="">getOwnPropertySymbols</a></p>
<p><a href="">getPrototypeOf</a></p>
<p><a href="">is</a></p>
<p><a href="">isExtensible</a></p>
<p><a href="">isFrozen</a></p>
<p><a href="">isSealed</a></p>
<p><a href="">keys</a></p>
<p><a href="">preventExtensions</a></p>
<p><a href="">seal</a></p>
<p><a href="">setPrototypeOf</a></p>
<p><a href="">values</a></p>
<p><a href="">prototype.constructor</a></p>
<p><a href="">prototype.<strong>proto</strong></a></p>
<p><a href="">prototype.<strong>defineGetter</strong></a></p>
<p><a href="">prototype.<strong>defineSetter</strong></a></p>
<p><a href="">prototype.<strong>lookupGetter</strong></a></p>
<p><a href="">prototype.<strong>lookupSetter</strong></a></p>
<p><a href="">prototype.hasOwnProperty</a></p>
<p><a href="">prototype.isPrototypeOf</a></p>
<p><a href="">prototype.propertyIsEnumerable</a></p>
<p><a href="">prototype.toLocaleString</a></p>
<p><a href="">prototype.toString</a></p>
<p><a href="">prototype.valueOf</a></p>
<p><a href="">constructor</a></p>
<p><a href=""><strong>proto</strong></a></p>
<p><a href=""><strong>defineGetter</strong></a></p>
<p><a href=""><strong>defineSetter</strong></a></p>
<p><a href=""><strong>lookupGetter</strong></a></p>
<p><a href=""><strong>lookupSetter</strong></a></p>
<p><a href="">hasOwnProperty</a></p>
<p><a href="">isPrototypeOf</a></p>
<p><a href="">propertyIsEnumerable</a></p>
<p><a href="">toLocaleString</a></p>
<p><a href="">toString</a></p>
<p><a href="">valueOf</a></p>

View File

@ -0,0 +1,91 @@
Object property names found at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[assign]()
[create]()
[defineProperty]()
[defineProperties]()
[entries]()
[freeze]()
[fromEntries]()
[getOwnPropertyDescriptor]()
[getOwnPropertyDescriptors]()
[getOwnPropertyNames]()
[getOwnPropertySymbols]()
[getPrototypeOf]()
[is]()
[isExtensible]()
[isFrozen]()
[isSealed]()
[keys]()
[preventExtensions]()
[seal]()
[setPrototypeOf]()
[values]()
[prototype.constructor]()
[prototype.__proto__]()
[prototype.__defineGetter__]()
[prototype.__defineSetter__]()
[prototype.__lookupGetter__]()
[prototype.__lookupSetter__]()
[prototype.hasOwnProperty]()
[prototype.isPrototypeOf]()
[prototype.propertyIsEnumerable]()
[prototype.toLocaleString]()
[prototype.toString]()
[prototype.valueOf]()
[constructor]()
[__proto__]()
[__defineGetter__]()
[__defineSetter__]()
[__lookupGetter__]()
[__lookupSetter__]()
[hasOwnProperty]()
[isPrototypeOf]()
[propertyIsEnumerable]()
[toLocaleString]()
[toString]()
[valueOf]()

View File

@ -55,3 +55,7 @@
<h2>Secondary title</h2> <h2>Secondary title</h2>
</hgroup> </hgroup>
<output name="result"></output> <output name="result"></output>
<details>
<summary>Summarise me</summary>
<p>Explain the details</p>
</details>

View File

@ -66,3 +66,8 @@ the end
</hgroup> </hgroup>
<output name="result"></output> <output name="result"></output>
<details>
<summary>Summarise me</summary>
<p>Explain the details</p>
</details>

View File

@ -18,7 +18,10 @@ var bootstrap = require('./makehtml.bootstrap.js'),
//literalMidWordAsterisksSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/literalMidWordAsterisks/'), //literalMidWordAsterisksSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/literalMidWordAsterisks/'),
completeHTMLOutputSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/completeHTMLOutput/'), completeHTMLOutputSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/completeHTMLOutput/'),
metadataSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/metadata/'), metadataSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/metadata/'),
splitAdjacentBlockquotesSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/splitAdjacentBlockquotes/'); splitAdjacentBlockquotesSuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/splitAdjacentBlockquotes/'),
http = require('http'),
https = require('https'),
expect = require('chai').expect;
describe('makeHtml() features testsuite', function () { describe('makeHtml() features testsuite', function () {
'use strict'; 'use strict';
@ -96,6 +99,8 @@ describe('makeHtml() features testsuite', function () {
converter = new showdown.Converter({openLinksInNewWindow: true}); converter = new showdown.Converter({openLinksInNewWindow: true});
} else if (testsuite[i].name === '#355.simplifiedAutoLink-URLs-inside-parenthesis-followed-by-another-character-are-not-parsed-correctly') { } else if (testsuite[i].name === '#355.simplifiedAutoLink-URLs-inside-parenthesis-followed-by-another-character-are-not-parsed-correctly') {
converter = new showdown.Converter({simplifiedAutoLink: true}); converter = new showdown.Converter({simplifiedAutoLink: true});
} else if (testsuite[i].name === 'relativePathBaseUrl') {
converter = new showdown.Converter({relativePathBaseUrl: 'http://my.site.com/'});
} else { } else {
converter = new showdown.Converter(); converter = new showdown.Converter();
} }
@ -188,7 +193,31 @@ describe('makeHtml() features testsuite', function () {
/** test emojis support **/ /** test emojis support **/
describe('emojis support', function () { describe('emojis support', function () {
var converter, var converter,
suite = emojisSuite; suite = emojisSuite,
imgSrcRegexp = /<img[^>]+src=("https?:\/\/[^"]+"|'https?:\/\/[^']+')/g;
function testImageUrlExists (imgUrl) {
// Strip the quotes
imgUrl = imgUrl.substr(0, imgUrl.length - 1).substr(1);
return function (done) {
(imgUrl.startsWith('http://') ? http : https).get(imgUrl, function (res) {
expect(res.statusCode).to.equal(200);
// Make sure we get some data and that it's a png
expect(parseInt(res.headers['content-length'], 10)).to.be.above(0);
expect(res.headers['content-type']).to.equal('image/png');
// Discard the data (but fetch it)
res.on('data', function () {});
res.on('end', function () {
done();
});
}).on('error', function (e) {
throw e;
});
};
}
for (var i = 0; i < suite.length; ++i) { for (var i = 0; i < suite.length; ++i) {
if (suite[i].name === 'simplifiedautolinks') { if (suite[i].name === 'simplifiedautolinks') {
converter = new showdown.Converter({emoji: true, simplifiedAutoLink: true}); converter = new showdown.Converter({emoji: true, simplifiedAutoLink: true});
@ -197,6 +226,11 @@ describe('makeHtml() features testsuite', function () {
} }
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter)); it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
var imgUrl = imgSrcRegexp.exec(suite[i].expected);
if (imgUrl) {
it('should use a working emoji URL', testImageUrlExists(imgUrl[1]));
}
} }
}); });

View File

@ -32,3 +32,7 @@
<h2>Secondary title</h2> <h2>Secondary title</h2>
</hgroup> </hgroup>
<output name="result"></output> <output name="result"></output>
<details>
<summary>Summarise me</summary>
<p>Explain the details</p>
</details>

View File

@ -31,3 +31,5 @@ the end
<hgroup><h1>Main title</h1><h2>Secondary title</h2></hgroup> <hgroup><h1>Main title</h1><h2>Secondary title</h2></hgroup>
<output name="result"></output> <output name="result"></output>
<details><summary>Summarise me</summary><p>Explain the details</p></details>

View File

@ -122,6 +122,14 @@ describe('showdown.Converter', function () {
converter.getAllExtensions().language.should.contain(extObjMock); converter.getAllExtensions().language.should.contain(extObjMock);
showdown.resetExtensions(); showdown.resetExtensions();
}); });
it('removeExtension() should remove an added extension', function () {
var converter = new showdown.Converter();
converter.addExtension(extObjMock);
converter.removeExtension(extObjMock);
converter.getAllExtensions().language.should.not.contain(extObjMock);
});
}); });
describe('events', function () { describe('events', function () {