mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
Major code refactoring
This commit is contained in:
parent
548becfd58
commit
eae5f0e01f
7
.gitattributes
vendored
7
.gitattributes
vendored
|
@ -1,7 +1,6 @@
|
|||
/test export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
/perlMarkdown export-ignore
|
||||
/example export-ignore
|
||||
grunt.js export-ignore
|
||||
|
||||
.jshintignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
Gruntfile.js export-ignore
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
.idea/
|
||||
build/
|
||||
.DS_Store
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.idea/
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Gruntfile.js
|
||||
dist/**/*.js
|
||||
dist/**/*.js
|
||||
build/**/*.js
|
||||
|
|
27
.jshintrc
Normal file
27
.jshintrc
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"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": false,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"globals": {
|
||||
"angular": true,
|
||||
"module": true,
|
||||
"define": true,
|
||||
"window": true,
|
||||
"showdown": true
|
||||
}
|
||||
}
|
39
CREDITS.md
Normal file
39
CREDITS.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
Credits
|
||||
=======
|
||||
- Showdown v2
|
||||
* [Estevão Santos](http://soares-dos-santos.com)<br/>
|
||||
Code Refactoring and project maintainer
|
||||
|
||||
- Showdown v1
|
||||
* [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
|
||||
* [Estevão Santos](http://soares-dos-santos.com)<br/>
|
||||
Bug fixing and later maintainer
|
||||
|
||||
- Original Project
|
||||
* [John Gruber](http://daringfireball.net/projects/markdown/)<br/>
|
||||
Author of Markdown
|
||||
* [John Fraser](http://attacklab.net/)
|
||||
Author of Showdown
|
68
Gruntfile.js
68
Gruntfile.js
|
@ -9,28 +9,13 @@ module.exports = function (grunt) {
|
|||
pkg: grunt.file.readJSON('package.json'),
|
||||
concat: {
|
||||
options: {
|
||||
separator: ';',
|
||||
sourceMap: true
|
||||
sourceMap: true,
|
||||
banner: ";/*! <%= pkg.name %> <%= grunt.template.today('dd-mm-yyyy') %> */\n(function(){\n 'use strict';\n",
|
||||
footer: "}).call(this)"
|
||||
},
|
||||
dist: {
|
||||
src: ['src/showdown.js', 'src/*.js'],
|
||||
dest: 'compressed/<%= pkg.name %>.js'
|
||||
},
|
||||
github_ext: {
|
||||
src: ['src/extensions/github.js'],
|
||||
dest: 'compressed/extensions/github.min.js'
|
||||
},
|
||||
prettify_ext: {
|
||||
src: ['src/extensions/prettify.js'],
|
||||
dest: 'compressed/extensions/prettify.min.js'
|
||||
},
|
||||
table_ext: {
|
||||
src: ['src/extensions/table.js'],
|
||||
dest: 'compressed/extensions/table.min.js'
|
||||
},
|
||||
twitter_ext: {
|
||||
src: ['src/extensions/twitter.js'],
|
||||
dest: 'compressed/extensions/twitter.min.js'
|
||||
src: ['src/showdown.js', 'src/helpers.js', 'src/subParsers/*.js', 'src/loader.js'],
|
||||
dest: 'dist/<%= pkg.name %>.js'
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
|
@ -39,41 +24,21 @@ module.exports = function (grunt) {
|
|||
},
|
||||
dist: {
|
||||
files: {
|
||||
'compressed/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
|
||||
}
|
||||
},
|
||||
github_ext: {
|
||||
files: {
|
||||
'compressed/extensions/github.min.js': ['<%= concat.github_ext.dest %>']
|
||||
}
|
||||
},
|
||||
prettify_ext: {
|
||||
files: {
|
||||
'compressed/extensions/prettify.min.js': ['<%= concat.prettify_ext.dest %>']
|
||||
}
|
||||
},
|
||||
table_ext: {
|
||||
files: {
|
||||
'compressed/extensions/table.min.js': ['<%= concat.table_ext.dest %>']
|
||||
}
|
||||
},
|
||||
twitter_ext: {
|
||||
files: {
|
||||
'compressed/extensions/twitter.min.js': ['<%= concat.twitter_ext.dest %>']
|
||||
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
|
||||
}
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js']
|
||||
files: ['Gruntfile.js', 'src/**/*.js']
|
||||
},
|
||||
simplemocha: {
|
||||
all: {
|
||||
src: 'test/run.js',
|
||||
src: 'test/**/*.js',
|
||||
options: {
|
||||
globals: ['should'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd'
|
||||
reporter: 'spec'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,16 +50,23 @@ module.exports = function (grunt) {
|
|||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
|
||||
// test
|
||||
/*
|
||||
grunt.registerTask('sourceMapsSupport', function() {
|
||||
'use strict';
|
||||
|
||||
//# sourceMappingURL=path/to/source.map
|
||||
sourceMapSupport.install();
|
||||
});
|
||||
*/
|
||||
grunt.registerTask('lint', ['jshint']);
|
||||
grunt.registerTask('test', ['simplemocha']);
|
||||
grunt.registerTask('test', ['jshint', 'concat', 'simplemocha']);
|
||||
grunt.registerTask('test-without-building', ['simplemocha']);
|
||||
|
||||
// build with uglify
|
||||
grunt.registerTask('build', ['concat', 'uglify']);
|
||||
|
||||
// Build with closure compiler
|
||||
grunt.registerTask('build-with-closure', ['test', 'concat', 'closure-compiler']);
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('default', []);
|
||||
|
||||
|
||||
};
|
||||
|
|
638
README.md
638
README.md
|
@ -1,315 +1,323 @@
|
|||
# Showdown
|
||||
|
||||
A JavaScript port of Markdown
|
||||
|
||||
## Note
|
||||
|
||||
> Showdown is now maintained by the [showdownjs](https://github.com/showdownjs) organization on Github.
|
||||
>
|
||||
> The organization needs members to maintain Showdown.
|
||||
>
|
||||
> Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.
|
||||
|
||||
## 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', filter: 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
|
||||
--------
|
||||
|
||||
A Markdown to HTML converter written in Javascript
|
||||
|
||||
## Note
|
||||
|
||||
> Showdown is now maintained by the [showdownjs](https://github.com/showdownjs) organization on Github.
|
||||
>
|
||||
> The organization needs members to maintain Showdown.
|
||||
>
|
||||
> Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.
|
||||
|
||||
## 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 Javascript's `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', filter: 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 folder using the md/html format and they will automatically be run when tests are run.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
- Showdown v2
|
||||
* [Estevão Santos](http://soares-dos-santos.com)<br/>
|
||||
Code Refactoring and Project Maintainer
|
||||
|
||||
- Showdown v1
|
||||
* [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
|
||||
* [Estevão Santos](http://soares-dos-santos.com):<br/>
|
||||
Bug fixes and later maintainer
|
||||
|
||||
- Original Project
|
||||
* [John Gruber](http://daringfireball.net/projects/markdown/)<br/>
|
||||
Author of Markdown
|
||||
* [John Fraser](http://attacklab.net/)<br/>
|
||||
Author of Showdown
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "showdown",
|
||||
"description": "JavaScript port of Markdown",
|
||||
"description": "A Markdown to HTML converter written in Javascript",
|
||||
"homepage": "https://github.com/showdownjs/showdown",
|
||||
"authors": [
|
||||
"John Fraser",
|
||||
"Corey Innis (https://github.com/coreyti)",
|
||||
"Estevão Santos (https://github.com/tivie)",
|
||||
"Pascal Deschênes (https://github.com/pdeschen)",
|
||||
"Estevão Santos (https://github.com/tivie)"
|
||||
"Corey Innis (https://github.com/coreyti)",
|
||||
"John Fraser"
|
||||
],
|
||||
"main": ["src/showdown.js"],
|
||||
"ignore": [
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2
compressed/Showdown.min.js
vendored
2
compressed/Showdown.min.js
vendored
File diff suppressed because one or more lines are too long
2
compressed/extensions/github.min.js
vendored
2
compressed/extensions/github.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/*! showdown 06-01-2015 */
|
||||
!function(){var a=function(){return[{type:"lang",regex:"(~T){2}([^~]+)(~T){2}",replace:function(a,b,c){return"<del>"+c+"</del>"}}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.github=a),"undefined"!=typeof module&&(module.exports=a)}();
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sources":["..\\..\\src\\extensions\\github.js"],"names":[],"mappings":"AAAA;AAAA,IACI,MAAM,CAAC,SAAS,EAAE,GAAG;AAAA,MACnB,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG;AAAA;AAAA;AAAA,CAGpD,QAAQ;AAAA,IACL,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC3B,MAAM;AAAA;AAAA,iBAEG,MAAM,CAAC,OAAO;AAAA,iBACd,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,KAAK,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW;AAAA,cAClF,IAAI,OAAO,IAAI;AAAA,cACf,KAAK,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;AAAA,cAC/B,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;AAAA,kBAC7C,MAAM,GAAG,GAAG,KAAK,OAAO,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAM5C,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,MAAM;AAAA,OAC7H,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM;AAAA","file":"github.min.js","sourcesContent":["//\r\n// Github Extension (WIP)\r\n// ~~strike-through~~ -> <del>strike-through</del>\r\n//\r\n\r\n(function(){\r\n var github = function(converter) {\r\n return [\r\n {\r\n // strike-through\r\n // NOTE: showdown already replaced \"~\" with \"~T\", so we need to adjust accordingly.\r\n type : 'lang',\r\n regex : '(~T){2}([^~]+)(~T){2}',\r\n replace : function(match, prefix, content, suffix) {\r\n return '<del>' + content + '</del>';\r\n }\r\n }\r\n ];\r\n };\r\n\r\n // Client-side export\r\n if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.github = github; }\r\n // Server-side export\r\n if (typeof module !== 'undefined') module.exports = github;\r\n}());\r\n"]}
|
2
compressed/extensions/prettify.min.js
vendored
2
compressed/extensions/prettify.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/*! showdown 06-01-2015 */
|
||||
!function(){var a=function(){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">'})}}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.prettify=a),"undefined"!=typeof module&&(module.exports=a)}();
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sources":["..\\..\\src\\extensions\\prettify.js"],"names":[],"mappings":"AAAA;AAAA,IACI,MAAM,CAAC,QAAQ;AAAA,IACf,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;AAAA,IAC1F,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;AAAA;AAAA;AAAA,CAGlC,QAAQ;AAAA;AAAA,IAEL,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC7B,MAAM;AAAA,cACA,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM;AAAA;AAAA,gBAErC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,GAAG,IAAI,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG;AAAA,oBACzD,EAAE,EAAE,GAAG;AAAA,wBACH,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AAAA,sBAC5E,IAAI;AAAA,wBACF,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAOjD,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG,QAAQ;AAAA,OACjI,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,QAAQ;AAAA;AAAA","file":"prettify.min.js","sourcesContent":["//\r\n// Google Prettify\r\n// A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)\r\n// hints to showdown's HTML output.\r\n//\r\n\r\n(function(){\r\n\r\n var prettify = function(converter) {\r\n return [\r\n { type: 'output', filter: function(source){\r\n\r\n return source.replace(/(<pre>)?<code>/gi, function(match, pre) {\r\n if (pre) {\r\n return '<pre class=\"prettyprint linenums\" tabIndex=\"0\"><code data-inner=\"1\">';\r\n } else {\r\n return '<code class=\"prettyprint\">';\r\n }\r\n });\r\n }}\r\n ];\r\n };\r\n\r\n // Client-side export\r\n if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.prettify = prettify; }\r\n // Server-side export\r\n if (typeof module !== 'undefined') module.exports = prettify;\r\n\r\n}());\r\n"]}
|
2
compressed/extensions/table.min.js
vendored
2
compressed/extensions/table.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/*! showdown 06-01-2015 */
|
||||
!function(){var a=function(a){var b,c={},d="text-align:left;";return c.th=function(a){if(""===a.trim())return"";var b=a.trim().replace(/ /g,"_").toLowerCase();return'<th id="'+b+'" style="'+d+'">'+a+"</th>"},c.td=function(b){return'<td style="'+d+'">'+a.makeHtml(b)+"</td>"},c.ths=function(){var a="",b=0,d=[].slice.apply(arguments);for(b;b<d.length;b+=1)a+=c.th(d[b])+"\n";return a},c.tds=function(){var a="",b=0,d=[].slice.apply(arguments);for(b;b<d.length;b+=1)a+=c.td(d[b])+"\n";return a},c.thead=function(){var a,b=[].slice.apply(arguments);return a="<thead>\n",a+="<tr>\n",a+=c.ths.apply(this,b),a+="</tr>\n",a+="</thead>\n"},c.tr=function(){var a,b=[].slice.apply(arguments);return a="<tr>\n",a+=c.tds.apply(this,b),a+="</tr>\n"},b=function(a){var b,d,e=0,f=a.split("\n"),g=[];for(e;e<f.length;e+=1){if(b=f[e],b.trim().match(/^[|]{1}.*[|]{1}$/)){b=b.trim();var h=[];if(h.push("<table>"),d=b.substring(1,b.length-1).split("|"),h.push(c.thead.apply(this,d)),b=f[++e],b.trim().match(/^[|]{1}[-=| ]+[|]{1}$/)){for(b=f[++e],h.push("<tbody>");b.trim().match(/^[|]{1}.*[|]{1}$/);)b=b.trim(),h.push(c.tr.apply(this,b.substring(1,b.length-1).split("|"))),b=f[++e];h.push("</tbody>"),h.push("</table>"),g.push(h.join("\n"));continue}b=f[--e]}g.push(b)}return g.join("\n")},[{type:"lang",filter:b}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.table=a),"undefined"!=typeof module&&(module.exports=a)}();
|
File diff suppressed because one or more lines are too long
2
compressed/extensions/twitter.min.js
vendored
2
compressed/extensions/twitter.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/*! showdown 06-01-2015 */
|
||||
!function(){var a=function(){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:"@"}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.twitter=a),"undefined"!=typeof module&&(module.exports=a)}();
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sources":["..\\..\\src\\extensions\\twitter.js"],"names":[],"mappings":"AAAA;AAAA,IACI,OAAO,CAAC,SAAS;AAAA,KAChB,QAAQ,QAAQ,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC;AAAA,KACjE,OAAO,SAAS,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,EAAE,CAAC;AAAA;AAAA;AAAA,CAG7E,QAAQ;AAAA;AAAA,IAEL,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC5B,MAAM;AAAA;AAAA,gBAEE,QAAQ,CAAC,MAAM;AAAA,cACjB,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ;AAAA,mBACzF,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;AAAA,gBACrE,EAAE,EAAE,YAAY;AAAA,oBACZ,MAAM,CAAC,KAAK;AAAA,kBACd,IAAI;AAAA,oBACF,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,KAAK,QAAQ,WAAW,QAAQ,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,gBAI9E,OAAO,CAAC,MAAM;AAAA,cAChB,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG;AAAA,mBACpF,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;AAAA,gBACrE,EAAE,EAAE,YAAY;AAAA,oBACZ,MAAM,CAAC,KAAK;AAAA,kBACd,IAAI;AAAA,oBACF,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,GAAG,WAAW,GAAG,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,eAI/E,OAAO,GAAG,CAAC;AAAA,cACZ,IAAI,GAAG,IAAI,GAAG,KAAK,WAAW,OAAO;AAAA;AAAA;AAAA;AAAA,OAI5C,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,OAAO;AAAA,OAC/H,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,OAAO;AAAA;AAAA","file":"twitter.min.js","sourcesContent":["//\r\n// Twitter Extension\r\n// @username -> <a href=\"http://twitter.com/username\">@username</a>\r\n// #hashtag -> <a href=\"http://twitter.com/search/%23hashtag\">#hashtag</a>\r\n//\r\n\r\n(function(){\r\n\r\n var twitter = function(converter) {\r\n return [\r\n\r\n // @username syntax\r\n { type: 'lang', regex: '\\\\B(\\\\\\\\)?@([\\\\S]+)\\\\b', replace: function(match, leadingSlash, username) {\r\n // Check if we matched the leading \\ and return nothing changed if so\r\n if (leadingSlash === '\\\\') {\r\n return match;\r\n } else {\r\n return '<a href=\"http://twitter.com/' + username + '\">@' + username + '</a>';\r\n }\r\n }},\r\n\r\n // #hashtag syntax\r\n { type: 'lang', regex: '\\\\B(\\\\\\\\)?#([\\\\S]+)\\\\b', replace: function(match, leadingSlash, tag) {\r\n // Check if we matched the leading \\ and return nothing changed if so\r\n if (leadingSlash === '\\\\') {\r\n return match;\r\n } else {\r\n return '<a href=\"http://twitter.com/search/%23' + tag + '\">#' + tag + '</a>';\r\n }\r\n }},\r\n\r\n // Escaped @'s\r\n { type: 'lang', regex: '\\\\\\\\@', replace: '@' }\r\n ];\r\n };\r\n\r\n // Client-side export\r\n if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.twitter = twitter; }\r\n // Server-side export\r\n if (typeof module !== 'undefined') module.exports = twitter;\r\n\r\n}());\r\n"]}
|
BIN
dist/showdown.js
vendored
Normal file
BIN
dist/showdown.js
vendored
Normal file
Binary file not shown.
BIN
dist/showdown.js.map
vendored
Normal file
BIN
dist/showdown.js.map
vendored
Normal file
Binary file not shown.
|
@ -1,83 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap-theme.min.css">
|
||||
<style>
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#ed-txt-area {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
display: block;
|
||||
padding: 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.42857143;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
#banner {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body ng-app="showdownTest">
|
||||
<div id="banner" class="jumbotron">
|
||||
<h1>Angular Showdown</h1>
|
||||
|
||||
<h3>Showdown integration with angular</h3>
|
||||
</div>
|
||||
<div class="container" data-ng-controller="shTestCtrl">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Markdown</h2>
|
||||
<textarea id="ed-txt-area" data-ng-model="mdText"></textarea>
|
||||
<p>*for syntax.txt to load, requires a localhost server</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h2>HTML</h2>
|
||||
<div data-sd-model-to-html="mdText"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../src/showdown.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
|
||||
<script src="../src/ng-showdown.js"></script>
|
||||
|
||||
<script>
|
||||
var shTest = angular.module('showdownTest', ['Showdown']);
|
||||
|
||||
shTest.controller('shTestCtrl', ['$scope', '$http', function ($scope, $http) {
|
||||
$scope.mdText = '**foo**';
|
||||
|
||||
$http.get('syntax.txt').then(
|
||||
function (data) {
|
||||
$scope.mdText = data.data;
|
||||
},
|
||||
function (error) {
|
||||
console.log(error);
|
||||
alert("couldn't load syntax.txt");
|
||||
}
|
||||
)
|
||||
}]);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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]:
|
||||
|
||||
> The overriding design goal for Markdown's
|
||||
> formatting syntax is to make it as readable
|
||||
> as possible. The idea is that a
|
||||
> Markdown-formatted document should be
|
||||
> publishable as-is, as plain text, without
|
||||
> looking like it's been marked up with tags
|
||||
> 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 <http://www.attacklab.net>.
|
||||
|
||||
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: <me@privacy.net>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 `<p>` 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:
|
||||
|
||||
> Skip a line and
|
||||
> indent the >'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:
|
||||
|
||||
> The syntax is based on the way email programs
|
||||
> usually do quotations. You don't need to hard-wrap
|
||||
> 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 `>` followed by a space:
|
||||
|
||||
Parragraph breaks in a blockquote:
|
||||
|
||||
> The > on the blank lines is optional.
|
||||
> Include it or don't; Markdown doesn't care.
|
||||
>
|
||||
> But your plain text looks better to
|
||||
> humans if you include the extra `>`
|
||||
> between paragraphs.
|
||||
|
||||
|
||||
Blockquotes within a blockquote:
|
||||
|
||||
> A standard blockquote is indented
|
||||
> > A nested blockquote is indented more
|
||||
> > > > You can nest to any depth.
|
||||
|
||||
|
||||
Lists in a blockquote:
|
||||
|
||||
> - A list in a blockquote
|
||||
> - With a > and space in front of it
|
||||
> * A sublist
|
||||
|
||||
Preformatted text in a blockquote:
|
||||
|
||||
> Indent five spaces total. The first
|
||||
> 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 <strike>funny</strike>.
|
||||
|
||||
Markdown is smart enough not to mangle your span-level HTML:
|
||||
|
||||
<u>Markdown works *fine* in here.</u>
|
||||
|
||||
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:
|
||||
|
||||
<div style="background-color: lightgray">
|
||||
You can <em>not</em> use Markdown in here.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 `<pre>` and `<code>` 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:
|
||||
|
||||
<blink>
|
||||
You would hate this if it weren't
|
||||
wrapped in a code block.
|
||||
</blink>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Code Spans
|
||||
==========
|
||||
|
||||
|
||||
You can make inline `<code>` tags by using code spans. Use backticks to make a code span:
|
||||
|
||||
Press the `<Tab>` 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 `<font>` 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 © 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>
|
|
@ -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";
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
../src/showdown.js
|
|
@ -1,896 +0,0 @@
|
|||
Markdown: Syntax
|
||||
================
|
||||
|
||||
<ul id="ProjectSubmenu">
|
||||
<li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
|
||||
<li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
|
||||
<li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
|
||||
<li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
|
||||
<li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Philosophy](#philosophy)
|
||||
* [Inline HTML](#html)
|
||||
* [Automatic Escaping for Special Characters](#autoescape)
|
||||
* [Block Elements](#block)
|
||||
* [Paragraphs and Line Breaks](#p)
|
||||
* [Headers](#header)
|
||||
* [Blockquotes](#blockquote)
|
||||
* [Lists](#list)
|
||||
* [Code Blocks](#precode)
|
||||
* [Horizontal Rules](#hr)
|
||||
* [Span Elements](#span)
|
||||
* [Links](#link)
|
||||
* [Emphasis](#em)
|
||||
* [Code](#code)
|
||||
* [Images](#img)
|
||||
* [Miscellaneous](#misc)
|
||||
* [Backslash Escapes](#backslash)
|
||||
* [Automatic Links](#autolink)
|
||||
|
||||
|
||||
**Note:** This document is itself written using Markdown; you
|
||||
can [see the source for it by adding '.text' to the URL][src].
|
||||
|
||||
[src]: /projects/markdown/syntax.text
|
||||
|
||||
* * *
|
||||
|
||||
<h2 id="overview">Overview</h2>
|
||||
|
||||
<h3 id="philosophy">Philosophy</h3>
|
||||
|
||||
Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
|
||||
|
||||
Readability, however, is emphasized above all else. A Markdown-formatted
|
||||
document should be publishable as-is, as plain text, without looking
|
||||
like it's been marked up with tags or formatting instructions. While
|
||||
Markdown's syntax has been influenced by several existing text-to-HTML
|
||||
filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
|
||||
[Grutatext] [5], and [EtText] [6] -- the single biggest source of
|
||||
inspiration for Markdown's syntax is the format of plain text email.
|
||||
|
||||
[1]: http://docutils.sourceforge.net/mirror/setext.html
|
||||
[2]: http://www.aaronsw.com/2002/atx/
|
||||
[3]: http://textism.com/tools/textile/
|
||||
[4]: http://docutils.sourceforge.net/rst.html
|
||||
[5]: http://www.triptico.com/software/grutatxt.html
|
||||
[6]: http://ettext.taint.org/doc/
|
||||
|
||||
To this end, Markdown's syntax is comprised entirely of punctuation
|
||||
characters, which punctuation characters have been carefully chosen so
|
||||
as to look like what they mean. E.g., asterisks around a word actually
|
||||
look like \*emphasis\*. Markdown lists look like, well, lists. Even
|
||||
blockquotes look like quoted passages of text, assuming you've ever
|
||||
used email.
|
||||
|
||||
|
||||
|
||||
<h3 id="html">Inline HTML</h3>
|
||||
|
||||
Markdown's syntax is intended for one purpose: to be used as a
|
||||
format for *writing* for the web.
|
||||
|
||||
Markdown is not a replacement for HTML, or even close to it. Its
|
||||
syntax is very small, corresponding only to a very small subset of
|
||||
HTML tags. The idea is *not* to create a syntax that makes it easier
|
||||
to insert HTML tags. In my opinion, HTML tags are already easy to
|
||||
insert. The idea for Markdown is to make it easy to read, write, and
|
||||
edit prose. HTML is a *publishing* format; Markdown is a *writing*
|
||||
format. Thus, Markdown's formatting syntax only addresses issues that
|
||||
can be conveyed in plain text.
|
||||
|
||||
For any markup that is not covered by Markdown's syntax, you simply
|
||||
use HTML itself. There's no need to preface it or delimit it to
|
||||
indicate that you're switching from Markdown to HTML; you just use
|
||||
the tags.
|
||||
|
||||
The only restrictions are that block-level HTML elements -- e.g. `<div>`,
|
||||
`<table>`, `<pre>`, `<p>`, etc. -- must be separated from surrounding
|
||||
content by blank lines, and the start and end tags of the block should
|
||||
not be indented with tabs or spaces. Markdown is smart enough not
|
||||
to add extra (unwanted) `<p>` tags around HTML block-level tags.
|
||||
|
||||
For example, to add an HTML table to a Markdown article:
|
||||
|
||||
This is a regular paragraph.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Foo</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This is another regular paragraph.
|
||||
|
||||
Note that Markdown formatting syntax is not processed within block-level
|
||||
HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
|
||||
HTML block.
|
||||
|
||||
Span-level HTML tags -- e.g. `<span>`, `<cite>`, or `<del>` -- can be
|
||||
used anywhere in a Markdown paragraph, list item, or header. If you
|
||||
want, you can even use HTML tags instead of Markdown formatting; e.g. if
|
||||
you'd prefer to use HTML `<a>` or `<img>` tags instead of Markdown's
|
||||
link or image syntax, go right ahead.
|
||||
|
||||
Unlike block-level HTML tags, Markdown syntax *is* processed within
|
||||
span-level tags.
|
||||
|
||||
|
||||
<h3 id="autoescape">Automatic Escaping for Special Characters</h3>
|
||||
|
||||
In HTML, there are two characters that demand special treatment: `<`
|
||||
and `&`. Left angle brackets are used to start tags; ampersands are
|
||||
used to denote HTML entities. If you want to use them as literal
|
||||
characters, you must escape them as entities, e.g. `<`, and
|
||||
`&`.
|
||||
|
||||
Ampersands in particular are bedeviling for web writers. If you want to
|
||||
write about 'AT&T', you need to write '`AT&T`'. You even need to
|
||||
escape ampersands within URLs. Thus, if you want to link to:
|
||||
|
||||
http://images.google.com/images?num=30&q=larry+bird
|
||||
|
||||
you need to encode the URL as:
|
||||
|
||||
http://images.google.com/images?num=30&q=larry+bird
|
||||
|
||||
in your anchor tag `href` attribute. Needless to say, this is easy to
|
||||
forget, and is probably the single most common source of HTML validation
|
||||
errors in otherwise well-marked-up web sites.
|
||||
|
||||
Markdown allows you to use these characters naturally, taking care of
|
||||
all the necessary escaping for you. If you use an ampersand as part of
|
||||
an HTML entity, it remains unchanged; otherwise it will be translated
|
||||
into `&`.
|
||||
|
||||
So, if you want to include a copyright symbol in your article, you can write:
|
||||
|
||||
©
|
||||
|
||||
and Markdown will leave it alone. But if you write:
|
||||
|
||||
AT&T
|
||||
|
||||
Markdown will translate it to:
|
||||
|
||||
AT&T
|
||||
|
||||
Similarly, because Markdown supports [inline HTML](#html), if you use
|
||||
angle brackets as delimiters for HTML tags, Markdown will treat them as
|
||||
such. But if you write:
|
||||
|
||||
4 < 5
|
||||
|
||||
Markdown will translate it to:
|
||||
|
||||
4 < 5
|
||||
|
||||
However, inside Markdown code spans and blocks, angle brackets and
|
||||
ampersands are *always* encoded automatically. This makes it easy to use
|
||||
Markdown to write about HTML code. (As opposed to raw HTML, which is a
|
||||
terrible format for writing about HTML syntax, because every single `<`
|
||||
and `&` in your example code needs to be escaped.)
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
|
||||
<h2 id="block">Block Elements</h2>
|
||||
|
||||
|
||||
<h3 id="p">Paragraphs and Line Breaks</h3>
|
||||
|
||||
A paragraph is simply one or more consecutive lines of text, separated
|
||||
by one or more blank lines. (A blank line is any line that looks like a
|
||||
blank line -- a line containing nothing but spaces or tabs is considered
|
||||
blank.) Normal paragraphs should not be indented with spaces or tabs.
|
||||
|
||||
The implication of the "one or more consecutive lines of text" rule is
|
||||
that Markdown supports "hard-wrapped" text paragraphs. This differs
|
||||
significantly from most other text-to-HTML formatters (including Movable
|
||||
Type's "Convert Line Breaks" option) which translate every line break
|
||||
character in a paragraph into a `<br />` tag.
|
||||
|
||||
When you *do* want to insert a `<br />` break tag using Markdown, you
|
||||
end a line with two or more spaces, then type return.
|
||||
|
||||
Yes, this takes a tad more effort to create a `<br />`, but a simplistic
|
||||
"every line break is a `<br />`" rule wouldn't work for Markdown.
|
||||
Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
|
||||
work best -- and look better -- when you format them with hard breaks.
|
||||
|
||||
[bq]: #blockquote
|
||||
[l]: #list
|
||||
|
||||
|
||||
|
||||
<h3 id="header">Headers</h3>
|
||||
|
||||
Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
|
||||
|
||||
Setext-style headers are "underlined" using equal signs (for first-level
|
||||
headers) and dashes (for second-level headers). For example:
|
||||
|
||||
This is an H1
|
||||
=============
|
||||
|
||||
This is an H2
|
||||
-------------
|
||||
|
||||
Any number of underlining `=`'s or `-`'s will work.
|
||||
|
||||
Atx-style headers use 1-6 hash characters at the start of the line,
|
||||
corresponding to header levels 1-6. For example:
|
||||
|
||||
# This is an H1
|
||||
|
||||
## This is an H2
|
||||
|
||||
###### This is an H6
|
||||
|
||||
Optionally, you may "close" atx-style headers. This is purely
|
||||
cosmetic -- you can use this if you think it looks better. The
|
||||
closing hashes don't even need to match the number of hashes
|
||||
used to open the header. (The number of opening hashes
|
||||
determines the header level.) :
|
||||
|
||||
# This is an H1 #
|
||||
|
||||
## This is an H2 ##
|
||||
|
||||
### This is an H3 ######
|
||||
|
||||
|
||||
<h3 id="blockquote">Blockquotes</h3>
|
||||
|
||||
Markdown uses email-style `>` characters for blockquoting. If you're
|
||||
familiar with quoting passages of text in an email message, then you
|
||||
know how to create a blockquote in Markdown. It looks best if you hard
|
||||
wrap the text and put a `>` before every line:
|
||||
|
||||
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
|
||||
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
|
||||
> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
|
||||
>
|
||||
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
|
||||
> id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
Markdown allows you to be lazy and only put the `>` before the first
|
||||
line of a hard-wrapped paragraph:
|
||||
|
||||
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
|
||||
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
|
||||
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
|
||||
|
||||
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
|
||||
id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
|
||||
adding additional levels of `>`:
|
||||
|
||||
> This is the first level of quoting.
|
||||
>
|
||||
> > This is nested blockquote.
|
||||
>
|
||||
> Back to the first level.
|
||||
|
||||
Blockquotes can contain other Markdown elements, including headers, lists,
|
||||
and code blocks:
|
||||
|
||||
> ## This is a header.
|
||||
>
|
||||
> 1. This is the first list item.
|
||||
> 2. This is the second list item.
|
||||
>
|
||||
> Here's some example code:
|
||||
>
|
||||
> return shell_exec("echo $input | $markdown_script");
|
||||
|
||||
Any decent text editor should make email-style quoting easy. For
|
||||
example, with BBEdit, you can make a selection and choose Increase
|
||||
Quote Level from the Text menu.
|
||||
|
||||
|
||||
<h3 id="list">Lists</h3>
|
||||
|
||||
Markdown supports ordered (numbered) and unordered (bulleted) lists.
|
||||
|
||||
Unordered lists use asterisks, pluses, and hyphens -- interchangably
|
||||
-- as list markers:
|
||||
|
||||
* Red
|
||||
* Green
|
||||
* Blue
|
||||
|
||||
is equivalent to:
|
||||
|
||||
+ Red
|
||||
+ Green
|
||||
+ Blue
|
||||
|
||||
and:
|
||||
|
||||
- Red
|
||||
- Green
|
||||
- Blue
|
||||
|
||||
Ordered lists use numbers followed by periods:
|
||||
|
||||
1. Bird
|
||||
2. McHale
|
||||
3. Parish
|
||||
|
||||
It's important to note that the actual numbers you use to mark the
|
||||
list have no effect on the HTML output Markdown produces. The HTML
|
||||
Markdown produces from the above list is:
|
||||
|
||||
<ol>
|
||||
<li>Bird</li>
|
||||
<li>McHale</li>
|
||||
<li>Parish</li>
|
||||
</ol>
|
||||
|
||||
If you instead wrote the list in Markdown like this:
|
||||
|
||||
1. Bird
|
||||
1. McHale
|
||||
1. Parish
|
||||
|
||||
or even:
|
||||
|
||||
3. Bird
|
||||
1. McHale
|
||||
8. Parish
|
||||
|
||||
you'd get the exact same HTML output. The point is, if you want to,
|
||||
you can use ordinal numbers in your ordered Markdown lists, so that
|
||||
the numbers in your source match the numbers in your published HTML.
|
||||
But if you want to be lazy, you don't have to.
|
||||
|
||||
If you do use lazy list numbering, however, you should still start the
|
||||
list with the number 1. At some point in the future, Markdown may support
|
||||
starting ordered lists at an arbitrary number.
|
||||
|
||||
List markers typically start at the left margin, but may be indented by
|
||||
up to three spaces. List markers must be followed by one or more spaces
|
||||
or a tab.
|
||||
|
||||
To make lists look nice, you can wrap items with hanging indents:
|
||||
|
||||
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||
viverra nec, fringilla in, laoreet vitae, risus.
|
||||
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
But if you want to be lazy, you don't have to:
|
||||
|
||||
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
|
||||
viverra nec, fringilla in, laoreet vitae, risus.
|
||||
* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
|
||||
Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
If list items are separated by blank lines, Markdown will wrap the
|
||||
items in `<p>` tags in the HTML output. For example, this input:
|
||||
|
||||
* Bird
|
||||
* Magic
|
||||
|
||||
will turn into:
|
||||
|
||||
<ul>
|
||||
<li>Bird</li>
|
||||
<li>Magic</li>
|
||||
</ul>
|
||||
|
||||
But this:
|
||||
|
||||
* Bird
|
||||
|
||||
* Magic
|
||||
|
||||
will turn into:
|
||||
|
||||
<ul>
|
||||
<li><p>Bird</p></li>
|
||||
<li><p>Magic</p></li>
|
||||
</ul>
|
||||
|
||||
List items may consist of multiple paragraphs. Each subsequent
|
||||
paragraph in a list item must be indented by either 4 spaces
|
||||
or one tab:
|
||||
|
||||
1. This is a list item with two paragraphs. Lorem ipsum dolor
|
||||
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
|
||||
mi posuere lectus.
|
||||
|
||||
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
|
||||
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
|
||||
sit amet velit.
|
||||
|
||||
2. Suspendisse id sem consectetuer libero luctus adipiscing.
|
||||
|
||||
It looks nice if you indent every line of the subsequent
|
||||
paragraphs, but here again, Markdown will allow you to be
|
||||
lazy:
|
||||
|
||||
* This is a list item with two paragraphs.
|
||||
|
||||
This is the second paragraph in the list item. You're
|
||||
only required to indent the first line. Lorem ipsum dolor
|
||||
sit amet, consectetuer adipiscing elit.
|
||||
|
||||
* Another item in the same list.
|
||||
|
||||
To put a blockquote within a list item, the blockquote's `>`
|
||||
delimiters need to be indented:
|
||||
|
||||
* A list item with a blockquote:
|
||||
|
||||
> This is a blockquote
|
||||
> inside a list item.
|
||||
|
||||
To put a code block within a list item, the code block needs
|
||||
to be indented *twice* -- 8 spaces or two tabs:
|
||||
|
||||
* A list item with a code block:
|
||||
|
||||
<code goes here>
|
||||
|
||||
|
||||
It's worth noting that it's possible to trigger an ordered list by
|
||||
accident, by writing something like this:
|
||||
|
||||
1986. What a great season.
|
||||
|
||||
In other words, a *number-period-space* sequence at the beginning of a
|
||||
line. To avoid this, you can backslash-escape the period:
|
||||
|
||||
1986\. What a great season.
|
||||
|
||||
|
||||
|
||||
<h3 id="precode">Code Blocks</h3>
|
||||
|
||||
Pre-formatted code blocks are used for writing about programming or
|
||||
markup source code. Rather than forming normal paragraphs, the lines
|
||||
of a code block are interpreted literally. Markdown wraps a code block
|
||||
in both `<pre>` and `<code>` tags.
|
||||
|
||||
To produce a code block in Markdown, simply indent every line of the
|
||||
block by at least 4 spaces or 1 tab. For example, given this input:
|
||||
|
||||
This is a normal paragraph:
|
||||
|
||||
This is a code block.
|
||||
|
||||
Markdown will generate:
|
||||
|
||||
<p>This is a normal paragraph:</p>
|
||||
|
||||
<pre><code>This is a code block.
|
||||
</code></pre>
|
||||
|
||||
One level of indentation -- 4 spaces or 1 tab -- is removed from each
|
||||
line of the code block. For example, this:
|
||||
|
||||
Here is an example of AppleScript:
|
||||
|
||||
tell application "Foo"
|
||||
beep
|
||||
end tell
|
||||
|
||||
will turn into:
|
||||
|
||||
<p>Here is an example of AppleScript:</p>
|
||||
|
||||
<pre><code>tell application "Foo"
|
||||
beep
|
||||
end tell
|
||||
</code></pre>
|
||||
|
||||
A code block continues until it reaches a line that is not indented
|
||||
(or the end of the article).
|
||||
|
||||
Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
|
||||
are automatically converted into HTML entities. This makes it very
|
||||
easy to include example HTML source code using Markdown -- just paste
|
||||
it and indent it, and Markdown will handle the hassle of encoding the
|
||||
ampersands and angle brackets. For example, this:
|
||||
|
||||
<div class="footer">
|
||||
© 2004 Foo Corporation
|
||||
</div>
|
||||
|
||||
will turn into:
|
||||
|
||||
<pre><code><div class="footer">
|
||||
&copy; 2004 Foo Corporation
|
||||
</div>
|
||||
</code></pre>
|
||||
|
||||
Regular Markdown syntax is not processed within code blocks. E.g.,
|
||||
asterisks are just literal asterisks within a code block. This means
|
||||
it's also easy to use Markdown to write about Markdown's own syntax.
|
||||
|
||||
|
||||
|
||||
<h3 id="hr">Horizontal Rules</h3>
|
||||
|
||||
You can produce a horizontal rule tag (`<hr />`) by placing three or
|
||||
more hyphens, asterisks, or underscores on a line by themselves. If you
|
||||
wish, you may use spaces between the hyphens or asterisks. Each of the
|
||||
following lines will produce a horizontal rule:
|
||||
|
||||
* * *
|
||||
|
||||
***
|
||||
|
||||
*****
|
||||
|
||||
- - -
|
||||
|
||||
---------------------------------------
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
<h2 id="span">Span Elements</h2>
|
||||
|
||||
<h3 id="link">Links</h3>
|
||||
|
||||
Markdown supports two style of links: *inline* and *reference*.
|
||||
|
||||
In both styles, the link text is delimited by [square brackets].
|
||||
|
||||
To create an inline link, use a set of regular parentheses immediately
|
||||
after the link text's closing square bracket. Inside the parentheses,
|
||||
put the URL where you want the link to point, along with an *optional*
|
||||
title for the link, surrounded in quotes. For example:
|
||||
|
||||
This is [an example](http://example.com/ "Title") inline link.
|
||||
|
||||
[This link](http://example.net/) has no title attribute.
|
||||
|
||||
Will produce:
|
||||
|
||||
<p>This is <a href="http://example.com/" title="Title">
|
||||
an example</a> inline link.</p>
|
||||
|
||||
<p><a href="http://example.net/">This link</a> has no
|
||||
title attribute.</p>
|
||||
|
||||
If you're referring to a local resource on the same server, you can
|
||||
use relative paths:
|
||||
|
||||
See my [About](/about/) page for details.
|
||||
|
||||
Reference-style links use a second set of square brackets, inside
|
||||
which you place a label of your choosing to identify the link:
|
||||
|
||||
This is [an example][id] reference-style link.
|
||||
|
||||
You can optionally use a space to separate the sets of brackets:
|
||||
|
||||
This is [an example] [id] reference-style link.
|
||||
|
||||
Then, anywhere in the document, you define your link label like this,
|
||||
on a line by itself:
|
||||
|
||||
[id]: http://example.com/ "Optional Title Here"
|
||||
|
||||
That is:
|
||||
|
||||
* Square brackets containing the link identifier (optionally
|
||||
indented from the left margin using up to three spaces);
|
||||
* followed by a colon;
|
||||
* followed by one or more spaces (or tabs);
|
||||
* followed by the URL for the link;
|
||||
* optionally followed by a title attribute for the link, enclosed
|
||||
in double or single quotes, or enclosed in parentheses.
|
||||
|
||||
The following three link definitions are equivalent:
|
||||
|
||||
[foo]: http://example.com/ "Optional Title Here"
|
||||
[foo]: http://example.com/ 'Optional Title Here'
|
||||
[foo]: http://example.com/ (Optional Title Here)
|
||||
|
||||
**Note:** There is a known bug in Markdown.pl 1.0.1 which prevents
|
||||
single quotes from being used to delimit link titles.
|
||||
|
||||
The link URL may, optionally, be surrounded by angle brackets:
|
||||
|
||||
[id]: <http://example.com/> "Optional Title Here"
|
||||
|
||||
You can put the title attribute on the next line and use extra spaces
|
||||
or tabs for padding, which tends to look better with longer URLs:
|
||||
|
||||
[id]: http://example.com/longish/path/to/resource/here
|
||||
"Optional Title Here"
|
||||
|
||||
Link definitions are only used for creating links during Markdown
|
||||
processing, and are stripped from your document in the HTML output.
|
||||
|
||||
Link definition names may consist of letters, numbers, spaces, and
|
||||
punctuation -- but they are *not* case sensitive. E.g. these two
|
||||
links:
|
||||
|
||||
[link text][a]
|
||||
[link text][A]
|
||||
|
||||
are equivalent.
|
||||
|
||||
The *implicit link name* shortcut allows you to omit the name of the
|
||||
link, in which case the link text itself is used as the name.
|
||||
Just use an empty set of square brackets -- e.g., to link the word
|
||||
"Google" to the google.com web site, you could simply write:
|
||||
|
||||
[Google][]
|
||||
|
||||
And then define the link:
|
||||
|
||||
[Google]: http://google.com/
|
||||
|
||||
Because link names may contain spaces, this shortcut even works for
|
||||
multiple words in the link text:
|
||||
|
||||
Visit [Daring Fireball][] for more information.
|
||||
|
||||
And then define the link:
|
||||
|
||||
[Daring Fireball]: http://daringfireball.net/
|
||||
|
||||
Link definitions can be placed anywhere in your Markdown document. I
|
||||
tend to put them immediately after each paragraph in which they're
|
||||
used, but if you want, you can put them all at the end of your
|
||||
document, sort of like footnotes.
|
||||
|
||||
Here's an example of reference links in action:
|
||||
|
||||
I get 10 times more traffic from [Google] [1] than from
|
||||
[Yahoo] [2] or [MSN] [3].
|
||||
|
||||
[1]: http://google.com/ "Google"
|
||||
[2]: http://search.yahoo.com/ "Yahoo Search"
|
||||
[3]: http://search.msn.com/ "MSN Search"
|
||||
|
||||
Using the implicit link name shortcut, you could instead write:
|
||||
|
||||
I get 10 times more traffic from [Google][] than from
|
||||
[Yahoo][] or [MSN][].
|
||||
|
||||
[google]: http://google.com/ "Google"
|
||||
[yahoo]: http://search.yahoo.com/ "Yahoo Search"
|
||||
[msn]: http://search.msn.com/ "MSN Search"
|
||||
|
||||
Both of the above examples will produce the following HTML output:
|
||||
|
||||
<p>I get 10 times more traffic from <a href="http://google.com/"
|
||||
title="Google">Google</a> than from
|
||||
<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
|
||||
or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
|
||||
|
||||
For comparison, here is the same paragraph written using
|
||||
Markdown's inline link style:
|
||||
|
||||
I get 10 times more traffic from [Google](http://google.com/ "Google")
|
||||
than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
|
||||
[MSN](http://search.msn.com/ "MSN Search").
|
||||
|
||||
The point of reference-style links is not that they're easier to
|
||||
write. The point is that with reference-style links, your document
|
||||
source is vastly more readable. Compare the above examples: using
|
||||
reference-style links, the paragraph itself is only 81 characters
|
||||
long; with inline-style links, it's 176 characters; and as raw HTML,
|
||||
it's 234 characters. In the raw HTML, there's more markup than there
|
||||
is text.
|
||||
|
||||
With Markdown's reference-style links, a source document much more
|
||||
closely resembles the final output, as rendered in a browser. By
|
||||
allowing you to move the markup-related metadata out of the paragraph,
|
||||
you can add links without interrupting the narrative flow of your
|
||||
prose.
|
||||
|
||||
|
||||
<h3 id="em">Emphasis</h3>
|
||||
|
||||
Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
|
||||
emphasis. Text wrapped with one `*` or `_` will be wrapped with an
|
||||
HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
|
||||
`<strong>` tag. E.g., this input:
|
||||
|
||||
*single asterisks*
|
||||
|
||||
_single underscores_
|
||||
|
||||
**double asterisks**
|
||||
|
||||
__double underscores__
|
||||
|
||||
will produce:
|
||||
|
||||
<em>single asterisks</em>
|
||||
|
||||
<em>single underscores</em>
|
||||
|
||||
<strong>double asterisks</strong>
|
||||
|
||||
<strong>double underscores</strong>
|
||||
|
||||
You can use whichever style you prefer; the lone restriction is that
|
||||
the same character must be used to open and close an emphasis span.
|
||||
|
||||
Emphasis can be used in the middle of a word:
|
||||
|
||||
un*frigging*believable
|
||||
|
||||
But if you surround an `*` or `_` with spaces, it'll be treated as a
|
||||
literal asterisk or underscore.
|
||||
|
||||
To produce a literal asterisk or underscore at a position where it
|
||||
would otherwise be used as an emphasis delimiter, you can backslash
|
||||
escape it:
|
||||
|
||||
\*this text is surrounded by literal asterisks\*
|
||||
|
||||
|
||||
|
||||
<h3 id="code">Code</h3>
|
||||
|
||||
To indicate a span of code, wrap it with backtick quotes (`` ` ``).
|
||||
Unlike a pre-formatted code block, a code span indicates code within a
|
||||
normal paragraph. For example:
|
||||
|
||||
Use the `printf()` function.
|
||||
|
||||
will produce:
|
||||
|
||||
<p>Use the <code>printf()</code> function.</p>
|
||||
|
||||
To include a literal backtick character within a code span, you can use
|
||||
multiple backticks as the opening and closing delimiters:
|
||||
|
||||
``There is a literal backtick (`) here.``
|
||||
|
||||
which will produce this:
|
||||
|
||||
<p><code>There is a literal backtick (`) here.</code></p>
|
||||
|
||||
The backtick delimiters surrounding a code span may include spaces --
|
||||
one after the opening, one before the closing. This allows you to place
|
||||
literal backtick characters at the beginning or end of a code span:
|
||||
|
||||
A single backtick in a code span: `` ` ``
|
||||
|
||||
A backtick-delimited string in a code span: `` `foo` ``
|
||||
|
||||
will produce:
|
||||
|
||||
<p>A single backtick in a code span: <code>`</code></p>
|
||||
|
||||
<p>A backtick-delimited string in a code span: <code>`foo`</code></p>
|
||||
|
||||
With a code span, ampersands and angle brackets are encoded as HTML
|
||||
entities automatically, which makes it easy to include example HTML
|
||||
tags. Markdown will turn this:
|
||||
|
||||
Please don't use any `<blink>` tags.
|
||||
|
||||
into:
|
||||
|
||||
<p>Please don't use any <code><blink></code> tags.</p>
|
||||
|
||||
You can write this:
|
||||
|
||||
`—` is the decimal-encoded equivalent of `—`.
|
||||
|
||||
to produce:
|
||||
|
||||
<p><code>&#8212;</code> is the decimal-encoded
|
||||
equivalent of <code>&mdash;</code>.</p>
|
||||
|
||||
|
||||
|
||||
<h3 id="img">Images</h3>
|
||||
|
||||
Admittedly, it's fairly difficult to devise a "natural" syntax for
|
||||
placing images into a plain text document format.
|
||||
|
||||
Markdown uses an image syntax that is intended to resemble the syntax
|
||||
for links, allowing for two styles: *inline* and *reference*.
|
||||
|
||||
Inline image syntax looks like this:
|
||||
|
||||
![Alt text](/path/to/img.jpg)
|
||||
|
||||
![Alt text](/path/to/img.jpg "Optional title")
|
||||
|
||||
That is:
|
||||
|
||||
* An exclamation mark: `!`;
|
||||
* followed by a set of square brackets, containing the `alt`
|
||||
attribute text for the image;
|
||||
* followed by a set of parentheses, containing the URL or path to
|
||||
the image, and an optional `title` attribute enclosed in double
|
||||
or single quotes.
|
||||
|
||||
Reference-style image syntax looks like this:
|
||||
|
||||
![Alt text][id]
|
||||
|
||||
Where "id" is the name of a defined image reference. Image references
|
||||
are defined using syntax identical to link references:
|
||||
|
||||
[id]: url/to/image "Optional title attribute"
|
||||
|
||||
As of this writing, Markdown has no syntax for specifying the
|
||||
dimensions of an image; if this is important to you, you can simply
|
||||
use regular HTML `<img>` tags.
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
|
||||
<h2 id="misc">Miscellaneous</h2>
|
||||
|
||||
<h3 id="autolink">Automatic Links</h3>
|
||||
|
||||
Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
|
||||
|
||||
<http://example.com/>
|
||||
|
||||
Markdown will turn this into:
|
||||
|
||||
<a href="http://example.com/">http://example.com/</a>
|
||||
|
||||
Automatic links for email addresses work similarly, except that
|
||||
Markdown will also perform a bit of randomized decimal and hex
|
||||
entity-encoding to help obscure your address from address-harvesting
|
||||
spambots. For example, Markdown will turn this:
|
||||
|
||||
<address@example.com>
|
||||
|
||||
into something like this:
|
||||
|
||||
<a href="mailto:addre
|
||||
ss@example.co
|
||||
m">address@exa
|
||||
mple.com</a>
|
||||
|
||||
which will render in a browser as a clickable link to "address@example.com".
|
||||
|
||||
(This sort of entity-encoding trick will indeed fool many, if not
|
||||
most, address-harvesting bots, but it definitely won't fool all of
|
||||
them. It's better than nothing, but an address published in this way
|
||||
will probably eventually start receiving spam.)
|
||||
|
||||
|
||||
|
||||
<h3 id="backslash">Backslash Escapes</h3>
|
||||
|
||||
Markdown allows you to use backslash escapes to generate literal
|
||||
characters which would otherwise have special meaning in Markdown's
|
||||
formatting syntax. For example, if you wanted to surround a word
|
||||
with literal asterisks (instead of an HTML `<em>` tag), you can use
|
||||
backslashes before the asterisks, like this:
|
||||
|
||||
\*literal asterisks\*
|
||||
|
||||
Markdown provides backslash escapes for the following characters:
|
||||
|
||||
\ backslash
|
||||
` backtick
|
||||
* asterisk
|
||||
_ underscore
|
||||
{} curly braces
|
||||
[] square brackets
|
||||
() parentheses
|
||||
# hash mark
|
||||
+ plus sign
|
||||
- minus sign (hyphen)
|
||||
. dot
|
||||
! exclamation mark
|
|
@ -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
|
||||
|
|
11
package.json
11
package.json
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "showdown",
|
||||
"version": "0.3.4",
|
||||
"author": "John Fraser",
|
||||
"version": "2.0.0-alpha1",
|
||||
"description": "A Markdown to HTML converter written in Javascript",
|
||||
"authors": "Estevão Santos",
|
||||
"contributors": [
|
||||
"John Gruber",
|
||||
"John Fraser",
|
||||
|
@ -30,18 +31,20 @@
|
|||
"url": "https://github.com/coreyti/showdown/raw/master/license.txt"
|
||||
}
|
||||
],
|
||||
"main": "./src/showdown",
|
||||
"main": "./dist/showdown.js",
|
||||
"scripts": {
|
||||
"test": "mocha ./test/run.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular": "^1.3.2",
|
||||
"chai": "^1.10.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-concat": "^0.5.0",
|
||||
"grunt-contrib-jshint": "^0.10.0",
|
||||
"grunt-contrib-uglify": "^0.6.0",
|
||||
"grunt-simple-mocha": "^0.4.0",
|
||||
"mocha": "*",
|
||||
"should": "^4.4.2"
|
||||
"should": "^4.4.2",
|
||||
"source-map-support": "^0.2.9"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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`.
|
28
src/ng-showdown.js → src/angular.js
vendored
28
src/ng-showdown.js → src/angular.js
vendored
|
@ -4,13 +4,14 @@
|
|||
|
||||
|
||||
//Check if AngularJs and Showdown is defined and only load ng-Showdown if both are present
|
||||
if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
||||
if (typeof angular !== 'undefined' && typeof showdown !== 'undefined') {
|
||||
|
||||
(function (module, Showdown) {
|
||||
(function (module, showdown) {
|
||||
'use strict';
|
||||
|
||||
module
|
||||
.provider('$Showdown', provider)
|
||||
.directive('sdModelToHtml', ['$Showdown', markdownToHtmlDirective])
|
||||
.provider('$showdown', provider)
|
||||
.directive('sdModelToHtml', ['$showdown', markdownToHtmlDirective])
|
||||
.filter('sdStripHtml', stripHtmlFilter);
|
||||
|
||||
/**
|
||||
|
@ -20,7 +21,6 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
* with the user configurations.
|
||||
* If the user wants to use a different configuration in a determined context, he can use the "classic" Showdown
|
||||
* object instead.
|
||||
*
|
||||
*/
|
||||
function provider() {
|
||||
|
||||
|
@ -35,9 +35,9 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
*
|
||||
* @param {string} key Config parameter key
|
||||
* @param {string} value Config parameter value
|
||||
*/
|
||||
*/ /* jshint validthis: true */
|
||||
this.setOption = function (key, value) {
|
||||
config.key = value;
|
||||
config[key] = value;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -68,7 +68,7 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
};
|
||||
|
||||
function SDObject() {
|
||||
var converter = new Showdown.converter(config);
|
||||
var converter = new showdown.Converter(config);
|
||||
|
||||
/**
|
||||
* Converts a markdown text into HTML
|
||||
|
@ -103,16 +103,16 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
* Usage example:
|
||||
* <div sd-md-to-html-model="markdownText" ></div>
|
||||
*
|
||||
* @param $Showdown
|
||||
* @param $showdown
|
||||
* @returns {*}
|
||||
*/
|
||||
function markdownToHtmlDirective($Showdown) {
|
||||
function markdownToHtmlDirective($showdown) {
|
||||
|
||||
var link = function (scope, element) {
|
||||
scope.$watch('model', function (newValue) {
|
||||
var val;
|
||||
if (typeof newValue === 'string') {
|
||||
val = $Showdown.makeHtml(newValue);
|
||||
val = $showdown.makeHtml(newValue);
|
||||
} else {
|
||||
val = typeof newValue;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
scope: {
|
||||
model: '=sdModelToHtml'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,11 +140,11 @@ if (typeof angular !== 'undefined' && typeof Showdown !== 'undefined') {
|
|||
};
|
||||
}
|
||||
|
||||
})(angular.module('Showdown', []), Showdown);
|
||||
})(angular.module('showdown', []), showdown);
|
||||
|
||||
} else {
|
||||
|
||||
/** TODO Since this library is opt out, maybe we should not throw an error so we can concatenate this
|
||||
script with the main lib */
|
||||
script with the main lib */
|
||||
// throw new Error("ng-showdown was not loaded because one of it's dependencies (AngularJS or Showdown) wasn't met");
|
||||
}
|
|
@ -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;
|
||||
}());
|
|
@ -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;
|
||||
|
||||
}());
|
|
@ -1,106 +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'), 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();
|
||||
var tbl = [];
|
||||
tbl.push('<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;
|
||||
}
|
||||
}());
|
|
@ -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;
|
||||
|
||||
}());
|
98
src/helpers.js
Normal file
98
src/helpers.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
function isString(a) {
|
||||
return (typeof a === 'string' || a instanceof String);
|
||||
}
|
||||
|
||||
function forEach(obj, callback) {
|
||||
if (typeof obj.forEach === 'function') {
|
||||
obj.forEach(callback);
|
||||
} else {
|
||||
var i, len = obj.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
callback(obj[i], i, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isArray(a) {
|
||||
return a.constructor === Array;
|
||||
}
|
||||
|
||||
function isUndefined(value) {
|
||||
return typeof value === 'undefined';
|
||||
}
|
||||
|
||||
var escapeCharactersCallback = function (wholeMatch, m1) {
|
||||
var charCodeToEscape = m1.charCodeAt(0);
|
||||
return '~E' + charCodeToEscape + 'E';
|
||||
};
|
||||
|
||||
var escapeCharacters = function (text, charsToEscape, afterBackslash) {
|
||||
// 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;
|
||||
};
|
||||
|
||||
if (!showdown.hasOwnProperty('helper')) {
|
||||
showdown.helper = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* isString helper function
|
||||
* @param a
|
||||
* @returns {boolean}
|
||||
*/
|
||||
showdown.helper.isString = isString;
|
||||
|
||||
/**
|
||||
* ForEach helper function
|
||||
* @param {*} obj
|
||||
* @param callback
|
||||
*/
|
||||
showdown.helper.forEach = forEach;
|
||||
|
||||
/**
|
||||
* isArray helper function
|
||||
* @param {*} a
|
||||
* @returns {boolean}
|
||||
*/
|
||||
showdown.helper.isArray = isArray;
|
||||
|
||||
/**
|
||||
* 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 = isUndefined;
|
||||
|
||||
/**
|
||||
* Callback used to escape characters when passing through String.replace
|
||||
* @param {string} wholeMatch
|
||||
* @param {string} m1
|
||||
* @returns {string}
|
||||
*/
|
||||
showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
|
||||
|
||||
/**
|
||||
* Escape characters in a string
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {string} charsToEscape
|
||||
* @param {boolean} afterBackslash
|
||||
* @returns {XML|string|void|*}
|
||||
*/
|
||||
showdown.helper.escapeCharacters = escapeCharacters;
|
20
src/loader.js
Normal file
20
src/loader.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Created by Estevao on 15-01-2015.
|
||||
*/
|
||||
|
||||
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 () {
|
||||
return showdown;
|
||||
});
|
||||
}
|
||||
// Regular Browser loader
|
||||
else {
|
||||
root.showdown = showdown;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
145
src/showdown.js
Normal file
145
src/showdown.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Created by Tivie on 06-01-2015.
|
||||
*/
|
||||
|
||||
// Private properties
|
||||
var showdown = {},
|
||||
parsers = {},
|
||||
globalOptions = {
|
||||
omitExtraWLInCodeBlocks: false
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
//
|
||||
/**
|
||||
* helper namespace
|
||||
* @type {{}}
|
||||
*/
|
||||
showdown.helper = {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
//
|
||||
|
||||
// Public properties
|
||||
showdown.extensions = {};
|
||||
|
||||
//Public methods
|
||||
showdown.setOption = function (key, value) {
|
||||
globalOptions[key] = value;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Static Method
|
||||
*
|
||||
* subParser(name) - Get a registered subParser
|
||||
* subParser(name, func) - Register a subParser
|
||||
* @param {string} name
|
||||
* @param {function} [func]
|
||||
* @returns {*}
|
||||
*/
|
||||
showdown.subParser = function (name, func) {
|
||||
if (showdown.helper.isString(name)) {
|
||||
if (typeof func !== 'undefined') {
|
||||
parsers[name] = func;
|
||||
} else {
|
||||
if (parsers.hasOwnProperty(name)) {
|
||||
return parsers[name];
|
||||
} else {
|
||||
throw Error('SubParser named ' + name + ' not registered!');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} [converterOptions]
|
||||
* @returns {{makeHtml: Function}}
|
||||
*/
|
||||
showdown.Converter = function (converterOptions) {
|
||||
|
||||
converterOptions = converterOptions || {};
|
||||
|
||||
var options = globalOptions,
|
||||
parserOrder = [
|
||||
'detab',
|
||||
'stripBlankLines',
|
||||
//runLanguageExtensions,
|
||||
'githubCodeBlocks',
|
||||
'hashHTMLBlocks',
|
||||
'stripLinkDefinitions',
|
||||
'blockGamut',
|
||||
'unescapeSpecialChars'
|
||||
];
|
||||
|
||||
// Merge options
|
||||
if (typeof converterOptions === 'object') {
|
||||
for (var opt in converterOptions) {
|
||||
if (converterOptions.hasOwnProperty(opt)) {
|
||||
options[opt] = converterOptions[opt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var makeHtml = function (text) {
|
||||
|
||||
//check if text is not falsy
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
var globals = {
|
||||
gHtmlBlocks: [],
|
||||
gUrls: {},
|
||||
gTitles: {},
|
||||
gListLevel: 0
|
||||
};
|
||||
|
||||
// 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';
|
||||
|
||||
// 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.forEach(g_output_modifiers, function (x) {
|
||||
// text = _ExecuteExtension(x, text);
|
||||
//});
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
makeHtml: makeHtml
|
||||
};
|
||||
};
|
136
src/subParsers/anchors.js
Normal file
136
src/subParsers/anchors.js
Normal file
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, '*_');
|
||||
var result = '<a href="' + url + '"';
|
||||
|
||||
if (title !== '' && title !== null) {
|
||||
title = title.replace(/"/g, '"');
|
||||
title = showdown.helper.escapeCharacters(title, '*_');
|
||||
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;
|
||||
|
||||
|
||||
});
|
33
src/subParsers/autoLinks.js
Normal file
33
src/subParsers/autoLinks.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
showdown.subParser('autoLinks', function (text) {
|
||||
'use strict';
|
||||
|
||||
text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi, "<a href=\"$1\">$1</a>");
|
||||
|
||||
// Email addresses: <address@domain.foo>
|
||||
|
||||
/*
|
||||
text = text.replace(/
|
||||
<
|
||||
(?:mailto:)?
|
||||
(
|
||||
[-.\w]+
|
||||
\@
|
||||
[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
|
||||
)
|
||||
>
|
||||
/gi, _DoAutoLinks_callback());
|
||||
*/
|
||||
text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
|
||||
function (wholeMatch, m1) {
|
||||
var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
|
||||
return showdown.subParser('encodeEmailAddress')(unescapedStr);
|
||||
}
|
||||
);
|
||||
|
||||
return text;
|
||||
|
||||
});
|
33
src/subParsers/blockGamut.js
Normal file
33
src/subParsers/blockGamut.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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('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;
|
||||
});
|
||||
|
50
src/subParsers/blockQuotes.js
Normal file
50
src/subParsers/blockQuotes.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
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;
|
||||
});
|
48
src/subParsers/codeBlocks.js
Normal file
48
src/subParsers/codeBlocks.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
|
||||
function (wholeMatch, m1, m2) {
|
||||
var codeblock = m1,
|
||||
nextChar = m2;
|
||||
|
||||
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 whitespace
|
||||
|
||||
codeblock = '<pre><code>' + codeblock + '\n</code></pre>';
|
||||
|
||||
return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
|
||||
}
|
||||
);
|
||||
|
||||
// attacklab: strip sentinel
|
||||
text = text.replace(/~0/, '');
|
||||
|
||||
return text;
|
||||
});
|
57
src/subParsers/codeSpans.js
Normal file
57
src/subParsers/codeSpans.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* * 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';
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
});
|
38
src/subParsers/detab.js
Normal file
38
src/subParsers/detab.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
});
|
18
src/subParsers/encodeAmpsAndAngles.js
Normal file
18
src/subParsers/encodeAmpsAndAngles.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, '&');
|
||||
|
||||
// Encode naked <'s
|
||||
text = text.replace(/<(?![a-z\/?\$!])/gi, '<');
|
||||
|
||||
return text;
|
||||
});
|
21
src/subParsers/encodeBackslashEscapes.js
Normal file
21
src/subParsers/encodeBackslashEscapes.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
33
src/subParsers/encodeCode.js
Normal file
33
src/subParsers/encodeCode.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, '&');
|
||||
|
||||
// Do the angle bracket song and dance:
|
||||
text = text.replace(/</g, '<');
|
||||
text = text.replace(/>/g, '>');
|
||||
|
||||
// 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;
|
||||
|
||||
});
|
60
src/subParsers/encodeEmailAddress.js
Normal file
60
src/subParsers/encodeEmailAddress.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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="mailto:foo@e
|
||||
* xample.com">foo
|
||||
* @example.com</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;
|
||||
|
||||
});
|
23
src/subParsers/escapeSpecialCharsWithinTagAttributes.js
Normal file
23
src/subParsers/escapeSpecialCharsWithinTagAttributes.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, '\\`*_');
|
||||
return tag;
|
||||
});
|
||||
|
||||
return text;
|
||||
});
|
46
src/subParsers/githubCodeBlocks.js
Normal file
46
src/subParsers/githubCodeBlocks.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
text += '~0';
|
||||
|
||||
text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
|
||||
function (wholeMatch, m1, m2) {
|
||||
var language = m1,
|
||||
codeblock = m2,
|
||||
end = '\n';
|
||||
|
||||
if (options.omitExtraWLInCodeBlocks) {
|
||||
end = '';
|
||||
}
|
||||
|
||||
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 + '"' : '') + '>' + codeblock + end + '</code></pre>';
|
||||
|
||||
return showdown.subParser('hashBlock')(codeblock, options, globals);
|
||||
}
|
||||
);
|
||||
|
||||
// attacklab: strip sentinel
|
||||
text = text.replace(/~0/, '');
|
||||
|
||||
return text;
|
||||
|
||||
});
|
9
src/subParsers/hashBlock.js
Normal file
9
src/subParsers/hashBlock.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
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';
|
||||
});
|
23
src/subParsers/hashElement.js
Normal file
23
src/subParsers/hashElement.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
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;
|
||||
};
|
||||
});
|
131
src/subParsers/hashHTMLBlocks.js
Normal file
131
src/subParsers/hashHTMLBlocks.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
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)\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;
|
||||
|
||||
|
||||
});
|
59
src/subParsers/headers.js
Normal file
59
src/subParsers/headers.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
showdown.subParser('headers', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
// Set text-style headers:
|
||||
// Header 1
|
||||
// ========
|
||||
//
|
||||
// Header 2
|
||||
// --------
|
||||
//
|
||||
text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
|
||||
function (wholeMatch, m1) {
|
||||
return showdown.subParser('hashBlock')('<h1 id="' + headerId(m1) + '">' +
|
||||
showdown.subParser('spanGamut')(m1, options, globals) + '</h1>', options, globals);
|
||||
});
|
||||
|
||||
text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
|
||||
function (matchFound, m1) {
|
||||
return showdown.subParser('hashBlock')('<h2 id="' + headerId(m1) + '">' +
|
||||
showdown.subParser('spanGamut')(m1, options, globals) + '</h2>', options, globals);
|
||||
});
|
||||
|
||||
// atx-style headers:
|
||||
// # Header 1
|
||||
// ## Header 2
|
||||
// ## Header 2 with closing hashes ##
|
||||
// ...
|
||||
// ###### Header 6
|
||||
//
|
||||
|
||||
/*
|
||||
text = text.replace(/
|
||||
^(\#{1,6}) // $1 = string of #'s
|
||||
[ \t]*
|
||||
(.+?) // $2 = Header text
|
||||
[ \t]*
|
||||
\#* // optional closing #'s (not counted)
|
||||
\n+
|
||||
/gm, function() {...});
|
||||
*/
|
||||
|
||||
text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
|
||||
function (wholeMatch, m1, m2) {
|
||||
var span = showdown.subParser('spanGamut')(m2, options, globals),
|
||||
header = '<h' + m1.length + ' id="' + headerId(m2) + '">' + span + '</h' + m1.length + '>';
|
||||
|
||||
return showdown.subParser('hashBlock')(header, options, globals);
|
||||
});
|
||||
|
||||
function headerId(m) {
|
||||
return m.replace(/[^\w]/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
return text;
|
||||
});
|
109
src/subParsers/images.js
Normal file
109
src/subParsers/images.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Turn Markdown image shortcuts into <img> tags.
|
||||
*/
|
||||
showdown.subParser('images', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
var writeImageTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
|
||||
|
||||
wholeMatch = m1;
|
||||
var altText = m2,
|
||||
linkId = m3.toLowerCase(),
|
||||
url = m4,
|
||||
title = m7,
|
||||
gUrls = globals.gUrls,
|
||||
gTitles = globals.gTitles;
|
||||
|
||||
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 (typeof gUrls[linkId] !== 'undefined') {
|
||||
url = gUrls[linkId];
|
||||
if (typeof gTitles[linkId] !== 'undefined') {
|
||||
title = gTitles[linkId];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return wholeMatch;
|
||||
}
|
||||
}
|
||||
|
||||
altText = altText.replace(/"/g, '"');
|
||||
url = showdown.helper.escapeCharacters(url, '*_');
|
||||
var result = '<img src="' + url + '" alt="' + altText + '"';
|
||||
|
||||
// attacklab: Markdown.pl adds empty title attributes to images.
|
||||
// Replicate this bug.
|
||||
|
||||
//if (title != "") {
|
||||
title = title.replace(/"/g, '"');
|
||||
title = escapeCharacters(title, '*_');
|
||||
result += ' title="' + title + '"';
|
||||
//}
|
||||
|
||||
result += ' />';
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
// First, handle reference-style labeled images: ![alt text][id]
|
||||
/*
|
||||
text = text.replace(/
|
||||
( // wrap whole match in $1
|
||||
!\[
|
||||
(.*?) // alt text = $2
|
||||
\]
|
||||
|
||||
[ ]? // one optional space
|
||||
(?:\n[ ]*)? // one optional newline followed by spaces
|
||||
|
||||
\[
|
||||
(.*?) // id = $3
|
||||
\]
|
||||
)()()()() // pad rest of backreferences
|
||||
/g,writeImageTag);
|
||||
*/
|
||||
text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
|
||||
|
||||
// Next, handle inline images: ![alt text](url "optional title")
|
||||
// Don't forget: encode * and _
|
||||
/*
|
||||
text = text.replace(/
|
||||
( // wrap whole match in $1
|
||||
!\[
|
||||
(.*?) // alt text = $2
|
||||
\]
|
||||
\s? // One optional whitespace character
|
||||
\( // literal paren
|
||||
[ \t]*
|
||||
() // no id, so leave $3 empty
|
||||
<?(\S+?)>? // src url = $4
|
||||
[ \t]*
|
||||
( // $5
|
||||
(['"]) // quote char = $6
|
||||
(.*?) // title = $7
|
||||
\6 // matching quote
|
||||
[ \t]*
|
||||
)? // title is optional
|
||||
\)
|
||||
)
|
||||
/g,writeImageTag);
|
||||
*/
|
||||
text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
|
||||
|
||||
return text;
|
||||
});
|
15
src/subParsers/italicsAndBold.js
Normal file
15
src/subParsers/italicsAndBold.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
showdown.subParser('italicsAndBold', function (text) {
|
||||
'use strict';
|
||||
// <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;
|
||||
});
|
150
src/subParsers/lists.js
Normal file
150
src/subParsers/lists.js
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 listStr
|
||||
* @returns {string|*}
|
||||
*/
|
||||
var processListItems = function (listStr) {
|
||||
// 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';
|
||||
|
||||
/*
|
||||
list_str = list_str.replace(/
|
||||
(\n)? // leading line = $1
|
||||
(^[ \t]*) // leading whitespace = $2
|
||||
([*+-]|\d+[.]) [ \t]+ // list marker = $3
|
||||
([^\r]+? // list item text = $4
|
||||
(\n{1,2}))
|
||||
(?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
|
||||
/gm, function(){...});
|
||||
*/
|
||||
listStr = listStr.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
|
||||
function (wholeMatch, m1, m2, m3, m4) {
|
||||
var item = showdown.subParser('outdent')(m4, options, globals);
|
||||
//m1 - LeadingLine
|
||||
|
||||
if (m1 || (item.search(/\n{2,}/) > -1)) {
|
||||
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)
|
||||
item = showdown.subParser('spanGamut')(item, options, globals);
|
||||
}
|
||||
|
||||
return '<li>' + item + '</li>\n';
|
||||
}
|
||||
);
|
||||
|
||||
// attacklab: strip sentinel
|
||||
listStr = listStr.replace(/~0/g, '');
|
||||
|
||||
globals.gListLevel--;
|
||||
return listStr;
|
||||
};
|
||||
|
||||
|
||||
// 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 entirel ul or ol list:
|
||||
|
||||
/*
|
||||
var whole_list = /
|
||||
( // $1 = whole list
|
||||
( // $2
|
||||
[ ]{0,3} // attacklab: g_tab_width - 1
|
||||
([*+-]|\d+[.]) // $3 = first list item marker
|
||||
[ \t]+
|
||||
)
|
||||
[^\r]+?
|
||||
( // $4
|
||||
~0 // sentinel for workaround; should be $
|
||||
|
|
||||
\n{2,}
|
||||
(?=\S)
|
||||
(?! // Negative lookahead for another list item marker
|
||||
[ \t]*
|
||||
(?:[*+-]|\d+[.])[ \t]+
|
||||
)
|
||||
)
|
||||
)/g
|
||||
*/
|
||||
var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
||||
|
||||
if (globals.gListLevel) {
|
||||
text = text.replace(wholeList, function (wholeMatch, m1, m2) {
|
||||
var list = m1,
|
||||
listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
|
||||
// Turn double returns into triple returns, so that we can make a
|
||||
// paragraph for the last item in a list, if necessary:
|
||||
list = list.replace(/\n{2,}/g, '\n\n\n');
|
||||
|
||||
var result = processListItems(list);
|
||||
|
||||
// Trim any trailing whitespace, to put the closing `</$list_type>`
|
||||
// up on the preceding line, to get it past the current stupid
|
||||
// HTML block parser. This is a hack to work around the terrible
|
||||
// hack that is the HTML block parser.
|
||||
result = result.replace(/\s+$/, '');
|
||||
result = '<' + listType + '>' + result + '</' + listType + '>\n';
|
||||
return result;
|
||||
});
|
||||
} else {
|
||||
wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
|
||||
|
||||
text = text.replace(wholeList, function(wholeMatch,m1,m2,m3) {
|
||||
|
||||
// Turn double returns into triple returns, so that we can make a
|
||||
// paragraph for the last item in a list, if necessary:
|
||||
var list = m2.replace(/\n{2,}/g, '\n\n\n'),
|
||||
listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
|
||||
result = processListItems(list);
|
||||
|
||||
return m1 + '<' + listType + '>\n' + result + '</' + listType + '>\n';
|
||||
});
|
||||
}
|
||||
|
||||
// attacklab: strip sentinel
|
||||
text = text.replace(/~0/, '');
|
||||
|
||||
return text;
|
||||
});
|
19
src/subParsers/outdent.js
Normal file
19
src/subParsers/outdent.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
47
src/subParsers/paragraphs.js
Normal file
47
src/subParsers/paragraphs.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 = [];
|
||||
|
||||
/** Wrap <p> tags. */
|
||||
var end = grafs.length;
|
||||
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');
|
||||
});
|
34
src/subParsers/spanGamut.js
Normal file
34
src/subParsers/spanGamut.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// Do hard breaks:
|
||||
text = text.replace(/ +\n/g, ' <br />\n');
|
||||
|
||||
return text;
|
||||
|
||||
});
|
14
src/subParsers/stripBlankLines.js
Normal file
14
src/subParsers/stripBlankLines.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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, '');
|
||||
});
|
57
src/subParsers/stripLinkDefinitions.js
Normal file
57
src/subParsers/stripLinkDefinitions.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Created by Estevao on 11-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
// 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();
|
||||
globals.gUrls[m1] = showdown.subParser('encodeAmpsAndAngles')(m2); // Link IDs are case-insensitive
|
||||
if (m3) {
|
||||
// Oops, found blank lines, so it's not a title.
|
||||
// Put back the parenthetical statement we stole.
|
||||
return m3 + m4;
|
||||
} else if (m4) {
|
||||
globals.gTitles[m1] = m4.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
// Completely remove the definition from the text
|
||||
return '';
|
||||
}
|
||||
);
|
||||
|
||||
// attacklab: strip sentinel
|
||||
text = text.replace(/~0/, '');
|
||||
|
||||
return text;
|
||||
});
|
18
src/subParsers/unescapeSpecialChars.js
Normal file
18
src/subParsers/unescapeSpecialChars.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Created by Estevao on 12-01-2015.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
|
@ -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">a fourth</a> reference-style link.</p>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
<p>This is a multi line blockquote test</p>
|
||||
|
||||
<p>With more than one line.</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
<p>This is some HTML:</p>
|
||||
|
||||
<pre><code><h1>Heading</h1>
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
<p>This is a normal paragraph:</p>
|
||||
|
||||
<pre><code>This is a code block.
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
<ul>
|
||||
<li><p>Bird</p></li>
|
||||
<li><p>Magic</p></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
<p><em>important</em></p>
|
||||
|
||||
<p><em>important</em></p>
|
||||
|
||||
<p>this mid<em>important</em>sentence</p>
|
||||
|
||||
<p>*not important*</p>
|
||||
<p>*not important*</p>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<p>It happened in 1986. What a great season.</p>
|
||||
<p>It happened in 1986. What a great season.</p>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<p>These should all be escaped:</p>
|
||||
|
||||
<p>\</p>
|
||||
|
@ -29,4 +28,4 @@
|
|||
|
||||
<p>.</p>
|
||||
|
||||
<p>!</p>
|
||||
<p>!</p>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
<pre><code>function MyFunc(a) {
|
||||
// ...
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>That is some code!</p>
|
||||
<p>That is some code!</p>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
<p>Define a function in javascript:</p>
|
||||
|
||||
<pre><code>function MyFunc(a) {
|
||||
|
@ -10,4 +8,4 @@
|
|||
<p>And some HTML</p>
|
||||
|
||||
<pre><code class="html"><div>HTML!</div>
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<pre><code>code can go here
|
||||
this is rendered on a second line
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
<h1 id="thisisanh1">This is an H1</h1>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
<h2 id="thisisanh2">This is an H2</h2>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h3 id="thisisanh3">This is an H3</h3>
|
||||
<h3 id="thisisanh3">This is an H3</h3>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h3 id="thisisanh3">This is an H3</h3>
|
||||
<h3 id="thisisanh3">This is an H3</h3>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h4 id="thisisanh4">This is an H4</h4>
|
||||
<h4 id="thisisanh4">This is an H4</h4>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h5 id="thisisanh5">This is an H5</h5>
|
||||
<h5 id="thisisanh5">This is an H5</h5>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<h6 id="thisisanh6">This is an H6</h6>
|
||||
<h6 id="thisisanh6">This is an H6</h6>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<hr />
|
||||
|
||||
<hr />
|
||||
|
@ -7,4 +6,4 @@
|
|||
|
||||
<hr />
|
||||
|
||||
<hr />
|
||||
<hr />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<p>These HTML5 tags should pass through just fine.</p>
|
||||
|
||||
<section>hello</section>
|
||||
|
@ -20,4 +19,4 @@ me</article>
|
|||
ignore me
|
||||
</aside>
|
||||
|
||||
<p>the end</p>
|
||||
<p>the end</p>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
<p><img src="/path/to/img.jpg" alt="Alt text" title="" /></p>
|
||||
|
||||
<p><img src="/path/to/img.jpg" alt="Alt text" title="Optional title" /></p>
|
||||
|
||||
<p><img src="url/to/image" alt="Alt text" title="Optional title attribute" /></p>
|
||||
<p><img src="url/to/image" alt="Alt text" title="Optional title attribute" /></p>
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
|
||||
<p>Search the web at <a href="http://google.com/">Google</a> or <a href="http://daringfireball.net/">Daring Fireball</a>.</p>
|
||||
<p>Search the web at <a href="http://google.com/">Google</a> or <a href="http://daringfireball.net/">Daring Fireball</a>.</p>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<p>This is <a href="http://example.com/" title="Title">an example</a> inline link.</p>
|
||||
|
||||
<p><a href="http://example.net/">This link</a> has no title attribute.</p>
|
||||
<p><a href="http://example.net/">This link</a> has no title attribute.</p>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<p>Create a new <code>function</code>.</p>
|
||||
|
||||
<p>Use the backtick in MySQL syntax <code>SELECT `column` FROM whatever</code>.</p>
|
||||
|
@ -9,4 +8,4 @@
|
|||
|
||||
<p>Please don't use any <code><blink></code> tags.</p>
|
||||
|
||||
<p><code>&#8212;</code> is the decimal-encoded equivalent of <code>&mdash;</code>.</p>
|
||||
<p><code>&#8212;</code> is the decimal-encoded equivalent of <code>&mdash;</code>.</p>
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
|
||||
<p>Hello.this_is_a_variable
|
||||
and.this.is.another_one</p>
|
||||
and.this.is.another_one</p>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
<style>
|
||||
p { line-height: 20px; }
|
||||
</style>
|
||||
|
||||
<p>An exciting sentence.</p>
|
||||
<p>An exciting sentence.</p>
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
<p>This is a multi line blockquote test</p>
|
||||
|
||||
<p>With more than one line.</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
<p>This is a blockquote
|
||||
inside a list item.</p>
|
||||
</blockquote></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user