mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
Merge branch 'master' into feature-table-extension
Conflicts: README.md src/showdown.js
This commit is contained in:
commit
dc28410a3b
94
README.md
94
README.md
|
@ -1,10 +1,6 @@
|
|||
# NOTE -- Showdown on GitHub
|
||||
|
||||
[![Build Status](https://travis-ci.org/pdeschen/showdown.png)](https://travis-ci.org/pdeschen/showdown])
|
||||
|
||||
NOTE -- Showdown on GitHub
|
||||
==========================
|
||||
|
||||
Please note that I, Corey, am not the author of Showdown. Rather, I found it
|
||||
**Please note** that I, Corey, 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.
|
||||
|
@ -23,8 +19,7 @@ Cheers,<br/>
|
|||
Corey
|
||||
|
||||
|
||||
Showdown -- A JavaScript port of Markdown
|
||||
=========================================
|
||||
# Showdown -- A JavaScript port of Markdown
|
||||
|
||||
Showdown Copyright (c) 2007 John Fraser.
|
||||
<http://www.attacklab.net/>
|
||||
|
@ -36,21 +31,18 @@ Redistributable under a BSD-style open source license.
|
|||
See license.txt for more information.
|
||||
|
||||
|
||||
Quick Example
|
||||
-------------
|
||||
## Quick Example
|
||||
|
||||
``` js
|
||||
```js
|
||||
var Showdown = require('showdown');
|
||||
var converter = new Showdown.converter();
|
||||
|
||||
converter.makeHtml('#hello markdown!');
|
||||
|
||||
// <h1 id="hellomarkdown">hello, markdown</h1>
|
||||
|
||||
```
|
||||
|
||||
What's it for?
|
||||
--------------
|
||||
## What's it for?
|
||||
|
||||
Developers can use Showdown to:
|
||||
|
||||
|
@ -92,23 +84,21 @@ Developers can use Showdown to:
|
|||
Markdown.)
|
||||
|
||||
|
||||
Browser Compatibility
|
||||
---------------------
|
||||
## 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
|
||||
* 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
|
||||
----------
|
||||
## Extensions
|
||||
|
||||
Showdown allows additional functionality to be loaded via extensions.
|
||||
|
||||
|
@ -118,7 +108,7 @@ Showdown allows additional functionality to be loaded via extensions.
|
|||
<script src="src/showdown.js" />
|
||||
<script src="src/extensions/twitter.js" />
|
||||
|
||||
var converter = new Showdown().converter({ extensions: ['twitter'] });
|
||||
var converter = new Showdown.converter({ extensions: 'twitter' });
|
||||
```
|
||||
|
||||
### Server-side Extension Usage
|
||||
|
@ -126,20 +116,17 @@ var converter = new Showdown().converter({ extensions: ['twitter'] });
|
|||
```js
|
||||
// Using a bundled extension
|
||||
var Showdown = require('showdown');
|
||||
var converter = new Showdown().converter({ extensions: ['twitter'] });
|
||||
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] });
|
||||
var converter = new Showdown.converter({ extensions: ['twitter', mine] });
|
||||
```
|
||||
|
||||
|
||||
## Known Differences in Output
|
||||
|
||||
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 email me if you find more.
|
||||
|
||||
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
|
||||
|
@ -151,7 +138,6 @@ In most cases, Showdown's output is identical to that of Perl Markdown v1.0.2b7.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
* Showdown doesn't support the markdown="1" attribute:
|
||||
|
||||
<div markdown="1">
|
||||
|
@ -221,8 +207,7 @@ In most cases, Showdown's output is identical to that of Perl Markdown v1.0.2b7.
|
|||
Showdown won't. But still, don't do that.
|
||||
|
||||
|
||||
Tests
|
||||
---------------------------
|
||||
## 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
|
||||
|
@ -234,21 +219,20 @@ Once installed the tests can be run from the project root using:
|
|||
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
|
||||
----------------------------
|
||||
## 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.
|
||||
* 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
|
||||
|
||||
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 substituation, 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.
|
||||
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.
|
||||
|
||||
#### Regex/Replace Example
|
||||
**Example:**
|
||||
|
||||
``` js
|
||||
var demo = function(converter) {
|
||||
|
@ -259,11 +243,11 @@ var demo = function(converter) {
|
|||
}
|
||||
```
|
||||
|
||||
#### Filter
|
||||
### 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).
|
||||
|
||||
#### Filter Example
|
||||
**Example:**
|
||||
|
||||
``` js
|
||||
var demo = function(converter) {
|
||||
|
@ -276,9 +260,9 @@ var demo = function(converter) {
|
|||
}
|
||||
```
|
||||
|
||||
#### Implementation Concerns
|
||||
### 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.
|
||||
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(){
|
||||
|
@ -301,7 +285,7 @@ Second, client-side extensions should add a property onto `Showdown.extensions`
|
|||
}());
|
||||
```
|
||||
|
||||
#### Testing Extensions
|
||||
### 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.
|
||||
|
||||
|
@ -314,11 +298,13 @@ Credits
|
|||
Author of Showdown
|
||||
* [John Gruber](http://daringfireball.net/projects/markdown/):<br/>
|
||||
Author of Markdown
|
||||
* Maintenance
|
||||
* Maintenance/Contributions
|
||||
* [Corey Innis](http://github.com/coreyti):<br/>
|
||||
GitHub project maintainer
|
||||
* [Remy Sharp](http://remysharp.com/):<br/>
|
||||
* [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/>
|
||||
|
@ -326,5 +312,13 @@ Credits
|
|||
* [Cat Chen](https://github.com/CatChen):<br/>
|
||||
Export fix
|
||||
* [Titus Stone](https://github.com/tstone):<br/>
|
||||
Mocha tests + bug fixes
|
||||
**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
|
||||
|
||||
|
|
5
compressed/extensions/github.js
Normal file
5
compressed/extensions/github.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
//
|
||||
// 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)})();
|
6
compressed/extensions/google-prettify.js
Normal file
6
compressed/extensions/google-prettify.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
//
|
||||
// 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.googlePrettify=a),typeof module!="undefined"&&(module.exports=a)})();
|
6
compressed/extensions/twitter.js
Normal file
6
compressed/extensions/twitter.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
//
|
||||
// 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
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "showdown",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"author": "John Fraser",
|
||||
"scripts": {
|
||||
"test": "mocha ./test/run.js"
|
||||
|
@ -14,7 +14,11 @@
|
|||
"Roger Braun",
|
||||
"Dominic Tarr",
|
||||
"Cat Chen",
|
||||
"Titus Stone"
|
||||
"Titus Stone",
|
||||
"Rob Sutherland",
|
||||
"Pavel Lang",
|
||||
"Ben Combee",
|
||||
"Adam Backstrom"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
25
src/extensions/github.js
Normal file
25
src/extensions/github.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// 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;
|
||||
}());
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// Google Prettify
|
||||
// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// Twitter Extension
|
||||
// @username -> <a href="http://twitter.com/username">@username</a>
|
||||
|
|
|
@ -128,7 +128,7 @@ if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof req
|
|||
return file.replace(/\.js$/, '');
|
||||
});
|
||||
// Load extensions into Showdown namespace
|
||||
extensions.forEach(function(ext){
|
||||
Showdown.forEach(extensions, function(ext){
|
||||
var name = stdExtName(ext);
|
||||
Showdown.extensions[name] = require('./extensions/' + ext);
|
||||
});
|
||||
|
@ -139,11 +139,11 @@ if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof req
|
|||
// Options:
|
||||
//
|
||||
|
||||
// Parse extensinos options into separate arrays
|
||||
// Parse extensions options into separate arrays
|
||||
if (converter_options && converter_options.extensions) {
|
||||
|
||||
// Iterate over each plugin
|
||||
forEach(converter_options.extensions, function(plugin){
|
||||
Showdown.forEach(converter_options.extensions, function(plugin){
|
||||
|
||||
// Assume it's a bundled plugin if a string is given
|
||||
if (typeof plugin === 'string') {
|
||||
|
@ -152,7 +152,7 @@ if (converter_options && converter_options.extensions) {
|
|||
|
||||
if (typeof plugin === 'function') {
|
||||
// Iterate over each extension within that plugin
|
||||
plugin(this).forEach(function(ext){
|
||||
Showdown.forEach(plugin(this), function(ext){
|
||||
// Sort extensions by type
|
||||
if (ext.type) {
|
||||
if (ext.type === 'language' || ext.type === 'lang') {
|
||||
|
@ -215,7 +215,7 @@ this.makeHtml = function(text) {
|
|||
text = text.replace(/^[ \t]+$/mg,"");
|
||||
|
||||
// Run language extensions
|
||||
g_lang_extensions.forEach(function(x){
|
||||
Showdown.forEach(g_lang_extensions, function(x){
|
||||
text = _ExecuteExtension(x, text);
|
||||
});
|
||||
|
||||
|
@ -240,7 +240,7 @@ this.makeHtml = function(text) {
|
|||
text = text.replace(/~T/g,"~");
|
||||
|
||||
// Run output modifiers
|
||||
g_output_modifiers.forEach(function(x){
|
||||
Showdown.forEach(g_output_modifiers, function(x){
|
||||
text = _ExecuteExtension(x, text);
|
||||
});
|
||||
|
||||
|
@ -286,7 +286,11 @@ var _StripLinkDefinitions = function(text) {
|
|||
/gm,
|
||||
function(){...});
|
||||
*/
|
||||
var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
|
||||
|
||||
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
|
||||
text += "~0";
|
||||
|
||||
text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,
|
||||
function (wholeMatch,m1,m2,m3,m4) {
|
||||
m1 = m1.toLowerCase();
|
||||
g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
|
||||
|
@ -303,6 +307,9 @@ var _StripLinkDefinitions = function(text) {
|
|||
}
|
||||
);
|
||||
|
||||
// attacklab: strip sentinel
|
||||
text = text.replace(/~0/,"");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@ -361,13 +368,13 @@ var _HashHTMLBlocks = function(text) {
|
|||
\b // word break
|
||||
// attacklab: hack around khtml/pcre bug...
|
||||
[^\r]*? // any number of lines, minimally matching
|
||||
.*</\2> // the matching end tag
|
||||
</\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)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
|
||||
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)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
|
||||
|
||||
// Special case just for <hr />. It was easier to make a special case than
|
||||
// to make the other regex more complicated.
|
||||
|
|
4
test/cases/github-style-linebreaks.html
Normal file
4
test/cases/github-style-linebreaks.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
<pre><code>code can go here
|
||||
this is rendered on a second line
|
||||
</code></pre>
|
4
test/cases/github-style-linebreaks.md
Normal file
4
test/cases/github-style-linebreaks.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
```
|
||||
code can go here
|
||||
this is rendered on a second line
|
||||
```
|
|
@ -13,4 +13,11 @@
|
|||
|
||||
<aside>ignore me</aside>
|
||||
|
||||
<article>read
|
||||
me</article>
|
||||
|
||||
<aside>
|
||||
ignore me
|
||||
</aside>
|
||||
|
||||
<p>the end</p>
|
|
@ -7,5 +7,10 @@ These HTML5 tags should pass through just fine.
|
|||
<nav>navigation</nav>
|
||||
<article>read me</article>
|
||||
<aside>ignore me</aside>
|
||||
<article>read
|
||||
me</article>
|
||||
<aside>
|
||||
ignore me
|
||||
</aside>
|
||||
|
||||
the end
|
5
test/extensions/github/basic.html
Normal file
5
test/extensions/github/basic.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<p><a href="http://github.github.com/github-flavored-markdown/">github-flavored markdown</a> adds support for:</p>
|
||||
|
||||
<ul>
|
||||
<li><del>strike-through text</del></li>
|
||||
</ul>
|
3
test/extensions/github/basic.md
Normal file
3
test/extensions/github/basic.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
[github-flavored markdown](http://github.github.com/github-flavored-markdown/) adds support for:
|
||||
|
||||
* ~~strike-through text~~
|
|
@ -14,7 +14,7 @@ var runTestsInDir = function(dir, converter) {
|
|||
});
|
||||
|
||||
// Run each test case (markdown -> html)
|
||||
cases.forEach(function(test){
|
||||
showdown.forEach(cases, function(test){
|
||||
var name = test.replace(/[-.]/g, ' ');
|
||||
it (name, function(){
|
||||
var mdpath = path.join(dir, test + '.md'),
|
||||
|
@ -70,7 +70,7 @@ if (path.existsSync('test/extensions')) {
|
|||
});
|
||||
|
||||
// Run tests in each extension sub-folder
|
||||
extensions.forEach(function(ext){
|
||||
showdown.forEach(extensions, function(ext){
|
||||
// Make sure extension exists
|
||||
var src = 'src/extensions/' + ext + '.js';
|
||||
if (!path.existsSync(src)) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user