Merge remote-tracking branch 'remotes/upstream/master'

Conflicts:
	src/extensions/table.js
This commit is contained in:
Yanis Wang 2015-08-26 14:08:00 +08:00
commit 8efe006160
421 changed files with 5748 additions and 6750 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
[*.js]
indent_style = space
indent_size = 2
continuation_indent_size = 2
insert_final_newline = true
quote_type = single
space_after_anonymous_functions = true
space_after_control_statements = true
spaces_around_operators = true
trim_trailing_whitespace = true
spaces_in_brackets = false
curly_bracket_next_line = true
indent_brace_style = 1TBS
end_of_line = lf
charset = utf-8

11
.gitattributes vendored
View File

@ -1,7 +1,10 @@
/test export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
/perlMarkdown export-ignore
/example export-ignore
grunt.js export-ignore
.jscs.json export-ignore
.jshintignore export-ignore
.jshintrc
.travis.yml export-ignore
bower.json
Gruntfile.js export-ignore

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
.idea/
.build/
.DS_Store
node_modules
npm-debug.log
npm-debug.log
/*.test.*

90
.jscs.json Normal file
View File

@ -0,0 +1,90 @@
{
"validateIndentation": 2,
"requireCurlyBraces": [
"if",
"else",
"for",
"while",
"do",
"try",
"catch"
],
"requireOperatorBeforeLineBreak": true,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"validateIndentation": 2,
"validateQuoteMarks": "'",
"disallowMultipleLineStrings": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"requireMultipleVarDecl": true,
"disallowKeywordsOnNewLine": ["else"],
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"return",
"try",
"catch"
],
"requireSpaceBeforeBinaryOperators": [
"=",
"+=",
"-=",
"*=",
"/=",
"%=",
"<<=",
">>=",
">>>=",
"&=",
"|=",
"^=",
"+=",
"+",
"-",
"*",
"/",
"%",
"<<",
">>",
">>>",
"&",
"|",
"^",
"&&",
"||",
"===",
"==",
">=",
"<=",
"<",
">",
"!=",
"!=="
],
"requireSpaceAfterBinaryOperators": true,
"requireSpacesInConditionalExpression": true,
"requireSpaceBeforeBlockStatements": true,
"requireSpacesInForStatement": true,
"requireLineFeedAtFileEnd": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"requireSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true,
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideObjectBrackets": "all",
"disallowSpacesInsideArrayBrackets": "all",
"disallowSpacesInsideParentheses": true,
"validateJSDoc": {
"checkParamNames": true,
"requireParamTypes": true
},
"disallowMultipleLineBreaks": true,
"disallowNewlineBeforeBlockStatements": true
}

5
.jshintignore Normal file
View File

@ -0,0 +1,5 @@
Gruntfile.js
dist/**/*.js
build/**/*.js
src/options.js
bin/*

28
.jshintrc Normal file
View File

@ -0,0 +1,28 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"quotmark": "single",
"undef": false,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"onevar": true,
"globals": {
"angular": true,
"module": true,
"define": true,
"window": true,
"showdown": true
}
}

View File

@ -1,4 +1,25 @@
language: node_js
node_js:
- 0.6
- 0.8
- "0.8"
- "0.10"
- "0.12"
before_install:
- '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm@1.4.28'
- npm install -g npm@latest
- npm install -g grunt-cli
#travis build speed up
sudo: false
cache:
directories:
- node_modules
# hooks
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e369617839852624aa69
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

162
CHANGELOG.md Normal file
View File

@ -0,0 +1,162 @@
<a name"1.2.2"></a>
### 1.2.2 (2015-08-02)
#### Bug Fixes
* **lists:** fix github code blocks not being parsed inside lists ([7720c88b](http://github.com/showdownjs/showdown/commit/7720c88b), closes [#142](http://github.com/showdownjs/showdown/issues/142), [#183](http://github.com/showdownjs/showdown/issues/183), [#184](http://github.com/showdownjs/showdown/issues/184))
<a name"1.2.1"></a>
### 1.2.1 (2015-07-22)
#### Features
* **smoothLivePreview:** fix weird effects due to parsing incomplete input ([62ba3733](http://github.com/showdownjs/showdown/commit/62ba3733))
* **subParsers/githubCodeBlock:** add extra language class to conform to html5 spec ([b7f5e32](http://github.com/showdownjs/showdown/commit/b7f5e32))
#### Bug Fixes
* **tables:**
* fix undefined error in malformed tables ([6176977](http://github.com/showdownjs/showdown/commit/6176977))
Cannot read property 'trim' of undefined happens when the parser is fed a malformed table.
This happens in live previews (for instance, when using Angularjs).
* add support for md span elements in table headers ([789dc18](http://github.com/showdownjs/showdown/commit/789dc18))
Closes #179
* **italicsAndBold:**
* fix broken em/strong tags when used with literalMidWordUnderscores ([7ee2017](http://github.com/showdownjs/showdown/commit/7ee2017))
When literalMidWordUnderscoresis set to true, em and strong tags that start or end a paragraph don't get parsed as such.
This fixes this issue.
Closes #174
* fix underscores not being correctly parsed when used in conjunction with literalMidWordsUnderscores option ([c9e85f1](http://github.com/showdownjs/showdown/commit/c9e85f1))
* **codeSpans:** Fix issue with code html tags not being correctly escaped ([5f043ca](http://github.com/showdownjs/showdown/commit/5f043ca))
* **images:** fix alt attribute not being escaped correctly ([542194e](http://github.com/showdownjs/showdown/commit/542194e))
<a name"1.2.0"></a>
## 1.2.0 (2015-07-13)
This release moves some of the most popular extensions (such as table-extension and github-extension) to core.
Also introduces a simple cli tool that you can use to quickly convert markdown files into html.
#### Bug Fixes
* **headerLevelStart:** fix for NaN error when specifying a non number as headerLevelStart param ([be72b487](http://github.com/showdownjs/showdown/commit/be72b487))
#### Features
* **CLI:** simple cli tool (ALPHA) ([f6a33e40](http://github.com/showdownjs/showdown/commit/f6a33e40))
* **flavours:** add markdown presets/flavors ([7e55bceb](http://github.com/showdownjs/showdown/commit/7e55bceb), closes [#164](http://github.com/showdownjs/showdown/issues/164))
* **ghCodeBlocks:** add option to disable GH codeblocks ([c33f9888](http://github.com/showdownjs/showdown/commit/c33f9888))
* **literalMidWordUnderscores:** add support for GFM literal midword underscores ([0c0cd7db](http://github.com/showdownjs/showdown/commit/0c0cd7db))
* **simplifiedAutoLink:** add support for GFM autolinks ([cff02372](http://github.com/showdownjs/showdown/commit/cff02372))
* **strikethrough:** add support for GFM strikethrough ([43e9448d](http://github.com/showdownjs/showdown/commit/43e9448d))
* **tables:** add support for GFM tables ([3a924e3c](http://github.com/showdownjs/showdown/commit/3a924e3c))
* **tasklists:** add support for GFM tasklists ([dc72403a](http://github.com/showdownjs/showdown/commit/dc72403a))
<a name"1.1.0"></a>
## 1.1.0 (2015-06-18)
#### Bug Fixes
* **converter.js:** add error if the passed constructor argument is not an object ([d86ed450](http://github.com/showdownjs/showdown/commit/d86ed450))
* **output modifiers:** fix for output modifiers running twice ([dcbdc61e](http://github.com/showdownjs/showdown/commit/dcbdc61e))
#### Features
* **headerLevelStart:** add support for setting the header starting level ([b84ac67d](http://github.com/showdownjs/showdown/commit/b84ac67d), closes [#69](http://github.com/showdownjs/showdown/issues/69))
* **image dimensions:** add support for setting image dimensions within markdown syntax ([af82c2b6](http://github.com/showdownjs/showdown/commit/af82c2b6), closes [#143](http://github.com/showdownjs/showdown/issues/143))
* **noHeaderId:** add option to suppress automatic generation of ids in headers ([7ac893e9](http://github.com/showdownjs/showdown/commit/7ac893e9))
* **showdown.getDefaultOptions:** add method to retrieve default global options keypairs ([2de53a7d](http://github.com/showdownjs/showdown/commit/2de53a7d))
#### Breaking Changes
* Deprecates `showdown.extensions` property. To migrate, extensions should use the new method `showdown.extension(<ext name>, <extension>)` to register.
For more information on the new extension loading mechanism, please check the wiki pages.
([4ebd0caa](http://github.com/showdownjs/showdown/commit/4ebd0caa))
<a name"1.0.2"></a>
### 1.0.2 (2015-05-28)
#### Bug Fixes
* **Gruntfile.js** add missing comma in footer. This bug prevented concatenating other js scripts and libraries
with showdown([5315508](http://github.com/showdownjs/showdown/commit/5315508). Credits to Alexandre Courtiol.
<a name"1.0.1"></a>
### 1.0.1 (2015-05-27)
#### Bug Fixes
* **bower.json:** update bower.json main attribute to point to dist directory ([bc3a092f](http://github.com/showdownjs/showdown/commit/bc3a092f))
<a name"1.0.0"></a>
## 1.0.0 (2015-05-27)
#### Release Information
This is a major code refactor with some big changes such as:
- showdown.js file was split in several files, called sub-parsers. This should improve code maintainability.
- angular integration was removed from core and move to its own repository, similar to what was done with extensions
- A new extension registering system is on the "cooks" that should reduce errors when using extensions. The old mechanism
is kept so old extensions should be compatible.
#### Bug Fixes
* **extensions:** support for old extension loading mechanism ([95ed7c68](http://github.com/showdownjs/showdown/commit/95ed7c68))
* **helpers:** fix wrong function call 'escapeCharacters' due to old strayed code ([18ba4e75](http://github.com/showdownjs/showdown/commit/18ba4e75))
* **showdown.js:**
- fix showdown extension loader ([a38c76d2](http://github.com/showdownjs/showdown/commit/a38c76d2)),
closes [#50](http://github.com/showdownjs/showdown/issues/50),[#56](http://github.com/showdownjs/showdown/issues/56),
[#104](http://github.com/showdownjs/showdown/issues/104), [#108](http://github.com/showdownjs/showdown/issues/108),
[#109](http://github.com/showdownjs/showdown/issues/109), [#111](http://github.com/showdownjs/showdown/issues/111),
[#118](http://github.com/showdownjs/showdown/issues/118), [#122](http://github.com/showdownjs/showdown/issues/122)
- add unique id prefix and suffix to headers ([c367a4b9](http://github.com/showdownjs/showdown/commit/c367a4b9), closes [#81](http://github.com/showdownjs/showdown/issues/81), [#82](http://github.com/showdownjs/showdown/issues/82))
* **options.omitExtraWLInCodeBlocks:** fix for options.omitExtraWLInCodeBlocks only applying in gitHub flavoured code b ([e6f40e19](http://github.com/showdownjs/showdown/commit/e6f40e19))
* **showdown:** fix for options merging into globalOptions ([ddd6011d](http://github.com/showdownjs/showdown/commit/ddd6011d), closes [#153](http://github.com/showdownjs/showdown/issues/153))
#### Features
* **registerExtension():** new extension loading mechanism. Now extensions can be registered using this function.
The system, however, is not final and will probably be changed until the final version([0fd10cb] (http://github.com/showdownjs/showdown/commit/0fd10cb))
* **allowBlockIndents:** indented inline block elements can now be parsed as markdown ([f6326b84](http://github.com/showdownjs/showdown/commit/f6326b84))
* **omitExtraWLInCodeBlocks:** add option to omit extra newline at the end of codeblocks ([141e3f5](http://github.com/showdownjs/showdown/commit/141e3f5))
* **prefixHeaderId:** add options to prefix header ids to prevent id clash ([141e3f5](http://github.com/showdownjs/showdown/commit/141e3f5))
* **Converter.options:** add getOption(), setOption() and getOptions() to Converter object ([db6f79b0](http://github.com/showdownjs/showdown/commit/db6f79b0))
#### Breaking Changes
* **NAMESPACE:** showdown's namespace changed.
To migrate your code you should update all references to `Showdown` with `showdown`.
* **Converter:** converter reference changed from `converter` to `Converter`.
To migrate you should update all references to `Showdown.converter` with `showdown.Converter`
* **angular:** angular integration was removed from core and now lives in it's own [repository](http://github.com/showdownjs/angular/).
If you're using angular integration, you should install ng-showdown. Ex: `bower install ng-showdown`
* **extensions:** showdown extensions were removed from core package and now live in their own repository. See the [project's github page](https://github.com/showdownjs) for available extensions

48
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,48 @@
Contributing
============
If you wish to contribute please read the following quick guide.
# Want a Feature?
You can request a new feature by submitting an issue. If you would like to implement a new feature feel free to issue a
Pull Request.
# Pull requests (PRs)
PRs are awesome. However, before you submit your pull request consider the following guidelines:
- Search GitHub for an open or closed Pull Request that relates to your submission. You don't want to duplicate effort.
- When issuing PRs that change code, make your changes in a new git branch based on master:
```bash
git checkout -b my-fix-branch master
```
- Documentation (i.e: README.md) changes can be made directly against master.
- Run the full test suite before submitting and make sure all tests pass (obviously =P).
- Try to follow our [**coding style rules**](https://github.com/showdownjs/code-style/blob/master/README.md).
Breaking them prevents the PR to pass the tests.
- Refrain from fixing multiple issues in the same pull request. It's preferable to open multiple small PRs instead of one
hard to review big one. Also, don't reuse old forks (or PRs) to fix new issues.
- If the PR introduces a new feature or fixes an issue, please add the appropriate test case.
- We use commit notes to generate the changelog. It's extremely helpful if your commit messages adhere to the
[**AngularJS Git Commit Guidelines**](https://github.com/showdownjs/code-style/blob/master/README.md#commit-message-convention).
- If we suggest changes then:
- Make the required updates.
- Re-run the Angular test suite to ensure tests are still passing.
- Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```bash
git rebase master -i
git push origin my-fix-branch -f
```
- After your pull request is merged, you can safely delete your branch.
If you have time to contribute to this project, we feel obliged that you get credit for it.
These rules enable us to review your PR faster and will give you appropriate credit in your GitHub profile.
We thank you in advance for your contribution!
# Joining the team
We're looking for members to help maintaining Showdown.
Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.

48
CREDITS.md Normal file
View File

@ -0,0 +1,48 @@
Credits
=======
- Showdown v1
* [Estevão Santos](https://github.com/tivie)
* [Pascal Deschênes](https://github.com/pdeschen)
- Showdown v0
* [Corey Innis](http://github.com/coreyti):<br/>
Original GitHub project maintainer
* [Remy Sharp](https://github.com/remy/):<br/>
CommonJS-compatibility and more
* [Konstantin Käfer](https://github.com/kkaefer/):<br/>
CommonJS packaging
* [Roger Braun](https://github.com/rogerbraun):<br/>
Github-style code blocks
* [Dominic Tarr](https://github.com/dominictarr):<br/>
Documentation
* [Cat Chen](https://github.com/CatChen):<br/>
Export fix
* [Titus Stone](https://github.com/tstone):<br/>
Mocha tests, extension mechanism, and bug fixes
* [Rob Sutherland](https://github.com/roberocity):<br/>
The idea that lead to extensions
* [Pavel Lang](https://github.com/langpavel):<br/>
Code cleanup
* [Ben Combee](https://github.com/unwiredben):<br/>
Regex optimization
* [Adam Backstrom](https://github.com/abackstrom):<br/>
WebKit bugfix
* [Pascal Deschênes](https://github.com/pdeschen):<br/>
Grunt support, extension fixes + additions, packaging improvements, documentation
* [Estevão Santos](https://github.com/tivie)<br/>
Bug fixing and late maintainer
* [Hannah Wolfe](https://github.com/ErisDS)<br/>
Bug fixes
* [Alexandre Courtiol](https://github.com/acourtiol)<br/>
Bug fixes and build optimization
* [Karthik Balakrishnan](https://github.com/torcellite)<br/>
Support for table alignment
* [rheber](https://github.com/rheber)<br/>
Cli
- Original Project
* [John Gruber](http://daringfireball.net/projects/markdown/)<br/>
Author of Markdown
* [John Fraser](http://attacklab.net/)<br/>
Author of Showdown

170
Gruntfile.js Normal file
View File

@ -0,0 +1,170 @@
/**
* Created by Tivie on 12-11-2014.
*/
module.exports = function (grunt) {
if (grunt.option('q') || grunt.option('quiet')) {
require('quiet-grunt');
}
// Project configuration.
var config = {
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
sourceMap: true,
banner: ';/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n(function(){\n',
footer: '}).call(this);'
},
dist: {
src: [
'src/options.js',
'src/showdown.js',
'src/helpers.js',
'src/converter.js',
'src/subParsers/*.js',
'src/loader.js'
],
dest: 'dist/<%= pkg.name %>.js'
},
test: {
src: '<%= concat.dist.src %>',
dest: '.build/<%= pkg.name %>.js',
options: {
sourceMap: false
}
}
},
clean: ['.build/'],
uglify: {
options: {
sourceMap: true,
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
files: [
'Gruntfile.js',
'src/**/*.js',
'test/**/*.js'
]
},
jscs: {
options: {
config: '.jscs.json'
},
files: {
src: [
'Gruntfile.js',
'src/**/*.js',
'test/**/*.js'
]
}
},
changelog: {
options: {
repository: 'http://github.com/showdownjs/showdown',
dest: 'CHANGELOG.md'
}
},
simplemocha: {
node: {
src: 'test/node/**/*.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: true,
reporter: 'spec'
}
},
karlcow: {
src: 'test/node/testsuite.karlcow.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
reporter: 'spec'
}
},
issues: {
src: 'test/node/testsuite.issues.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
reporter: 'spec'
}
},
standard: {
src: 'test/node/testsuite.standard.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
reporter: 'spec'
}
},
features: {
src: 'test/node/testsuite.features.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
reporter: 'spec'
}
},
single: {
src: 'test/node/**/*.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
reporter: 'spec'
}
}
}
};
grunt.initConfig(config);
require('load-grunt-tasks')(grunt);
grunt.registerTask('single-test', function (grep) {
'use strict';
grunt.config.merge({
simplemocha: {
single: {
options: {
grep: grep
}
}
}
});
grunt.task.run(['lint', 'concat:test', 'simplemocha:single', 'clean']);
});
grunt.registerTask('lint', ['jshint', 'jscs']);
grunt.registerTask('test', ['clean', 'lint', 'concat:test', 'simplemocha:node', 'clean']);
grunt.registerTask('build', ['test', 'concat:dist', 'uglify']);
grunt.registerTask('prep-release', ['build', 'changelog']);
// Default task(s).
grunt.registerTask('default', ['test']);
};

701
README.md
View File

@ -1,327 +1,374 @@
# Showdown [![build status](https://secure.travis-ci.org/coreyti/showdown.png)](http://travis-ci.org/coreyti/showdown)
A JavaScript port of Markdown
## Note
> **Please note** that I, [Corey](https://github.com/coreyti), am not the author
> of Showdown. Rather, I found it some time back at <http://attacklab.net/showdown/>
> (website removed, see: <http://wayback.archive.org/web/*/http://attacklab.net/showdown>)
> and wanted to see it available on GitHub.
>
> All credit and praise for authoring this library should go to John Fraser.
>
> Oh, and John Gruber of course.
>
> That said, Showdown *is* evolving. See below for a list of contributors and an
> overview of their contributions to the project.
>
> Apologies for any confusion or perceived misinformation.
>
> Cheers,<br/>
> Corey
## Original Attributions
Showdown Copyright (c) 2007 John Fraser.
<http://www.attacklab.net/>
Original Markdown Copyright (c) 2004-2005 John Gruber
<http://daringfireball.net/projects/markdown/>
Redistributable under a BSD-style open source license.
See license.txt for more information.
## Quick Example
```js
var Showdown = require('showdown');
var converter = new Showdown.converter();
converter.makeHtml('#hello markdown!');
// <h1 id="hellomarkdown">hello, markdown</h1>
```
## What's it for?
Developers can use Showdown to:
* Add in-browser preview to existing Markdown apps
Showdown's output is (almost always) identical to
markdown.pl's, so the server can reproduce exactly
the output that the user saw. (See below for
exceptions.)
* Add Markdown input to programs that don't support it
Any app that accepts HTML input can now be made to speak
Markdown by modifying the input pages's HTML. If your
application lets users edit documents again later,
then they won't have access to the original Markdown
text. But this should be good enough for many
uses -- and you can do it with just a two-line
`onsubmit` function!
* Add Markdown input to closed-source web apps
You can write bookmarklets or userscripts to extend
any standard textarea on the web so that it accepts
Markdown instead of HTML. With a little more hacking,
the same can probably be done with many rich edit
controls.
* Build new web apps from scratch
A Showdown front-end can send back text in Markdown,
HTML or both, so you can trade bandwidth for server
load to reduce your cost of operation. If your app
requires JavaScript, you won't need to do any
Markdown processing on the server at all. (For most
uses, you'll still need to sanitize the HTML before
showing it to other users -- but you'd need to do
that anyway if you're allowing raw HTML in your
Markdown.)
## Browser Compatibility
Showdown has been tested successfully with:
* Firefox 1.5 and 2.0
* Internet Explorer 6 and 7
* Safari 2.0.4
* Opera 8.54 and 9.10
* Netscape 8.1.2
* Konqueror 3.5.4
In theory, Showdown will work in any browser that supports ECMA 262 3rd Edition (JavaScript 1.5). The converter itself might even work in things that aren't web browsers, like Acrobat. No promises.
## Extensions
Showdown allows additional functionality to be loaded via extensions.
### Client-side Extension Usage
```js
<script src="src/showdown.js" />
<script src="src/extensions/twitter.js" />
var converter = new Showdown.converter({ extensions: 'twitter' });
```
### Server-side Extension Usage
```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] });
```
## Known Differences in Output
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.
* This release uses the HTML parser from Markdown 1.0.2b2,
which means it fails `Inline HTML (Advanced).text` from
the Markdown test suite:
<div>
<div>
unindented == broken
</div>
</div>
* Showdown doesn't support the markdown="1" attribute:
<div markdown="1">
Markdown does *not* work in here.
</div>
This is half laziness on my part and half stubbornness.
Markdown is smart enough to process the contents of span-
level tags without screwing things up; shouldn't it be
able to do the same inside block elements? Let's find a
way to make markdown="1" the default.
* You can only nest square brackets in link titles to a
depth of two levels:
[[fine]](http://www.attacklab.net/)
[[[broken]]](http://www.attacklab.net/)
If you need more, you can escape them with backslashes.
* When sublists have paragraphs, Showdown produces equivalent
HTML with a slightly different arrangement of newlines:
+ item
- subitem
The HTML has a superfluous newline before this
paragraph.
- subitem
The HTML here is unchanged.
- subitem
The HTML is missing a newline after this
list subitem.
* Markdown.pl creates empty title attributes for
inline-style images:
Here's an empty title on an inline-style
![image](http://w3.org/Icons/valid-xhtml10).
I tried to replicate this to clean up my diffs during
testing, but I went too far: now Showdown also makes
empty titles for reference-style images:
Showdown makes an empty title for
reference-style ![images][] too.
[images]: http://w3.org/Icons/valid-xhtml10
* With crazy input, Markdown will mistakenly put
`<strong>` or `<em>` tags in URLs:
<a href="<*Markdown adds em tags in here*>">
improbable URL
</a>
Showdown won't. But still, don't do that.
## Tests
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 development dependencies:
npm install --dev
Once installed the tests can be run from the project root using:
npm test
New test cases can easily be added. Create a markdown file (ending in `.md`) which contains the markdown to test. Create a `.html` file of the exact same name. It will automatically be tested when the tests are executed with `mocha`.
## Creating Markdown Extensions
A showdown extension is simply a function which returns an array of extensions. Each single extension can be one of two types:
* Language Extension -- Language extensions are ones that that add new markdown syntax to showdown. For example, say you wanted `^^youtube http://www.youtube.com/watch?v=oHg5SJYRHA0` to automatically render as an embedded YouTube video, that would be a language extension.
* Output Modifiers -- After showdown has run, and generated HTML, an output modifier would change that HTML. For example, say you wanted to change `<div class="header">` to be `<header>`, that would be an output modifier.
Each extension can provide two combinations of interfaces for showdown.
### Regex/Replace
Regex/replace style extensions are very similar to javascripts `string.replace` function. Two properties are given, `regex` and `replace`. `regex` is a string and `replace` can be either a string or a function. If `replace` is a string, it can use the `$1` syntax for group substitution, exactly as if it were making use of `string.replace` (internally it does this actually); The value of `regex` is assumed to be a global replacement.
**Example:**
```js
var demo = function(converter) {
return [
// Replace escaped @ symbols
{ type: 'lang', regex: '\\@', replace: '@' }
];
}
```
### Filter
Alternately, if you'd just like to do everything yourself, you can specify a filter which is a callback with a single input parameter, text (the current source text within the showdown engine).
**Example:**
```js
var demo = function(converter) {
return [
// Replace escaped @ symbols
{ type: 'lang', function(text) {
return text.replace(/\\@/g, '@');
}}
];
}
```
### Implementation Concerns
One bit which should be taken into account is maintaining both client-side and server-side compatibility. This can be achieved with a few lines of boilerplate code. First, to prevent polluting the global scope for client-side code, the extension definition should be wrapped in a self-executing function.
```js
(function(){
// Your extension here
}());
```
Second, client-side extensions should add a property onto `Showdown.extensions` which matches the name of the file. As an example, a file named `demo.js` should then add `Showdown.extensions.demo`. Server-side extensions can simply export themselves.
```js
(function(){
var demo = function(converter) {
// ... extension code here ...
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.demo = demo; }
// Server-side export
if (typeof module !== 'undefined') module.exports = demo;
}());
```
### Testing Extensions
The showdown test runner is setup to automatically test cases for extensions. To add test cases for an extension, create a new folder under `./test/extensions` which matches the name of the `.js` file in `./src/extensions`. Place any test cases into the filder using the md/html format and they will automatically be run when tests are run.
## Credits
* Origins
* [John Fraser](http://attacklab.net/):<br/>
Author of Showdown
* [John Gruber](http://daringfireball.net/projects/markdown/):<br/>
Author of Markdown
* Maintenance/Contributions (roughly chronologically)
* [Corey Innis](http://github.com/coreyti):<br/>
GitHub project maintainer
* [Remy Sharp](https://github.com/remy/):<br/>
CommonJS-compatibility and more
* [Konstantin Käfer](https://github.com/kkaefer/):<br/>
CommonJS packaging
* [Roger Braun](https://github.com/rogerbraun):<br/>
Github-style code blocks
* [Dominic Tarr](https://github.com/dominictarr):<br/>
Documentation
* [Cat Chen](https://github.com/CatChen):<br/>
Export fix
* [Titus Stone](https://github.com/tstone):<br/>
Mocha tests, extension mechanism, and bug fixes
* [Rob Sutherland](https://github.com/roberocity):<br/>
The idea that lead to extensions
* [Pavel Lang](https://github.com/langpavel):<br/>
Code cleanup
* [Ben Combee](https://github.com/unwiredben):<br/>
Regex optimization
* [Adam Backstrom](https://github.com/abackstrom):<br/>
WebKit bugfix
* [Pascal Deschênes](https://github.com/pdeschen):<br/>
Grunt support, extension fixes + additions, packaging improvements, documentation
![Showdown][sd-logo]
[![Build Status](https://travis-ci.org/showdownjs/showdown.svg?branch=master)](https://travis-ci.org/showdownjs/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) [![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)
------
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).
## Live DEMO
Check a live Demo here http://showdownjs.github.io/demo/
## Who uses Showdown (or a fork)
- [GoogleCloudPlatform](https://github.com/GoogleCloudPlatform)
- [Ghost](https://ghost.org/)
- [Meteor](https://www.meteor.com/)
- [Stackexchange](http://stackexchange.com/) - forked as [PageDown](https://code.google.com/p/pagedown/)
- [docular](https://github.com/Vertafore/docular)
- [and some others...](https://www.npmjs.com/browse/depended/showdown)
## Installation
### Download tarball
You can download the latest release tarball directly from [releases][releases]
### Bower
bower install showdown
### npm (server-side)
npm install showdown
### CDN
You can also use one of several CDNs available:
* github CDN
https://cdn.rawgit.com/showdownjs/showdown/<version tag>/dist/showdown.min.js
* cdnjs
https://cdnjs.cloudflare.com/ajax/libs/showdown/<version tag>/showdown.min.js
## Browser Compatibility
Showdown has been tested successfully with:
* Firefox 1.5 and 2.0
* Chrome 12.0
* Internet Explorer 6 and 7
* Safari 2.0.4
* Opera 8.54 and 9.10
* Netscape 8.1.2
* Konqueror 3.5.4
In theory, Showdown will work in any browser that supports ECMA 262 3rd Edition (JavaScript 1.5). The converter itself might even work in things that aren't web browsers, like Acrobat. No promises.
## Node compatibility
Showdown has been tested with node 0.8 and 0.10. However, it should work with previous versions, such as node 0.6.
## Legacy version
If you're looking for showdown v<1.0.0, you can find it in the [**legacy branch**][legacy-branch].
## Changelog
You can check the full [changelog][changelog]
## Extended documentation
Check our [wiki pages][wiki] for examples and a more in-depth documentation.
## Quick Example
### Node
```js
var showdown = require('showdown'),
converter = new showdown.Converter(),
text = '#hello, markdown!',
html = converter.makeHtml(text);
```
### Browser
```js
var converter = new showdown.Converter(),
text = '#hello, markdown!',
html = converter.makeHtml(text);
```
### Output
Both examples should output...
<h1 id="hellomarkdown">hello, markdown!</h1>
## Options
You can change some of showdown's default behavior through options.
### Setting options
Options can be set:
#### Globally
Setting a "global" option affects all instances of showdown
```js
showdown.setOption('optionKey', 'value');
```
#### Locally
Setting a "local" option only affects the specified Converter object.
Local options can be set:
* **through the constructor**
```js
var converter = new showdown.Converter({optionKey: 'value'});
```
* **through the setOption() method**
```js
var converter = new showdown.Converter();
converter.setOption('optionKey', 'value');
```
### Getting an option
Showdown provides 2 methods (both local and global) to retrieve previous 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();
```
### Retrieve the default options
You can get showdown's default options with:
```js
var defaultOptions = showdown.getDefaultOptions();
```
### Valid Options
* **omitExtraWLInCodeBlocks**: (boolean) [default false] Omit the trailing newline in a code block. Ex:
This:
```html
<code><pre>var foo = 'bar';
</pre></code>
```
Becomes this:
```html
<code><pre>var foo = 'bar';</pre></code>
```
* **noHeaderId**: (boolean) [default false] Disable the automatic generation of header ids. Setting to true overrides **prefixHeaderId**
* **prefixHeaderId**: (string/boolean) [default false] Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to `true` will add a generic 'section' prefix.
* **parseImgDimensions**: (boolean) [default false] Enable support for setting image dimensions from within markdown syntax.
Examples:
```
![foo](foo.jpg =100x80) simple, assumes units are in px
![bar](bar.jpg =100x*) sets the height to "auto"
![baz](baz.jpg =80%x5em) Image with width of 80% and height of 5em
```
* **headerLevelStart**: (integer) [default 1] Set the header starting level. For instance, setting this to 3 means that
```md
# foo
```
will be parsed as
```html
<h3>foo</h3>
```
* **simplifiedAutoLink**: (boolean) [default false] Turning this on will enable GFM autolink style. This means that
```md
some text www.google.com
```
will be parsed as
````
<p>some text <a href="www.google.com">www.google.com</a>
```
* **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.
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~~` as `<del>strikethrough</del>`
* **tables**: (boolean) [default false] Enable support for tables syntax. Example:
```md
| h1 | h2 | h3 |
|:------|:-------:|--------:|
| 100 | [a][1] | ![b][2] |
| *foo* | **bar** | ~~baz~~ |
```
See the wiki for more info
* **tablesHeaderId**: (boolean) [default false] If enabled adds an id property to table headers tags.
* **ghCodeBlocks**: (boolean) [default true] Enable support for GFM code block style.
* **tasklists**:(boolean) [default false] Enable support for GFM takslists. Example:
```md
- [x] This task is done
- [ ] This is still pending
```
* **smoothLivePreview**: (boolean) [default false] Prevents weird effects in live previews due to incomplete input
## CLI Tool
Showdown also comes bundled with a Command Line Interface tool. You can check the [CLI wiki page][cli-wiki] for more info
## Integration with AngularJS
ShowdownJS project also provides seamlessly integration with AngularJS via a "plugin".
Please visit https://github.com/showdownjs/ngShowdown for more information.
## Integration with TypeScript
If you're using TypeScript you maybe want to use the types from [DefinitelyTyped][definitely-typed]
## XSS vulnerability
Showdown doesn't sanitize the input. This is by design since markdown relies on it to allow certain features to be correctly parsed into HTML.
This, however, means XSS injection is quite possible.
Please refer to the wiki article [Markdown's XSS Vulnerability (and how to mitigate it)][xss-wiki]
for more information.
## Extensions
Showdown allows additional functionality to be loaded via extensions. (you can find a list of known showdown extensions [here][ext-wiki])
### Client-side Extension Usage
```js
<script src="showdown.js" />
<script src="twitter-extension.js" />
var converter = new showdown.Converter({ extensions: 'twitter' });
```
### Server-side Extension Usage
```js
var showdown = require('showdown'),
myExtension = require('myExtension'),
converter = new showdown.Converter({ extensions: ['myExtension'] });
```
## Tests
A suite of tests is available which require node.js. Once node is installed, run the following command from the project root to install the dependencies:
npm install
Once installed the tests can be run from the project root using:
npm test
New test cases can easily be added. Create a markdown file (ending in `.md`) which contains the markdown to test. Create a `.html` file of the exact same name. It will automatically be tested when the tests are executed with `mocha`.
## Contributing
If you wish to contribute please read the following quick guide.
### Want a Feature?
You can request a new feature by submitting an issue. If you would like to implement a new feature feel free to issue a
Pull Request.
### Pull requests (PRs)
PRs are awesome. However, before you submit your pull request consider the following guidelines:
- Search GitHub for an open or closed Pull Request that relates to your submission. You don't want to duplicate effort.
- When issuing PRs that change code, make your changes in a new git branch based on master:
```bash
git checkout -b my-fix-branch master
```
- Documentation (i.e: README.md) changes can be made directly against master.
- Run the full test suite before submitting and make sure all tests pass (obviously =P).
- Try to follow our [**coding style rules**][coding-rules].
Breaking them prevents the PR to pass the tests.
- Refrain from fixing multiple issues in the same pull request. It's preferable to open multiple small PRs instead of one
hard to review big one.
- If the PR introduces a new feature or fixes an issue, please add the appropriate test case.
- We use commit notes to generate the changelog. It's extremely helpful if your commit messages adhere to the
[**AngularJS Git Commit Guidelines**][ng-commit-guide].
- If we suggest changes then:
- Make the required updates.
- Re-run the Angular test suite to ensure tests are still passing.
- Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```bash
git rebase master -i
git push origin my-fix-branch -f
```
- After your pull request is merged, you can safely delete your branch.
If you have time to contribute to this project, we feel obliged that you get credit for it.
These rules enable us to review your PR faster and will give you appropriate credit in your GitHub profile.
We thank you in advance for your contribution!
### Joining the team
We're looking for members to help maintaining Showdown.
Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.
## Credits
Full credit list at https://github.com/showdownjs/showdown/blob/master/CREDITS.md
Showdown is powered by:<br/>
[![webstorm](https://www.jetbrains.com/webstorm/documentation/docs/logo_webstorm.png)](https://www.jetbrains.com/webstorm/)
[sd-logo]: https://raw.githubusercontent.com/showdownjs/logo/master/dist/logo.readme.png
[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
[wiki]: https://github.com/showdownjs/showdown/wiki
[cli-wiki]: https://github.com/showdownjs/showdown/wiki/CLI-tool
[definitely-typed]: https://github.com/borisyankov/DefinitelyTyped/tree/master/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

BIN
bin/showdown.js Normal file

Binary file not shown.

33
bower.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "showdown",
"description": "A Markdown to HTML converter written in Javascript",
"homepage": "https://github.com/showdownjs/showdown",
"authors": [
"Estevão Santos (https://github.com/tivie)",
"Pascal Deschênes (https://github.com/pdeschen)"
],
"main": ["dist/showdown.js"],
"ignore": [
".editorconfig",
".gitattributes",
".gitignore",
".jscs.json",
".jshintignore",
".jshintrc",
".travis.yml",
"Gruntfile.js",
"package.json",
"src/*",
"test/*"
],
"repository": {
"type": "git",
"url": "https://github.com/showdownjs/showdown.git"
},
"keywords": [
"markdown",
"md",
"mdown"
],
"license": "https://github.com/showdownjs/showdown/blob/master/license.txt"
}

View File

@ -1,5 +0,0 @@
//
// Github Extension (WIP)
// ~~strike-through~~ -> <del>strike-through</del>
//
(function(){var a=function(a){return[{type:"lang",regex:"(~T){2}([^~]+)(~T){2}",replace:function(a,b,c,d){return"<del>"+c+"</del>"}}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.github=a),typeof module!="undefined"&&(module.exports=a)})();

View File

@ -1,6 +0,0 @@
//
// Google Prettify
// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)
// hints to showdown's HTML output.
//
(function(){var a=function(a){return[{type:"output",filter:function(a){return a.replace(/(<pre>)?<code>/gi,function(a,b){return b?'<pre class="prettyprint linenums" tabIndex="0"><code data-inner="1">':'<code class="prettyprint">'})}}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.prettify=a),typeof module!="undefined"&&(module.exports=a)})();

View File

@ -1,13 +0,0 @@
/*global module:true*//*
* Basic table support with re-entrant parsing, where cell content
* can also specify markdown.
*
* Tables
* ======
*
* | Col 1 | Col 2 |
* |======== |====================================================|
* |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
* | Plain | Value |
*
*/(function(){var a=function(a){var b={},c="text-align:left;",d;return b.th=function(a){if(a.trim()==="")return"";var b=a.trim().replace(/ /g,"_").toLowerCase();return'<th id="'+b+'" style="'+c+'">'+a+"</th>"},b.td=function(b){return'<td style="'+c+'">'+a.makeHtml(b)+"</td>"},b.ths=function(){var a="",c=0,d=[].slice.apply(arguments);for(c;c<d.length;c+=1)a+=b.th(d[c])+"\n";return a},b.tds=function(){var a="",c=0,d=[].slice.apply(arguments);for(c;c<d.length;c+=1)a+=b.td(d[c])+"\n";return a},b.thead=function(){var a,c=0,d=[].slice.apply(arguments);return a="<thead>\n",a+="<tr>\n",a+=b.ths.apply(this,d),a+="</tr>\n",a+="</thead>\n",a},b.tr=function(){var a,c=0,d=[].slice.apply(arguments);return a="<tr>\n",a+=b.tds.apply(this,d),a+="</tr>\n",a},d=function(a){var c=0,d=a.split("\n"),e=[],f,g,h,i=[];for(c;c<d.length;c+=1){f=d[c];if(f.trim().match(/^[|]{1}.*[|]{1}$/)){f=f.trim(),e.push("<table>"),g=f.substring(1,f.length-1).split("|"),e.push(b.thead.apply(this,g)),f=d[++c];if(!!f.trim().match(/^[|]{1}[-=| ]+[|]{1}$/)){f=d[++c],e.push("<tbody>");while(f.trim().match(/^[|]{1}.*[|]{1}$/))f=f.trim(),e.push(b.tr.apply(this,f.substring(1,f.length-1).split("|"))),f=d[++c];e.push("</tbody>"),e.push("</table>"),i.push(e.join("\n"));continue}f=d[--c]}i.push(f)}return i.join("\n")},[{type:"lang",filter:d}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.table=a),typeof module!="undefined"&&(module.exports=a)})();

View File

@ -1,6 +0,0 @@
//
// Twitter Extension
// @username -> <a href="http://twitter.com/username">@username</a>
// #hashtag -> <a href="http://twitter.com/search/%23hashtag">#hashtag</a>
//
(function(){var a=function(a){return[{type:"lang",regex:"\\B(\\\\)?@([\\S]+)\\b",replace:function(a,b,c){return b==="\\"?a:'<a href="http://twitter.com/'+c+'">@'+c+"</a>"}},{type:"lang",regex:"\\B(\\\\)?#([\\S]+)\\b",replace:function(a,b,c){return b==="\\"?a:'<a href="http://twitter.com/search/%23'+c+'">#'+c+"</a>"}},{type:"lang",regex:"\\\\@",replace:"@"}]};typeof window!="undefined"&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.twitter=a),typeof module!="undefined"&&(module.exports=a)})();

File diff suppressed because one or more lines are too long

BIN
dist/showdown.js vendored Normal file

Binary file not shown.

BIN
dist/showdown.js.map vendored Normal file

Binary file not shown.

BIN
dist/showdown.min.js vendored Normal file

Binary file not shown.

BIN
dist/showdown.min.js.map vendored Normal file

Binary file not shown.

View File

@ -1,720 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Showdown - Markdown in Javascript</title>
<script type="text/javascript" src="showdown.js"></script>
<script type="text/javascript" src="showdown-gui.js"></script>
<style type="text/css">
html,body {
margin:0;
padding:0;
font-family: Helvetica, Arial, Verdana, sans-serif;
font-size: 90%;
background-color: #e0d8d8;
}
html {
overflow: hidden;
}
textarea {
font-family: monospace;
}
#pageHeader {
margin: 0;
padding: 0;
text-align: center;
margin-bottom: 0;
margin-top: 0.4em;
color: #766;
}
#pageHeader h1 {
font-size: 3em;
}
#pageHeader * {
margin: 0;
padding: 0;
line-height: 1em;
font-weight: 100;
}
#pageHeader a {
color: #766;
text-decoration: none;
position: relative;
z-index: 20;
}
#pageHeader h1 a:hover {
color: #fff;
}
#pageHeader h4 a:hover {
text-decoration: underline;
}
#leftContainer, #rightContainer {
margin: 0;
padding: 0;
position: relative;
width: 47.5%;
margin-top: -1.4em;
}
#leftContainer {
float: left;
left: 1.5%;
}
#rightContainer {
float: right;
right: 1.5%;
}
#rightContainer > * {
float: right;
}
.paneHeader {
margin: 0;
padding: 0;
position: relative;
width: 100%;
display: block;
height: 2em;
}
.paneHeader * {
position: relative;
font-weight: 900;
}
.paneHeader span {
background-color: #ddd5d5;
color: #444;
padding: 0 0.75em;
font-size: 110%;
}
#paneSetting {
display: block;
margin-left: auto;
margin-right: 0.5em;
font-size: 110%;
font-weight: 900;
font-family: Arial, Verdana, sans-serif;
background-color: #dacccc;
color: #444;
border: 1px solid #999;
}
.pane {
margin: 0;
padding: 0;
padding-left: 4px; /* pane padding */
width: 100%;
border: none;
background-color: #eee;
display: block;
border: 1px solid #888;
border-right: 1px solid #000;
border-bottom: 1px solid #000;
/* note: the panes get their height set with
javascript; see sizeTextAreas(). */
/* for now, set a height so things look nice
if the user has javascript disabled */
height: 400px;
}
#previewPane {
background-color: #f3eeee;
}
#outputPane {
background-color: #6c6666;
color: #fff;
display: none;
}
#syntaxPane {
background-color: #e6dede;
background-color: #f7ecec;
display: none;
}
div.pane {
overflow: auto;
}
#inputPane {
background-color: #fff;
}
#previewPane {
padding: 0;
}
#previewPane > * {
margin-left: 4px;
margin-right: 4px;
}
#previewPane > blockquote {
margin-left: 3em;
}
#previewPane > :first-child {
margin-top: 4px; /* pane padding */
}
#previewPane * {
line-height: 1.4em;
}
#previewPane code {
font-size: 1.3em;
}
#footer {
margin: 0;
padding: 0;
position: relative;
float: left;
width: 100%;
height: 2.5em;
margin-top: 0.5em;
font-family: Helvetica, Arial, Verdana, sans-serif;
}
#footer a {
text-decoration: none;
color: #666;
}
#footer a:hover {
text-decoration: underline;
}
#byline {
padding-left: 2em;
color: #666;
}
#convertTextControls {
position: absolute;
right: 5em;
}
#convertTextButton {
line-height: 1em;
background-color: #ccbfbf;
color: #000;
border: none;
}
#convertTextButton:hover {
background-color: #fff;
color: black;
}
#convertTextSetting {
background-color: #dacccc;
color: #222;
border: 1px solid #999;
}
#processingTime {
margin: 0;
padding: 0;
width: 4em;
text-align: right;
color: #999;
position: absolute;
right: 1em;
top: 0;
}
</style>
</head>
<body>
<div id="pageHeader">
<h1><a href="http://www.attacklab.net/showdown-gui.html">Showdown</a></h1>
<h4>a javascript port of <a href="http://daringfireball.net/projects/markdown/" title="The Markdown web site">Markdown</a></h4>
</div>
<div id="leftContainer">
<div class="paneHeader">
<span>Input</span>
</div>
<textarea id="inputPane" cols="80" rows="20" class="pane">Using this tool
---------------
This page lets you create HTML by entering text in a simple format that's easy to read and write.
- Type Markdown text in the left window
- See the HTML in the right
Markdown is a lightweight markup language based on the formatting conventions that people naturally use in email. As [John Gruber] writes on the [Markdown site] [1]:
&gt; The overriding design goal for Markdown's
&gt; formatting syntax is to make it as readable
&gt; as possible. The idea is that a
&gt; Markdown-formatted document should be
&gt; publishable as-is, as plain text, without
&gt; looking like it's been marked up with tags
&gt; or formatting instructions.
This document is written in Markdown; you can see the plain-text version on the left. To get a feel for Markdown's syntax, type some text into the left window and watch the results in the right. You can see a Markdown syntax guide by switching the right-hand window from *Preview* to *Syntax Guide*.
Showdown is a Javascript port of Markdown. You can get the full [source code] by clicking on the version number at the bottom of the page.
**Start with a [blank page] or edit this document in the left window.**
[john gruber]: http://daringfireball.net/
[1]: http://daringfireball.net/projects/markdown/
[source code]: http://www.attacklab.net/showdown-v0.9.zip
[blank page]: ?blank=1 "Clear all text"
</textarea>
</div>
<div id="rightContainer">
<div class="paneHeader">
<select id="paneSetting">
<option value="previewPane">Preview</option>
<option value="outputPane">HTML Output</option>
<option value="syntaxPane">Syntax Guide</option>
</select>
</div>
<textarea id="outputPane" class="pane" cols="80" rows="20" readonly="readonly"></textarea>
<div id="previewPane" class="pane"><noscript><h2>You'll need to enable Javascript to use this tool.</h2></noscript></div>
<textarea id="syntaxPane" class="pane" cols="80" rows="20" readonly="readonly">
Markdown Syntax Guide
=====================
This is an overview of Markdown's syntax. For more information, visit the [Markdown web site].
[Markdown web site]:
http://daringfireball.net/projects/markdown/
Italics and Bold
================
*This is italicized*, and so is _this_.
**This is bold**, and so is __this__.
You can use ***italics and bold together*** if you ___have to___.
Links
=====
Simple links
------------
There are three ways to write links. Each is easier to read than the last:
Here's an inline link to [Google](http://www.google.com/).
Here's a reference-style link to [Google] [1].
Here's a very readable link to [Yahoo!].
[1]: http://www.google.com/
[yahoo!]: http://www.yahoo.com/
The link definitions can appear anywhere in the document -- before or after the place where you use them. The link definition names (`1` and `Yahoo!`) can be any unique string, and are case-insensitive; `[Yahoo!]` is the same as `[YAHOO!]`.
Advanced links: Title attributes
--------------------------------
You can also add a `title` attribute to a link, which will show up when the user holds the mouse pointer it. Title attributes are helpful if your link text is not descriptive enough to tell users where they're going. (In reference links, you can use optionally parentheses for the link title instead of quotation marks.)
Here's a [poorly-named link](http://www.google.com/ "Google").
Never write "[click here][^2]".
Trust [me].
[^2]: http://www.w3.org/QA/Tips/noClickHere
(Advice against the phrase "click here")
[me]: http://www.attacklab.net/ "Attacklab"
Advanced links: Bare URLs
-------------------------
You can write bare URLs by enclosing them in angle brackets:
My web site is at &lt;http://www.attacklab.net&gt;.
If you use this format for email addresses, Showdown will encode the address to make it harder for spammers to harvest. Try it and look in the *HTML Output* pane to see the results:
Humans can read this, but most spam harvesting robots can't: &lt;me@privacy.net&gt;
Headers
=======
There are two ways to do headers in Markdown. (In these examples, Header 1 is the biggest, and Header 6 is the smallest.)
You can underline text to make the two top-level headers:
Header 1
========
Header 2
--------
The number of `=` or `-` signs doesn't matter; you can get away with just one. But using enough to underline the text makes your titles look better in plain text.
You can also use hash marks for all six levels of HTML headers:
# Header 1 #
## Header 2 ##
### Header 3 ###
#### Header 4 ####
##### Header 5 #####
###### Header 6 ######
The closing `#` characters are optional.
Horizontal Rules
================
You can insert a horizontal rule by putting three or more hyphens, asterisks, or underscores on a line by themselves:
---
*******
___
You can also use spaces between the characters:
- - - -
All of these examples produce the same output.
Lists
=====
Simple lists
------------
A bulleted list:
- You can use a minus sign for a bullet
+ Or plus sign
* Or an asterisk
A numbered list:
1. Numbered lists are easy
2. Markdown keeps track of the numbers for you
7. So this will be item 3.
A double-spaced list:
- This list gets wrapped in `&lt;p&gt;` tags
- So there will be extra space between items
Advanced lists: Nesting
-----------------------
You can put other Markdown blocks in a list; just indent four spaces for each nesting level. So:
1. Lists in a list item:
- Indented four spaces.
* indented eight spaces.
- Four spaces again.
2. Multiple paragraphs in a list items:
It's best to indent the paragraphs four spaces
You can get away with three, but it can get
confusing when you nest other things.
Stick to four.
We indented the first line an extra space to align
it with these paragraphs. In real use, we might do
that to the entire list so that all items line up.
This paragraph is still part of the list item, but it looks messy to humans. So it's a good idea to wrap your nested paragraphs manually, as we did with the first two.
3. Blockquotes in a list item:
&gt; Skip a line and
&gt; indent the &gt;'s four spaces.
4. Preformatted text in a list item:
Skip a line and indent eight spaces.
That's four spaces for the list
and four to trigger the code block.
Blockquotes
===========
Simple blockquotes
------------------
Blockquotes are indented:
&gt; The syntax is based on the way email programs
&gt; usually do quotations. You don't need to hard-wrap
&gt; the paragraphs in your blockquotes, but it looks much nicer if you do. Depends how lazy you feel.
Advanced blockquotes: Nesting
-----------------------------
You can put other Markdown blocks in a blockquote; just add a `&gt;` followed by a space:
Parragraph breaks in a blockquote:
&gt; The &gt; on the blank lines is optional.
&gt; Include it or don't; Markdown doesn't care.
&gt;
&gt; But your plain text looks better to
&gt; humans if you include the extra `&gt;`
&gt; between paragraphs.
Blockquotes within a blockquote:
&gt; A standard blockquote is indented
&gt; &gt; A nested blockquote is indented more
&gt; &gt; &gt; &gt; You can nest to any depth.
Lists in a blockquote:
&gt; - A list in a blockquote
&gt; - With a &gt; and space in front of it
&gt; * A sublist
Preformatted text in a blockquote:
&gt; Indent five spaces total. The first
&gt; one is part of the blockquote designator.
Images
======
Images are exactly like links, but they have an exclamation point in front of them:
![Valid XHTML] (http://w3.org/Icons/valid-xhtml10).
The word in square brackets is the alt text, which gets displayed if the browser can't show the image. Be sure to include meaningful alt text for blind users' screen-reader software.
Just like links, images work with reference syntax and titles:
This page is ![valid XHTML][checkmark].
[checkmark]: http://w3.org/Icons/valid-xhtml10
"What are you smiling at?"
**Note:**
Markdown does not currently support the shortest reference syntax for images:
Here's a broken ![checkmark].
But you can use a slightly more verbose version of implicit reference names:
This ![checkmark][] works.
The reference name (`valid icon`) is also used as the alt text.
Inline HTML
===========
If you need to do something that Markdown can't handle, you can always just use HTML:
Strikethrough humor is &lt;strike&gt;funny&lt;/strike&gt;.
Markdown is smart enough not to mangle your span-level HTML:
&lt;u&gt;Markdown works *fine* in here.&lt;/u&gt;
Block-level HTML elments have a few restrictions:
1. They must be separated from surrounding text by blank
lines.
2. The begin and end tags of the outermost block element
must not be indented.
3. You can't use Markdown within HTML blocks.
So:
&lt;div style="background-color: lightgray"&gt;
You can &lt;em&gt;not&lt;/em&gt; use Markdown in here.
&lt;/div&gt;
Preformatted Text
=================
You can include preformatted text in a Markdown document.
To make a code block, indent four spaces:
printf("goodbye world!"); /* his suicide note
was in C */
The text will be wrapped in `&lt;pre&gt;` and `&lt;code&gt;` tags, and the browser will display it in a monospaced typeface. The first four spaces will be stripped off, but all other whitespace will be preserved.
You cannot use Markdown or HTML within a code block, which makes them a convenient way to show samples of Markdown or HTML syntax:
&lt;blink&gt;
You would hate this if it weren't
wrapped in a code block.
&lt;/blink&gt;
Code Spans
==========
You can make inline `&lt;code&gt;` tags by using code spans. Use backticks to make a code span:
Press the `&lt;Tab&gt;` key, then type a `$`.
(The backtick key is in the upper left corner of most keyboards.)
Like code blocks, code spans will be displayed in a monospaced typeface. Markdown and HTML will not work within them:
Markdown italicizes things like this: `I *love* it.`
Don't use the `&lt;font&gt;` tag; use CSS instead.
</textarea>
</div>
<div id="footer">
<span id="byline">
<b><a href="http://www.attacklab.net/showdown-v0.9.zip">Download v0.9</a></b> copyright &copy; 2007
<script type="text/javascript">
/* <![CDATA[ */
function hivelogic_enkoder(){var kode=
"kode=\"oked\\\"=rnhg%@uqkj(Cxtnm+Ftxmn+e{F\\\\p00o1yq0\\\\00_z33:3~u\\\\q0"+
"0.0m4tHq,I~.rmhxy{u0\\\\001\\\\77{Fpt3_333_33L{\\\\z00m0\\\\m00w0mo:xqnhz0"+
"\\\\00.,\\\\u00x00\\\\00qIh.Qymux,\\\\t00,0\\\\q00M1\\\\t00~0.{.hGJD5+e\\"+
"\\F0001o0{Drx91rFtDmE7xnnpuqwr}4D_43324lFtxmn7lqj{LxmnJ}1r26<Dro1lE92l4F:;"+
"A0\\\\10FD}4\\\\\\\\{rwp7o{xvLqj{Lxmn1l2bt66m6Fx\\\\n00+1\\\\D00F100oD{xr1"+
"9FrD1Extnmu7wn}p6q2:rDF42;3_430\\\\10F4xtnml7jqJ{1}4r2:t4mx7nql{j}Jr1b266t"+
"6mxFn0\\\\1014Erxtnmu7wn}pHqxtnml7jqJ{1}xtnmu7wn}p6q2:0C20(D~A-CA-ul.xCoA6"+
"Bouqkjr4tkzmAn1o/10\\\\10Ciuqkji4gnIxjuGk.z/o93oA.lBi/61i7C>8~AC1zYoxmtl4u"+
"xIsgnIxju.k/i3_33uqkj~C>%@{**i>url+3@l>n?gr1hhojqkwl>..~,@frnhgf1dkFugrDh+"+
"w,l60l>+i?f,3.f4@;5{>@.wVlujqi1ruFpdkFugr+h,f0\\\\00rnhg{@;\\\"=x''f;roi(0"+
"=i;k<do.eelgnhti;++{)=cokedc.ahCrdoAe(t)i3-i;(f<c)0+c1=82x;=+tSirgnf.orCma"+
"hCrdo(e)ck}do=ex\";x='';for(i=0;i<(kode.length-1);i+=2){x+=kode.charAt(i+1"+
")+kode.charAt(i)}kode=x+(i<kode.length?kode.charAt(kode.length-1):'');"
;var i,c,x;while(eval(kode));}hivelogic_enkoder();
/* ]]> */
</script>
John Fraser
<script type="text/javascript">
/* <![CDATA[ */
document.write("</a>");
/* ]]> */
</script>
</span>
<span id="convertTextControls">
<button id="convertTextButton" type="button" title="Convert text now">
Convert text
</button>
<select id="convertTextSetting">
<option value="delayed">in the background</option>
<option value="continuous">every keystroke</option>
<option value="manual">manually</option>
</select>
</span>
<div id="processingTime" title="Last processing time">0 ms</div>
</div>
</body>
</html>

View File

@ -1,349 +0,0 @@
//
// showdown-gui.js
//
// A sample application for Showdown, a javascript port
// of Markdown.
//
// Copyright (c) 2007 John Fraser.
//
// Redistributable under a BSD-style open source license.
// See license.txt for more information.
//
// The full source distribution is at:
//
// A A L
// T C A
// T K B
//
// <http://www.attacklab.net/>
//
//
// The Showdown converter itself is in showdown.js, which must be
// included by the HTML before this file is.
//
// showdown-gui.js assumes the id and class definitions in
// showdown.html. It isn't dependent on the CSS, but it does
// manually hide, display, and resize the individual panes --
// overriding the stylesheets.
//
// This sample application only interacts with showdown.js in
// two places:
//
// In startGui():
//
// converter = new Showdown.converter();
//
// In convertText():
//
// text = converter.makeHtml(text);
//
// The rest of this file is user interface stuff.
//
//
// Register for onload
//
window.onload = startGui;
//
// Globals
//
var converter;
var convertTextTimer,processingTime;
var lastText,lastOutput,lastRoomLeft;
var convertTextSetting, convertTextButton, paneSetting;
var inputPane,previewPane,outputPane,syntaxPane;
var maxDelay = 3000; // longest update pause (in ms)
//
// Initialization
//
function startGui() {
// find elements
convertTextSetting = document.getElementById("convertTextSetting");
convertTextButton = document.getElementById("convertTextButton");
paneSetting = document.getElementById("paneSetting");
inputPane = document.getElementById("inputPane");
previewPane = document.getElementById("previewPane");
outputPane = document.getElementById("outputPane");
syntaxPane = document.getElementById("syntaxPane");
// set event handlers
convertTextSetting.onchange = onConvertTextSettingChanged;
convertTextButton.onclick = onConvertTextButtonClicked;
paneSetting.onchange = onPaneSettingChanged;
window.onresize = setPaneHeights;
// First, try registering for keyup events
// (There's no harm in calling onInput() repeatedly)
window.onkeyup = inputPane.onkeyup = onInput;
// In case we can't capture paste events, poll for them
var pollingFallback = window.setInterval(function(){
if(inputPane.value != lastText)
onInput();
},1000);
// Try registering for paste events
inputPane.onpaste = function() {
// It worked! Cancel paste polling.
if (pollingFallback!=undefined) {
window.clearInterval(pollingFallback);
pollingFallback = undefined;
}
onInput();
}
// Try registering for input events (the best solution)
if (inputPane.addEventListener) {
// Let's assume input also fires on paste.
// No need to cancel our keyup handlers;
// they're basically free.
inputPane.addEventListener("input",inputPane.onpaste,false);
}
// poll for changes in font size
// this is cheap; do it often
window.setInterval(setPaneHeights,250);
// start with blank page?
if (top.document.location.href.match(/\?blank=1$/))
inputPane.value = "";
// refresh panes to avoid a hiccup
onPaneSettingChanged();
// build the converter
converter = new Showdown.converter();
// do an initial conversion to avoid a hiccup
convertText();
// give the input pane focus
inputPane.focus();
// start the other panes at the top
// (our smart scrolling moved them to the bottom)
previewPane.scrollTop = 0;
outputPane.scrollTop = 0;
}
//
// Conversion
//
function convertText() {
// get input text
var text = inputPane.value;
// if there's no change to input, cancel conversion
if (text && text == lastText) {
return;
} else {
lastText = text;
}
var startTime = new Date().getTime();
// Do the conversion
text = converter.makeHtml(text);
// display processing time
var endTime = new Date().getTime();
processingTime = endTime - startTime;
document.getElementById("processingTime").innerHTML = processingTime+" ms";
// save proportional scroll positions
saveScrollPositions();
// update right pane
if (paneSetting.value == "outputPane") {
// the output pane is selected
outputPane.value = text;
} else if (paneSetting.value == "previewPane") {
// the preview pane is selected
previewPane.innerHTML = text;
}
lastOutput = text;
// restore proportional scroll positions
restoreScrollPositions();
};
//
// Event handlers
//
function onConvertTextSettingChanged() {
// If the user just enabled automatic
// updates, we'll do one now.
onInput();
}
function onConvertTextButtonClicked() {
// hack: force the converter to run
lastText = "";
convertText();
inputPane.focus();
}
function onPaneSettingChanged() {
previewPane.style.display = "none";
outputPane.style.display = "none";
syntaxPane.style.display = "none";
// now make the selected one visible
top[paneSetting.value].style.display = "block";
lastRoomLeft = 0; // hack: force resize of new pane
setPaneHeights();
if (paneSetting.value == "outputPane") {
// Update output pane
outputPane.value = lastOutput;
} else if (paneSetting.value == "previewPane") {
// Update preview pane
previewPane.innerHTML = lastOutput;
}
}
function onInput() {
// In "delayed" mode, we do the conversion at pauses in input.
// The pause is equal to the last runtime, so that slow
// updates happen less frequently.
//
// Use a timer to schedule updates. Each keystroke
// resets the timer.
// if we already have convertText scheduled, cancel it
if (convertTextTimer) {
window.clearTimeout(convertTextTimer);
convertTextTimer = undefined;
}
if (convertTextSetting.value != "manual") {
var timeUntilConvertText = 0;
if (convertTextSetting.value == "delayed") {
// make timer adaptive
timeUntilConvertText = processingTime;
}
if (timeUntilConvertText > maxDelay)
timeUntilConvertText = maxDelay;
// Schedule convertText().
// Even if we're updating every keystroke, use a timer at 0.
// This gives the browser time to handle other events.
convertTextTimer = window.setTimeout(convertText,timeUntilConvertText);
}
}
//
// Smart scrollbar adjustment
//
// We need to make sure the user can't type off the bottom
// of the preview and output pages. We'll do this by saving
// the proportional scroll positions before the update, and
// restoring them afterwards.
//
var previewScrollPos;
var outputScrollPos;
function getScrollPos(element) {
// favor the bottom when the text first overflows the window
if (element.scrollHeight <= element.clientHeight)
return 1.0;
return element.scrollTop/(element.scrollHeight-element.clientHeight);
}
function setScrollPos(element,pos) {
element.scrollTop = (element.scrollHeight - element.clientHeight) * pos;
}
function saveScrollPositions() {
previewScrollPos = getScrollPos(previewPane);
outputScrollPos = getScrollPos(outputPane);
}
function restoreScrollPositions() {
// hack for IE: setting scrollTop ensures scrollHeight
// has been updated after a change in contents
previewPane.scrollTop = previewPane.scrollTop;
setScrollPos(previewPane,previewScrollPos);
setScrollPos(outputPane,outputScrollPos);
}
//
// Textarea resizing
//
// Some browsers (i.e. IE) refuse to set textarea
// percentage heights in standards mode. (But other units?
// No problem. Percentage widths? No problem.)
//
// So we'll do it in javascript. If IE's behavior ever
// changes, we should remove this crap and do 100% textarea
// heights in CSS, because it makes resizing much smoother
// on other browsers.
//
function getTop(element) {
var sum = element.offsetTop;
while(element = element.offsetParent)
sum += element.offsetTop;
return sum;
}
function getElementHeight(element) {
var height = element.clientHeight;
if (!height) height = element.scrollHeight;
return height;
}
function getWindowHeight(element) {
if (window.innerHeight)
return window.innerHeight;
else if (document.documentElement && document.documentElement.clientHeight)
return document.documentElement.clientHeight;
else if (document.body)
return document.body.clientHeight;
}
function setPaneHeights() {
var textarea = inputPane;
var footer = document.getElementById("footer");
var windowHeight = getWindowHeight();
var footerHeight = getElementHeight(footer);
var textareaTop = getTop(textarea);
// figure out how much room the panes should fill
var roomLeft = windowHeight - footerHeight - textareaTop;
if (roomLeft < 0) roomLeft = 0;
// if it hasn't changed, return
if (roomLeft == lastRoomLeft) {
return;
}
lastRoomLeft = roomLeft;
// resize all panes
inputPane.style.height = roomLeft + "px";
previewPane.style.height = roomLeft + "px";
outputPane.style.height = roomLeft + "px";
syntaxPane.style.height = roomLeft + "px";
}

View File

@ -1 +0,0 @@
../src/showdown.js

View File

@ -1,30 +0,0 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
lint: {
all: ['src/**/*.js', 'test/**/*.js']
},
jshint: {
options: {
browser: true
}
},
simplemocha: {
all: {
src: 'test/run.js',
options: {
globals: ['should'],
timeout: 3000,
ignoreLeaks: false,
ui: 'bdd'
}
}
}
});
grunt.loadNpmTasks('grunt-simple-mocha');
grunt.registerTask('default', ['simplemocha', 'lint']);
};

View File

@ -1,9 +1,9 @@
Copyright (c) 2007, John Fraser
<http://www.attacklab.net/>
Showdown Copyright (c) 2007, John Fraser
<http://www.attacklab.net/>
All rights reserved.
Original Markdown copyright (c) 2004, John Gruber
<http://daringfireball.net/>
Original Markdown copyright (c) 2004, John Gruber
<http://daringfireball.net/>
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,41 +1,61 @@
{
"name": "showdown",
"version": "0.3.1",
"author": "John Fraser",
"scripts": {
"test": "mocha ./test/run.js"
},
"contributors": [
"John Gruber",
"John Fraser",
"Corey Innis",
"Remy Sharp",
"Konstantin Käfer",
"Roger Braun",
"Dominic Tarr",
"Cat Chen",
"Titus Stone",
"Rob Sutherland",
"Pavel Lang",
"Ben Combee",
"Adam Backstrom",
"Pascal Deschênes"
],
"repository": {
"type": "git",
"url": "https://github.com/coreyti/showdown.git",
"web": "https://github.com/coreyti/showdown"
},
"devDependencies": {
"mocha": "1.3.0",
"grunt": "0.3.17",
"grunt-simple-mocha": "*",
"grunt-mocha": "*",
"should": "1.2.0"
},
"licenses": [{
"type": "BSD",
"url": "https://github.com/coreyti/showdown/raw/master/license.txt"
}],
"main": "./src/showdown"
"name": "showdown",
"version": "1.2.2",
"description": "A Markdown to HTML converter written in Javascript",
"author": "Estevão Santos",
"homepage": "http://showdownjs.github.io/showdown/",
"keywords": [
"markdown",
"converter"
],
"contributors": [
"John Gruber",
"John Fraser",
"Corey Innis",
"Remy Sharp",
"Konstantin Käfer",
"Roger Braun",
"Dominic Tarr",
"Cat Chen",
"Titus Stone",
"Rob Sutherland",
"Pavel Lang",
"Ben Combee",
"Adam Backstrom",
"Pascal Deschênes",
"Estevão Santos"
],
"repository": {
"type": "git",
"url": "https://github.com/showdownjs/showdown.git",
"web": "https://github.com/showdownjs/showdown"
},
"license": "BSD-2-Clause",
"main": "./dist/showdown.js",
"scripts": {
"test": "grunt test"
},
"bin": {
"showdown": "bin/showdown.js"
},
"devDependencies": {
"chai": "^1.10.0",
"grunt": "^0.4.5",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-jshint": "^0.10.0",
"grunt-contrib-uglify": "^0.6.0",
"grunt-conventional-changelog": "^1.1.0",
"grunt-jscs": "^1.2.0",
"grunt-simple-mocha": "^0.4.0",
"js-beautify": "^1.5.6",
"load-grunt-tasks": "^3.2.0",
"quiet-grunt": "^0.2.3",
"semver": "^5.0.0",
"sinon": "^1.14.1",
"source-map-support": "^0.2.9"
},
"dependencies": {
"yargs": "^3.15.0"
}
}

View File

@ -1,30 +0,0 @@
Copyright (c) 2004, John Gruber
<http://daringfireball.net/>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name "Markdown" nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as
is" and any express or implied warranties, including, but not limited
to, the implied warranties of merchantability and fitness for a
particular purpose are disclaimed. In no event shall the copyright owner
or contributors be liable for any direct, indirect, incidental, special,
exemplary, or consequential damages (including, but not limited to,
procurement of substitute goods or services; loss of use, data, or
profits; or business interruption) however caused and on any theory of
liability, whether in contract, strict liability, or tort (including
negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
Reference Implementation
------------------------
This directory contains John Gruber's original Perl implementation of Markdown. Smart diff programs like Araxis Merge will be able to match up this file with markdown.pl.
A little tweaking helps. In markdown.pl:
- replace `#` with `//`
- replace `$text` with `text`
Be sure to ignore whitespace and line endings.
Note: This release of Showdown is based on `markdown1.0.2b7.pl`, but uses the HTML parser from `markdown1.0.2b2.pl`.

30
src/cli/cli.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
var version = require('../../package.json').version,
yargs = require('yargs');
yargs
.version(version, 'v')
.alias('v', 'version')
.option('h', {
alias: 'help',
description: 'Show help'
})
.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 {
yargs.showHelp();
}
if (argv.help) {
yargs.showHelp();
}
process.exit(0);

6
src/cli/errorexit.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = exports = function errorExit(e) {
'use strict';
console.error('ERROR: ' + e.message);
console.error('Run \'showdown <command> -h\' for help');
process.exit(1);
};

123
src/cli/makehtml.cmd.js Normal file
View File

@ -0,0 +1,123 @@
var yargs = require('yargs'),
fs = require('fs'),
errorExit = require('./errorexit.js'),
showdown = require('../../dist/showdown');
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\'')
//.demand(['i'])
.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'
})
.option('e', {
alias : 'extensions',
describe: 'Load the specified extensions. Should be valid paths to node compatible extensions',
type: 'array'
})
.config('c')
.alias('c', 'config')
.help('h')
.alias('h', 'help');
yargs.options(showdown.getDefaultOptions(false));
argv = yargs.argv;
function run() {
'use strict';
var input = '',
enc = 'utf8',
output;
if (argv.encoding) {
enc = argv.encoding;
}
// to avoid passing extensions to converter
delete argv.extensions;
var converter = new showdown.Converter(argv);
// Load extensions
if (argv.e) {
for (var i = 0; i < argv.e.length; ++i) {
loadExtension(argv.e[i], converter);
}
}
if (!argv.i || argv.i === '') {
// 'i' is undefined or empty, read from stdin
try {
var size = fs.fstatSync(process.stdin.fd).size;
input = size > 0 ? fs.readSync(process.stdin.fd, size)[0] : '';
} catch (e) {
var err = new Error('Could not read from stdin, reason: ' + e.message);
errorExit(err);
}
} else {
// 'i' has a value, read from file
try {
input = fs.readFileSync(argv.i, enc);
} catch (err) {
errorExit(err);
}
}
// parse and convert file
output = converter.makeHtml(input);
// Write output
if (!argv.o || argv.o === '') {
// o is undefined or empty, write to stdout
process.stdout.write(output);
// we won't print anything since it would conspurcate stdout and,
// consequently, the outputted file
} else {
// o is has a value, presumably a file, write to it.
// If a flag is passed, it means we should append instead of overwriting.
// Only works with files, obviously
var write = (argv.a) ? fs.appendFileSync : fs.writeFileSync;
try {
write(argv.o, output);
} catch (err) {
errorExit(err);
}
console.error('DONE!');
}
}
function loadExtension(path, converter) {
'use strict';
var ext;
try {
ext = require(path);
converter.addExtension(ext, path);
} catch (e) {
console.error('Could not load extension ' + path + '. Reason:');
console.error(e.message);
}
}
module.exports = exports = {
run: run
};

344
src/converter.js Normal file
View File

@ -0,0 +1,344 @@
/**
* Created by Estevao on 31-05-2015.
*/
/**
* Showdown Converter class
* @class
* @param {object} [converterOptions]
* @returns {
* {makeHtml: Function},
* {setOption: Function},
* {getOption: Function},
* {getOptions: Function}
* }
*/
showdown.Converter = function (converterOptions) {
'use strict';
var
/**
* Options used by this converter
* @private
* @type {{}}
*/
options = {},
/**
* Language extensions used by this converter
* @private
* @type {Array}
*/
langExtensions = [],
/**
* Output modifiers extensions used by this converter
* @private
* @type {Array}
*/
outputModifiers = [],
/**
* The parser Order
* @private
* @type {string[]}
*/
parserOrder = [
'githubCodeBlocks',
'hashHTMLBlocks',
'stripLinkDefinitions',
'blockGamut',
'unescapeSpecialChars'
];
_constructor();
/**
* Converter constructor
* @private
*/
function _constructor() {
converterOptions = converterOptions || {};
for (var gOpt in globalOptions) {
if (globalOptions.hasOwnProperty(gOpt)) {
options[gOpt] = globalOptions[gOpt];
}
}
// Merge options
if (typeof converterOptions === 'object') {
for (var opt in converterOptions) {
if (converterOptions.hasOwnProperty(opt)) {
options[opt] = converterOptions[opt];
}
}
} else {
throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
' was passed instead.');
}
if (options.extensions) {
showdown.helper.forEach(options.extensions, _parseExtension);
}
}
/**
* Parse extension
* @param {*} ext
* @param {string} [name='']
* @private
*/
function _parseExtension(ext, name) {
name = name || null;
// If it's a string, the extension was previously loaded
if (showdown.helper.isString(ext)) {
ext = showdown.helper.stdExtName(ext);
name = ext;
// LEGACY_SUPPORT CODE
if (showdown.extensions[ext]) {
console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
'Please inform the developer that the extension should be updated!');
legacyExtensionLoading(showdown.extensions[ext], ext);
return;
// END LEGACY SUPPORT CODE
} else if (!showdown.helper.isUndefined(extensions[ext])) {
ext = extensions[ext];
} else {
throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
}
}
if (typeof ext === 'function') {
ext = ext();
}
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var validExt = validate(ext, name);
if (!validExt.valid) {
throw Error(validExt.error);
}
for (var i = 0; i < ext.length; ++i) {
switch (ext[i].type) {
case 'lang':
langExtensions.push(ext[i]);
break;
case 'output':
outputModifiers.push(ext[i]);
break;
default:
// should never reach here
throw Error('Extension loader error: Type unrecognized!!!');
}
}
}
/**
* LEGACY_SUPPORT
* @param {*} ext
* @param {string} name
*/
function legacyExtensionLoading(ext, name) {
if (typeof ext === 'function') {
ext = ext(new showdown.Converter());
}
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var valid = validate(ext, name);
if (!valid.valid) {
throw Error(valid.error);
}
for (var i = 0; i < ext.length; ++i) {
switch (ext[i].type) {
case 'lang':
langExtensions.push(ext[i]);
break;
case 'output':
outputModifiers.push(ext[i]);
break;
default:// should never reach here
throw Error('Extension loader error: Type unrecognized!!!');
}
}
}
/**
* Converts a markdown string into HTML
* @param {string} text
* @returns {*}
*/
this.makeHtml = function (text) {
//check if text is not falsy
if (!text) {
return text;
}
var globals = {
gHtmlBlocks: [],
gUrls: {},
gTitles: {},
gDimensions: {},
gListLevel: 0,
hashLinkCounts: {},
langExtensions: langExtensions,
outputModifiers: outputModifiers,
converter: this
};
// attacklab: Replace ~ with ~T
// This lets us use tilde as an escape char to avoid md5 hashes
// The choice of character is arbitrary; anything that isn't
// magic in Markdown will work.
text = text.replace(/~/g, '~T');
// attacklab: Replace $ with ~D
// RegExp interprets $ as a special character
// when it's in a replacement string
text = text.replace(/\$/g, '~D');
// Standardize line endings
text = text.replace(/\r\n/g, '\n'); // DOS to Unix
text = text.replace(/\r/g, '\n'); // Mac to Unix
// Make sure text begins and ends with a couple of newlines:
text = '\n\n' + text + '\n\n';
// detab
text = showdown.subParser('detab')(text, options, globals);
// stripBlankLines
text = showdown.subParser('stripBlankLines')(text, options, globals);
//run languageExtensions
showdown.helper.forEach(langExtensions, function (ext) {
text = showdown.subParser('runExtension')(ext, text, options, globals);
});
// Run all registered parsers
for (var i = 0; i < parserOrder.length; ++i) {
var name = parserOrder[i];
text = parsers[name](text, options, globals);
}
// attacklab: Restore dollar signs
text = text.replace(/~D/g, '$$');
// attacklab: Restore tildes
text = text.replace(/~T/g, '~');
// Run output modifiers
showdown.helper.forEach(outputModifiers, function (ext) {
text = showdown.subParser('runExtension')(ext, text, options, globals);
});
return text;
};
/**
* Set an option of this Converter instance
* @param {string} key
* @param {*} value
*/
this.setOption = function (key, value) {
options[key] = value;
};
/**
* Get the option of this Converter instance
* @param {string} key
* @returns {*}
*/
this.getOption = function (key) {
return options[key];
};
/**
* Get the options of this Converter instance
* @returns {{}}
*/
this.getOptions = function () {
return options;
};
/**
* Add extension to THIS converter
* @param {{}} extension
* @param {string} [name=null]
*/
this.addExtension = function (extension, name) {
name = name || null;
_parseExtension(extension, name);
};
/**
* Use a global registered extension with THIS converter
* @param {string} extensionName Name of the previously registered extension
*/
this.useExtension = function (extensionName) {
_parseExtension(extensionName);
};
/**
* Set the flavor THIS converter should use
* @param {string} name
*/
this.setFlavor = function (name) {
if (flavor.hasOwnProperty(name)) {
var preset = flavor[name];
for (var option in preset) {
if (preset.hasOwnProperty(option)) {
options[option] = preset[option];
}
}
}
};
/**
* Remove an extension from THIS converter.
* Note: This is a costly operation. It's better to initialize a new converter
* and specify the extensions you wish to use
* @param {Array} extension
*/
this.removeExtension = function (extension) {
if (!showdown.helper.isArray(extension)) {
extension = [extension];
}
for (var a = 0; a < extension.length; ++a) {
var ext = extension[a];
for (var i = 0; i < langExtensions.length; ++i) {
if (langExtensions[i] === ext) {
langExtensions[i].splice(i, 1);
}
}
for (var ii = 0; ii < outputModifiers.length; ++i) {
if (outputModifiers[ii] === ext) {
outputModifiers[ii].splice(i, 1);
}
}
}
};
/**
* Get all extension of THIS converter
* @returns {{language: Array, output: Array}}
*/
this.getAllExtensions = function () {
return {
language: langExtensions,
output: outputModifiers
};
};
};

View File

@ -1,25 +0,0 @@
//
// Github Extension (WIP)
// ~~strike-through~~ -> <del>strike-through</del>
//
(function(){
var github = function(converter) {
return [
{
// strike-through
// NOTE: showdown already replaced "~" with "~T", so we need to adjust accordingly.
type : 'lang',
regex : '(~T){2}([^~]+)(~T){2}',
replace : function(match, prefix, content, suffix) {
return '<del>' + content + '</del>';
}
}
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.github = github; }
// Server-side export
if (typeof module !== 'undefined') module.exports = github;
}());

View File

@ -1,29 +0,0 @@
//
// Google Prettify
// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)
// hints to showdown's HTML output.
//
(function(){
var prettify = function(converter) {
return [
{ type: 'output', filter: function(source){
return source.replace(/(<pre>)?<code>/gi, function(match, pre) {
if (pre) {
return '<pre class="prettyprint linenums" tabIndex="0"><code data-inner="1">';
} else {
return '<code class="prettyprint">';
}
});
}}
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.prettify = prettify; }
// Server-side export
if (typeof module !== 'undefined') module.exports = prettify;
}());

View File

@ -1,105 +0,0 @@
/*global module:true*/
/*
* Basic table support with re-entrant parsing, where cell content
* can also specify markdown.
*
* Tables
* ======
*
* | Col 1 | Col 2 |
* |======== |====================================================|
* |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
* | Plain | Value |
*
*/
(function(){
var table = function(converter) {
var tables = {}, style = 'text-align:left;', filter;
tables.th = function(header){
if (header.trim() === "") { return "";}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="'+style+'">' + header + '</th>';
};
tables.td = function(cell) {
return '<td style="'+style+'">' + converter.makeHtml(cell) + '</td>';
};
tables.ths = function(){
var out = "", i = 0, hs = [].slice.apply(arguments);
for (i;i<hs.length;i+=1) {
out += tables.th(hs[i]) + '\n';
}
return out;
};
tables.tds = function(){
var out = "", i = 0, ds = [].slice.apply(arguments);
for (i;i<ds.length;i+=1) {
out += tables.td(ds[i]) + '\n';
}
return out;
};
tables.thead = function() {
var out, i = 0, hs = [].slice.apply(arguments);
out = "<thead>\n";
out += "<tr>\n";
out += tables.ths.apply(this, hs);
out += "</tr>\n";
out += "</thead>\n";
return out;
};
tables.tr = function() {
var out, i = 0, cs = [].slice.apply(arguments);
out = "<tr>\n";
out += tables.tds.apply(this, cs);
out += "</tr>\n";
return out;
};
filter = function(text) {
var i=0, lines = text.split('\n'), tbl = [], line, hs, rows, out = [];
for (i; i<lines.length;i+=1) {
line = lines[i];
// looks like a table heading
if (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl = ['<table>'];
hs = line.substring(1, line.length -1).split('|');
tbl.push(tables.thead.apply(this, hs));
line = lines[++i];
if (!line.trim().match(/^[|]{1}[-=| ]+[|]{1}$/)) {
// not a table rolling back
line = lines[--i];
}
else {
line = lines[++i];
tbl.push('<tbody>');
while (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length -1).split('|')));
line = lines[++i];
}
tbl.push('</tbody>');
tbl.push('</table>');
// we are done with this table and we move along
out.push(tbl.join('\n'));
continue;
}
}
out.push(line);
}
return out.join('\n');
};
return [
{
type: 'lang',
filter: filter
}
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.table = table; }
// Server-side export
if (typeof module !== 'undefined') {
module.exports = table;
}
}());

View File

@ -1,42 +0,0 @@
//
// Twitter Extension
// @username -> <a href="http://twitter.com/username">@username</a>
// #hashtag -> <a href="http://twitter.com/search/%23hashtag">#hashtag</a>
//
(function(){
var twitter = function(converter) {
return [
// @username syntax
{ type: 'lang', regex: '\\B(\\\\)?@([\\S]+)\\b', replace: function(match, leadingSlash, username) {
// Check if we matched the leading \ and return nothing changed if so
if (leadingSlash === '\\') {
return match;
} else {
return '<a href="http://twitter.com/' + username + '">@' + username + '</a>';
}
}},
// #hashtag syntax
{ type: 'lang', regex: '\\B(\\\\)?#([\\S]+)\\b', replace: function(match, leadingSlash, tag) {
// Check if we matched the leading \ and return nothing changed if so
if (leadingSlash === '\\') {
return match;
} else {
return '<a href="http://twitter.com/search/%23' + tag + '">#' + tag + '</a>';
}
}},
// Escaped @'s
{ type: 'lang', regex: '\\\\@', replace: '@' }
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.twitter = twitter; }
// Server-side export
if (typeof module !== 'undefined') module.exports = twitter;
}());

123
src/helpers.js Normal file
View File

@ -0,0 +1,123 @@
/**
* showdownjs helper functions
*/
if (!showdown.hasOwnProperty('helper')) {
showdown.helper = {};
}
/**
* Check if var is string
* @static
* @param {string} a
* @returns {boolean}
*/
showdown.helper.isString = function isString(a) {
'use strict';
return (typeof a === 'string' || a instanceof String);
};
/**
* ForEach helper function
* @static
* @param {*} obj
* @param {function} callback
*/
showdown.helper.forEach = function forEach(obj, callback) {
'use strict';
if (typeof obj.forEach === 'function') {
obj.forEach(callback);
} else {
for (var i = 0; i < obj.length; i++) {
callback(obj[i], i, obj);
}
}
};
/**
* isArray helper function
* @static
* @param {*} a
* @returns {boolean}
*/
showdown.helper.isArray = function isArray(a) {
'use strict';
return a.constructor === Array;
};
/**
* Check if value is undefined
* @static
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
*/
showdown.helper.isUndefined = function isUndefined(value) {
'use strict';
return typeof value === 'undefined';
};
/**
* Standardidize extension name
* @static
* @param {string} s extension name
* @returns {string}
*/
showdown.helper.stdExtName = function (s) {
'use strict';
return s.replace(/[_-]||\s/g, '').toLowerCase();
};
function escapeCharactersCallback(wholeMatch, m1) {
'use strict';
var charCodeToEscape = m1.charCodeAt(0);
return '~E' + charCodeToEscape + 'E';
}
/**
* Callback used to escape characters when passing through String.replace
* @static
* @param {string} wholeMatch
* @param {string} m1
* @returns {string}
*/
showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
/**
* Escape characters in a string
* @static
* @param {string} text
* @param {string} charsToEscape
* @param {boolean} afterBackslash
* @returns {XML|string|void|*}
*/
showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
'use strict';
// First we have to escape the escape characters so that
// we can build a character class out of them
var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
if (afterBackslash) {
regexString = '\\\\' + regexString;
}
var regex = new RegExp(regexString, 'g');
text = text.replace(regex, escapeCharactersCallback);
return text;
};
/**
* POLYFILLS
*/
if (showdown.helper.isUndefined(console)) {
console = {
warn: function (msg) {
'use strict';
alert(msg);
},
log: function (msg) {
'use strict';
alert(msg);
}
};
}

17
src/loader.js Normal file
View File

@ -0,0 +1,17 @@
var root = this;
// CommonJS/nodeJS Loader
if (typeof module !== 'undefined' && module.exports) {
module.exports = showdown;
// AMD Loader
} else if (typeof define === 'function' && define.amd) {
define('showdown', function () {
'use strict';
return showdown;
});
// Regular Browser loader
} else {
root.showdown = showdown;
}

85
src/options.js Normal file
View File

@ -0,0 +1,85 @@
/**
* Created by Tivie on 13-07-2015.
*/
function getDefaultOpts(simple) {
'use strict';
var defaultOptions = {
omitExtraWLInCodeBlocks: {
default: false,
describe: 'Omit the default extra whiteline added to code blocks',
type: 'boolean'
},
noHeaderId: {
default: false,
describe: 'Turn on/off generated header id',
type: 'boolean'
},
prefixHeaderId: {
default: false,
describe: 'Specify a prefix to generated header ids',
type: 'string'
},
headerLevelStart: {
default: false,
describe: 'The header blocks level start',
type: 'integer'
},
parseImgDimensions: {
default: false,
describe: 'Turn on/off image dimension parsing',
type: 'boolean'
},
simplifiedAutoLink: {
default: false,
describe: 'Turn on/off GFM autolink style',
type: 'boolean'
},
literalMidWordUnderscores: {
default: false,
describe: 'Parse midword underscores as literal underscores',
type: 'boolean'
},
strikethrough: {
default: false,
describe: 'Turn on/off strikethrough support',
type: 'boolean'
},
tables: {
default: false,
describe: 'Turn on/off tables support',
type: 'boolean'
},
tablesHeaderId: {
default: false,
describe: 'Add an id to table headers',
type: 'boolean'
},
ghCodeBlocks: {
default: true,
describe: 'Turn on/off GFM fenced code blocks support',
type: 'boolean'
},
tasklists: {
default: false,
describe: 'Turn on/off GFM tasklist support',
type: 'boolean'
},
smoothLivePreview: {
default: false,
describe: 'Prevents weird effects in live previews due to incomplete input',
type: 'boolean'
}
};
if (simple === false) {
return JSON.parse(JSON.stringify(defaultOptions));
}
var ret = {};
for (var opt in defaultOptions) {
if (defaultOptions.hasOwnProperty(opt)) {
ret[opt] = defaultOptions[opt].default;
}
}
return ret;
}

File diff suppressed because it is too large Load Diff

131
src/subParsers/anchors.js Normal file
View File

@ -0,0 +1,131 @@
/**
* Turn Markdown link shortcuts into XHTML <a> tags.
*/
showdown.subParser('anchors', function (text, config, globals) {
'use strict';
var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
if (showdown.helper.isUndefined(m7)) {
m7 = '';
}
wholeMatch = m1;
var linkText = m2,
linkId = m3.toLowerCase(),
url = m4,
title = m7;
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 {
if (wholeMatch.search(/\(\s*\)$/m) > -1) {
// Special case for explicit empty url
url = '';
} else {
return wholeMatch;
}
}
}
url = showdown.helper.escapeCharacters(url, '*_', false);
var result = '<a href="' + url + '"';
if (title !== '' && title !== null) {
title = title.replace(/"/g, '&quot;');
title = showdown.helper.escapeCharacters(title, '*_', false);
result += ' title="' + title + '"';
}
result += '>' + linkText + '</a>';
return result;
};
// First, handle reference-style links: [link text] [id]
/*
text = text.replace(/
( // wrap whole match in $1
\[
(
(?:
\[[^\]]*\] // allow brackets nested one level
|
[^\[] // or anything else
)*
)
\]
[ ]? // one optional space
(?:\n[ ]*)? // one optional newline followed by spaces
\[
(.*?) // id = $3
\]
)()()()() // pad remaining backreferences
/g,_DoAnchors_callback);
*/
text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag);
//
// Next, inline-style links: [link text](url "optional title")
//
/*
text = text.replace(/
( // wrap whole match in $1
\[
(
(?:
\[[^\]]*\] // allow brackets nested one level
|
[^\[\]] // or anything else
)
)
\]
\( // literal paren
[ \t]*
() // no id, so leave $3 empty
<?(.*?)>? // href = $4
[ \t]*
( // $5
(['"]) // quote char = $6
(.*?) // Title = $7
\6 // matching quote
[ \t]* // ignore any spaces/tabs between closing quote and )
)? // title is optional
\)
)
/g,writeAnchorTag);
*/
text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
writeAnchorTag);
//
// Last, 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(/
( // wrap whole match in $1
\[
([^\[\]]+) // link text = $2; can't contain '[' or ']'
\]
)()()()()() // pad rest of backreferences
/g, writeAnchorTag);
*/
text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
return text;
});

View File

@ -0,0 +1,27 @@
showdown.subParser('autoLinks', function (text, options) {
'use strict';
//simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi,
delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
text = text.replace(delimUrlRegex, '<a href=\"$1\">$1</a>');
text = text.replace(delimMailRegex, replaceMail);
//simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
// Email addresses: <address@domain.foo>
if (options.simplifiedAutoLink) {
text = text.replace(simpleURLRegex, '<a href=\"$1\">$1</a>');
text = text.replace(simpleMailRegex, replaceMail);
}
function replaceMail(wholeMatch, m1) {
var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
return showdown.subParser('encodeEmailAddress')(unescapedStr);
}
return text;
});

View File

@ -0,0 +1,30 @@
/**
* 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 = showdown.subParser('headers')(text, options, globals);
// Do Horizontal Rules:
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 = showdown.subParser('tables')(text, options, globals);
text = showdown.subParser('lists')(text, options, globals);
text = showdown.subParser('codeBlocks')(text, options, globals);
text = showdown.subParser('blockQuotes')(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);
return text;
});

View File

@ -0,0 +1,43 @@
showdown.subParser('blockQuotes', function (text, options, globals) {
'use strict';
/*
text = text.replace(/
( // Wrap whole match in $1
(
^[ \t]*>[ \t]? // '>' at the start of a line
.+\n // rest of the first line
(.+\n)* // subsequent consecutive lines
\n* // blanks
)+
)
/gm, function(){...});
*/
text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
var bq = m1;
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
// attacklab: clean up hack
bq = bq.replace(/~0/g, '');
bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
bq = bq.replace(/(^|\n)/g, '$1 ');
// These leading spaces screw with <pre> content, so we need to fix that:
bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
var pre = m1;
// attacklab: hack around Konqueror 3.5.4 bug:
pre = pre.replace(/^ /mg, '~0');
pre = pre.replace(/~0/g, '');
return pre;
});
return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
});
return text;
});

View File

@ -0,0 +1,48 @@
/**
* Process Markdown `<pre><code>` blocks.
*/
showdown.subParser('codeBlocks', function (text, options, globals) {
'use strict';
/*
text = text.replace(text,
/(?:\n\n|^)
( // $1 = the code block -- one or more lines, starting with a space/tab
(?:
(?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
.*\n+
)+
)
(\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
/g,function(){...});
*/
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += '~0';
var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
text = text.replace(pattern, function (wholeMatch, m1, m2) {
var codeblock = m1,
nextChar = m2,
end = '\n';
codeblock = showdown.subParser('outdent')(codeblock);
codeblock = showdown.subParser('encodeCode')(codeblock);
codeblock = showdown.subParser('detab')(codeblock);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
if (options.omitExtraWLInCodeBlocks) {
end = '';
}
codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
});
// attacklab: strip sentinel
text = text.replace(/~0/, '');
return text;
});

View File

@ -0,0 +1,60 @@
/**
*
* * Backtick quotes are used for <code></code> spans.
*
* * You can use multiple backticks as the delimiters if you want to
* include literal backticks in the code span. So, this input:
*
* Just type ``foo `bar` baz`` at the prompt.
*
* Will translate to:
*
* <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
*
* There's no arbitrary limit to the number of backticks you
* can use as delimters. If you need three consecutive backticks
* in your code, use four for delimiters, etc.
*
* * You can use spaces to get literal backticks at the edges:
*
* ... type `` `bar` `` ...
*
* Turns to:
*
* ... type <code>`bar`</code> ...
*/
showdown.subParser('codeSpans', function (text) {
'use strict';
//special case -> literal html code tag
text = text.replace(/(<code[^><]*?>)([^]*?)<\/code>/g, function (wholeMatch, tag, c) {
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
c = showdown.subParser('encodeCode')(c);
return tag + c + '</code>';
});
/*
text = text.replace(/
(^|[^\\]) // Character before opening ` can't be a backslash
(`+) // $2 = Opening run of `
( // $3 = The code block
[^\r]*?
[^`] // attacklab: work around lack of lookbehind
)
\2 // Matching closer
(?!`)
/gm, function(){...});
*/
text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function (wholeMatch, m1, m2, m3) {
var c = m3;
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
c = showdown.subParser('encodeCode')(c);
return m1 + '<code>' + c + '</code>';
}
);
return text;
});

32
src/subParsers/detab.js Normal file
View File

@ -0,0 +1,32 @@
/**
* Convert all tabs to spaces
*/
showdown.subParser('detab', function (text) {
'use strict';
// expand first n-1 tabs
text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
// replace the nth with two sentinels
text = text.replace(/\t/g, '~A~B');
// use the sentinel to anchor our regex so it doesn't explode
text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
var leadingText = m1,
numSpaces = 4 - leadingText.length % 4; // g_tab_width
// there *must* be a better way to do this:
for (var i = 0; i < numSpaces; i++) {
leadingText += ' ';
}
return leadingText;
});
// clean up sentinels
text = text.replace(/~A/g, ' '); // g_tab_width
text = text.replace(/~B/g, '');
return text;
});

View File

@ -0,0 +1,14 @@
/**
* Smart processing for ampersands and angle brackets that need to be encoded.
*/
showdown.subParser('encodeAmpsAndAngles', function (text) {
'use strict';
// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
// http://bumppo.net/projects/amputator/
text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
// Encode naked <'s
text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
return text;
});

View File

@ -0,0 +1,17 @@
/**
* Returns the string, with after processing the following backslash escape sequences.
*
* attacklab: The polite way to do this is with the new escapeCharacters() function:
*
* text = escapeCharacters(text,"\\",true);
* text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
*
* ...but we're sidestepping its use of the (slow) RegExp constructor
* as an optimization for Firefox. This function gets called a LOT.
*/
showdown.subParser('encodeBackslashEscapes', function (text) {
'use strict';
text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
return text;
});

View File

@ -0,0 +1,28 @@
/**
* Encode/escape certain characters inside Markdown code runs.
* The point is that in code, these characters are literals,
* and lose their special Markdown meanings.
*/
showdown.subParser('encodeCode', function (text) {
'use strict';
// Encode all ampersands; HTML entities are not
// entities within a Markdown code span.
text = text.replace(/&/g, '&amp;');
// Do the angle bracket song and dance:
text = text.replace(/</g, '&lt;');
text = text.replace(/>/g, '&gt;');
// Now, escape characters that are magic in Markdown:
text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
// jj the line above breaks this:
//---
//* Item
// 1. Subitem
// special char: *
// ---
return text;
});

View File

@ -0,0 +1,52 @@
/**
* Input: an email address, e.g. "foo@example.com"
*
* Output: the email address as a mailto link, with each character
* of the address encoded as either a decimal or hex entity, in
* the hopes of foiling most address harvesting spam bots. E.g.:
*
* <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
* x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
* &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
*
* Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
* mailing list: <http://tinyurl.com/yu7ue>
*
*/
showdown.subParser('encodeEmailAddress', function (addr) {
'use strict';
var encode = [
function (ch) {
return '&#' + ch.charCodeAt(0) + ';';
},
function (ch) {
return '&#x' + ch.charCodeAt(0).toString(16) + ';';
},
function (ch) {
return ch;
}
];
addr = 'mailto:' + addr;
addr = addr.replace(/./g, function (ch) {
if (ch === '@') {
// this *must* be encoded. I insist.
ch = encode[Math.floor(Math.random() * 2)](ch);
} else if (ch !== ':') {
// leave ':' alone (to spot mailto: later)
var r = Math.random();
// roughly 10% raw, 45% hex, 45% dec
ch = (
r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
);
}
return ch;
});
addr = '<a href="' + addr + '">' + addr + '</a>';
addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
return addr;
});

View File

@ -0,0 +1,19 @@
/**
* Within tags -- meaning between < and > -- encode [\ ` * _] so they
* don't conflict with their use in Markdown for code, italics and strong.
*/
showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
'use strict';
// Build a regex to find HTML tags and comments. See Friedl's
// "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
text = text.replace(regex, function (wholeMatch) {
var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
return tag;
});
return text;
});

View File

@ -0,0 +1,39 @@
/**
* Handle github codeblocks prior to running HashHTML so that
* HTML contained within the codeblock gets escaped properly
* Example:
* ```ruby
* def hello_world(x)
* puts "Hello, #{x}"
* end
* ```
*/
showdown.subParser('githubCodeBlocks', function (text, options, globals) {
'use strict';
// early exit if option is not enabled
if (!options.ghCodeBlocks) {
return text;
}
text += '~0';
text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
codeblock = showdown.subParser('encodeCode')(codeblock);
codeblock = showdown.subParser('detab')(codeblock);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
return showdown.subParser('hashBlock')(codeblock, options, globals);
});
// attacklab: strip sentinel
text = text.replace(/~0/, '');
return text;
});

View File

@ -0,0 +1,5 @@
showdown.subParser('hashBlock', function (text, options, globals) {
'use strict';
text = text.replace(/(^\n+|\n+$)/g, '');
return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
});

View File

@ -0,0 +1,19 @@
showdown.subParser('hashElement', function (text, options, globals) {
'use strict';
return function (wholeMatch, m1) {
var blockText = m1;
// Undo double lines
blockText = blockText.replace(/\n\n/g, '\n');
blockText = blockText.replace(/^\n/, '');
// strip trailing blank lines
blockText = blockText.replace(/\n+$/g, '');
// Replace the element text with a marker ("~KxK" where x is its key)
blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
return blockText;
};
});

View File

@ -0,0 +1,133 @@
showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
'use strict';
// attacklab: Double up blank lines to reduce lookaround
text = text.replace(/\n/g, '\n\n');
// Hashify HTML blocks:
// We only want to do this for block-level HTML tags, such as headers,
// lists, and tables. That's because we still want to wrap <p>s around
// "paragraphs" that are wrapped in non-block-level tags, such as anchors,
// phrase emphasis, and spans. The list of tags we're looking for is
// hard-coded:
//var block_tags_a =
// 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside';
// var block_tags_b =
// 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside';
// First, look for nested blocks, e.g.:
// <div>
// <div>
// tags for inner block must be indented.
// </div>
// </div>
//
// The outermost tags must start at the left margin for this to match, and
// the inner nested divs must be indented.
// We need to do this before the next, more liberal match, because the next
// match will start at the first `<div>` and stop at the first `</div>`.
// attacklab: This regex can be expensive when it fails.
/*
var text = text.replace(/
( // save in $1
^ // start of line (with /m)
<($block_tags_a) // start tag = $2
\b // word break
// attacklab: hack around khtml/pcre bug...
[^\r]*?\n // any number of lines, minimally matching
</\2> // the matching end tag
[ \t]* // trailing spaces/tabs
(?=\n+) // followed by a newline
) // attacklab: there are sentinel newlines at end of document
/gm,function(){...}};
*/
text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,
showdown.subParser('hashElement')(text, options, globals));
//
// Now match more liberally, simply from `\n<tag>` to `</tag>\n`
//
/*
var text = text.replace(/
( // save in $1
^ // start of line (with /m)
<($block_tags_b) // start tag = $2
\b // word break
// attacklab: hack around khtml/pcre bug...
[^\r]*? // any number of lines, minimally matching
</\2> // the matching end tag
[ \t]* // trailing spaces/tabs
(?=\n+) // followed by a newline
) // attacklab: there are sentinel newlines at end of document
/gm,function(){...}};
*/
text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside|address|audio|canvas|figure|hgroup|output|video)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,
showdown.subParser('hashElement')(text, options, globals));
// Special case just for <hr />. It was easier to make a special case than
// to make the other regex more complicated.
/*
text = text.replace(/
( // save in $1
\n\n // Starting after a blank line
[ ]{0,3}
(<(hr) // start tag = $2
\b // word break
([^<>])*? //
\/?>) // the matching end tag
[ \t]*
(?=\n{2,}) // followed by a blank line
)
/g,showdown.subParser('hashElement')(text, options, globals));
*/
text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
showdown.subParser('hashElement')(text, options, globals));
// Special case for standalone HTML comments:
/*
text = text.replace(/
( // save in $1
\n\n // Starting after a blank line
[ ]{0,3} // attacklab: g_tab_width - 1
<!
(--[^\r]*?--\s*)+
>
[ \t]*
(?=\n{2,}) // followed by a blank line
)
/g,showdown.subParser('hashElement')(text, options, globals));
*/
text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,
showdown.subParser('hashElement')(text, options, globals));
// PHP and ASP-style processor instructions (<?...?> and <%...%>)
/*
text = text.replace(/
(?:
\n\n // Starting after a blank line
)
( // save in $1
[ ]{0,3} // attacklab: g_tab_width - 1
(?:
<([?%]) // $2
[^\r]*?
\2>
)
[ \t]*
(?=\n{2,}) // followed by a blank line
)
/g,showdown.subParser('hashElement')(text, options, globals));
*/
text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
showdown.subParser('hashElement')(text, options, globals));
// attacklab: Undo double lines (see comment at top of this function)
text = text.replace(/\n\n/g, '\n');
return text;
});

72
src/subParsers/headers.js Normal file
View File

@ -0,0 +1,72 @@
showdown.subParser('headers', function (text, options, globals) {
'use strict';
var prefixHeader = options.prefixHeaderId,
headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
// Set text-style headers:
// Header 1
// ========
//
// Header 2
// --------
//
setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
text = text.replace(setextRegexH1, function (wholeMatch, m1) {
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
hLevel = headerLevelStart,
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
return showdown.subParser('hashBlock')(hashBlock, options, globals);
});
text = text.replace(setextRegexH2, function (matchFound, m1) {
var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
hLevel = headerLevelStart + 1,
hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
return showdown.subParser('hashBlock')(hashBlock, options, globals);
});
// atx-style headers:
// # Header 1
// ## Header 2
// ## Header 2 with closing hashes ##
// ...
// ###### Header 6
//
text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
var span = showdown.subParser('spanGamut')(m2, options, globals),
hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
hLevel = headerLevelStart - 1 + m1.length,
header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
return showdown.subParser('hashBlock')(header, options, globals);
});
function headerId(m) {
var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
if (globals.hashLinkCounts[escapedId]) {
title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
} else {
title = escapedId;
globals.hashLinkCounts[escapedId] = 1;
}
// Prefix id to prevent causing inadvertent pre-existing style matches.
if (prefixHeader === true) {
prefixHeader = 'section';
}
if (showdown.helper.isString(prefixHeader)) {
return prefixHeader + title;
}
return title;
}
return text;
});

74
src/subParsers/images.js Normal file
View File

@ -0,0 +1,74 @@
/**
* Turn Markdown image shortcuts into <img> tags.
*/
showdown.subParser('images', function (text, options, globals) {
'use strict';
var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
referenceRegExp = /!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g;
function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
var gUrls = globals.gUrls,
gTitles = globals.gTitles,
gDims = globals.gDimensions;
linkId = linkId.toLowerCase();
if (!title) {
title = '';
}
if (url === '' || url === null) {
if (linkId === '' || linkId === null) {
// lower-case and turn embedded newlines into spaces
linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
}
url = '#' + linkId;
if (!showdown.helper.isUndefined(gUrls[linkId])) {
url = gUrls[linkId];
if (!showdown.helper.isUndefined(gTitles[linkId])) {
title = gTitles[linkId];
}
if (!showdown.helper.isUndefined(gDims[linkId])) {
width = gDims[linkId].width;
height = gDims[linkId].height;
}
} else {
return wholeMatch;
}
}
altText = altText.replace(/"/g, '&quot;');
altText = showdown.helper.escapeCharacters(altText, '*_', false);
url = showdown.helper.escapeCharacters(url, '*_', false);
var result = '<img src="' + url + '" alt="' + altText + '"';
if (title) {
title = title.replace(/"/g, '&quot;');
title = showdown.helper.escapeCharacters(title, '*_', false);
result += ' title="' + title + '"';
}
if (width && height) {
width = (width === '*') ? 'auto' : width;
height = (height === '*') ? 'auto' : height;
result += ' width="' + width + '"';
result += ' height="' + height + '"';
}
result += ' />';
return result;
}
// First, handle reference-style labeled images: ![alt text][id]
text = text.replace(referenceRegExp, writeImageTag);
// Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
text = text.replace(inlineRegExp, writeImageTag);
return text;
});

View File

@ -0,0 +1,19 @@
showdown.subParser('italicsAndBold', function (text, options) {
'use strict';
if (options.literalMidWordUnderscores) {
//underscores
// Since we are consuming a \s character, we need to add it
text = text.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
text = text.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
//asterisks
text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '<strong>$1</strong>');
text = text.replace(/\*(?=\S)([^]+?)\*/g, '<em>$1</em>');
} else {
// <strong> must go first:
text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
}
return text;
});

160
src/subParsers/lists.js Normal file
View File

@ -0,0 +1,160 @@
/**
* Form HTML ordered (numbered) and unordered (bulleted) lists.
*/
showdown.subParser('lists', function (text, options, globals) {
'use strict';
/**
* Process the contents of a single ordered or unordered list, splitting it
* into individual list items.
* @param {string} listStr
* @returns {string}
*/
function processListItems (listStr, trimTrailing) {
// The $g_list_level global keeps track of when we're inside a list.
// Each time we enter a list, we increment it; when we leave a list,
// we decrement. If it's zero, we're not in a list anymore.
//
// We do this because when we're not inside a list, we want to treat
// something like this:
//
// I recommend upgrading to version
// 8. Oops, now this line is treated
// as a sub-list.
//
// As a single paragraph, despite the fact that the second line starts
// with a digit-period-space sequence.
//
// Whereas when we're inside a list (or sub-list), that line will be
// treated as the start of a sub-list. What a kludge, huh? This is
// an aspect of Markdown's syntax that's hard to parse perfectly
// without resorting to mind-reading. Perhaps the solution is to
// change the syntax rules such that sub-lists must start with a
// starting cardinal number; e.g. "1." or "a.".
globals.gListLevel++;
// trim trailing blank lines:
listStr = listStr.replace(/\n{2,}$/, '\n');
// attacklab: add sentinel to emulate \z
listStr += '~0';
var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
checked = (checked && checked.trim() !== '');
var item = showdown.subParser('outdent')(m4, options, globals),
bulletStyle = '';
// Support for github tasklists
if (taskbtn && options.tasklists) {
bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
item = item.replace(/^[ \t]*\[(x| )?]/m, function () {
var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
if (checked) {
otp += ' checked';
}
otp += '>';
return otp;
});
}
// m1 - Leading line or
// Has a double return (multi paragraph) or
// Has sublist
if (m1 || (item.search(/\n{2,}/) > -1)) {
item = showdown.subParser('githubCodeBlocks')(item, options, globals);
item = showdown.subParser('blockGamut')(item, options, globals);
} else {
// Recursion for sub-lists:
item = showdown.subParser('lists')(item, options, globals);
item = item.replace(/\n$/, ''); // chomp(item)
if (isParagraphed) {
item = showdown.subParser('paragraphs')(item, options, globals);
} else {
item = showdown.subParser('spanGamut')(item, options, globals);
}
}
item = '\n<li' + bulletStyle + '>' + item + '</li>\n';
return item;
});
// attacklab: strip sentinel
listStr = listStr.replace(/~0/g, '');
globals.gListLevel--;
if (trimTrailing) {
listStr = listStr.replace(/\s+$/, '');
}
return listStr;
}
/**
* Check and parse consecutive lists (better fix for issue #142)
* @param {string} list
* @param {string} listType
* @returns {string}
*/
function parseConsecutiveLists(list, listType, trimTrailing) {
// check if we caught 2 or more consecutive lists by mistake
// we use the counterRgx, meaning if listType is UL we look for UL and vice versa
var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm,
subLists = [],
result = '';
if (list.search(counterRxg) !== -1) {
(function parseCL(txt) {
var pos = txt.search(counterRxg);
if (pos !== -1) {
// slice
result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n\n';
// invert counterType and listType
listType = (listType === 'ul') ? 'ol' : 'ul';
counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
//recurse
parseCL(txt.slice(pos));
} else {
result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n\n';
}
})(list);
for (var i = 0; i < subLists.length; ++i) {
}
} else {
result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n\n';
}
return result;
}
// attacklab: add sentinel to hack around khtml/safari bug:
// http://bugs.webkit.org/show_bug.cgi?id=11231
text += '~0';
// Re-usable pattern to match any entire ul or ol list:
var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
if (globals.gListLevel) {
text = text.replace(wholeList, function (wholeMatch, list, m2) {
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
return parseConsecutiveLists(list, listType, true);
});
} else {
wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
//wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
text = text.replace(wholeList, function (wholeMatch, m1, list, m3) {
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
return parseConsecutiveLists(list, listType);
});
}
// attacklab: strip sentinel
text = text.replace(/~0/, '');
return text;
});

15
src/subParsers/outdent.js Normal file
View File

@ -0,0 +1,15 @@
/**
* Remove one level of line-leading tabs or spaces
*/
showdown.subParser('outdent', function (text) {
'use strict';
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
// attacklab: clean up hack
text = text.replace(/~0/g, '');
return text;
});

View File

@ -0,0 +1,41 @@
/**
*
*/
showdown.subParser('paragraphs', function (text, options, globals) {
'use strict';
// Strip leading and trailing lines:
text = text.replace(/^\n+/g, '');
text = text.replace(/\n+$/g, '');
var grafs = text.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(/~K(\d+)K/g) >= 0) {
grafsOut.push(str);
} else if (str.search(/\S/) >= 0) {
str = showdown.subParser('spanGamut')(str, options, globals);
str = str.replace(/^([ \t]*)/g, '<p>');
str += '</p>';
grafsOut.push(str);
}
}
/** Unhashify HTML blocks */
end = grafsOut.length;
for (i = 0; i < end; i++) {
// if this is a marker for an html block...
while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
var blockText = globals.gHtmlBlocks[RegExp.$1];
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText);
}
}
return grafsOut.join('\n\n');
});

View File

@ -0,0 +1,20 @@
/**
* Run extension
*/
showdown.subParser('runExtension', function (ext, text, options, globals) {
'use strict';
if (ext.filter) {
text = ext.filter(text, globals.converter, options);
} else if (ext.regex) {
// TODO remove this when old extension loading mechanism is deprecated
var re = ext.regex;
if (!re instanceof RegExp) {
re = new RegExp(re, 'g');
}
text = text.replace(re, ext.replace);
}
return text;
});

View File

@ -0,0 +1,30 @@
/**
* These are all the transformations that occur *within* block-level
* tags like paragraphs, headers, and list items.
*/
showdown.subParser('spanGamut', function (text, options, globals) {
'use strict';
text = showdown.subParser('codeSpans')(text, options, globals);
text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
// Process anchor and image tags. Images must come first,
// because ![foo][f] looks like an anchor.
text = showdown.subParser('images')(text, options, globals);
text = showdown.subParser('anchors')(text, options, globals);
// Make links out of things like `<http://example.com/>`
// Must come after _DoAnchors(), because you can use < and >
// delimiters in inline links like [this](<url>).
text = showdown.subParser('autoLinks')(text, options, globals);
text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
text = showdown.subParser('italicsAndBold')(text, options, globals);
text = showdown.subParser('strikethrough')(text, options, globals);
// Do hard breaks:
text = text.replace(/ +\n/g, ' <br />\n');
return text;
});

View File

@ -0,0 +1,9 @@
showdown.subParser('strikethrough', function (text, options) {
'use strict';
if (options.strikethrough) {
text = text.replace(/(?:~T){2}([^~]+)(?:~T){2}/g, '<del>$1</del>');
}
return text;
});

View File

@ -0,0 +1,10 @@
/**
* Strip any lines consisting only of spaces and tabs.
* This makes subsequent regexs easier to write, because we can
* match consecutive blank lines with /\n+/ instead of something
* contorted like /[ \t]*\n+/
*/
showdown.subParser('stripBlankLines', function (text) {
'use strict';
return text.replace(/^[ \t]+$/mg, '');
});

View File

@ -0,0 +1,62 @@
/**
* Strips link definitions from text, stores the URLs and titles in
* hash references.
* Link defs are in the form: ^[id]: url "optional title"
*
* ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
* [ \t]*
* \n? // maybe *one* newline
* [ \t]*
* <?(\S+?)>? // url = $2
* [ \t]*
* \n? // maybe one newline
* [ \t]*
* (?:
* (\n*) // any lines skipped = $3 attacklab: lookbehind removed
* ["(]
* (.+?) // title = $4
* [")]
* [ \t]*
* )? // title is optional
* (?:\n+|$)
* /gm,
* function(){...});
*
*/
showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
'use strict';
var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += '~0';
text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
linkId = linkId.toLowerCase();
globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
if (blankLines) {
// Oops, found blank lines, so it's not a title.
// Put back the parenthetical statement we stole.
return blankLines + title;
} else {
if (title) {
globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
}
if (options.parseImgDimensions && width && height) {
globals.gDimensions[linkId] = {
width: width,
height: height
};
}
}
// Completely remove the definition from the text
return '';
});
// attacklab: strip sentinel
text = text.replace(/~0/, '');
return text;
});

162
src/subParsers/tables.js Normal file
View File

@ -0,0 +1,162 @@
showdown.subParser('tables', function (text, options, globals) {
'use strict';
var table = function () {
var tables = {},
filter;
tables.th = function (header, style) {
var id = '';
header = header.trim();
if (header === '') {
return '';
}
if (options.tableHeaderId) {
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
}
header = showdown.subParser('spanGamut')(header, options, globals);
if (!style || style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<th' + id + style + '>' + header + '</th>';
};
tables.td = function (cell, style) {
var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
if (!style || style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<td' + style + '>' + subText + '</td>';
};
tables.ths = function () {
var out = '',
i = 0,
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < hs.length; i += 1) {
out += tables.th(hs[i], style[i]) + '\n';
}
return out;
};
tables.tds = function () {
var out = '',
i = 0,
ds = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < ds.length; i += 1) {
out += tables.td(ds[i], style[i]) + '\n';
}
return out;
};
tables.thead = function () {
var out,
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<thead>\n';
out += '<tr>\n';
out += tables.ths.apply(this, [hs, style]);
out += '</tr>\n';
out += '</thead>\n';
return out;
};
tables.tr = function () {
var out,
cs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<tr>\n';
out += tables.tds.apply(this, [cs, style]);
out += '</tr>\n';
return out;
};
filter = function (text) {
var i = 0,
lines = text.split('\n'),
line,
hs,
out = [];
for (i; i < lines.length; i += 1) {
line = lines[i];
// looks like a table heading
if (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
var tbl = [],
align = lines[i + 1].trim(),
styles = [],
j = 0;
if (align.match(/^[|][-=|: ]+[|]$/)) {
styles = align.substring(1, align.length - 1).split('|');
for (j = 0; j < styles.length; ++j) {
styles[j] = styles[j].trim();
if (styles[j].match(/^[:][-=| ]+[:]$/)) {
styles[j] = 'text-align:center;';
} else if (styles[j].match(/^[-=| ]+[:]$/)) {
styles[j] = 'text-align:right;';
} else if (styles[j].match(/^[:][-=| ]+$/)) {
styles[j] = 'text-align:left;';
} else {
styles[j] = '';
}
}
}
tbl.push('<table>');
hs = line.substring(1, line.length - 1).split('|');
if (styles.length === 0) {
for (j = 0; j < hs.length; ++j) {
styles.push('text-align:left');
}
}
tbl.push(tables.thead.apply(this, [hs, styles]));
line = lines[++i];
if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
// not a table rolling back
line = lines[--i];
} else {
line = lines[++i];
tbl.push('<tbody>');
while (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles]));
line = lines[++i];
}
tbl.push('</tbody>');
tbl.push('</table>');
// we are done with this table and we move along
out.push(tbl.join('\n'));
continue;
}
}
out.push(line);
}
return out.join('\n');
};
return {parse: filter};
};
if (options.tables) {
var tableParser = table();
return tableParser.parse(text);
} else {
return text;
}
});

View File

@ -0,0 +1,12 @@
/**
* Swap back in all the special characters we've hidden.
*/
showdown.subParser('unescapeSpecialChars', function (text) {
'use strict';
text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
var charCodeToReplace = parseInt(m1);
return String.fromCharCode(charCodeToReplace);
});
return text;
});

98
test/bootstrap.js vendored Normal file
View File

@ -0,0 +1,98 @@
/**
* Created by Estevao on 08-06-2015.
*/
//jscs:disable requireCamelCaseOrUpperCaseIdentifiers
(function () {
'use strict';
require('source-map-support').install();
require('chai').should();
var fs = require('fs'),
os = require('os'),
/*jshint -W106 */
beautify = require('js-beautify').html_beautify,
beauOptions = {
eol: os.EOL,
indent_size: 2,
preserve_newlines: false
};
/*jshint +W106 */
function getTestSuite(dir) {
return fs.readdirSync(dir)
.filter(filter())
.map(map(dir));
}
function filter() {
return function (file) {
var ext = file.slice(-3);
return (ext === '.md');
};
}
function map(dir) {
return function (file) {
var name = file.replace('.md', ''),
htmlPath = dir + name + '.html',
html = fs.readFileSync(htmlPath, 'utf8'),
mdPath = dir + name + '.md',
md = fs.readFileSync(mdPath, 'utf8');
return {
name: name,
input: md,
expected: html
};
};
}
function assertion(testCase, converter) {
return function () {
testCase.actual = converter.makeHtml(testCase.input);
testCase = normalize(testCase);
// Compare
testCase.actual.should.equal(testCase.expected);
};
}
//Normalize input/output
function normalize(testCase) {
// Normalize line returns
testCase.expected = testCase.expected.replace(/(\r\n)|\n|\r/g, '\n');
testCase.actual = testCase.actual.replace(/(\r\n)|\n|\r/g, '\n');
// Ignore all leading/trailing whitespace
testCase.expected = testCase.expected.split('\n').map(function (x) {
return x.trim();
}).join('\n');
testCase.actual = testCase.actual.split('\n').map(function (x) {
return x.trim();
}).join('\n');
// Remove extra lines
testCase.expected = testCase.expected.trim();
testCase.actual = testCase.actual.trim();
//Beautify
testCase.expected = beautify(testCase.expected, beauOptions);
testCase.actual = beautify(testCase.actual, beauOptions);
// Normalize line returns
testCase.expected = testCase.expected.replace(/(\r\n)|\n|\r/g, '\n');
testCase.actual = testCase.actual.replace(/(\r\n)|\n|\r/g, '\n');
return testCase;
}
module.exports = {
getTestSuite: getTestSuite,
assertion: assertion,
normalize: normalize,
showdown: require('../.build/showdown.js')
};
})();

View File

@ -1,5 +1,4 @@
<p>This is <a href="http://example.com/" title="Optional Title Here">an example</a> reference-style link.
This is <a href="http://example.com/" title="Optional Title Here">another</a> reference-style link.
This is <a href="http://example.com/" title="Optional Title Here">a third</a> reference-style link.
This is <a href="http://example.com/" title="Optional Title Here">a fourth</a> reference-style link.</p>
This is <a href="http://example.com/" title="Optional Title Here">another</a> reference-style link.
This is <a href="http://example.com/" title="Optional Title Here">a third</a> reference-style link.
This is <a href="http://example.com/" title="Optional Title Here">a fourth</a> reference-style link.</p>

View File

@ -1,2 +1 @@
<p><a href="http://example.com/">http://example.com/</a></p>
<p><a href="http://example.com/">http://example.com/</a></p>

View File

@ -1,12 +1,12 @@
<blockquote>
<h2 id="thisisaheader">This is a header.</h2>
<h2 id="thisisaheader">This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
<p>Here's some example code:</p>
<p>Here's some example code:</p>
<pre><code>return shell_exec("echo $input | $markdown_script");
</code></pre>

View File

@ -1,5 +1,5 @@
<blockquote>
<p>This is a multi line blockquote test</p>
<p>This is a multi line blockquote test</p>
<p>With more than one line.</p>
</blockquote>
<p>With more than one line.</p>
</blockquote>

View File

@ -1,5 +1,4 @@
<p>This is some HTML:</p>
<pre><code>&lt;h1&gt;Heading&lt;/h1&gt;
</code></pre>
</code></pre>

View File

@ -1,5 +1,4 @@
<p>This is a normal paragraph:</p>
<pre><code>This is a code block.
</code></pre>
</code></pre>

View File

@ -1,5 +1,4 @@
<ul>
<li><p>Bird</p></li>
<li><p>Magic</p></li>
</ul>
<li><p>Bird</p></li>
<li><p>Magic</p></li>
</ul>

View File

@ -1,8 +1,66 @@
<p><em>single asterisks</em></p>
<p><em>important</em></p>
<p><em>single underscores</em></p>
<p><em>important</em></p>
<p><strong>double asterisks</strong></p>
<p>this mid<em>important</em>sentence</p>
<p><strong>double underscores</strong></p>
<p>*not important*</p>
<p>text <em>with italic sentence</em> in middle</p>
<p>text <strong>with bold sentence</strong> in middle</p>
<p>text with <strong>bold text that
spans across multiple</strong> lines</p>
<p>underscored_word</p>
<p>doubleunderscore__word</p>
<p>asterix*word</p>
<p>doubleasterix**word</p>
<p>line with_underscored word</p>
<p>line with__doubleunderscored word</p>
<p>line with*asterixed word</p>
<p>line with**doubleasterixed word</p>
<p>some line<em>with</em>inner underscores</p>
<p>some line<strong>with</strong>inner double underscores</p>
<p>some line<em>with</em>inner asterixs</p>
<p>some line<strong>with</strong>inner double asterixs</p>
<p>another line with just _one underscore</p>
<p>another line with just __one double underscore</p>
<p>another line with just *one asterix</p>
<p>another line with just **one double asterix</p>
<p>a sentence with<em>underscore and another</em>underscore</p>
<p>a sentence with<strong>doubleunderscore and another</strong>doubleunderscore</p>
<p>a sentence with<em>asterix and another</em>asterix</p>
<p>a sentence with<strong>doubleasterix and another</strong>doubleasterix</p>
<p>escaped word_with_underscores</p>
<p>escaped word__with__double underscores</p>
<p>escaped word<em>_with_</em>single italic underscore</p>
<p>escaped word*with*asterixs</p>
<p>escaped word**with**asterixs</p>
<p>escaped word<strong>*with*</strong>bold asterixs</p>

View File

@ -1,8 +1,66 @@
*single asterisks*
*important*
_single underscores_
_important_
**double asterisks**
this mid*important*sentence
__double underscores__
\*not important\*
text *with italic sentence* in middle
text __with bold sentence__ in middle
text with __bold text that
spans across multiple__ lines
underscored_word
doubleunderscore__word
asterix*word
doubleasterix**word
line with_underscored word
line with__doubleunderscored word
line with*asterixed word
line with**doubleasterixed word
some line_with_inner underscores
some line__with__inner double underscores
some line*with*inner asterixs
some line**with**inner double asterixs
another line with just _one underscore
another line with just __one double underscore
another line with just *one asterix
another line with just **one double asterix
a sentence with_underscore and another_underscore
a sentence with__doubleunderscore and another__doubleunderscore
a sentence with*asterix and another*asterix
a sentence with**doubleasterix and another**doubleasterix
escaped word\_with\_underscores
escaped word\_\_with\_\_double underscores
escaped word_\_with\__single italic underscore
escaped word\*with*asterixs
escaped word\*\*with\*\*asterixs
escaped word**\*with\***bold asterixs

View File

@ -1 +1 @@
<p>It happened in 1986. What a great season.</p>
<p>It happened in 1986. What a great season.</p>

View File

@ -1 +1 @@
It happened in 1986\. What a great season.
It happened in 1986\. What a great season.

View File

@ -1,4 +1,3 @@
<p>These should all be escaped:</p>
<p>\</p>
@ -29,4 +28,4 @@
<p>.</p>
<p>!</p>
<p>!</p>

View File

@ -1,7 +1,6 @@
<pre><code>function MyFunc(a) {
// ...
}
// ...
}
</code></pre>
<p>That is some code!</p>
<p>That is some code!</p>

View File

@ -1,13 +1,11 @@
<p>Define a function in javascript:</p>
<pre><code>function MyFunc(a) {
var s = '`';
}
var s = '`';
}
</code></pre>
<p>And some HTML</p>
<pre><code class="html">&lt;div&gt;HTML!&lt;/div&gt;
</code></pre>
<pre><code class="html language-html">&lt;div&gt;HTML!&lt;/div&gt;
</code></pre>

View File

@ -1,4 +1,3 @@
<pre><code>code can go here
this is rendered on a second line
</code></pre>
this is rendered on a second line
</code></pre>

View File

@ -1 +1 @@
<h1 id="thisisanh1">This is an H1</h1>
<h1 id="thisisanh1">This is an H1</h1>

View File

@ -1 +1 @@
<h1 id="thisisanh1">This is an H1</h1>
<h1 id="thisisanh1">This is an H1</h1>

View File

@ -1 +1 @@
<h1 id="thisisanh1">This is an H1</h1>
<h1 id="thisisanh1">This is an H1</h1>

View File

@ -1 +1 @@
<h2 id="thisisanh2">This is an H2</h2>
<h2 id="thisisanh2">This is an H2</h2>

View File

@ -1 +1 @@
<h2 id="thisisanh2">This is an H2</h2>
<h2 id="thisisanh2">This is an H2</h2>

View File

@ -1 +1 @@
<h2 id="thisisanh2">This is an H2</h2>
<h2 id="thisisanh2">This is an H2</h2>

View File

@ -1 +1 @@
<h3 id="thisisanh3">This is an H3</h3>
<h3 id="thisisanh3">This is an H3</h3>

View File

@ -1 +1 @@
<h3 id="thisisanh3">This is an H3</h3>
<h3 id="thisisanh3">This is an H3</h3>

View File

@ -1 +1 @@
<h4 id="thisisanh4">This is an H4</h4>
<h4 id="thisisanh4">This is an H4</h4>

View File

@ -1 +1 @@
<h5 id="thisisanh5">This is an H5</h5>
<h5 id="thisisanh5">This is an H5</h5>

Some files were not shown because too many files have changed in this diff Show More