mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
Compare commits
247 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
95255984ad | ||
|
1165736eef | ||
|
1a24a4c24c | ||
|
df814d8771 | ||
|
5aac76dbbb | ||
|
4098c2806c | ||
|
4e577e846b | ||
|
440170aadc | ||
|
db5ce64172 | ||
|
9e5d0f3f11 | ||
|
3e1731f1fe | ||
|
1c81fa6475 | ||
|
fdf9272af6 | ||
|
5c6c4659ca | ||
|
019b137aa8 | ||
|
947ec9bdd4 | ||
|
3c2e25a3c7 | ||
|
3a616c5bf6 | ||
|
1cf1908935 | ||
|
141c23793b | ||
|
ef726d79a4 | ||
|
fae04b6549 | ||
|
c4eef0b8c1 | ||
|
f3d73f5b54 | ||
|
4a887331c9 | ||
|
16cac70aee | ||
|
fc01abd708 | ||
|
565bbdc94a | ||
|
a9ee330ac2 | ||
|
e098ec684c | ||
|
010f94fd04 | ||
|
d11e5cf6e0 | ||
|
bdddfb9131 | ||
|
568445fa43 | ||
|
6e6dde5c55 | ||
|
8d9b8f2f92 | ||
|
a1bb3e0a9b | ||
|
24016bfd0b | ||
|
5efc75d894 | ||
|
b3dac51d9e | ||
|
8a959a6ddd | ||
|
29538dda96 | ||
|
2715e76471 | ||
|
eb4b78ac66 | ||
|
cd83919bff | ||
|
fab6987e56 | ||
|
f37113adca | ||
|
6a86880bbe | ||
|
788e329d6e | ||
|
17bcd56eae | ||
|
0839b3a556 | ||
|
c45d66d974 | ||
|
f85d82fb23 | ||
|
691487f8c0 | ||
|
b0bb58d954 | ||
|
1f15a18adb | ||
|
9a3e714b2c | ||
|
db571fbaac | ||
|
5fc843e175 | ||
|
949c2bcf86 | ||
|
0d3ca4da5a | ||
|
3eff10bbbf | ||
|
a5f3add2b6 | ||
|
a946f557a2 | ||
|
314808aa3e | ||
|
53aa1cc79f | ||
|
223017e218 | ||
|
7788ba2bc0 | ||
|
d02fe7caf9 | ||
|
b8fb79d85f | ||
|
bad26cd2ed | ||
|
9ddb19479c | ||
|
6397c203f6 | ||
|
95eb3b4168 | ||
|
4f0f76ca76 | ||
|
925bbceab4 | ||
|
15b159c846 | ||
|
f2f30349d6 | ||
|
490f4eb562 | ||
|
df76f984a3 | ||
|
5f5304ccaa | ||
|
849ed40b70 | ||
|
169cd1ed89 | ||
|
14dbda2dcf | ||
|
1f4a9885cc | ||
|
7acd65e498 | ||
|
f87cf01282 | ||
|
54a54271ec | ||
|
1a3b8db8cd | ||
|
c7dd148b08 | ||
|
c3411a567d | ||
|
bd093ab493 | ||
|
3b2a3029b4 | ||
|
16ad404b85 | ||
|
3e1a815e18 | ||
|
8cecdf0382 | ||
|
1615b3ea15 | ||
|
b02c1dd31b | ||
|
5e0ed809db | ||
|
67114255ad | ||
|
5a84b1cdf0 | ||
|
85caa462de | ||
|
9f0b36d95b | ||
|
626f661e8e | ||
|
cb2e4853b7 | ||
|
1967652acf | ||
|
ebc730c0a0 | ||
|
3fd1ea6d7f | ||
|
6f93b3eab1 | ||
|
e24d06e265 | ||
|
6ded499b91 | ||
|
d23b028bb3 | ||
|
80fbe08874 | ||
|
4f1bc668c4 | ||
|
d54a6101b9 | ||
|
17837832b4 | ||
|
def6f0b453 | ||
|
838c5dbecc | ||
|
ed51972315 | ||
|
41cee10659 | ||
|
5d699c228e | ||
|
9f779b36a8 | ||
|
005e83262c | ||
|
ac1047815f | ||
|
75ac5c9b6e | ||
|
8e2b339fe2 | ||
|
5544e4d995 | ||
|
f9d9705d3c | ||
|
735c5d2a55 | ||
|
d5cc678941 | ||
|
6efd75cb83 | ||
|
45fcc8435b | ||
|
965789589e | ||
|
ea72183ad7 | ||
|
ae1d5db9e0 | ||
|
e3a5b5928f | ||
|
24211cc907 | ||
|
9c043d8848 | ||
|
3d5391e96a | ||
|
9c362e832e | ||
|
cb689aa23b | ||
|
775f2abe97 | ||
|
a9f38b6f05 | ||
|
dae65c6e36 | ||
|
fe5d12525f | ||
|
7664beae70 | ||
|
caab5bb7bc | ||
|
5d494c8202 | ||
|
260d889427 | ||
|
f0d37ad3c5 | ||
|
001b1881d0 | ||
|
5f85c53910 | ||
|
33bba54535 | ||
|
cfca8cbce6 | ||
|
5bac7e0564 | ||
|
201969473a | ||
|
4af13e5e76 | ||
|
bd927b5b7a | ||
|
a64a18bef8 | ||
|
82f90ebda2 | ||
|
66798912de | ||
|
8a8f961bc4 | ||
|
bb4db60452 | ||
|
b08f9a04a0 | ||
|
bd6beeaf0d | ||
|
68733e6684 | ||
|
05e8b22917 | ||
|
5e5b96c26a | ||
|
a488b4728f | ||
|
0ebef28c66 | ||
|
74e77369e0 | ||
|
80f5c7e703 | ||
|
6ee6d8c9d9 | ||
|
4378abb4fa | ||
|
e0fc6f8d6b | ||
|
054829b2dc | ||
|
fc1717780e | ||
|
81edc70da7 | ||
|
db69d02974 | ||
|
dc03c7e647 | ||
|
4da93e8e13 | ||
|
d3ebff7ef0 | ||
|
798bbe6751 | ||
|
61aca9a7d6 | ||
|
f46479b4ee | ||
|
d309b5b1c1 | ||
|
5ed36a508c | ||
|
f4c018ffca | ||
|
73cf218f25 | ||
|
f24e314a70 | ||
|
f874043725 | ||
|
48de9101f4 | ||
|
d1ae7b2bc5 | ||
|
18c4b6ebac | ||
|
6259f37bd6 | ||
|
26abc7a795 | ||
|
d9eea64794 | ||
|
a4be301331 | ||
|
c7a89eaae4 | ||
|
2ba00751cc | ||
|
b0d475fc08 | ||
|
63763b136f | ||
|
fc7ac1e1ca | ||
|
0c6b5bdc4b | ||
|
05ef5c55dc | ||
|
686d3dd146 | ||
|
baf325f120 | ||
|
1f0242c6ea | ||
|
e80a548c9b | ||
|
0d5c9df1a8 | ||
|
62270d4a4b | ||
|
358947bd29 | ||
|
3d51939b4e | ||
|
a54ed664f7 | ||
|
0c6ce94673 | ||
|
7628471928 | ||
|
fc43100286 | ||
|
b53f71bdf3 | ||
|
099a6da5da | ||
|
f572d368ed | ||
|
49ed464183 | ||
|
d915c837d9 | ||
|
8366b6cb48 | ||
|
55f22de0a0 | ||
|
699bb625ce | ||
|
f20dc75024 | ||
|
039dd66256 | ||
|
2c1062cb52 | ||
|
4e64a20463 | ||
|
706813d9bc | ||
|
6376d40584 | ||
|
ac23b8cb72 | ||
|
410b453049 | ||
|
17222b3d5a | ||
|
173ad51b05 | ||
|
13ba2675fe | ||
|
be2a10797c | ||
|
94aa3e53ca | ||
|
a42c2d7ed3 | ||
|
19d2e9f9d4 | ||
|
ea3db5f180 | ||
|
9825fd2bd0 | ||
|
3db9200d2c | ||
|
eec56fb1c0 | ||
|
248254c99a | ||
|
5bb009702b | ||
|
e5d7705569 |
|
@ -1,42 +0,0 @@
|
|||
# branches to build
|
||||
#branches:
|
||||
# whitelist
|
||||
#only:
|
||||
# - master
|
||||
|
||||
# What combinations to test
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "12"
|
||||
platform: x64
|
||||
- nodejs_version: "12"
|
||||
platform: x86
|
||||
- nodejs_version: "14"
|
||||
platform: x86
|
||||
- nodejs_version: "14"
|
||||
platform: x64
|
||||
- nodejs_version: "16"
|
||||
platform: x86
|
||||
- nodejs_version: "16"
|
||||
platform: x64
|
||||
|
||||
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
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}],
|
||||
"curly": [2, "all"],
|
||||
|
|
63
.github/workflows/browserstack.yml
vendored
Normal file
63
.github/workflows/browserstack.yml
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
name: 'BrowserStack Test'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
ubuntu-job:
|
||||
name: 'BrowserStack Test on Ubuntu'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: set up env vars
|
||||
# Only the first line of commit msg
|
||||
run: echo "COMMIT_MSG=$(printf "%s" "${{ github.event.head_commit.message }}" | head -n 1)" >> $GITHUB_ENV
|
||||
|
||||
- name: '📦 Checkout the repository'
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: '🚚 Upgrade NPM'
|
||||
run: npm install -g npm
|
||||
|
||||
- name: '⚙ Setup Node.js v17.x'
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 17.x
|
||||
cache: 'npm'
|
||||
|
||||
- name: '📖 Get current package version'
|
||||
id: package-version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
|
||||
- name: '📝 Print build version and commit msg'
|
||||
run: 'printf "version: %s\n build:%s\n message:%s\n" "${{ steps.package-version.outputs.current-version}}" "${{ github.run_id }}" "$COMMIT_MSG"'
|
||||
|
||||
- name: '📱 BrowserStack Env Setup' # Invokes the setup-env action
|
||||
uses: browserstack/github-actions/setup-env@master
|
||||
with:
|
||||
username: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
access-key: ${{ secrets.BROWSERSTACK_ACCESSKEY }}
|
||||
project-name: 'showdown'
|
||||
build-name: ${{ steps.package-version.outputs.current-version}}-${{ github.run_id }}
|
||||
|
||||
- name: '🚇 BrowserStack Local Tunnel Setup' # Invokes the setup-local action
|
||||
uses: browserstack/github-actions/setup-local@master
|
||||
with:
|
||||
local-testing: start
|
||||
local-identifier: random
|
||||
|
||||
- name: '🚚 Install dependencies for CI'
|
||||
run: npm ci
|
||||
|
||||
- name: '🏗 Building src files for testing'
|
||||
run: npx grunt concat:test
|
||||
|
||||
- name: '✅ Running test on BrowserStack with Karma'
|
||||
run: npx karma start karma.browserstack.js
|
||||
|
||||
- name: '🛑 BrowserStackLocal Stop' # Terminating the BrowserStackLocal tunnel connection
|
||||
uses: browserstack/github-actions/setup-local@master
|
||||
with:
|
||||
local-testing: stop
|
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master, develop ]
|
||||
schedule:
|
||||
- cron: '39 3 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
31
.github/workflows/docs.yml
vendored
Normal file
31
.github/workflows/docs.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'mkdocs.yml'
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs.yml'
|
||||
|
||||
jobs:
|
||||
build_docs:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Trigger external build
|
||||
env:
|
||||
TOKEN: ${{ secrets.DOCS_DEPLOY_KEY }}
|
||||
COMMIT: ${{ github.event.head_commit.id }}
|
||||
COMMITTER: ${{ github.event.head_commit.author.username }}
|
||||
OWNER: showdownjs
|
||||
REPO: showdownjs.github.io
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/${OWNER}/${REPO}/dispatches \
|
||||
-d '{ "event_type": "e: \"'"${COMMIT}"'\" by '"${COMMITTER}"'", "client_payload": { "source": "showdown" } }'
|
34
.github/workflows/node.linux.yml
vendored
Normal file
34
.github/workflows/node.linux.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 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, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
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: 🚚 Upgrade NPM
|
||||
run: npm install -g npm
|
||||
|
||||
- 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
|
|
@ -1,18 +1,18 @@
|
|||
# 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.js CI
|
||||
name: Node Windows CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
/*.test.*
|
||||
*.log
|
||||
|
|
|
@ -3,3 +3,4 @@ dist/**/*.js
|
|||
build/**/*.js
|
||||
src/options.js
|
||||
bin/*
|
||||
/karma.browserstack.js
|
||||
|
|
|
@ -14,15 +14,15 @@
|
|||
"quotmark": "single",
|
||||
"undef": false,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"onevar": true,
|
||||
"globals": {
|
||||
"angular": true,
|
||||
"module": true,
|
||||
"define": true,
|
||||
"window": true,
|
||||
"document": true,
|
||||
"showdown": true
|
||||
}
|
||||
}
|
||||
|
|
62
CHANGELOG.md
62
CHANGELOG.md
|
@ -1,3 +1,27 @@
|
|||
## [2.1.0](https://github.com/showdownjs/showdown/compare/2.0.0...2.1.0) (2022-04-21)
|
||||
|
||||
* refactor(cli)!: Remove support for "extra options" and add -c flag, closes [#916](https://github.com/showdownjs/showdown/issues/916)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** cli displays the correct version number ([8b48882](https://github.com/showdownjs/showdown/commit/8b48882))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* the CLI no longer accepts "extra options". Instead you should pass the `-c` flag. To update:
|
||||
|
||||
before:
|
||||
```
|
||||
showdown makehtml -i foo.md -o bar.html --strikethrough --emoji
|
||||
```
|
||||
|
||||
after:
|
||||
```
|
||||
showdown makehtml -i foo.md -o bar.html -c strikethrough -c emoji
|
||||
```
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/showdownjs/showdown/compare/1.9.1...2.0.0) (2022-02-15)
|
||||
|
||||
|
@ -25,43 +49,35 @@
|
|||
|
||||
* **openLinksInNewWindow:** add rel="noopener noreferrer" to links ([1cd281f](https://github.com/showdownjs/showdown/commit/1cd281f)), closes [#670](https://github.com/showdownjs/showdown/issues/670)
|
||||
|
||||
|
||||
|
||||
<a name="1.9.0"></a>
|
||||
<a name="1.0.0"></a>
|
||||
# [1.9.0](https://github.com/showdownjs/showdown/compare/1.8.7...1.9.0) (2018-11-10)
|
||||
|
||||
|
||||
Version 1.9.0 introduces a new feature, the Markdown to HTML converter. This feature is still experimental and is a partial backport of the new Reverse Converter planned for version 2.0.
|
||||
### Bug Fixes
|
||||
|
||||
* **italicsAndBold:** fix issue with consecutive spans ([#608](https://github.com/showdownjs/showdown/issues/608)) ([5c0d67e](https://github.com/showdownjs/showdown/commit/5c0d67e)), closes [#544](https://github.com/showdownjs/showdown/issues/544)
|
||||
* **underline**: fix issue with consecutive spans ([81edc70](https://github.com/showdownjs/showdown/commit/81edc70))
|
||||
* **underline:** fix issue with consecutive spans ([81edc70](https://github.com/showdownjs/showdown/commit/81edc70))
|
||||
|
||||
### Features
|
||||
|
||||
* **converter.makeMarkdown:** [EXPERIMENTAL] add an HTML to MD converter ([e4b0e69](https://github.com/showdownjs/showdown/commit/e4b0e69)), closes [#388](https://github.com/showdownjs/showdown/issues/388) [#233](https://github.com/showdownjs/showdown/issues/233)
|
||||
|
||||
|
||||
|
||||
<a name="1.8.7"></a>
|
||||
## [1.8.7](https://github.com/showdownjs/showdown/compare/1.8.6...1.8.7) (2018-10-16)
|
||||
|
||||
# [1.8.7](https://github.com/showdownjs/showdown/compare/1.8.6...1.8.7) (2018-10-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **emojis:** fix emoji excessive size ([4aca41c](https://github.com/showdownjs/showdown/commit/4aca41c))
|
||||
* **gfm-codeblocks:**
|
||||
* add support for spaces before language declaration ([24bf7b1](https://github.com/showdownjs/showdown/commit/24bf7b1)), closes [#569](https://github.com/showdownjs/showdown/issues/569)
|
||||
* leading space no longer breaks gfm codeblocks ([828c32f](https://github.com/showdownjs/showdown/commit/828c32f)), closes [#523](https://github.com/showdownjs/showdown/issues/523)
|
||||
* **gfm-codeblocks:** add support for spaces before language declaration ([24bf7b1](https://github.com/showdownjs/showdown/commit/24bf7b1)), closes [#569](https://github.com/showdownjs/showdown/issues/569)
|
||||
leading space no longer breaks gfm codeblocks ([828c32f](https://github.com/showdownjs/showdown/commit/828c32f)), closes [#523](https://github.com/showdownjs/showdown/issues/523)
|
||||
|
||||
* **images:** fix js error when using image references ([980e702](https://github.com/showdownjs/showdown/commit/980e702)), closes [#585](https://github.com/showdownjs/showdown/issues/585)
|
||||
* **literalMidWordAsterisks:** now parses single characters enclosed by * correctly ([fe70e45](https://github.com/showdownjs/showdown/commit/fe70e45)), closes [#478](https://github.com/showdownjs/showdown/issues/478)
|
||||
* **mentions:** allow for usernames with dot, underscore and dash ([dfeb1e2](https://github.com/showdownjs/showdown/commit/dfeb1e2)), closes [#574](https://github.com/showdownjs/showdown/issues/574)
|
||||
* **nbsp:** fix replacing of nbsp with regular spaces ([8bc1f42](https://github.com/showdownjs/showdown/commit/8bc1f42))
|
||||
|
||||
|
||||
|
||||
<a name="1.8.6"></a>
|
||||
## [1.8.6](https://github.com/showdownjs/showdown/compare/1.8.5...1.8.6) (2017-12-22)
|
||||
|
||||
# [1.8.6](https://github.com/showdownjs/showdown/compare/1.8.5...1.8.6) (2017-12-22)
|
||||
|
||||
### Features
|
||||
|
||||
|
@ -123,7 +139,7 @@
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
* **CDNjs:** bump version to fix version missmatch with CDNjs ([#452](https://github.com/showdownjs/showdown/issues/452))
|
||||
* **CDNjs:** bump version to fix version mismatch with CDNjs ([#452](https://github.com/showdownjs/showdown/issues/452))
|
||||
|
||||
|
||||
<a name="1.8.0"></a>
|
||||
|
@ -249,7 +265,7 @@ list output may differ.
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
* **html-comments:** changed regex to precent malformed long comment to freeze showdown ([3efcd10](https://github.com/showdownjs/showdown/commit/3efcd10)), closes [#439](https://github.com/showdownjs/showdown/issues/439)
|
||||
* **html-comments:** changed regex to prevent malformed long comment to freeze showdown ([3efcd10](https://github.com/showdownjs/showdown/commit/3efcd10)), closes [#439](https://github.com/showdownjs/showdown/issues/439)
|
||||
|
||||
|
||||
|
||||
|
@ -294,7 +310,7 @@ list output may differ.
|
|||
|
||||
* **githubMentions:** githubMentions now works with openLinksInNewWindow options ([1194d88](https://github.com/showdownjs/showdown/commit/1194d88)), closes [#403](https://github.com/showdownjs/showdown/issues/403)
|
||||
* **lists:** fix multi paragraph lists with sublists ([a2259c0](https://github.com/showdownjs/showdown/commit/a2259c0)), closes [#397](https://github.com/showdownjs/showdown/issues/397)
|
||||
* **tablesHeaderId:** fix missmatch of option name ([51e4693](https://github.com/showdownjs/showdown/commit/51e4693)), closes [#412](https://github.com/showdownjs/showdown/issues/412)
|
||||
* **tablesHeaderId:** fix mismatch of option name ([51e4693](https://github.com/showdownjs/showdown/commit/51e4693)), closes [#412](https://github.com/showdownjs/showdown/issues/412)
|
||||
|
||||
|
||||
### Features
|
||||
|
@ -374,7 +390,7 @@ Important HOTFIX
|
|||
* **italicsAndBold:** fix inconsistency in italicsAndBold parsing ([a4f05d4](https://github.com/showdownjs/showdown/commit/a4f05d4)), closes [#332](https://github.com/showdownjs/showdown/issues/332)
|
||||
* **literalMidWordUnderscores:** fix inconsistent behavior of emphasis and strong with literalMidWordUndescores ([0292ae0](https://github.com/showdownjs/showdown/commit/0292ae0)), closes [#333](https://github.com/showdownjs/showdown/issues/333)
|
||||
* **paragraphs:** fix empty lines generating empty paragraphs ([54bf744](https://github.com/showdownjs/showdown/commit/54bf744)), closes [#334](https://github.com/showdownjs/showdown/issues/334)
|
||||
* **strikethrough:** fix striketrough being wrongly parsed inside codeSpans ([169cbe8](https://github.com/showdownjs/showdown/commit/169cbe8))
|
||||
* **strikethrough:** fix strikethrough being wrongly parsed inside codeSpans ([169cbe8](https://github.com/showdownjs/showdown/commit/169cbe8))
|
||||
|
||||
### Features
|
||||
|
||||
|
@ -389,7 +405,7 @@ Important HOTFIX
|
|||
### Bug Fixes
|
||||
|
||||
* **escapeSpecialCharsWithinTagAttributes:** add ~ and = to escaped chars ([bfcc0e4](https://github.com/showdownjs/showdown/commit/bfcc0e4))
|
||||
* **strikethrough:** allow escapinging tilde char ([24d47d7](https://github.com/showdownjs/showdown/commit/24d47d7)), closes [#331](https://github.com/showdownjs/showdown/issues/331)
|
||||
* **strikethrough:** allow escaping tilde char ([24d47d7](https://github.com/showdownjs/showdown/commit/24d47d7)), closes [#331](https://github.com/showdownjs/showdown/issues/331)
|
||||
|
||||
### Features
|
||||
|
||||
|
@ -482,7 +498,7 @@ This release also improves performance a bit (around 8%)
|
|||
### Bug Fixes
|
||||
|
||||
* **listeners:** fix listeners typo ([f0d25b7](https://github.com/showdownjs/showdown/commit/f0d25b7)), closes [#290](https://github.com/showdownjs/showdown/issues/290)
|
||||
* **lists:** lines with mutiple dashes being parsed as multilists ([10b3410](https://github.com/showdownjs/showdown/commit/10b3410)), closes [#312](https://github.com/showdownjs/showdown/issues/312)
|
||||
* **lists:** lines with multiple dashes being parsed as multilists ([10b3410](https://github.com/showdownjs/showdown/commit/10b3410)), closes [#312](https://github.com/showdownjs/showdown/issues/312)
|
||||
* **nbsp:** nbsp are replaced with simple spaces ([6e90f7c](https://github.com/showdownjs/showdown/commit/6e90f7c))
|
||||
|
||||
|
||||
|
@ -525,7 +541,7 @@ This release also improves performance a bit (around 8%)
|
|||
* three
|
||||
```
|
||||
|
||||
Before (ouput):
|
||||
Before (output):
|
||||
```html
|
||||
<ul>
|
||||
<li>one
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
Credits
|
||||
=======
|
||||
|
||||
- Showdown v2
|
||||
* [Estevão Santos](https://github.com/tivie)
|
||||
* [SyntaxRules](https://github.com/SyntaxRules)
|
||||
|
||||
- Showdown v1
|
||||
* [Estevão Santos](https://github.com/tivie)
|
||||
* [Pascal Deschênes](https://github.com/pdeschen)
|
||||
|
||||
- Showdown v0
|
||||
* [Corey Innis](http://github.com/coreyti):<br/>
|
||||
Original GitHub project maintainer
|
||||
|
|
150
Gruntfile.js
150
Gruntfile.js
|
@ -8,28 +8,44 @@ module.exports = function (grunt) {
|
|||
require('quiet-grunt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load common tasks for legacy and normal tests
|
||||
*/
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-mocha-test');
|
||||
grunt.loadNpmTasks('grunt-endline');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
// Project configuration.
|
||||
var config = {
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
concat: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
banner: ';/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */\n(function(){\n',
|
||||
footer: '}).call(this);\n'
|
||||
},
|
||||
dist: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
banner: ';/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */\n(function(){\n',
|
||||
footer: '}).call(this);\n'
|
||||
},
|
||||
src: [
|
||||
'src/options.js',
|
||||
'src/showdown.js',
|
||||
'src/helpers.js',
|
||||
'src/subParsers/makehtml/*.js',
|
||||
'src/subParsers/makemarkdown/*.js',
|
||||
'src/converter.js',
|
||||
'src/subParsers/*.js',
|
||||
'src/subParsers/makeMarkdown/*.js',
|
||||
'src/loader.js'
|
||||
],
|
||||
dest: 'dist/<%= pkg.name %>.js'
|
||||
},
|
||||
cli: {
|
||||
src: [
|
||||
'src/cli/cli.js'
|
||||
],
|
||||
dest: 'bin/showdown.js'
|
||||
},
|
||||
test: {
|
||||
src: '<%= concat.dist.src %>',
|
||||
dest: '.build/<%= pkg.name %>.js',
|
||||
|
@ -42,14 +58,23 @@ module.exports = function (grunt) {
|
|||
clean: ['.build/'],
|
||||
|
||||
uglify: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
banner: '/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */'
|
||||
},
|
||||
dist: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
banner: '/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */'
|
||||
},
|
||||
files: {
|
||||
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
|
||||
}
|
||||
},
|
||||
cli: {
|
||||
options: {
|
||||
sourceMap: false,
|
||||
banner: '#!/usr/bin/env node'
|
||||
},
|
||||
files: {
|
||||
'bin/showdown.js': ['<%= concat.cli.dest %>']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -64,8 +89,7 @@ module.exports = function (grunt) {
|
|||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc',
|
||||
reporterOutput: ''
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
|
@ -110,59 +134,40 @@ module.exports = function (grunt) {
|
|||
}
|
||||
},
|
||||
|
||||
simplemocha: {
|
||||
node: {
|
||||
src: 'test/node/**/*.js',
|
||||
mochaTest: {
|
||||
functional: {
|
||||
src: 'test/functional/**/*.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: true,
|
||||
reporter: 'spec'
|
||||
reporter: 'spec',
|
||||
require: ['test/bootstrap.js']
|
||||
}
|
||||
},
|
||||
karlcow: {
|
||||
src: 'test/node/testsuite.karlcow.js',
|
||||
unit: {
|
||||
src: 'test/unit/**/*.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec'
|
||||
}
|
||||
},
|
||||
issues: {
|
||||
src: 'test/node/testsuite.issues.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec'
|
||||
}
|
||||
},
|
||||
standard: {
|
||||
src: 'test/node/testsuite.standard.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec'
|
||||
}
|
||||
},
|
||||
features: {
|
||||
src: 'test/node/testsuite.features.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec'
|
||||
ignoreLeaks: true,
|
||||
reporter: 'spec',
|
||||
require: ['test/bootstrap.js']
|
||||
}
|
||||
},
|
||||
single: {
|
||||
src: 'test/node/**/*.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec'
|
||||
reporter: 'spec',
|
||||
require: ['test/bootstrap.js']
|
||||
}
|
||||
},
|
||||
cli: {
|
||||
src: 'test/unit/cli.js',
|
||||
options: {
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
reporter: 'spec',
|
||||
require: ['test/bootstrap.js']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,16 +175,6 @@ module.exports = function (grunt) {
|
|||
|
||||
grunt.initConfig(config);
|
||||
|
||||
/**
|
||||
* Load common tasks for legacy and normal tests
|
||||
*/
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
grunt.loadNpmTasks('grunt-endline');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
/**
|
||||
* Generate Changelog
|
||||
*/
|
||||
|
@ -204,7 +199,7 @@ module.exports = function (grunt) {
|
|||
*/
|
||||
grunt.registerTask('performancejs', function () {
|
||||
'use strict';
|
||||
var perf = require('./test/node/performance.js');
|
||||
var perf = require('./test/performance/performance.js');
|
||||
perf.runTests();
|
||||
perf.generateLogs();
|
||||
});
|
||||
|
@ -212,34 +207,31 @@ module.exports = function (grunt) {
|
|||
/**
|
||||
* Run a single test
|
||||
*/
|
||||
grunt.registerTask('single-test', function (grep) {
|
||||
grunt.registerTask('single-test', function (file) {
|
||||
'use strict';
|
||||
grunt.config.merge({
|
||||
simplemocha: {
|
||||
mochaTest: {
|
||||
single: {
|
||||
options: {
|
||||
grep: grep
|
||||
}
|
||||
src: file
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.task.run(['lint', 'concat:test', 'simplemocha:single', 'clean']);
|
||||
grunt.task.run(['lint', 'concat:test', 'mochaTest:single', 'clean']);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Test in Legacy Node
|
||||
* Tasks
|
||||
*/
|
||||
grunt.registerTask('test-old', ['concat:test', 'simplemocha:node', 'clean']);
|
||||
grunt.registerTask('test', ['clean', 'lint', 'concat:test', 'mochaTest:unit', 'mochaTest:functional', 'clean']);
|
||||
grunt.registerTask('test-functional', ['concat:test', 'mochaTest:functional', 'clean']);
|
||||
grunt.registerTask('test-unit', ['concat:test', 'mochaTest:unit', 'clean']);
|
||||
grunt.registerTask('test-cli', ['clean', 'lint', 'concat:test', 'mochaTest:cli', 'clean']);
|
||||
|
||||
/**
|
||||
* Tasks for new node versions
|
||||
*/
|
||||
grunt.registerTask('test', ['clean', 'lint', 'concat:test', 'simplemocha:node', 'clean']);
|
||||
grunt.registerTask('performance', ['concat:test', 'performancejs', 'clean']);
|
||||
grunt.registerTask('build', ['test', 'concat:dist', 'uglify', 'endline']);
|
||||
grunt.registerTask('prep-release', ['build', 'generate-changelog']);
|
||||
grunt.registerTask('build', ['test', 'concat:dist', 'concat:cli', 'uglify:dist', 'uglify:cli', 'endline']);
|
||||
grunt.registerTask('build-without-test', ['concat:dist', 'uglify', 'endline']);
|
||||
grunt.registerTask('prep-release', ['build', 'performance', 'generate-changelog']);
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('default', ['test']);
|
||||
|
|
142
README.md
142
README.md
|
@ -1,32 +1,29 @@
|
|||
![Showdown][sd-logo]
|
||||
|
||||
![Build Status: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.js.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: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.linux.yml/badge.svg)
|
||||
![Build Status: Windows](https://github.com/showdownjs/showdown/actions/workflows/node.win.yml/badge.svg)
|
||||
[![Browserstack Tests](https://automate.browserstack.com/badge.svg?badge_key=VTIvTDNqWVdaTHljbS9RNmYrcTBiL0Uxc3dkRDhaN1dPaXpPb2VOc1B2VT0tLU1Ib09kVjVzMjhFcHExbWFSWlJEV3c9PQ==--1fb92e1730e4a00630d17d533822de6403ca65ec)](https://automate.browserstack.com/public-build/VTIvTDNqWVdaTHljbS9RNmYrcTBiL0Uxc3dkRDhaN1dPaXpPb2VOc1B2VT0tLU1Ib09kVjVzMjhFcHExbWFSWlJEV3c9PQ==--1fb92e1730e4a00630d17d533822de6403ca65ec)
|
||||
[![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)
|
||||
[![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)
|
||||
[![Greenkeeper badge](https://badges.greenkeeper.io/showdownjs/showdown.svg)](https://greenkeeper.io/)
|
||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/tiviesantos)
|
||||
|
||||
------
|
||||
|
||||
Showdown is a Javascript Markdown to HTML converter, based on the original works by John Gruber.
|
||||
Showdown can be used client side (in the browser) or server side (with NodeJs).
|
||||
Showdown is a JavaScript Markdown to HTML converter, based on the original works by John Gruber.
|
||||
Showdown can be used client side (in the browser) or server side (with Node.js).
|
||||
|
||||
## Live DEMO
|
||||
|
||||
Check a live Demo here http://demo.showdownjs.com/
|
||||
|
||||
## [![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/bePatron?u=11141581)
|
||||
Check out a live demo here: http://demo.showdownjs.com/
|
||||
|
||||
As you know, ShowdownJS is a free library and it will remain free forever. However, maintaining and improving the library costs time and money.
|
||||
|
||||
If you like our work and find our library useful, please donate through [patreon](https://www.patreon.com/showdownjs) or directly through [paypal](https://www.paypal.me/tiviesantos)!! Your contribution will be greatly appreciated and help me continue to develop this awesome library.
|
||||
If you like our work and find our library useful, please donate through [PayPal](https://www.paypal.me/tiviesantos)! Your contribution will be greatly appreciated and help me continue to develop this awesome library.
|
||||
|
||||
## License
|
||||
|
||||
ShowdownJS v 2.0 is release under the MIT version.
|
||||
Previous versions are release under BSD.
|
||||
ShowdownJS v 2.0 is released under the MIT license.
|
||||
Previous versions are released under BSD.
|
||||
|
||||
## Who uses Showdown (or a fork)
|
||||
|
||||
|
@ -42,7 +39,7 @@ Previous versions are release under BSD.
|
|||
|
||||
### Download tarball
|
||||
|
||||
You can download the latest release tarball directly from [releases][releases]
|
||||
You can download the latest release tarball directly from [releases][releases].
|
||||
|
||||
### Bower
|
||||
|
||||
|
@ -56,7 +53,7 @@ You can download the latest release tarball directly from [releases][releases]
|
|||
|
||||
PM> Install-Package showdownjs
|
||||
|
||||
The NuGet Packages can be [found here](https://www.nuget.org/packages/showdownjs/).
|
||||
The NuGet Packages can be found [here](https://www.nuget.org/packages/showdownjs/).
|
||||
|
||||
### CDN
|
||||
|
||||
|
@ -93,16 +90,13 @@ The converter itself might even work in things that aren't web browsers, like Ac
|
|||
|
||||
## Node compatibility
|
||||
|
||||
Showdown supports node versions in the "Current", "Active" and "Maintenance" phases. Currently this includes Node 12.x, 14.x, 16.x and 17.x. See the [node release roadmap for more details](12.x, 14.x, 16.x, 17.x).
|
||||
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.
|
||||
|
||||
Other versions of node may likely work, but they are not tested regularly.
|
||||
|
||||
## Pervious versions
|
||||
## Legacy version
|
||||
|
||||
If you're looking for showdown v<1.0.0, you can find it in the [**legacy branch**][legacy-branch].
|
||||
|
||||
If you are looking for showdown 1.* you can find it in the [version_1.x][version_1.x] branch.
|
||||
|
||||
## Changelog
|
||||
|
||||
You can check the full [changelog][changelog]
|
||||
|
@ -115,7 +109,6 @@ Check our [wiki pages][wiki] for examples and a more in-depth documentation.
|
|||
|
||||
### Node
|
||||
|
||||
**Markdown to HTML**
|
||||
```js
|
||||
var showdown = require('showdown'),
|
||||
converter = new showdown.Converter(),
|
||||
|
@ -123,21 +116,12 @@ var showdown = require('showdown'),
|
|||
html = converter.makeHtml(text);
|
||||
```
|
||||
|
||||
**HTML to Markdown**
|
||||
```js
|
||||
var showdown = require('showdown'),
|
||||
converter = new showdown.Converter(),
|
||||
html = '<a href="https://patreon.com/showdownjs">Please Support us!</a>',
|
||||
md = converter.makeMarkdown(text);
|
||||
```
|
||||
|
||||
|
||||
### Browser
|
||||
|
||||
```js
|
||||
var converter = new showdown.Converter(),
|
||||
html = converter.makeHtml('# hello, markdown!'),
|
||||
md = converter.makeMd('<a href="https://patreon.com/showdownjs">Please Support us!</a>');
|
||||
text = '# hello, markdown!',
|
||||
html = converter.makeHtml(text);
|
||||
```
|
||||
|
||||
### Output
|
||||
|
@ -148,10 +132,6 @@ Both examples should output...
|
|||
<h1 id="hellomarkdown">hello, markdown!</h1>
|
||||
```
|
||||
|
||||
```md
|
||||
[Please Support us!](https://patreon.com/showdownjs)
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
You can change some of showdown's default behavior through options.
|
||||
|
@ -250,14 +230,6 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
* **rawHeaderId**: (boolean) [default false] Remove only spaces, ' and " from generated header ids (including prefixes),
|
||||
replacing them with dashes (-). WARNING: This might result in malformed ids **(since v1.7.3)**
|
||||
|
||||
* **parseImgDimensions**: (boolean) [default false] Enable support for setting image dimensions from within markdown syntax.
|
||||
Examples:
|
||||
```
|
||||
![foo](foo.jpg =100x80) simple, assumes units are in px
|
||||
![bar](bar.jpg =100x*) sets the height to "auto"
|
||||
![baz](baz.jpg =80%x5em) Image with width of 80% and height of 5em
|
||||
```
|
||||
|
||||
* **headerLevelStart**: (integer) [default 1] Set the header starting level. For instance, setting this to 3 means that
|
||||
|
||||
```md
|
||||
|
@ -269,6 +241,14 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
<h3>foo</h3>
|
||||
```
|
||||
|
||||
* **parseImgDimensions**: (boolean) [default false] Enable support for setting image dimensions from within markdown syntax.
|
||||
Examples:
|
||||
```
|
||||
![foo](foo.jpg =100x80) simple, assumes units are in px
|
||||
![bar](bar.jpg =100x*) sets the height to "auto"
|
||||
![baz](baz.jpg =80%x5em) Image with width of 80% and height of 5em
|
||||
```
|
||||
|
||||
* **simplifiedAutoLink**: (boolean) [default false] Turning this option on will enable automatic linking to urls.
|
||||
This means that:
|
||||
|
||||
|
@ -280,16 +260,8 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
<p>some text <a href="www.google.com">www.google.com</a>
|
||||
```
|
||||
|
||||
* **excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls.
|
||||
Punctuation excluded: `. ! ? ( )`. Only applies if **simplifiedAutoLink** option is set to `true`.
|
||||
|
||||
```md
|
||||
check this link www.google.com!
|
||||
```
|
||||
will be parsed as
|
||||
```html
|
||||
<p>check this link <a href="www.google.com">www.google.com</a>!</p>
|
||||
```
|
||||
* ~~**excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls.
|
||||
Punctuation excluded: `. ! ? ( )`. Only applies if **simplifiedAutoLink** option is set to `true`.~~
|
||||
|
||||
* **literalMidWordUnderscores**: (boolean) [default false] Turning this on will stop showdown from interpreting
|
||||
underscores in the middle of words as `<em>` and `<strong>` and instead treat them as literal underscores.
|
||||
|
@ -304,18 +276,8 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
<p>some text with__underscores__in middle</p>
|
||||
```
|
||||
|
||||
* **literalMidWordAsterisks**: (boolean) [default false] Turning this on will stop showdown from interpreting asterisks
|
||||
in the middle of words as `<em>` and `<strong>` and instead treat them as literal asterisks.
|
||||
|
||||
Example:
|
||||
|
||||
```md
|
||||
some text with**underscores**in middle
|
||||
```
|
||||
will be parsed as
|
||||
```html
|
||||
<p>some text with**underscores**in middle</p>
|
||||
```
|
||||
* ~~**literalMidWordAsterisks**: (boolean) [default false] Turning this on will stop showdown from interpreting asterisks
|
||||
in the middle of words as `<em>` and `<strong>` and instead treat them as literal asterisks.~~
|
||||
|
||||
* **strikethrough**: (boolean) [default false] Enable support for strikethrough syntax.
|
||||
`~~strikethrough~~` as `<del>strikethrough</del>`
|
||||
|
@ -350,7 +312,7 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
by 4 spaces for them to be nested, effectively reverting to the old behavior where 2 or 3 spaces were enough.
|
||||
**(since v1.5.0)**
|
||||
|
||||
* **simpleLineBreaks**: (boolean) [default false] Parses line breaks as `<br>`, like GitHub does, without
|
||||
* **simpleLineBreaks**: (boolean) [default false] Parses line breaks as `<br>`, without
|
||||
needing 2 spaces at the end of the line **(since v1.5.1)**
|
||||
|
||||
```md
|
||||
|
@ -404,6 +366,10 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
|
||||
* **splitAdjacentBlockquotes**: (boolean) [default false] Split adjacent blockquote blocks.(since v.1.8.6)
|
||||
|
||||
* **moreStyling**: (boolean) [default false] Adds some useful classes for css styling. (since v2.0.1)
|
||||
|
||||
- Tasklists: Adds the class `task-list-item-complete` to completed tasks items in GFM tasklists.
|
||||
|
||||
**NOTE**: Please note that until **version 1.6.0**, all of these options are ***DISABLED*** by default in the cli tool.
|
||||
|
||||
|
||||
|
@ -465,12 +431,11 @@ You can also find a boilerplate, to create your own extensions in [this reposito
|
|||
|
||||
### Client-side Extension Usage
|
||||
|
||||
```html
|
||||
<script src="showdown.js"></script>
|
||||
<script src="twitter-extension.js"></script>
|
||||
<script>
|
||||
```js
|
||||
<script src="showdown.js" />
|
||||
<script src="twitter-extension.js" />
|
||||
|
||||
var converter = new showdown.Converter({ extensions: ['twitter'] });
|
||||
</script>
|
||||
```
|
||||
|
||||
### Server-side Extension Usage
|
||||
|
@ -481,9 +446,22 @@ var showdown = require('showdown'),
|
|||
converter = new showdown.Converter({ extensions: ['myExtension'] });
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Building your clone of the repository is easy.
|
||||
> Prerequesites: [Node.js](https://nodejs.org/) v12, [npm](https://www.npmjs.com/package/npm) and [npx](https://www.npmjs.com/package/npx) must be installed.
|
||||
|
||||
1. run `npm install`.
|
||||
2. run `npx grunt build` (see [`Gruntfile.js`](/Gruntfile.js)). This command:
|
||||
|
||||
1. Cleans the repo.
|
||||
2. Checks code quality ([JSHint](https://jshint.com/) and [ESLint](https://eslint.org/)).
|
||||
3. Runs tests.
|
||||
4. Creates the [distributable](/showdown.js) and [minified](/showdown.min.js) files in the [`dist`](/dist) folder.
|
||||
|
||||
## Tests
|
||||
|
||||
A suite of tests is available which require node.js. Once node is installed, run the following command from
|
||||
A suite of tests is available which require Node.js. Once Node is installed, run the following command from
|
||||
the project root to install the dependencies:
|
||||
|
||||
npm install
|
||||
|
@ -508,28 +486,26 @@ Pull Request.
|
|||
PRs are awesome. However, before you submit your pull request consider the following guidelines:
|
||||
|
||||
- Search GitHub for an open or closed Pull Request that relates to your submission. You don't want to duplicate effort.
|
||||
- When issuing PRs that change code, make your changes in a new git branch based on master:
|
||||
- When issuing PRs that change code, make your changes in a new git branch based on **develop**:
|
||||
|
||||
```bash
|
||||
git checkout -b my-fix-branch master
|
||||
git checkout -b my-fix-branch develop
|
||||
```
|
||||
|
||||
- Documentation (i.e: README.md) changes can be made directly against master.
|
||||
- Run the full test suite before submitting and make sure all tests pass (obviously =P).
|
||||
- Try to follow our [**coding style rules**][coding-rules].
|
||||
Breaking them prevents the PR to pass the tests.
|
||||
- Try to follow our [**coding style rules**][coding-rules]. Breaking them prevents the PR to pass the tests.
|
||||
- Refrain from fixing multiple issues in the same pull request. It's preferable to open multiple small PRs instead of one
|
||||
hard to review big one.
|
||||
- If the PR introduces a new feature or fixes an issue, please add the appropriate test case.
|
||||
- We use commit notes to generate the changelog. It's extremely helpful if your commit messages adhere to the
|
||||
[**AngularJS Git Commit Guidelines**][ng-commit-guide].
|
||||
- If the PR introduces a new feature or fixes an issue, **please add the appropriate test case**.
|
||||
- We use [conventional commit][conventional-commits] notes to generate the changelog that follow the conventional changelog spec. It's extremely helpful if your commit messages adhere to these [Commit Guidelines][conventional-commits].
|
||||
- Don't forget to add your name to the [CREDITS.md](https://github.com/showdownjs/showdown/blob/master/CREDITS.md) file. We like to give credit were it's due.
|
||||
- If we suggest changes then:
|
||||
- Make the required updates.
|
||||
- Re-run the Angular test suite to ensure tests are still passing.
|
||||
- Re-run the test suite to ensure tests are still passing.
|
||||
- Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```bash
|
||||
git rebase master -i
|
||||
git rebase develop -i
|
||||
git push origin my-fix-branch -f
|
||||
```
|
||||
- After your pull request is merged, you can safely delete your branch.
|
||||
|
@ -560,5 +536,5 @@ Showdown is powered by:<br/>
|
|||
[xss-wiki]: https://github.com/showdownjs/showdown/wiki/Markdown's-XSS-Vulnerability-(and-how-to-mitigate-it)
|
||||
[ext-wiki]: https://github.com/showdownjs/showdown/wiki/extensions
|
||||
[coding-rules]: https://github.com/showdownjs/code-style/blob/master/README.md
|
||||
[ng-commit-guide]: https://github.com/showdownjs/code-style/blob/master/README.md#commit-message-convention
|
||||
[conventional-commits]: https://www.conventionalcommits.org/
|
||||
[boilerplate-repo]: https://github.com/showdownjs/extension-boilerplate
|
||||
|
|
146
TASKS.TODO.md
Normal file
146
TASKS.TODO.md
Normal file
|
@ -0,0 +1,146 @@
|
|||
# ROADMAP TO VERSION 3.0
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
- [ ] **ghCompatibleHeaderId** (removal)
|
||||
|
||||
Will be removed and **will become the default behavior**.
|
||||
|
||||
- [ ] **customizedHeaderId** (removal)
|
||||
|
||||
This option introduced non compliant syntax so it really belongs in an extension.
|
||||
The new **listener extension system** allows users to directly modify and customize
|
||||
the HTML and add any attributes they wish.
|
||||
|
||||
- [ ] **rawPrefixHeaderId** (removal)
|
||||
|
||||
This option will be superseeded by the option `rawHeaderId`. So basically activating `rawHeaderId` will make
|
||||
showdown only to replace spaces, ', ", > and < with dashes (-) from generated header ids, including prefixes.
|
||||
|
||||
- [X] **literalMidWordAsterisks** (removal)
|
||||
|
||||
This option is weird, hard to maintain and really... makes little sense.
|
||||
|
||||
- [X] **excludeTrailingPunctuationFromURLs** (removal)
|
||||
|
||||
This option will be removed and will be the default behavior from now on.
|
||||
|
||||
- [ ] **strikethrough** (change)
|
||||
|
||||
Will be enabled by default
|
||||
|
||||
- [ ] **disableForced4SpacesIndentedSublists** (to think/postpone)
|
||||
|
||||
This was only a temporary option for backwards compatibility reason. However, most flavours support lists indented
|
||||
with 2 spaces, so it puts us in a tight spot, specially since some markdown beautifiers out there insist in
|
||||
indenting lists with 2 spaces, probably in a misguided try to follow the CommonMark spec.
|
||||
|
||||
The CommonMark spec is, IMHO, a bit confusing for users regarding this, since list sub-blocks (and lists)
|
||||
are determined by the spaces from the start of the line and the first character after the list mark. And the proof
|
||||
are the MD beautifiers out there, which misinterpreted the spec and made a mess
|
||||
|
||||
Showdown syntax is actually easier (and fully compliant with the original spec): if you indent something 4 spaces,
|
||||
it becomes a sub-block. Since lists are blocks, you must indent it 4 spaces for it to become a sub-block.
|
||||
|
||||
Regardless, we kinda have 2 solutions:
|
||||
|
||||
- Drop support for 2 space indentation (and make a lot of users confused since GFM, CommonMark and others allow this)
|
||||
- Create a new list subparser that can be turned on with an option, like gfmListStyle
|
||||
(but postpones even more the alpha 3.0 release since the list subparser is probably the hardest thing to rewrite)
|
||||
|
||||
Tough choices...
|
||||
|
||||
- [ ] **simpleLineBreaks** (change)
|
||||
|
||||
Will be removed from Github Flavor since github only does this in comments (which is weird...)
|
||||
|
||||
- [ ] **openLinksInNewWindow** (removal)
|
||||
|
||||
Will be removed in favor of the new listener extension, which will allow users to manipulate HTML tags attributes
|
||||
directly.
|
||||
|
||||
- [ ] Revamp the option system
|
||||
|
||||
Revamp the option system so that it becomes more simple. Right now, it's really confusing. And option names are weird
|
||||
too. The idea is to pass options to the constructor under an option object, that can have hierarchical structure.
|
||||
Ex:
|
||||
|
||||
```js
|
||||
var conv = new showdown.Converter({
|
||||
options: {
|
||||
links: {
|
||||
autoLinks: true
|
||||
},
|
||||
headings: {
|
||||
startLevel: 2
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Legacy Code Removal
|
||||
- [ ] Legacy extension support
|
||||
|
||||
Old extensions that inject directly into extensions object property will no longer be supported
|
||||
|
||||
- [ ] HTML and OUTPUT extensions
|
||||
|
||||
HTML and OTP extensions will be dropped in favor of Listener Extensions. We might even give them a new name
|
||||
|
||||
## Subparsers
|
||||
- [X] **Anchors**: Revamp the anchors subparser so it calls strikethrough, bold, italic and underline directly
|
||||
- [X] **autoLinks**: Fix some lingering bugs and issues with autolinks
|
||||
|
||||
## Priority Bugs
|
||||
- [X] **#355**: *simplifiedAutoLink URLs inside parenthesis followed by another character are not parsed correctly*
|
||||
- [X] **#534**: *Multiple parentheses () in url link is not parsed correctly*
|
||||
- [ ] **#367**: *sublists rendering with 2 spaces* - related to disableForced4SpacesIndentedSublists option...
|
||||
- [ ] **#537**: *master branch doesn't work in a web worker*
|
||||
|
||||
## CLI
|
||||
- [ ] Refactor the CLI
|
||||
- [ ] **#381**: *Support for src and dst directories in showdown cli*
|
||||
- [X] **#584**: *Fails to read from stdin*
|
||||
- [X] **#554**: *CLI not working with jsdom v10*
|
||||
|
||||
## Other stuff
|
||||
- [X] Regexp rewrite for more performance oompf
|
||||
- [X] Full unit testing
|
||||
- [ ] Better error reporting
|
||||
|
||||
## Stuff that probably won't make it to v2.0
|
||||
- [ ] **#486**: *A backslash at the end of the line is a hard line break*
|
||||
- [ ] **#548**: *anchors and images of subParser are errors when they are specific strings*
|
||||
- [ ] **#549**: *Strange parsing issue with `<pre><code>`*
|
||||
- [ ] Rethink the global variable
|
||||
|
||||
## NEW Features
|
||||
|
||||
### Event system
|
||||
- [X] Listener system revamp
|
||||
- [ ] Standardize events for all event types
|
||||
- [ ] Unit testing
|
||||
- [ ] Functional testing
|
||||
|
||||
This should address:
|
||||
- **#567**: Allow not making header ids lowercase
|
||||
- **#540**: Add complete class to the tasklist list element
|
||||
|
||||
### MD to HTML conversion
|
||||
- [X] Basic support
|
||||
- [X] Basic functional testcase
|
||||
- [ ] Advanced support for all showdown MD features
|
||||
- [ ] Advanced functional testcase
|
||||
- [ ] Unit testing
|
||||
|
||||
## Documentation (for v2.0)
|
||||
- [ ] Options
|
||||
- [ ] Extensions (and the new event system)
|
||||
- [ ] Cookbook (with stuff for backwards compatibility, specially regarding removed options)
|
||||
|
||||
## Browser Testing
|
||||
|
||||
- [X] Implement unit tests in Karma
|
||||
- [ ] Implement functional tests in Karma
|
||||
- [ ] Integrate with browserstack
|
BIN
bin/showdown.js
BIN
bin/showdown.js
Binary file not shown.
BIN
dist/showdown.js
vendored
BIN
dist/showdown.js
vendored
Binary file not shown.
BIN
dist/showdown.js.map
vendored
BIN
dist/showdown.js.map
vendored
Binary file not shown.
BIN
dist/showdown.min.js
vendored
BIN
dist/showdown.min.js
vendored
Binary file not shown.
BIN
dist/showdown.min.js.map
vendored
BIN
dist/showdown.min.js.map
vendored
Binary file not shown.
4
docs/assets/extra.css
Normal file
4
docs/assets/extra.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
:root {
|
||||
--md-primary-fg-color: rgb(196, 54, 39);
|
||||
--md-accent-fg-color: rgb(62, 139, 138);
|
||||
}
|
BIN
docs/assets/markdown-editor.png
Normal file
BIN
docs/assets/markdown-editor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 KiB |
773
docs/available-options.md
Normal file
773
docs/available-options.md
Normal file
|
@ -0,0 +1,773 @@
|
|||
!!! warning ""
|
||||
Starting from the version `1.6.0` and earlier, all the options are `disabled` by default in the cli tool.
|
||||
|
||||
### backslashEscapesHTMLTags
|
||||
|
||||
Support escaping of HTML tags.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.7.2`
|
||||
|
||||
=== "input"
|
||||
|
||||
```html
|
||||
\<div>foo\</div>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p><div>foo</div></p>
|
||||
```
|
||||
|
||||
### completeHTMLDocument
|
||||
|
||||
Output a complete HTML document, including `<html>`, `<head>`, and `<body>` tags instead of an HTML fragment.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.8.5`
|
||||
|
||||
### customizedHeaderId
|
||||
|
||||
Set custom ID for a heading.
|
||||
|
||||
!!! warning ""
|
||||
This option can be overridden with the [`noHeaderId`](#noheaderid) option.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.7.0`
|
||||
|
||||
=== "code"
|
||||
|
||||
```html
|
||||
## Sample heading {mycustomid}
|
||||
```
|
||||
|
||||
=== "output"
|
||||
|
||||
```html
|
||||
<h1 id="mycustomid">This is a heading</h1>
|
||||
```
|
||||
|
||||
!!! hint ""
|
||||
For better readability and human-friendliness of the heading IDs, it is also recommended to set the [`ghCompatibleHeaderId`](#ghcompatibleheaderid) option to `true`.
|
||||
|
||||
### disableForced4SpacesIndentedSublists
|
||||
|
||||
Disable the rule of 4 spaces to indent sub-lists. If enabled, this option effectively reverts to the old behavior where you can indent sub-lists with 2 or 3 spaces.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.5.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
- one
|
||||
- two
|
||||
|
||||
...
|
||||
|
||||
- one
|
||||
- two
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>one</li>
|
||||
<li>two</li>
|
||||
</ul>
|
||||
<p>...</p>
|
||||
<ul>
|
||||
<li>one
|
||||
<ul>
|
||||
<li>two</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>one
|
||||
<ul>
|
||||
<li>two</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>...</p>
|
||||
<ul>
|
||||
<li>one
|
||||
<ul>
|
||||
<li>two</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
### emoji
|
||||
|
||||
Enable emoji support. For more info on available emojis, see https://github.com/showdownjs/showdown/wiki/Emojis (since v.1.8.0)
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.8.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
this is a :smile: emoji
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>this is a :smile: emoji</p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>this is a 😄 emoji</p>
|
||||
```
|
||||
|
||||
!!! hint "Full list of supported emojies"
|
||||
|
||||
Check the [Showdown Wiki](https://github.com/showdownjs/showdown/wiki/Emojis#emoji-list) for a full list of supported emojies.
|
||||
|
||||
### encodeEmails
|
||||
|
||||
Enable automatic obfuscation of email addresses. During this process, email addresses are encoded via Character Entities, transforming ASCII email addresses into their equivalent decimal entities.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.6.1`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
<myself@example.com>
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<a href="mailto:myself@example.com">myself@example.com</a>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<a href="mailto:myself@example.com">myself@example.com</a>
|
||||
```
|
||||
|
||||
### excludeTrailingPunctuationFromURLs
|
||||
|
||||
Exclude trailing punctuation from autolinked URLs: `.` `!` `?` `(` `)`
|
||||
|
||||
This option applies only to links generated by [`simplifiedAutoLink`](#simplifiedautolink).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.5.1`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
check this link www.google.com.
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>check this link <a href="www.google.com">www.google.com.</a></p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>check this link <a href="www.google.com">www.google.com</a>.</p>
|
||||
```
|
||||
|
||||
### ghCodeBlocks
|
||||
|
||||
Enable support for GFM code block style syntax (fenced codeblocks).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `true`
|
||||
* introduced in: `0.3.1`
|
||||
|
||||
=== "example"
|
||||
|
||||
```
|
||||
```
|
||||
some code here
|
||||
```
|
||||
```
|
||||
|
||||
### ghCompatibleHeaderId
|
||||
|
||||
Generate heading IDs compatible with GitHub style: spaces are replaced with dashes, and certain non-alphanumeric chars are removed.
|
||||
|
||||
!!! warning ""
|
||||
This option can be overridden with the [`noHeaderId`](#noheaderid) option.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.5.5`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
# This is a heading with @#$%
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<h1 id="thisisaheading">This is a heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<h1 id="this-is-a-heading-with-">This is a heading with @#$%</h1>
|
||||
```
|
||||
|
||||
### ghMentions
|
||||
|
||||
Enables support for GitHub `@mentions` that allows you to link to the GitHub profile page of the mentioned username.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.6.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
hello there @tivie
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>hello there @tivie</p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>hello there <a href="https://www.github.com/tivie">@tivie</a></p>
|
||||
```
|
||||
|
||||
### ghMentionsLink
|
||||
|
||||
Specify where the link generated by `@mentions` should point to. Works only when [`ghMentions: true`](#ghmentions).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `https://github.com/{u}`
|
||||
* introduced in: `1.6.2`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
hello there @tivie
|
||||
```
|
||||
|
||||
=== "output (value is `https://github.com/{u}`)"
|
||||
|
||||
```html
|
||||
<p>hello there <a href="https://www.github.com/tivie">@tivie</a></p>
|
||||
```
|
||||
|
||||
=== "output (value is `http://mysite.com/{u}/profile`)"
|
||||
|
||||
```html
|
||||
<p>hello there <a href="//mysite.com/tivie/profile">@tivie</a></p>
|
||||
```
|
||||
|
||||
### headerLevelStart
|
||||
|
||||
Set starting level for the heading tags.
|
||||
|
||||
* type: `integer`
|
||||
* default value: `1`
|
||||
* introduced in: `1.1.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
# This is a heading
|
||||
```
|
||||
|
||||
=== "output (value is `1`)"
|
||||
|
||||
```html
|
||||
<h1>This is a heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `3`)"
|
||||
|
||||
```html
|
||||
<h3>This is a heading</h3>
|
||||
```
|
||||
|
||||
### literalMidWordUnderscores
|
||||
|
||||
Treat underscores in the middle of words as literal characters.
|
||||
|
||||
Underscores allow you to specify the words that should be emphasized. However, in some cases, this may be unwanted behavior. With this option enabled, underscores in the middle of words will no longer be interpreted as `<em>` and `<strong>`, but as literal underscores.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
some text with__underscores__in the middle
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>some text with<strong>underscores</strong>in the middle</p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>some text with__underscores__in the middle</p>
|
||||
```
|
||||
|
||||
### metadata
|
||||
|
||||
Enable support for document metadata (front-matter). You can define metadata at the top of a document between `««« »»»` or `--- ---` symbols.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.8.5`
|
||||
|
||||
=== "input"
|
||||
|
||||
```js
|
||||
let ref = `referenced value`;
|
||||
|
||||
var markdown = `
|
||||
---
|
||||
first: Lorem
|
||||
second: Ipsum
|
||||
ref_variable: ${ref}
|
||||
---
|
||||
`
|
||||
|
||||
var conv = new showdown.Converter({metadata: true});
|
||||
var html = conv.makeHtml(markdown);
|
||||
var metadata = conv.getMetadata();
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```js
|
||||
// console.log(metadata)
|
||||
{
|
||||
first: 'Lorem',
|
||||
second: 'Ipsum',
|
||||
ref_variable: 'referenced value'
|
||||
}
|
||||
```
|
||||
|
||||
### noHeaderId
|
||||
|
||||
Disable automatic generation of heading IDs.
|
||||
|
||||
!!! warning ""
|
||||
Setting the option to `true` overrides the following options:
|
||||
|
||||
* [`prefixHeaderId`](#prefixheaderid)
|
||||
* [`customizedHeaderId`](#customizedheaderid)
|
||||
* [`ghCompatibleHeaderId`](#ghcompatibleheaderid)
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.1.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
# This is a heading
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<h1 id="thisisaheading">This is a heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<h1>This is a heading</h1>
|
||||
```
|
||||
|
||||
### omitExtraWLInCodeBlocks
|
||||
|
||||
Omit trailing newline in code blocks (which is set by default before the closing tag). This option affects both indented and fenced (gfm style) code blocks.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.0.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
var foo = 'bar';
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<code><pre>var foo = 'bar';
|
||||
</pre></code>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<code><pre>var foo = 'bar';</pre></code>
|
||||
```
|
||||
|
||||
### openLinksInNewWindow
|
||||
|
||||
Open links in new windows.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.7.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
[link](https://google.com)
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<a href="https://google.com">link</a>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<a href="https://google.com" rel="noopener noreferrer" target="_blank">link</a>
|
||||
```
|
||||
|
||||
### parseImgDimensions
|
||||
|
||||
Set image dimensions from within Markdown syntax.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.1.0`
|
||||
|
||||
=== "example"
|
||||
|
||||
```
|
||||
![foo](foo.jpg =100x80) set width to 100px and height to 80px
|
||||
![bar](bar.jpg =100x*) set width to 100px and height to "auto"
|
||||
![baz](baz.jpg =80%x5em) set width to 80% and height to 5em
|
||||
```
|
||||
|
||||
### prefixHeaderId
|
||||
|
||||
Add a prefix to the generated heading ID:
|
||||
|
||||
* Passing a string will add that string to the heading ID.
|
||||
* Passing `true` will add a generic `section` prefix.
|
||||
|
||||
!!! warning ""
|
||||
This option can be overridden with the [`noHeaderId`](#noheaderid) option.
|
||||
|
||||
* type: `string / boolean`
|
||||
* default value: `false`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
# This is a heading
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<h1 id="thisisaheading">This is a heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<h1 id="sectionthisisaheading">This is a heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `showdown`)"
|
||||
|
||||
```html
|
||||
<h1 id="showdownthisisaheading">This is a heading</h1>
|
||||
```
|
||||
|
||||
### rawHeaderId
|
||||
|
||||
Replace ` ` (space), `'` (single quote), and `"` (double quote) with `-` (dash) in the generated heading IDs, including prefixes.
|
||||
|
||||
!!! danger ""
|
||||
**Use with caution** as it might result in malformed IDs.
|
||||
|
||||
* type:
|
||||
* default value:
|
||||
* introduced in: `1.7.3`
|
||||
|
||||
### rawPrefixHeaderId
|
||||
|
||||
Prevent Showndown from modifying the prefix. Works only when [`prefixHeaderId`](#prefixheaderid) is set to a string value.
|
||||
|
||||
!!! danger ""
|
||||
**Use with caution** as it might result in malformed IDs. For example, when the prefix contains special characters like `"` `\` `/` or others.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.7.3`
|
||||
|
||||
### requireSpaceBeforeHeadingText
|
||||
|
||||
Require a space between a heading `#` and the heading text.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.5.3`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
#heading
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<h1 id="heading">heading</h1>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>#heading</p>
|
||||
```
|
||||
|
||||
### simpleLineBreaks
|
||||
|
||||
Parse line breaks as `<br/>` in paragraphs (GitHub-style behavior).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.5.1`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
a line
|
||||
wrapped in two
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>a line
|
||||
wrapped in two</p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>a line<br>
|
||||
wrapped in two</p>
|
||||
```
|
||||
|
||||
### simplifiedAutoLink
|
||||
|
||||
Enable automatic linking for plain text URLs.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
Lorem ipsum www.google.com
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<p>Lorem ipsum www.google.com</p>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<p>Lorem ipsum <a href="www.google.com">www.google.com</a></p>
|
||||
```
|
||||
|
||||
### smartIndentationFix
|
||||
|
||||
Resolve indentation problems related to ES6 template strings in the midst of indented code.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.4.2`
|
||||
|
||||
### smoothLivePreview
|
||||
|
||||
Resolve an awkward effect when a paragraph is followed by a list. This effect appears on some circumstances, in live preview editors.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.1`
|
||||
|
||||
!!! example "awkward effect"
|
||||
|
||||
![](http://i.imgur.com/YQ9iHTL.gif)
|
||||
|
||||
### splitAdjacentBlockquotes
|
||||
|
||||
Split adjacent blockquote blocks.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.8.6`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
> Quote #1
|
||||
>> Sub-quote 1
|
||||
|
||||
> Quote #2
|
||||
>> Sub-quote 2
|
||||
```
|
||||
|
||||
=== "output (value is `false`)"
|
||||
|
||||
```html
|
||||
<blockquote>
|
||||
<p>Quote #1</p>
|
||||
<blockquote>
|
||||
<p>Sub-quote 1</p>
|
||||
</blockquote>
|
||||
<p>Quote #2</p>
|
||||
<blockquote>
|
||||
<p>Sub-quote 2</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<blockquote>
|
||||
<p>Quote #1</p>
|
||||
<blockquote>
|
||||
<p>Sub-quote 1</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>Quote #2</p>
|
||||
<blockquote>
|
||||
<p>Sub-quote 2</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
```
|
||||
|
||||
### strikethrough
|
||||
|
||||
Enable support for strikethrough (`<del>`).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
=== "input"
|
||||
|
||||
```
|
||||
~~strikethrough~~
|
||||
```
|
||||
|
||||
=== "output (value is `true`)"
|
||||
|
||||
```html
|
||||
<del>strikethrough</del>
|
||||
```
|
||||
|
||||
### tables
|
||||
|
||||
Enable support for tables syntax.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
=== "example"
|
||||
|
||||
```
|
||||
| h1 | h2 | h3 |
|
||||
|:------|:-------:|--------:|
|
||||
| 100 | [a][1] | ![b][2] |
|
||||
| *foo* | **bar** | ~~baz~~ |
|
||||
```
|
||||
|
||||
### tablesHeaderId
|
||||
|
||||
Generate automatic IDs for table headings. Works only when [`tables: true`](#tables).
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
### tasklists
|
||||
|
||||
Enable support for GitHub style tasklists.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* introduced in: `1.2.0`
|
||||
|
||||
=== "example"
|
||||
|
||||
```
|
||||
- [x] This task is done
|
||||
- [ ] This task is still pending
|
||||
```
|
||||
|
||||
### underline
|
||||
|
||||
Enable support for underline. If enabled, underscores will no longer be parsed as `<em>` and `<strong>`.
|
||||
|
||||
* type: `boolean`
|
||||
* default value: `false`
|
||||
* status: `Experimental`
|
||||
|
||||
=== "example"
|
||||
|
||||
```
|
||||
__underlined word__ // double underscores
|
||||
|
||||
___underlined word___ // triple underscores
|
||||
```
|
196
docs/cli.md
Normal file
196
docs/cli.md
Normal file
|
@ -0,0 +1,196 @@
|
|||
Showdown comes bundled with a Command-line interface (CLI) tool that allows you to run Showdown converter from the command line.
|
||||
|
||||
## Requirements
|
||||
|
||||
* [Node.js](https://nodejs.org/en/)
|
||||
|
||||
## Quick start guide
|
||||
|
||||
1. Check that Showdown CLI is accessible.
|
||||
|
||||
* If you installed Showdown globally via `npm install showdown -g`, you can access the CLI tool help by typing `showdown -h` in the command line:
|
||||
|
||||
=== "input"
|
||||
|
||||
```sh
|
||||
showdown -h
|
||||
```
|
||||
|
||||
=== "output"
|
||||
|
||||
```
|
||||
Usage: showdown <command> [options]
|
||||
|
||||
CLI to Showdownjs markdown parser v3.0.0-alpha
|
||||
|
||||
Options:
|
||||
-V, --version output the version number
|
||||
-q, --quiet Quiet mode. Only print errors
|
||||
-m, --mute Mute mode. Does not print anything
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
makehtml [options] Converts markdown into html
|
||||
help [command] display help for command
|
||||
```
|
||||
|
||||
* If you installed Showdown locally via `npm install showdown`, open the folder where Showdown is installed, and type `node ./bin/showdown.js -h` in the command line:
|
||||
|
||||
=== "input"
|
||||
|
||||
```sh
|
||||
node ./bin/showdown.js -h
|
||||
```
|
||||
|
||||
=== "output"
|
||||
|
||||
```
|
||||
Usage: showdown <command> [options]
|
||||
|
||||
CLI to Showdownjs markdown parser v3.0.0-alpha
|
||||
|
||||
Options:
|
||||
-V, --version output the version number
|
||||
-q, --quiet Quiet mode. Only print errors
|
||||
-m, --mute Mute mode. Does not print anything
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
makehtml [options] Converts markdown into html
|
||||
help [command] display help for command
|
||||
```
|
||||
|
||||
1. Use `makehtml` command to convert your document to HTML. For example:
|
||||
|
||||
!!! example "Convert `foo.md` into `bar.html`"
|
||||
|
||||
```sh
|
||||
showdown makehtml -i foo.md -o bar.html
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### `makehtml`
|
||||
|
||||
Convert a Markdown input into HTML.
|
||||
|
||||
**Usage**
|
||||
|
||||
```sh
|
||||
showdown makehtml [options]
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
###### `-i / --input`
|
||||
|
||||
* Short format: `-i`
|
||||
* Alias: `--input`
|
||||
* Description: Input source. Usually a `.md` file. If omitted or empty, reads from `stdin`.
|
||||
* Examples:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
// Read from stdin and output to stdout
|
||||
showdown makehtml -i
|
||||
|
||||
// Read from the foo.md file and output to stdout
|
||||
showdown makehtml --input foo.md
|
||||
```
|
||||
|
||||
###### `-o/--output`
|
||||
|
||||
* Short format: `-o`
|
||||
* Alias: `--output`
|
||||
* Description: Output target. Usually a `.html` file. If omitted or empty, writes to `stdout`.
|
||||
* Example:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
// Read from the foo.md file and output to bar.html
|
||||
showdown makehtml -i foo.md -o bar.html
|
||||
```
|
||||
|
||||
###### `-a/--append`
|
||||
|
||||
* Short format: `-a`
|
||||
* Alias: `--append`
|
||||
* Description: Append data to output instead of overwriting.
|
||||
* Example:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
showdown makehtml -a
|
||||
```
|
||||
|
||||
###### `-u/--encoding`
|
||||
|
||||
* Short format: `-u`
|
||||
* Alias: `--encoding`
|
||||
* Description: Specify the input encoding.
|
||||
* Example:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
showdown makehtml -u UTF8
|
||||
```
|
||||
|
||||
###### `-e/--extensions`
|
||||
|
||||
* Short format: `-e`
|
||||
* Alias: `--extension`
|
||||
* Description: Load the specified extension(s). Should be valid path(s) to Node-compatible extensions.
|
||||
* Example:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
showdown makehtml -e ~/twitter.js -e ~/youtube.js
|
||||
```
|
||||
|
||||
###### `-c/--config`
|
||||
|
||||
* Short format: `-c`
|
||||
* Alias: `--config`
|
||||
* Description: Enable or disable parser options.
|
||||
* Introduced in: `2.0.1` (Breaking change. See the [`Extra options`](#extra-options) section below)
|
||||
* Example:
|
||||
|
||||
!!! example ""
|
||||
|
||||
```sh
|
||||
showdown makehtml -i foo.md -o bar.html -c strikethrough
|
||||
showdown makehtml -i foo.md -o bar.html -c strikethrough -c emoji
|
||||
```
|
||||
|
||||
## Extra options
|
||||
|
||||
Starting from the version `2.0.1`, CLI the format of passing extra options has changed. Please make the necessary changes to your code, if required.
|
||||
|
||||
=== "since `v2.0.1`"
|
||||
|
||||
```sh
|
||||
showdown makehtml -i foo.md -o bar.html -c strikethrough -c emoji
|
||||
```
|
||||
|
||||
=== "before `v2.0.1`"
|
||||
|
||||
```sh
|
||||
showdown makehtml -i foo.md -o bar.html --strikethrough --emoji
|
||||
```
|
||||
|
||||
|
||||
You can specify any of the [supported options](available-options.md), and they will be passed to the converter.
|
||||
|
||||
The above commands are equivalent of doing:
|
||||
|
||||
```js
|
||||
var conv = new showdown.Converter({strikethrough: true, emoji: true});
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
In the CLI tool, all the extra options are **disabled** by default. This is the opposite of what is defined for node and browser, where some options, like `ghCodeBlocks` are enabled (for backward compatibility and historical reasons).
|
21
docs/compatibility.md
Normal file
21
docs/compatibility.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
## Browsers
|
||||
|
||||
Showdown has been tested successfully with:
|
||||
|
||||
* <img src="https://img.icons8.com/external-tal-revivo-color-tal-revivo/16/000000/external-firefox-a-free-and-open-source-web-browser-developed-by-the-mozilla-foundation-logo-color-tal-revivo.png"/> Firefox 1.5 and 2.0
|
||||
* <img src="https://img.icons8.com/color/16/000000/chrome--v1.png"/> Chrome 12.0
|
||||
* <img src="https://img.icons8.com/color/16/000000/internet-explorer.png"/> Internet Explorer 6 and 7
|
||||
* <img src="https://img.icons8.com/color/16/000000/safari--v1.png"/> Safari 2.0.4
|
||||
* <img src="https://img.icons8.com/color/16/000000/opera--v1.png"/> Opera 8.54 and 9.10
|
||||
* <img src="https://img.icons8.com/color/16/000000/netscape.png"/> Netscape 8.1.2
|
||||
* <img src="https://www.freepngimg.com/save/69063-konqueror-web-kde-manager-file-linux-browser/16x16"/> Konqueror 3.5.4
|
||||
|
||||
Generally, Showdown should work in any browser that supports ECMA 262 3<sup>rd</sup> Edition (JavaScript 1.5).
|
||||
|
||||
The converter might even work in things that aren't web browsers, like Acrobat. However, no promises.
|
||||
|
||||
## Node.js
|
||||
|
||||
Showdown is intended to work on any supported Node.js version (see the [Node.js releases schedule](https://nodejs.org/en/about/releases/).
|
||||
|
||||
Previous versions may also be supported, but no accomodations are made to ensure this.
|
60
docs/configuration.md
Normal file
60
docs/configuration.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
You can change Showdown's default behavior via options.
|
||||
|
||||
## Set option
|
||||
|
||||
### Globally
|
||||
|
||||
Setting an option globally affects all Showdown instances.
|
||||
|
||||
```js
|
||||
showdown.setOption('optionKey', 'value');
|
||||
```
|
||||
|
||||
### Locally
|
||||
|
||||
Setting an option locally affects the specified Converter object only. You can set local options via:
|
||||
|
||||
=== "Constructor"
|
||||
|
||||
```js
|
||||
var converter = new showdown.Converter({optionKey: 'value'});
|
||||
```
|
||||
|
||||
=== "setOption() method"
|
||||
|
||||
```js
|
||||
var converter = new showdown.Converter();
|
||||
converter.setOption('optionKey', 'value');
|
||||
```
|
||||
|
||||
## Get option
|
||||
|
||||
Showdown provides both local and global methods to retrieve previously set options:
|
||||
|
||||
=== "getOption()"
|
||||
|
||||
```js
|
||||
// Global
|
||||
var myOption = showdown.getOption('optionKey');
|
||||
|
||||
//Local
|
||||
var myOption = converter.getOption('optionKey');
|
||||
```
|
||||
|
||||
=== "getOptions()"
|
||||
|
||||
```js
|
||||
// Global
|
||||
var showdownGlobalOptions = showdown.getOptions();
|
||||
|
||||
//Local
|
||||
var thisConverterSpecificOptions = converter.getOptions();
|
||||
```
|
||||
|
||||
### Get default options
|
||||
|
||||
You can get Showdown's default options with:
|
||||
|
||||
```js
|
||||
var defaultOptions = showdown.getDefaultOptions();
|
||||
```
|
182
docs/create-extension.md
Normal file
182
docs/create-extension.md
Normal file
|
@ -0,0 +1,182 @@
|
|||
A Showdown extension is a function that returns an array of language or outputs extensions (henceforth called "sub-extensions").
|
||||
|
||||
```js
|
||||
var myext = function () {
|
||||
var myext1 = {
|
||||
type: 'lang',
|
||||
regex: /markdown/g,
|
||||
replace: 'showdown'
|
||||
};
|
||||
var myext2 = {
|
||||
/* extension code */
|
||||
};
|
||||
return [myext1, myext2];
|
||||
}
|
||||
```
|
||||
|
||||
Each sub-extension (`myext1` and `myext2` in the example above) should be an object that defines the behavior of the corresponding sub-extension.
|
||||
|
||||
## Sub-extension object properties
|
||||
|
||||
A sub-extension object should have a [`type` property](#type) that defines the type of the sub-extension, and either [`regex` and `replace` properties](#regex-and-replace) or a [`filter` property](#filter).
|
||||
|
||||
### Type
|
||||
|
||||
**Type** is a **required** property that defines the nature of the corresponding sub-extensions. It takes one of the two values:
|
||||
|
||||
* **`lang`**: language extension to add new Markdown syntax to Showdown.
|
||||
|
||||
`lang` extensions have the **highest priority** in the subparser order, so they are called after [escaping and normalizing](#escape-and-normalization) the input text and before calling any other subparser (or extension).
|
||||
|
||||
!!! example "When to use `lang` type"
|
||||
|
||||
For example, if you want the `^^youtube http://www.youtube.com/watch?v=oHg5SJYRHA0` syntax to automatically be rendered as an embedded YouTube video.
|
||||
|
||||
* **`output`**: output extension (or modifier) to alter the HTML output generated by Showdown.
|
||||
|
||||
`output` extensions have the **lowest priority** in the subparser order, so they are called right before the cleanup step and after calling all other subparsers.
|
||||
|
||||
!!! example "When to use `output` type"
|
||||
|
||||
For example, if you want the `<div class="header">` to become `<header>`.
|
||||
|
||||
### Regex and replace
|
||||
|
||||
`regex`/`replace` properties are similar to the Javascript's `string.replace` function and work the same way:
|
||||
|
||||
* `regex`: a `string` or a `RegExp` object.
|
||||
|
||||
If `regex` is a `string`, it will automatically be assigned a `g` (global) modifier, that is, all matches of that string will be replaced.
|
||||
|
||||
* `replace` a `string` or a `function`.
|
||||
|
||||
If `replace` is a `string`, you can use the `$1` syntax for group substitution, exactly as if it were making use of `string.replace`.
|
||||
|
||||
!!! example "Regex and replace example"
|
||||
|
||||
In this example, all the occurrences of `markdown` will be replaced with `showndown`.
|
||||
|
||||
```js
|
||||
var myext = {
|
||||
type: 'lang',
|
||||
regex: /markdown/g,
|
||||
replace: 'showdown'
|
||||
};
|
||||
```
|
||||
|
||||
### Filter
|
||||
|
||||
Alternately, if you'd like to have more control over the modification process, you can use `filter` property.
|
||||
|
||||
This property should be used as a function that acts as a callback. The callback should receive the following parameters:
|
||||
|
||||
1. `text`: the source text within the Showdown's engine.
|
||||
1. `converter`: the full instance of the current Showdown's converter object.
|
||||
1. `options`: the options used to initialize the converter
|
||||
|
||||
!!! warning ""
|
||||
The filter function **should return the transformed text**. If it doesn't, it will fail **silently** and return an empty output.
|
||||
|
||||
!!! example "Filter example"
|
||||
|
||||
```js
|
||||
var myext = {
|
||||
type: 'lang',
|
||||
filter: function (text, converter, options) {
|
||||
// ... do stuff to text ...
|
||||
return text;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
!!! warning "Use `filter` with care"
|
||||
|
||||
Although Filter extensions are more powerful, they have a few pitfalls that you should keep in mind before using them, especially regarding the `converter` parameter.
|
||||
|
||||
Since the `converter` parameter passed to the filter function is the fully initialized instance, any change made to it will be propagated outside the scope of the filter function and will remain there until a new converter instance is created. So, **it is not recommended to make ANY change to the converter object**.
|
||||
|
||||
Another aspect is that if you call the `converter` recursively, it will call your extension itself at some point. It may lead to infinite recursion in some circumstances, and it's up to you to prevent this. A simple solution is to place a kind of safeguard to disable your extension if it's called more than x times:
|
||||
|
||||
```js
|
||||
var x = 0;
|
||||
var myext = {
|
||||
type: 'lang',
|
||||
filter: function (text, converter) {
|
||||
if (x < 3) {
|
||||
++x;
|
||||
someSubText = converter.makeHtml(someSubText);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Register an extension
|
||||
|
||||
|
||||
To let Showdown know what extensions are available, you need to register them in the Showdown global object.
|
||||
|
||||
To register an extension, call the `showdown.extension` function with two parameters: the first one is the extension name; the second one is the actual extension.
|
||||
|
||||
```js
|
||||
showdown.extension('myext', myext);
|
||||
```
|
||||
|
||||
## Test an extension
|
||||
|
||||
The Showdown test runner is configured to automatically test cases for extensions.
|
||||
|
||||
To add test cases for an extension:
|
||||
|
||||
1. Create a new folder under `./test/extensions` that matches with the name of the `.js` file in `./src/extensions`.
|
||||
1. Place any test cases into the filter using the `md/html` format. These cases will automatically be executed when running tests.
|
||||
|
||||
## Additional information
|
||||
|
||||
### Escape and normalization
|
||||
|
||||
Showdown performs the following escape/normalization:
|
||||
|
||||
* Replaces `¨` (trema) with `¨T`
|
||||
* Replaces `$` (dollar sign) with `¨D`
|
||||
* Normalizes line endings (`\r`, `\r\n` are converted into `\n`)
|
||||
* Uses `\r` as a char placeholder
|
||||
|
||||
!!! note ""
|
||||
This only applies to **language extensions** since these chars are unescaped before output extensions are run.
|
||||
|
||||
!!! warning ""
|
||||
|
||||
Keep in mind that these modifications happen **before language extensions** are run, so if your extension relies on any of those chars, you have to make the appropriate adjustments.
|
||||
|
||||
|
||||
### Implementation concerns
|
||||
|
||||
One of the concerns is maintaining both client-side and server-side compatibility. You can do this with a few lines of boilerplate code.:
|
||||
|
||||
```js
|
||||
(function (extension) {
|
||||
if (typeof showdown !== 'undefined') {
|
||||
// global (browser or node.js global)
|
||||
extension(showdown);
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
// AMD
|
||||
define(['showdown'], extension);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node, CommonJS-like
|
||||
module.exports = extension(require('showdown'));
|
||||
} else {
|
||||
// showdown was not found so an error is thrown
|
||||
throw Error('Could not find showdown library');
|
||||
}
|
||||
}(function (showdown) {
|
||||
// loading extension into showdown
|
||||
showdown.extension('myext', function () {
|
||||
var myext = { /* ... actual extension code ... */ };
|
||||
return [myext];
|
||||
});
|
||||
}));
|
||||
```
|
||||
|
||||
In the code above, the extension definition is wrapped in a self-executing function to prevent pollution of the global scope. It has another benefit of creating several scope layers that can be useful for interaction between sub-extensions global-wise or local-wise.
|
||||
|
||||
It is also loaded conditionally to make it compatible with different loading mechanisms (such as browser, CommonJS, or AMD).
|
35
docs/credits.md
Normal file
35
docs/credits.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
=== "v.2"
|
||||
|
||||
* [Estevão Santos](https://github.com/tivie)
|
||||
* [SyntaxRules](https://github.com/SyntaxRules)
|
||||
|
||||
=== "v.1"
|
||||
|
||||
* [Estevão Santos](https://github.com/tivie)
|
||||
* [Pascal Deschênes](https://github.com/pdeschen)
|
||||
|
||||
=== "v.0"
|
||||
|
||||
* [Corey Innis](http://github.com/coreyti) - Original GitHub project maintainer
|
||||
* [Remy Sharp](https://github.com/remy/) - CommonJS-compatibility and more
|
||||
* [Konstantin Käfer](https://github.com/kkaefer/) - CommonJS packaging
|
||||
* [Roger Braun](https://github.com/rogerbraun) - GitHub-style code blocks
|
||||
* [Dominic Tarr](https://github.com/dominictarr) - Documentation
|
||||
* [Cat Chen](https://github.com/CatChen) - Export fix
|
||||
* [Titus Stone](https://github.com/tstone) - Mocha tests, extension mechanism, bug fixes
|
||||
* [Rob Sutherland](https://github.com/roberocity) - The idea that lead to extensions
|
||||
* [Pavel Lang](https://github.com/langpavel) - Code cleanup
|
||||
* [Ben Combee](https://github.com/unwiredben) - Regex optimization
|
||||
* [Adam Backstrom](https://github.com/abackstrom) - WebKit bug fixes
|
||||
* [Pascal Deschênes](https://github.com/pdeschen) - Grunt support, extension fixes + additions, packaging improvements, documentation
|
||||
* [Estevão Santos](https://github.com/tivie) - Bug fixes and late maintainer
|
||||
* [Hannah Wolfe](https://github.com/ErisDS) - Bug fixes
|
||||
* [Alexandre Courtiol](https://github.com/acourtiol) - Bug fixes and build optimization
|
||||
* [Karthik Balakrishnan](https://github.com/torcellite) - Support for table alignment
|
||||
* [rheber](https://github.com/rheber) - CLI
|
||||
|
||||
|
||||
=== "Original Project"
|
||||
|
||||
* [John Gruber](http://daringfireball.net/projects/markdown/) - Author of Markdown
|
||||
* [John Fraser](http://attacklab.net/) - Author of Showdown
|
7
docs/donations.md
Normal file
7
docs/donations.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
ShowdownJS is a **free** library and it will remain **free forever**.
|
||||
|
||||
However, maintaining and improving the library costs time and money.
|
||||
|
||||
If you like our work and find it useful, please donate through [PayPal](https://www.paypal.me/tiviesantos).
|
||||
|
||||
:heart: :pray: Your contributions are greatly appreciated and will help us with the development of this awesome library.
|
149
docs/event_system.md
Normal file
149
docs/event_system.md
Normal file
|
@ -0,0 +1,149 @@
|
|||
# Event System
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
## The Event Object
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
Events are raised when a subparser is run (or about to be run).
|
||||
Within a subparser, the events always follow a certain order (sequence). For instance, **.before** events always run before **.captureStart**.
|
||||
Each subparser raises several events sequentially:
|
||||
|
||||
1. **.start**: **always runs** except it subparser is disabled
|
||||
|
||||
Raised when the **subparser has started**, but no capturing or any modification to the text was done.
|
||||
|
||||
**Always runs** (except if the subparser is deactivated through options).
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|-----------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The full text that was passed to the subparser |
|
||||
| output | string | write | The full text with modification that will be passed along the chain|
|
||||
| regexp | null | | |
|
||||
| matches | null | | |
|
||||
|
||||
Usually you would want to use this event if you wish to change the input to the subparser
|
||||
|
||||
2. **.captureStart**: *might not be run*;
|
||||
|
||||
Raised when a regex match is found and a capture was successful. Some normalization and modification
|
||||
of the regex captured groups might be performed.
|
||||
|
||||
Might not be run if no regex match is found.
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|-----------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The captured text |
|
||||
| output | string | write | The text that will be passed to the subparser/other listeners |
|
||||
| regexp | RegExp | readonly | Regular Expression used to capture groups |
|
||||
| matches | object | read/write | Matches groups. Changes to this object are reflected in the output |
|
||||
|
||||
Usually you would want to use this event if you wish to modify a certain subparser behavior.
|
||||
Exs: remove all title attributes from links; change indentation of code blocks; etc...
|
||||
|
||||
3. **.captureEnd**: *might not be run*;
|
||||
|
||||
Raised after the modifications to the captured text are done but before the replacement is introduced in the document.
|
||||
|
||||
Might not be run if no regex match is found.
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|------------|-----------|------------|--------------------------------------------------------------------------------|
|
||||
| input | string | read | The captured text |
|
||||
| output | string | write | The text that will be passed to the subparser/other listeners |
|
||||
| regexp | RegExp | readonly | Regular Expression used to capture groups |
|
||||
| matches | object | read/write | Keypairs of matches groups. Changes to this object are reflected in the output |
|
||||
| attributes | object | read/write | Attributes to add to the HTML output |
|
||||
|
||||
4. **.beforeHash**: *might not be run*;
|
||||
|
||||
Raised before the output is hashed.
|
||||
|
||||
Always run (except if the subparser was deactivated through options), even if no hashing is performed.
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|------------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The captured text |
|
||||
| output | string | write | The text that will be passed to the subparser/other listeners |
|
||||
| regexp | null | | |
|
||||
| matches | null | | |
|
||||
|
||||
Usually you would want to use this event if you wish change the subparser output before it is hashed
|
||||
|
||||
5. **.end**: *always runs*;
|
||||
|
||||
Raised when the subparser has finished its work and is about to exit.
|
||||
|
||||
Always runs (except if the subparser is deactivated through options).
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|-----------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The partial/full text with the subparser modifications |
|
||||
| output | string | write | The text that will be passed to other subparsers |
|
||||
| regexp | null | | |
|
||||
| matches | null | | |
|
||||
|
||||
Usually you would want to use this event if you wish change the subparser hashed output
|
||||
|
||||
|
||||
### Special Events
|
||||
|
||||
There are some special events that are useful for *"positioning"* a listener extension in the main chain of events.
|
||||
Usually these extensions introduce new syntax that, due to precedence
|
||||
These events are always guaranteed to be called, regardless of options or circumstances.
|
||||
|
||||
1. **.before_{subparserName}**: *always runs*
|
||||
|
||||
Raised just before the **{subparserName} is about to be entered**.
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|-----------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The full text that was passed to the subparser |
|
||||
| output | string | write | The full text with modification that will be passed along the chain|
|
||||
| regexp | null | | |
|
||||
| matches | null | | |
|
||||
|
||||
2. **.after**.{subparserName}: *always runs*;
|
||||
|
||||
Raised when the **{subparserName} has exited** and before the next one is called.
|
||||
|
||||
***Properties***:
|
||||
|
||||
| property | type | access | description |
|
||||
|----------|-----------|------------|--------------------------------------------------------------------|
|
||||
| input | string | read | The partial/full text with the subparser modifications |
|
||||
| output | string | write | The text that will be passed to other subparsers |
|
||||
| regexp | null | | |
|
||||
| matches | null | | |
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
- There are 2 main differences between **before.{subparserName}** and **{subparserName}.start**.
|
||||
|
||||
1. **before.{subparserName}** is always guaranteed to be called, even if the subparser is disabled,
|
||||
while **{subparserName}.start** doesn't.
|
||||
|
||||
ex: `makehtml.before.strikethrough` is always called even if the option `strikethrough` is false
|
||||
|
||||
2. **before.{subparserName}** is only raised once in a span context while **{subparserName}.start** is raised
|
||||
everytime **{subparserName}** is called.
|
||||
|
||||
As a rule of thumb,
|
||||
|
||||
## Events List
|
24
docs/extensions-list.md
Normal file
24
docs/extensions-list.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
## Official
|
||||
|
||||
* [twitter-extension][1] - Adds support of Twitter usernames and hastags
|
||||
* [prettify-extension][2] - Adds [Google Prettify][3] hints to HTML output
|
||||
|
||||
## Community
|
||||
|
||||
* [showdown-icon][4] - Adds support of Glyphicon and font-awesome into Markdown
|
||||
* [showdown-xss-filter][5] - Filters XSS, using leizongmin/js-xss
|
||||
* [showdown-toc][6] - Adds Table of Contents
|
||||
* [showdown-footnotes][7] - Adds simple footnotes
|
||||
* [katex-latex][8] - Displays math using KaTeX and LaTeX or AsciiMath
|
||||
|
||||
!!! note ""
|
||||
If you have a Showdown extension you would like to add here, you can [raise an issue](https://github.com/showdownjs/showdown/issues).
|
||||
|
||||
[1]: https://github.com/showdownjs/twitter-extension
|
||||
[2]: https://github.com/showdownjs/prettify-extension
|
||||
[3]: https://github.com/googlearchive/code-prettify
|
||||
[4]: https://github.com/dbtek/showdown-icon
|
||||
[5]: https://github.com/VisionistInc/showdown-xss-filter
|
||||
[6]: https://github.com/ravisorg/showdown-toc
|
||||
[7]: https://github.com/Kriegslustig/showdown-footnotes
|
||||
[8]: https://obedm503.github.io/showdown-katex
|
36
docs/extensions.md
Normal file
36
docs/extensions.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
Showdown allows you to load additional functionality via extensions. You can find a list of known Showdown extensions [here][ext-wiki].
|
||||
|
||||
You can also check the [boilerplate repo][boilerplate-repo], to create your own extension(s).
|
||||
|
||||
## Usage
|
||||
|
||||
=== "Server-side"
|
||||
|
||||
```js
|
||||
// Using a bundled extension
|
||||
var showdown = require('showdown');
|
||||
var converter = new showdown.Converter({ extensions: ['twitter'] });
|
||||
|
||||
// Using a custom extension
|
||||
var mine = require('./custom-extensions/mine');
|
||||
var converter = new showdown.Converter({ extensions: ['twitter', mine] });
|
||||
```
|
||||
|
||||
=== "Client-side"
|
||||
|
||||
```js
|
||||
<script src="src/showdown.js"></script>
|
||||
<script src="src/extensions/twitter.js"></script>
|
||||
<script>var converter = new showdown.Converter({ extensions: ['twitter'] });</script>
|
||||
```
|
||||
|
||||
=== "CLI"
|
||||
|
||||
In the CLI tool, use the [`-e` flag](/cli/#-e-extensions) to load an extension.
|
||||
|
||||
```sh
|
||||
showdown -e twitter -i foo.md -o bar.html
|
||||
```
|
||||
|
||||
[ext-wiki]: https://github.com/showdownjs/showdown/wiki/extensions
|
||||
[boilerplate-repo]: https://github.com/showdownjs/extension-boilerplate
|
23
docs/flavors.md
Normal file
23
docs/flavors.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
## Overview
|
||||
|
||||
You can use _flavors_ (or presets) to set the preferred options automatically. In this way, Showdown behaves like popular Markdown flavors.
|
||||
|
||||
Currently, the following flavors are available:
|
||||
|
||||
* `original`: Original Markdown flavor as in [John Gruber's spec](https://daringfireball.net/projects/markdown/)
|
||||
* `vanilla`: Showdown base flavor (v1.3.1 onwards)
|
||||
* `github`: [GitHub Flavored Markdown, or GFM](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
|
||||
|
||||
## Set flavor
|
||||
|
||||
=== "Globally"
|
||||
|
||||
```js
|
||||
showdown.setFlavor('github');
|
||||
```
|
||||
|
||||
=== "Locally"
|
||||
|
||||
```js
|
||||
converter.setFlavor('github');
|
||||
```
|
51
docs/index.md
Normal file
51
docs/index.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Showdown documentation
|
||||
|
||||
![Showdown][sd-logo]
|
||||
|
||||
![Build Status: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.linux.yml/badge.svg)
|
||||
![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)
|
||||
[![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)
|
||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/tiviesantos)
|
||||
|
||||
Showdown is a JavaScript Markdown to HTML converter, based on the original works by John Gruber.
|
||||
Showdown can be used on the client-side (in the browser) or server-side (with Node.js).
|
||||
|
||||
----
|
||||
|
||||
## Live demo
|
||||
|
||||
<http://demo.showdownjs.com/>
|
||||
|
||||
## Who uses Showdown (or a fork)
|
||||
|
||||
* [Antmarky](https://github.com/bandantonio/antmarky)
|
||||
* [GoogleCloudPlatform](https://github.com/GoogleCloudPlatform)
|
||||
* [Meteor](https://www.meteor.com/)
|
||||
* [StackExchange](http://stackexchange.com/) - forked as [PageDown](https://code.google.com/p/pagedown/)
|
||||
* [docular](https://github.com/Vertafore/docular)
|
||||
* [md-page](https://github.com/oscarmorrison/md-page)
|
||||
* [QCObjects](https://qcobjects.dev)
|
||||
* [and some others](https://www.npmjs.com/browse/depended/showdown)
|
||||
|
||||
## Installation
|
||||
|
||||
To install Showdown, follow the instructions from the [Quickstart guide](quickstart.md).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
ShowdownJS v 2.0 is release under the MIT version.
|
||||
|
||||
Previous versions are release under BSD.
|
||||
|
||||
[sd-logo]: https://raw.githubusercontent.com/showdownjs/logo/master/dist/logo.readme.png
|
||||
[wiki]: https://github.com/showdownjs/showdown/wiki
|
||||
[cli-wiki]: https://github.com/showdownjs/showdown/wiki/CLI-tool
|
||||
[definitely-typed]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/showdown
|
||||
[xss-wiki]: https://github.com/showdownjs/showdown/wiki/Markdown's-XSS-Vulnerability-(and-how-to-mitigate-it)
|
||||
[ext-wiki]: https://github.com/showdownjs/showdown/wiki/extensions
|
||||
[coding-rules]: https://github.com/showdownjs/code-style/blob/master/README.md
|
||||
[ng-commit-guide]: https://github.com/showdownjs/code-style/blob/master/README.md#commit-message-convention
|
||||
[boilerplate-repo]: https://github.com/showdownjs/extension-boilerplate
|
20
docs/integrations.md
Normal file
20
docs/integrations.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## AngularJS
|
||||
|
||||
ShowdownJS project provides seamless integration with AngularJS via a plugin.
|
||||
|
||||
Check [`ng-showdown`](https://github.com/showdownjs/ngShowdown) repository for more information.
|
||||
|
||||
## TypeScript
|
||||
|
||||
If you're using TypeScript, you may want to use the types from the [DefinitelyTyped][definitely-typed] repository.
|
||||
|
||||
## SystemJS/JSPM
|
||||
|
||||
To integrate ShowdownJS with SystemJS, you can use a third-party [system-md plugin](https://github.com/guybedford/system-md).
|
||||
|
||||
## Vue.js
|
||||
|
||||
To use ShowdownJS as a Vue component, you can check [vue-showdown](https://vue-showdown.js.org/).
|
||||
|
||||
|
||||
[definitely-typed]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/showdown
|
716
docs/markdown-syntax.md
Normal file
716
docs/markdown-syntax.md
Normal file
|
@ -0,0 +1,716 @@
|
|||
## Introduction
|
||||
|
||||
Showdown was created by John Fraser as a direct port of the original parser written by Markdown's creator, John Gruber.
|
||||
|
||||
Although Showdown has evolved since its inception, in "vanilla mode", it tries to follow the [original markdown spec][md-spec] (henceforth referred as vanilla) as much as possible. There are, however, a few important differences, mainly due to inconsistencies in the original spec, which Showdown addressed following the author's advice as stated in the [markdown's "official" newsletter][md-newsletter].
|
||||
|
||||
Showdown also supports opt-in features, that is, an "extra" syntax that is not defined in the original spec. Users can enable these features via options (All the new syntax elements are disabled by default).
|
||||
|
||||
This document provides a quick reference of the supported syntax and the differences in output from the original markdown.pl implementation.
|
||||
|
||||
## Paragraphs
|
||||
|
||||
Paragraphs in Showdown are **one or more lines of consecutive text** followed by one or more blank lines.
|
||||
|
||||
```md
|
||||
On July 2, an alien mothership entered Earth's orbit and deployed several dozen
|
||||
saucer-shaped "destroyer" spacecraft, each 15 miles (24 km) wide.
|
||||
|
||||
On July 3, the Black Knights, a squadron of Marine Corps F/A-18 Hornets,
|
||||
participated in an assault on a destroyer near the city of Los Angeles.
|
||||
```
|
||||
|
||||
The implication of the "one or more consecutive lines of text" is that Showdown supports
|
||||
"hard-wrapped" text paragraphs. It means the following examples produce the same output:
|
||||
|
||||
```md
|
||||
A very long line of text
|
||||
```
|
||||
|
||||
```md
|
||||
A very
|
||||
long line
|
||||
of text
|
||||
```
|
||||
|
||||
If you **do** want to add soft line breaks (which translate to `<br>` in HTML) to a paragraph,
|
||||
you can do so by adding 3 space characters to the end of the line.
|
||||
|
||||
You can also force every line break in paragraphs to translate to `<br>` (as Github does) by
|
||||
enabling the option [**`simpleLineBreaks`**][simpleLineBreaks].
|
||||
|
||||
[simpleLineBreaks]: available-options.md#simplelinebreaks
|
||||
|
||||
## Headings
|
||||
|
||||
### Atx Style
|
||||
|
||||
You can create a heading by adding one or more `#` symbols before your heading text. The number of `#` determines the level of the heading. This is similar to [**atx style**][atx].
|
||||
|
||||
```md
|
||||
# The 1st level heading (an <h1> tag)
|
||||
## The 2nd level heading (an <h2> tag)
|
||||
…
|
||||
###### The 6th level heading (an <h6> tag)
|
||||
```
|
||||
|
||||
The space between `#` and the heading text is not required but you can make it mandatory by enabling the option [**`requireSpaceBeforeHeadingText`**][requireSpaceBeforeHeadingText].
|
||||
|
||||
[requireSpaceBeforeHeadingText]: available-options.md#requirespacebeforeheadingtext
|
||||
|
||||
You can wrap the headings in `#`. Both leading and trailing `#` will be removed.
|
||||
|
||||
```md
|
||||
## My Heading ##
|
||||
```
|
||||
|
||||
If, for some reason, you need to keep a leading or trailing `#`, you can either add a space or escape it:
|
||||
|
||||
```md
|
||||
# # My header # #
|
||||
|
||||
#\# My Header \# #
|
||||
```
|
||||
|
||||
### Setext style
|
||||
|
||||
You can also use [**setext style**][setext] headings, although only two levels are available.
|
||||
|
||||
```md
|
||||
This is an H1
|
||||
=============
|
||||
|
||||
This is an H2
|
||||
-------------
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
There is an awkward effect when a paragraph is followed by a list. This effect appears on some circumstances, in live preview editors.
|
||||
|
||||
![awkward effect][]
|
||||
|
||||
You can prevent this by enabling the option [**`smoothPreview`**][smoothlivepreview].
|
||||
|
||||
[smoothlivepreview]: available-options.md#smoothlivepreview
|
||||
|
||||
### Header IDs
|
||||
|
||||
Showdown automatically generates bookmark anchors in titles by adding an id property to a heading.
|
||||
|
||||
```md
|
||||
# My cool header with ID
|
||||
```
|
||||
|
||||
```html
|
||||
<h1 id="mycoolheaderwithid">My cool header with ID</h1>
|
||||
```
|
||||
|
||||
This behavior can be modified with options:
|
||||
|
||||
- [**`noHeaderId`**][noHeaderId] disables automatic id generation;
|
||||
- [**`ghCompatibleHeaderId`**][ghCompatibleHeaderId] generates header ids compatible with github style (spaces are replaced with dashes and a bunch of non alphanumeric chars are removed)
|
||||
- [**`prefixHeaderId`**][prefixHeaderId] adds a prefix to the generated header ids (either automatic or custom).
|
||||
- [**`headerLevelStart`**][headerLevelStart] sets the header starting level. For instance, setting this to 3 means that `# header` will be converted to `<h3>`.
|
||||
|
||||
Read the [README.md][readme] for more info
|
||||
|
||||
[noHeaderId]: available-options.md#noheaderid
|
||||
[ghCompatibleHeaderId]: available-options.md#ghcompatibleheaderid
|
||||
[prefixHeaderId]: available-options.md#prefixheaderid
|
||||
[headerLevelStart]: available-options.md#headerlevelstart
|
||||
|
||||
## Blockquotes
|
||||
|
||||
You can indicate blockquotes with a `>`.
|
||||
|
||||
```md
|
||||
In the words of Abraham Lincoln:
|
||||
|
||||
> Pardon my french
|
||||
```
|
||||
|
||||
Blockquotes can have multiple paragraphs and can have other block elements inside.
|
||||
|
||||
```md
|
||||
> A paragraph of text
|
||||
>
|
||||
> Another paragraph
|
||||
>
|
||||
> - A list
|
||||
> - with items
|
||||
```
|
||||
|
||||
## Bold and Italic
|
||||
|
||||
You can make text bold or italic.
|
||||
|
||||
```md
|
||||
*This text will be italic*
|
||||
**This text will be bold**
|
||||
```
|
||||
|
||||
Both bold and italic can use either a `*` or an `_` around the text for styling. This allows you to combine both bold and italic if needed.
|
||||
|
||||
```md
|
||||
**Everyone _must_ attend the meeting at 5 o'clock today.**
|
||||
```
|
||||
|
||||
## Strikethrough
|
||||
|
||||
With the option [**`strikethrough`**][] enabled, Showdown supports strikethrough elements.
|
||||
The syntax is the same as GFM, that is, by adding two tilde (`~~`) characters around
|
||||
a word or groups of words.
|
||||
|
||||
```md
|
||||
a ~~strikethrough~~ element
|
||||
```
|
||||
|
||||
a <s>strikethrough</s> element
|
||||
|
||||
[strikethrough]: available-options.md#strikethrough
|
||||
|
||||
## Emojis
|
||||
|
||||
Since version 1.8.0, Showdown supports Github's emojis. A complete list of available emojis can be found [here][emoji list].
|
||||
|
||||
```md
|
||||
this is a :smile: smile emoji
|
||||
```
|
||||
|
||||
this is a :smile: smile emoji
|
||||
|
||||
## Code formatting
|
||||
|
||||
### Inline formats
|
||||
|
||||
Use single backticks (`) to format text in a special monospace format. Everything within the backticks appear as-is, with no other special formatting.
|
||||
|
||||
```md
|
||||
Here's an idea: why don't we take `SuperiorProject` and turn it into `**Reasonable**Project`.
|
||||
```
|
||||
|
||||
```html
|
||||
<p>Here's an idea: why don't we take <code>SuperiorProject</code> and turn it into <code>**Reasonable**Project</code>.</p>
|
||||
```
|
||||
|
||||
### Multiple lines
|
||||
|
||||
To create blocks of code you should indent it by four spaces.
|
||||
|
||||
```md
|
||||
this is a piece
|
||||
of
|
||||
code
|
||||
```
|
||||
|
||||
If the option [**`ghCodeBlocks`**][ghCodeBlocks] is activated (which is by default), you can use triple backticks <code>```</code> to format text as its own distinct block.
|
||||
|
||||
Check out this neat program I wrote:
|
||||
|
||||
```
|
||||
x = 0
|
||||
x = 2 + 2
|
||||
what is x
|
||||
```
|
||||
|
||||
[ghCodeBlocks]: available-options.md#ghcodeblocks
|
||||
|
||||
## Lists
|
||||
|
||||
Showdown supports unordered (bulleted) and ordered (numbered) lists.
|
||||
|
||||
### Unordered lists
|
||||
|
||||
You can make an unordered list by preceding list items with either `*`, `-`, or `+`. Markers are interchangeable too.
|
||||
|
||||
```md
|
||||
* Item
|
||||
+ Item
|
||||
- Item
|
||||
```
|
||||
|
||||
### Ordered lists
|
||||
|
||||
You can make an ordered list by preceding list items with a number.
|
||||
|
||||
```md
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
3. Item 3
|
||||
```
|
||||
|
||||
!!! earning ""
|
||||
The actual numbers you use to mark the list have no effect on the HTML output that Showdown produces. So you can use the same number in all items if you wish to. For example:
|
||||
|
||||
```md
|
||||
1. Item 1
|
||||
1. Item 2
|
||||
1. Item 3
|
||||
|
||||
2. Item 1
|
||||
2. Item 2
|
||||
2. Item 3
|
||||
```
|
||||
|
||||
### TaskLists (GFM Style)
|
||||
|
||||
Showdown supports GFM-styled takslists if the [**`tasklists`**][tasklists] option is enabled.
|
||||
|
||||
```md
|
||||
- [x] checked list item
|
||||
- [ ] unchecked list item
|
||||
```
|
||||
|
||||
- [x] checked list item
|
||||
- [ ] unchecked list item
|
||||
|
||||
|
||||
[tasklists]: available-options.md#tasklists
|
||||
|
||||
### List syntax
|
||||
|
||||
List markers typically start at the left margin, but may be indented by up to three spaces.
|
||||
|
||||
```md
|
||||
* valid list item
|
||||
* this is valid too
|
||||
* this is too
|
||||
```
|
||||
|
||||
List markers must be followed by one or more spaces or a tab.
|
||||
|
||||
To make lists look nicer, you can wrap items with hanging indents:
|
||||
|
||||
```md
|
||||
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||
viverra nec, fringilla in, laoreet vitae, risus.
|
||||
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
```
|
||||
|
||||
But if you want to be lazy, you don't have to :grin:
|
||||
|
||||
If one list item is separated by a blank line, Showdown will wrap all the list items in `<p>` tags in the HTML output.
|
||||
So this input:
|
||||
|
||||
```md
|
||||
* Bird
|
||||
|
||||
* Magic
|
||||
* Johnson
|
||||
```
|
||||
|
||||
results in:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li><p>Bird</p></li>
|
||||
<li><p>Magic</p></li>
|
||||
<li><p>Johnson</p></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
This differs from other Markdown implementations such as GFM (GitHub) or CommonMark.
|
||||
|
||||
### Nested blocks
|
||||
|
||||
List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be indented by either 4 spaces or one tab:
|
||||
|
||||
```md
|
||||
1. This is a list item with two paragraphs. Lorem ipsum dolor
|
||||
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
|
||||
mi posuere lectus.
|
||||
|
||||
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
|
||||
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
|
||||
sit amet velit.
|
||||
|
||||
2. Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
```
|
||||
|
||||
This is valid for other block elements such as blockquotes:
|
||||
|
||||
```md
|
||||
* A list item with a blockquote:
|
||||
|
||||
> This is a blockquote
|
||||
> inside a list item.
|
||||
```
|
||||
|
||||
or even other lists.
|
||||
|
||||
### Nested lists
|
||||
|
||||
You can create nested lists by indenting list items by **four** spaces.
|
||||
|
||||
```md
|
||||
1. Item 1
|
||||
1. A corollary to the above item.
|
||||
2. Yet another point to consider.
|
||||
2. Item 2
|
||||
* A corollary that does not need to be ordered.
|
||||
* This is indented four spaces
|
||||
* You might want to consider making a new list.
|
||||
3. Item 3
|
||||
```
|
||||
|
||||
This behavior is consistent with the original spec but differs from other implementations such as GFM or CommonMark. Prior to version 1.5, you just needed to indent two spaces for it to be considered a sublist.
|
||||
|
||||
You can disable the **four spaces requirement** with option [**`disableForced4SpacesIndentedSublists`**][disableForced4SpacesIndentedSublists]
|
||||
|
||||
To nest a third (or more) sublist level, you need to indent 4 extra spaces (or 1 extra tab) for each level:
|
||||
|
||||
```md
|
||||
1. level 1
|
||||
1. Level 2
|
||||
* Level 3
|
||||
2. level 2
|
||||
1. Level 3
|
||||
1. Level 1
|
||||
```
|
||||
[disableForced4SpacesIndentedSublists]: available-options.md#disableforced4spacesindentedsublists
|
||||
|
||||
### Nested code blocks
|
||||
|
||||
You can nest fenced codeblocks the same way you nest other block elements, by indenting by four spaces or a tab:
|
||||
|
||||
```md
|
||||
1. Some code:
|
||||
|
||||
```js
|
||||
var foo = 'bar';
|
||||
console.log(foo);
|
||||
```
|
||||
```
|
||||
|
||||
To put an *indented style* code block within a list item, the code block needs to be indented twice — 8 spaces or two tabs:
|
||||
|
||||
```md
|
||||
1. Some code:
|
||||
|
||||
var foo = 'bar';
|
||||
console.log(foo);
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
### Simple
|
||||
|
||||
If you wrap a valid URL or email in `<>` it will be turned into a link whose text is the link itself.
|
||||
|
||||
```md
|
||||
link to <http://www.google.com/>
|
||||
|
||||
this is my email <somedude@mail.com>
|
||||
```
|
||||
|
||||
In the case of email addresses, Showdown also performs a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots.
|
||||
You can disable this obfuscation by setting [**`encodeEmails`**][encodeEmails] option to `false`.
|
||||
|
||||
With the option [**`simplifiedAutoLink`**][simplifiedAutoLink] enabled, Showdown will automagically turn every valid URL it finds in the text body into links without the need to wrap them in `<>`.
|
||||
|
||||
```md
|
||||
link to http://www.google.com/
|
||||
|
||||
this is my email somedude@mail.com
|
||||
```
|
||||
|
||||
[encodeEmails]: available-options.md#encodeemails
|
||||
[simplifiedAutoLink]: available-options.md#simplifiedautolink
|
||||
|
||||
### Inline
|
||||
|
||||
You can create an inline link by wrapping link text in brackets `[ ]`, and then wrapping the link in parentheses `( )`.
|
||||
|
||||
For example, a hyperlink to `github.com/showdownjs/showdown`, with a link text that says, `Get Showdown!` will look as follows:
|
||||
|
||||
```
|
||||
[Get Showdown!](https://github.com/showdownjs/showdown)
|
||||
```
|
||||
|
||||
### Reference Style
|
||||
|
||||
You can also use the reference style, like this:
|
||||
|
||||
```md
|
||||
this is a [link to google][1]
|
||||
|
||||
[1]: www.google.com
|
||||
```
|
||||
|
||||
Showdown also supports implicit link references:
|
||||
|
||||
```md
|
||||
this is a link to [google][]
|
||||
|
||||
[google]: www.google.com
|
||||
```
|
||||
|
||||
## Images
|
||||
|
||||
In Markdown, the syntax for images is similar to that of links, supporting both inline and reference styles as well. The only difference in syntax for images is the leading exclamation mark before brackets: `![]`.
|
||||
|
||||
### Inline
|
||||
|
||||
Inline image syntax looks like this:
|
||||
|
||||
```md
|
||||
![Alt text](url/to/image)
|
||||
|
||||
![Alt text](url/to/image "Optional title")
|
||||
```
|
||||
|
||||
That is:
|
||||
|
||||
* An exclamation mark: `!`
|
||||
* followed by a set of square brackets `[ ]` containing the alt attribute text for the image
|
||||
* followed by a set of parentheses `( )` containing the URL or path to the image and an optional title attribute enclosed in double or single quotes.
|
||||
|
||||
|
||||
### Reference Style
|
||||
|
||||
Reference-style image syntax looks like this:
|
||||
|
||||
```md
|
||||
![Alt text][id]
|
||||
```
|
||||
|
||||
Where `id` is the name of a defined image reference. Image references are defined using syntax identical to link references:
|
||||
|
||||
```md
|
||||
[id]: url/to/image "Optional title attribute"
|
||||
```
|
||||
|
||||
Implicit references are also supported:
|
||||
|
||||
```md
|
||||
![showdown logo][]
|
||||
|
||||
[showdown logo]: http://showdownjs.github.io/demo/img/editor.logo.white.png
|
||||
```
|
||||
|
||||
### Image dimensions
|
||||
|
||||
When the option [**`parseImgDimensions`**][parseImgDimensions] is activated, you can define the image dimensions, like this:
|
||||
|
||||
```md
|
||||
![Alt text](url/to/image =250x250 "Optional title")
|
||||
```
|
||||
|
||||
or in reference style:
|
||||
|
||||
```md
|
||||
![Alt text][id]
|
||||
|
||||
[id]: url/to/image =250x250
|
||||
```
|
||||
|
||||
[parseImgDimensions]: available-options.md#parseimgdimensions
|
||||
|
||||
### Base64 encoded images
|
||||
|
||||
Showdown supports Base64 encoded images, both reference and inline style.
|
||||
|
||||
**Since version 1.7.4**, Showdown supports wrapping of base64 strings, which are usually extremely long lines of text.
|
||||
You can add newlines arbitrarily, as long as they are added after the `,` character.
|
||||
|
||||
inline style
|
||||
|
||||
```md
|
||||
![Alt text](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7l
|
||||
jmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAY
|
||||
SURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=)
|
||||
```
|
||||
|
||||
reference style
|
||||
|
||||
```md
|
||||
![Alt text][id]
|
||||
|
||||
[id]:
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7l
|
||||
jmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7D
|
||||
AcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
With reference-style base64 image sources, regardless of "wrapping", a double newline is **required** after the base64 string to separate them from a paragraph or other text block (but references can be adjacent):
|
||||
|
||||
!!! example "Wrapped reference style"
|
||||
|
||||
```md
|
||||
![Alt text][id]
|
||||
![Alt text][id2]
|
||||
|
||||
[id]:
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7l
|
||||
jmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7D
|
||||
AcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=
|
||||
[id2]:
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7l
|
||||
jmRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7D
|
||||
AcdvqGQAAAAYSURBVBhXYwCC/2AAZYEoOAMs8Z+BgQEAXdcR7/Q1gssAAAAASUVORK5CYII=
|
||||
|
||||
|
||||
this text needs to be separated from the references by 2 newlines
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
Tables aren't part of the core Markdown spec, but they are part of GFM. You can enable them in Showdown via the option [**`tables`**][tables].
|
||||
|
||||
* Colons can be used to align columns.
|
||||
* The outer pipes (`|`) are optional, matching GFM spec.
|
||||
* You don't need to make the raw Markdown line up prettily.
|
||||
* You can use other Markdown syntax inside them.
|
||||
|
||||
```md
|
||||
| Tables | Are | Cool |
|
||||
| ------------- |:-------------:| -----:|
|
||||
| **col 3 is** | right-aligned | $1600 |
|
||||
| col 2 is | *centered* | $12 |
|
||||
| zebra stripes | ~~are neat~~ | $1 |
|
||||
```
|
||||
|
||||
[tables]: available-options.md#tables
|
||||
|
||||
## Mentions
|
||||
|
||||
Showdown supports GitHub mentions by enabling the option [**`ghMentions`**][mentions]. This will turn every `@username` into a link to their github profile.
|
||||
|
||||
```md
|
||||
hey @tivie, check this out
|
||||
```
|
||||
|
||||
Since version 1.6.2, you can customize the generated link in mentions with the option [**`ghMentionsLink`**][ghMentionsLink].
|
||||
|
||||
For example, setting this option to `http://mysite.com/{u}/profile`:
|
||||
|
||||
```html
|
||||
<p>hey <a href="http://mysite.com/tivie/profile">@tivie</a>, check this out</p>
|
||||
```
|
||||
|
||||
[mentions]: available-options.md#ghmentions
|
||||
[ghMentionsLink]: available-options.md#ghmentionslink
|
||||
|
||||
## Handle HTML in markdown documents
|
||||
|
||||
Showdown, in most cases, leaves HTML tags untouched in the output document:
|
||||
|
||||
```md
|
||||
some markdown **here**
|
||||
<div>this is *not* **parsed**</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<p>some markdown <strong>here</strong></p>
|
||||
<div>this is *not* **parsed**</div>
|
||||
```
|
||||
|
||||
However, the content of `<code>` and `<pre><code>` tags is always escaped.
|
||||
|
||||
```md
|
||||
some markdown **here** with <code>foo & bar <baz></baz></code>
|
||||
```
|
||||
|
||||
```html
|
||||
<p>some markdown <strong>here</strong> with <code>foo & bar <baz></baz></code></p>
|
||||
```
|
||||
|
||||
If you want to enable markdown parsing inside a specific HTML tag, you can use the html attribute **`markdown`**, **`markdown="1"`**, or **`data-markdown="1"`**.
|
||||
|
||||
```md
|
||||
some markdown **here**
|
||||
<div markdown="1">this is *not* **parsed**</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<p>some markdown <strong>here</strong></p>
|
||||
<div markdown="1"><p>this is <em>not</em> <strong>parsed</strong></p></div>
|
||||
```
|
||||
|
||||
## Escape entities
|
||||
|
||||
### Escape markdown entities
|
||||
|
||||
Showdown allows you to use backslash (`\`) to escape characters that have special meaning in markdown's syntax and generate literal characters instead. For example, if you want to surround a word with literal underscores (instead of an HTML `<em>` tag), you can use backslashes before the underscores, like this:
|
||||
|
||||
```md
|
||||
\_literal underscores\_
|
||||
```
|
||||
|
||||
Showdown provides backslash escapes for the following characters:
|
||||
|
||||
```
|
||||
\ backslash
|
||||
` backtick
|
||||
* asterisk
|
||||
_ underscore
|
||||
{} curly braces
|
||||
[] square brackets
|
||||
() parentheses
|
||||
# hash mark
|
||||
+ plus sign
|
||||
- minus sign (hyphen)
|
||||
. dot
|
||||
! exclamation mark
|
||||
```
|
||||
|
||||
### Escape HTML tags
|
||||
|
||||
Since [version 1.7.2](https://github.com/showdownjs/showdown/tree/1.7.2), backslash escaping of HTML tags is supported when [**`backslashEscapesHTMLTags`**][backslashEscapesHTMLTags] option is enabled.
|
||||
|
||||
```md
|
||||
\<div>a literal div\</div>
|
||||
```
|
||||
|
||||
[backslashEscapesHTMLTags]: available-options.md#backslashescapeshtmltags
|
||||
|
||||
## Known differences and gotchas
|
||||
|
||||
In most cases, Showdown's output is identical to that of Perl Markdown v1.0.2b7. What follows is a list of all known deviations. Please file an issue if you find more.
|
||||
|
||||
* **Since version 1.4.0, Showdown supports the markdown="1" attribute**, but for older versions, this attribute is ignored. This means:
|
||||
|
||||
```md
|
||||
<div markdown="1">
|
||||
Markdown does *not* work in here.
|
||||
</div>
|
||||
```
|
||||
|
||||
* You can only nest square brackets in link titles to a depth of two levels:
|
||||
|
||||
[[fine]](http://www.github.com/)
|
||||
[[[broken]]](http://www.github.com/)
|
||||
|
||||
If you need more, you can escape them with backslashes.
|
||||
|
||||
* A list is **single paragraph** if it has only **1 line break separating items** and it becomes **multi-paragraph if ANY of its items is separated by 2 line breaks**:
|
||||
|
||||
```md
|
||||
- foo
|
||||
|
||||
- bar
|
||||
- baz
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li><p>foo</p></li>
|
||||
<li><p>bar</p></li>
|
||||
<li><p>baz</p></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
This new ruleset is based on the comments of Markdown's author John Gruber in the [Markdown discussion list][md-newsletter].
|
||||
|
||||
[md-spec]: http://daringfireball.net/projects/markdown/
|
||||
[md-newsletter]: https://pairlist6.pair.net/mailman/listinfo/markdown-discuss
|
||||
[atx]: http://www.aaronsw.com/2002/atx/intro
|
||||
[setext]: https://en.wikipedia.org/wiki/Setext
|
||||
[readme]: https://github.com/showdownjs/showdown/blob/master/README.md
|
||||
[awkward effect]: http://i.imgur.com/YQ9iHTL.gif
|
||||
[emoji list]: https://github.com/showdownjs/showdown/wiki/emojis
|
118
docs/quickstart.md
Normal file
118
docs/quickstart.md
Normal file
|
@ -0,0 +1,118 @@
|
|||
To quickstart with Showdown, install it as a package (for server-side) or include it to your browser (client-side) via CDN:
|
||||
|
||||
## Installation
|
||||
|
||||
### Server-side
|
||||
|
||||
=== "npm"
|
||||
|
||||
```
|
||||
npm install showdown
|
||||
```
|
||||
|
||||
=== "bower"
|
||||
|
||||
```
|
||||
bower install showdown
|
||||
```
|
||||
|
||||
=== "NuGet"
|
||||
|
||||
```
|
||||
PM> Install-Package showdownjs
|
||||
```
|
||||
|
||||
More information about the package you can find on the [NuGet website](https://www.nuget.org/packages/showdownjs/).
|
||||
|
||||
### Client-side
|
||||
|
||||
=== "jsDelivr"
|
||||
|
||||
```
|
||||
https://cdn.jsdelivr.net/npm/showdown@<version>/dist/showdown.min.js
|
||||
```
|
||||
|
||||
[Showndown page on jsDelivr](https://www.jsdelivr.com/package/npm/showdown)
|
||||
|
||||
=== "cdnjs"
|
||||
|
||||
```
|
||||
https://cdnjs.cloudflare.com/ajax/libs/showdown/<version>/showdown.min.js
|
||||
```
|
||||
|
||||
[Showndown page on cdnjs](https://cdnjs.com/libraries/showdown)
|
||||
|
||||
=== "unpkg"
|
||||
|
||||
```
|
||||
https://unpkg.com/showdown/dist/showdown.min.js
|
||||
```
|
||||
|
||||
[Showndown page on unpkg](https://unpkg.com/browse/showdown@latest/)
|
||||
|
||||
!!! note ""
|
||||
Replace `<version>` with an actual full length version you're interested in. For example, `2.0.3`.
|
||||
|
||||
## Usage
|
||||
|
||||
Once installed, you can use Showndown according to the chosen method:
|
||||
|
||||
### Server-side
|
||||
|
||||
!!! example "Node.js"
|
||||
|
||||
=== "code"
|
||||
|
||||
```js
|
||||
var showdown = require('showdown'),
|
||||
converter = new showdown.Converter(),
|
||||
text = '# hello, markdown!',
|
||||
html = converter.makeHtml(text);
|
||||
```
|
||||
|
||||
=== "output"
|
||||
|
||||
```html
|
||||
<h1 id="hellomarkdown">hello, markdown!</h1>
|
||||
```
|
||||
|
||||
### Client-side
|
||||
|
||||
!!! example "Browser"
|
||||
|
||||
=== "code"
|
||||
|
||||
```js
|
||||
var converter = new showdown.Converter(),
|
||||
text = '# hello, markdown!',
|
||||
html = converter.makeHtml(text);
|
||||
```
|
||||
|
||||
=== "output"
|
||||
|
||||
```html
|
||||
<h1 id="hellomarkdown">hello, markdown!</h1>
|
||||
```
|
||||
|
||||
!!! warning "Potential XSS vulnerabilities"
|
||||
Showdown doesn't sanitize the input since Markdown relies on it to parse certain features correctly into HTML. As a result, this may lead to potential XSS injection vulnerabilities.
|
||||
|
||||
Please refer to the [Markdown's XSS vulnerability](xss.md) page for more information.
|
||||
|
||||
## Other installation methods
|
||||
|
||||
### Tarball
|
||||
|
||||
You can download the latest tarball directly from [releases][releases].
|
||||
|
||||
## Previous versions
|
||||
|
||||
If you're looking for Showdown prior to version 1.0.0, you can find them in the [legacy branch][legacy-branch].
|
||||
|
||||
## Changelog
|
||||
|
||||
The full changelog is available [here][changelog].
|
||||
|
||||
[legacy-branch]: https://github.com/showdownjs/showdown/tree/legacy
|
||||
[releases]: https://github.com/showdownjs/showdown/releases
|
||||
[changelog]: https://github.com/showdownjs/showdown/blob/master/CHANGELOG.md
|
60
docs/tutorials/add-default-class-to-html.md
Normal file
60
docs/tutorials/add-default-class-to-html.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Add default class for each HTML element
|
||||
|
||||
Many people use CSS kits like Bootstrap, Semantic UI, or others that require default name classes for HTML elements:
|
||||
|
||||
```html
|
||||
<h1 class="ui large header">1st Heading</h1>
|
||||
<h2 class="ui medium header">2nd Heading</h2>
|
||||
<ul class="ui list">
|
||||
<li class="ui item">first item</li>
|
||||
<li class="ui item">second item</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Showdown does not support this out-of-the-box. But you can create an extension for this:
|
||||
|
||||
```js
|
||||
const showdown = require('showdown');
|
||||
|
||||
const classMap = {
|
||||
h1: 'ui large header',
|
||||
h2: 'ui medium header',
|
||||
ul: 'ui list',
|
||||
li: 'ui item'
|
||||
}
|
||||
|
||||
const bindings = Object.keys(classMap)
|
||||
.map(key => ({
|
||||
type: 'output',
|
||||
regex: new RegExp(`<${key}(.*)>`, 'g'),
|
||||
replace: `<${key} class="${classMap[key]}" $1>`
|
||||
}));
|
||||
|
||||
const conv = new showdown.Converter({
|
||||
extensions: [...bindings]
|
||||
});
|
||||
|
||||
const text = `
|
||||
# 1st Heading
|
||||
## 2nd Heading
|
||||
|
||||
- first item
|
||||
- second item
|
||||
`;
|
||||
```
|
||||
|
||||
With this extension, the output will be as follows:
|
||||
|
||||
```html
|
||||
<h1 class="ui large header">1st Heading</h1>
|
||||
<h2 class="ui medium header">2nd Heading</h2>
|
||||
<ul class="ui list">
|
||||
<li class="ui item">first item</li>
|
||||
<li class="ui item">second item</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* Initial creator: [@zusamann](https://github.com/zusamann), [(original issue)](https://github.com/showdownjs/showdown/issues/376).
|
||||
* Updated by [@Kameelridder](https://github.com/Kameelridder), [(original issue)](https://github.com/showdownjs/showdown/issues/509).
|
5
docs/tutorials/index.md
Normal file
5
docs/tutorials/index.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Tutorials
|
||||
|
||||
* [Add default class for each HTML element](add-default-class-to-html.md)
|
||||
* [Markdown editor with Showdown](markdown-editor-with-showdown.md)
|
||||
* [Use language and output extensions on the same block](use-both-extension-types-together.md)
|
143
docs/tutorials/markdown-editor-with-showdown.md
Normal file
143
docs/tutorials/markdown-editor-with-showdown.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
# Markdown editor with Showdown
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, you will create a simple in-browser Markdown editor using Showdown and some of its extensions. The purpose is to show how easy it is to include and configure Showdown in your project.
|
||||
|
||||
The fully working example you can see in [Fiddle][1].
|
||||
|
||||
## Step 1: Prepare project
|
||||
|
||||
1. Install [node.js](https://nodejs.org/en/).
|
||||
1. Install project package management tool
|
||||
|
||||
!!! info ""
|
||||
Showdown core library doesn't have any dependencies so the setup is pretty straightforward. However, you are strongly encouraged to use a package manager such as [**npm**](http://npmjs.com) or [**yarn**](https://yarnpkg.com) to manage project dependencies.
|
||||
|
||||
To install package management tool:
|
||||
|
||||
1. Create a directory called `showdown-editor` and recreate the following structure:
|
||||
|
||||
```
|
||||
showdown-editor
|
||||
├── css
|
||||
│ └── style.css
|
||||
├── js
|
||||
│ └── script.js
|
||||
└── index.html
|
||||
```
|
||||
|
||||
1. Initialize `package.json` file by running the following interactive console command:
|
||||
|
||||
```
|
||||
npm init -y
|
||||
```
|
||||
|
||||
This command creates `package.json` file in the root of the project folder, and populates the default content that you can change later if you wish.
|
||||
|
||||
## Step 2: Install Showdown
|
||||
|
||||
Inside the `showdown-editor` directory, run the following command:
|
||||
|
||||
```
|
||||
npm install showdown --save
|
||||
```
|
||||
|
||||
This command will install `showdown` inside the `node_modules` directory and save `showdown` as a dependency in the `package.json` file.
|
||||
|
||||
## Step 3: Update project files
|
||||
|
||||
Add the following content to the corresponding project files:
|
||||
|
||||
=== "index.html"
|
||||
|
||||
```html
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<textarea id="sourceTA" rows="10" cols="82">
|
||||
Showdown Tutorial
|
||||
=================
|
||||
|
||||
This is a showdown tutorial.
|
||||
|
||||
Showdown supports a number of cool features, namely:
|
||||
|
||||
- headers
|
||||
- lists
|
||||
- and other stuff too
|
||||
|
||||
It is also possible to include code:
|
||||
|
||||
var foo = 'bar';
|
||||
|
||||
var baz = {
|
||||
markdown: 'is great',
|
||||
showdown: 'is awesome'
|
||||
}
|
||||
|
||||
Don't forget to check the [extensions wiki][1].
|
||||
|
||||
[1]: https://github.com/showdownjs/showdown/wiki/extensions
|
||||
</textarea>
|
||||
<hr/>
|
||||
<button id="runBtn" onClick="run()">Convert</button>
|
||||
<hr/>
|
||||
<div id="targetDiv"></div>
|
||||
|
||||
<script src="node_modules/showdown/dist/showdown.min.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
Please note how Showdown and the script file are included to the `index.html` via the `script` tag at the bottom of the file.
|
||||
|
||||
=== "style.css"
|
||||
|
||||
```css
|
||||
#sourceTA {
|
||||
display: block;
|
||||
}
|
||||
#targetDiv {
|
||||
border: 1px dashed #333333;
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
}
|
||||
```
|
||||
|
||||
=== "script.js"
|
||||
|
||||
```js
|
||||
function run() {
|
||||
var text = document.getElementById('sourceTA').value,
|
||||
target = document.getElementById('targetDiv'),
|
||||
converter = new showdown.Converter(),
|
||||
html = converter.makeHtml(text);
|
||||
|
||||
target.innerHTML = html;
|
||||
}
|
||||
```
|
||||
|
||||
The `script.js` file is simple: when the `runBtn` button is clicked, the script gets the text of the textarea, passes it through Showdown to convert the markdown text into HTML. The resulting HTML is then put inside the `targetDiv`, replacing the previous content.
|
||||
|
||||
## Step 4: Check the result
|
||||
|
||||
1. Open your `index.html` file. You should see your editor with prefilled markdown text in the text area.
|
||||
1. Click `Convert` button. You show see the text to be converted to HTML:
|
||||
|
||||
![](../assets/markdown-editor.png)
|
||||
|
||||
The fully working example you can see in [Fiddle][1].
|
||||
|
||||
## Conclusion
|
||||
|
||||
Congratulations! :tada: You have successfully created a simple Markdown editor!
|
||||
|
||||
[1]: http://jsfiddle.net/tivie/6bnpptkb/
|
62
docs/tutorials/use-both-extension-types-together.md
Normal file
62
docs/tutorials/use-both-extension-types-together.md
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Use language and output extensions on the same block
|
||||
|
||||
## Overview
|
||||
|
||||
Showdown allows you to define and use any number of extensions that act on the same block. These extensions can be executed sequentially or at different moments.
|
||||
|
||||
This enables you to pre-parse/mark a block of text but defer any modifications for the last by using a combination of language and output extensions.
|
||||
|
||||
This is useful if you, for example, don't want Showdown to parse the contents of your new language construct.
|
||||
|
||||
## Example
|
||||
|
||||
Let's say you create an extension that captures everything between `%start%` and `%end%`. However, that content should not be modified by Showdown. Obviously, you can use `<pre>` tags but that is beside the point.
|
||||
|
||||
Although Showdown doesn't have any flag to prevent parsing the content of an extension, the same effect can be easily achieved by using lang and output extensions together.
|
||||
|
||||
!!! example ""
|
||||
The fully working example you can see in [Fiddle][1].
|
||||
|
||||
### Code
|
||||
|
||||
[Create your extensions](../create-extension.md) with the following content:
|
||||
|
||||
```js
|
||||
showdown.extension('myExt', function() {
|
||||
var matches = [];
|
||||
return [
|
||||
{
|
||||
type: 'lang',
|
||||
regex: /%start%([^]+?)%end%/gi,
|
||||
replace: function(s, match) {
|
||||
matches.push(match);
|
||||
var n = matches.length - 1;
|
||||
return '%PLACEHOLDER' + n + '%';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'output',
|
||||
filter: function (text) {
|
||||
for (var i=0; i< matches.length; ++i) {
|
||||
var pat = '<p>%PLACEHOLDER' + i + '% *<\/p>';
|
||||
text = text.replace(new RegExp(pat, 'gi'), matches[i]);
|
||||
}
|
||||
//reset array
|
||||
matches = [];
|
||||
return text;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
In this example, you created a [`lang` extension](../create-extension.md#type) that:
|
||||
|
||||
1. Checks for the pseudo tags `%start%` and `%end%`.
|
||||
1. Extracts everything in between the tags.
|
||||
1. Saves the content between the tags in a variable.
|
||||
1. Replaces the saved content with a placeholder to identify the exact position of the extracted text.
|
||||
|
||||
and an [`output` extension](../create-extension.md#type) that replaces the placeholder with the saved content, once Showdown is finished parsing.
|
||||
|
||||
[1]: http://jsfiddle.net/tivie/1rqr7xy8/
|
110
docs/xss.md
Normal file
110
docs/xss.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
# Markdown's XSS vulnerability
|
||||
|
||||
## Introduction
|
||||
|
||||
Cross-Site Scripting (XSS) is a well-known technique to gain access to the private information of users on a website. The attacker injects spurious HTML content (a script) on the web page. This script can read the user’s cookies and do other malicious actions (like steal credentials). As a countermeasure, you should always filter user input for suspicious content. Showdown doesn’t include an XSS filter, so you must provide your own. But be careful in how you do it.
|
||||
|
||||
## Markdown is inherently unsafe
|
||||
|
||||
Markdown syntax allows the inclusion of arbitrary HTML. For example, below is a perfectly valid Markdown:
|
||||
|
||||
```md
|
||||
This is a regular paragraph.
|
||||
|
||||
<table>
|
||||
<tr><td>Foo</td></tr>
|
||||
</table>
|
||||
|
||||
This is another regular paragraph.
|
||||
```
|
||||
|
||||
This means that an attacker could do something like this:
|
||||
|
||||
```md
|
||||
This is a regular paragraph.
|
||||
|
||||
<script>alert('xss');</script>
|
||||
|
||||
This is another regular paragraph.
|
||||
```
|
||||
|
||||
While `alert('xss');` is hardly problematic (maybe just annoying) a real-world scenario might be a lot worse. Obviously, you can easily prevent this kind of this straightforward attack. For example, you can define a whitelist for Showdown that will contain a limited set of allowed HTML tags. However, an attacker can easily circumvent this "defense".
|
||||
|
||||
## Whitelist / blacklist can't prevent XSS
|
||||
|
||||
Consider the following Markdown content:
|
||||
|
||||
```md
|
||||
hello <a href="www.google.com">*you*</a>
|
||||
```
|
||||
|
||||
As you can see, it's a link, nothing malicious about this. And `<a>` tags are pretty innocuous, right? Showdown should definitely allow them. But what if the content is slightly altered, like this:
|
||||
|
||||
```md
|
||||
hello <a name="n" href="javascript:alert('xss')">*you*</a>
|
||||
```
|
||||
|
||||
Now this is a lot more problematic. Once again, it's not that hard to filter Showdown's input to expunge problematic attributes (such as `href` in `<a>` tags) of scripting attacks. In fact, a regular HTML XSS prevention library should catch this kind of straightforward attack.
|
||||
|
||||
At this point you're probably thinking that the best way is to follow Stackoverflow's cue and disallow embedded HTML in Markdown. Unfortunately it's still not enough.
|
||||
|
||||
## Strip HTML tags is not enough
|
||||
|
||||
Consider the following Markdown input:
|
||||
|
||||
```md
|
||||
[some text](javascript:alert('xss'))
|
||||
```
|
||||
|
||||
Showdown will correctly parse this piece of Markdown input as:
|
||||
|
||||
```html
|
||||
<a href="javascript:alert('xss')">some text</a>
|
||||
```
|
||||
|
||||
In this case, it was Markdown's syntax itself to create the dangerous link. HTML XSS filter cannot catch this. And unless you start striping dangerous words like *javascript* (which would make this article extremely hard to write), there's nothing you can really do to filter XSS attacks from your input. Things get even harder when you tightly mix HTML with Markdown.
|
||||
|
||||
## Mixed HTML/Markdown XSS attack
|
||||
|
||||
Consider the following piece of Markdown:
|
||||
|
||||
```md
|
||||
> hello <a name="n"
|
||||
> href="javascript:alert('xss')">*you*</a>
|
||||
```
|
||||
|
||||
If you apply an XSS filter to filter bad HTML in this Markdown input, the XSS filter, expecting HTML, will likely think the `<a>` tag ends with the first character on the second line and will leave the text snippet untouched. It will probably fail to see that the `href="javascript:…"` is part of the `<a>` element and leave it alone. But when Markdown converts this to HTML, you get this:
|
||||
|
||||
```html
|
||||
<blockquote>
|
||||
<p>hello <a name="n"
|
||||
href="javascript:alert('xss')"><em>you</em></a></p>
|
||||
</blockquote>
|
||||
```
|
||||
|
||||
After parsing with Markdown, the first `>` on the second line disappears because it was the blockquote marker in the Markdown blockquote syntax. As a result, you’ve got a link containing an XSS attack!
|
||||
|
||||
Did Markdown generate the HTML? No, the HTML was already in plain sight in the input. The XSS filter couldn’t catch it because the input doesn’t follow HTML rules: it’s a mix of Markdown and HTML, and the filter doesn’t know a dime about Markdown.
|
||||
|
||||
## Mitigate XSS
|
||||
|
||||
So, is it all lost? Not really. The answer is not to filter the *input* but rather the *output*. After the *input* text is converted into full-fledged HTML, you can reliably apply the correct XSS filters to remove any dangerous or malicious content.
|
||||
|
||||
Also, client-side validations are not reliable. It should be a given, but in case you're wondering, you should (almost) never trust data sent by the client. If there's some critical operation you must perform on the data (such as XSS filtering), you should do it *SERVER-SIDE* not client-side.
|
||||
|
||||
HTML XSS filtering libraries are useful here since they prevent most of the attacks. However, you should not use them blindly: a library can't predict all the contexts and situations your application may face.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Showdown tries to convert the input text as closely as possible, without any concerns for XSS attacks or malicious intent. So, the basic rules are:
|
||||
|
||||
* **removing HTML entities from Markdown does not prevent XSS**. Markdown syntax can generate XSS attacks.
|
||||
* **XSS filtering should be done after Showdown has processed input, not before or during**. If you filter before, it will break some of Markdown’s features and will leave security holes.
|
||||
* **perform the necessary filtering server-side, not client-side**. XSS filtering libraries are useful but should not be used blindly.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This page is based on the excellent article: ["Markdown and XSS"][1] by [Michel Fortin][2]
|
||||
|
||||
[1]: https://michelf.ca/blog/2010/markdown-and-xss/
|
||||
[2]: https://github.com/michelf
|
80
karma.browserstack.js
Normal file
80
karma.browserstack.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
// global config of your BrowserStack account
|
||||
browserStack: {
|
||||
username: process.env.BROWSERSTACK_USERNAME,
|
||||
accessKey: process.env.BROWSERSTACK_ACCESSKEY,
|
||||
project: process.env.BROWSERSTACK_PROJECT_NAME || 'showdown',
|
||||
build: process.env.BROWSERSTACK_BUILD_NAME || require('./package.json').version,
|
||||
name: process.env.COMMIT_MSG || 'Unit Testing'
|
||||
},
|
||||
|
||||
// define browsers
|
||||
customLaunchers: {
|
||||
bstack_chrome_windows: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
browser_version: '49',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
bstack_firefox_windows: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
browser_version: '44',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
bstack_edge_windows: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'edge',
|
||||
browser_version: '15',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
bstack_ie11_windows: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '11',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
bstack_macos_safari: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
browser_version: '10.1',
|
||||
os: 'OS X',
|
||||
os_version: 'Sierra'
|
||||
},
|
||||
bstack_iphoneX: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'ios',
|
||||
os_version: '11.0',
|
||||
device: 'iPhone X',
|
||||
real_mobile: true
|
||||
},
|
||||
bstack_android: {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'android',
|
||||
os_version:'4.4',
|
||||
device: 'Samsung Galaxy Tab 4',
|
||||
realMobile: true
|
||||
}
|
||||
},
|
||||
|
||||
browsers: ['bstack_chrome_windows', 'bstack_firefox_windows', 'bstack_ie11_windows', 'bstack_edge_windows', 'bstack_iphoneX', 'bstack_macos_safari', 'bstack_android'],
|
||||
frameworks: ['mocha', 'chai'],
|
||||
reporters: ['dots', 'BrowserStack'],
|
||||
files: [
|
||||
{ pattern: '.build/showdown.js'},
|
||||
{ pattern: 'src/options.js'},
|
||||
// tests
|
||||
{ pattern: 'test/unit/showdown*.js' }
|
||||
//{ pattern: 'test/functional/showdown*.js' },
|
||||
],
|
||||
singleRun: true,
|
||||
concurrency: Infinity
|
||||
});
|
||||
};
|
36
karma.conf.js
Normal file
36
karma.conf.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
client: {
|
||||
captureConsole: true
|
||||
},
|
||||
browserConsoleLogOptions: {
|
||||
level: 'log',
|
||||
format: '%b %T: %m',
|
||||
terminal: true
|
||||
},
|
||||
logLevel: config.LOG_LOG,
|
||||
frameworks: ['mocha', 'chai'],
|
||||
files: [
|
||||
{ pattern: '.build/showdown.js'},
|
||||
{ pattern: 'src/options.js'},
|
||||
// tests
|
||||
{ pattern: 'test/unit/showdown*.js' },
|
||||
{ pattern: 'test/functional/showdown*.js' },
|
||||
],
|
||||
reporters: ['progress'],
|
||||
port: 9876, // karma web server port
|
||||
colors: true,
|
||||
browsers: ['ChromeHeadless', 'FirefoxHeadless', 'jsdom'],
|
||||
autoWatch: false,
|
||||
singleRun: true, // Karma captures browsers, runs the tests and exits
|
||||
//concurrency: Infinity,
|
||||
customLaunchers: {
|
||||
'FirefoxHeadless': {
|
||||
base: 'Firefox',
|
||||
flags: [
|
||||
'-headless',
|
||||
]
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
49
mkdocs.yml
Normal file
49
mkdocs.yml
Normal file
|
@ -0,0 +1,49 @@
|
|||
site_name: Showdown documentation
|
||||
site_description: Showdown is a JavaScript Markdown to HTML converter
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: http://showdownjs.com/apple-touch-icon.png
|
||||
favicon: http://showdownjs.com/apple-touch-icon.png
|
||||
icon:
|
||||
repo: fontawesome/brands/github
|
||||
features:
|
||||
- navigation.tabs
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.superfences
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.tasklist
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
|
||||
extra_css:
|
||||
- assets/extra.css
|
||||
|
||||
repo_url: https://github.com/showdownjs/showdown
|
||||
repo_name: showdownjs/showdown
|
||||
site_dir: public
|
||||
|
||||
nav:
|
||||
- Home:
|
||||
- Introduction: index.md
|
||||
- Donations: donations.md
|
||||
- Credits: credits.md
|
||||
- Quickstart:
|
||||
- Quickstart: quickstart.md
|
||||
- Showdown's Markdown syntax: markdown-syntax.md
|
||||
- Compatibility: compatibility.md
|
||||
- Configuration:
|
||||
- Showdown options: configuration.md
|
||||
- Available options: available-options.md
|
||||
- Flavors: flavors.md
|
||||
- CLI: cli.md
|
||||
- Integrations: integrations.md
|
||||
- Extensions:
|
||||
- Overview: extensions.md
|
||||
- Create an extension: create-extension.md
|
||||
- List of known extensions: extensions-list.md
|
||||
- Tutorials: tutorials/index.md
|
2872
package-lock.json
generated
2872
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "showdown",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.0-alpha",
|
||||
"description": "A Markdown to HTML converter written in Javascript",
|
||||
"author": "Estevão Santos",
|
||||
"homepage": "http://showdownjs.com/",
|
||||
|
@ -25,6 +25,10 @@
|
|||
"Pascal Deschênes",
|
||||
"Estevão Santos"
|
||||
],
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://www.paypal.me/tiviesantos"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/showdownjs/showdown.git",
|
||||
|
@ -43,7 +47,8 @@
|
|||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.4",
|
||||
"chai": "*",
|
||||
"chai-match": "*",
|
||||
"grunt": "^1.4.1",
|
||||
"grunt-contrib-clean": "^2.0.0",
|
||||
"grunt-contrib-concat": "^2.0.0",
|
||||
|
@ -53,17 +58,27 @@
|
|||
"grunt-conventional-github-releaser": "^1.0.0",
|
||||
"grunt-endline": "^0.7.0",
|
||||
"grunt-eslint": "^24.0.0",
|
||||
"grunt-mocha-test": "^0.13.3",
|
||||
"grunt-simple-mocha": "^0.4.0",
|
||||
"jsdom": "^19.0.0",
|
||||
"karma": "^6.3.17",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^3.1.1",
|
||||
"karma-firefox-launcher": "^2.1.2",
|
||||
"karma-jsdom-launcher": "^12.0.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"load-grunt-tasks": "^5.1.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"quiet-grunt": "^0.2.0",
|
||||
"semver": "^7.3.0",
|
||||
"semver-sort": "^0.0.4",
|
||||
"sinon": "^12.0.1",
|
||||
"source-map-support": "^0.5.20"
|
||||
"semver-sort": "^1.0.0",
|
||||
"sinon": "*",
|
||||
"source-map-support": "^0.5.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"yargs": "^17.2.1"
|
||||
"commander": "^9.0.0",
|
||||
"jsdom": "^19.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,785 +1,43 @@
|
|||
# Performance Tests for showdown
|
||||
|
||||
|
||||
## [version 1.9.0](https://github.com/showdownjs/showdown/tree/1.9.0)
|
||||
## [version 2.0.0](https://github.com/showdownjs/showdown/tree/2.0.0)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.394|9.154|0.104|
|
||||
|performance.testfile.md|49.286|177.704|26.155|
|
||||
|Simple "Hello World"|0.581|12.279|0.151|
|
||||
|performance.testfile.md|35.491|79.405|29.046|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|6.101|16.106|2.376|
|
||||
|anchors|0.767|3.507|0.323|
|
||||
|autoLinks|0.244|0.548|0.124|
|
||||
|blockQuotes|2.397|4.000|2.013|
|
||||
|codeBlocks|0.226|0.343|0.208|
|
||||
|codeSpans|0.316|1.136|0.258|
|
||||
|detab|0.095|0.184|0.085|
|
||||
|encodeAmpsAndAngles|0.104|0.153|0.096|
|
||||
|encodeBackslashEscapes|0.062|0.137|0.056|
|
||||
|encodeCode|0.558|1.469|0.485|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.243|0.714|0.192|
|
||||
|githubCodeBlocks|0.213|0.407|0.186|
|
||||
|hashBlock|0.046|0.147|0.036|
|
||||
|hashElement|0.003|0.050|0.000|
|
||||
|hashHTMLSpans|4.914|7.364|4.474|
|
||||
|hashPreCodeTags|0.134|0.234|0.110|
|
||||
|headers|1.515|3.866|1.153|
|
||||
|horizontalRule|0.216|0.293|0.194|
|
||||
|images|0.144|0.286|0.124|
|
||||
|italicsAndBold|0.234|0.656|0.190|
|
||||
|lists|4.483|7.664|2.482|
|
||||
|outdent|0.286|0.538|0.179|
|
||||
|paragraphs|10.257|18.656|5.229|
|
||||
|spanGamut|10.288|31.124|6.102|
|
||||
|strikethrough|0.007|0.106|0.001|
|
||||
|stripLinkDefinitions|0.438|0.678|0.392|
|
||||
|tables|0.007|0.096|0.001|
|
||||
|unescapeSpecialChars|0.041|0.086|0.008|
|
||||
|
||||
|
||||
## [version 1.8.7](https://github.com/showdownjs/showdown/tree/1.8.7)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.339|9.454|0.104|
|
||||
|performance.testfile.md|31.606|62.066|24.851|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|3.987|7.594|2.211|
|
||||
|anchors|0.763|7.966|0.290|
|
||||
|autoLinks|0.094|0.193|0.071|
|
||||
|blockQuotes|2.922|9.315|2.021|
|
||||
|codeBlocks|0.239|0.346|0.205|
|
||||
|codeSpans|0.290|0.378|0.243|
|
||||
|detab|0.094|0.161|0.084|
|
||||
|encodeAmpsAndAngles|0.262|1.468|0.093|
|
||||
|encodeBackslashEscapes|0.092|0.177|0.054|
|
||||
|encodeCode|0.535|1.179|0.457|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.190|0.252|0.175|
|
||||
|githubCodeBlocks|0.220|0.446|0.184|
|
||||
|hashBlock|0.041|0.094|0.036|
|
||||
|hashElement|0.002|0.025|0.000|
|
||||
|hashHTMLSpans|4.397|5.805|4.071|
|
||||
|hashPreCodeTags|0.119|0.221|0.108|
|
||||
|headers|1.327|3.385|1.085|
|
||||
|horizontalRule|0.212|0.270|0.198|
|
||||
|images|0.228|1.336|0.123|
|
||||
|italicsAndBold|0.211|0.363|0.190|
|
||||
|lists|2.677|4.028|2.235|
|
||||
|outdent|0.148|0.218|0.135|
|
||||
|paragraphs|5.846|7.679|4.920|
|
||||
|spanGamut|4.082|5.226|3.633|
|
||||
|strikethrough|0.005|0.079|0.000|
|
||||
|stripLinkDefinitions|0.327|1.681|0.221|
|
||||
|tables|0.003|0.043|0.000|
|
||||
|unescapeSpecialChars|0.010|0.042|0.007|
|
||||
|
||||
|
||||
## [version 1.8.6](https://github.com/showdownjs/showdown/tree/1.8.6)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.454|9.635|0.087|
|
||||
|performance.testfile.md|31.987|60.669|27.816|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|5.333|15.245|2.412|
|
||||
|anchors|0.399|0.670|0.291|
|
||||
|autoLinks|0.118|0.291|0.072|
|
||||
|blockQuotes|2.897|6.028|1.997|
|
||||
|codeBlocks|0.305|1.120|0.189|
|
||||
|codeSpans|0.294|0.626|0.235|
|
||||
|detab|0.129|0.765|0.087|
|
||||
|encodeAmpsAndAngles|0.110|0.166|0.094|
|
||||
|encodeBackslashEscapes|0.099|0.349|0.068|
|
||||
|encodeCode|0.948|1.386|0.842|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.214|0.473|0.162|
|
||||
|githubCodeBlocks|0.161|0.252|0.148|
|
||||
|hashBlock|0.042|0.070|0.037|
|
||||
|hashElement|0.002|0.023|0.000|
|
||||
|hashHTMLSpans|4.292|5.134|3.921|
|
||||
|hashPreCodeTags|0.131|0.361|0.110|
|
||||
|headers|1.550|3.810|1.149|
|
||||
|horizontalRule|0.214|0.287|0.201|
|
||||
|images|0.176|0.432|0.132|
|
||||
|italicsAndBold|0.324|1.552|0.228|
|
||||
|lists|2.931|3.835|2.586|
|
||||
|outdent|0.154|0.272|0.137|
|
||||
|paragraphs|6.549|8.261|5.730|
|
||||
|spanGamut|4.223|5.585|3.756|
|
||||
|strikethrough|0.005|0.087|0.000|
|
||||
|stripLinkDefinitions|0.242|0.373|0.224|
|
||||
|tables|0.003|0.042|0.001|
|
||||
|unescapeSpecialChars|0.010|0.053|0.007|
|
||||
|
||||
|
||||
## [version 1.8.4](https://github.com/showdownjs/showdown/tree/1.8.4)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.736|11.076|0.117|
|
||||
|performance.testfile.md|32.918|62.427|27.941|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|5.260|17.333|2.340|
|
||||
|anchors|0.522|2.898|0.307|
|
||||
|autoLinks|0.124|0.300|0.071|
|
||||
|blockQuotes|2.244|3.333|2.015|
|
||||
|codeBlocks|0.244|0.817|0.190|
|
||||
|codeSpans|0.354|1.201|0.243|
|
||||
|detab|0.096|0.143|0.088|
|
||||
|encodeAmpsAndAngles|0.138|0.198|0.096|
|
||||
|encodeBackslashEscapes|0.093|0.184|0.071|
|
||||
|encodeCode|0.961|1.611|0.858|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.252|0.520|0.158|
|
||||
|githubCodeBlocks|0.262|0.390|0.161|
|
||||
|hashBlock|0.052|0.129|0.037|
|
||||
|hashElement|0.003|0.040|0.000|
|
||||
|hashHTMLSpans|4.240|4.673|4.044|
|
||||
|hashPreCodeTags|0.134|0.337|0.113|
|
||||
|headers|1.412|4.475|1.077|
|
||||
|horizontalRule|0.358|2.686|0.196|
|
||||
|images|0.184|0.480|0.130|
|
||||
|italicsAndBold|0.300|0.458|0.234|
|
||||
|lists|3.074|4.651|2.626|
|
||||
|outdent|0.204|0.931|0.137|
|
||||
|paragraphs|6.406|8.020|5.821|
|
||||
|spanGamut|4.136|6.038|3.840|
|
||||
|strikethrough|0.007|0.132|0.000|
|
||||
|stripLinkDefinitions|0.248|0.403|0.217|
|
||||
|tables|0.003|0.040|0.001|
|
||||
|unescapeSpecialChars|0.009|0.039|0.007|
|
||||
|
||||
|
||||
## [version 1.8.3](https://github.com/showdownjs/showdown/tree/1.8.3)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.927|32.654|0.147|
|
||||
|performance.testfile.md|32.485|62.282|28.403|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|5.454|18.356|2.385|
|
||||
|anchors|0.504|3.110|0.290|
|
||||
|autoLinks|0.114|0.284|0.069|
|
||||
|blockQuotes|2.269|3.374|1.997|
|
||||
|codeBlocks|0.250|0.840|0.192|
|
||||
|codeSpans|0.352|1.231|0.249|
|
||||
|detab|0.115|0.179|0.087|
|
||||
|encodeAmpsAndAngles|0.105|0.162|0.095|
|
||||
|encodeBackslashEscapes|0.108|0.235|0.075|
|
||||
|encodeCode|0.994|1.915|0.847|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.237|0.475|0.160|
|
||||
|githubCodeBlocks|0.202|0.771|0.151|
|
||||
|hashBlock|0.071|0.493|0.039|
|
||||
|hashElement|0.002|0.036|0.001|
|
||||
|hashHTMLSpans|4.162|4.708|3.959|
|
||||
|hashPreCodeTags|0.130|0.331|0.112|
|
||||
|headers|1.409|4.622|1.044|
|
||||
|horizontalRule|0.351|2.655|0.196|
|
||||
|images|0.199|0.545|0.131|
|
||||
|italicsAndBold|0.269|0.357|0.235|
|
||||
|lists|3.057|4.403|2.686|
|
||||
|outdent|0.153|0.307|0.136|
|
||||
|paragraphs|6.455|7.901|5.708|
|
||||
|spanGamut|4.256|5.542|3.930|
|
||||
|strikethrough|0.005|0.089|0.000|
|
||||
|stripLinkDefinitions|0.248|0.394|0.225|
|
||||
|tables|0.002|0.028|0.001|
|
||||
|unescapeSpecialChars|0.009|0.039|0.007|
|
||||
|
||||
|
||||
## [version 1.8.2](https://github.com/showdownjs/showdown/tree/1.8.2)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.361|8.977|0.104|
|
||||
|performance.testfile.md|33.109|56.478|29.179|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|5.488|20.714|2.321|
|
||||
|anchors|0.506|3.158|0.292|
|
||||
|autoLinks|0.141|0.365|0.072|
|
||||
|blockQuotes|2.300|3.642|2.046|
|
||||
|codeBlocks|0.243|0.877|0.189|
|
||||
|codeSpans|0.268|1.176|0.159|
|
||||
|detab|0.095|0.172|0.089|
|
||||
|encodeAmpsAndAngles|0.108|0.230|0.097|
|
||||
|encodeBackslashEscapes|0.078|0.119|0.074|
|
||||
|encodeCode|1.002|1.544|0.851|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.256|0.566|0.164|
|
||||
|githubCodeBlocks|0.253|0.999|0.152|
|
||||
|hashBlock|0.042|0.080|0.037|
|
||||
|hashElement|0.002|0.032|0.000|
|
||||
|hashHTMLSpans|4.444|5.282|3.987|
|
||||
|hashPreCodeTags|0.152|0.265|0.117|
|
||||
|headers|1.465|4.970|1.059|
|
||||
|horizontalRule|0.245|0.562|0.205|
|
||||
|images|0.312|2.615|0.131|
|
||||
|italicsAndBold|0.287|0.427|0.244|
|
||||
|lists|3.261|4.098|2.792|
|
||||
|outdent|0.179|0.377|0.141|
|
||||
|paragraphs|6.661|9.047|5.884|
|
||||
|spanGamut|4.561|6.173|4.009|
|
||||
|strikethrough|0.005|0.097|0.000|
|
||||
|stripLinkDefinitions|0.251|0.402|0.216|
|
||||
|tables|0.006|0.086|0.001|
|
||||
|unescapeSpecialChars|0.013|0.064|0.008|
|
||||
|
||||
|
||||
## [version 1.8.0](https://github.com/showdownjs/showdown/tree/1.8.0)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.357|9.000|0.091|
|
||||
|performance.testfile.md|31.434|57.439|26.735|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.177|7.661|2.346|
|
||||
|anchors|0.542|3.749|0.300|
|
||||
|autoLinks|0.087|0.183|0.069|
|
||||
|blockQuotes|2.049|3.552|1.815|
|
||||
|codeBlocks|0.264|1.163|0.185|
|
||||
|codeSpans|0.271|0.790|0.163|
|
||||
|detab|0.092|0.120|0.086|
|
||||
|encodeAmpsAndAngles|0.106|0.146|0.097|
|
||||
|encodeBackslashEscapes|0.091|0.152|0.077|
|
||||
|encodeCode|0.962|1.552|0.862|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.239|0.487|0.173|
|
||||
|githubCodeBlocks|0.222|0.914|0.140|
|
||||
|hashBlock|0.063|0.402|0.035|
|
||||
|hashElement|0.001|0.025|0.000|
|
||||
|hashHTMLSpans|4.303|4.889|4.021|
|
||||
|hashPreCodeTags|0.164|0.541|0.110|
|
||||
|headers|1.159|3.779|0.968|
|
||||
|horizontalRule|0.244|0.419|0.194|
|
||||
|images|0.324|3.058|0.133|
|
||||
|italicsAndBold|0.289|0.419|0.237|
|
||||
|lists|2.671|3.139|2.494|
|
||||
|outdent|0.159|0.253|0.139|
|
||||
|paragraphs|5.594|6.833|5.159|
|
||||
|spanGamut|5.069|9.600|4.128|
|
||||
|strikethrough|0.003|0.062|0.000|
|
||||
|stripLinkDefinitions|0.271|0.400|0.225|
|
||||
|tables|0.002|0.031|0.000|
|
||||
|unescapeSpecialChars|0.008|0.038|0.007|
|
||||
|
||||
|
||||
## [version 1.7.6](https://github.com/showdownjs/showdown/tree/1.7.6)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.313|6.267|0.092|
|
||||
|performance.testfile.md|30.962|54.583|26.381|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.099|7.072|2.360|
|
||||
|anchors|0.574|4.502|0.294|
|
||||
|autoLinks|0.087|0.210|0.066|
|
||||
|blockQuotes|2.176|4.602|1.823|
|
||||
|codeBlocks|0.282|0.885|0.193|
|
||||
|codeSpans|0.265|0.764|0.166|
|
||||
|detab|0.102|0.155|0.091|
|
||||
|encodeAmpsAndAngles|0.107|0.175|0.098|
|
||||
|encodeBackslashEscapes|0.120|0.872|0.073|
|
||||
|encodeCode|0.983|1.842|0.873|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.301|0.389|0.277|
|
||||
|githubCodeBlocks|0.204|0.889|0.146|
|
||||
|hashBlock|0.063|0.415|0.035|
|
||||
|hashElement|0.002|0.032|0.000|
|
||||
|hashHTMLSpans|4.131|4.411|3.988|
|
||||
|hashPreCodeTags|0.262|2.429|0.108|
|
||||
|headers|1.264|4.308|0.953|
|
||||
|horizontalRule|0.230|0.331|0.194|
|
||||
|images|0.184|0.564|0.134|
|
||||
|italicsAndBold|0.312|0.828|0.251|
|
||||
|lists|2.642|3.274|2.451|
|
||||
|outdent|0.159|0.240|0.144|
|
||||
|paragraphs|6.724|12.672|5.367|
|
||||
|spanGamut|4.991|9.206|4.173|
|
||||
|strikethrough|0.003|0.058|0.000|
|
||||
|stripLinkDefinitions|0.246|0.390|0.219|
|
||||
|tables|0.002|0.044|0.000|
|
||||
|unescapeSpecialChars|0.010|0.051|0.007|
|
||||
|
||||
|
||||
## [version 1.7.5](https://github.com/showdownjs/showdown/tree/1.7.5)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.562|14.434|0.118|
|
||||
|performance.testfile.md|30.396|57.886|26.628|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.280|8.392|2.357|
|
||||
|anchors|0.602|5.341|0.285|
|
||||
|autoLinks|0.092|0.193|0.065|
|
||||
|blockQuotes|2.068|4.430|1.736|
|
||||
|codeBlocks|0.279|0.937|0.181|
|
||||
|codeSpans|0.222|0.592|0.158|
|
||||
|detab|0.120|0.145|0.091|
|
||||
|encodeAmpsAndAngles|0.116|0.222|0.096|
|
||||
|encodeBackslashEscapes|0.140|0.914|0.071|
|
||||
|encodeCode|1.195|2.009|0.861|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.307|0.468|0.269|
|
||||
|githubCodeBlocks|0.197|0.837|0.144|
|
||||
|hashBlock|0.060|0.442|0.036|
|
||||
|hashElement|0.002|0.041|0.000|
|
||||
|hashHTMLSpans|4.289|4.712|4.002|
|
||||
|hashPreCodeTags|0.281|2.439|0.108|
|
||||
|headers|1.221|4.603|0.908|
|
||||
|horizontalRule|0.208|0.352|0.193|
|
||||
|images|0.182|0.634|0.128|
|
||||
|italicsAndBold|0.335|1.276|0.239|
|
||||
|lists|3.143|6.411|2.393|
|
||||
|outdent|0.398|0.585|0.159|
|
||||
|paragraphs|5.926|11.596|4.961|
|
||||
|spanGamut|4.443|6.012|4.024|
|
||||
|strikethrough|0.003|0.055|0.000|
|
||||
|stripLinkDefinitions|0.243|0.424|0.215|
|
||||
|tables|0.003|0.049|0.000|
|
||||
|unescapeSpecialChars|0.008|0.041|0.006|
|
||||
|
||||
|
||||
## [version 1.7.4](https://github.com/showdownjs/showdown/tree/1.7.4)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.972|25.186|0.160|
|
||||
|performance.testfile.md|30.397|61.913|26.550|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|3.999|6.603|2.314|
|
||||
|anchors|0.527|3.823|0.285|
|
||||
|autoLinks|0.090|0.188|0.063|
|
||||
|blockQuotes|2.057|4.122|1.780|
|
||||
|codeBlocks|0.247|1.085|0.186|
|
||||
|codeSpans|0.263|1.017|0.162|
|
||||
|detab|0.123|0.158|0.097|
|
||||
|encodeAmpsAndAngles|0.118|0.171|0.096|
|
||||
|encodeBackslashEscapes|0.079|0.146|0.071|
|
||||
|encodeCode|0.945|1.453|0.866|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.285|0.438|0.246|
|
||||
|githubCodeBlocks|0.225|0.969|0.142|
|
||||
|hashBlock|0.068|0.577|0.036|
|
||||
|hashElement|0.002|0.041|0.000|
|
||||
|hashHTMLSpans|4.126|4.528|3.950|
|
||||
|hashPreCodeTags|0.149|0.537|0.110|
|
||||
|headers|1.171|3.877|0.884|
|
||||
|horizontalRule|0.381|3.457|0.197|
|
||||
|images|0.195|0.618|0.133|
|
||||
|italicsAndBold|0.298|0.562|0.245|
|
||||
|lists|3.790|6.139|2.612|
|
||||
|outdent|0.167|0.276|0.139|
|
||||
|paragraphs|5.349|6.076|4.897|
|
||||
|spanGamut|4.370|6.111|3.946|
|
||||
|strikethrough|0.003|0.048|0.000|
|
||||
|stripLinkDefinitions|0.255|0.401|0.218|
|
||||
|tables|0.002|0.033|0.000|
|
||||
|unescapeSpecialChars|0.009|0.040|0.007|
|
||||
|
||||
|
||||
## [version 1.7.3](https://github.com/showdownjs/showdown/tree/1.7.3)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.277|5.743|0.088|
|
||||
|performance.testfile.md|30.733|54.768|26.972|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.316|8.271|2.339|
|
||||
|anchors|0.525|3.812|0.288|
|
||||
|autoLinks|0.085|0.220|0.063|
|
||||
|blockQuotes|2.033|3.622|1.745|
|
||||
|codeBlocks|0.251|1.060|0.178|
|
||||
|codeSpans|0.246|0.749|0.157|
|
||||
|detab|0.142|0.752|0.087|
|
||||
|encodeAmpsAndAngles|0.100|0.129|0.095|
|
||||
|encodeBackslashEscapes|0.079|0.125|0.070|
|
||||
|encodeCode|0.977|1.774|0.852|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.271|0.441|0.244|
|
||||
|githubCodeBlocks|0.235|0.985|0.139|
|
||||
|hashBlock|0.068|0.550|0.036|
|
||||
|hashElement|0.002|0.030|0.000|
|
||||
|hashHTMLSpans|4.197|4.564|4.006|
|
||||
|hashPreCodeTags|0.139|0.543|0.106|
|
||||
|headers|1.148|4.214|0.880|
|
||||
|horizontalRule|0.214|0.273|0.199|
|
||||
|images|0.310|3.095|0.120|
|
||||
|italicsAndBold|0.279|0.378|0.235|
|
||||
|lists|3.843|8.278|2.630|
|
||||
|outdent|0.193|0.386|0.144|
|
||||
|paragraphs|5.541|8.153|4.836|
|
||||
|spanGamut|4.638|5.775|4.142|
|
||||
|strikethrough|0.003|0.052|0.000|
|
||||
|stripLinkDefinitions|0.167|0.275|0.142|
|
||||
|tables|0.002|0.036|0.000|
|
||||
|unescapeSpecialChars|0.009|0.032|0.008|
|
||||
|
||||
|
||||
## [version 1.7.2](https://github.com/showdownjs/showdown/tree/1.7.2)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.292|5.780|0.087|
|
||||
|performance.testfile.md|30.396|53.860|26.054|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.303|7.798|2.377|
|
||||
|anchors|0.347|0.647|0.287|
|
||||
|autoLinks|0.088|0.165|0.063|
|
||||
|blockQuotes|2.101|5.121|1.738|
|
||||
|codeBlocks|0.239|0.878|0.184|
|
||||
|codeSpans|0.252|0.628|0.160|
|
||||
|detab|0.094|0.129|0.088|
|
||||
|encodeAmpsAndAngles|0.131|0.733|0.093|
|
||||
|encodeBackslashEscapes|0.080|0.116|0.070|
|
||||
|encodeCode|0.939|1.480|0.857|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.285|0.473|0.243|
|
||||
|githubCodeBlocks|0.214|1.047|0.140|
|
||||
|hashBlock|0.068|0.553|0.036|
|
||||
|hashElement|0.002|0.030|0.000|
|
||||
|hashHTMLSpans|4.323|6.162|4.004|
|
||||
|hashPreCodeTags|0.147|0.558|0.109|
|
||||
|headers|1.176|4.491|0.884|
|
||||
|horizontalRule|0.216|0.264|0.193|
|
||||
|images|0.156|0.559|0.118|
|
||||
|italicsAndBold|0.322|1.013|0.237|
|
||||
|lists|2.753|5.613|2.328|
|
||||
|outdent|0.163|0.232|0.140|
|
||||
|paragraphs|5.109|6.168|4.741|
|
||||
|spanGamut|4.423|6.149|4.001|
|
||||
|strikethrough|0.003|0.051|0.000|
|
||||
|stripLinkDefinitions|0.160|0.226|0.142|
|
||||
|tables|0.002|0.043|0.000|
|
||||
|unescapeSpecialChars|0.011|0.046|0.007|
|
||||
|
||||
|
||||
## [version 1.7.1](https://github.com/showdownjs/showdown/tree/1.7.1)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|1.074|20.566|0.324|
|
||||
|performance.testfile.md|30.463|82.116|26.022|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.233|9.062|2.359|
|
||||
|anchors|0.351|0.763|0.286|
|
||||
|autoLinks|0.089|0.190|0.065|
|
||||
|blockQuotes|2.074|4.989|1.729|
|
||||
|codeBlocks|0.256|0.937|0.179|
|
||||
|codeSpans|0.242|0.839|0.158|
|
||||
|detab|0.099|0.168|0.086|
|
||||
|encodeAmpsAndAngles|0.131|0.646|0.093|
|
||||
|encodeBackslashEscapes|0.076|0.140|0.070|
|
||||
|encodeCode|0.994|1.706|0.865|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.267|0.375|0.250|
|
||||
|githubCodeBlocks|0.192|0.966|0.140|
|
||||
|hashBlock|0.059|0.397|0.036|
|
||||
|hashElement|0.002|0.031|0.000|
|
||||
|hashHTMLSpans|4.117|5.585|3.890|
|
||||
|hashPreCodeTags|0.142|0.529|0.108|
|
||||
|headers|1.145|4.103|0.864|
|
||||
|horizontalRule|0.217|0.366|0.194|
|
||||
|images|0.151|0.553|0.117|
|
||||
|italicsAndBold|0.312|1.241|0.236|
|
||||
|lists|4.023|7.077|2.498|
|
||||
|outdent|0.175|0.261|0.148|
|
||||
|paragraphs|6.557|8.645|4.997|
|
||||
|spanGamut|5.073|6.347|4.137|
|
||||
|strikethrough|0.006|0.110|0.000|
|
||||
|stripLinkDefinitions|0.164|0.277|0.142|
|
||||
|tables|0.004|0.080|0.000|
|
||||
|unescapeSpecialChars|0.009|0.046|0.007|
|
||||
|
||||
|
||||
## [version 1.7.0](https://github.com/showdownjs/showdown/tree/1.7.0)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.393|9.953|0.097|
|
||||
|performance.testfile.md|29.416|54.253|25.949|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|4.062|7.185|2.326|
|
||||
|anchors|0.488|4.086|0.281|
|
||||
|autoLinks|0.086|0.200|0.063|
|
||||
|blockQuotes|2.071|4.554|1.733|
|
||||
|codeBlocks|0.253|0.864|0.178|
|
||||
|codeSpans|0.261|0.592|0.160|
|
||||
|detab|0.095|0.130|0.089|
|
||||
|encodeAmpsAndAngles|0.103|0.192|0.095|
|
||||
|encodeBackslashEscapes|0.106|0.589|0.071|
|
||||
|encodeCode|0.927|1.182|0.835|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.276|0.617|0.245|
|
||||
|githubCodeBlocks|0.195|0.980|0.139|
|
||||
|hashBlock|0.062|0.483|0.035|
|
||||
|hashElement|0.001|0.025|0.000|
|
||||
|hashHTMLSpans|4.120|4.610|3.859|
|
||||
|hashPreCodeTags|0.147|0.535|0.105|
|
||||
|headers|1.308|4.253|0.856|
|
||||
|horizontalRule|0.220|0.374|0.194|
|
||||
|images|0.150|0.507|0.116|
|
||||
|italicsAndBold|0.306|0.872|0.241|
|
||||
|lists|3.447|4.893|2.407|
|
||||
|outdent|0.267|0.868|0.181|
|
||||
|paragraphs|5.867|8.331|4.970|
|
||||
|spanGamut|5.039|7.124|4.116|
|
||||
|strikethrough|0.004|0.073|0.000|
|
||||
|stripLinkDefinitions|0.153|0.243|0.140|
|
||||
|tables|0.002|0.044|0.000|
|
||||
|unescapeSpecialChars|0.009|0.041|0.007|
|
||||
|
||||
|
||||
## [version 1.6.4](https://github.com/showdownjs/showdown/tree/1.6.4)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.376|6.381|0.183|
|
||||
|performance.testfile.md|33.835|61.049|30.186|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|2.564|8.347|1.871|
|
||||
|anchors|0.499|4.222|0.270|
|
||||
|autoLinks|0.080|0.174|0.061|
|
||||
|blockQuotes|3.343|7.306|2.850|
|
||||
|codeBlocks|0.221|0.822|0.172|
|
||||
|codeSpans|0.229|0.744|0.156|
|
||||
|detab|0.097|0.154|0.086|
|
||||
|encodeAmpsAndAngles|0.117|0.200|0.094|
|
||||
|encodeBackslashEscapes|0.086|0.230|0.068|
|
||||
|encodeCode|0.885|1.165|0.816|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.298|0.495|0.240|
|
||||
|githubCodeBlocks|0.183|0.785|0.133|
|
||||
|hashBlock|0.044|0.098|0.035|
|
||||
|hashElement|0.002|0.033|0.000|
|
||||
|hashHTMLSpans|4.200|4.552|3.987|
|
||||
|hashPreCodeTags|0.130|0.313|0.106|
|
||||
|headers|1.224|4.010|0.945|
|
||||
|horizontalRule|0.412|4.175|0.196|
|
||||
|images|0.088|0.203|0.073|
|
||||
|italicsAndBold|0.276|0.414|0.233|
|
||||
|lists|5.005|6.109|4.663|
|
||||
|outdent|0.152|0.337|0.139|
|
||||
|paragraphs|5.336|7.117|4.843|
|
||||
|spanGamut|4.450|6.153|3.857|
|
||||
|strikethrough|0.003|0.049|0.000|
|
||||
|stripLinkDefinitions|0.180|0.316|0.147|
|
||||
|tables|0.003|0.055|0.000|
|
||||
|unescapeSpecialChars|0.009|0.047|0.007|
|
||||
|
||||
|
||||
## [version 1.6.3](https://github.com/showdownjs/showdown/tree/1.6.3)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.388|6.064|0.174|
|
||||
|performance.testfile.md|26.899|49.063|24.845|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|2.616|8.181|1.899|
|
||||
|anchors|0.515|4.691|0.264|
|
||||
|autoLinks|0.093|0.188|0.073|
|
||||
|blockQuotes|4.518|8.953|3.036|
|
||||
|codeBlocks|0.223|0.348|0.188|
|
||||
|codeSpans|0.318|1.095|0.177|
|
||||
|detab|0.092|0.137|0.087|
|
||||
|encodeAmpsAndAngles|0.044|0.089|0.038|
|
||||
|encodeBackslashEscapes|0.108|0.265|0.078|
|
||||
|encodeCode|1.535|9.896|0.865|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.294|0.523|0.253|
|
||||
|githubCodeBlocks|0.208|0.790|0.142|
|
||||
|hashBlock|0.042|0.123|0.036|
|
||||
|hashElement|0.002|0.029|0.000|
|
||||
|hashHTMLSpans|0.410|1.598|0.240|
|
||||
|hashPreCodeTags|0.132|0.395|0.110|
|
||||
|headers|1.015|1.502|0.806|
|
||||
|horizontalRule|0.220|0.357|0.195|
|
||||
|images|0.158|0.978|0.077|
|
||||
|italicsAndBold|0.288|0.639|0.241|
|
||||
|lists|5.151|6.331|4.629|
|
||||
|outdent|0.180|0.363|0.143|
|
||||
|paragraphs|4.548|6.309|4.002|
|
||||
|spanGamut|1.519|1.864|1.372|
|
||||
|strikethrough|0.003|0.065|0.000|
|
||||
|stripLinkDefinitions|0.179|0.313|0.144|
|
||||
|tables|0.004|0.063|0.000|
|
||||
|unescapeSpecialChars|0.011|0.049|0.007|
|
||||
|
||||
|
||||
## [version 1.6.2](https://github.com/showdownjs/showdown/tree/1.6.2)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.613|5.894|0.169|
|
||||
|performance.testfile.md|25.970|62.882|23.710|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|2.669|8.479|1.885|
|
||||
|anchors|0.500|3.841|0.268|
|
||||
|autoLinks|0.098|0.211|0.072|
|
||||
|blockQuotes|3.222|5.826|2.791|
|
||||
|codeBlocks|0.177|0.371|0.157|
|
||||
|codeSpans|0.218|0.483|0.151|
|
||||
|detab|0.135|0.655|0.085|
|
||||
|encodeAmpsAndAngles|0.042|0.118|0.036|
|
||||
|encodeBackslashEscapes|0.080|0.133|0.068|
|
||||
|encodeCode|0.560|0.982|0.484|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.353|0.568|0.291|
|
||||
|githubCodeBlocks|0.180|0.773|0.127|
|
||||
|hashBlock|0.058|0.312|0.037|
|
||||
|hashElement|0.003|0.046|0.000|
|
||||
|hashHTMLSpans|0.475|2.325|0.234|
|
||||
|hashPreCodeTags|0.122|0.307|0.107|
|
||||
|headers|0.858|0.954|0.780|
|
||||
|horizontalRule|0.227|0.418|0.197|
|
||||
|images|0.171|1.453|0.077|
|
||||
|italicsAndBold|0.101|0.202|0.088|
|
||||
|lists|4.931|5.460|4.556|
|
||||
|outdent|0.163|0.315|0.142|
|
||||
|paragraphs|3.790|5.564|3.278|
|
||||
|spanGamut|1.442|2.012|1.203|
|
||||
|strikethrough|0.004|0.082|0.000|
|
||||
|stripBlankLines|0.086|0.130|0.080|
|
||||
|stripLinkDefinitions|0.160|0.217|0.145|
|
||||
|tables|0.004|0.076|0.000|
|
||||
|unescapeSpecialChars|0.010|0.058|0.007|
|
||||
|
||||
|
||||
## [version 1.6.1](https://github.com/showdownjs/showdown/tree/1.6.1)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.317|5.498|0.161|
|
||||
|readme.md|26.014|46.799|24.245|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|2.641|7.792|1.936|
|
||||
|anchors|0.475|4.063|0.259|
|
||||
|autoLinks|0.089|0.197|0.069|
|
||||
|blockQuotes|3.213|6.054|2.880|
|
||||
|codeBlocks|0.162|0.269|0.153|
|
||||
|codeSpans|0.169|0.399|0.141|
|
||||
|detab|0.125|0.665|0.086|
|
||||
|encodeAmpsAndAngles|0.042|0.089|0.038|
|
||||
|encodeBackslashEscapes|0.076|0.133|0.068|
|
||||
|encodeCode|0.577|0.970|0.479|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.246|0.350|0.221|
|
||||
|githubCodeBlocks|0.177|0.815|0.125|
|
||||
|hashBlock|0.065|0.430|0.038|
|
||||
|hashElement|0.002|0.034|0.000|
|
||||
|hashHTMLSpans|0.424|2.321|0.241|
|
||||
|hashPreCodeTags|0.122|0.238|0.104|
|
||||
|headers|0.804|0.946|0.726|
|
||||
|horizontalRule|0.219|0.274|0.194|
|
||||
|images|0.124|0.902|0.071|
|
||||
|italicsAndBold|0.101|0.150|0.090|
|
||||
|lists|4.939|5.421|4.624|
|
||||
|outdent|0.165|0.337|0.140|
|
||||
|paragraphs|3.495|4.555|3.171|
|
||||
|spanGamut|1.319|1.992|1.147|
|
||||
|strikethrough|0.007|0.143|0.000|
|
||||
|stripBlankLines|0.094|0.155|0.082|
|
||||
|stripLinkDefinitions|0.176|0.311|0.146|
|
||||
|tables|0.002|0.039|0.000|
|
||||
|unescapeSpecialChars|0.008|0.034|0.007|
|
||||
|
||||
|
||||
## [version 1.6.0](https://github.com/showdownjs/showdown/tree/1.6.0)
|
||||
|
||||
### Test Suite: Basic (50 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|Simple "Hello World"|0.308|5.369|0.157|
|
||||
|readme.md|25.818|47.795|23.775|
|
||||
|
||||
### Test Suite: subParsers (20 cycles)
|
||||
| test | avgTime | max | min |
|
||||
|:-----|--------:|----:|----:|
|
||||
|hashHTMLBlocks|2.653|8.558|1.880|
|
||||
|anchors|0.517|4.142|0.271|
|
||||
|autoLinks|0.089|0.194|0.071|
|
||||
|blockGamut|17.372|22.941|14.082|
|
||||
|blockQuotes|3.011|4.110|2.774|
|
||||
|codeBlocks|0.243|0.834|0.193|
|
||||
|codeSpans|0.227|0.458|0.191|
|
||||
|detab|0.095|0.133|0.090|
|
||||
|encodeAmpsAndAngles|0.040|0.073|0.038|
|
||||
|encodeBackslashEscapes|0.100|0.510|0.068|
|
||||
|encodeCode|0.532|0.706|0.479|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.386|0.702|0.327|
|
||||
|githubCodeBlocks|0.214|0.778|0.156|
|
||||
|hashBlock|0.057|0.280|0.035|
|
||||
|hashElement|0.002|0.033|0.000|
|
||||
|hashHTMLSpans|0.384|1.997|0.236|
|
||||
|hashPreCodeTags|0.133|0.200|0.116|
|
||||
|headers|0.944|2.468|0.782|
|
||||
|images|0.120|0.486|0.086|
|
||||
|italicsAndBold|0.111|0.445|0.088|
|
||||
|lists|5.783|13.249|4.464|
|
||||
|outdent|0.306|0.956|0.225|
|
||||
|paragraphs|6.583|8.811|4.499|
|
||||
|spanGamut|2.437|3.067|1.647|
|
||||
|strikethrough|0.005|0.100|0.000|
|
||||
|stripBlankLines|0.121|0.175|0.092|
|
||||
|stripLinkDefinitions|0.247|0.573|0.171|
|
||||
|tables|0.006|0.099|0.000|
|
||||
|unescapeSpecialChars|0.017|0.066|0.011|
|
||||
|hashHTMLBlocks|4.561|7.807|2.461|
|
||||
|anchors|2.910|6.510|1.056|
|
||||
|blockQuotes|4.142|19.023|2.541|
|
||||
|codeBlocks|0.264|0.858|0.194|
|
||||
|codeSpans|0.365|1.421|0.278|
|
||||
|detab|0.055|0.116|0.047|
|
||||
|encodeAmpsAndAngles|0.145|0.796|0.092|
|
||||
|encodeBackslashEscapes|0.087|0.266|0.063|
|
||||
|encodeCode|0.605|1.144|0.522|
|
||||
|escapeSpecialCharsWithinTagAttributes|0.206|0.287|0.184|
|
||||
|githubCodeBlocks|0.251|1.003|0.192|
|
||||
|hashBlock|0.038|0.104|0.034|
|
||||
|hashElement|0.005|0.053|0.001|
|
||||
|hashHTMLSpans|5.229|9.835|4.240|
|
||||
|hashPreCodeTags|0.155|0.705|0.117|
|
||||
|headers|2.278|4.825|1.631|
|
||||
|horizontalRule|0.159|0.276|0.148|
|
||||
|images|0.159|0.390|0.124|
|
||||
|italicsAndBold|0.280|0.773|0.211|
|
||||
|lists|4.253|8.173|3.146|
|
||||
|outdent|0.181|0.238|0.162|
|
||||
|paragraphs|8.968|11.331|7.857|
|
||||
|spanGamut|2.985|4.162|2.486|
|
||||
|strikethrough|0.007|0.099|0.001|
|
||||
|stripLinkDefinitions|1.911|2.855|1.447|
|
||||
|tables|0.008|0.127|0.002|
|
||||
|unescapeSpecialChars|0.013|0.073|0.010|
|
||||
|
||||
|
||||
|
|
362
src/cli/cli.js
362
src/cli/cli.js
|
@ -1,45 +1,333 @@
|
|||
/**
|
||||
* Created by tivie
|
||||
*/
|
||||
'use strict';
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
Command = require('commander').Command,
|
||||
program = new Command(),
|
||||
path1 = path.resolve(__dirname + '/../dist/showdown.js'),
|
||||
path2 = path.resolve(__dirname + '/../../.build/showdown.js'),
|
||||
showdown,
|
||||
version;
|
||||
|
||||
var yargs = require('yargs');
|
||||
|
||||
yargs
|
||||
.version()
|
||||
.alias('v', 'version')
|
||||
.option('h', {
|
||||
alias: 'help',
|
||||
description: 'Show help'
|
||||
})
|
||||
.option('q', {
|
||||
alias: 'quiet',
|
||||
description: 'Quiet mode. Only print errors',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
.option('m', {
|
||||
alias: 'mute',
|
||||
description: 'Mute mode. Does not print anything',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
.usage('Usage: showdown <command> [options]')
|
||||
.demand(1, 'You must provide a valid command')
|
||||
.command('makehtml', 'Converts markdown into html')
|
||||
.example('showdown makehtml -i foo.md -o bar.html', 'Converts \'foo.md\' to \'bar.html\'')
|
||||
.wrap(yargs.terminalWidth());
|
||||
|
||||
var argv = yargs.argv,
|
||||
command = argv._[0];
|
||||
|
||||
if (command === 'makehtml') {
|
||||
require('./makehtml.cmd.js').run();
|
||||
// require shodown. We use conditional loading for each use case
|
||||
if (fs.existsSync(path1)) {
|
||||
// production. File lives in bin directory
|
||||
showdown = require(path1);
|
||||
version = require(path.resolve(__dirname + '/../package.json')).version;
|
||||
} else if (fs.existsSync(path2)) {
|
||||
// testing envo, uses the concatenated stuff for testing
|
||||
showdown = require(path2);
|
||||
version = require(path.resolve(__dirname + '/../../package.json')).version;
|
||||
} else {
|
||||
yargs.showHelp();
|
||||
// cold testing (manual) of cli.js in the src file. We load the dist file
|
||||
showdown = require('../../dist/showdown');
|
||||
version = require('../../package.json');
|
||||
}
|
||||
|
||||
if (argv.help) {
|
||||
yargs.showHelp();
|
||||
|
||||
program
|
||||
.name('showdown')
|
||||
.description('CLI to Showdownjs markdown parser v' + version)
|
||||
.version(version)
|
||||
.usage('<command> [options]')
|
||||
.option('-q, --quiet', 'Quiet mode. Only print errors')
|
||||
.option('-m, --mute', 'Mute mode. Does not print anything');
|
||||
|
||||
program.command('makehtml')
|
||||
.description('Converts markdown into html')
|
||||
|
||||
.addHelpText('after', '\n\nExamples:')
|
||||
.addHelpText('after', ' showdown makehtml -i Reads from stdin and outputs to stdout')
|
||||
.addHelpText('after', ' showdown makehtml -i foo.md -o bar.html Reads \'foo.md\' and writes to \'bar.html\'')
|
||||
.addHelpText('after', ' showdown makehtml -i --flavor="github" Parses stdin using GFM style')
|
||||
|
||||
.addHelpText('after', '\nNote for windows users:')
|
||||
.addHelpText('after', 'When reading from stdin, use option -u to set the proper encoding or run `chcp 65001` prior to calling showdown cli to set the command line to utf-8')
|
||||
|
||||
.option('-i, --input [file]', 'Input source. Usually a md file. If omitted or empty, reads from stdin. Windows users see note below.', true)
|
||||
.option('-o, --output [file]', 'Output target. Usually a html file. If omitted or empty, writes to stdout', true)
|
||||
.option('-u, --encoding <encoding>', 'Sets the input encoding', 'utf8')
|
||||
.option('-y, --output-encoding <encoding>', 'Sets the output encoding', 'utf8')
|
||||
.option('-a, --append', 'Append data to output instead of overwriting. Ignored if writing to stdout', false)
|
||||
.option('-e, --extensions <extensions...>', 'Load the specified extensions. Should be valid paths to node compatible extensions')
|
||||
.option('-p, --flavor <flavor>', 'Run with a predetermined flavor of options. Default is vanilla', 'vanilla')
|
||||
.option('-c, --config <config...>', 'Enables showdown makehtml parser config options. Overrides flavor')
|
||||
.option('--config-help', 'Shows configuration options for showdown parser')
|
||||
.action(makehtmlCommand);
|
||||
|
||||
program.parse();
|
||||
|
||||
|
||||
//
|
||||
// HELPER FUCNTIONS
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Messenger helper object to the CLI
|
||||
* @param {string} writeMode
|
||||
* @param {boolean} supress
|
||||
* @param {boolean} mute
|
||||
* @constructor
|
||||
*/
|
||||
function Messenger (writeMode, supress, mute) {
|
||||
'use strict';
|
||||
writeMode = writeMode || 'stderr';
|
||||
supress = (!!supress || !!mute);
|
||||
mute = !!mute;
|
||||
this._print = (writeMode === 'stdout') ? console.log : console.error;
|
||||
|
||||
this.errorExit = function (e) {
|
||||
if (!mute) {
|
||||
console.error('ERROR: ' + e.message);
|
||||
console.error('Run \'showdown <command> -h\' for help');
|
||||
}
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
this.okExit = function () {
|
||||
if (!mute) {
|
||||
this._print('\n');
|
||||
this._print('DONE!');
|
||||
}
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
this.printMsg = function (msg) {
|
||||
if (supress || mute || !msg) {
|
||||
return;
|
||||
}
|
||||
this._print(msg);
|
||||
};
|
||||
|
||||
this.printError = function (msg) {
|
||||
if (mute) {
|
||||
return;
|
||||
}
|
||||
console.error(msg);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to show Showdown Options
|
||||
*/
|
||||
function showShowdownOptions () {
|
||||
'use strict';
|
||||
var showdownOptions = showdown.getDefaultOptions(false);
|
||||
console.log('\nshowdown makehtml config options:');
|
||||
// show showdown options
|
||||
for (var sopt in showdownOptions) {
|
||||
if (showdownOptions.hasOwnProperty(sopt)) {
|
||||
console.log(' ' + sopt + ':', '[default=' + showdownOptions[sopt].defaultValue + ']',showdownOptions[sopt].describe);
|
||||
}
|
||||
}
|
||||
console.log('\n\nExample: showdown makehtml -c openLinksInNewWindow ghMentions ghMentionsLink="https://google.com"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to parse showdown options
|
||||
* @param {{}} configOptions
|
||||
* @param {{}} defaultOptions
|
||||
* @returns {{}}
|
||||
*/
|
||||
function parseShowdownOptions (configOptions, defaultOptions) {
|
||||
'use strict';
|
||||
var shOpt = defaultOptions;
|
||||
|
||||
// first prepare passed options
|
||||
if (configOptions) {
|
||||
for (var i = 0; i < configOptions.length; ++i) {
|
||||
var opt = configOptions[i],
|
||||
key = configOptions[i],
|
||||
val = true;
|
||||
if (/=/.test(opt)) {
|
||||
key = opt.split('=')[0];
|
||||
val = opt.split('=')[1];
|
||||
}
|
||||
shOpt[key] = val;
|
||||
}
|
||||
}
|
||||
return shOpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads stdin
|
||||
* @returns {string}
|
||||
*/
|
||||
function readFromStdIn (encoding) {
|
||||
'use strict';
|
||||
/*
|
||||
// aparently checking the size of stdin is unreliable so we just won't test
|
||||
var size = fs.fstatSync(process.stdin.fd).size;
|
||||
if (size <= 0) {
|
||||
throw new Error('Could not read from stdin, reason: stdin is empty');
|
||||
}
|
||||
*/
|
||||
encoding = encoding || 'utf8';
|
||||
try {
|
||||
return fs.readFileSync(process.stdin.fd, encoding).toString();
|
||||
} catch (e) {
|
||||
throw new Error('Could not read from stdin, reason: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from a file
|
||||
* @param {string} file Filepath to dile
|
||||
* @param {string} encoding Encoding of the file
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function readFromFile (file, encoding) {
|
||||
'use strict';
|
||||
try {
|
||||
return fs.readFileSync(file, encoding);
|
||||
} catch (err) {
|
||||
throw new Error('Could not read from file ' + file + ', reason: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to stdout
|
||||
* @param {string} html
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function writeToStdOut (html) {
|
||||
'use strict';
|
||||
if (!process.stdout.write(html)) {
|
||||
throw new Error('Could not write to StdOut');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to file
|
||||
* @param {string} html HTML to write
|
||||
* @param {string} file Filepath
|
||||
* @param {boolean} append If the result should be appended
|
||||
*/
|
||||
function writeToFile (html, file, append) {
|
||||
'use strict';
|
||||
// If a flag is passed, it means we should append instead of overwriting.
|
||||
// Only works with files, obviously
|
||||
var write = (append) ? fs.appendFileSync : fs.writeFileSync;
|
||||
try {
|
||||
write(file, html);
|
||||
} catch (err) {
|
||||
throw new Error('Could not write to file ' + file + ', readon: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* makehtml command
|
||||
* @param {{}} options
|
||||
* @param {Command} cmd
|
||||
*/
|
||||
function makehtmlCommand (options, cmd) {
|
||||
'use strict';
|
||||
|
||||
// show configuration options for showdown helper if configHelp was passed
|
||||
if (options.configHelp) {
|
||||
showShowdownOptions();
|
||||
return;
|
||||
}
|
||||
|
||||
var quiet = !!(cmd.parent._optionValues.quiet),
|
||||
mute = !!(cmd.parent._optionValues.mute),
|
||||
readMode = (!options.input || options.input === '' || options.input === true) ? 'stdin' : 'file',
|
||||
writeMode = (!options.output || options.output === '' || options.output === true) ? 'stdout' : 'file',
|
||||
msgMode = (writeMode === 'file') ? 'stdout' : 'stderr',
|
||||
// initiate Messenger helper, can maybe be replaced with commanderjs internal stuff
|
||||
messenger = new Messenger(msgMode, quiet, mute),
|
||||
defaultOptions = showdown.getDefaultOptions(true),
|
||||
md, html;
|
||||
|
||||
// deal with flavor first since config flag overrides flavor individual options
|
||||
if (options.flavor) {
|
||||
messenger.printMsg('Enabling flavor ' + options.flavor + '...');
|
||||
defaultOptions = showdown.getFlavorOptions(options.flavor);
|
||||
if (!defaultOptions) {
|
||||
messenger.errorExit(new Error('Flavor ' + options.flavor + ' is not recognised'));
|
||||
return;
|
||||
}
|
||||
messenger.printMsg('OK!');
|
||||
}
|
||||
// store config options in the options.config as an object
|
||||
options.config = parseShowdownOptions(options.config, defaultOptions);
|
||||
|
||||
// print enabled options
|
||||
for (var o in options.config) {
|
||||
if (options.config.hasOwnProperty(o) && options.config[o] === true) {
|
||||
messenger.printMsg('Enabling option ' + o);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the converter
|
||||
messenger.printMsg('\nInitializing converter...');
|
||||
var converter;
|
||||
try {
|
||||
converter = new showdown.Converter(options.config);
|
||||
} catch (e) {
|
||||
messenger.errorExit(e);
|
||||
return;
|
||||
}
|
||||
messenger.printMsg('OK!');
|
||||
|
||||
// load extensions
|
||||
if (options.extensions) {
|
||||
messenger.printMsg('\nLoading extensions...');
|
||||
for (var i = 0; i < options.extensions.length; ++i) {
|
||||
try {
|
||||
messenger.printMsg(options.extensions[i]);
|
||||
var ext = require(options.extensions[i]);
|
||||
converter.addExtension(ext, options.extensions[i]);
|
||||
messenger.printMsg(options.extensions[i] + ' loaded...');
|
||||
} catch (e) {
|
||||
messenger.printError('ERROR: Could not load extension ' + options.extensions[i] + '. Reason:');
|
||||
messenger.errorExit(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messenger.printMsg('...');
|
||||
// read the input
|
||||
messenger.printMsg('Reading data from ' + readMode + '...');
|
||||
|
||||
if (readMode === 'stdin') {
|
||||
try {
|
||||
md = readFromStdIn(options.encoding);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
md = readFromFile(options.input, options.encoding);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// process the input
|
||||
messenger.printMsg('Parsing markdown...');
|
||||
html = converter.makeHtml(md);
|
||||
|
||||
// write the output
|
||||
messenger.printMsg('Writing data to ' + writeMode + '...');
|
||||
if (writeMode === 'stdout') {
|
||||
try {
|
||||
writeToStdOut(html);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
writeToFile(html, options.output, options.append);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
messenger.okExit();
|
||||
}
|
||||
process.exit(0);
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
var yargs = require('yargs'),
|
||||
fs = require('fs'),
|
||||
Messenger = require('./messenger.js'),
|
||||
showdown = require('../../dist/showdown'),
|
||||
showdownOptions = showdown.getDefaultOptions(false);
|
||||
|
||||
yargs.reset()
|
||||
.usage('Usage: showdown makehtml [options]')
|
||||
.example('showdown makehtml -i', 'Reads from stdin and outputs to stdout')
|
||||
.example('showdown makehtml -i foo.md -o bar.html', 'Reads \'foo.md\' and writes to \'bar.html\'')
|
||||
.example('showdown makehtml -i --flavor="github"', 'Parses stdin using GFM style')
|
||||
.version()
|
||||
.alias('v', 'version')
|
||||
.config('c')
|
||||
.alias('c', 'config')
|
||||
.help('h')
|
||||
.alias('h', 'help')
|
||||
.option('i', {
|
||||
alias : 'input',
|
||||
describe: 'Input source. Usually a md file. If omitted or empty, reads from stdin',
|
||||
type: 'string'
|
||||
})
|
||||
.option('o', {
|
||||
alias : 'output',
|
||||
describe: 'Output target. Usually a html file. If omitted or empty, writes to stdout',
|
||||
type: 'string',
|
||||
default: false
|
||||
})
|
||||
.option('u', {
|
||||
alias : 'encoding',
|
||||
describe: 'Input encoding',
|
||||
type: 'string'
|
||||
})
|
||||
.option('a', {
|
||||
alias : 'append',
|
||||
describe: 'Append data to output instead of overwriting',
|
||||
type: 'string',
|
||||
default: false
|
||||
})
|
||||
.option('e', {
|
||||
alias : 'extensions',
|
||||
describe: 'Load the specified extensions. Should be valid paths to node compatible extensions',
|
||||
type: 'array'
|
||||
})
|
||||
.option('p', {
|
||||
alias : 'flavor',
|
||||
describe: 'Run with a predetermined flavor of options. Default is vanilla',
|
||||
type: 'string'
|
||||
})
|
||||
.option('q', {
|
||||
alias: 'quiet',
|
||||
description: 'Quiet mode. Only print errors',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
.option('m', {
|
||||
alias: 'mute',
|
||||
description: 'Mute mode. Does not print anything',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
});
|
||||
|
||||
// load showdown default options
|
||||
for (var opt in showdownOptions) {
|
||||
if (showdownOptions.hasOwnProperty(opt)) {
|
||||
if (showdownOptions[opt].defaultValue === false) {
|
||||
showdownOptions[opt].default = null;
|
||||
} else {
|
||||
showdownOptions[opt].default = showdownOptions[opt].defaultValue;
|
||||
}
|
||||
yargs.option(opt, showdownOptions[opt]);
|
||||
}
|
||||
}
|
||||
|
||||
function run () {
|
||||
'use strict';
|
||||
var argv = yargs.argv,
|
||||
readMode = (!argv.i || argv.i === '') ? 'stdin' : 'file',
|
||||
writeMode = (!argv.o || argv.o === '') ? 'stdout' : 'file',
|
||||
msgMode = (writeMode === 'file') ? 'stdout' : 'stderr',
|
||||
/**
|
||||
* MSG object
|
||||
* @type {Messenger}
|
||||
*/
|
||||
messenger = new Messenger(msgMode, argv.q, argv.m),
|
||||
read = (readMode === 'stdin') ? readFromStdIn : readFromFile,
|
||||
write = (writeMode === 'stdout') ? writeToStdOut : writeToFile,
|
||||
enc = argv.encoding || 'utf8',
|
||||
flavor = argv.p,
|
||||
append = argv.a || false,
|
||||
options = parseOptions(flavor),
|
||||
converter = new showdown.Converter(options),
|
||||
md, html;
|
||||
|
||||
// Load extensions
|
||||
if (argv.e) {
|
||||
messenger.printMsg('Loading extensions');
|
||||
for (var i = 0; i < argv.e.length; ++i) {
|
||||
try {
|
||||
var ext = require(argv.e[i]);
|
||||
converter.addExtension(ext, argv.e[i]);
|
||||
} catch (e) {
|
||||
messenger.printError('Could not load extension ' + argv.e[i] + '. Reason:');
|
||||
messenger.errorExit(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messenger.printMsg('...');
|
||||
// read the input
|
||||
messenger.printMsg('Reading data from ' + readMode + '...');
|
||||
md = read(enc);
|
||||
|
||||
// process the input
|
||||
messenger.printMsg('Parsing markdown...');
|
||||
html = converter.makeHtml(md);
|
||||
|
||||
// write the output
|
||||
messenger.printMsg('Writing data to ' + writeMode + '...');
|
||||
write(html, append);
|
||||
messenger.okExit();
|
||||
|
||||
function parseOptions (flavor) {
|
||||
var options = {},
|
||||
flavorOpts = showdown.getFlavorOptions(flavor) || {};
|
||||
|
||||
// if flavor is not undefined, let's tell the user we're loading that preset
|
||||
if (flavor) {
|
||||
messenger.printMsg('Loading ' + flavor + ' flavor.');
|
||||
}
|
||||
|
||||
for (var opt in argv) {
|
||||
if (argv.hasOwnProperty(opt)) {
|
||||
// first we load the default options
|
||||
if (showdownOptions.hasOwnProperty(opt) && showdownOptions[opt].default !== null) {
|
||||
options[opt] = showdownOptions[opt].default;
|
||||
}
|
||||
|
||||
// we now override defaults with flavor, if a flavor was indeed passed
|
||||
if (flavorOpts.hasOwnProperty(opt)) {
|
||||
options[opt] = flavorOpts[opt];
|
||||
}
|
||||
|
||||
// lastly we override with explicit passed options
|
||||
// being careful not to pass CLI specific options, such as -v, -h or --extensions
|
||||
if (showdownOptions.hasOwnProperty(opt)) {
|
||||
if (argv[opt] === true) {
|
||||
messenger.printMsg('Enabling option ' + opt);
|
||||
options[opt] = argv[opt];
|
||||
} else if (argv[opt] === false) {
|
||||
options[opt] = argv[opt];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function readFromStdIn () {
|
||||
try {
|
||||
var size = fs.fstatSync(process.stdin.fd).size;
|
||||
return size > 0 ? fs.readSync(process.stdin.fd, size)[0] : '';
|
||||
} catch (e) {
|
||||
var err = new Error('Could not read from stdin, reason: ' + e.message);
|
||||
messenger.errorExit(err);
|
||||
}
|
||||
}
|
||||
|
||||
function readFromFile (encoding) {
|
||||
try {
|
||||
return fs.readFileSync(argv.i, encoding);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
}
|
||||
}
|
||||
|
||||
function writeToStdOut (html) {
|
||||
return process.stdout.write(html);
|
||||
}
|
||||
|
||||
function writeToFile (html, append) {
|
||||
// If a flag is passed, it means we should append instead of overwriting.
|
||||
// Only works with files, obviously
|
||||
var write = (append) ? fs.appendFileSync : fs.writeFileSync;
|
||||
try {
|
||||
write(argv.o, html);
|
||||
} catch (err) {
|
||||
messenger.errorExit(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = {
|
||||
run: run
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
function Messenger (writeMode, supress, mute) {
|
||||
'use strict';
|
||||
writeMode = writeMode || 'stderr';
|
||||
supress = (!!supress || !!mute);
|
||||
mute = !!mute;
|
||||
this._print = (writeMode === 'stdout') ? console.log : console.error;
|
||||
|
||||
this.errorExit = function (e) {
|
||||
if (!mute) {
|
||||
console.error('ERROR: ' + e.message);
|
||||
console.error('Run \'showdown <command> -h\' for help');
|
||||
}
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
this.okExit = function () {
|
||||
if (!mute) {
|
||||
this._print('\n');
|
||||
this._print('DONE!');
|
||||
}
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
this.printMsg = function (msg) {
|
||||
if (supress || mute || !msg) {
|
||||
return;
|
||||
}
|
||||
this._print(msg);
|
||||
};
|
||||
|
||||
this.printError = function (msg) {
|
||||
if (mute) {
|
||||
return;
|
||||
}
|
||||
console.error(msg);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = Messenger;
|
|
@ -197,7 +197,7 @@ showdown.Converter = function (converterOptions) {
|
|||
if (typeof callback !== 'function') {
|
||||
throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
|
||||
}
|
||||
|
||||
name = name.toLowerCase();
|
||||
if (!listeners.hasOwnProperty(name)) {
|
||||
listeners[name] = [];
|
||||
}
|
||||
|
@ -211,24 +211,33 @@ showdown.Converter = function (converterOptions) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Dispatch an event
|
||||
* @private
|
||||
*
|
||||
* @param {string} evtName Event name
|
||||
* @param {string} text Text
|
||||
* @param {{}} options Converter Options
|
||||
* @param {{}} globals
|
||||
* @returns {string}
|
||||
* @param {{}} globals Converter globals
|
||||
* @param {{}} [pParams] extra params for event
|
||||
* @returns showdown.helper.Event
|
||||
* @private
|
||||
*/
|
||||
this._dispatch = function dispatch (evtName, text, options, globals) {
|
||||
this._dispatch = function dispatch (evtName, text, options, globals, pParams) {
|
||||
evtName = evtName.toLowerCase();
|
||||
var params = pParams || {};
|
||||
params.converter = this;
|
||||
params.text = text;
|
||||
params.options = options;
|
||||
params.globals = globals;
|
||||
var event = new showdown.helper.Event(evtName, text, params);
|
||||
|
||||
if (listeners.hasOwnProperty(evtName)) {
|
||||
for (var ei = 0; ei < listeners[evtName].length; ++ei) {
|
||||
var nText = listeners[evtName][ei](evtName, text, this, options, globals);
|
||||
var nText = listeners[evtName][ei](event);
|
||||
if (nText && typeof nText !== 'undefined') {
|
||||
text = nText;
|
||||
event.setText(nText);
|
||||
}
|
||||
}
|
||||
}
|
||||
return text;
|
||||
return event;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -243,7 +252,7 @@ showdown.Converter = function (converterOptions) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Converts a markdown string into HTML
|
||||
* Converts a markdown string into HTML string
|
||||
* @param {string} text
|
||||
* @returns {*}
|
||||
*/
|
||||
|
@ -298,7 +307,7 @@ showdown.Converter = function (converterOptions) {
|
|||
text = '\n\n' + text + '\n\n';
|
||||
|
||||
// detab
|
||||
text = showdown.subParser('detab')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.detab')(text, options, globals);
|
||||
|
||||
/**
|
||||
* Strip any lines consisting only of spaces and tabs.
|
||||
|
@ -310,19 +319,19 @@ showdown.Converter = function (converterOptions) {
|
|||
|
||||
//run languageExtensions
|
||||
showdown.helper.forEach(langExtensions, function (ext) {
|
||||
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||||
text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals);
|
||||
});
|
||||
|
||||
// run the sub parsers
|
||||
text = showdown.subParser('metadata')(text, options, globals);
|
||||
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
|
||||
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
|
||||
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
||||
text = showdown.subParser('hashCodeTags')(text, options, globals);
|
||||
text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
|
||||
text = showdown.subParser('blockGamut')(text, options, globals);
|
||||
text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
|
||||
text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.metadata')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.hashPreCodeTags')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.githubCodeBlocks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.hashCodeTags')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.stripLinkDefinitions')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.blockGamut')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.unhashHTMLSpans')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.unescapeSpecialChars')(text, options, globals);
|
||||
|
||||
// attacklab: Restore dollar signs
|
||||
text = text.replace(/¨D/g, '$$');
|
||||
|
@ -331,11 +340,11 @@ showdown.Converter = function (converterOptions) {
|
|||
text = text.replace(/¨T/g, '¨');
|
||||
|
||||
// render a complete html document instead of a partial if the option is enabled
|
||||
text = showdown.subParser('completeHTMLDocument')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.completeHTMLDocument')(text, options, globals);
|
||||
|
||||
// Run output modifiers
|
||||
showdown.helper.forEach(outputModifiers, function (ext) {
|
||||
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||||
text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals);
|
||||
});
|
||||
|
||||
// update metadata
|
||||
|
@ -346,10 +355,9 @@ showdown.Converter = function (converterOptions) {
|
|||
/**
|
||||
* Converts an HTML string into a markdown string
|
||||
* @param src
|
||||
* @param [HTMLParser] A WHATWG DOM and HTML parser, such as JSDOM. If none is supplied, window.document will be used.
|
||||
* @returns {string}
|
||||
*/
|
||||
this.makeMarkdown = this.makeMd = function (src, HTMLParser) {
|
||||
this.makeMarkdown = function (src) {
|
||||
|
||||
// replace \r\n with \n
|
||||
src = src.replace(/\r\n/g, '\n');
|
||||
|
@ -360,15 +368,7 @@ showdown.Converter = function (converterOptions) {
|
|||
// ex: <em>this is</em> <strong>sparta</strong>
|
||||
src = src.replace(/>[ \t]+</, '>¨NBSP;<');
|
||||
|
||||
if (!HTMLParser) {
|
||||
if (window && window.document) {
|
||||
HTMLParser = window.document;
|
||||
} else {
|
||||
throw new Error('HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM');
|
||||
}
|
||||
}
|
||||
|
||||
var doc = HTMLParser.createElement('div');
|
||||
var doc = showdown.helper.document.createElement('div');
|
||||
doc.innerHTML = src;
|
||||
|
||||
var globals = {
|
||||
|
@ -386,7 +386,7 @@ showdown.Converter = function (converterOptions) {
|
|||
mdDoc = '';
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], globals);
|
||||
mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], options, globals);
|
||||
}
|
||||
|
||||
function clean (node) {
|
||||
|
|
3254
src/helpers.js
3254
src/helpers.js
File diff suppressed because it is too large
Load Diff
|
@ -51,11 +51,6 @@ function getDefaultOpts (simple) {
|
|||
describe: 'Turn on/off GFM autolink style',
|
||||
type: 'boolean'
|
||||
},
|
||||
excludeTrailingPunctuationFromURLs: {
|
||||
defaultValue: false,
|
||||
describe: 'Excludes trailing punctuation from links generated with autoLinking',
|
||||
type: 'boolean'
|
||||
},
|
||||
literalMidWordUnderscores: {
|
||||
defaultValue: false,
|
||||
describe: 'Parse midword underscores as literal underscores',
|
||||
|
@ -98,79 +93,89 @@ function getDefaultOpts (simple) {
|
|||
},
|
||||
smartIndentationFix: {
|
||||
defaultValue: false,
|
||||
description: 'Tries to smartly fix indentation in es6 strings',
|
||||
describe: 'Tries to smartly fix indentation in es6 strings',
|
||||
type: 'boolean'
|
||||
},
|
||||
disableForced4SpacesIndentedSublists: {
|
||||
defaultValue: false,
|
||||
description: 'Disables the requirement of indenting nested sublists by 4 spaces',
|
||||
describe: 'Disables the requirement of indenting nested sublists by 4 spaces',
|
||||
type: 'boolean'
|
||||
},
|
||||
simpleLineBreaks: {
|
||||
defaultValue: false,
|
||||
description: 'Parses simple line breaks as <br> (GFM Style)',
|
||||
describe: 'Parses simple line breaks as <br> (GFM Style)',
|
||||
type: 'boolean'
|
||||
},
|
||||
requireSpaceBeforeHeadingText: {
|
||||
defaultValue: false,
|
||||
description: 'Makes adding a space between `#` and the header text mandatory (GFM Style)',
|
||||
describe: 'Makes adding a space between `#` and the header text mandatory (GFM Style)',
|
||||
type: 'boolean'
|
||||
},
|
||||
ghMentions: {
|
||||
defaultValue: false,
|
||||
description: 'Enables github @mentions',
|
||||
describe: 'Enables github @mentions',
|
||||
type: 'boolean'
|
||||
},
|
||||
ghMentionsLink: {
|
||||
defaultValue: 'https://github.com/{u}',
|
||||
description: 'Changes the link generated by @mentions. Only applies if ghMentions option is enabled.',
|
||||
describe: 'Changes the link generated by @mentions. Only applies if ghMentions option is enabled.',
|
||||
type: 'string'
|
||||
},
|
||||
encodeEmails: {
|
||||
defaultValue: true,
|
||||
description: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities',
|
||||
describe: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities',
|
||||
type: 'boolean'
|
||||
},
|
||||
openLinksInNewWindow: {
|
||||
defaultValue: false,
|
||||
description: 'Open all links in new windows',
|
||||
describe: 'Open all links in new windows',
|
||||
type: 'boolean'
|
||||
},
|
||||
backslashEscapesHTMLTags: {
|
||||
defaultValue: false,
|
||||
description: 'Support for HTML Tag escaping. ex: \<div>foo\</div>',
|
||||
describe: 'Support for HTML Tag escaping. ex: \<div>foo\</div>',
|
||||
type: 'boolean'
|
||||
},
|
||||
emoji: {
|
||||
defaultValue: false,
|
||||
description: 'Enable emoji support. Ex: `this is a :smile: emoji`',
|
||||
describe: 'Enable emoji support. Ex: `this is a :smile: emoji`',
|
||||
type: 'boolean'
|
||||
},
|
||||
underline: {
|
||||
defaultValue: false,
|
||||
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
||||
describe: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
||||
type: 'boolean'
|
||||
},
|
||||
ellipsis: {
|
||||
defaultValue: true,
|
||||
description: 'Replaces three dots with the ellipsis unicode character',
|
||||
describe: 'Replaces three dots with the ellipsis unicode character',
|
||||
type: 'boolean'
|
||||
},
|
||||
completeHTMLDocument: {
|
||||
defaultValue: false,
|
||||
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
||||
describe: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
||||
type: 'boolean'
|
||||
},
|
||||
metadata: {
|
||||
defaultValue: false,
|
||||
description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
|
||||
describe: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
|
||||
type: 'boolean'
|
||||
},
|
||||
splitAdjacentBlockquotes: {
|
||||
defaultValue: false,
|
||||
description: 'Split adjacent blockquote blocks',
|
||||
describe: 'Split adjacent blockquote blocks',
|
||||
type: 'boolean'
|
||||
}
|
||||
},
|
||||
moreStyling: {
|
||||
defaultValue: false,
|
||||
describe: 'Adds some useful styling css classes in the generated html',
|
||||
type: 'boolean'
|
||||
},
|
||||
relativePathBaseUrl: {
|
||||
defaultValue: false,
|
||||
describe: 'Prepends a base URL to relative paths',
|
||||
type: 'string'
|
||||
},
|
||||
};
|
||||
if (simple === false) {
|
||||
return JSON.parse(JSON.stringify(defaultOptions));
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* Created by Tivie on 06-01-2015.
|
||||
*/
|
||||
|
||||
// Private properties
|
||||
var showdown = {},
|
||||
parsers = {},
|
||||
|
@ -12,7 +11,6 @@ var showdown = {},
|
|||
github: {
|
||||
omitExtraWLInCodeBlocks: true,
|
||||
simplifiedAutoLink: true,
|
||||
excludeTrailingPunctuationFromURLs: true,
|
||||
literalMidWordUnderscores: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
|
@ -36,7 +34,6 @@ var showdown = {},
|
|||
omitExtraWLInCodeBlocks: true,
|
||||
parseImgDimensions: true,
|
||||
simplifiedAutoLink: true,
|
||||
excludeTrailingPunctuationFromURLs: true,
|
||||
literalMidWordUnderscores: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
|
@ -181,6 +178,8 @@ showdown.subParser = function (name, func) {
|
|||
throw Error('SubParser named ' + name + ' not registered!');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Error('showdown.subParser function first argument must be a string (the name of the subparser)');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -188,7 +187,7 @@ showdown.subParser = function (name, func) {
|
|||
* Gets or registers an extension
|
||||
* @static
|
||||
* @param {string} name
|
||||
* @param {object|function=} ext
|
||||
* @param {object|object[]|function=} ext
|
||||
* @returns {*}
|
||||
*/
|
||||
showdown.extension = function (name, ext) {
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/**
|
||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||
*/
|
||||
showdown.subParser('anchors', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('anchors.before', text, options, globals);
|
||||
|
||||
var writeAnchorTag = function (wholeMatch, linkText, linkId, url, m5, m6, title) {
|
||||
if (showdown.helper.isUndefined(title)) {
|
||||
title = '';
|
||||
}
|
||||
linkId = linkId.toLowerCase();
|
||||
|
||||
// Special case for explicit empty url
|
||||
if (wholeMatch.search(/\(<?\s*>? ?(['"].*['"])?\)$/m) > -1) {
|
||||
url = '';
|
||||
} else if (!url) {
|
||||
if (!linkId) {
|
||||
// lower-case and turn embedded newlines into spaces
|
||||
linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
|
||||
}
|
||||
url = '#' + linkId;
|
||||
|
||||
if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
|
||||
url = globals.gUrls[linkId];
|
||||
if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
|
||||
title = globals.gTitles[linkId];
|
||||
}
|
||||
} else {
|
||||
return wholeMatch;
|
||||
}
|
||||
}
|
||||
|
||||
//url = showdown.helper.escapeCharacters(url, '*_', false); // replaced line to improve performance
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
var result = '<a href="' + url + '"';
|
||||
|
||||
if (title !== '' && title !== null) {
|
||||
title = title.replace(/"/g, '"');
|
||||
//title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
|
||||
title = title.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
result += ' title="' + title + '"';
|
||||
}
|
||||
|
||||
// optionLinksInNewWindow only applies
|
||||
// to external links. Hash links (#) open in same page
|
||||
if (options.openLinksInNewWindow && !/^#/.test(url)) {
|
||||
// escaped _
|
||||
result += ' rel="noopener noreferrer" target="¨E95Eblank"';
|
||||
}
|
||||
|
||||
result += '>' + linkText + '</a>';
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// First, handle reference-style links: [link text] [id]
|
||||
text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g, writeAnchorTag);
|
||||
|
||||
// Next, inline-style links: [link text](url "optional title")
|
||||
// cases with crazy urls like ./image/cat1).png
|
||||
text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,
|
||||
writeAnchorTag);
|
||||
|
||||
// normal cases
|
||||
text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,
|
||||
writeAnchorTag);
|
||||
|
||||
// handle reference-style shortcuts: [link text]
|
||||
// These must come last in case you've also got [link test][1]
|
||||
// or [link test](/foo)
|
||||
text = text.replace(/\[([^\[\]]+)]()()()()()/g, writeAnchorTag);
|
||||
|
||||
// Lastly handle GithubMentions if option is enabled
|
||||
if (options.ghMentions) {
|
||||
text = text.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gmi, function (wm, st, escape, mentions, username) {
|
||||
if (escape === '\\') {
|
||||
return st + mentions;
|
||||
}
|
||||
|
||||
//check if options.ghMentionsLink is a string
|
||||
if (!showdown.helper.isString(options.ghMentionsLink)) {
|
||||
throw new Error('ghMentionsLink option must be a string');
|
||||
}
|
||||
var lnk = options.ghMentionsLink.replace(/\{u}/g, username),
|
||||
target = '';
|
||||
if (options.openLinksInNewWindow) {
|
||||
target = ' rel="noopener noreferrer" target="¨E95Eblank"';
|
||||
}
|
||||
return st + '<a href="' + lnk + '"' + target + '>' + mentions + '</a>';
|
||||
});
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('anchors.after', text, options, globals);
|
||||
return text;
|
||||
});
|
|
@ -1,79 +0,0 @@
|
|||
// url allowed chars [a-z\d_.~:/?#[]@!$&'()*+,;=-]
|
||||
|
||||
var simpleURLRegex = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,
|
||||
simpleURLRegex2 = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,
|
||||
delimUrlRegex = /()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,
|
||||
simpleMailRegex = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi,
|
||||
delimMailRegex = /<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
|
||||
|
||||
replaceLink = function (options) {
|
||||
'use strict';
|
||||
return function (wm, leadingMagicChars, link, m2, m3, trailingPunctuation, trailingMagicChars) {
|
||||
link = link.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
var lnkTxt = link,
|
||||
append = '',
|
||||
target = '',
|
||||
lmc = leadingMagicChars || '',
|
||||
tmc = trailingMagicChars || '';
|
||||
if (/^www\./i.test(link)) {
|
||||
link = link.replace(/^www\./i, 'http://www.');
|
||||
}
|
||||
if (options.excludeTrailingPunctuationFromURLs && trailingPunctuation) {
|
||||
append = trailingPunctuation;
|
||||
}
|
||||
if (options.openLinksInNewWindow) {
|
||||
target = ' rel="noopener noreferrer" target="¨E95Eblank"';
|
||||
}
|
||||
return lmc + '<a href="' + link + '"' + target + '>' + lnkTxt + '</a>' + append + tmc;
|
||||
};
|
||||
},
|
||||
|
||||
replaceMail = function (options, globals) {
|
||||
'use strict';
|
||||
return function (wholeMatch, b, mail) {
|
||||
var href = 'mailto:';
|
||||
b = b || '';
|
||||
mail = showdown.subParser('unescapeSpecialChars')(mail, options, globals);
|
||||
if (options.encodeEmails) {
|
||||
href = showdown.helper.encodeEmailAddress(href + mail);
|
||||
mail = showdown.helper.encodeEmailAddress(mail);
|
||||
} else {
|
||||
href = href + mail;
|
||||
}
|
||||
return b + '<a href="' + href + '">' + mail + '</a>';
|
||||
};
|
||||
};
|
||||
|
||||
showdown.subParser('autoLinks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('autoLinks.before', text, options, globals);
|
||||
|
||||
text = text.replace(delimUrlRegex, replaceLink(options));
|
||||
text = text.replace(delimMailRegex, replaceMail(options, globals));
|
||||
|
||||
text = globals.converter._dispatch('autoLinks.after', text, options, globals);
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
showdown.subParser('simplifiedAutoLinks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.simplifiedAutoLink) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('simplifiedAutoLinks.before', text, options, globals);
|
||||
|
||||
if (options.excludeTrailingPunctuationFromURLs) {
|
||||
text = text.replace(simpleURLRegex2, replaceLink(options));
|
||||
} else {
|
||||
text = text.replace(simpleURLRegex, replaceLink(options));
|
||||
}
|
||||
text = text.replace(simpleMailRegex, replaceMail(options, globals));
|
||||
|
||||
text = globals.converter._dispatch('simplifiedAutoLinks.after', text, options, globals);
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* These are all the transformations that form block-level
|
||||
* tags like paragraphs, headers, and list items.
|
||||
*/
|
||||
showdown.subParser('blockGamut', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('blockGamut.before', text, options, globals);
|
||||
|
||||
// we parse blockquotes first so that we can have headings and hrs
|
||||
// inside blockquotes
|
||||
text = showdown.subParser('blockQuotes')(text, options, globals);
|
||||
text = showdown.subParser('headers')(text, options, globals);
|
||||
|
||||
// Do Horizontal Rules:
|
||||
text = showdown.subParser('horizontalRule')(text, options, globals);
|
||||
|
||||
text = showdown.subParser('lists')(text, options, globals);
|
||||
text = showdown.subParser('codeBlocks')(text, options, globals);
|
||||
text = showdown.subParser('tables')(text, options, globals);
|
||||
|
||||
// We already ran _HashHTMLBlocks() before, in Markdown(), but that
|
||||
// was to escape raw HTML in the original Markdown source. This time,
|
||||
// we're escaping the markup we've just created, so that we don't wrap
|
||||
// <p> tags around block-level tags.
|
||||
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
||||
text = showdown.subParser('paragraphs')(text, options, globals);
|
||||
|
||||
text = globals.converter._dispatch('blockGamut.after', text, options, globals);
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
showdown.subParser('ellipsis', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.ellipsis) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('ellipsis.before', text, options, globals);
|
||||
|
||||
text = text.replace(/\.\.\./g, '…');
|
||||
|
||||
text = globals.converter._dispatch('ellipsis.after', text, options, globals);
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
showdown.subParser('hashBlock', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('hashBlock.before', text, options, globals);
|
||||
text = text.replace(/(^\n+|\n+$)/g, '');
|
||||
text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
|
||||
text = globals.converter._dispatch('hashBlock.after', text, options, globals);
|
||||
return text;
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* Hash and escape <code> elements that should not be parsed as markdown
|
||||
*/
|
||||
showdown.subParser('hashCodeTags', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('hashCodeTags.before', text, options, globals);
|
||||
|
||||
var repFunc = function (wholeMatch, match, left, right) {
|
||||
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
|
||||
return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
|
||||
};
|
||||
|
||||
// Hash naked <code>
|
||||
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>]*>', '</code>', 'gim');
|
||||
|
||||
text = globals.converter._dispatch('hashCodeTags.after', text, options, globals);
|
||||
return text;
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||
*/
|
||||
showdown.subParser('horizontalRule', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('horizontalRule.before', text, options, globals);
|
||||
|
||||
var key = showdown.subParser('hashBlock')('<hr />', options, globals);
|
||||
text = text.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm, key);
|
||||
text = text.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm, key);
|
||||
text = text.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm, key);
|
||||
|
||||
text = globals.converter._dispatch('horizontalRule.after', text, options, globals);
|
||||
return text;
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
showdown.subParser('makeMarkdown.links', function (node, globals) {
|
||||
'use strict';
|
||||
|
||||
var txt = '';
|
||||
if (node.hasChildNodes() && node.hasAttribute('href')) {
|
||||
var children = node.childNodes,
|
||||
childrenLength = children.length;
|
||||
txt = '[';
|
||||
for (var i = 0; i < childrenLength; ++i) {
|
||||
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
|
||||
}
|
||||
txt += '](';
|
||||
txt += '<' + node.getAttribute('href') + '>';
|
||||
if (node.hasAttribute('title')) {
|
||||
txt += ' "' + node.getAttribute('title') + '"';
|
||||
}
|
||||
txt += ')';
|
||||
}
|
||||
return txt;
|
||||
});
|
|
@ -1,70 +0,0 @@
|
|||
showdown.subParser('makeMarkdown.table', function (node, globals) {
|
||||
'use strict';
|
||||
|
||||
var txt = '',
|
||||
tableArray = [[], []],
|
||||
headings = node.querySelectorAll('thead>tr>th'),
|
||||
rows = node.querySelectorAll('tbody>tr'),
|
||||
i, ii;
|
||||
for (i = 0; i < headings.length; ++i) {
|
||||
var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals),
|
||||
allign = '---';
|
||||
|
||||
if (headings[i].hasAttribute('style')) {
|
||||
var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, '');
|
||||
switch (style) {
|
||||
case 'text-align:left;':
|
||||
allign = ':---';
|
||||
break;
|
||||
case 'text-align:right;':
|
||||
allign = '---:';
|
||||
break;
|
||||
case 'text-align:center;':
|
||||
allign = ':---:';
|
||||
break;
|
||||
}
|
||||
}
|
||||
tableArray[0][i] = headContent.trim();
|
||||
tableArray[1][i] = allign;
|
||||
}
|
||||
|
||||
for (i = 0; i < rows.length; ++i) {
|
||||
var r = tableArray.push([]) - 1,
|
||||
cols = rows[i].getElementsByTagName('td');
|
||||
|
||||
for (ii = 0; ii < headings.length; ++ii) {
|
||||
var cellContent = ' ';
|
||||
if (typeof cols[ii] !== 'undefined') {
|
||||
cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals);
|
||||
}
|
||||
tableArray[r].push(cellContent);
|
||||
}
|
||||
}
|
||||
|
||||
var cellSpacesCount = 3;
|
||||
for (i = 0; i < tableArray.length; ++i) {
|
||||
for (ii = 0; ii < tableArray[i].length; ++ii) {
|
||||
var strLen = tableArray[i][ii].length;
|
||||
if (strLen > cellSpacesCount) {
|
||||
cellSpacesCount = strLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tableArray.length; ++i) {
|
||||
for (ii = 0; ii < tableArray[i].length; ++ii) {
|
||||
if (i === 1) {
|
||||
if (tableArray[i][ii].slice(-1) === ':') {
|
||||
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':';
|
||||
} else {
|
||||
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-');
|
||||
}
|
||||
} else {
|
||||
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount);
|
||||
}
|
||||
}
|
||||
txt += '| ' + tableArray[i].join(' | ') + ' |\n';
|
||||
}
|
||||
|
||||
return txt.trim();
|
||||
});
|
32
src/subParsers/makehtml/blockGamut.js
Normal file
32
src/subParsers/makehtml/blockGamut.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* These are all the transformations that form block-level
|
||||
* tags like paragraphs, headers, and list items.
|
||||
*/
|
||||
showdown.subParser('makehtml.blockGamut', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('makehtml.blockGamut.before', text, options, globals).getText();
|
||||
|
||||
// we parse blockquotes first so that we can have headings and hrs
|
||||
// inside blockquotes
|
||||
text = showdown.subParser('makehtml.blockQuotes')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.headers')(text, options, globals);
|
||||
|
||||
// Do Horizontal Rules:
|
||||
text = showdown.subParser('makehtml.horizontalRule')(text, options, globals);
|
||||
|
||||
text = showdown.subParser('makehtml.lists')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.codeBlocks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.tables')(text, options, globals);
|
||||
|
||||
// We already ran _HashHTMLBlocks() before, in Markdown(), but that
|
||||
// was to escape raw HTML in the original Markdown source. This time,
|
||||
// we're escaping the markup we've just created, so that we don't wrap
|
||||
// <p> tags around block-level tags.
|
||||
text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.paragraphs')(text, options, globals);
|
||||
|
||||
text = globals.converter._dispatch('makehtml.blockGamut.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
showdown.subParser('blockQuotes', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.blockQuotes', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.blockQuotes.before', text, options, globals).getText();
|
||||
|
||||
// add a couple extra lines after the text and endtext mark
|
||||
text = text + '\n\n';
|
||||
|
@ -21,8 +21,8 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
|
|||
bq = bq.replace(/¨0/g, '');
|
||||
|
||||
bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
|
||||
bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
|
||||
bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
|
||||
bq = showdown.subParser('makehtml.githubCodeBlocks')(bq, options, globals);
|
||||
bq = showdown.subParser('makehtml.blockGamut')(bq, options, globals); // recurse
|
||||
|
||||
bq = bq.replace(/(^|\n)/g, '$1 ');
|
||||
// These leading spaces screw with <pre> content, so we need to fix that:
|
||||
|
@ -34,9 +34,9 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
|
|||
return pre;
|
||||
});
|
||||
|
||||
return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
|
||||
return showdown.subParser('makehtml.hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.blockQuotes.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* Process Markdown `<pre><code>` blocks.
|
||||
*/
|
||||
showdown.subParser('codeBlocks', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.codeBlocks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.codeBlocks.before', text, options, globals).getText();
|
||||
|
||||
// sentinel workarounds for lack of \A and \Z, safari\khtml bug
|
||||
text += '¨0';
|
||||
|
@ -15,9 +15,9 @@ showdown.subParser('codeBlocks', function (text, options, globals) {
|
|||
nextChar = m2,
|
||||
end = '\n';
|
||||
|
||||
codeblock = showdown.subParser('outdent')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('detab')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.outdent')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals);
|
||||
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
|
||||
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
|
||||
|
||||
|
@ -27,12 +27,12 @@ showdown.subParser('codeBlocks', function (text, options, globals) {
|
|||
|
||||
codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
|
||||
|
||||
return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
|
||||
return showdown.subParser('makehtml.hashBlock')(codeblock, options, globals) + nextChar;
|
||||
});
|
||||
|
||||
// strip sentinel
|
||||
text = text.replace(/¨0/, '');
|
||||
|
||||
text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.codeBlocks.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -23,10 +23,10 @@
|
|||
*
|
||||
* ... type <code>`bar`</code> ...
|
||||
*/
|
||||
showdown.subParser('codeSpans', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.codeSpans', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('codeSpans.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.codeSpans.before', text, options, globals).getText();
|
||||
|
||||
if (typeof (text) === 'undefined') {
|
||||
text = '';
|
||||
|
@ -36,13 +36,13 @@ showdown.subParser('codeSpans', function (text, options, globals) {
|
|||
var c = m3;
|
||||
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
|
||||
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
|
||||
c = showdown.subParser('encodeCode')(c, options, globals);
|
||||
c = showdown.subParser('makehtml.encodeCode')(c, options, globals);
|
||||
c = m1 + '<code>' + c + '</code>';
|
||||
c = showdown.subParser('hashHTMLSpans')(c, options, globals);
|
||||
c = showdown.subParser('makehtml.hashHTMLSpans')(c, options, globals);
|
||||
return c;
|
||||
}
|
||||
);
|
||||
|
||||
text = globals.converter._dispatch('codeSpans.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.codeSpans.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,14 +1,14 @@
|
|||
/**
|
||||
* Create a full HTML document from the processed markdown
|
||||
*/
|
||||
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.completeHTMLDocument', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.completeHTMLDocument) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.completeHTMLDocument.before', text, options, globals).getText();
|
||||
|
||||
var doctype = 'html',
|
||||
doctypeParsed = '<!DOCTYPE HTML>\n',
|
||||
|
@ -57,6 +57,6 @@ showdown.subParser('completeHTMLDocument', function (text, options, globals) {
|
|||
|
||||
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
|
||||
|
||||
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.completeHTMLDocument.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Convert all tabs to spaces
|
||||
*/
|
||||
showdown.subParser('detab', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.detab', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('detab.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.detab.before', text, options, globals).getText();
|
||||
|
||||
// expand first n-1 tabs
|
||||
text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
|
||||
|
@ -28,6 +28,6 @@ showdown.subParser('detab', function (text, options, globals) {
|
|||
text = text.replace(/¨A/g, ' '); // g_tab_width
|
||||
text = text.replace(/¨B/g, '');
|
||||
|
||||
text = globals.converter._dispatch('detab.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.detab.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
15
src/subParsers/makehtml/ellipsis.js
Normal file
15
src/subParsers/makehtml/ellipsis.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
showdown.subParser('makehtml.ellipsis', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.ellipsis) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('makehtml.ellipsis.before', text, options, globals).getText();
|
||||
|
||||
text = text.replace(/\.\.\./g, '…');
|
||||
|
||||
text = globals.converter._dispatch('makehtml.ellipsis.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
|
@ -3,14 +3,14 @@
|
|||
*
|
||||
* List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis
|
||||
*/
|
||||
showdown.subParser('emoji', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.emoji', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.emoji) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('emoji.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.emoji.before', text, options, globals).getText();
|
||||
|
||||
var emojiRgx = /:([\S]+?):/g;
|
||||
|
||||
|
@ -21,7 +21,7 @@ showdown.subParser('emoji', function (text, options, globals) {
|
|||
return wm;
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch('emoji.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.emoji.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Smart processing for ampersands and angle brackets that need to be encoded.
|
||||
*/
|
||||
showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.encodeAmpsAndAngles', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('encodeAmpsAndAngles.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.before', text, options, globals).getText();
|
||||
|
||||
// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
|
||||
// http://bumppo.net/projects/amputator/
|
||||
|
@ -18,6 +18,6 @@ showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
|
|||
// Encode >
|
||||
text = text.replace(/>/g, '>');
|
||||
|
||||
text = globals.converter._dispatch('encodeAmpsAndAngles.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -9,13 +9,13 @@
|
|||
* ...but we're sidestepping its use of the (slow) RegExp constructor
|
||||
* as an optimization for Firefox. This function gets called a LOT.
|
||||
*/
|
||||
showdown.subParser('encodeBackslashEscapes', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.encodeBackslashEscapes', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('encodeBackslashEscapes.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.before', text, options, globals).getText();
|
||||
|
||||
text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
|
||||
text = text.replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
text = globals.converter._dispatch('encodeBackslashEscapes.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -3,10 +3,10 @@
|
|||
* The point is that in code, these characters are literals,
|
||||
* and lose their special Markdown meanings.
|
||||
*/
|
||||
showdown.subParser('encodeCode', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.encodeCode', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('encodeCode.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeCode.before', text, options, globals).getText();
|
||||
|
||||
// Encode all ampersands; HTML entities are not
|
||||
// entities within a Markdown code span.
|
||||
|
@ -18,6 +18,6 @@ showdown.subParser('encodeCode', function (text, options, globals) {
|
|||
// Now, escape characters that are magic in Markdown:
|
||||
.replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
text = globals.converter._dispatch('encodeCode.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.encodeCode.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -2,9 +2,9 @@
|
|||
* Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they
|
||||
* don't conflict with their use in Markdown for code, italics and strong.
|
||||
*/
|
||||
showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.before', text, options, globals).getText();
|
||||
|
||||
// Build a regex to find HTML tags.
|
||||
var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
|
||||
|
@ -21,6 +21,6 @@ showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text, opti
|
|||
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -8,7 +8,7 @@
|
|||
* end
|
||||
* ```
|
||||
*/
|
||||
showdown.subParser('githubCodeBlocks', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.githubCodeBlocks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
// early exit if option is not enabled
|
||||
|
@ -16,22 +16,26 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
|
|||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.githubCodeBlocks.before', text, options, globals).getText();
|
||||
|
||||
text += '¨0';
|
||||
|
||||
text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) {
|
||||
text = text.replace(/(?:^|\n) {0,3}(```+|~~~+) *([^\n\t`~]*)\n([\s\S]*?)\n {0,3}\1/g, function (wholeMatch, delim, language, codeblock) {
|
||||
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
|
||||
|
||||
// if the language has spaces followed by some other chars, according to the spec we should just ignore everything
|
||||
// after the first space
|
||||
language = language.trim().split(' ')[0];
|
||||
|
||||
// First parse the github code block
|
||||
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('detab')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals);
|
||||
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
|
||||
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
|
||||
|
||||
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
|
||||
|
||||
codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
|
||||
codeblock = showdown.subParser('makehtml.hashBlock')(codeblock, options, globals);
|
||||
|
||||
// Since GHCodeblocks can be false positives, we need to
|
||||
// store the primitive text and the parsed text in a global var,
|
||||
|
@ -42,5 +46,5 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
|
|||
// attacklab: strip sentinel
|
||||
text = text.replace(/¨0/, '');
|
||||
|
||||
return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
|
||||
return globals.converter._dispatch('makehtml.githubCodeBlocks.after', text, options, globals).getText();
|
||||
});
|
8
src/subParsers/makehtml/hashBlock.js
Normal file
8
src/subParsers/makehtml/hashBlock.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
showdown.subParser('makehtml.hashBlock', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('makehtml.hashBlock.before', text, options, globals).getText();
|
||||
text = text.replace(/(^\n+|\n+$)/g, '');
|
||||
text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
|
||||
text = globals.converter._dispatch('makehtml.hashBlock.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
18
src/subParsers/makehtml/hashCodeTags.js
Normal file
18
src/subParsers/makehtml/hashCodeTags.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Hash and escape <code> elements that should not be parsed as markdown
|
||||
*/
|
||||
showdown.subParser('makehtml.hashCodeTags', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('makehtml.hashCodeTags.before', text, options, globals).getText();
|
||||
|
||||
var repFunc = function (wholeMatch, match, left, right) {
|
||||
var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right;
|
||||
return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
|
||||
};
|
||||
|
||||
// Hash naked <code>
|
||||
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>]*>', '</code>', 'gim');
|
||||
|
||||
text = globals.converter._dispatch('makehtml.hashCodeTags.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
showdown.subParser('hashElement', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.hashElement', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
return function (wholeMatch, m1) {
|
|
@ -1,6 +1,6 @@
|
|||
showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.hashHTMLBlocks', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('hashHTMLBlocks.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLBlocks.before', text, options, globals).getText();
|
||||
|
||||
var blockTags = [
|
||||
'pre',
|
||||
|
@ -36,6 +36,7 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
|
|||
'hgroup',
|
||||
'output',
|
||||
'video',
|
||||
'details',
|
||||
'p'
|
||||
],
|
||||
repFunc = function (wholeMatch, match, left, right) {
|
||||
|
@ -82,7 +83,7 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
|
|||
}
|
||||
// HR SPECIAL CASE
|
||||
text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
|
||||
showdown.subParser('hashElement')(text, options, globals));
|
||||
showdown.subParser('makehtml.hashElement')(text, options, globals));
|
||||
|
||||
// Special case for standalone HTML comments
|
||||
text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
|
||||
|
@ -90,9 +91,9 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
|
|||
}, '^ {0,3}<!--', '-->', 'gm');
|
||||
|
||||
// PHP and ASP-style processor instructions (<?...?> and <%...%>)
|
||||
text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
|
||||
showdown.subParser('hashElement')(text, options, globals));
|
||||
text = text.replace(/\n\n( {0,3}<([?%])[^\r]*?\2>[ \t]*(?=\n{2,}))/g,
|
||||
showdown.subParser('makehtml.hashElement')(text, options, globals));
|
||||
|
||||
text = globals.converter._dispatch('hashHTMLBlocks.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLBlocks.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,46 +1,40 @@
|
|||
/**
|
||||
* Hash span elements that should not be parsed as markdown
|
||||
*/
|
||||
showdown.subParser('hashHTMLSpans', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.hashHTMLSpans', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('hashHTMLSpans.before', text, options, globals);
|
||||
|
||||
function hashHTMLSpan (html) {
|
||||
return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
|
||||
}
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLSpans.before', text, options, globals).getText();
|
||||
|
||||
// Hash Self Closing tags
|
||||
text = text.replace(/<[^>]+?\/>/gi, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash tags without properties
|
||||
text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash tags with properties
|
||||
text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash self closing tags without />
|
||||
text = text.replace(/<[^>]+?>/gi, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
/*showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');*/
|
||||
|
||||
text = globals.converter._dispatch('hashHTMLSpans.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLSpans.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* Unhash HTML spans
|
||||
*/
|
||||
showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.unhashHTMLSpans', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.unhashHTMLSpans.before', text, options, globals).getText();
|
||||
|
||||
for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
|
||||
var repText = globals.gHtmlSpans[i],
|
||||
|
@ -59,6 +53,6 @@ showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
|
|||
text = text.replace('¨C' + i + 'C', repText);
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.unhashHTMLSpans.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,19 +1,19 @@
|
|||
/**
|
||||
* Hash and escape <pre><code> elements that should not be parsed as markdown
|
||||
*/
|
||||
showdown.subParser('hashPreCodeTags', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.hashPreCodeTags', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('hashPreCodeTags.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.hashPreCodeTags.before', text, options, globals).getText();
|
||||
|
||||
var repFunc = function (wholeMatch, match, left, right) {
|
||||
// encode html entities
|
||||
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
|
||||
var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right;
|
||||
return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
|
||||
};
|
||||
|
||||
// Hash <pre><code>
|
||||
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim');
|
||||
|
||||
text = globals.converter._dispatch('hashPreCodeTags.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.hashPreCodeTags.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
showdown.subParser('headers', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.headers', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('headers.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.headers.before', text, options, globals).getText();
|
||||
|
||||
var headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
|
||||
|
||||
|
@ -17,19 +17,19 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
|
||||
text = text.replace(setextRegexH1, function (wholeMatch, m1) {
|
||||
|
||||
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
|
||||
var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
|
||||
hLevel = headerLevelStart,
|
||||
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
|
||||
return showdown.subParser('hashBlock')(hashBlock, options, globals);
|
||||
return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals);
|
||||
});
|
||||
|
||||
text = text.replace(setextRegexH2, function (matchFound, m1) {
|
||||
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
|
||||
var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
|
||||
hLevel = headerLevelStart + 1,
|
||||
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
|
||||
return showdown.subParser('hashBlock')(hashBlock, options, globals);
|
||||
return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals);
|
||||
});
|
||||
|
||||
// atx-style headers:
|
||||
|
@ -44,15 +44,15 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
|
||||
var hText = m2;
|
||||
if (options.customizedHeaderId) {
|
||||
hText = m2.replace(/\s?\{([^{]+?)}\s*$/, '');
|
||||
hText = m2.replace(/\s?{([^{]+?)}\s*$/, '');
|
||||
}
|
||||
|
||||
var span = showdown.subParser('spanGamut')(hText, options, globals),
|
||||
var span = showdown.subParser('makehtml.spanGamut')(hText, options, globals),
|
||||
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
|
||||
hLevel = headerLevelStart - 1 + m1.length,
|
||||
header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
|
||||
|
||||
return showdown.subParser('hashBlock')(header, options, globals);
|
||||
return showdown.subParser('makehtml.hashBlock')(header, options, globals);
|
||||
});
|
||||
|
||||
function headerId (m) {
|
||||
|
@ -61,7 +61,7 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
|
||||
// It is separate from other options to allow combining prefix and customized
|
||||
if (options.customizedHeaderId) {
|
||||
var match = m.match(/\{([^{]+?)}\s*$/);
|
||||
var match = m.match(/{([^{]+?)}\s*$/);
|
||||
if (match && match[1]) {
|
||||
m = match[1];
|
||||
}
|
||||
|
@ -121,6 +121,6 @@ showdown.subParser('headers', function (text, options, globals) {
|
|||
return title;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('headers.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.headers.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
18
src/subParsers/makehtml/horizontalRule.js
Normal file
18
src/subParsers/makehtml/horizontalRule.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Turn Markdown horizontal rule shortcuts into <hr /> tags.
|
||||
*
|
||||
* Any 3 or more unindented consecutive hyphens, asterisks or underscores with or without a space beetween them
|
||||
* in a single line is considered a horizontal rule
|
||||
*/
|
||||
showdown.subParser('makehtml.horizontalRule', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('makehtml.horizontalRule.before', text, options, globals).getText();
|
||||
|
||||
var key = showdown.subParser('makehtml.hashBlock')('<hr />', options, globals);
|
||||
text = text.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm, key);
|
||||
text = text.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm, key);
|
||||
text = text.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm, key);
|
||||
|
||||
text = globals.converter._dispatch('makehtml.horizontalRule.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* Turn Markdown image shortcuts into <img> tags.
|
||||
*/
|
||||
showdown.subParser('images', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.images', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('images.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.images.before', text, options, globals).getText();
|
||||
|
||||
var inlineRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
|
||||
crazyRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
|
||||
|
@ -17,6 +17,12 @@ showdown.subParser('images', function (text, options, globals) {
|
|||
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) {
|
||||
|
||||
var gUrls = globals.gUrls,
|
||||
|
@ -56,16 +62,16 @@ showdown.subParser('images', function (text, options, globals) {
|
|||
altText = altText
|
||||
.replace(/"/g, '"')
|
||||
//altText = showdown.helper.escapeCharacters(altText, '*_', false);
|
||||
.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
//url = showdown.helper.escapeCharacters(url, '*_', false);
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
var result = '<img src="' + url + '" alt="' + altText + '"';
|
||||
|
||||
if (title && showdown.helper.isString(title)) {
|
||||
title = title
|
||||
.replace(/"/g, '"')
|
||||
//title = showdown.helper.escapeCharacters(title, '*_', false);
|
||||
.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
result += ' title="' + title + '"';
|
||||
}
|
||||
|
||||
|
@ -91,14 +97,14 @@ showdown.subParser('images', function (text, options, globals) {
|
|||
text = text.replace(base64RegExp, writeImageTagBase64);
|
||||
|
||||
// cases with crazy urls like ./image/cat1).png
|
||||
text = text.replace(crazyRegExp, writeImageTag);
|
||||
text = text.replace(crazyRegExp, writeImageTagBaseUrl);
|
||||
|
||||
// normal cases
|
||||
text = text.replace(inlineRegExp, writeImageTag);
|
||||
text = text.replace(inlineRegExp, writeImageTagBaseUrl);
|
||||
|
||||
// handle reference-style shortcuts: ![img text]
|
||||
text = text.replace(refShortcutRegExp, writeImageTag);
|
||||
|
||||
text = globals.converter._dispatch('images.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.images.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,18 +1,13 @@
|
|||
showdown.subParser('italicsAndBold', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.italicsAndBold', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.italicsAndBold.before', text, options, globals).getText();
|
||||
|
||||
// it's faster to have 3 separate regexes for each case than have just one
|
||||
// because of backtracing, in some cases, it could lead to an exponential effect
|
||||
// called "catastrophic backtrace". Ominous!
|
||||
|
||||
function parseInside (txt, left, right) {
|
||||
/*
|
||||
if (options.simplifiedAutoLink) {
|
||||
txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
|
||||
}
|
||||
*/
|
||||
return left + txt + right;
|
||||
}
|
||||
|
||||
|
@ -41,30 +36,31 @@ showdown.subParser('italicsAndBold', function (text, options, globals) {
|
|||
}
|
||||
|
||||
// Now parse asterisks
|
||||
/*
|
||||
if (options.literalMidWordAsterisks) {
|
||||
text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]+?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
return parseInside (txt, lead + '<strong><em>', '</em></strong>');
|
||||
});
|
||||
text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]+?)\*\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
return parseInside (txt, lead + '<strong>', '</strong>');
|
||||
});
|
||||
text = text.replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
text = text.replace(/([^*]|^)\B\*(\S[\s\S]+?)\*\B(?!\*)/g, function (wm, lead, txt) {
|
||||
return parseInside (txt, lead + '<em>', '</em>');
|
||||
});
|
||||
} else {
|
||||
text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
|
||||
});
|
||||
text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
|
||||
});
|
||||
text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
|
||||
// !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
|
||||
});
|
||||
}
|
||||
*/
|
||||
text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
|
||||
});
|
||||
text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
|
||||
});
|
||||
text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
|
||||
// !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
|
||||
return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
|
||||
});
|
||||
//}
|
||||
|
||||
|
||||
text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.italicsAndBold.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
428
src/subParsers/makehtml/links.js
Normal file
428
src/subParsers/makehtml/links.js
Normal file
|
@ -0,0 +1,428 @@
|
|||
////
|
||||
// makehtml/links.js
|
||||
// Copyright (c) 2018 ShowdownJS
|
||||
//
|
||||
// Transforms MD links into `<a>` html anchors
|
||||
//
|
||||
// A link contains link text (the visible text), a link destination (the URI that is the link destination), and
|
||||
// optionally a link title. There are two basic kinds of links in Markdown.
|
||||
// In inline links the destination and title are given immediately after the link text.
|
||||
// In reference links the destination and title are defined elsewhere in the document.
|
||||
//
|
||||
// ***Author:***
|
||||
// - Estevão Soares dos Santos (Tivie) <https://github.com/tivie>
|
||||
////
|
||||
|
||||
(function () {
|
||||
/**
|
||||
* Helper function: Wrapper function to pass as second replace parameter
|
||||
*
|
||||
* @param {RegExp} rgx
|
||||
* @param {string} evtRootName
|
||||
* @param {{}} options
|
||||
* @param {{}} globals
|
||||
* @returns {Function}
|
||||
*/
|
||||
function replaceAnchorTagReference (rgx, evtRootName, options, globals, emptyCase) {
|
||||
emptyCase = !!emptyCase;
|
||||
return function (wholeMatch, text, id, url, m5, m6, title) {
|
||||
// bail we we find 2 newlines somewhere
|
||||
if (/\n\n/.test(wholeMatch)) {
|
||||
return wholeMatch;
|
||||
}
|
||||
|
||||
var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
|
||||
return writeAnchorTag(evt, options, globals, emptyCase);
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
* Helper function: Create a capture event
|
||||
* @param {RegExp} rgx
|
||||
* @param {String} evtName Event name
|
||||
* @param {String} wholeMatch
|
||||
* @param {String} text
|
||||
* @param {String} id
|
||||
* @param {String} url
|
||||
* @param {String} title
|
||||
* @param {{}} options
|
||||
* @param {{}} globals
|
||||
* @returns {showdown.helper.Event|*}
|
||||
*/
|
||||
function createEvent (rgx, evtName, wholeMatch, text, id, url, title, options, globals) {
|
||||
return globals.converter._dispatch(evtName, wholeMatch, options, globals, {
|
||||
regexp: rgx,
|
||||
matches: {
|
||||
wholeMatch: wholeMatch,
|
||||
text: text,
|
||||
id: id,
|
||||
url: url,
|
||||
title: title
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper Function: Normalize and write an anchor tag based on passed parameters
|
||||
* @param evt
|
||||
* @param options
|
||||
* @param globals
|
||||
* @param {boolean} emptyCase
|
||||
* @returns {string}
|
||||
*/
|
||||
function writeAnchorTag (evt, options, globals, emptyCase) {
|
||||
|
||||
var wholeMatch = evt.getMatches().wholeMatch;
|
||||
var text = evt.getMatches().text;
|
||||
var id = evt.getMatches().id;
|
||||
var url = evt.getMatches().url;
|
||||
var title = evt.getMatches().title;
|
||||
var target = '';
|
||||
|
||||
if (!title) {
|
||||
title = '';
|
||||
}
|
||||
id = (id) ? id.toLowerCase() : '';
|
||||
|
||||
if (emptyCase) {
|
||||
url = '';
|
||||
} else if (!url) {
|
||||
if (!id) {
|
||||
// lower-case and turn embedded newlines into spaces
|
||||
id = text.toLowerCase().replace(/ ?\n/g, ' ');
|
||||
}
|
||||
url = '#' + id;
|
||||
|
||||
if (!showdown.helper.isUndefined(globals.gUrls[id])) {
|
||||
url = globals.gUrls[id];
|
||||
if (!showdown.helper.isUndefined(globals.gTitles[id])) {
|
||||
title = globals.gTitles[id];
|
||||
}
|
||||
} else {
|
||||
return wholeMatch;
|
||||
}
|
||||
}
|
||||
//url = showdown.helper.escapeCharacters(url, '*_:~', false); // replaced line to improve performance
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
if (title !== '' && title !== null) {
|
||||
title = title.replace(/"/g, '"');
|
||||
//title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
|
||||
title = title.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
title = ' title="' + title + '"';
|
||||
}
|
||||
|
||||
// optionLinksInNewWindow only applies
|
||||
// to external links. Hash links (#) open in same page
|
||||
if (options.openLinksInNewWindow && !/^#/.test(url)) {
|
||||
// escaped _
|
||||
target = ' rel="noopener noreferrer" target="¨E95Eblank"';
|
||||
}
|
||||
|
||||
// Text can be a markdown element, so we run through the appropriate parsers
|
||||
text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.emoji')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.underline')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
|
||||
|
||||
//evt = createEvent(rgx, evtRootName + '.captureEnd', wholeMatch, text, id, url, title, options, globals);
|
||||
|
||||
var result = '<a href="' + url + '"' + title + target + '>' + text + '</a>';
|
||||
|
||||
//evt = createEvent(rgx, evtRootName + '.beforeHash', wholeMatch, text, id, url, title, options, globals);
|
||||
|
||||
result = showdown.subParser('makehtml.hashHTMLSpans')(result, options, globals);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var evtRootName = 'makehtml.links';
|
||||
|
||||
/**
|
||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||
*/
|
||||
showdown.subParser('makehtml.links', function (text, options, globals) {
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
// 1. Handle reference-style links: [link text] [id]
|
||||
text = showdown.subParser('makehtml.links.reference')(text, options, globals);
|
||||
|
||||
// 2. Handle inline-style links: [link text](url "optional title")
|
||||
text = showdown.subParser('makehtml.links.inline')(text, options, globals);
|
||||
|
||||
// 3. Handle reference-style shortcuts: [link text]
|
||||
// These must come last in case there's a [link text][1] or [link text](/foo)
|
||||
text = showdown.subParser('makehtml.links.referenceShortcut')(text, options, globals);
|
||||
|
||||
// 4. Handle angle brackets links -> `<http://example.com/>`
|
||||
// Must come after links, because you can use < and > delimiters in inline links like [this](<url>).
|
||||
text = showdown.subParser('makehtml.links.angleBrackets')(text, options, globals);
|
||||
|
||||
// 5. Handle GithubMentions (if option is enabled)
|
||||
text = showdown.subParser('makehtml.links.ghMentions')(text, options, globals);
|
||||
|
||||
// 6. Handle <a> tags and img tags
|
||||
text = text.replace(/<a\s[^>]*>[\s\S]*<\/a>/g, function (wholeMatch) {
|
||||
return showdown.helper._hashHTMLSpan(wholeMatch, globals);
|
||||
});
|
||||
|
||||
text = text.replace(/<img\s[^>]*\/?>/g, function (wholeMatch) {
|
||||
return showdown.helper._hashHTMLSpan(wholeMatch, globals);
|
||||
});
|
||||
|
||||
// 7. Handle naked links (if option is enabled)
|
||||
text = showdown.subParser('makehtml.links.naked')(text, options, globals);
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.inline', function (text, options, globals) {
|
||||
var evtRootName = evtRootName + '.inline';
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
// 1. Look for empty cases: []() and [empty]() and []("title")
|
||||
var rgxEmpty = /\[(.*?)]()()()()\(<? ?>? ?(?:["'](.*)["'])?\)/g;
|
||||
text = text.replace(rgxEmpty, replaceAnchorTagBaseUrl(rgxEmpty, evtRootName, options, globals, true));
|
||||
|
||||
// 2. Look for cases with crazy urls like ./image/cat1).png
|
||||
var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
|
||||
text = text.replace(rgxCrazy, replaceAnchorTagBaseUrl(rgxCrazy, evtRootName, options, globals));
|
||||
|
||||
// 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")
|
||||
//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;
|
||||
text = text.replace(rgx2, replaceAnchorTagBaseUrl(rgx2, evtRootName, options, globals));
|
||||
|
||||
// 4. inline links with titles wrapped in (): [foo](bar.com (title))
|
||||
var rgx3 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s+()()\((.*?)\) *\)/g;
|
||||
text = text.replace(rgx3, replaceAnchorTagBaseUrl(rgx3, evtRootName, options, globals));
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.reference', function (text, options, globals) {
|
||||
var evtRootName = evtRootName + '.reference';
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
|
||||
text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.referenceShortcut', function (text, options, globals) {
|
||||
var evtRootName = evtRootName + '.referenceShortcut';
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
var rgx = /\[([^\[\]]+)]()()()()()/g;
|
||||
text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.ghMentions', function (text, options, globals) {
|
||||
var evtRootName = evtRootName + 'ghMentions';
|
||||
|
||||
if (!options.ghMentions) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
var rgx = /(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gi;
|
||||
|
||||
text = text.replace(rgx, function (wholeMatch, st, escape, mentions, username) {
|
||||
// bail if the mentions was escaped
|
||||
if (escape === '\\') {
|
||||
return st + mentions;
|
||||
}
|
||||
|
||||
// check if options.ghMentionsLink is a string
|
||||
// TODO Validation should be done at initialization not at runtime
|
||||
if (!showdown.helper.isString(options.ghMentionsLink)) {
|
||||
throw new Error('ghMentionsLink option must be a string');
|
||||
}
|
||||
var url = options.ghMentionsLink.replace(/{u}/g, username);
|
||||
var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, mentions, null, url, null, options, globals);
|
||||
// captureEnd Event is triggered inside writeAnchorTag function
|
||||
return st + writeAnchorTag(evt, options, globals);
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.angleBrackets', function (text, options, globals) {
|
||||
var evtRootName = 'makehtml.links.angleBrackets';
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
// 1. Parse links first
|
||||
var urlRgx = /<(((?:https?|ftp):\/\/|www\.)[^'">\s]+)>/gi;
|
||||
text = text.replace(urlRgx, function (wholeMatch, url, urlStart) {
|
||||
var text = url;
|
||||
url = (urlStart === 'www.') ? 'http://' + url : url;
|
||||
var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
|
||||
return writeAnchorTag(evt, options, globals);
|
||||
});
|
||||
|
||||
// 2. Then Mail Addresses
|
||||
var mailRgx = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
|
||||
text = text.replace(mailRgx, function (wholeMatch, mail) {
|
||||
var url = 'mailto:';
|
||||
mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
|
||||
if (options.encodeEmails) {
|
||||
url = showdown.helper.encodeEmailAddress(url + mail);
|
||||
mail = showdown.helper.encodeEmailAddress(mail);
|
||||
} else {
|
||||
url = url + mail;
|
||||
}
|
||||
var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
|
||||
return writeAnchorTag(evt, options, globals);
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO MAKE THIS WORK (IT'S NOT ACTIVATED)
|
||||
* TODO WRITE THIS DOCUMENTATION
|
||||
*/
|
||||
showdown.subParser('makehtml.links.naked', function (text, options, globals) {
|
||||
if (!options.simplifiedAutoLink) {
|
||||
return text;
|
||||
}
|
||||
|
||||
var evtRootName = 'makehtml.links.naked';
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
|
||||
|
||||
// 2. Now we check for
|
||||
// we also include leading markdown magic chars [_*~] for cases like __https://www.google.com/foobar__
|
||||
var urlRgx = /([_*~]*?)(((?:https?|ftp):\/\/|www\.)[^\s<>"'`´.-][^\s<>"'`´]*?\.[a-z\d.]+[^\s<>"']*)\1/gi;
|
||||
text = text.replace(urlRgx, function (wholeMatch, leadingMDChars, url, urlPrefix) {
|
||||
|
||||
// we now will start traversing the url from the front to back, looking for punctuation chars [_*~,;:.!?\)\]]
|
||||
var len = url.length;
|
||||
var suffix = '';
|
||||
for (var i = len - 1; i >= 0; --i) {
|
||||
var char = url.charAt(i);
|
||||
|
||||
if (/[_*~,;:.!?]/.test(char)) {
|
||||
// it's a punctuation char
|
||||
// we remove it from the url
|
||||
url = url.slice(0, -1);
|
||||
// and prepend it to the suffix
|
||||
suffix = char + suffix;
|
||||
} else if (/\)/.test(char)) {
|
||||
var opPar = url.match(/\(/g) || [];
|
||||
var clPar = url.match(/\)/g);
|
||||
|
||||
// it's a curved parenthesis so we need to check for "balance" (kinda)
|
||||
if (opPar.length < clPar.length) {
|
||||
// there are more closing Parenthesis than opening so chop it!!!!!
|
||||
url = url.slice(0, -1);
|
||||
// and prepend it to the suffix
|
||||
suffix = char + suffix;
|
||||
} else {
|
||||
// it's (kinda) balanced so our work is done
|
||||
break;
|
||||
}
|
||||
} else if (/]/.test(char)) {
|
||||
var opPar2 = url.match(/\[/g) || [];
|
||||
var clPar2 = url.match(/\]/g);
|
||||
// it's a squared parenthesis so we need to check for "balance" (kinda)
|
||||
if (opPar2.length < clPar2.length) {
|
||||
// there are more closing Parenthesis than opening so chop it!!!!!
|
||||
url = url.slice(0, -1);
|
||||
// and prepend it to the suffix
|
||||
suffix = char + suffix;
|
||||
} else {
|
||||
// it's (kinda) balanced so our work is done
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// it's not a punctuation or a parenthesis so our work is done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we copy the treated url to the text variable
|
||||
var text = url;
|
||||
// finally, if it's a www shortcut, we prepend http
|
||||
url = (urlPrefix === 'www.') ? 'http://' + url : url;
|
||||
|
||||
// url part is done so let's take care of text now
|
||||
// we need to escape the text (because of links such as www.example.com/foo__bar__baz)
|
||||
text = text.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
// finally we dispatch the event
|
||||
var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
|
||||
|
||||
// and return the link tag, with the leadingMDChars and suffix. The leadingMDChars are added at the end too because
|
||||
// we consumed those characters in the regexp
|
||||
return leadingMDChars + writeAnchorTag(evt, options, globals) + suffix + leadingMDChars;
|
||||
});
|
||||
|
||||
// 2. Then mails
|
||||
var mailRgx = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi;
|
||||
text = text.replace(mailRgx, function (wholeMatch, leadingChar, mail) {
|
||||
var url = 'mailto:';
|
||||
mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
|
||||
if (options.encodeEmails) {
|
||||
url = showdown.helper.encodeEmailAddress(url + mail);
|
||||
mail = showdown.helper.encodeEmailAddress(mail);
|
||||
} else {
|
||||
url = url + mail;
|
||||
}
|
||||
var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
|
||||
return leadingChar + writeAnchorTag(evt, options, globals);
|
||||
});
|
||||
|
||||
|
||||
text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
})();
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Form HTML ordered (numbered) and unordered (bulleted) lists.
|
||||
*/
|
||||
showdown.subParser('lists', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.lists', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
|
@ -40,26 +40,32 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
// attacklab: add sentinel to emulate \z
|
||||
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]+((\[([xX ])])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
|
||||
isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
|
||||
|
||||
// Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
|
||||
// which is a syntax breaking change
|
||||
// activating this option reverts to old behavior
|
||||
// This will be removed in version 2.0
|
||||
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]+((\[([xX ])])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
|
||||
}
|
||||
|
||||
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
|
||||
checked = (checked && checked.trim() !== '');
|
||||
|
||||
var item = showdown.subParser('outdent')(m4, options, globals),
|
||||
var item = showdown.subParser('makehtml.outdent')(m4, options, globals),
|
||||
bulletStyle = '';
|
||||
|
||||
// Support for github tasklists
|
||||
if (taskbtn && options.tasklists) {
|
||||
bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
|
||||
item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
|
||||
|
||||
// Style used for tasklist bullets
|
||||
bulletStyle = ' class="task-list-item';
|
||||
if (options.moreStyling) {bulletStyle += checked ? ' task-list-item-complete' : '';}
|
||||
bulletStyle += '" style="list-style-type: none;"';
|
||||
|
||||
item = item.replace(/^[ \t]*\[([xX ])?]/m, function () {
|
||||
var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
|
||||
if (checked) {
|
||||
otp += ' checked';
|
||||
|
@ -81,24 +87,77 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
return '¨A' + wm2;
|
||||
});
|
||||
|
||||
// SPECIAL CASE: a heading followed by a paragraph of text that is not separated by a double newline
|
||||
// or/nor indented. ex:
|
||||
//
|
||||
// - # foo
|
||||
// bar is great
|
||||
//
|
||||
// While this does now follow the spec per se, not allowing for this might cause confusion since
|
||||
// header blocks don't need double-newlines after
|
||||
if (/^#+.+\n.+/.test(item)) {
|
||||
item = item.replace(/^(#+.+)$/m, '$1\n');
|
||||
}
|
||||
|
||||
// m1 - Leading line or
|
||||
// Has a double return (multi paragraph) or
|
||||
// Has sublist
|
||||
// Has a double return (multi paragraph)
|
||||
if (m1 || (item.search(/\n{2,}/) > -1)) {
|
||||
item = showdown.subParser('githubCodeBlocks')(item, options, globals);
|
||||
item = showdown.subParser('blockGamut')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.githubCodeBlocks')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.blockQuotes')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.headers')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.lists')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.codeBlocks')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.tables')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.hashHTMLBlocks')(item, options, globals);
|
||||
//item = showdown.subParser('makehtml.paragraphs')(item, options, globals);
|
||||
|
||||
// TODO: This is a copy of the paragraph parser
|
||||
// This is a provisory fix for issue #494
|
||||
// For a permanente fix we need to rewrite the paragraph parser, passing the unhashify logic outside
|
||||
// so that we can call the paragraph parser without accidently unashifying previously parsed blocks
|
||||
|
||||
// Strip leading and trailing lines:
|
||||
item = item.replace(/^\n+/g, '');
|
||||
item = item.replace(/\n+$/g, '');
|
||||
|
||||
var grafs = item.split(/\n{2,}/g),
|
||||
grafsOut = [],
|
||||
end = grafs.length; // Wrap <p> tags
|
||||
|
||||
for (var i = 0; i < end; i++) {
|
||||
var str = grafs[i];
|
||||
// if this is an HTML marker, copy it
|
||||
if (str.search(/¨([KG])(\d+)\1/g) >= 0) {
|
||||
grafsOut.push(str);
|
||||
|
||||
// test for presence of characters to prevent empty lines being parsed
|
||||
// as paragraphs (resulting in undesired extra empty paragraphs)
|
||||
} else if (str.search(/\S/) >= 0) {
|
||||
str = showdown.subParser('makehtml.spanGamut')(str, options, globals);
|
||||
str = str.replace(/^([ \t]*)/g, '<p>');
|
||||
str += '</p>';
|
||||
grafsOut.push(str);
|
||||
}
|
||||
}
|
||||
item = grafsOut.join('\n');
|
||||
// Strip leading and trailing lines:
|
||||
item = item.replace(/^\n+/g, '');
|
||||
item = item.replace(/\n+$/g, '');
|
||||
|
||||
} else {
|
||||
|
||||
// Recursion for sub-lists:
|
||||
item = showdown.subParser('lists')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.lists')(item, options, globals);
|
||||
item = item.replace(/\n$/, ''); // chomp(item)
|
||||
item = showdown.subParser('hashHTMLBlocks')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.hashHTMLBlocks')(item, options, globals);
|
||||
|
||||
// Colapse double linebreaks
|
||||
item = item.replace(/\n\n+/g, '\n\n');
|
||||
|
||||
if (isParagraphed) {
|
||||
item = showdown.subParser('paragraphs')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.paragraphs')(item, options, globals);
|
||||
} else {
|
||||
item = showdown.subParser('spanGamut')(item, options, globals);
|
||||
item = showdown.subParser('makehtml.spanGamut')(item, options, globals);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,30 +233,29 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** Start of list parsing **/
|
||||
text = globals.converter._dispatch('lists.before', text, options, globals);
|
||||
// Start of list parsing
|
||||
var subListRgx = /^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
||||
var mainListRgx = /(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
||||
|
||||
text = globals.converter._dispatch('lists.before', text, options, globals).getText();
|
||||
// add sentinel to hack around khtml/safari bug:
|
||||
// http://bugs.webkit.org/show_bug.cgi?id=11231
|
||||
text += '¨0';
|
||||
|
||||
if (globals.gListLevel) {
|
||||
text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
|
||||
function (wholeMatch, list, m2) {
|
||||
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
return parseConsecutiveLists(list, listType, true);
|
||||
}
|
||||
);
|
||||
text = text.replace(subListRgx, function (wholeMatch, list, m2) {
|
||||
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
return parseConsecutiveLists(list, listType, true);
|
||||
});
|
||||
} else {
|
||||
text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
|
||||
function (wholeMatch, m1, list, m3) {
|
||||
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
return parseConsecutiveLists(list, listType, false);
|
||||
}
|
||||
);
|
||||
text = text.replace(mainListRgx, function (wholeMatch, m1, list, m3) {
|
||||
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
return parseConsecutiveLists(list, listType, false);
|
||||
});
|
||||
}
|
||||
|
||||
// strip sentinel
|
||||
text = text.replace(/¨0/, '');
|
||||
text = globals.converter._dispatch('lists.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.lists.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,14 +1,14 @@
|
|||
/**
|
||||
* Parse metadata at the top of the document
|
||||
*/
|
||||
showdown.subParser('metadata', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.metadata', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.metadata) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('metadata.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.metadata.before', text, options, globals).getText();
|
||||
|
||||
function parseMetadataContents (content) {
|
||||
// raw is raw so it's not changed in any way
|
||||
|
@ -22,6 +22,11 @@ showdown.subParser('metadata', function (text, options, globals) {
|
|||
// double quotes
|
||||
.replace(/"/g, '"');
|
||||
|
||||
// Restore dollar signs and tremas
|
||||
content = content
|
||||
.replace(/¨D/g, '$$')
|
||||
.replace(/¨T/g, '¨');
|
||||
|
||||
content = content.replace(/\n {4}/g, ' ');
|
||||
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
|
||||
globals.metadata.parsed[key] = value;
|
||||
|
@ -29,12 +34,12 @@ showdown.subParser('metadata', function (text, options, globals) {
|
|||
});
|
||||
}
|
||||
|
||||
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
|
||||
text = text.replace(/^\s*«««+\s*(\S*?)\n([\s\S]+?)\n»»»+\s*\n/, function (wholematch, format, content) {
|
||||
parseMetadataContents(content);
|
||||
return '¨M';
|
||||
});
|
||||
|
||||
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
|
||||
text = text.replace(/^\s*---+\s*(\S*?)\n([\s\S]+?)\n---+\s*\n/, function (wholematch, format, content) {
|
||||
if (format) {
|
||||
globals.metadata.format = format;
|
||||
}
|
||||
|
@ -44,6 +49,6 @@ showdown.subParser('metadata', function (text, options, globals) {
|
|||
|
||||
text = text.replace(/¨M/g, '');
|
||||
|
||||
text = globals.converter._dispatch('metadata.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.metadata.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Remove one level of line-leading tabs or spaces
|
||||
*/
|
||||
showdown.subParser('outdent', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.outdent', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('outdent.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.outdent.before', text, options, globals).getText();
|
||||
|
||||
// attacklab: hack around Konqueror 3.5.4 bug:
|
||||
// "----------bug".replace(/^-/g,"") == "bug"
|
||||
|
@ -12,6 +12,6 @@ showdown.subParser('outdent', function (text, options, globals) {
|
|||
// attacklab: clean up hack
|
||||
text = text.replace(/¨0/g, '');
|
||||
|
||||
text = globals.converter._dispatch('outdent.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.outdent.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
showdown.subParser('paragraphs', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.paragraphs', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('paragraphs.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.paragraphs.before', text, options, globals).getText();
|
||||
// Strip leading and trailing lines:
|
||||
text = text.replace(/^\n+/g, '');
|
||||
text = text.replace(/\n+$/g, '');
|
||||
|
@ -22,7 +22,7 @@ showdown.subParser('paragraphs', function (text, options, globals) {
|
|||
// test for presence of characters to prevent empty lines being parsed
|
||||
// as paragraphs (resulting in undesired extra empty paragraphs)
|
||||
} else if (str.search(/\S/) >= 0) {
|
||||
str = showdown.subParser('spanGamut')(str, options, globals);
|
||||
str = showdown.subParser('makehtml.spanGamut')(str, options, globals);
|
||||
str = str.replace(/^([ \t]*)/g, '<p>');
|
||||
str += '</p>';
|
||||
grafsOut.push(str);
|
||||
|
@ -47,7 +47,7 @@ showdown.subParser('paragraphs', function (text, options, globals) {
|
|||
// we need to check if ghBlock is a false positive
|
||||
if (codeFlag) {
|
||||
// use encoded version of all text
|
||||
blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text, options, globals);
|
||||
blockText = showdown.subParser('makehtml.encodeCode')(globals.ghCodeBlocks[num].text, options, globals);
|
||||
} else {
|
||||
blockText = globals.ghCodeBlocks[num].codeblock;
|
||||
}
|
||||
|
@ -66,5 +66,5 @@ showdown.subParser('paragraphs', function (text, options, globals) {
|
|||
// Strip leading and trailing lines:
|
||||
text = text.replace(/^\n+/g, '');
|
||||
text = text.replace(/\n+$/g, '');
|
||||
return globals.converter._dispatch('paragraphs.after', text, options, globals);
|
||||
return globals.converter._dispatch('makehtml.paragraphs.after', text, options, globals).getText();
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Run extension
|
||||
*/
|
||||
showdown.subParser('runExtension', function (ext, text, options, globals) {
|
||||
showdown.subParser('makehtml.runExtension', function (ext, text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (ext.filter) {
|
50
src/subParsers/makehtml/spanGamut.js
Normal file
50
src/subParsers/makehtml/spanGamut.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* These are all the transformations that occur *within* block-level
|
||||
* tags like paragraphs, headers, and list items.
|
||||
*/
|
||||
showdown.subParser('makehtml.spanGamut', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('makehtml.span.before', text, options, globals).getText();
|
||||
|
||||
text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.encodeBackslashEscapes')(text, options, globals);
|
||||
|
||||
// Process link and image tags. Images must come first,
|
||||
// because ![foo][f] looks like a link.
|
||||
text = showdown.subParser('makehtml.images')(text, options, globals);
|
||||
|
||||
text = globals.converter._dispatch('smakehtml.links.before', text, options, globals).getText();
|
||||
text = showdown.subParser('makehtml.links')(text, options, globals);
|
||||
text = globals.converter._dispatch('smakehtml.links.after', text, options, globals).getText();
|
||||
|
||||
//text = showdown.subParser('makehtml.autoLinks')(text, options, globals);
|
||||
//text = showdown.subParser('makehtml.simplifiedAutoLinks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.emoji')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.underline')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
|
||||
|
||||
// we need to hash HTML tags inside spans
|
||||
text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
|
||||
|
||||
// now we encode amps and angles
|
||||
text = showdown.subParser('makehtml.encodeAmpsAndAngles')(text, options, globals);
|
||||
|
||||
// Do hard breaks
|
||||
if (options.simpleLineBreaks) {
|
||||
// GFM style hard breaks
|
||||
// only add line breaks if the text does not contain a block (special case for lists)
|
||||
if (!/\n\n¨K/.test(text)) {
|
||||
text = text.replace(/\n+/g, '<br />\n');
|
||||
}
|
||||
} else {
|
||||
// Vanilla hard breaks
|
||||
text = text.replace(/ +\n/g, '<br />\n');
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('makehtml.spanGamut.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
11
src/subParsers/makehtml/strikethrough.js
Normal file
11
src/subParsers/makehtml/strikethrough.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
showdown.subParser('makehtml.strikethrough', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (options.strikethrough) {
|
||||
text = globals.converter._dispatch('makehtml.strikethrough.before', text, options, globals).getText();
|
||||
text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return '<del>' + txt + '</del>'; });
|
||||
text = globals.converter._dispatch('makehtml.strikethrough.after', text, options, globals).getText();
|
||||
}
|
||||
|
||||
return text;
|
||||
});
|
|
@ -3,7 +3,7 @@
|
|||
* hash references.
|
||||
* Link defs are in the form: ^[id]: url "optional title"
|
||||
*/
|
||||
showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.stripLinkDefinitions', function (text, options, globals) {
|
||||
'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,
|
||||
|
@ -23,7 +23,9 @@ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
|
|||
// remove newlines
|
||||
globals.gUrls[linkId] = url.replace(/\s/g, '');
|
||||
} else {
|
||||
globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive
|
||||
url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
|
||||
|
||||
globals.gUrls[linkId] = showdown.subParser('makehtml.encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive
|
||||
}
|
||||
|
||||
if (blankLines) {
|
|
@ -1,13 +1,13 @@
|
|||
showdown.subParser('tables', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.tables', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.tables) {
|
||||
return text;
|
||||
}
|
||||
|
||||
var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,
|
||||
var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*[-=]{2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*[-=]{2,}[\s\S]+?(?:\n\n|¨0)/gm,
|
||||
//singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm;
|
||||
singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm;
|
||||
singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*[-=]{2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm;
|
||||
|
||||
function parseStyles (sLine) {
|
||||
if (/^:[ \t]*--*$/.test(sLine)) {
|
||||
|
@ -28,13 +28,13 @@ showdown.subParser('tables', function (text, options, globals) {
|
|||
if (options.tablesHeaderId || options.tableHeaderId) {
|
||||
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
|
||||
}
|
||||
header = showdown.subParser('spanGamut')(header, options, globals);
|
||||
header = showdown.subParser('makehtml.spanGamut')(header, options, globals);
|
||||
|
||||
return '<th' + id + style + '>' + header + '</th>\n';
|
||||
}
|
||||
|
||||
function parseCells (cell, style) {
|
||||
var subText = showdown.subParser('spanGamut')(cell, options, globals);
|
||||
var subText = showdown.subParser('makehtml.spanGamut')(cell, options, globals);
|
||||
return '<td' + style + '>' + subText + '</td>\n';
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,8 @@ showdown.subParser('tables', function (text, options, globals) {
|
|||
tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
|
||||
}
|
||||
// parse code spans first, but we only support one line code spans
|
||||
tableLines[i] = showdown.subParser('codeSpans')(tableLines[i], options, globals);
|
||||
|
||||
tableLines[i] = showdown.subParser('makehtml.codeSpans')(tableLines[i], options, globals);
|
||||
}
|
||||
|
||||
var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
|
||||
|
@ -125,7 +126,7 @@ showdown.subParser('tables', function (text, options, globals) {
|
|||
return buildTable(headers, cells);
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('tables.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.tables.before', text, options, globals).getText();
|
||||
|
||||
// find escaped pipe characters
|
||||
text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback);
|
||||
|
@ -136,7 +137,7 @@ showdown.subParser('tables', function (text, options, globals) {
|
|||
// parse one column tables
|
||||
text = text.replace(singeColTblRgx, parseTable);
|
||||
|
||||
text = globals.converter._dispatch('tables.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.tables.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
|
@ -1,11 +1,11 @@
|
|||
showdown.subParser('underline', function (text, options, globals) {
|
||||
showdown.subParser('makehtml.underline', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.underline) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('underline.before', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.underline.before', text, options, globals).getText();
|
||||
|
||||
if (options.literalMidWordUnderscores) {
|
||||
text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
|
||||
|
@ -26,7 +26,7 @@ showdown.subParser('underline', function (text, options, globals) {
|
|||
// escape remaining underscores to prevent them being parsed by italic and bold
|
||||
text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
text = globals.converter._dispatch('underline.after', text, options, globals);
|
||||
text = globals.converter._dispatch('makehtml.underline.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
15
src/subParsers/makehtml/unescapeSpecialChars.js
Normal file
15
src/subParsers/makehtml/unescapeSpecialChars.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Swap back in all the special characters we've hidden.
|
||||
*/
|
||||
showdown.subParser('makehtml.unescapeSpecialChars', function (text, options, globals) {
|
||||
'use strict';
|
||||
text = globals.converter._dispatch('makehtml.unescapeSpecialChars.before', text, options, globals).getText();
|
||||
|
||||
text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) {
|
||||
var charCodeToReplace = parseInt(m1);
|
||||
return String.fromCharCode(charCodeToReplace);
|
||||
});
|
||||
|
||||
text = globals.converter._dispatch('makehtml.unescapeSpecialChars.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user