Compare commits

..

247 Commits

Author SHA1 Message Date
dependabot[bot]
95255984ad
chore(deps): bump socket.io-parser from 4.0.4 to 4.0.5 (#962)
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.0.4...4.0.5)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 17:16:38 +00:00
Antonio
1165736eef
Merge pull request #944 from showdownjs/fix/link-to-quickstart
fix: resolve broken link to installation guide
2022-07-26 01:31:08 +03:00
Antonio
1a24a4c24c
fix: resolve broken link to installation guide 2022-07-26 01:20:58 +03:00
dependabot[bot]
df814d8771
chore(deps-dev): bump grunt from 1.5.2 to 1.5.3 (#928)
Bumps [grunt](https://github.com/gruntjs/grunt) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/gruntjs/grunt/releases)
- [Changelog](https://github.com/gruntjs/grunt/blob/main/CHANGELOG)
- [Commits](https://github.com/gruntjs/grunt/compare/v1.5.2...v1.5.3)

---
updated-dependencies:
- dependency-name: grunt
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 08:38:03 +01:00
Estevão Soares dos Santos
5aac76dbbb
docs(README.md): update contributing section 2022-04-29 03:36:38 +01:00
Estevão Soares dos Santos
4098c2806c Merge branch 'master' into develop 2022-04-27 07:05:50 +01:00
dependabot[bot]
4e577e846b
chore(deps-dev): bump grunt from 1.4.1 to 1.5.2 (#921)
Bumps [grunt](https://github.com/gruntjs/grunt) from 1.4.1 to 1.5.2.
- [Release notes](https://github.com/gruntjs/grunt/releases)
- [Changelog](https://github.com/gruntjs/grunt/blob/main/CHANGELOG)
- [Commits](https://github.com/gruntjs/grunt/compare/v1.4.1...v1.5.2)

---
updated-dependencies:
- dependency-name: grunt
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-27 07:04:56 +01:00
ggboy-shakalaka
440170aadc
fix(makemarkdown.image): fix bug width|height = auto
`<img src="a" width="auto" height="auto" />` transforms into `![null](<a> =autoxauto)`.
After fix, the output becomes `![null](<a> =*x*)`

Closes #622
2022-04-25 18:43:58 +01:00
Martin Beentjes
db5ce64172
chore: fix jq error (#917) 2022-04-25 18:41:00 +01:00
Antonio
9e5d0f3f11
docs: updated cli page to describe the updated format of passing extra options 2022-04-23 23:14:29 +03:00
Estevão Soares dos Santos
3e1731f1fe update changelog 2022-04-21 01:37:01 +01:00
Estevão Soares dos Santos
1c81fa6475 Merge branch 'master' into develop 2022-04-21 00:43:44 +01:00
Martin Beentjes
fdf9272af6
Update browserstack.yml (#915)
Update github action `martinbeentjes/npm-get-version-action` to newest version
2022-04-20 13:32:05 +01:00
Antonio
5c6c4659ca
docs: added revised credits page 2022-04-17 15:03:05 +03:00
dependabot[bot]
019b137aa8
chore(deps): bump async from 2.6.3 to 2.6.4 (#914)
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-16 20:25:28 +01:00
Estevão Soares dos Santos
947ec9bdd4 Merge branch 'develop' into feat/910-auto-linking-mention-from-html-to-markdown 2022-04-16 20:10:29 +01:00
Estevão Soares dos Santos
3c2e25a3c7 Merge branch 'develop' into feat/910-auto-linking-mention-from-html-to-markdown
# Conflicts:
#	src/subParsers/makemarkdown/table.js
2022-04-16 20:09:50 +01:00
Estevão Soares dos Santos
3a616c5bf6 feat(makeMarkdown.ghMentions): add support for ghMentions in makeMarkdown
Related to #910
2022-04-16 20:06:05 +01:00
Antonio
1cf1908935 docs: added page describing supported markdown syntax 2022-04-09 23:20:41 +03:00
Antonio
141c23793b docs: added a list of known extensions 2022-04-09 19:01:05 +03:00
Antonio
ef726d79a4 docs: added tutorial on how to use lang and output extensions together 2022-04-09 18:28:28 +03:00
Antonio
fae04b6549 docs: added tutuorial on how to create markdown editor 2022-04-09 16:22:34 +03:00
Antonio
c4eef0b8c1 docs: added section about cli extra options 2022-04-09 13:39:50 +03:00
Antonio
f3d73f5b54 docs: added tutorial on how to add classes to html elements 2022-04-09 02:06:07 +03:00
VoltanFr
4a887331c9
docs: added repo build guidelines in README.md (#909)
Helps developer start discovering the repo for coding.
2022-04-05 23:31:33 +01:00
Estevão Soares dos Santos
16cac70aee fix: add polyfill method for array.isArray
Closes #497
2022-04-03 09:36:37 +01:00
Estevão Soares dos Santos
fc01abd708 chore: try fixing github broswerstack action
these lines are for testing purposes

test test test
2022-03-31 08:22:18 +01:00
root
565bbdc94a chore: try to fix browserstack name 2022-03-31 08:03:09 +01:00
Estevão Soares dos Santos
a9ee330ac2 Merge branch 'develop' of github.com:showdownjs/showdown into develop 2022-03-31 02:44:42 +01:00
Estevão Soares dos Santos
e098ec684c test: fix name of browserstack name being too long 2022-03-31 02:44:15 +01:00
Antonio
010f94fd04 docs: added create extension page 2022-03-31 01:29:59 +03:00
Antonio
d11e5cf6e0 docs: added extensions overview page 2022-03-30 23:35:17 +03:00
Antonio
bdddfb9131 docs: added page describing markdown's xss vulnerability 2022-03-30 21:29:13 +03:00
Antonio
568445fa43 docs: added integrations 2022-03-30 20:12:35 +03:00
Antonio
6e6dde5c55 docs: added page describing cli tool 2022-03-30 02:13:37 +03:00
Antonio
8d9b8f2f92 docs: added page about flavors 2022-03-30 01:27:06 +03:00
Antonio
a1bb3e0a9b docs: sort showdown options alphabetically 2022-03-28 22:18:38 +03:00
Antonio
24016bfd0b docs: added page describing options supported by showdown 2022-03-28 21:54:41 +03:00
Antonio
5efc75d894 Merge branch 'develop' of github.com:showdownjs/showdown into develop 2022-03-28 21:52:54 +03:00
Antonio
b3dac51d9e
test: removed redundant path-ignore (#906) 2022-03-28 16:28:56 +01:00
Antonio
8a959a6ddd docs: added showdown configuration document 2022-03-28 01:59:41 +03:00
Estevão Soares dos Santos
29538dda96 test: add commit msg as browserstack test name
This lines are just for testing purposes to see if everything in the msg is grabbed or only the first part of the message.
2022-03-27 22:33:06 +01:00
Estevão Soares dos Santos
2715e76471 test: fix browserstack workflow(build-name) 2022-03-27 04:16:12 +01:00
Estevão Soares dos Santos
eb4b78ac66 test: fix browserstack workflow(build-name) 2022-03-27 04:13:12 +01:00
Estevão Soares dos Santos
cd83919bff test: fix browserstack workflow(build-name) 2022-03-27 04:02:21 +01:00
Estevão Soares dos Santos
fab6987e56 test: fix browserstack workflow 2022-03-27 03:59:45 +01:00
Estevão Soares dos Santos
f37113adca test: split build by commits in browserstack 2022-03-27 03:54:45 +01:00
Estevão Soares dos Santos
6a86880bbe chore: fix mix github actions problems 2022-03-27 03:33:01 +01:00
Estevão Soares dos Santos
788e329d6e test: fix cli test to work with named versions 2022-03-27 02:17:48 +01:00
Estevão Soares dos Santos
17bcd56eae test: add project name and build to browserstack 2022-03-27 00:40:11 +00:00
Estevão Soares dos Santos
0839b3a556 bump version to 3.0.0-alpha 2022-03-27 00:35:47 +00:00
Estevão Soares dos Santos
c45d66d974 try: fix github browserstack action 2022-03-27 00:25:36 +00:00
Estevão Soares dos Santos
f85d82fb23 try: fix github browserstack action 2022-03-27 00:17:58 +00:00
Estevão Soares dos Santos
691487f8c0 try: fix github browserstack action 2022-03-27 00:15:12 +00:00
Estevão Soares dos Santos
b0bb58d954 try: fix github browserstack action 2022-03-27 00:13:16 +00:00
Estevão Soares dos Santos
1f15a18adb try: fix browserstack github action 2022-03-27 00:08:22 +00:00
Estevão Soares dos Santos
9a3e714b2c test: implement karma and browserstack tests 2022-03-27 00:05:30 +00:00
Estevão Soares dos Santos
db571fbaac fix: tables parse correctly with new version of jsdom 2022-03-26 04:34:50 +00:00
Chris
5fc843e175
feat(makemarkdown.table): support non-strict tables
* feat(makemarkdown.table): support non-strict tables

change to support non strict html tables.
currently html tables require both 'thead' and 'tbody' to be set.
since there are many tables out there that dont meet these requirements,
i added support for most common tables. all of the following tables will now
work (the last one, is the only version that is currently working):
<table><tr><td>t 0 - missing thead/tbody</td></tr></table>
<table><tr><td>t 1 - missing thead/tbody</td></tr><tr><td>t 1 body</td></tr></table>
<table><thead><tr><th>t 2 - thead only</th></tr></thead></table>
<table><thead><tr><td>t 3 - thead with td</td></tr></thead></table>
<table><tbody><tr><td>t 4 - tbody only</td></tr></tbody></table>
<table><thead><tr><th>t 5 - both thead and tbody</th></tr></thead><tbody><tr><td>t 5</td></tr></tbody></table>
i thought this feature should be made optional in case you only want
to support fully compliant tables. but then i realized, that the options
are only passed to the markdown subparsers, not the html ones.
and since this does not break anything (all tests pass), its ok, i guess...

Closes #687

* refactor: code refactor and added tests

Co-authored-by: Estevão Soares dos Santos <estevao.santos@gmail.com>
2022-03-26 04:18:05 +00:00
chandi
949c2bcf86
fix(email): now email address obfuscation always returns the same output
* feat(helpers): determined results for email address obfuscation

email address is used as the seed, so it should always provide the same result for a given mail.
utilizes a random number generator shown here: https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316

* feat(helpers): added Math.imul() support for older browsers

Co-authored-by: Estevão Soares dos Santos <estevao.santos@gmail.com>
2022-03-26 00:31:41 +00:00
Swain
0d3ca4da5a
fix(metadata): allow whitespaces after closing marks
Co-authored-by: Estevão Soares dos Santos <estevao.santos@gmail.com>
2022-03-25 20:55:19 +00:00
CommanderRoot
3eff10bbbf
refactor: replace deprecated String.prototype.substr() (#902)
.substr() is deprecated so we replace it with .slice() which works similarily but isn't deprecated

Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
2022-03-25 20:18:41 +00:00
Antonio
a5f3add2b6
Merge pull request #898 from showdownjs/updated-event-type-for-external-event
docs: updated event_type for the repository_dispatch event
2022-03-25 21:03:02 +02:00
Estevão Soares dos Santos
a946f557a2
Merge pull request #900 from showdownjs/docs-quickstart-guide
docs: refactored installation to become quickstart guide
2022-03-25 18:41:05 +00:00
Antonio
314808aa3e docs: refactored installation to become quickstart guide 2022-03-25 20:21:21 +02:00
Antonio
53aa1cc79f
Merge pull request #899 from showdownjs/proper-colors-for-docs
docs: added 'corporate' colors for docs
2022-03-25 20:15:47 +02:00
Antonio
223017e218 docs: added 'corporate' colors for docs 2022-03-25 19:23:08 +02:00
Antonio
7788ba2bc0 docs: updated event_type for the repository_dispatch event 2022-03-22 00:10:34 +02:00
Estevao Soares dos Santos
d02fe7caf9 Merge branch 'develop' 2022-03-20 19:06:06 +00:00
Estevão Soares dos Santos
b8fb79d85f
docs: minor corrections to README.md 2022-03-20 19:05:11 +00:00
Estevão Soares dos Santos
bad26cd2ed
Merge pull request #897 from showdownjs/docs/docs-flow-triggers
docs: updated triggers for the documentation flow
2022-03-20 19:01:02 +00:00
Antonio
9ddb19479c docs: updated triggers for the documentation flow 2022-03-20 20:39:08 +02:00
Estevão Soares dos Santos
6397c203f6
docs(events): fix typo in properties 2022-03-20 15:34:16 +00:00
Anthony DePasquale
95eb3b4168
Update README.md 2022-03-20 16:26:13 +01:00
Estevao Soares dos Santos
4f0f76ca76 Merge branch 'develop' 2022-03-20 15:04:15 +00:00
Estevão Soares dos Santos
925bbceab4
docs: added installation guide, compatibility and donation pages
Merge pull request #895 from showdownjs/decoupling-readme-stage-1
2022-03-20 14:48:48 +00:00
Antonio
15b159c846 docs: added installation guide, compatibility and donation pages 2022-03-20 16:37:51 +02:00
Estevão Soares dos Santos
f2f30349d6
docs(project): add initial setup for docs publishing
Merge pull request #892 from showdownjs/documentation-initial-setup
2022-03-15 20:37:45 +00:00
Antonio
490f4eb562 docs: added ci pipeline to build and deploy docs 2022-03-15 20:28:53 +02:00
Estevao Soares dos Santos
df76f984a3 chore: add test for repeat helper
Several test cleanups and minor test fixes
2022-03-10 12:56:34 +00:00
Estevao Soares dos Santos
5f5304ccaa Merge branch 'develop' of https://github.com/showdownjs/showdown into develop 2022-03-10 11:24:48 +00:00
Estevão Soares dos Santos
849ed40b70
fix(polyfill) String.prototype.repeat
Merge pull request #629 from dennisss/fix2
2022-03-10 11:24:20 +00:00
Estevao Soares dos Santos
169cd1ed89 clean: clean stray console.log 2022-03-10 00:21:25 +00:00
Estevao Soares dos Santos
14dbda2dcf chore: build cli 2022-03-10 00:19:41 +00:00
Estevao Soares dos Santos
1f4a9885cc chore: minor fix in tests 2022-03-10 00:17:27 +00:00
Estevao Soares dos Santos
7acd65e498 fix(cli): remove checking stdin size
Also fix some errors related to testing and inconsistent behavior between linux and windows
2022-03-10 00:09:42 +00:00
Estevao Soares dos Santos
f87cf01282 chore: fix tests for CI 2022-03-09 15:19:34 +00:00
Estevao Soares dos Santos
54a54271ec second try in fixing github actions 2022-03-09 14:47:44 +00:00
Estevao Soares dos Santos
1a3b8db8cd chore: Fix problem in github actions CI 2022-03-09 13:51:44 +00:00
Estevao Soares dos Santos
c7dd148b08 chore: remove unused dev dependencies 2022-03-07 18:04:56 +00:00
Estevao Soares dos Santos
c3411a567d fix(cli): cli now works properly
The CLI was completely rewrote. Changed dependency from yargs to
commanderjs,
which is cleaner, faster and has no dependencies.
Also added a complete testsuite for the cli.

Merged branch 'cli_refactor' into develop

Closes #893, #894
2022-03-03 12:48:23 +00:00
Estevao Soares dos Santos
bd093ab493 chore: fix jsdoc in converter.js 2022-03-01 15:30:59 +00:00
Antonio
3b2a3029b4 docs(project): add initial setup for docs publishing 2022-02-27 13:12:55 +02:00
Estevao Soares dos Santos
16ad404b85 Merge branch 'develop' 2022-02-25 01:11:13 +00:00
Estevão Soares dos Santos
3e1a815e18
fix(lists): codeblocks inside lists are now correctly parsed
Closes #494
2022-02-25 01:10:07 +00:00
Estevao Soares dos Santos
8cecdf0382 fix(lists): codeblocks inside lists are now correctly parsed
Closes #494
2022-02-25 01:06:09 +00:00
Estevão Soares dos Santos
1615b3ea15
fix(makemarkdown.table): col text now aligns right properly 2022-02-25 00:48:14 +00:00
Estevão Soares dos Santos
b02c1dd31b
Merge pull request #889 from showdownjs/develop
Develop
2022-02-25 00:25:09 +00:00
Estevao Soares dos Santos
5e0ed809db feat(moreStyling): add some useful classes for css styling
Currently, only adds the class `task-list-item-complete` to completed tasks items in GFM tasklists.
But in the future, each time a css class is deemed to be necessary, should be added under this umbrella
option.

Closes #540
2022-02-24 01:57:08 +00:00
Estevao Soares dos Santos
67114255ad fix(gfmCodeBlocks): allow the info string in gfmCodeBlocks to contain spaces
The line with the opening code fence may optionally contain some text following the code fence (the info string); this is trimmed of leading and trailing whitespace and can contain multiple words (but not newlines).

Closes #856
2022-02-24 01:22:11 +00:00
Estevao Soares dos Santos
5a84b1cdf0 Merge branch 'master' into develop 2022-02-24 00:45:04 +00:00
Estevão Soares dos Santos
85caa462de
Create codeql-analysis.yml 2022-02-24 00:40:41 +00:00
Estevao Soares dos Santos
9f0b36d95b chore: active CI in develop branch 2022-02-24 00:23:15 +00:00
cjwind
626f661e8e
fix(lists): Fix makeMarkdown tasklist (#846)
Fix makeMarkdown() with tasklist by adding input subparser.

Close #774

Co-authored-by: Estevão Soares dos Santos <estevao.santos@gmail.com>
2022-02-24 00:16:31 +00:00
Estevao Soares dos Santos
cb2e4853b7 Merge branch 'master' into develop 2022-02-24 00:06:58 +00:00
mh-cbon
1967652acf
fix(cli): read input data using stream (#358)
* cli: prevent brutal exit when the process is doing async work

* cli: read input data suing a stream to fix #353

* cli: add error return support

* cli: add error return support

* lint

Closes #358
2022-02-24 00:06:08 +00:00
Vladimir Vuksanovic
ebc730c0a0
fix(metadata): Restore dollar signs and tremas. (#730)
Closes #626

Co-authored-by: Estevão Soares dos Santos <estevao.santos@gmail.com>
2022-02-24 00:03:20 +00:00
Estevao Soares dos Santos
3fd1ea6d7f chore: try to make CI work in master 2022-02-23 23:52:58 +00:00
Liu Xinyu
6f93b3eab1 fix: reduce npm package size 2022-02-23 19:25:29 +00:00
Estevao Soares dos Santos
e24d06e265 Merge branch 'master' into develop
# Conflicts:
#	Gruntfile.js
#	dist/showdown.js
#	dist/showdown.js.map
#	dist/showdown.min.js
#	dist/showdown.min.js.map
#	package-lock.json
#	package.json
#	src/converter.js
#	src/subParsers/makehtml/tables.js
2022-02-23 19:15:46 +00:00
Estevao Soares dos Santos
6ded499b91 refactor: code cleanup 2022-02-23 05:11:26 +00:00
Estevao Soares dos Santos
d23b028bb3 chore: update dev dependencies and fix code style 2022-02-23 05:04:55 +00:00
Devyn
80fbe08874
doc(security): add a security policy 2022-02-15 20:30:00 -07:00
Devyn
4f1bc668c4
Merge pull request #885 from showdownjs/test_anchors
test(anchor): reserved keywords and object properties and protocols
2022-02-07 11:29:44 -07:00
SyntaxRules
d54a6101b9 test(anchor): check other unicode characters 2022-02-07 11:19:44 -07:00
SyntaxRules
17837832b4 test(anchor): no protocol links are still treated as links
Closes https://github.com/showdownjs/showdown/issues/755
2022-02-07 11:07:11 -07:00
SyntaxRules
def6f0b453 test(anchor): fragments work as intended
Closes https://github.com/showdownjs/showdown/issues/845
2022-02-07 10:57:27 -07:00
SyntaxRules
838c5dbecc test(anchor): check that reserved keywords and object properties are usable in links 2022-02-07 10:29:59 -07:00
Daniel
ed51972315
fix(helpers): allow usage in ES6 modules fix for #676
* Fix for issue #676

This will check for situations when this is undefined, eg ES6 module wrapper

Co-authored-by: SyntaxRules <devyn.stott@gmail.com>
2021-12-23 12:10:23 -07:00
SyntaxRules
41cee10659 chore(node): update package-lock license to MIT 2021-12-23 11:30:07 -07:00
Devyn S
5d699c228e
Merge pull request #628 from dennisss/fix1
fix(extension-registering) removeExtension implementation
2021-11-14 13:50:57 -07:00
Dennis Shtatnov
9f779b36a8 fix(extension-registering) removeExtension implementation 2021-11-14 13:48:20 -07:00
Devyn S
005e83262c
Merge pull request #732 from VladimirV99/tasklist
fix(lists): Fix tasklists to comply with GFM
2021-11-14 13:40:25 -07:00
Vladimir Vuksanovic
ac1047815f fix(lists): Fix tasklists to comply with GFM
GFM requires a whitespace between brackets.

Closes #655
2021-11-14 13:37:38 -07:00
Devyn S
75ac5c9b6e
Merge pull request #702 from systemsthinkinginstitute/fix-upstream-reference-link-impostors
fix(makeHtml): allow using of squadron brackets in non-link locations
2021-11-14 13:30:06 -07:00
Marco Montalbano
8e2b339fe2
fix(helpers): update github flavored emoji to the latest (#837) 2021-11-14 13:23:03 -07:00
Thomas P
5544e4d995
fix(helpers): update octocat emoji image location; add tests 2021-11-14 13:17:19 -07:00
SyntaxRules
f9d9705d3c chore(release): update dist/* files for further testing 2021-11-14 13:04:45 -07:00
Devyn S
735c5d2a55
Chore/update ci (#876)
* chore(ci): update CI node version to match node supported versions

* chore(ci): appveyor only supports up to node 16.x

* chore(ci): travis CI is not longer free/working; remove it

* chore(ci): add git workflow to replace travisCI linux builds

* doc(readme): add back in a badge for linux CI

* chore(ci): add windows to CI

* chore(ci): remove appveyor in favor of github actions

* doc(readme): update info about node supportability
2021-11-14 12:58:28 -07:00
Devyn S
d5cc678941
Merge pull request #875 from showdownjs/develop
Develop
2021-11-12 10:05:38 -07:00
Devyn S
6efd75cb83
Merge pull request #731 from VladimirV99/ellipsis
feature(ellipsis): Add option to disable ellipsis
2021-11-12 09:58:50 -07:00
Devyn S
45fcc8435b
Merge pull request #698 from systemsthinkinginstitute/fix-space-between-inline-elements
Fix space between inline elements
2021-11-12 09:54:35 -07:00
Devyn S
965789589e
Merge pull request #788 from henrahmagix/include-html51-details-in-block-elements
Include HTML5.1 <details> in known block tags
2021-11-10 13:45:13 -07:00
Devyn S
ea72183ad7
Merge pull request #671 from childish-sambino/base-url
feat(relativePathBaseUrl): Add support for prepending a base URL
2021-11-10 00:00:37 -07:00
SyntaxRules
ae1d5db9e0 Merge branch 'master' of https://github.com/showdownjs/showdown into base-url 2021-11-09 23:49:48 -07:00
Sam Harrison
e3a5b5928f feat(relativePathBaseUrl): Add support for prepending a base URL
This feature enables support for prepending a base URL to relative paths in
links and images when converting Markdown to HTML.

Closes #536
2021-11-09 23:34:42 -07:00
SyntaxRules
24211cc907 chore(license): update liscense to MIT 2021-11-09 22:40:28 -07:00
SyntaxRules
9c043d8848 chore(deps): make dependecies consistent with ^ operator 2021-11-09 22:40:28 -07:00
SyntaxRules
3d5391e96a chore(deps): update all dependecies to the latest; make eslint work 2021-11-09 22:40:28 -07:00
Devyn S
9c362e832e
chore(ci): update CI to lastest node versions & github actions
* update CI node versions to match node supported versions
* remove node 17.x on appveyor, it only supports up to node 16.x
* travis CI is not longer free/working; remove it in favor of github actions
2021-11-09 13:05:44 -07:00
Henry Blyth
cb689aa23b Add <details> corresponding makeMarkdown test 2020-04-13 23:05:07 +01:00
Henry Blyth
775f2abe97 Include HTML5.1 <details> in known block tags
Fix #787
2020-04-13 22:55:14 +01:00
Jean Machuca
a9f38b6f05 Including QCObjects in "Who uses Showdown"-README (#744) 2019-11-02 21:24:43 +00:00
Adam Cook
dae65c6e36 fix: Update yargs to 14.2.0
This fixes the mem security vulnerability (https://github.com/jimmyleray/Emendare/issues/584).

The vulnerability only presents itself when showdown is used as a CLI; it is unlikely that this would have been exploitable.

Closes #738
2019-11-02 21:19:24 +00:00
Artur Haurylkevich
fe5d12525f docs: clear ambiguity about CDN usage
Add unpkg as an alternative cdn and clear ambiguity about CDN usage
It allows to skip version tag (it uses a latest version as default) and it's appealing due to its conciseness.

Closes  #742
2019-11-02 21:18:37 +00:00
Estevao Soares dos Santos
7664beae70 Merge branch 'master' into develop 2019-11-02 21:14:03 +00:00
Jammerware
caab5bb7bc fix: Add rel="noopener noreferrer" to links when openLinksInNewWindow is on
Add rel="noreferrer" to links when openLinksInNewWindow is on. Also add noopener when openLinksInNewWindow is on.

Closes #670
2019-11-02 18:58:07 +00:00
Vladimir Vuksanovic
5d494c8202 feature(ellipsis): Add option to disable ellipsis
Add ability to disable ellipsis parser.
This is needed for some use cases. See #634
Defaults to true to keep backwards compatibility.
2019-10-04 16:45:27 +02:00
David Chester
260d889427 fix reference link impostors 2019-05-25 18:01:56 +00:00
David Chester
f0d37ad3c5 rename test files to match convention 2019-05-25 17:13:24 +00:00
David Chester
001b1881d0 preserve spaces between inline elements 2019-05-14 13:15:15 +00:00
chris
5f85c53910 fix(makemarkdown.table): col text align right
fix for html columns align right. currently hmtl cols
with align right get converted to md with align center
e.g. html
<th style="text-align: right">head</th>
get converted to
:---: (suddenly center)
this pull request fixes that bug
2019-05-01 12:56:38 +02:00
Estevão Soares dos Santos
33bba54535
Update README.md 2019-03-18 03:33:16 +00:00
Sibiraj
cfca8cbce6 docs: update README.md (#652) 2019-02-02 01:14:52 +00:00
Joshua Cline
5bac7e0564 Escaped the <br> in README.md (#645) 2019-01-27 03:38:51 +00:00
David Chester
201969473a fix(makeMarkdown): handle <br> tags converting html to markdown
Convert <br> tags to 2 spaces + newline rather than leaving the tag
in place when converting HTML to MD
2019-01-01 20:22:49 +00:00
Liu Xinyu
4af13e5e76 chore: add vue-showdown (#630) 2018-12-17 22:40:32 +00:00
Fabian Korak
bd927b5b7a docs: Update Changelog.md (#623)
Update the changelog to the same version the release-tag-system has
2018-12-16 15:39:32 +00:00
Dennis Shtatnov
a64a18bef8 fix(polyfill) String.prototype.repeat 2018-12-16 09:35:15 -05:00
Futago-za Ryuu
82f90ebda2 fix: reduce npm package size
Add files field to package.json so that the size of the published package is reduced by only including the files required (the bin and the dist folders)

Closes #619
2018-11-13 21:47:31 +00:00
Estevao Soares dos Santos
66798912de Merge branch 'master' into develop 2018-11-09 23:47:36 +00:00
Estevão Soares dos Santos
8a8f961bc4
Update DONATIONS.md 2018-11-09 23:32:47 +00:00
Estevão Soares dos Santos
bb4db60452
Update DONATIONS.md 2018-11-09 23:02:52 +00:00
Estevão Soares dos Santos
b08f9a04a0
Update README.md 2018-11-04 03:06:18 +00:00
Estevão Soares dos Santos
bd6beeaf0d
Update README.md 2018-11-04 03:03:11 +00:00
Oscar Morrison
68733e6684 add md-page to people who use (#604) 2018-11-04 03:02:18 +00:00
Estevão Soares dos Santos
05e8b22917
Update DONATIONS.md 2018-11-04 00:30:11 +00:00
Estevão Soares dos Santos
5e5b96c26a
Update DONATIONS.md 2018-11-04 00:29:50 +00:00
Estevão Soares dos Santos
a488b4728f
Update README.md 2018-11-03 19:28:17 +00:00
Estevão Soares dos Santos
0ebef28c66
Update README.md 2018-11-03 19:18:13 +00:00
Vladimir Vuksanovic
74e77369e0 docs(emoji): Change emoji comment (#611)
Fix emoji comment to reflect its actual behavior
2018-10-26 10:31:24 +01:00
Estevao Soares dos Santos
80f5c7e703 Merge branch 'VladimirV99-patch-2' into develop 2018-10-24 03:27:37 +01:00
Estevao Soares dos Santos
6ee6d8c9d9 rebuild 2018-10-24 03:27:16 +01:00
Vladimir Vuksanovic
4378abb4fa fix(italicsAndBold): Make italicsAndBold lazy (#608)
fix italicsAndBold if literalMidwordUnderscores option is enabled
it should end at the nearest closing underscores, not the furthest

Closes #544
2018-10-24 03:23:28 +01:00
Vladimir Vuksanovic
e0fc6f8d6b docs(completeHTMLDocument): Change completeHTMLDocument comment (#610)
Fix completeHTMLDocument comment to reflect its actual behavior
2018-10-23 11:04:41 +01:00
RAKESH PEELA
054829b2dc docs(readme.md): no space after ':' creates problem with Boldness of 'tasklists' and description in Wiki
No space after ':' creates problem with Boldness of 'tasklists' and description in Wiki
2018-10-21 22:17:28 +01:00
Vladimir Vuksanovic
fc1717780e docs(horizontalRule): Change horizontalRule documentation (#606)
Fix horizontalRule documentation to reflect its actual purpose
2018-10-21 22:15:10 +01:00
Vladimir Vuksanovic
81edc70da7 fix(underline): Make underline lazy
fix underline if literalMidwordUnderscores option is enabled
it should end at the nearest closing underscores, not the furthest
2018-10-21 15:20:39 +02:00
Estevão Soares dos Santos
db69d02974
Update DONATIONS.md 2018-10-06 00:53:35 +01:00
Estevão Soares dos Santos
dc03c7e647
Update DONATIONS.md 2018-10-06 00:52:07 +01:00
Estevao Soares dos Santos
4da93e8e13 docs: add first draft of documentation for version 2.0
Still has a long way to go
2018-09-25 04:10:40 +01:00
Estevao Soares dos Santos
d3ebff7ef0 fix(links): a number of issues with links subparser
This is a major refactor of the links subparser, previously known as anchors subparser.

Closes #355, #534

BREAKING CHANGE: `excludeTrailingPunctuationFromURLs` option was removed. This is now the default behavior
2018-09-25 04:04:59 +01:00
Estevao Soares dos Santos
798bbe6751 refactor: change anchor subparser name to link 2018-09-23 20:34:54 +01:00
Estevao Soares dos Santos
61aca9a7d6 Merge branch 'develop' 2018-09-23 19:05:43 +01:00
Estevao Soares dos Santos
f46479b4ee chore: update readme and add TASKS.TODO.md 2018-09-16 03:45:07 +01:00
Estevão Soares dos Santos
d309b5b1c1
Update CREDITS.md 2018-09-15 15:32:09 +01:00
Estevão Soares dos Santos
5ed36a508c
Create LICENSE 2018-09-15 15:31:03 +01:00
Estevão Soares dos Santos
f4c018ffca
Delete LICENSE 2018-09-15 15:30:10 +01:00
Estevão Soares dos Santos
73cf218f25
Update README.md 2018-09-15 15:26:56 +01:00
Estevao Soares dos Santos
f24e314a70 Merge branch 'develop' 2018-09-15 15:23:11 +01:00
Estevão Soares dos Santos
f874043725
Delete license.txt 2018-09-15 15:22:26 +01:00
Estevão Soares dos Santos
48de9101f4
Update LICENSE 2018-09-15 15:22:07 +01:00
Estevão Soares dos Santos
d1ae7b2bc5
Create LICENSE 2018-09-15 15:16:06 +01:00
Estevao Soares dos Santos
18c4b6ebac chore: build 2018-09-15 14:59:25 +01:00
Estevao Soares dos Santos
6259f37bd6 fix(gfm-codeblocks): leading space no longer breaks gfm codeblocks
Now GFM Code Blocks can have up to 3 spaces before the backticks

Closes #523
2018-09-15 14:58:50 +01:00
Estevao Soares dos Santos
26abc7a795 fix(headings): inconsistent behavior in lists
In text, headings only require a single linebreak to be treated as such.
However, in lists, they would require a double linebreak.
Now, the behavior in lists and text is consistent, requiring only a single
linebreak.

Closes #495
2018-09-15 14:45:48 +01:00
Estevao Soares dos Santos
d9eea64794 remove(literalMidWordAsterisks): remove literalMidWordAsterisks feature
This feature was seen as a bit "duh!" since midword asterisks are not really a thing and, for these situations, you can simply escape the asterisk character.

Closes #499

BREAKING CHANGE: literalMidWordAsterisks option was removed and so asterisks will always retain their markdown magic meaning in a source text.
If you're using this feature, and you wish to retain this option, you can find a shim here: <https://gist.github.com/tivie/7f8a88c89ffb00d2afe6c59a25528386>
2018-09-14 22:46:03 +01:00
Estevao Soares dos Santos
a4be301331 fix: allow escaping of colons
Previously, you couldn't escape colons (as they were semi-magic markdown characters).
Colons (:) can now be backslash escaped.
2018-09-14 21:34:48 +01:00
Estevao Soares dos Santos
c7a89eaae4 build 2018-09-14 17:04:38 +01:00
Estevao Soares dos Santos
2ba00751cc fix(mentions): allow for usernames with dot, underscore and dash
Closes #574
2018-09-14 17:03:07 +01:00
Estevao Soares dos Santos
b0d475fc08 fix(images): fix js error when using image references
In some circumstances, on a reference style image, the last capturing
group is ignored, which causes the fucntion argument to return the number
of matches instead of a string (or undefined).
Checking if the title parameter is a string ensures that the title
parameter is actually something that was caught by the regex and not some
metadata.

Closes #585
2018-09-14 15:49:24 +01:00
Estevao Soares dos Santos
63763b136f fix(images): fix error when using image references
In some circumstances, on a reference style image, the last capturing
group is ignored, which causes the fucntion argument to return the number
of matches instead of a string (or undefined).
Checking if the title parameter is a string ensures that the title
parameter is actually something that was caught by the regex and not some
metadata.

Closes #585
2018-09-14 15:40:18 +01:00
Estevao Soares dos Santos
fc7ac1e1ca chore: fix merge conflicts 2018-09-14 14:21:20 +01:00
Estevao Soares dos Santos
0c6b5bdc4b Merge branch 'develop' of https://github.com/showdownjs/showdown into develop
# Conflicts:
#	dist/showdown.js
#	dist/showdown.js.map
#	dist/showdown.min.js
#	dist/showdown.min.js.map
2018-09-14 14:11:08 +01:00
Estevao Soares dos Santos
05ef5c55dc refactor(event dispatcher): make event dispatcher return an object instead of text 2018-09-14 14:10:15 +01:00
Estevão Soares dos Santos
686d3dd146
Update CHANGELOG.md 2018-09-14 13:40:24 +01:00
Matt Budish
baf325f120 Upgrade yargs to address vulnerability in mem. (#580)
https://snyk.io/test/npm/yargs/10.1.2

Run grunt build.

Add preinstall script.

Revert "Add preinstall script."

This reverts commit 3034355c0b6c4ac70eeb4a00cdba1d46ccaf78eb.
2018-09-09 20:49:22 +01:00
Lee Moody
1f0242c6ea fix(gfm-codeblock): add support for spaces before language declaration
One or more spaces before the language declaration of a code block is supported by Github.

E.g.

```    html
<div>HTML!</div>
```

``` html
<div>HTML!</div>
```

```html
<div>HTML!</div>
```

Closes #569
2018-08-08 01:25:32 +01:00
Estevao Soares dos Santos
e80a548c9b build 2018-07-07 19:15:02 +01:00
Estevao Soares dos Santos
0d5c9df1a8 chore: update dependencies 2018-07-07 19:12:34 +01:00
Estevao Soares dos Santos
62270d4a4b Merge branch 'develop' 2018-07-07 19:07:04 +01:00
Estevao Soares dos Santos
358947bd29 feat(makeMarkdown): convert HTML to MD
This feature enables convertion of HTML into MD.
2018-07-07 19:05:16 +01:00
Estevão Soares dos Santos
3d51939b4e
Update README.md
Closes #559
2018-07-05 16:09:19 +01:00
jjangga0214
a54ed664f7 doc: Update README.md
correct markdown grammar
2018-06-05 22:33:56 +01:00
Christian Genco
0c6ce94673 doc: add metadata code example 2018-05-13 15:37:53 +01:00
Daniel Ruf
7628471928 chore: cache node_modules 2018-05-10 00:26:04 +01:00
Daniel Ruf
fc43100286 chore: cleanup ci scripts (#525) 2018-05-07 20:12:30 +01:00
Estevao Soares dos Santos
b53f71bdf3 Merge branch 'master' into develop 2018-05-07 20:07:22 +01:00
Estevao Soares dos Santos
099a6da5da chore: update jsdom to v10 2018-05-07 20:05:42 +01:00
Daniel Ruf
f572d368ed chore: use Node.js 6, 8 and 10 2018-05-07 19:54:37 +01:00
Estevao Soares dos Santos
49ed464183 update sinon 2018-05-07 19:43:31 +01:00
Estevao Soares dos Santos
d915c837d9 chore: update travis and appveyor 2018-05-07 19:39:09 +01:00
Estevao Soares dos Santos
8366b6cb48 chore: commit package-lock.json 2018-05-07 19:34:51 +01:00
maxwellito
55f22de0a0 fix: compress showdown emoji
* fix showdown emoji compression

* test: fix functional test
2018-05-07 19:27:19 +01:00
trickypr
699bb625ce doc: fix README.md small typos
Clean up two of the mistakes in the README.md file.
2018-05-07 19:25:04 +01:00
GenaBitu
f20dc75024 fix: replaces \u00A0 with &nbsp;
Closes #521
2018-05-07 19:14:58 +01:00
Estevão Soares dos Santos
039dd66256
Update README.md 2018-05-07 19:05:50 +01:00
Estevão Soares dos Santos
2c1062cb52
Update DONATIONS.md 2018-05-07 19:03:43 +01:00
Estevão Soares dos Santos
4e64a20463
Update DONATIONS.md 2018-05-07 19:03:18 +01:00
greenkeeper[bot]
706813d9bc chore(package): update grunt-contrib-uglify to version 3.3.0 (#481) 2018-02-02 16:05:27 +00:00
greenkeeper[bot]
6376d40584 fix(package): update yargs to version 11.0.0 (#491) 2018-02-02 16:03:29 +00:00
Estevão Soares dos Santos
ac23b8cb72
chore: pin sinon to 4.1.5 (#496) 2018-01-27 20:41:28 +00:00
Estevao Soares dos Santos
410b453049 fix jsdom polyfill 2017-12-23 14:00:20 +00:00
Estevao Soares dos Santos
17222b3d5a build 2017-12-23 13:29:39 +00:00
Estevao Soares dos Santos
173ad51b05 test: fix tests 2017-12-23 13:20:14 +00:00
Estevao Soares dos Santos
13ba2675fe Merge branch 'master' into develop 2017-12-23 12:42:36 +00:00
Estevao Soares dos Santos
be2a10797c refactor: force brackets in links 2017-12-23 12:41:21 +00:00
Estevao Soares dos Santos
94aa3e53ca test: add tests for reverse converter 2017-12-23 12:35:31 +00:00
Thomas Broadley
a42c2d7ed3 docs: fix typos (#480) 2017-12-23 08:18:03 +00:00
Estevao Soares dos Santos
19d2e9f9d4 test: testsuite refactoring 2017-12-22 15:28:41 +00:00
Estevao Soares dos Santos
ea3db5f180 feat(splitAdjacentBlockquotes): add option to split adjacent blockquote blocks
With this option enabled, this:

```md
> some text

> some other text
```

witll result in:

```html
<blockquote>
    <p>some text</p>
</blockquote>
<blockquote>
    <p>some other text</p>
</blockquote>
```

This is the default behavior of GFM.

Closes #477
2017-12-22 10:51:21 +00:00
Estevao Soares dos Santos
9825fd2bd0 Merge commit 'eec56fb1c015663947f6ed393f133bdc7a3e2a64' 2017-12-16 18:45:40 +00:00
Estevao Soares dos Santos
3db9200d2c refactor(subParsers): change name and directory of subparsers
BREAKING CHANGE: makeHtml subparsers names changed, by prepending 'makehtml.' to them.
Example: 'anchors', subparser is now named 'makehtml.anchors'.

Event names were also changed to reflect this.
Example: 'anchors.before' is now named 'makehtml.anchors.before'.

**To migrate:**

If you have a listener extension, replace the old event name with the new one. Example:

Replace this

```js
showdown.extension('myext', function() {
  return [{
    type: 'listener',
    listeners: {
      'anchors.before': function (event, text, converter, options, globals) {
        //... some code
        return text;
      }
  }];
});
```

with this
```js
showdown.extension('myext', function() {
  return [{
    type: 'listener',
    listeners: {
      'makehtml.anchors.before': function (event, text, converter, options, globals) {
        //... some code
        return text;
      }
  }];
});
```
2017-12-16 18:25:44 +00:00
Estevao Soares dos Santos
eec56fb1c0 lock packages.json packages to patch versions 2017-11-28 03:43:13 +00:00
Estevao Soares dos Santos
248254c99a freeze dev dependencies 2017-11-28 03:16:15 +00:00
Estevao Soares dos Santos
5bb009702b Merge branch 'master' into feature/reverse_convert 2017-11-28 03:14:32 +00:00
Estevao Soares dos Santos
e5d7705569 first step for reverse convert 2017-11-27 06:44:25 +00:00
945 changed files with 17229 additions and 4619 deletions

View File

@ -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

View File

@ -1,4 +1,7 @@
{ {
"env": {
"es6": true
},
"rules": { "rules": {
"indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}], "indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}],
"curly": [2, "all"], "curly": [2, "all"],

63
.github/workflows/browserstack.yml vendored Normal file
View 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
View 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
View 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
View 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

View File

@ -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 # 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 # 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: on:
push: push:
branches: [ master ] branches: [ master, develop ]
pull_request: pull_request:
branches: [ master ] branches: [ master, develop ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: windows-latest
strategy: strategy:
matrix: matrix:

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
node_modules node_modules
npm-debug.log npm-debug.log
/*.test.* /*.test.*
*.log

View File

@ -3,3 +3,4 @@ dist/**/*.js
build/**/*.js build/**/*.js
src/options.js src/options.js
bin/* bin/*
/karma.browserstack.js

View File

@ -14,15 +14,15 @@
"quotmark": "single", "quotmark": "single",
"undef": false, "undef": false,
"unused": true, "unused": true,
"strict": true, "strict": false,
"trailing": true, "trailing": true,
"smarttabs": true, "smarttabs": true,
"onevar": true, "onevar": true,
"globals": { "globals": {
"angular": true,
"module": true, "module": true,
"define": true, "define": true,
"window": true, "window": true,
"document": true,
"showdown": true "showdown": true
} }
} }

View File

@ -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> <a name="2.0.0"></a>
# [2.0.0](https://github.com/showdownjs/showdown/compare/1.9.1...2.0.0) (2022-02-15) # [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) * **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.0.0"></a>
<a name="1.9.0"></a>
# [1.9.0](https://github.com/showdownjs/showdown/compare/1.8.7...1.9.0) (2018-11-10) # [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 ### 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) * **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 ### 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) * **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> <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 ### Bug Fixes
* **emojis:** fix emoji excessive size ([4aca41c](https://github.com/showdownjs/showdown/commit/4aca41c)) * **emojis:** fix emoji excessive size ([4aca41c](https://github.com/showdownjs/showdown/commit/4aca41c))
* **gfm-codeblocks:** * **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)
* 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)
* 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) * **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) * **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) * **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)) * **nbsp:** fix replacing of nbsp with regular spaces ([8bc1f42](https://github.com/showdownjs/showdown/commit/8bc1f42))
<a name="1.8.6"></a> <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 ### Features
@ -123,7 +139,7 @@
### Bug Fixes ### 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> <a name="1.8.0"></a>
@ -249,7 +265,7 @@ list output may differ.
### Bug Fixes ### 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) * **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) * **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 ### 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) * **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) * **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) * **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 ### Features
@ -389,7 +405,7 @@ Important HOTFIX
### Bug Fixes ### Bug Fixes
* **escapeSpecialCharsWithinTagAttributes:** add ~ and = to escaped chars ([bfcc0e4](https://github.com/showdownjs/showdown/commit/bfcc0e4)) * **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 ### Features
@ -482,7 +498,7 @@ This release also improves performance a bit (around 8%)
### Bug Fixes ### Bug Fixes
* **listeners:** fix listeners typo ([f0d25b7](https://github.com/showdownjs/showdown/commit/f0d25b7)), closes [#290](https://github.com/showdownjs/showdown/issues/290) * **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)) * **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 * three
``` ```
Before (ouput): Before (output):
```html ```html
<ul> <ul>
<li>one <li>one

View File

@ -1,11 +1,14 @@
Credits Credits
======= =======
- Showdown v2 - Showdown v2
* [Estevão Santos](https://github.com/tivie) * [Estevão Santos](https://github.com/tivie)
* [SyntaxRules](https://github.com/SyntaxRules) * [SyntaxRules](https://github.com/SyntaxRules)
- Showdown v1 - Showdown v1
* [Estevão Santos](https://github.com/tivie) * [Estevão Santos](https://github.com/tivie)
* [Pascal Deschênes](https://github.com/pdeschen) * [Pascal Deschênes](https://github.com/pdeschen)
- Showdown v0 - Showdown v0
* [Corey Innis](http://github.com/coreyti):<br/> * [Corey Innis](http://github.com/coreyti):<br/>
Original GitHub project maintainer Original GitHub project maintainer

View File

@ -8,28 +8,44 @@ module.exports = function (grunt) {
require('quiet-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. // Project configuration.
var config = { var config = {
pkg: grunt.file.readJSON('package.json'), pkg: grunt.file.readJSON('package.json'),
concat: { concat: {
options: {
sourceMap: true,
banner: ';/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */\n(function(){\n',
footer: '}).call(this);\n'
},
dist: { 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: [
'src/options.js', 'src/options.js',
'src/showdown.js', 'src/showdown.js',
'src/helpers.js', 'src/helpers.js',
'src/subParsers/makehtml/*.js',
'src/subParsers/makemarkdown/*.js',
'src/converter.js', 'src/converter.js',
'src/subParsers/*.js',
'src/subParsers/makeMarkdown/*.js',
'src/loader.js' 'src/loader.js'
], ],
dest: 'dist/<%= pkg.name %>.js' dest: 'dist/<%= pkg.name %>.js'
}, },
cli: {
src: [
'src/cli/cli.js'
],
dest: 'bin/showdown.js'
},
test: { test: {
src: '<%= concat.dist.src %>', src: '<%= concat.dist.src %>',
dest: '.build/<%= pkg.name %>.js', dest: '.build/<%= pkg.name %>.js',
@ -42,14 +58,23 @@ module.exports = function (grunt) {
clean: ['.build/'], clean: ['.build/'],
uglify: { uglify: {
options: {
sourceMap: true,
banner: '/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */'
},
dist: { dist: {
options: {
sourceMap: true,
banner: '/*! <%= pkg.name %> v <%= pkg.version %> - <%= grunt.template.today("dd-mm-yyyy") %> */'
},
files: { files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>'] '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: { jshint: {
options: { options: {
jshintrc: '.jshintrc', jshintrc: '.jshintrc'
reporterOutput: ''
}, },
files: [ files: [
'Gruntfile.js', 'Gruntfile.js',
@ -110,59 +134,40 @@ module.exports = function (grunt) {
} }
}, },
simplemocha: { mochaTest: {
node: { functional: {
src: 'test/node/**/*.js', src: 'test/functional/**/*.js',
options: { options: {
globals: ['should'],
timeout: 3000, timeout: 3000,
ignoreLeaks: true, ignoreLeaks: true,
reporter: 'spec' reporter: 'spec',
require: ['test/bootstrap.js']
} }
}, },
karlcow: { unit: {
src: 'test/node/testsuite.karlcow.js', src: 'test/unit/**/*.js',
options: { options: {
globals: ['should'],
timeout: 3000, timeout: 3000,
ignoreLeaks: false, ignoreLeaks: true,
reporter: 'spec' reporter: 'spec',
} require: ['test/bootstrap.js']
},
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'
} }
}, },
single: { single: {
src: 'test/node/**/*.js',
options: { options: {
globals: ['should'],
timeout: 3000, timeout: 3000,
ignoreLeaks: false, 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); 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 * Generate Changelog
*/ */
@ -204,7 +199,7 @@ module.exports = function (grunt) {
*/ */
grunt.registerTask('performancejs', function () { grunt.registerTask('performancejs', function () {
'use strict'; 'use strict';
var perf = require('./test/node/performance.js'); var perf = require('./test/performance/performance.js');
perf.runTests(); perf.runTests();
perf.generateLogs(); perf.generateLogs();
}); });
@ -212,34 +207,31 @@ module.exports = function (grunt) {
/** /**
* Run a single test * Run a single test
*/ */
grunt.registerTask('single-test', function (grep) { grunt.registerTask('single-test', function (file) {
'use strict'; 'use strict';
grunt.config.merge({ grunt.config.merge({
simplemocha: { mochaTest: {
single: { single: {
options: { src: file
grep: grep
}
} }
} }
}); });
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('performance', ['concat:test', 'performancejs', 'clean']);
grunt.registerTask('build', ['test', 'concat:dist', 'uglify', 'endline']); grunt.registerTask('build', ['test', 'concat:dist', 'concat:cli', 'uglify:dist', 'uglify:cli', 'endline']);
grunt.registerTask('prep-release', ['build', 'generate-changelog']); grunt.registerTask('build-without-test', ['concat:dist', 'uglify', 'endline']);
grunt.registerTask('prep-release', ['build', 'performance', 'generate-changelog']);
// Default task(s). // Default task(s).
grunt.registerTask('default', ['test']); grunt.registerTask('default', ['test']);

142
README.md
View File

@ -1,32 +1,29 @@
![Showdown][sd-logo] ![Showdown][sd-logo]
![Build Status: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.js.yml/badge.svg) ![Build Status: Linux](https://github.com/showdownjs/showdown/actions/workflows/node.linux.yml/badge.svg)
[![Build Status: Windows](https://ci.appveyor.com/api/projects/status/github/showdownjs/showdown?branch=master&svg=true)](https://ci.appveyor.com/project/tivie/showdown/branch/master) ![Build Status: Windows](https://github.com/showdownjs/showdown/actions/workflows/node.win.yml/badge.svg)
[![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) [![npm version](https://badge.fury.io/js/showdown.svg)](http://badge.fury.io/js/showdown)
[![Bower version](https://badge.fury.io/bo/showdown.svg)](http://badge.fury.io/bo/showdown) [![Bower version](https://badge.fury.io/bo/showdown.svg)](http://badge.fury.io/bo/showdown)
[![Join the chat at https://gitter.im/showdownjs/showdown](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/showdownjs/showdown?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![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) [![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 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 can be used client side (in the browser) or server side (with Node.js).
## Live DEMO ## Live DEMO
Check a live Demo here http://demo.showdownjs.com/ Check out 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)
As you know, ShowdownJS is a free library and it will remain free forever. However, maintaining and improving the library costs time and money. 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 ## License
ShowdownJS v 2.0 is release under the MIT version. ShowdownJS v 2.0 is released under the MIT license.
Previous versions are release under BSD. Previous versions are released under BSD.
## Who uses Showdown (or a fork) ## Who uses Showdown (or a fork)
@ -42,7 +39,7 @@ Previous versions are release under BSD.
### Download tarball ### 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 ### Bower
@ -56,7 +53,7 @@ You can download the latest release tarball directly from [releases][releases]
PM> Install-Package showdownjs 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 ### CDN
@ -93,16 +90,13 @@ The converter itself might even work in things that aren't web browsers, like Ac
## Node compatibility ## 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'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 ## Changelog
You can check the full [changelog][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 ### Node
**Markdown to HTML**
```js ```js
var showdown = require('showdown'), var showdown = require('showdown'),
converter = new showdown.Converter(), converter = new showdown.Converter(),
@ -123,21 +116,12 @@ var showdown = require('showdown'),
html = converter.makeHtml(text); 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 ### Browser
```js ```js
var converter = new showdown.Converter(), var converter = new showdown.Converter(),
html = converter.makeHtml('# hello, markdown!'), text = '# hello, markdown!',
md = converter.makeMd('<a href="https://patreon.com/showdownjs">Please Support us!</a>'); html = converter.makeHtml(text);
``` ```
### Output ### Output
@ -148,10 +132,6 @@ Both examples should output...
<h1 id="hellomarkdown">hello, markdown!</h1> <h1 id="hellomarkdown">hello, markdown!</h1>
``` ```
```md
[Please Support us!](https://patreon.com/showdownjs)
```
## Options ## Options
You can change some of showdown's default behavior through 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), * **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)** 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 * **headerLevelStart**: (integer) [default 1] Set the header starting level. For instance, setting this to 3 means that
```md ```md
@ -269,6 +241,14 @@ var defaultOptions = showdown.getDefaultOptions();
<h3>foo</h3> <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. * **simplifiedAutoLink**: (boolean) [default false] Turning this option on will enable automatic linking to urls.
This means that: This means that:
@ -280,16 +260,8 @@ var defaultOptions = showdown.getDefaultOptions();
<p>some text <a href="www.google.com">www.google.com</a> <p>some text <a href="www.google.com">www.google.com</a>
``` ```
* **excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls. * ~~**excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls.
Punctuation excluded: `. ! ? ( )`. Only applies if **simplifiedAutoLink** option is set to `true`. 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>
```
* **literalMidWordUnderscores**: (boolean) [default false] Turning this on will stop showdown from interpreting * **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. 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> <p>some text with__underscores__in middle</p>
``` ```
* **literalMidWordAsterisks**: (boolean) [default false] Turning this on will stop showdown from interpreting asterisks * ~~**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. 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>
```
* **strikethrough**: (boolean) [default false] Enable support for strikethrough syntax. * **strikethrough**: (boolean) [default false] Enable support for strikethrough syntax.
`~~strikethrough~~` as `<del>strikethrough</del>` `~~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. 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)** **(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)** needing 2 spaces at the end of the line **(since v1.5.1)**
```md ```md
@ -404,6 +366,10 @@ var defaultOptions = showdown.getDefaultOptions();
* **splitAdjacentBlockquotes**: (boolean) [default false] Split adjacent blockquote blocks.(since v.1.8.6) * **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. **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 ### Client-side Extension Usage
```html ```js
<script src="showdown.js"></script> <script src="showdown.js" />
<script src="twitter-extension.js"></script> <script src="twitter-extension.js" />
<script>
var converter = new showdown.Converter({ extensions: ['twitter'] }); var converter = new showdown.Converter({ extensions: ['twitter'] });
</script>
``` ```
### Server-side Extension Usage ### Server-side Extension Usage
@ -481,9 +446,22 @@ var showdown = require('showdown'),
converter = new showdown.Converter({ extensions: ['myExtension'] }); 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 ## 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: the project root to install the dependencies:
npm install npm install
@ -508,28 +486,26 @@ Pull Request.
PRs are awesome. However, before you submit your pull request consider the following guidelines: 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. - 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 ```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). - Run the full test suite before submitting and make sure all tests pass (obviously =P).
- Try to follow our [**coding style rules**][coding-rules]. - Try to follow our [**coding style rules**][coding-rules]. Breaking them prevents the PR to pass the tests.
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 - 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. hard to review big one.
- If the PR introduces a new feature or fixes an issue, please add the appropriate test case. - 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 - 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].
[**AngularJS Git Commit Guidelines**][ng-commit-guide]. - 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: - If we suggest changes then:
- Make the required updates. - 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): - Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```bash ```bash
git rebase master -i git rebase develop -i
git push origin my-fix-branch -f git push origin my-fix-branch -f
``` ```
- After your pull request is merged, you can safely delete your branch. - 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) [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 [ext-wiki]: https://github.com/showdownjs/showdown/wiki/extensions
[coding-rules]: https://github.com/showdownjs/code-style/blob/master/README.md [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 [boilerplate-repo]: https://github.com/showdownjs/extension-boilerplate

146
TASKS.TODO.md Normal file
View 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

Binary file not shown.

BIN
dist/showdown.js vendored

Binary file not shown.

BIN
dist/showdown.js.map vendored

Binary file not shown.

BIN
dist/showdown.min.js vendored

Binary file not shown.

Binary file not shown.

4
docs/assets/extra.css Normal file
View File

@ -0,0 +1,4 @@
:root {
--md-primary-fg-color: rgb(196, 54, 39);
--md-accent-fg-color: rgb(62, 139, 138);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

773
docs/available-options.md Normal file
View 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>&lt;div&gt;foo&lt;/div&gt;</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="&#109;&#97;&#105;&#108;t&#x6f;&#x3a;&#109;&#x79;s&#x65;&#x6c;&#102;&#64;&#x65;xa&#109;&#112;&#108;&#101;&#x2e;c&#x6f;&#109;">&#x6d;&#121;s&#101;&#108;f&#x40;&#x65;&#120;a&#x6d;&#x70;&#108;&#x65;&#x2e;&#99;&#x6f;&#109;</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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &amp; bar &lt;baz&gt;&lt;/baz&gt;</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
View 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

View 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
View 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)

View 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/

View 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
View 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 users cookies and do other malicious actions (like steal credentials). As a countermeasure, you should always filter user input for suspicious content. Showdown doesnt 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, youve 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 couldnt catch it because the input doesnt follow HTML rules: its a mix of Markdown and HTML, and the filter doesnt 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 Markdowns 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "showdown", "name": "showdown",
"version": "2.0.0", "version": "3.0.0-alpha",
"description": "A Markdown to HTML converter written in Javascript", "description": "A Markdown to HTML converter written in Javascript",
"author": "Estevão Santos", "author": "Estevão Santos",
"homepage": "http://showdownjs.com/", "homepage": "http://showdownjs.com/",
@ -25,6 +25,10 @@
"Pascal Deschênes", "Pascal Deschênes",
"Estevão Santos" "Estevão Santos"
], ],
"funding": {
"type": "individual",
"url": "https://www.paypal.me/tiviesantos"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/showdownjs/showdown.git", "url": "https://github.com/showdownjs/showdown.git",
@ -43,7 +47,8 @@
"dist" "dist"
], ],
"devDependencies": { "devDependencies": {
"chai": "^4.3.4", "chai": "*",
"chai-match": "*",
"grunt": "^1.4.1", "grunt": "^1.4.1",
"grunt-contrib-clean": "^2.0.0", "grunt-contrib-clean": "^2.0.0",
"grunt-contrib-concat": "^2.0.0", "grunt-contrib-concat": "^2.0.0",
@ -53,17 +58,27 @@
"grunt-conventional-github-releaser": "^1.0.0", "grunt-conventional-github-releaser": "^1.0.0",
"grunt-endline": "^0.7.0", "grunt-endline": "^0.7.0",
"grunt-eslint": "^24.0.0", "grunt-eslint": "^24.0.0",
"grunt-mocha-test": "^0.13.3",
"grunt-simple-mocha": "^0.4.0", "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", "load-grunt-tasks": "^5.1.0",
"performance-now": "^2.1.0", "performance-now": "^2.1.0",
"quiet-grunt": "^0.2.0", "quiet-grunt": "^0.2.0",
"semver": "^7.3.0", "semver-sort": "^1.0.0",
"semver-sort": "^0.0.4", "sinon": "*",
"sinon": "^12.0.1", "source-map-support": "^0.5.21"
"source-map-support": "^0.5.20"
}, },
"dependencies": { "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

View File

@ -1,785 +1,43 @@
# Performance Tests for showdown # 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 Suite: Basic (50 cycles)
| test | avgTime | max | min | | test | avgTime | max | min |
|:-----|--------:|----:|----:| |:-----|--------:|----:|----:|
|Simple "Hello World"|0.394|9.154|0.104| |Simple "Hello World"|0.581|12.279|0.151|
|performance.testfile.md|49.286|177.704|26.155| |performance.testfile.md|35.491|79.405|29.046|
### Test Suite: subParsers (20 cycles) ### Test Suite: subParsers (20 cycles)
| test | avgTime | max | min | | test | avgTime | max | min |
|:-----|--------:|----:|----:| |:-----|--------:|----:|----:|
|hashHTMLBlocks|6.101|16.106|2.376| |hashHTMLBlocks|4.561|7.807|2.461|
|anchors|0.767|3.507|0.323| |anchors|2.910|6.510|1.056|
|autoLinks|0.244|0.548|0.124| |blockQuotes|4.142|19.023|2.541|
|blockQuotes|2.397|4.000|2.013| |codeBlocks|0.264|0.858|0.194|
|codeBlocks|0.226|0.343|0.208| |codeSpans|0.365|1.421|0.278|
|codeSpans|0.316|1.136|0.258| |detab|0.055|0.116|0.047|
|detab|0.095|0.184|0.085| |encodeAmpsAndAngles|0.145|0.796|0.092|
|encodeAmpsAndAngles|0.104|0.153|0.096| |encodeBackslashEscapes|0.087|0.266|0.063|
|encodeBackslashEscapes|0.062|0.137|0.056| |encodeCode|0.605|1.144|0.522|
|encodeCode|0.558|1.469|0.485| |escapeSpecialCharsWithinTagAttributes|0.206|0.287|0.184|
|escapeSpecialCharsWithinTagAttributes|0.243|0.714|0.192| |githubCodeBlocks|0.251|1.003|0.192|
|githubCodeBlocks|0.213|0.407|0.186| |hashBlock|0.038|0.104|0.034|
|hashBlock|0.046|0.147|0.036| |hashElement|0.005|0.053|0.001|
|hashElement|0.003|0.050|0.000| |hashHTMLSpans|5.229|9.835|4.240|
|hashHTMLSpans|4.914|7.364|4.474| |hashPreCodeTags|0.155|0.705|0.117|
|hashPreCodeTags|0.134|0.234|0.110| |headers|2.278|4.825|1.631|
|headers|1.515|3.866|1.153| |horizontalRule|0.159|0.276|0.148|
|horizontalRule|0.216|0.293|0.194| |images|0.159|0.390|0.124|
|images|0.144|0.286|0.124| |italicsAndBold|0.280|0.773|0.211|
|italicsAndBold|0.234|0.656|0.190| |lists|4.253|8.173|3.146|
|lists|4.483|7.664|2.482| |outdent|0.181|0.238|0.162|
|outdent|0.286|0.538|0.179| |paragraphs|8.968|11.331|7.857|
|paragraphs|10.257|18.656|5.229| |spanGamut|2.985|4.162|2.486|
|spanGamut|10.288|31.124|6.102| |strikethrough|0.007|0.099|0.001|
|strikethrough|0.007|0.106|0.001| |stripLinkDefinitions|1.911|2.855|1.447|
|stripLinkDefinitions|0.438|0.678|0.392| |tables|0.008|0.127|0.002|
|tables|0.007|0.096|0.001| |unescapeSpecialChars|0.013|0.073|0.010|
|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|

View File

@ -1,45 +1,333 @@
/** /**
* Created by tivie * 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'); // require shodown. We use conditional loading for each use case
if (fs.existsSync(path1)) {
yargs // production. File lives in bin directory
.version() showdown = require(path1);
.alias('v', 'version') version = require(path.resolve(__dirname + '/../package.json')).version;
.option('h', { } else if (fs.existsSync(path2)) {
alias: 'help', // testing envo, uses the concatenated stuff for testing
description: 'Show help' showdown = require(path2);
}) version = require(path.resolve(__dirname + '/../../package.json')).version;
.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();
} else { } 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);

View File

@ -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
};

View File

@ -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;

View File

@ -197,7 +197,7 @@ showdown.Converter = function (converterOptions) {
if (typeof callback !== 'function') { if (typeof callback !== 'function') {
throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
} }
name = name.toLowerCase();
if (!listeners.hasOwnProperty(name)) { if (!listeners.hasOwnProperty(name)) {
listeners[name] = []; listeners[name] = [];
} }
@ -211,24 +211,33 @@ showdown.Converter = function (converterOptions) {
} }
/** /**
* Dispatch an event *
* @private
* @param {string} evtName Event name * @param {string} evtName Event name
* @param {string} text Text * @param {string} text Text
* @param {{}} options Converter Options * @param {{}} options Converter Options
* @param {{}} globals * @param {{}} globals Converter globals
* @returns {string} * @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)) { if (listeners.hasOwnProperty(evtName)) {
for (var ei = 0; ei < listeners[evtName].length; ++ei) { 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') { 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 * @param {string} text
* @returns {*} * @returns {*}
*/ */
@ -298,7 +307,7 @@ showdown.Converter = function (converterOptions) {
text = '\n\n' + text + '\n\n'; text = '\n\n' + text + '\n\n';
// detab // 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. * Strip any lines consisting only of spaces and tabs.
@ -310,19 +319,19 @@ showdown.Converter = function (converterOptions) {
//run languageExtensions //run languageExtensions
showdown.helper.forEach(langExtensions, function (ext) { 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 // run the sub parsers
text = showdown.subParser('metadata')(text, options, globals); text = showdown.subParser('makehtml.metadata')(text, options, globals);
text = showdown.subParser('hashPreCodeTags')(text, options, globals); text = showdown.subParser('makehtml.hashPreCodeTags')(text, options, globals);
text = showdown.subParser('githubCodeBlocks')(text, options, globals); text = showdown.subParser('makehtml.githubCodeBlocks')(text, options, globals);
text = showdown.subParser('hashHTMLBlocks')(text, options, globals); text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals);
text = showdown.subParser('hashCodeTags')(text, options, globals); text = showdown.subParser('makehtml.hashCodeTags')(text, options, globals);
text = showdown.subParser('stripLinkDefinitions')(text, options, globals); text = showdown.subParser('makehtml.stripLinkDefinitions')(text, options, globals);
text = showdown.subParser('blockGamut')(text, options, globals); text = showdown.subParser('makehtml.blockGamut')(text, options, globals);
text = showdown.subParser('unhashHTMLSpans')(text, options, globals); text = showdown.subParser('makehtml.unhashHTMLSpans')(text, options, globals);
text = showdown.subParser('unescapeSpecialChars')(text, options, globals); text = showdown.subParser('makehtml.unescapeSpecialChars')(text, options, globals);
// attacklab: Restore dollar signs // attacklab: Restore dollar signs
text = text.replace(/¨D/g, '$$'); text = text.replace(/¨D/g, '$$');
@ -331,11 +340,11 @@ showdown.Converter = function (converterOptions) {
text = text.replace(/¨T/g, '¨'); text = text.replace(/¨T/g, '¨');
// render a complete html document instead of a partial if the option is enabled // 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 // Run output modifiers
showdown.helper.forEach(outputModifiers, function (ext) { 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 // update metadata
@ -346,10 +355,9 @@ showdown.Converter = function (converterOptions) {
/** /**
* Converts an HTML string into a markdown string * Converts an HTML string into a markdown string
* @param src * @param src
* @param [HTMLParser] A WHATWG DOM and HTML parser, such as JSDOM. If none is supplied, window.document will be used.
* @returns {string} * @returns {string}
*/ */
this.makeMarkdown = this.makeMd = function (src, HTMLParser) { this.makeMarkdown = function (src) {
// replace \r\n with \n // replace \r\n with \n
src = src.replace(/\r\n/g, '\n'); src = src.replace(/\r\n/g, '\n');
@ -360,15 +368,7 @@ showdown.Converter = function (converterOptions) {
// ex: <em>this is</em> <strong>sparta</strong> // ex: <em>this is</em> <strong>sparta</strong>
src = src.replace(/>[ \t]+</, '>¨NBSP;<'); src = src.replace(/>[ \t]+</, '>¨NBSP;<');
if (!HTMLParser) { var doc = showdown.helper.document.createElement('div');
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');
doc.innerHTML = src; doc.innerHTML = src;
var globals = { var globals = {
@ -386,7 +386,7 @@ showdown.Converter = function (converterOptions) {
mdDoc = ''; mdDoc = '';
for (var i = 0; i < nodes.length; i++) { 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) { function clean (node) {

File diff suppressed because it is too large Load Diff

View File

@ -51,11 +51,6 @@ function getDefaultOpts (simple) {
describe: 'Turn on/off GFM autolink style', describe: 'Turn on/off GFM autolink style',
type: 'boolean' type: 'boolean'
}, },
excludeTrailingPunctuationFromURLs: {
defaultValue: false,
describe: 'Excludes trailing punctuation from links generated with autoLinking',
type: 'boolean'
},
literalMidWordUnderscores: { literalMidWordUnderscores: {
defaultValue: false, defaultValue: false,
describe: 'Parse midword underscores as literal underscores', describe: 'Parse midword underscores as literal underscores',
@ -98,79 +93,89 @@ function getDefaultOpts (simple) {
}, },
smartIndentationFix: { smartIndentationFix: {
defaultValue: false, defaultValue: false,
description: 'Tries to smartly fix indentation in es6 strings', describe: 'Tries to smartly fix indentation in es6 strings',
type: 'boolean' type: 'boolean'
}, },
disableForced4SpacesIndentedSublists: { disableForced4SpacesIndentedSublists: {
defaultValue: false, 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' type: 'boolean'
}, },
simpleLineBreaks: { simpleLineBreaks: {
defaultValue: false, defaultValue: false,
description: 'Parses simple line breaks as <br> (GFM Style)', describe: 'Parses simple line breaks as <br> (GFM Style)',
type: 'boolean' type: 'boolean'
}, },
requireSpaceBeforeHeadingText: { requireSpaceBeforeHeadingText: {
defaultValue: false, 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' type: 'boolean'
}, },
ghMentions: { ghMentions: {
defaultValue: false, defaultValue: false,
description: 'Enables github @mentions', describe: 'Enables github @mentions',
type: 'boolean' type: 'boolean'
}, },
ghMentionsLink: { ghMentionsLink: {
defaultValue: 'https://github.com/{u}', 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' type: 'string'
}, },
encodeEmails: { encodeEmails: {
defaultValue: true, 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' type: 'boolean'
}, },
openLinksInNewWindow: { openLinksInNewWindow: {
defaultValue: false, defaultValue: false,
description: 'Open all links in new windows', describe: 'Open all links in new windows',
type: 'boolean' type: 'boolean'
}, },
backslashEscapesHTMLTags: { backslashEscapesHTMLTags: {
defaultValue: false, defaultValue: false,
description: 'Support for HTML Tag escaping. ex: \<div>foo\</div>', describe: 'Support for HTML Tag escaping. ex: \<div>foo\</div>',
type: 'boolean' type: 'boolean'
}, },
emoji: { emoji: {
defaultValue: false, defaultValue: false,
description: 'Enable emoji support. Ex: `this is a :smile: emoji`', describe: 'Enable emoji support. Ex: `this is a :smile: emoji`',
type: 'boolean' type: 'boolean'
}, },
underline: { underline: {
defaultValue: false, 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' type: 'boolean'
}, },
ellipsis: { ellipsis: {
defaultValue: true, defaultValue: true,
description: 'Replaces three dots with the ellipsis unicode character', describe: 'Replaces three dots with the ellipsis unicode character',
type: 'boolean' type: 'boolean'
}, },
completeHTMLDocument: { completeHTMLDocument: {
defaultValue: false, 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' type: 'boolean'
}, },
metadata: { metadata: {
defaultValue: false, 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' type: 'boolean'
}, },
splitAdjacentBlockquotes: { splitAdjacentBlockquotes: {
defaultValue: false, defaultValue: false,
description: 'Split adjacent blockquote blocks', describe: 'Split adjacent blockquote blocks',
type: 'boolean' 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) { if (simple === false) {
return JSON.parse(JSON.stringify(defaultOptions)); return JSON.parse(JSON.stringify(defaultOptions));

View File

@ -1,7 +1,6 @@
/** /**
* Created by Tivie on 06-01-2015. * Created by Tivie on 06-01-2015.
*/ */
// Private properties // Private properties
var showdown = {}, var showdown = {},
parsers = {}, parsers = {},
@ -12,7 +11,6 @@ var showdown = {},
github: { github: {
omitExtraWLInCodeBlocks: true, omitExtraWLInCodeBlocks: true,
simplifiedAutoLink: true, simplifiedAutoLink: true,
excludeTrailingPunctuationFromURLs: true,
literalMidWordUnderscores: true, literalMidWordUnderscores: true,
strikethrough: true, strikethrough: true,
tables: true, tables: true,
@ -36,7 +34,6 @@ var showdown = {},
omitExtraWLInCodeBlocks: true, omitExtraWLInCodeBlocks: true,
parseImgDimensions: true, parseImgDimensions: true,
simplifiedAutoLink: true, simplifiedAutoLink: true,
excludeTrailingPunctuationFromURLs: true,
literalMidWordUnderscores: true, literalMidWordUnderscores: true,
strikethrough: true, strikethrough: true,
tables: true, tables: true,
@ -181,6 +178,8 @@ showdown.subParser = function (name, func) {
throw Error('SubParser named ' + name + ' not registered!'); 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 * Gets or registers an extension
* @static * @static
* @param {string} name * @param {string} name
* @param {object|function=} ext * @param {object|object[]|function=} ext
* @returns {*} * @returns {*}
*/ */
showdown.extension = function (name, ext) { showdown.extension = function (name, ext) {

View File

@ -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, '&quot;');
//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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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();
});

View 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;
});

View File

@ -1,7 +1,7 @@
showdown.subParser('blockQuotes', function (text, options, globals) { showdown.subParser('makehtml.blockQuotes', function (text, options, globals) {
'use strict'; '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 // add a couple extra lines after the text and endtext mark
text = text + '\n\n'; text = text + '\n\n';
@ -21,8 +21,8 @@ showdown.subParser('blockQuotes', function (text, options, globals) {
bq = bq.replace(/¨0/g, ''); bq = bq.replace(/¨0/g, '');
bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); bq = showdown.subParser('makehtml.githubCodeBlocks')(bq, options, globals);
bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse bq = showdown.subParser('makehtml.blockGamut')(bq, options, globals); // recurse
bq = bq.replace(/(^|\n)/g, '$1 '); bq = bq.replace(/(^|\n)/g, '$1 ');
// These leading spaces screw with <pre> content, so we need to fix that: // 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 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; return text;
}); });

View File

@ -1,10 +1,10 @@
/** /**
* Process Markdown `<pre><code>` blocks. * Process Markdown `<pre><code>` blocks.
*/ */
showdown.subParser('codeBlocks', function (text, options, globals) { showdown.subParser('makehtml.codeBlocks', function (text, options, globals) {
'use strict'; '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 // sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += '¨0'; text += '¨0';
@ -15,9 +15,9 @@ showdown.subParser('codeBlocks', function (text, options, globals) {
nextChar = m2, nextChar = m2,
end = '\n'; end = '\n';
codeblock = showdown.subParser('outdent')(codeblock, options, globals); codeblock = showdown.subParser('makehtml.outdent')(codeblock, options, globals);
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals); codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(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 leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing 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>'; 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 // strip sentinel
text = text.replace(/¨0/, ''); 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; return text;
}); });

View File

@ -23,10 +23,10 @@
* *
* ... type <code>`bar`</code> ... * ... type <code>`bar`</code> ...
*/ */
showdown.subParser('codeSpans', function (text, options, globals) { showdown.subParser('makehtml.codeSpans', function (text, options, globals) {
'use strict'; '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') { if (typeof (text) === 'undefined') {
text = ''; text = '';
@ -36,13 +36,13 @@ showdown.subParser('codeSpans', function (text, options, globals) {
var c = m3; var c = m3;
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing 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 = m1 + '<code>' + c + '</code>';
c = showdown.subParser('hashHTMLSpans')(c, options, globals); c = showdown.subParser('makehtml.hashHTMLSpans')(c, options, globals);
return c; return c;
} }
); );
text = globals.converter._dispatch('codeSpans.after', text, options, globals); text = globals.converter._dispatch('makehtml.codeSpans.after', text, options, globals).getText();
return text; return text;
}); });

View File

@ -1,14 +1,14 @@
/** /**
* Create a full HTML document from the processed markdown * 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'; 'use strict';
if (!options.completeHTMLDocument) { if (!options.completeHTMLDocument) {
return text; 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', var doctype = 'html',
doctypeParsed = '<!DOCTYPE HTML>\n', 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 = 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; return text;
}); });

View File

@ -1,9 +1,9 @@
/** /**
* Convert all tabs to spaces * Convert all tabs to spaces
*/ */
showdown.subParser('detab', function (text, options, globals) { showdown.subParser('makehtml.detab', function (text, options, globals) {
'use strict'; '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 // expand first n-1 tabs
text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width 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(/¨A/g, ' '); // g_tab_width
text = text.replace(/¨B/g, ''); 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; return text;
}); });

View 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;
});

View File

@ -3,14 +3,14 @@
* *
* List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis * 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'; 'use strict';
if (!options.emoji) { if (!options.emoji) {
return text; 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; var emojiRgx = /:([\S]+?):/g;
@ -21,7 +21,7 @@ showdown.subParser('emoji', function (text, options, globals) {
return wm; return wm;
}); });
text = globals.converter._dispatch('emoji.after', text, options, globals); text = globals.converter._dispatch('makehtml.emoji.after', text, options, globals).getText();
return text; return text;
}); });

View File

@ -1,9 +1,9 @@
/** /**
* Smart processing for ampersands and angle brackets that need to be encoded. * 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'; '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: // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
// http://bumppo.net/projects/amputator/ // http://bumppo.net/projects/amputator/
@ -18,6 +18,6 @@ showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
// Encode > // Encode >
text = text.replace(/>/g, '&gt;'); text = text.replace(/>/g, '&gt;');
text = globals.converter._dispatch('encodeAmpsAndAngles.after', text, options, globals); text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.after', text, options, globals).getText();
return text; return text;
}); });

View File

@ -9,13 +9,13 @@
* ...but we're sidestepping its use of the (slow) RegExp constructor * ...but we're sidestepping its use of the (slow) RegExp constructor
* as an optimization for Firefox. This function gets called a LOT. * 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'; '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 = 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; return text;
}); });

View File

@ -3,10 +3,10 @@
* The point is that in code, these characters are literals, * The point is that in code, these characters are literals,
* and lose their special Markdown meanings. * and lose their special Markdown meanings.
*/ */
showdown.subParser('encodeCode', function (text, options, globals) { showdown.subParser('makehtml.encodeCode', function (text, options, globals) {
'use strict'; '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 // Encode all ampersands; HTML entities are not
// entities within a Markdown code span. // 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: // Now, escape characters that are magic in Markdown:
.replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback); .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; return text;
}); });

View File

@ -2,9 +2,9 @@
* Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they * Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they
* don't conflict with their use in Markdown for code, italics and strong. * 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'; '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. // Build a regex to find HTML tags.
var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi, var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
@ -21,6 +21,6 @@ showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text, opti
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback); .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; return text;
}); });

View File

@ -8,7 +8,7 @@
* end * end
* ``` * ```
*/ */
showdown.subParser('githubCodeBlocks', function (text, options, globals) { showdown.subParser('makehtml.githubCodeBlocks', function (text, options, globals) {
'use strict'; 'use strict';
// early exit if option is not enabled // early exit if option is not enabled
@ -16,22 +16,26 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
return text; 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 += '¨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'; 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 // First parse the github code block
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals); codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(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 leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>'; 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 // Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var, // 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 // attacklab: strip sentinel
text = text.replace(/¨0/, ''); text = text.replace(/¨0/, '');
return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals); return globals.converter._dispatch('makehtml.githubCodeBlocks.after', text, options, globals).getText();
}); });

View 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;
});

View 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;
});

View File

@ -1,4 +1,4 @@
showdown.subParser('hashElement', function (text, options, globals) { showdown.subParser('makehtml.hashElement', function (text, options, globals) {
'use strict'; 'use strict';
return function (wholeMatch, m1) { return function (wholeMatch, m1) {

View File

@ -1,6 +1,6 @@
showdown.subParser('hashHTMLBlocks', function (text, options, globals) { showdown.subParser('makehtml.hashHTMLBlocks', function (text, options, globals) {
'use strict'; 'use strict';
text = globals.converter._dispatch('hashHTMLBlocks.before', text, options, globals); text = globals.converter._dispatch('makehtml.hashHTMLBlocks.before', text, options, globals).getText();
var blockTags = [ var blockTags = [
'pre', 'pre',
@ -36,6 +36,7 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
'hgroup', 'hgroup',
'output', 'output',
'video', 'video',
'details',
'p' 'p'
], ],
repFunc = function (wholeMatch, match, left, right) { repFunc = function (wholeMatch, match, left, right) {
@ -82,7 +83,7 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
} }
// HR SPECIAL CASE // HR SPECIAL CASE
text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, 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 // Special case for standalone HTML comments
text = showdown.helper.replaceRecursiveRegExp(text, function (txt) { text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
@ -90,9 +91,9 @@ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
}, '^ {0,3}<!--', '-->', 'gm'); }, '^ {0,3}<!--', '-->', 'gm');
// PHP and ASP-style processor instructions (<?...?> and <%...%>) // PHP and ASP-style processor instructions (<?...?> and <%...%>)
text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, text = text.replace(/\n\n( {0,3}<([?%])[^\r]*?\2>[ \t]*(?=\n{2,}))/g,
showdown.subParser('hashElement')(text, options, globals)); 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; return text;
}); });

View File

@ -1,46 +1,40 @@
/** /**
* Hash span elements that should not be parsed as markdown * 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'; 'use strict';
text = globals.converter._dispatch('hashHTMLSpans.before', text, options, globals); text = globals.converter._dispatch('makehtml.hashHTMLSpans.before', text, options, globals).getText();
function hashHTMLSpan (html) {
return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
}
// Hash Self Closing tags // Hash Self Closing tags
text = text.replace(/<[^>]+?\/>/gi, function (wm) { text = text.replace(/<[^>]+?\/>/gi, function (wm) {
return hashHTMLSpan(wm); return showdown.helper._hashHTMLSpan(wm, globals);
}); });
// Hash tags without properties // Hash tags without properties
text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) { text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
return hashHTMLSpan(wm); return showdown.helper._hashHTMLSpan(wm, globals);
}); });
// Hash tags with properties // Hash tags with properties
text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) { text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
return hashHTMLSpan(wm); return showdown.helper._hashHTMLSpan(wm, globals);
}); });
// Hash self closing tags without /> // Hash self closing tags without />
text = text.replace(/<[^>]+?>/gi, function (wm) { 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('makehtml.hashHTMLSpans.after', text, options, globals).getText();
text = globals.converter._dispatch('hashHTMLSpans.after', text, options, globals);
return text; return text;
}); });
/** /**
* Unhash HTML spans * Unhash HTML spans
*/ */
showdown.subParser('unhashHTMLSpans', function (text, options, globals) { showdown.subParser('makehtml.unhashHTMLSpans', function (text, options, globals) {
'use strict'; '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) { for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
var repText = globals.gHtmlSpans[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 = 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; return text;
}); });

View File

@ -1,19 +1,19 @@
/** /**
* Hash and escape <pre><code> elements that should not be parsed as markdown * 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'; '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) { var repFunc = function (wholeMatch, match, left, right) {
// encode html entities // 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'; return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
}; };
// Hash <pre><code> // Hash <pre><code>
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim'); 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; return text;
}); });

View File

@ -1,7 +1,7 @@
showdown.subParser('headers', function (text, options, globals) { showdown.subParser('makehtml.headers', function (text, options, globals) {
'use strict'; '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), 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) { 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) + '"', hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
hLevel = headerLevelStart, hLevel = headerLevelStart,
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>'; 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) { 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) + '"', hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
hLevel = headerLevelStart + 1, hLevel = headerLevelStart + 1,
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>'; 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: // atx-style headers:
@ -44,15 +44,15 @@ showdown.subParser('headers', function (text, options, globals) {
text = text.replace(atxStyle, function (wholeMatch, m1, m2) { text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
var hText = m2; var hText = m2;
if (options.customizedHeaderId) { 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) + '"', hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
hLevel = headerLevelStart - 1 + m1.length, hLevel = headerLevelStart - 1 + m1.length,
header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>'; 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) { 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 // It is separate from other options to allow combining prefix and customized
if (options.customizedHeaderId) { if (options.customizedHeaderId) {
var match = m.match(/\{([^{]+?)}\s*$/); var match = m.match(/{([^{]+?)}\s*$/);
if (match && match[1]) { if (match && match[1]) {
m = match[1]; m = match[1];
} }
@ -121,6 +121,6 @@ showdown.subParser('headers', function (text, options, globals) {
return title; return title;
} }
text = globals.converter._dispatch('headers.after', text, options, globals); text = globals.converter._dispatch('makehtml.headers.after', text, options, globals).getText();
return text; return text;
}); });

View 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;
});

View File

@ -1,10 +1,10 @@
/** /**
* Turn Markdown image shortcuts into <img> tags. * Turn Markdown image shortcuts into <img> tags.
*/ */
showdown.subParser('images', function (text, options, globals) { showdown.subParser('makehtml.images', function (text, options, globals) {
'use strict'; '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, 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, 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); return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
} }
function writeImageTagBaseUrl (wholeMatch, altText, linkId, url, width, height, m5, title) {
url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
}
function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
var gUrls = globals.gUrls, var gUrls = globals.gUrls,
@ -56,16 +62,16 @@ showdown.subParser('images', function (text, options, globals) {
altText = altText altText = altText
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')
//altText = showdown.helper.escapeCharacters(altText, '*_', false); //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 = 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 + '"'; var result = '<img src="' + url + '" alt="' + altText + '"';
if (title && showdown.helper.isString(title)) { if (title && showdown.helper.isString(title)) {
title = title title = title
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')
//title = showdown.helper.escapeCharacters(title, '*_', false); //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 + '"'; result += ' title="' + title + '"';
} }
@ -91,14 +97,14 @@ showdown.subParser('images', function (text, options, globals) {
text = text.replace(base64RegExp, writeImageTagBase64); text = text.replace(base64RegExp, writeImageTagBase64);
// cases with crazy urls like ./image/cat1).png // cases with crazy urls like ./image/cat1).png
text = text.replace(crazyRegExp, writeImageTag); text = text.replace(crazyRegExp, writeImageTagBaseUrl);
// normal cases // normal cases
text = text.replace(inlineRegExp, writeImageTag); text = text.replace(inlineRegExp, writeImageTagBaseUrl);
// handle reference-style shortcuts: ![img text] // handle reference-style shortcuts: ![img text]
text = text.replace(refShortcutRegExp, writeImageTag); text = text.replace(refShortcutRegExp, writeImageTag);
text = globals.converter._dispatch('images.after', text, options, globals); text = globals.converter._dispatch('makehtml.images.after', text, options, globals).getText();
return text; return text;
}); });

View File

@ -1,18 +1,13 @@
showdown.subParser('italicsAndBold', function (text, options, globals) { showdown.subParser('makehtml.italicsAndBold', function (text, options, globals) {
'use strict'; '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 // 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 // because of backtracing, in some cases, it could lead to an exponential effect
// called "catastrophic backtrace". Ominous! // called "catastrophic backtrace". Ominous!
function parseInside (txt, left, right) { function parseInside (txt, left, right) {
/*
if (options.simplifiedAutoLink) {
txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
}
*/
return left + txt + right; return left + txt + right;
} }
@ -41,30 +36,31 @@ showdown.subParser('italicsAndBold', function (text, options, globals) {
} }
// Now parse asterisks // Now parse asterisks
/*
if (options.literalMidWordAsterisks) { 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>'); 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>'); 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>'); return parseInside (txt, lead + '<em>', '</em>');
}); });
} else { } 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><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) {
}); 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) text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm; // !/^\*[^*]/.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('makehtml.italicsAndBold.after', text, options, globals).getText();
text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
return text; return text;
}); });

View 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, '&quot;');
//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;
});
})();

View File

@ -1,7 +1,7 @@
/** /**
* Form HTML ordered (numbered) and unordered (bulleted) lists. * 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'; 'use strict';
/** /**
@ -40,26 +40,32 @@ showdown.subParser('lists', function (text, options, globals) {
// attacklab: add sentinel to emulate \z // attacklab: add sentinel to emulate \z
listStr += '¨0'; listStr += '¨0';
var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm, var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[([xX ])])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr)); isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
// Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation, // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
// which is a syntax breaking change // which is a syntax breaking change
// activating this option reverts to old behavior // activating this option reverts to old behavior
// This will be removed in version 2.0
if (options.disableForced4SpacesIndentedSublists) { if (options.disableForced4SpacesIndentedSublists) {
rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm; rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[([xX ])])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
} }
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
checked = (checked && checked.trim() !== ''); checked = (checked && checked.trim() !== '');
var item = showdown.subParser('outdent')(m4, options, globals), var item = showdown.subParser('makehtml.outdent')(m4, options, globals),
bulletStyle = ''; bulletStyle = '';
// Support for github tasklists // Support for github tasklists
if (taskbtn && options.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;"'; var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
if (checked) { if (checked) {
otp += ' checked'; otp += ' checked';
@ -81,24 +87,77 @@ showdown.subParser('lists', function (text, options, globals) {
return '¨A' + wm2; 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 // m1 - Leading line or
// Has a double return (multi paragraph) or // Has a double return (multi paragraph)
// Has sublist
if (m1 || (item.search(/\n{2,}/) > -1)) { if (m1 || (item.search(/\n{2,}/) > -1)) {
item = showdown.subParser('githubCodeBlocks')(item, options, globals); item = showdown.subParser('makehtml.githubCodeBlocks')(item, options, globals);
item = showdown.subParser('blockGamut')(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 { } else {
// Recursion for sub-lists: // 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 = item.replace(/\n$/, ''); // chomp(item)
item = showdown.subParser('hashHTMLBlocks')(item, options, globals); item = showdown.subParser('makehtml.hashHTMLBlocks')(item, options, globals);
// Colapse double linebreaks // Colapse double linebreaks
item = item.replace(/\n\n+/g, '\n\n'); item = item.replace(/\n\n+/g, '\n\n');
if (isParagraphed) { if (isParagraphed) {
item = showdown.subParser('paragraphs')(item, options, globals); item = showdown.subParser('makehtml.paragraphs')(item, options, globals);
} else { } 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; return result;
} }
/** Start of list parsing **/ // Start of list parsing
text = globals.converter._dispatch('lists.before', text, options, globals); 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: // add sentinel to hack around khtml/safari bug:
// http://bugs.webkit.org/show_bug.cgi?id=11231 // http://bugs.webkit.org/show_bug.cgi?id=11231
text += '¨0'; text += '¨0';
if (globals.gListLevel) { if (globals.gListLevel) {
text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, text = text.replace(subListRgx, function (wholeMatch, list, m2) {
function (wholeMatch, list, m2) { var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; return parseConsecutiveLists(list, listType, true);
return parseConsecutiveLists(list, listType, true); });
}
);
} else { } else {
text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm, text = text.replace(mainListRgx, function (wholeMatch, m1, list, m3) {
function (wholeMatch, m1, list, m3) { var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; return parseConsecutiveLists(list, listType, false);
return parseConsecutiveLists(list, listType, false); });
}
);
} }
// strip sentinel // strip sentinel
text = text.replace(/¨0/, ''); 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; return text;
}); });

View File

@ -1,14 +1,14 @@
/** /**
* Parse metadata at the top of the document * 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'; 'use strict';
if (!options.metadata) { if (!options.metadata) {
return text; 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) { function parseMetadataContents (content) {
// raw is raw so it's not changed in any way // raw is raw so it's not changed in any way
@ -22,6 +22,11 @@ showdown.subParser('metadata', function (text, options, globals) {
// double quotes // double quotes
.replace(/"/g, '&quot;'); .replace(/"/g, '&quot;');
// Restore dollar signs and tremas
content = content
.replace(/¨D/g, '$$')
.replace(/¨T/g, '¨');
content = content.replace(/\n {4}/g, ' '); content = content.replace(/\n {4}/g, ' ');
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) { content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
globals.metadata.parsed[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); parseMetadataContents(content);
return '¨M'; 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) { if (format) {
globals.metadata.format = format; globals.metadata.format = format;
} }
@ -44,6 +49,6 @@ showdown.subParser('metadata', function (text, options, globals) {
text = text.replace(/¨M/g, ''); 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; return text;
}); });

View File

@ -1,9 +1,9 @@
/** /**
* Remove one level of line-leading tabs or spaces * 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'; '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: // attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug" // "----------bug".replace(/^-/g,"") == "bug"
@ -12,6 +12,6 @@ showdown.subParser('outdent', function (text, options, globals) {
// attacklab: clean up hack // attacklab: clean up hack
text = text.replace(/¨0/g, ''); 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; return text;
}); });

View File

@ -1,10 +1,10 @@
/** /**
* *
*/ */
showdown.subParser('paragraphs', function (text, options, globals) { showdown.subParser('makehtml.paragraphs', function (text, options, globals) {
'use strict'; '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: // Strip leading and trailing lines:
text = text.replace(/^\n+/g, ''); text = text.replace(/^\n+/g, '');
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 // test for presence of characters to prevent empty lines being parsed
// as paragraphs (resulting in undesired extra empty paragraphs) // as paragraphs (resulting in undesired extra empty paragraphs)
} else if (str.search(/\S/) >= 0) { } 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 = str.replace(/^([ \t]*)/g, '<p>');
str += '</p>'; str += '</p>';
grafsOut.push(str); grafsOut.push(str);
@ -47,7 +47,7 @@ showdown.subParser('paragraphs', function (text, options, globals) {
// we need to check if ghBlock is a false positive // we need to check if ghBlock is a false positive
if (codeFlag) { if (codeFlag) {
// use encoded version of all text // 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 { } else {
blockText = globals.ghCodeBlocks[num].codeblock; blockText = globals.ghCodeBlocks[num].codeblock;
} }
@ -66,5 +66,5 @@ showdown.subParser('paragraphs', function (text, options, globals) {
// Strip leading and trailing lines: // Strip leading and trailing lines:
text = text.replace(/^\n+/g, ''); text = text.replace(/^\n+/g, '');
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();
}); });

View File

@ -1,7 +1,7 @@
/** /**
* Run extension * Run extension
*/ */
showdown.subParser('runExtension', function (ext, text, options, globals) { showdown.subParser('makehtml.runExtension', function (ext, text, options, globals) {
'use strict'; 'use strict';
if (ext.filter) { if (ext.filter) {

View 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;
});

View 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;
});

View File

@ -3,7 +3,7 @@
* hash references. * hash references.
* Link defs are in the form: ^[id]: url "optional title" * 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'; 'use strict';
var regex = /^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm, var regex = /^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
@ -23,7 +23,9 @@ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
// remove newlines // remove newlines
globals.gUrls[linkId] = url.replace(/\s/g, ''); globals.gUrls[linkId] = url.replace(/\s/g, '');
} else { } 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) { if (blankLines) {

View File

@ -1,13 +1,13 @@
showdown.subParser('tables', function (text, options, globals) { showdown.subParser('makehtml.tables', function (text, options, globals) {
'use strict'; 'use strict';
if (!options.tables) { if (!options.tables) {
return text; 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}\|.+\|\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) { function parseStyles (sLine) {
if (/^:[ \t]*--*$/.test(sLine)) { if (/^:[ \t]*--*$/.test(sLine)) {
@ -28,13 +28,13 @@ showdown.subParser('tables', function (text, options, globals) {
if (options.tablesHeaderId || options.tableHeaderId) { if (options.tablesHeaderId || options.tableHeaderId) {
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; 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'; return '<th' + id + style + '>' + header + '</th>\n';
} }
function parseCells (cell, style) { 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'; return '<td' + style + '>' + subText + '</td>\n';
} }
@ -70,7 +70,8 @@ showdown.subParser('tables', function (text, options, globals) {
tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
} }
// parse code spans first, but we only support one line code spans // 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();}), 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); 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 // find escaped pipe characters
text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback); text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback);
@ -136,7 +137,7 @@ showdown.subParser('tables', function (text, options, globals) {
// parse one column tables // parse one column tables
text = text.replace(singeColTblRgx, parseTable); 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; return text;
}); });

View File

@ -1,11 +1,11 @@
showdown.subParser('underline', function (text, options, globals) { showdown.subParser('makehtml.underline', function (text, options, globals) {
'use strict'; 'use strict';
if (!options.underline) { if (!options.underline) {
return text; 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) { if (options.literalMidWordUnderscores) {
text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) { 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 // escape remaining underscores to prevent them being parsed by italic and bold
text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback); 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; return text;
}); });

View 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