From eae5f0e01fd086cfee87de5a547fcd057f59a460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Estev=C3=A3o=20Soares=20dos=20Santos?= Date: Thu, 15 Jan 2015 21:21:33 +0000 Subject: [PATCH] Major code refactoring --- .gitattributes | 7 +- .gitignore | 3 +- .jshintignore | 3 +- .jshintrc | 27 + CREDITS.md | 39 + Gruntfile.js | 68 +- README.md | 638 +++---- bower.json | 8 +- compressed/Showdown.js | 1606 ---------------- compressed/Showdown.js.map | 1 - compressed/Showdown.min.js | 2 - compressed/extensions/github.min.js | 2 - compressed/extensions/github.min.js.map | 1 - compressed/extensions/prettify.min.js | 2 - compressed/extensions/prettify.min.js.map | 1 - compressed/extensions/table.min.js | 2 - compressed/extensions/table.min.js.map | 1 - compressed/extensions/twitter.min.js | 2 - compressed/extensions/twitter.min.js.map | 1 - dist/showdown.js | 1577 ++++++++++++++++ dist/showdown.js.map | 1 + example/angular.html | 83 - example/showdown-gui.html | 720 -------- example/showdown-gui.js | 349 ---- example/showdown.js | 1 - example/syntax.txt | 896 --------- license.txt | 8 +- package.json | 11 +- perlMarkdown/Markdown License.txt | 30 - perlMarkdown/Markdown-1.0.2b2.pl | 1509 --------------- perlMarkdown/Markdown-1.0.2b7.pl | 1642 ----------------- perlMarkdown/readme.txt | 13 - src/{ng-showdown.js => angular.js} | 28 +- src/extensions/github.js | 25 - src/extensions/prettify.js | 29 - src/extensions/table.js | 106 -- src/extensions/twitter.js | 42 - src/helpers.js | 98 + src/loader.js | 20 + src/showdown..legacy.js | 1454 --------------- src/showdown.js | 145 ++ src/subParsers/anchors.js | 136 ++ src/subParsers/autoLinks.js | 33 + src/subParsers/blockGamut.js | 33 + src/subParsers/blockQuotes.js | 50 + src/subParsers/codeBlocks.js | 48 + src/subParsers/codeSpans.js | 57 + src/subParsers/detab.js | 38 + src/subParsers/encodeAmpsAndAngles.js | 18 + src/subParsers/encodeBackslashEscapes.js | 21 + src/subParsers/encodeCode.js | 33 + src/subParsers/encodeEmailAddress.js | 60 + .../escapeSpecialCharsWithinTagAttributes.js | 23 + src/subParsers/githubCodeBlocks.js | 46 + src/subParsers/hashBlock.js | 9 + src/subParsers/hashElement.js | 23 + src/subParsers/hashHTMLBlocks.js | 131 ++ src/subParsers/headers.js | 59 + src/subParsers/images.js | 109 ++ src/subParsers/italicsAndBold.js | 15 + src/subParsers/lists.js | 150 ++ src/subParsers/outdent.js | 19 + src/subParsers/paragraphs.js | 47 + src/subParsers/spanGamut.js | 34 + src/subParsers/stripBlankLines.js | 14 + src/subParsers/stripLinkDefinitions.js | 57 + src/subParsers/unescapeSpecialChars.js | 18 + test/cases/anchors-by-reference.html | 3 +- test/cases/automatic-anchors.html | 3 +- test/cases/blockquote.html | 2 +- test/cases/code-block-html-escape.html | 3 +- test/cases/code-block.html | 3 +- test/cases/doubline-list.html | 3 +- test/cases/emphasis.html | 3 +- test/cases/escaped-number-period.html | 2 +- test/cases/escaping.html | 3 +- test/cases/github-style-at-start.html | 3 +- test/cases/github-style-codeblock.html | 4 +- test/cases/github-style-linebreaks.html | 3 +- test/cases/h1-with-double-hash.html | 2 +- test/cases/h1-with-equals.html | 2 +- test/cases/h1-with-single-hash.html | 2 +- test/cases/h2-with-dashes.html | 2 +- test/cases/h2-with-double-hash.html | 2 +- test/cases/h2-with-single-hash.html | 2 +- test/cases/h3-with-double-hash.html | 2 +- test/cases/h3-with-single-hash.html | 2 +- test/cases/h4-with-single-hash.html | 2 +- test/cases/h5-with-single-hash.html | 2 +- test/cases/h6-with-single-hash.html | 2 +- test/cases/horizontal-rules.html | 3 +- test/cases/html5-strutural-tags.html | 3 +- test/cases/images.html | 3 +- test/cases/implicit-anchors.html | 3 +- test/cases/inline-anchors.html | 3 +- test/cases/inline-code.html | 3 +- test/cases/inline-escaped-chars.html | 3 +- test/cases/inline-style-tag.html | 3 +- test/cases/lazy-blockquote.html | 2 +- test/cases/list-with-blockquote.html | 2 +- test/cases/list-with-code.html | 2 +- test/cases/multi-paragraph-list.html | 2 +- test/cases/multiline-unordered-list.html | 2 +- test/cases/nested-blockquote.html | 2 +- test/cases/ordered-list-same-number.html | 2 +- test/cases/ordered-list-wrong-numbers.html | 2 +- test/cases/ordered-list.html | 2 +- test/cases/relative-anchors.html | 3 +- test/cases/simple-paragraph.html | 2 +- test/cases/strong.html | 3 +- test/cases/unordered-list-asterisk.html | 2 +- test/cases/unordered-list-minus.html | 2 +- test/cases/unordered-list-plus.html | 2 +- test/cases/url-with-parenthesis.html | 3 +- test/converter/testMakeHtml.js | 80 + test/extensions/github/basic.html | 5 - test/extensions/github/basic.md | 3 - test/extensions/prettify/basic.html | 7 - test/extensions/prettify/basic.md | 6 - test/extensions/table/basic.html | 21 - test/extensions/table/basic.md | 4 - test/extensions/table/large.html | 48 - test/extensions/table/large.md | 7 - test/extensions/table/multiple-tables.html | 43 - test/extensions/table/multiple-tables.md | 17 - test/extensions/table/with-equals.html | 21 - test/extensions/table/with-equals.md | 4 - test/extensions/table/with-surroundings.html | 33 - test/extensions/table/with-surroundings.md | 16 - test/extensions/table/without-body.html | 11 - test/extensions/table/without-body.md | 2 - .../table/without-header-delimiter.html | 1 - .../table/without-header-delimiter.md | 1 - test/extensions/twitter/basic.html | 5 - test/extensions/twitter/basic.md | 5 - test/run.js | 86 - test/testTable.js | 24 - 137 files changed, 3694 insertions(+), 9354 deletions(-) create mode 100644 .jshintrc create mode 100644 CREDITS.md delete mode 100644 compressed/Showdown.js delete mode 100644 compressed/Showdown.js.map delete mode 100644 compressed/Showdown.min.js delete mode 100644 compressed/extensions/github.min.js delete mode 100644 compressed/extensions/github.min.js.map delete mode 100644 compressed/extensions/prettify.min.js delete mode 100644 compressed/extensions/prettify.min.js.map delete mode 100644 compressed/extensions/table.min.js delete mode 100644 compressed/extensions/table.min.js.map delete mode 100644 compressed/extensions/twitter.min.js delete mode 100644 compressed/extensions/twitter.min.js.map create mode 100644 dist/showdown.js create mode 100644 dist/showdown.js.map delete mode 100644 example/angular.html delete mode 100644 example/showdown-gui.html delete mode 100644 example/showdown-gui.js delete mode 120000 example/showdown.js delete mode 100644 example/syntax.txt delete mode 100644 perlMarkdown/Markdown License.txt delete mode 100644 perlMarkdown/Markdown-1.0.2b2.pl delete mode 100644 perlMarkdown/Markdown-1.0.2b7.pl delete mode 100644 perlMarkdown/readme.txt rename src/{ng-showdown.js => angular.js} (87%) delete mode 100644 src/extensions/github.js delete mode 100644 src/extensions/prettify.js delete mode 100644 src/extensions/table.js delete mode 100644 src/extensions/twitter.js create mode 100644 src/helpers.js create mode 100644 src/loader.js delete mode 100644 src/showdown..legacy.js create mode 100644 src/showdown.js create mode 100644 src/subParsers/anchors.js create mode 100644 src/subParsers/autoLinks.js create mode 100644 src/subParsers/blockGamut.js create mode 100644 src/subParsers/blockQuotes.js create mode 100644 src/subParsers/codeBlocks.js create mode 100644 src/subParsers/codeSpans.js create mode 100644 src/subParsers/detab.js create mode 100644 src/subParsers/encodeAmpsAndAngles.js create mode 100644 src/subParsers/encodeBackslashEscapes.js create mode 100644 src/subParsers/encodeCode.js create mode 100644 src/subParsers/encodeEmailAddress.js create mode 100644 src/subParsers/escapeSpecialCharsWithinTagAttributes.js create mode 100644 src/subParsers/githubCodeBlocks.js create mode 100644 src/subParsers/hashBlock.js create mode 100644 src/subParsers/hashElement.js create mode 100644 src/subParsers/hashHTMLBlocks.js create mode 100644 src/subParsers/headers.js create mode 100644 src/subParsers/images.js create mode 100644 src/subParsers/italicsAndBold.js create mode 100644 src/subParsers/lists.js create mode 100644 src/subParsers/outdent.js create mode 100644 src/subParsers/paragraphs.js create mode 100644 src/subParsers/spanGamut.js create mode 100644 src/subParsers/stripBlankLines.js create mode 100644 src/subParsers/stripLinkDefinitions.js create mode 100644 src/subParsers/unescapeSpecialChars.js create mode 100644 test/converter/testMakeHtml.js delete mode 100644 test/extensions/github/basic.html delete mode 100644 test/extensions/github/basic.md delete mode 100644 test/extensions/prettify/basic.html delete mode 100644 test/extensions/prettify/basic.md delete mode 100644 test/extensions/table/basic.html delete mode 100644 test/extensions/table/basic.md delete mode 100644 test/extensions/table/large.html delete mode 100644 test/extensions/table/large.md delete mode 100644 test/extensions/table/multiple-tables.html delete mode 100644 test/extensions/table/multiple-tables.md delete mode 100644 test/extensions/table/with-equals.html delete mode 100644 test/extensions/table/with-equals.md delete mode 100644 test/extensions/table/with-surroundings.html delete mode 100644 test/extensions/table/with-surroundings.md delete mode 100644 test/extensions/table/without-body.html delete mode 100644 test/extensions/table/without-body.md delete mode 100644 test/extensions/table/without-header-delimiter.html delete mode 100644 test/extensions/table/without-header-delimiter.md delete mode 100644 test/extensions/twitter/basic.html delete mode 100644 test/extensions/twitter/basic.md delete mode 100644 test/run.js delete mode 100644 test/testTable.js diff --git a/.gitattributes b/.gitattributes index 7e29a2cf..6234bf3b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore index 8d7c6bbe..61f9328f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ +.idea/ +build/ .DS_Store node_modules npm-debug.log -.idea/ diff --git a/.jshintignore b/.jshintignore index 62aa808e..a76422d5 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,2 +1,3 @@ Gruntfile.js -dist/**/*.js \ No newline at end of file +dist/**/*.js +build/**/*.js diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..03d59610 --- /dev/null +++ b/.jshintrc @@ -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 + } +} diff --git a/CREDITS.md b/CREDITS.md new file mode 100644 index 00000000..632bca4d --- /dev/null +++ b/CREDITS.md @@ -0,0 +1,39 @@ +Credits +======= + - Showdown v2 + * [Estevão Santos](http://soares-dos-santos.com)
+ Code Refactoring and project maintainer + + - Showdown v1 + * [Corey Innis](http://github.com/coreyti):
+ GitHub project maintainer + * [Remy Sharp](https://github.com/remy/):
+ CommonJS-compatibility and more + * [Konstantin Käfer](https://github.com/kkaefer/):
+ CommonJS packaging + * [Roger Braun](https://github.com/rogerbraun):
+ Github-style code blocks + * [Dominic Tarr](https://github.com/dominictarr):
+ Documentation + * [Cat Chen](https://github.com/CatChen):
+ Export fix + * [Titus Stone](https://github.com/tstone):
+ Mocha tests, extension mechanism, and bug fixes + * [Rob Sutherland](https://github.com/roberocity):
+ The idea that lead to extensions + * [Pavel Lang](https://github.com/langpavel):
+ Code cleanup + * [Ben Combee](https://github.com/unwiredben):
+ Regex optimization + * [Adam Backstrom](https://github.com/abackstrom):
+ WebKit bugfix + * [Pascal Deschênes](https://github.com/pdeschen):
+ Grunt support, extension fixes + additions, packaging improvements, documentation + * [Estevão Santos](http://soares-dos-santos.com)
+ Bug fixing and later maintainer + + - Original Project + * [John Gruber](http://daringfireball.net/projects/markdown/)
+ Author of Markdown + * [John Fraser](http://attacklab.net/) + Author of Showdown diff --git a/Gruntfile.js b/Gruntfile.js index 47d92628..dc955515 100644 --- a/Gruntfile.js +++ b/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', []); + }; diff --git a/README.md b/README.md index 139e0df9..036de306 100644 --- a/README.md +++ b/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. - - -Original Markdown Copyright (c) 2004-2005 John Gruber - - -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!'); - -//

hello, markdown

-``` - -## 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 - - - - - - - - \ No newline at end of file diff --git a/example/showdown-gui.html b/example/showdown-gui.html deleted file mode 100644 index c447221e..00000000 --- a/example/showdown-gui.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - Showdown - Markdown in Javascript - - - - - - - - - -
-
- Input -
- -
- -
-
- -
- - - -
- - - - - - - - - - - - - - -
- - - - - - \ No newline at end of file diff --git a/example/showdown-gui.js b/example/showdown-gui.js deleted file mode 100644 index 5f3519f3..00000000 --- a/example/showdown-gui.js +++ /dev/null @@ -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 -// -// -// - -// -// 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"; -} \ No newline at end of file diff --git a/example/showdown.js b/example/showdown.js deleted file mode 120000 index e165c71f..00000000 --- a/example/showdown.js +++ /dev/null @@ -1 +0,0 @@ -../src/showdown.js \ No newline at end of file diff --git a/example/syntax.txt b/example/syntax.txt deleted file mode 100644 index a8c129f6..00000000 --- a/example/syntax.txt +++ /dev/null @@ -1,896 +0,0 @@ -Markdown: Syntax -================ - - - - -* [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 - -* * * - -

Overview

- -

Philosophy

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

Inline HTML

- -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. `
`, -``, `
`, `

`, 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) `

` tags around HTML block-level tags. - -For example, to add an HTML table to a Markdown article: - - This is a regular paragraph. - -

- - - -
Foo
- - 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. ``, ``, or `` -- 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 `` or `` 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. - - -

Automatic Escaping for Special Characters

- -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.) - - -* * * - - -

Block Elements

- - -

Paragraphs and Line Breaks

- -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 `
` tag. - -When you *do* want to insert a `
` 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 `
`, but a simplistic -"every line break is a `
`" 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 - - - - - -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 ###### - - -

Blockquotes

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

Lists

- -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: - -
    -
  1. Bird
  2. -
  3. McHale
  4. -
  5. Parish
  6. -
- -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 `

` tags in the HTML output. For example, this input: - - * Bird - * Magic - -will turn into: - -

    -
  • Bird
  • -
  • Magic
  • -
- -But this: - - * Bird - - * Magic - -will turn into: - -
    -
  • Bird

  • -
  • Magic

  • -
- -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: - - - - -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. - - - -

Code Blocks

- -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 `
` and `` 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:
-
-    

This is a normal paragraph:

- -
This is a code block.
-    
- -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: - -

Here is an example of AppleScript:

- -
tell application "Foo"
-        beep
-    end tell
-    
- -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: - - - -will turn into: - -
<div class="footer">
-        &copy; 2004 Foo Corporation
-    </div>
-    
- -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. - - - -

Horizontal Rules

- -You can produce a horizontal rule tag (`
`) 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: - - * * * - - *** - - ***** - - - - - - - --------------------------------------- - - -* * * - -

Span Elements

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

This is - an example inline link.

- -

This link has no - title attribute.

- -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]: "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: - -

I get 10 times more traffic from Google than from - Yahoo - or MSN.

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

Emphasis

- -Markdown treats asterisks (`*`) and underscores (`_`) as indicators of -emphasis. Text wrapped with one `*` or `_` will be wrapped with an -HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML -`` tag. E.g., this input: - - *single asterisks* - - _single underscores_ - - **double asterisks** - - __double underscores__ - -will produce: - - single asterisks - - single underscores - - double asterisks - - double underscores - -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\* - - - -

Code

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

Use the printf() function.

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

There is a literal backtick (`) here.

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

A single backtick in a code span: `

- -

A backtick-delimited string in a code span: `foo`

- -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 `` tags. - -into: - -

Please don't use any <blink> tags.

- -You can write this: - - `—` is the decimal-encoded equivalent of `—`. - -to produce: - -

&#8212; is the decimal-encoded - equivalent of &mdash;.

- - - -

Images

- -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 `` tags. - - -* * * - - -

Miscellaneous

- - - -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: - - - -Markdown will turn this into: - - http://example.com/ - -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: - - - -into something like this: - - address@exa - mple.com - -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.) - - - -

Backslash Escapes

- -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 `` 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 diff --git a/license.txt b/license.txt index a6096c84..bb98b86a 100644 --- a/license.txt +++ b/license.txt @@ -1,9 +1,9 @@ -Copyright (c) 2007, John Fraser - +Showdown Copyright (c) 2007, John Fraser + All rights reserved. -Original Markdown copyright (c) 2004, John Gruber - +Original Markdown copyright (c) 2004, John Gruber + All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/package.json b/package.json index e227b424..43060f64 100644 --- a/package.json +++ b/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" } } diff --git a/perlMarkdown/Markdown License.txt b/perlMarkdown/Markdown License.txt deleted file mode 100644 index 6d765065..00000000 --- a/perlMarkdown/Markdown License.txt +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2004, John Gruber - -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. diff --git a/perlMarkdown/Markdown-1.0.2b2.pl b/perlMarkdown/Markdown-1.0.2b2.pl deleted file mode 100644 index 5c78edd4..00000000 --- a/perlMarkdown/Markdown-1.0.2b2.pl +++ /dev/null @@ -1,1509 +0,0 @@ -#!/usr/bin/perl - -# -# Markdown -- A text-to-HTML conversion tool for web writers -# -# Copyright (c) 2004-2005 John Gruber -# -# - - -package Markdown; -require 5.006_000; -use strict; -use warnings; - -use Digest::MD5 qw(md5_hex); -use vars qw($VERSION); -$VERSION = '1.0.2b2'; -# Sat 26 Mar 2005 - -## Disabled; causes problems under Perl 5.6.1: -# use utf8; -# binmode( STDOUT, ":utf8" ); # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html - - -# -# Global default settings: -# -my $g_empty_element_suffix = " />"; # Change to ">" for HTML output -my $g_tab_width = 4; - - -# -# Globals: -# - -# Regex to match balanced [brackets]. See Friedl's -# "Mastering Regular Expressions", 2nd Ed., pp. 328-331. -my $g_nested_brackets; -$g_nested_brackets = qr{ - (?> # Atomic matching - [^\[\]]+ # Anything other than brackets - | - \[ - (??{ $g_nested_brackets }) # Recursive set of nested brackets - \] - )* -}x; - - -# Table of hash values for escaped characters: -my %g_escape_table; -foreach my $char (split //, '\\`*_{}[]()>#+-.!') { - $g_escape_table{$char} = md5_hex($char); -} - - -# Global hashes, used by various utility routines -my %g_urls; -my %g_titles; -my %g_html_blocks; - -# Used to track when we're inside an ordered or unordered list -# (see _ProcessListItems() for details): -my $g_list_level = 0; - - -#### Blosxom plug-in interface ########################################## - -# Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine -# which posts Markdown should process, using a "meta-markup: markdown" -# header. If it's set to 0 (the default), Markdown will process all -# entries. -my $g_blosxom_use_meta = 0; - -sub start { 1; } -sub story { - my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_; - - if ( (! $g_blosxom_use_meta) or - (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i)) - ){ - $$body_ref = Markdown($$body_ref); - } - 1; -} - - -#### Movable Type plug-in interface ##################################### -eval {require MT}; # Test to see if we're running in MT. -unless ($@) { - require MT; - import MT; - require MT::Template::Context; - import MT::Template::Context; - - eval {require MT::Plugin}; # Test to see if we're running >= MT 3.0. - unless ($@) { - require MT::Plugin; - import MT::Plugin; - my $plugin = new MT::Plugin({ - name => "Markdown", - description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)", - doc_link => 'http://daringfireball.net/projects/markdown/' - }); - MT->add_plugin( $plugin ); - } - - MT::Template::Context->add_container_tag(MarkdownOptions => sub { - my $ctx = shift; - my $args = shift; - my $builder = $ctx->stash('builder'); - my $tokens = $ctx->stash('tokens'); - - if (defined ($args->{'output'}) ) { - $ctx->stash('markdown_output', lc $args->{'output'}); - } - - defined (my $str = $builder->build($ctx, $tokens) ) - or return $ctx->error($builder->errstr); - $str; # return value - }); - - MT->add_text_filter('markdown' => { - label => 'Markdown', - docs => 'http://daringfireball.net/projects/markdown/', - on_format => sub { - my $text = shift; - my $ctx = shift; - my $raw = 0; - if (defined $ctx) { - my $output = $ctx->stash('markdown_output'); - if (defined $output && $output =~ m/^html/i) { - $g_empty_element_suffix = ">"; - $ctx->stash('markdown_output', ''); - } - elsif (defined $output && $output eq 'raw') { - $raw = 1; - $ctx->stash('markdown_output', ''); - } - else { - $raw = 0; - $g_empty_element_suffix = " />"; - } - } - $text = $raw ? $text : Markdown($text); - $text; - }, - }); - - # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter: - my $smartypants; - - { - no warnings "once"; - $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'}; - } - - if ($smartypants) { - MT->add_text_filter('markdown_with_smartypants' => { - label => 'Markdown With SmartyPants', - docs => 'http://daringfireball.net/projects/markdown/', - on_format => sub { - my $text = shift; - my $ctx = shift; - if (defined $ctx) { - my $output = $ctx->stash('markdown_output'); - if (defined $output && $output eq 'html') { - $g_empty_element_suffix = ">"; - } - else { - $g_empty_element_suffix = " />"; - } - } - $text = Markdown($text); - $text = $smartypants->($text, '1'); - }, - }); - } -} -else { -#### BBEdit/command-line text filter interface ########################## -# Needs to be hidden from MT (and Blosxom when running in static mode). - - # We're only using $blosxom::version once; tell Perl not to warn us: - no warnings 'once'; - unless ( defined($blosxom::version) ) { - use warnings; - - #### Check for command-line switches: ################# - my %cli_opts; - use Getopt::Long; - Getopt::Long::Configure('pass_through'); - GetOptions(\%cli_opts, - 'version', - 'shortversion', - 'html4tags', - ); - if ($cli_opts{'version'}) { # Version info - print "\nThis is Markdown, version $VERSION.\n"; - print "Copyright 2004 John Gruber\n"; - print "http://daringfireball.net/projects/markdown/\n\n"; - exit 0; - } - if ($cli_opts{'shortversion'}) { # Just the version number string. - print $VERSION; - exit 0; - } - if ($cli_opts{'html4tags'}) { # Use HTML tag style instead of XHTML - $g_empty_element_suffix = ">"; - } - - - #### Process incoming text: ########################### - my $text; - { - local $/; # Slurp the whole file - $text = <>; - } - print Markdown($text); - } -} - - - -sub Markdown { -# -# Main function. The order in which other subs are called here is -# essential. Link and image substitutions need to happen before -# _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the -# and tags get encoded. -# - my $text = shift; - - # Clear the global hashes. If we don't clear these, you get conflicts - # from other articles when generating a page which contains more than - # one article (e.g. an index page that shows the N most recent - # articles): - %g_urls = (); - %g_titles = (); - %g_html_blocks = (); - - - # Standardize line endings: - $text =~ s{\r\n}{\n}g; # DOS to Unix - $text =~ s{\r}{\n}g; # Mac to Unix - - # Make sure $text ends with a couple of newlines: - $text .= "\n\n"; - - # Convert all tabs to spaces. - $text = _Detab($text); - - # Strip any lines consisting only of spaces and tabs. - # This makes subsequent regexen easier to write, because we can - # match consecutive blank lines with /\n+/ instead of something - # contorted like /[ \t]*\n+/ . - $text =~ s/^[ \t]+$//mg; - - # Turn block-level HTML blocks into hash entries - $text = _HashHTMLBlocks($text); - - # Strip link definitions, store in hashes. - $text = _StripLinkDefinitions($text); - - $text = _RunBlockGamut($text); - - $text = _UnescapeSpecialChars($text); - - return $text . "\n"; -} - - -sub _StripLinkDefinitions { -# -# Strips link definitions from text, stores the URLs and titles in -# hash references. -# - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - while ($text =~ s{ - ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1 - [ \t]* - \n? # maybe *one* newline - [ \t]* - ? # url = $2 - [ \t]* - \n? # maybe one newline - [ \t]* - (?: - (?<=\s) # lookbehind for whitespace - ["(] - (.+?) # title = $3 - [")] - [ \t]* - )? # title is optional - (?:\n+|\Z) - } - {}mx) { - $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 ); # Link IDs are case-insensitive - if ($3) { - $g_titles{lc $1} = $3; - $g_titles{lc $1} =~ s/"/"/g; - } - } - - return $text; -} - - -sub _HashHTMLBlocks { - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # 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

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: - my $block_tags_a = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del/; - my $block_tags_b = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math/; - - # First, look for nested blocks, e.g.: - #

- #
- # tags for inner block must be indented. - #
- #
- # - # 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 `
` and stop at the first `
`. - $text =~ s{ - ( # save in $1 - ^ # start of line (with /m) - <($block_tags_a) # start tag = $2 - \b # word break - (.*\n)*? # any number of lines, minimally matching - # the matching end tag - [ \t]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egmx; - - - # - # Now match more liberally, simply from `\n` to `\n` - # - $text =~ s{ - ( # save in $1 - ^ # start of line (with /m) - <($block_tags_b) # start tag = $2 - \b # word break - (.*\n)*? # any number of lines, minimally matching - .* # the matching end tag - [ \t]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egmx; - # Special case just for
. It was easier to make a special case than - # to make the other regex more complicated. - $text =~ s{ - (?: - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - [ ]{0,$less_than_tab} - <(hr) # start tag = $2 - \b # word break - ([^<>])*? # - /?> # the matching end tag - [ \t]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egx; - - # Special case for standalone HTML comments: - $text =~ s{ - (?: - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - [ ]{0,$less_than_tab} - (?s: - - ) - [ \t]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egx; - - - return $text; -} - - -sub _RunBlockGamut { -# -# These are all the transformations that form block-level -# tags like paragraphs, headers, and list items. -# - my $text = shift; - - $text = _DoHeaders($text); - - # Do Horizontal Rules: - $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n tags around block-level tags. - $text = _HashHTMLBlocks($text); - $text = _FormParagraphs($text); - - return $text; -} - - -sub _RunSpanGamut { -# -# These are all the transformations that occur *within* block-level -# tags like paragraphs, headers, and list items. -# - my $text = shift; - - $text = _EscapeSpecialCharsWithinTagAttributes($text); - $text = _DoCodeSpans($text); - $text = _EncodeBackslashEscapes($text); - - # Process anchor and image tags. Images must come first, - # because ![foo][f] looks like an anchor. - $text = _DoImages($text); - $text = _DoAnchors($text); - - # Make links out of things like `` - # Must come after _DoAnchors(), because you can use < and > - # delimiters in inline links like [this](). - $text = _DoAutoLinks($text); - $text = _EncodeAmpsAndAngles($text); - $text = _DoItalicsAndBold($text); - - # Do hard breaks: - $text =~ s/ {2,}\n/ -- encode [\ ` * _] so they -# don't conflict with their use in Markdown for code, italics and strong. -# We're replacing each such character with its corresponding MD5 checksum -# value; this is likely overkill, but it should prevent us from colliding -# with the escape values by accident. -# - my $text = shift; - my $tokens ||= _TokenizeHTML($text); - $text = ''; # rebuild $text from the tokens - - foreach my $cur_token (@$tokens) { - if ($cur_token->[0] eq "tag") { - $cur_token->[1] =~ s! \\ !$g_escape_table{'\\'}!gx; - $cur_token->[1] =~ s! ` !$g_escape_table{'`'}!gx; - $cur_token->[1] =~ s! \* !$g_escape_table{'*'}!gx; - $cur_token->[1] =~ s! _ !$g_escape_table{'_'}!gx; - } - $text .= $cur_token->[1]; - } - return $text; -} - - -sub _DoAnchors { -# -# Turn Markdown link shortcuts into XHTML
tags. -# - my $text = shift; - - # - # First, handle reference-style links: [link text] [id] - # - $text =~ s{ - ( # wrap whole match in $1 - \[ - ($g_nested_brackets) # link text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - ) - }{ - my $result; - my $whole_match = $1; - my $link_text = $2; - my $link_id = lc $3; - - if ($link_id eq "") { - $link_id = lc $link_text; # for shortcut links like [this][]. - } - - if (defined $g_urls{$link_id}) { - my $url = $g_urls{$link_id}; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "? # href = $3 - [ \t]* - ( # $4 - (['"]) # quote char = $5 - (.*?) # Title = $6 - \5 # matching quote - )? # title is optional - \) - ) - }{ - my $result; - my $whole_match = $1; - my $link_text = $2; - my $url = $3; - my $title = $6; - - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = " tags. -# - my $text = shift; - - # - # First, handle reference-style labeled images: ![alt text][id] - # - $text =~ s{ - ( # wrap whole match in $1 - !\[ - (.*?) # alt text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - - ) - }{ - my $result; - my $whole_match = $1; - my $alt_text = $2; - my $link_id = lc $3; - - if ($link_id eq "") { - $link_id = lc $alt_text; # for shortcut links like ![this][]. - } - - $alt_text =~ s/"/"/g; - if (defined $g_urls{$link_id}) { - my $url = $g_urls{$link_id}; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "\"$alt_text\"";? # src url = $3 - [ \t]* - ( # $4 - (['"]) # quote char = $5 - (.*?) # title = $6 - \5 # matching quote - [ \t]* - )? # title is optional - \) - ) - }{ - my $result; - my $whole_match = $1; - my $alt_text = $2; - my $url = $3; - my $title = ''; - if (defined($6)) { - $title = $6; - } - - $alt_text =~ s/"/"/g; - $title =~ s/"/"/g; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "\"$alt_text\"";" . _RunSpanGamut($1) . "\n\n"; - }egmx; - - $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{ - "

" . _RunSpanGamut($1) . "

\n\n"; - }egmx; - - - # atx-style headers: - # # Header 1 - # ## Header 2 - # ## Header 2 with closing hashes ## - # ... - # ###### Header 6 - # - $text =~ s{ - ^(\#{1,6}) # $1 = string of #'s - [ \t]* - (.+?) # $2 = Header text - [ \t]* - \#* # optional closing #'s (not counted) - \n+ - }{ - my $h_level = length($1); - "" . _RunSpanGamut($2) . "\n\n"; - }egmx; - - return $text; -} - - -sub _DoLists { -# -# Form HTML ordered (numbered) and unordered (bulleted) lists. -# - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # Re-usable patterns to match list item bullets and number markers: - my $marker_ul = qr/[*+-]/; - my $marker_ol = qr/\d+[.]/; - my $marker_any = qr/(?:$marker_ul|$marker_ol)/; - - # Re-usable pattern to match any entirel ul or ol list: - my $whole_list = qr{ - ( # $1 = whole list - ( # $2 - [ ]{0,$less_than_tab} - (${marker_any}) # $3 = first list item marker - [ \t]+ - ) - (?s:.+?) - ( # $4 - \z - | - \n{2,} - (?=\S) - (?! # Negative lookahead for another list item marker - [ \t]* - ${marker_any}[ \t]+ - ) - ) - ) - }mx; - - # We use a different prefix before nested lists than top-level lists. - # See extended comment in _ProcessListItems(). - # - # Note: There's a bit of duplication here. My original implementation - # created a scalar regex pattern as the conditional result of the test on - # $g_list_level, and then only ran the $text =~ s{...}{...}egmx - # substitution once, using the scalar as the pattern. This worked, - # everywhere except when running under MT on my hosting account at Pair - # Networks. There, this caused all rebuilds to be killed by the reaper (or - # perhaps they crashed, but that seems incredibly unlikely given that the - # same script on the same server ran fine *except* under MT. I've spent - # more time trying to figure out why this is happening than I'd like to - # admit. My only guess, backed up by the fact that this workaround works, - # is that Perl optimizes the substition when it can figure out that the - # pattern will never change, and when this optimization isn't on, we run - # afoul of the reaper. Thus, the slightly redundant code that uses two - # static s/// patterns rather than one conditional pattern. - - if ($g_list_level) { - $text =~ s{ - ^ - $whole_list - }{ - my $list = $1; - my $list_type = ($3 =~ m/$marker_ul/) ? "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 =~ s/\n{2,}/\n\n\n/g; - my $result = _ProcessListItems($list, $marker_any); - - # Trim any trailing whitespace, to put the closing `` - # 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 =~ s{\s+$}{}; - $result = "<$list_type>" . $result . "\n"; - $result; - }egmx; - } - else { - $text =~ s{ - (?:(?<=\n\n)|\A\n?) - $whole_list - }{ - my $list = $1; - my $list_type = ($3 =~ m/$marker_ul/) ? "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 =~ s/\n{2,}/\n\n\n/g; - my $result = _ProcessListItems($list, $marker_any); - $result = "<$list_type>\n" . $result . "\n"; - $result; - }egmx; - } - - - return $text; -} - - -sub _ProcessListItems { -# -# Process the contents of a single ordered or unordered list, splitting it -# into individual list items. -# - - my $list_str = shift; - my $marker_any = shift; - - - # 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.". - - $g_list_level++; - - # trim trailing blank lines: - $list_str =~ s/\n{2,}\z/\n/; - - - $list_str =~ s{ - (\n)? # leading line = $1 - (^[ \t]*) # leading whitespace = $2 - ($marker_any) [ \t]+ # list marker = $3 - ((?s:.+?) # list item text = $4 - (\n{1,2})) - (?= \n* (\z | \2 ($marker_any) [ \t]+)) - }{ - my $item = $4; - my $leading_line = $1; - my $leading_space = $2; - - if ($leading_line or ($item =~ m/\n{2,}/)) { - $item = _RunBlockGamut(_Outdent($item)); - } - else { - # Recursion for sub-lists: - $item = _DoLists(_Outdent($item)); - chomp $item; - $item = _RunSpanGamut($item); - } - - "
  • " . $item . "
  • \n"; - }egmx; - - $g_list_level--; - return $list_str; -} - - - -sub _DoCodeBlocks { -# -# Process Markdown `
    ` blocks.
    -#  
    -
    -  my $text = shift;
    -
    -  $text =~ s{
    -      (?:\n\n|\A)
    -      (              # $1 = the code block -- one or more lines, starting with a space/tab
    -        (?:
    -          (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
    -          .*\n+
    -        )+
    -      )
    -      ((?=^[ ]{0,$g_tab_width}\S)|\Z)  # Lookahead for non-space at line-start, or end of doc
    -    }{
    -      my $codeblock = $1;
    -      my $result; # return value
    -
    -      $codeblock = _EncodeCode(_Outdent($codeblock));
    -      $codeblock = _Detab($codeblock);
    -      $codeblock =~ s/\A\n+//; # trim leading newlines
    -      $codeblock =~ s/\s+\z//; # trim trailing whitespace
    -
    -      $result = "\n\n
    " . $codeblock . "\n
    \n\n"; - - $result; - }egmx; - - return $text; -} - - -sub _DoCodeSpans { -# -# * Backtick quotes are used for 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: -# -#

    Just type foo `bar` baz at the prompt.

    -# -# 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 `bar` ... -# - - my $text = shift; - - $text =~ s@ - (?$c
    "; - @egsx; - - return $text; -} - - -sub _EncodeCode { -# -# Encode/escape certain characters inside Markdown code runs. -# The point is that in code, these characters are literals, -# and lose their special Markdown meanings. -# - local $_ = shift; - - # Encode all ampersands; HTML entities are not - # entities within a Markdown code span. - s/&/&/g; - - # Encode $'s, but only if we're running under Blosxom. - # (Blosxom interpolates Perl variables in article bodies.) - { - no warnings 'once'; - if (defined($blosxom::version)) { - s/\$/$/g; - } - } - - - # Do the angle bracket song and dance: - s! < !<!gx; - s! > !>!gx; - - # Now, escape characters that are magic in Markdown: - s! \* !$g_escape_table{'*'}!gx; - s! _ !$g_escape_table{'_'}!gx; - s! { !$g_escape_table{'{'}!gx; - s! } !$g_escape_table{'}'}!gx; - s! \[ !$g_escape_table{'['}!gx; - s! \] !$g_escape_table{']'}!gx; - s! \\ !$g_escape_table{'\\'}!gx; - - return $_; -} - - -sub _DoItalicsAndBold { - my $text = shift; - - # must go first: - $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 } - {$2}gsx; - - $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 } - {$2}gsx; - - return $text; -} - - -sub _DoBlockQuotes { - my $text = shift; - - $text =~ s{ - ( # 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 - )+ - ) - }{ - my $bq = $1; - $bq =~ s/^[ \t]*>[ \t]?//gm; # trim one level of quoting - $bq =~ s/^[ \t]+$//mg; # trim whitespace-only lines - $bq = _RunBlockGamut($bq); # recurse - - $bq =~ s/^/ /g; - # These leading spaces screw with
     content, so we need to fix that:
    -      $bq =~ s{
    -          (\s*
    .+?
    ) - }{ - my $pre = $1; - $pre =~ s/^ //mg; - $pre; - }egsx; - - "
    \n$bq\n
    \n\n"; - }egmx; - - - return $text; -} - - -sub _FormParagraphs { -# -# Params: -# $text - string to process with html

    tags -# - my $text = shift; - - # Strip leading and trailing lines: - $text =~ s/\A\n+//; - $text =~ s/\n+\z//; - - my @grafs = split(/\n{2,}/, $text); - - # - # Wrap

    tags. - # - foreach (@grafs) { - unless (defined( $g_html_blocks{$_} )) { - $_ = _RunSpanGamut($_); - s/^([ \t]*)/

    /; - $_ .= "

    "; - } - } - - # - # Unhashify HTML blocks - # - foreach (@grafs) { - if (defined( $g_html_blocks{$_} )) { - $_ = $g_html_blocks{$_}; - } - } - - return join "\n\n", @grafs; -} - - -sub _EncodeAmpsAndAngles { -# Smart processing for ampersands and angle brackets that need to be encoded. - - my $text = shift; - - # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - # http://bumppo.net/projects/amputator/ - $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&/g; - - # Encode naked <'s - $text =~ s{<(?![a-z/?\$!])}{<}gi; - - return $text; -} - - -sub _EncodeBackslashEscapes { -# -# Parameter: String. -# Returns: The string, with after processing the following backslash -# escape sequences. -# - local $_ = shift; - - s! \\\\ !$g_escape_table{'\\'}!gx; # Must process escaped backslashes first. - s! \\` !$g_escape_table{'`'}!gx; - s! \\\* !$g_escape_table{'*'}!gx; - s! \\_ !$g_escape_table{'_'}!gx; - s! \\\{ !$g_escape_table{'{'}!gx; - s! \\\} !$g_escape_table{'}'}!gx; - s! \\\[ !$g_escape_table{'['}!gx; - s! \\\] !$g_escape_table{']'}!gx; - s! \\\( !$g_escape_table{'('}!gx; - s! \\\) !$g_escape_table{')'}!gx; - s! \\> !$g_escape_table{'>'}!gx; - s! \\\# !$g_escape_table{'#'}!gx; - s! \\\+ !$g_escape_table{'+'}!gx; - s! \\\- !$g_escape_table{'-'}!gx; - s! \\\. !$g_escape_table{'.'}!gx; - s{ \\! }{$g_escape_table{'!'}}gx; - - return $_; -} - - -sub _DoAutoLinks { - my $text = shift; - - $text =~ s{<((https?|ftp):[^'">\s]+)>}{
    $1}gi; - - # Email addresses: - $text =~ s{ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - }{ - _EncodeEmailAddress( _UnescapeSpecialChars($1) ); - }egix; - - return $text; -} - - -sub _EncodeEmailAddress { -# -# 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.: -# -# foo -# @example.com -# -# Based on a filter by Matthew Wickline, posted to the BBEdit-Talk -# mailing list: -# - - my $addr = shift; - - srand; - my @encode = ( - sub { '&#' . ord(shift) . ';' }, - sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' }, - sub { shift }, - ); - - $addr = "mailto:" . $addr; - - $addr =~ s{(.)}{ - my $char = $1; - if ( $char eq '@' ) { - # this *must* be encoded. I insist. - $char = $encode[int rand 1]->($char); - } elsif ( $char ne ':' ) { - # leave ':' alone (to spot mailto: later) - my $r = rand; - # roughly 10% raw, 45% hex, 45% dec - $char = ( - $r > .9 ? $encode[2]->($char) : - $r < .45 ? $encode[1]->($char) : - $encode[0]->($char) - ); - } - $char; - }gex; - - $addr = qq{$addr}; - $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part - - return $addr; -} - - -sub _UnescapeSpecialChars { -# -# Swap back in all the special characters we've hidden. -# - my $text = shift; - - while( my($char, $hash) = each(%g_escape_table) ) { - $text =~ s/$hash/$char/g; - } - return $text; -} - - -sub _TokenizeHTML { -# -# Parameter: String containing HTML markup. -# Returns: Reference to an array of the tokens comprising the input -# string. Each token is either a tag (possibly with nested, -# tags contained therein, such as , or a -# run of text between tags. Each element of the array is a -# two-element array; the first is either 'tag' or 'text'; -# the second is the actual value. -# -# -# Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin. -# -# - - my $str = shift; - my $pos = 0; - my $len = length $str; - my @tokens; - - my $depth = 6; - my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x $depth); - my $match = qr/(?s: ) | # comment - (?s: <\? .*? \?> ) | # processing instruction - $nested_tags/ix; # nested tags - - while ($str =~ m/($match)/g) { - my $whole_tag = $1; - my $sec_start = pos $str; - my $tag_start = $sec_start - length $whole_tag; - if ($pos < $tag_start) { - push @tokens, ['text', substr($str, $pos, $tag_start - $pos)]; - } - push @tokens, ['tag', $whole_tag]; - $pos = pos $str; - } - push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len; - \@tokens; -} - - -sub _Outdent { -# -# Remove one level of line-leading tabs or spaces -# - my $text = shift; - - $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm; - return $text; -} - - -sub _Detab { -# -# Cribbed from a post by Bart Lateur: -# -# - my $text = shift; - - $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge; - return $text; -} - - -1; - -__END__ - - -=pod - -=head1 NAME - -B - - -=head1 SYNOPSIS - -B [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ] - [ I ... ] - - -=head1 DESCRIPTION - -Markdown is a text-to-HTML filter; it translates an easy-to-read / -easy-to-write structured text format into HTML. Markdown's text format -is most similar to that of plain text email, and supports features such -as headers, *emphasis*, code blocks, blockquotes, and links. - -Markdown's syntax is designed not as a generic markup language, but -specifically to serve as a front-end to (X)HTML. You can use span-level -HTML tags anywhere in a Markdown document, and you can use block level -HTML tags (like
    and as well). - -For more information about Markdown's syntax, see: - - http://daringfireball.net/projects/markdown/ - - -=head1 OPTIONS - -Use "--" to end switch parsing. For example, to open a file named "-z", use: - - Markdown.pl -- -z - -=over 4 - - -=item B<--html4tags> - -Use HTML 4 style for empty element tags, e.g.: - -
    - -instead of Markdown's default XHTML style tags, e.g.: - -
    - - -=item B<-v>, B<--version> - -Display Markdown's version number and copyright information. - - -=item B<-s>, B<--shortversion> - -Display the short-form version number. - - -=back - - - -=head1 BUGS - -To file bug reports or feature requests (other than topics listed in the -Caveats section above) please send email to: - - support@daringfireball.net - -Please include with your report: (1) the example input; (2) the output -you expected; (3) the output Markdown actually produced. - - -=head1 VERSION HISTORY - -See the readme file for detailed release notes for this version. - -1.0.2b2 - 20 Mar 2005 - - + Fix for nested sub-lists in list-paragraph mode. Previously we got - a spurious extra level of `

    ` tags for something like this: - - * this - - * sub - - that - - + Experimental support for [this] as a synonym for [this][]. - (Note to self: No test yet for this.) - Be sure to test, e.g.: [permutations of this sort of [thing][].] - - -1.0.2b1 - 28 Feb 2005 - - + Fix for backticks within HTML tag: like this - - + Fix for escaped backticks still triggering code spans: - - There are two raw backticks here: \` and here: \`, not a code span - -1.0.1 - 14 Dec 2004 - -1.0 - 28 Aug 2004 - - -=head1 AUTHOR - - John Gruber - http://daringfireball.net - - PHP port and other contributions by Michel Fortin - http://michelf.com - - -=head1 COPYRIGHT AND LICENSE - -Copyright (c) 2003-2005 John Gruber - -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. - -=cut diff --git a/perlMarkdown/Markdown-1.0.2b7.pl b/perlMarkdown/Markdown-1.0.2b7.pl deleted file mode 100644 index c3b351fe..00000000 --- a/perlMarkdown/Markdown-1.0.2b7.pl +++ /dev/null @@ -1,1642 +0,0 @@ -#!/usr/bin/env perl - -# -# Markdown -- A text-to-HTML conversion tool for web writers -# -# Copyright (c) 2004-2005 John Gruber -# -# - - -package Markdown; -require 5.006_000; -use strict; -use warnings; - -use Digest::MD5 qw(md5_hex); -use vars qw($VERSION); -$VERSION = '1.0.2b7'; -# Tue 29 Aug 2006 - -## Disabled; causes problems under Perl 5.6.1: -# use utf8; -# binmode( STDOUT, ":utf8" ); # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html - -# -# Global default settings: -# -my $g_empty_element_suffix = " />"; # Change to ">" for HTML output -my $g_tab_width = 4; - - -# -# Globals: -# - -# Regex to match balanced [brackets]. See Friedl's -# "Mastering Regular Expressions", 2nd Ed., pp. 328-331. -my $g_nested_brackets; -$g_nested_brackets = qr{ - (?> # Atomic matching - [^\[\]]+ # Anything other than brackets - | - \[ - (??{ $g_nested_brackets }) # Recursive set of nested brackets - \] - )* -}x; - - -# Table of hash values for escaped characters: -my %g_escape_table; -foreach my $char (split //, '\\`*_{}[]()>#+-.!') { - $g_escape_table{$char} = md5_hex($char); -} - - -# Global hashes, used by various utility routines -my %g_urls; -my %g_titles; -my %g_html_blocks; - -# Used to track when we're inside an ordered or unordered list -# (see _ProcessListItems() for details): -my $g_list_level = 0; - - -#### Blosxom plug-in interface ########################################## - -# Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine -# which posts Markdown should process, using a "meta-markup: markdown" -# header. If it's set to 0 (the default), Markdown will process all -# entries. -my $g_blosxom_use_meta = 0; - -sub start { 1; } -sub story { - my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_; - - if ( (! $g_blosxom_use_meta) or - (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i)) - ){ - $$body_ref = Markdown($$body_ref); - } - 1; -} - - -#### Movable Type plug-in interface ##################################### -eval {require MT}; # Test to see if we're running in MT. -unless ($@) { - require MT; - import MT; - require MT::Template::Context; - import MT::Template::Context; - - eval {require MT::Plugin}; # Test to see if we're running >= MT 3.0. - unless ($@) { - require MT::Plugin; - import MT::Plugin; - my $plugin = new MT::Plugin({ - name => "Markdown", - description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)", - doc_link => 'http://daringfireball.net/projects/markdown/' - }); - MT->add_plugin( $plugin ); - } - - MT::Template::Context->add_container_tag(MarkdownOptions => sub { - my $ctx = shift; - my $args = shift; - my $builder = $ctx->stash('builder'); - my $tokens = $ctx->stash('tokens'); - - if (defined ($args->{'output'}) ) { - $ctx->stash('markdown_output', lc $args->{'output'}); - } - - defined (my $str = $builder->build($ctx, $tokens) ) - or return $ctx->error($builder->errstr); - $str; # return value - }); - - MT->add_text_filter('markdown' => { - label => 'Markdown', - docs => 'http://daringfireball.net/projects/markdown/', - on_format => sub { - my $text = shift; - my $ctx = shift; - my $raw = 0; - if (defined $ctx) { - my $output = $ctx->stash('markdown_output'); - if (defined $output && $output =~ m/^html/i) { - $g_empty_element_suffix = ">"; - $ctx->stash('markdown_output', ''); - } - elsif (defined $output && $output eq 'raw') { - $raw = 1; - $ctx->stash('markdown_output', ''); - } - else { - $raw = 0; - $g_empty_element_suffix = " />"; - } - } - $text = $raw ? $text : Markdown($text); - $text; - }, - }); - - # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter: - my $smartypants; - - { - no warnings "once"; - $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'}; - } - - if ($smartypants) { - MT->add_text_filter('markdown_with_smartypants' => { - label => 'Markdown With SmartyPants', - docs => 'http://daringfireball.net/projects/markdown/', - on_format => sub { - my $text = shift; - my $ctx = shift; - if (defined $ctx) { - my $output = $ctx->stash('markdown_output'); - if (defined $output && $output eq 'html') { - $g_empty_element_suffix = ">"; - } - else { - $g_empty_element_suffix = " />"; - } - } - $text = Markdown($text); - $text = $smartypants->($text, '1'); - }, - }); - } -} -else { -#### BBEdit/command-line text filter interface ########################## -# Needs to be hidden from MT (and Blosxom when running in static mode). - - # We're only using $blosxom::version once; tell Perl not to warn us: - no warnings 'once'; - unless ( defined($blosxom::version) ) { - use warnings; - - #### Check for command-line switches: ################# - my %cli_opts; - use Getopt::Long; - Getopt::Long::Configure('pass_through'); - GetOptions(\%cli_opts, - 'version', - 'shortversion', - 'html4tags', - ); - if ($cli_opts{'version'}) { # Version info - print "\nThis is Markdown, version $VERSION.\n"; - print "Copyright 2004 John Gruber\n"; - print "http://daringfireball.net/projects/markdown/\n\n"; - exit 0; - } - if ($cli_opts{'shortversion'}) { # Just the version number string. - print $VERSION; - exit 0; - } - if ($cli_opts{'html4tags'}) { # Use HTML tag style instead of XHTML - $g_empty_element_suffix = ">"; - } - - - #### Process incoming text: ########################### - my $text; - { - local $/; # Slurp the whole file - $text = <>; - } - print Markdown($text); - } -} - - - -sub Markdown { -# -# Main function. The order in which other subs are called here is -# essential. Link and image substitutions need to happen before -# _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the -# and tags get encoded. -# - my $text = shift; - - # Clear the global hashes. If we don't clear these, you get conflicts - # from other articles when generating a page which contains more than - # one article (e.g. an index page that shows the N most recent - # articles): - %g_urls = (); - %g_titles = (); - %g_html_blocks = (); - - - # Standardize line endings: - $text =~ s{\r\n}{\n}g; # DOS to Unix - $text =~ s{\r}{\n}g; # Mac to Unix - - # Make sure $text ends with a couple of newlines: - $text .= "\n\n"; - - # Convert all tabs to spaces. - $text = _Detab($text); - - # Strip any lines consisting only of spaces and tabs. - # This makes subsequent regexen easier to write, because we can - # match consecutive blank lines with /\n+/ instead of something - # contorted like /[ \t]*\n+/ . - $text =~ s/^[ \t]+$//mg; - - # Turn block-level HTML blocks into hash entries - $text = _HashHTMLBlocks($text); - - # Strip link definitions, store in hashes. - $text = _StripLinkDefinitions($text); - - $text = _RunBlockGamut($text); - - $text = _UnescapeSpecialChars($text); - - return $text . "\n"; -} - - -sub _StripLinkDefinitions { -# -# Strips link definitions from text, stores the URLs and titles in -# hash references. -# - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - while ($text =~ s{ - ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1 - [ \t]* - \n? # maybe *one* newline - [ \t]* - ? # url = $2 - [ \t]* - \n? # maybe one newline - [ \t]* - (?: - (?<=\s) # lookbehind for whitespace - ["(] - (.+?) # title = $3 - [")] - [ \t]* - )? # title is optional - (?:\n+|\Z) - } - {}mx) { - $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 ); # Link IDs are case-insensitive - if ($3) { - $g_titles{lc $1} = $3; - $g_titles{lc $1} =~ s/"/"/g; - } - } - - return $text; -} - - -sub _HashHTMLBlocks { - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # 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

    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: - my $block_tags = qr{ - (?: - p | div | h[1-6] | blockquote | pre | table | - dl | ol | ul | script | noscript | form | - fieldset | iframe | math | ins | del - ) - }x; - - my $tag_attrs = qr{ - (?: # Match one attr name/value pair - \s+ # There needs to be at least some whitespace - # before each attribute name. - [\w.:_-]+ # Attribute name - \s*=\s* - (["']) # Attribute quoter - .+? # Attribute value - \1 # Closing quoter - )* # Zero or more - }x; - - my $empty_tag = qr{< \w+ $tag_attrs \s* />}xms; - my $open_tag = qr{< $block_tags $tag_attrs \s* >}xms; - my $close_tag = undef; # let Text::Balanced handle this - - use Text::Balanced qw(gen_extract_tagged); - my $extract_block = gen_extract_tagged($open_tag, $close_tag, undef, { ignore => [$empty_tag] }); - - my @chunks; - ## TO-DO: the 0,3 on the next line ought to respect the - ## tabwidth, or else, we should mandate 4-space tabwidth and - ## be done with it: - while ($text =~ s{^(([ ]{0,3}<)?.*\n)}{}m) { - my $cur_line = $1; - if (defined $2) { - # current line could be start of code block - - my ($tag, $remainder) = $extract_block->($cur_line . $text); - if ($tag) { - my $key = md5_hex($tag); - $g_html_blocks{$key} = $tag; - push @chunks, "\n\n" . $key . "\n\n"; - $text = $remainder; - } - else { - # No tag match, so toss $cur_line into @chunks - push @chunks, $cur_line; - } - } - else { - # current line could NOT be start of code block - push @chunks, $cur_line; - } - - } - push @chunks, $text; # Whatever is left. - - $text = join '', @chunks; - - - - # Special case just for


    . It was easier to make a special case than - # to make the other regex more complicated. - $text =~ s{ - (?: - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - [ ]{0,$less_than_tab} - <(hr) # start tag = $2 - \b # word break - ([^<>])*? # - /?> # the matching end tag - [ \t]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egx; - - # Special case for standalone HTML comments: - $text =~ s{ - (?: - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - [ ]{0,$less_than_tab} - (?s: - - ) - [ \t]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egx; - - # PHP and ASP-style processor instructions ( and <%…%>) - $text =~ s{ - (?: - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - [ ]{0,$less_than_tab} - (?s: - <([?%]) # $2 - .*? - \2> - ) - [ \t]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - ) - }{ - my $key = md5_hex($1); - $g_html_blocks{$key} = $1; - "\n\n" . $key . "\n\n"; - }egx; - - - return $text; -} - - -sub _RunBlockGamut { -# -# These are all the transformations that form block-level -# tags like paragraphs, headers, and list items. -# - my $text = shift; - - $text = _DoHeaders($text); - - # Do Horizontal Rules: - $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n tags around block-level tags. - $text = _HashHTMLBlocks($text); - $text = _FormParagraphs($text); - - return $text; -} - - -sub _RunSpanGamut { -# -# These are all the transformations that occur *within* block-level -# tags like paragraphs, headers, and list items. -# - my $text = shift; - - $text = _DoCodeSpans($text); - $text = _EscapeSpecialCharsWithinTagAttributes($text); - $text = _EncodeBackslashEscapes($text); - - # Process anchor and image tags. Images must come first, - # because ![foo][f] looks like an anchor. - $text = _DoImages($text); - $text = _DoAnchors($text); - - # Make links out of things like `` - # Must come after _DoAnchors(), because you can use < and > - # delimiters in inline links like [this](). - $text = _DoAutoLinks($text); - $text = _EncodeAmpsAndAngles($text); - $text = _DoItalicsAndBold($text); - - # Do hard breaks: - $text =~ s/ {2,}\n/ -- encode [\ ` * _] so they -# don't conflict with their use in Markdown for code, italics and strong. -# We're replacing each such character with its corresponding MD5 checksum -# value; this is likely overkill, but it should prevent us from colliding -# with the escape values by accident. -# - my $text = shift; - my $tokens ||= _TokenizeHTML($text); - $text = ''; # rebuild $text from the tokens - - foreach my $cur_token (@$tokens) { - if ($cur_token->[0] eq "tag") { - $cur_token->[1] =~ s! \\ !$g_escape_table{'\\'}!gx; - $cur_token->[1] =~ s{ (?<=.)(?=.) }{$g_escape_table{'`'}}gx; - $cur_token->[1] =~ s! \* !$g_escape_table{'*'}!gx; - $cur_token->[1] =~ s! _ !$g_escape_table{'_'}!gx; - } - $text .= $cur_token->[1]; - } - return $text; -} - - -sub _DoAnchors { -# -# Turn Markdown link shortcuts into XHTML tags. -# - my $text = shift; - - # - # First, handle reference-style links: [link text] [id] - # - $text =~ s{ - ( # wrap whole match in $1 - \[ - ($g_nested_brackets) # link text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - ) - }{ - my $result; - my $whole_match = $1; - my $link_text = $2; - my $link_id = lc $3; - - if ($link_id eq "") { - $link_id = lc $link_text; # for shortcut links like [this][]. - } - - if (defined $g_urls{$link_id}) { - my $url = $g_urls{$link_id}; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "? # href = $3 - [ \t]* - ( # $4 - (['"]) # quote char = $5 - (.*?) # Title = $6 - \5 # matching quote - [ \t]* # ignore any spaces/tabs between closing quote and ) - )? # title is optional - \) - ) - }{ - my $result; - my $whole_match = $1; - my $link_text = $2; - my $url = $3; - my $title = $6; - - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = " tags. -# - my $text = shift; - - # - # First, handle reference-style labeled images: ![alt text][id] - # - $text =~ s{ - ( # wrap whole match in $1 - !\[ - (.*?) # alt text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - - ) - }{ - my $result; - my $whole_match = $1; - my $alt_text = $2; - my $link_id = lc $3; - - if ($link_id eq "") { - $link_id = lc $alt_text; # for shortcut links like ![this][]. - } - - $alt_text =~ s/"/"/g; - if (defined $g_urls{$link_id}) { - my $url = $g_urls{$link_id}; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "\"$alt_text\"";? # src url = $3 - [ \t]* - ( # $4 - (['"]) # quote char = $5 - (.*?) # title = $6 - \5 # matching quote - [ \t]* - )? # title is optional - \) - ) - }{ - my $result; - my $whole_match = $1; - my $alt_text = $2; - my $url = $3; - my $title = ''; - if (defined($6)) { - $title = $6; - } - - $alt_text =~ s/"/"/g; - $title =~ s/"/"/g; - $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid - $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. - $result = "\"$alt_text\"";" . _RunSpanGamut($1) . "\n\n"; - }egmx; - - $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{ - "

    " . _RunSpanGamut($1) . "

    \n\n"; - }egmx; - - - # atx-style headers: - # # Header 1 - # ## Header 2 - # ## Header 2 with closing hashes ## - # ... - # ###### Header 6 - # - $text =~ s{ - ^(\#{1,6}) # $1 = string of #'s - [ \t]* - (.+?) # $2 = Header text - [ \t]* - \#* # optional closing #'s (not counted) - \n+ - }{ - my $h_level = length($1); - "" . _RunSpanGamut($2) . "\n\n"; - }egmx; - - return $text; -} - - -sub _DoLists { -# -# Form HTML ordered (numbered) and unordered (bulleted) lists. -# - my $text = shift; - my $less_than_tab = $g_tab_width - 1; - - # Re-usable patterns to match list item bullets and number markers: - my $marker_ul = qr/[*+-]/; - my $marker_ol = qr/\d+[.]/; - my $marker_any = qr/(?:$marker_ul|$marker_ol)/; - - # Re-usable pattern to match any entirel ul or ol list: - my $whole_list = qr{ - ( # $1 = whole list - ( # $2 - [ ]{0,$less_than_tab} - (${marker_any}) # $3 = first list item marker - [ \t]+ - ) - (?s:.+?) - ( # $4 - \z - | - \n{2,} - (?=\S) - (?! # Negative lookahead for another list item marker - [ \t]* - ${marker_any}[ \t]+ - ) - ) - ) - }mx; - - # We use a different prefix before nested lists than top-level lists. - # See extended comment in _ProcessListItems(). - # - # Note: There's a bit of duplication here. My original implementation - # created a scalar regex pattern as the conditional result of the test on - # $g_list_level, and then only ran the $text =~ s{...}{...}egmx - # substitution once, using the scalar as the pattern. This worked, - # everywhere except when running under MT on my hosting account at Pair - # Networks. There, this caused all rebuilds to be killed by the reaper (or - # perhaps they crashed, but that seems incredibly unlikely given that the - # same script on the same server ran fine *except* under MT. I've spent - # more time trying to figure out why this is happening than I'd like to - # admit. My only guess, backed up by the fact that this workaround works, - # is that Perl optimizes the substition when it can figure out that the - # pattern will never change, and when this optimization isn't on, we run - # afoul of the reaper. Thus, the slightly redundant code that uses two - # static s/// patterns rather than one conditional pattern. - - if ($g_list_level) { - $text =~ s{ - ^ - $whole_list - }{ - my $list = $1; - my $list_type = ($3 =~ m/$marker_ul/) ? "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 =~ s/\n{2,}/\n\n\n/g; - my $result = _ProcessListItems($list, $marker_any); - - # Trim any trailing whitespace, to put the closing `` - # 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 =~ s{\s+$}{}; - $result = "<$list_type>" . $result . "\n"; - $result; - }egmx; - } - else { - $text =~ s{ - (?:(?<=\n\n)|\A\n?) - $whole_list - }{ - my $list = $1; - my $list_type = ($3 =~ m/$marker_ul/) ? "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 =~ s/\n{2,}/\n\n\n/g; - my $result = _ProcessListItems($list, $marker_any); - $result = "<$list_type>\n" . $result . "\n"; - $result; - }egmx; - } - - - return $text; -} - - -sub _ProcessListItems { -# -# Process the contents of a single ordered or unordered list, splitting it -# into individual list items. -# - - my $list_str = shift; - my $marker_any = shift; - - - # 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.". - - $g_list_level++; - - # trim trailing blank lines: - $list_str =~ s/\n{2,}\z/\n/; - - - $list_str =~ s{ - (\n)? # leading line = $1 - (^[ \t]*) # leading whitespace = $2 - ($marker_any) [ \t]+ # list marker = $3 - ((?s:.+?) # list item text = $4 - (\n{1,2})) - (?= \n* (\z | \2 ($marker_any) [ \t]+)) - }{ - my $item = $4; - my $leading_line = $1; - my $leading_space = $2; - - if ($leading_line or ($item =~ m/\n{2,}/)) { - $item = _RunBlockGamut(_Outdent($item)); - } - else { - # Recursion for sub-lists: - $item = _DoLists(_Outdent($item)); - chomp $item; - $item = _RunSpanGamut($item); - } - - "
  • " . $item . "
  • \n"; - }egmx; - - $g_list_level--; - return $list_str; -} - - - -sub _DoCodeBlocks { -# -# Process Markdown `
    ` blocks.
    -#	
    -
    -	my $text = shift;
    -
    -	$text =~ s{
    -			(?:\n\n|\A)
    -			(	            # $1 = the code block -- one or more lines, starting with a space/tab
    -			  (?:
    -			    (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
    -			    .*\n+
    -			  )+
    -			)
    -			((?=^[ ]{0,$g_tab_width}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    -		}{
    -			my $codeblock = $1;
    -			my $result; # return value
    -
    -			$codeblock = _EncodeCode(_Outdent($codeblock));
    -			$codeblock = _Detab($codeblock);
    -			$codeblock =~ s/\A\n+//; # trim leading newlines
    -			$codeblock =~ s/\n+\z//; # trim trailing newlines
    -
    -			$result = "\n\n
    " . $codeblock . "\n
    \n\n"; - - $result; - }egmx; - - return $text; -} - - -sub _DoCodeSpans { -# -# * Backtick quotes are used for 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: -# -#

    Just type foo `bar` baz at the prompt.

    -# -# 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 `bar` ... -# - - my $text = shift; - - $text =~ s@ - (?$c
    "; - @egsx; - - return $text; -} - - -sub _EncodeCode { -# -# Encode/escape certain characters inside Markdown code runs. -# The point is that in code, these characters are literals, -# and lose their special Markdown meanings. -# - local $_ = shift; - - # Encode all ampersands; HTML entities are not - # entities within a Markdown code span. - s/&/&/g; - - # Encode $'s, but only if we're running under Blosxom. - # (Blosxom interpolates Perl variables in article bodies.) - { - no warnings 'once'; - if (defined($blosxom::version)) { - s/\$/$/g; - } - } - - - # Do the angle bracket song and dance: - s! < !<!gx; - s! > !>!gx; - - # Now, escape characters that are magic in Markdown: - s! \* !$g_escape_table{'*'}!gx; - s! _ !$g_escape_table{'_'}!gx; - s! { !$g_escape_table{'{'}!gx; - s! } !$g_escape_table{'}'}!gx; - s! \[ !$g_escape_table{'['}!gx; - s! \] !$g_escape_table{']'}!gx; - s! \\ !$g_escape_table{'\\'}!gx; - - return $_; -} - - -sub _DoItalicsAndBold { - my $text = shift; - - # must go first: - $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 } - {$2}gsx; - - $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 } - {$2}gsx; - - return $text; -} - - -sub _DoBlockQuotes { - my $text = shift; - - $text =~ s{ - ( # 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 - )+ - ) - }{ - my $bq = $1; - $bq =~ s/^[ \t]*>[ \t]?//gm; # trim one level of quoting - $bq =~ s/^[ \t]+$//mg; # trim whitespace-only lines - $bq = _RunBlockGamut($bq); # recurse - - $bq =~ s/^/ /g; - # These leading spaces screw with
     content, so we need to fix that:
    -			$bq =~ s{
    -					(\s*
    .+?
    ) - }{ - my $pre = $1; - $pre =~ s/^ //mg; - $pre; - }egsx; - - "
    \n$bq\n
    \n\n"; - }egmx; - - - return $text; -} - - -sub _FormParagraphs { -# -# Params: -# $text - string to process with html

    tags -# - my $text = shift; - - # Strip leading and trailing lines: - $text =~ s/\A\n+//; - $text =~ s/\n+\z//; - - my @grafs = split(/\n{2,}/, $text); - - # - # Wrap

    tags. - # - foreach (@grafs) { - unless (defined( $g_html_blocks{$_} )) { - $_ = _RunSpanGamut($_); - s/^([ \t]*)/

    /; - $_ .= "

    "; - } - } - - # - # Unhashify HTML blocks - # -# foreach my $graf (@grafs) { -# my $block = $g_html_blocks{$graf}; -# if (defined $block) { -# $graf = $block; -# } -# } - - foreach my $graf (@grafs) { - # Modify elements of @grafs in-place... - my $block = $g_html_blocks{$graf}; - if (defined $block) { - $graf = $block; - if ($block =~ m{ - \A - ( # $1 =
    tag -
    ]* - \b - markdown\s*=\s* (['"]) # $2 = attr quote char - 1 - \2 - [^>]* - > - ) - ( # $3 = contents - .* - ) - (
    ) # $4 = closing tag - \z - - }xms - ) { - my ($div_open, $div_content, $div_close) = ($1, $3, $4); - - # We can't call Markdown(), because that resets the hash; - # that initialization code should be pulled into its own sub, though. - $div_content = _HashHTMLBlocks($div_content); - $div_content = _StripLinkDefinitions($div_content); - $div_content = _RunBlockGamut($div_content); - $div_content = _UnescapeSpecialChars($div_content); - - $div_open =~ s{\smarkdown\s*=\s*(['"]).+?\1}{}ms; - - $graf = $div_open . "\n" . $div_content . "\n" . $div_close; - } - } - } - - - return join "\n\n", @grafs; -} - - -sub _EncodeAmpsAndAngles { -# Smart processing for ampersands and angle brackets that need to be encoded. - - my $text = shift; - - # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - # http://bumppo.net/projects/amputator/ - $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&/g; - - # Encode naked <'s - $text =~ s{<(?![a-z/?\$!])}{<}gi; - - return $text; -} - - -sub _EncodeBackslashEscapes { -# -# Parameter: String. -# Returns: The string, with after processing the following backslash -# escape sequences. -# - local $_ = shift; - - s! \\\\ !$g_escape_table{'\\'}!gx; # Must process escaped backslashes first. - s! \\` !$g_escape_table{'`'}!gx; - s! \\\* !$g_escape_table{'*'}!gx; - s! \\_ !$g_escape_table{'_'}!gx; - s! \\\{ !$g_escape_table{'{'}!gx; - s! \\\} !$g_escape_table{'}'}!gx; - s! \\\[ !$g_escape_table{'['}!gx; - s! \\\] !$g_escape_table{']'}!gx; - s! \\\( !$g_escape_table{'('}!gx; - s! \\\) !$g_escape_table{')'}!gx; - s! \\> !$g_escape_table{'>'}!gx; - s! \\\# !$g_escape_table{'#'}!gx; - s! \\\+ !$g_escape_table{'+'}!gx; - s! \\\- !$g_escape_table{'-'}!gx; - s! \\\. !$g_escape_table{'.'}!gx; - s{ \\! }{$g_escape_table{'!'}}gx; - - return $_; -} - - -sub _DoAutoLinks { - my $text = shift; - - $text =~ s{<((https?|ftp|dict):[^'">\s]+)>}{
    $1}gi; - - # Email addresses: - $text =~ s{ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - }{ - _EncodeEmailAddress( _UnescapeSpecialChars($1) ); - }egix; - - return $text; -} - - -sub _EncodeEmailAddress { -# -# 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.: -# -# foo -# @example.com -# -# Based on a filter by Matthew Wickline, posted to the BBEdit-Talk -# mailing list: -# - - my $addr = shift; - - srand; - my @encode = ( - sub { '&#' . ord(shift) . ';' }, - sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' }, - sub { shift }, - ); - - $addr = "mailto:" . $addr; - - $addr =~ s{(.)}{ - my $char = $1; - if ( $char eq '@' ) { - # this *must* be encoded. I insist. - $char = $encode[int rand 1]->($char); - } elsif ( $char ne ':' ) { - # leave ':' alone (to spot mailto: later) - my $r = rand; - # roughly 10% raw, 45% hex, 45% dec - $char = ( - $r > .9 ? $encode[2]->($char) : - $r < .45 ? $encode[1]->($char) : - $encode[0]->($char) - ); - } - $char; - }gex; - - $addr = qq{$addr}; - $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part - - return $addr; -} - - -sub _UnescapeSpecialChars { -# -# Swap back in all the special characters we've hidden. -# - my $text = shift; - - while( my($char, $hash) = each(%g_escape_table) ) { - $text =~ s/$hash/$char/g; - } - return $text; -} - - -sub _TokenizeHTML { -# -# Parameter: String containing HTML markup. -# Returns: Reference to an array of the tokens comprising the input -# string. Each token is either a tag (possibly with nested, -# tags contained therein, such as , or a -# run of text between tags. Each element of the array is a -# two-element array; the first is either 'tag' or 'text'; -# the second is the actual value. -# -# -# Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin. -# -# - - my $str = shift; - my $pos = 0; - my $len = length $str; - my @tokens; - - my $depth = 6; - my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x $depth); - my $match = qr/(?s: ) | # comment - (?s: <\? .*? \?> ) | # processing instruction - $nested_tags/ix; # nested tags - - while ($str =~ m/($match)/g) { - my $whole_tag = $1; - my $sec_start = pos $str; - my $tag_start = $sec_start - length $whole_tag; - if ($pos < $tag_start) { - push @tokens, ['text', substr($str, $pos, $tag_start - $pos)]; - } - push @tokens, ['tag', $whole_tag]; - $pos = pos $str; - } - push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len; - - return \@tokens; -} - - -sub _Outdent { -# -# Remove one level of line-leading tabs or spaces -# - my $text = shift; - - $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm; - return $text; -} - - -sub _Detab { -# -# Cribbed from a post by Bart Lateur: -# -# - my $text = shift; - - $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge; - return $text; -} - - -1; - -__END__ - - -=pod - -=head1 NAME - -B - - -=head1 SYNOPSIS - -B [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ] - [ I ... ] - - -=head1 DESCRIPTION - -Markdown is a text-to-HTML filter; it translates an easy-to-read / -easy-to-write structured text format into HTML. Markdown's text format -is most similar to that of plain text email, and supports features such -as headers, *emphasis*, code blocks, blockquotes, and links. - -Markdown's syntax is designed not as a generic markup language, but -specifically to serve as a front-end to (X)HTML. You can use span-level -HTML tags anywhere in a Markdown document, and you can use block level -HTML tags (like
    and
    as well). - -For more information about Markdown's syntax, see: - - http://daringfireball.net/projects/markdown/ - - -=head1 OPTIONS - -Use "--" to end switch parsing. For example, to open a file named "-z", use: - - Markdown.pl -- -z - -=over 4 - - -=item B<--html4tags> - -Use HTML 4 style for empty element tags, e.g.: - -
    - -instead of Markdown's default XHTML style tags, e.g.: - -
    - - -=item B<-v>, B<--version> - -Display Markdown's version number and copyright information. - - -=item B<-s>, B<--shortversion> - -Display the short-form version number. - - -=back - - - -=head1 BUGS - -To file bug reports or feature requests (other than topics listed in the -Caveats section above) please send email to: - - support@daringfireball.net - -Please include with your report: (1) the example input; (2) the output -you expected; (3) the output Markdown actually produced. - - -=head1 VERSION HISTORY - -See the readme file for detailed release notes for this version. - -1.0.2b7 - - + Changed shebang line from "/usr/bin/perl" to "/usr/bin/env perl" - - + Now only trim trailing newlines from code blocks, instead of trimming - all trailing whitespace characters. - - -1.0.2b6 - Mon 03 Apr 2006 - - + Fixed bad performance bug in new `Text::Balanced`-based block-level parser. - - -1.0.2b5 - Thu 08 Dec 2005 - - + Fixed bug where this: - - [text](http://m.com "title" ) - - wasn't working as expected, because the parser wasn't allowing for spaces - before the closing paren. - - -1.0.2b4 - Thu 08 Sep 2005 - - + Filthy hack to support markdown='1' in div tags, because I need it - to write today's fireball. - - + First crack at a new, smarter, block-level HTML parser. - -1.0.2b3 - Thu 28 Apr 2005 - - + _DoAutoLinks() now supports the 'dict://' URL scheme. - - + PHP- and ASP-style processor instructions are now protected as - raw HTML blocks. - - - <% ... %> - - + Workarounds for regressions introduced with fix for "backticks within - tags" bug in 1.0.2b1. The fix is to allow `...` to be turned into - ... within an HTML tag attribute, and then to turn - these spurious `` tags back into literal backtick characters - in _EscapeSpecialCharsWithinTagAttributes(). - - The regression was caused because in the fix, we moved - _EscapeSpecialCharsWithinTagAttributes() ahead of _DoCodeSpans() - in _RunSpanGamut(), but that's no good. We need to process code - spans first, otherwise we can get tripped up by something like this: - - `` - - -1.0.2b2 - 20 Mar 2005 - - + Fix for nested sub-lists in list-paragraph mode. Previously we got - a spurious extra level of `

    ` tags for something like this: - - * this - - * sub - - that - - + Experimental support for [this] as a synonym for [this][]. - (Note to self: No test yet for this.) - Be sure to test, e.g.: [permutations of this sort of [thing][].] - - -1.0.2b1 - 28 Feb 2005 - - + Fix for backticks within HTML tag: like this - - + Fix for escaped backticks still triggering code spans: - - There are two raw backticks here: \` and here: \`, not a code span - -1.0.1 - 14 Dec 2004 - -1.0 - 28 Aug 2004 - - -=head1 AUTHOR - - John Gruber - http://daringfireball.net - - PHP port and other contributions by Michel Fortin - http://michelf.com - - -=head1 COPYRIGHT AND LICENSE - -Copyright (c) 2003-2005 John Gruber - -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. - -=cut diff --git a/perlMarkdown/readme.txt b/perlMarkdown/readme.txt deleted file mode 100644 index 9e73e56c..00000000 --- a/perlMarkdown/readme.txt +++ /dev/null @@ -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`. diff --git a/src/ng-showdown.js b/src/angular.js similarity index 87% rename from src/ng-showdown.js rename to src/angular.js index 9e9e617c..e2bc6068 100644 --- a/src/ng-showdown.js +++ b/src/angular.js @@ -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: *

    * - * @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"); } diff --git a/src/extensions/github.js b/src/extensions/github.js deleted file mode 100644 index c4f86827..00000000 --- a/src/extensions/github.js +++ /dev/null @@ -1,25 +0,0 @@ -// -// Github Extension (WIP) -// ~~strike-through~~ -> strike-through -// - -(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 '' + content + ''; - } - } - ]; - }; - - // 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; -}()); diff --git a/src/extensions/prettify.js b/src/extensions/prettify.js deleted file mode 100644 index 4b3a70b2..00000000 --- a/src/extensions/prettify.js +++ /dev/null @@ -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(/(
    )?/gi, function(match, pre) {
    -                    if (pre) {
    -                        return '
    ';
    -                    } else {
    -                        return '';
    -                    }
    -                });
    -            }}
    -        ];
    -    };
    -
    -    // 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;
    -
    -}());
    diff --git a/src/extensions/table.js b/src/extensions/table.js
    deleted file mode 100644
    index faa96598..00000000
    --- a/src/extensions/table.js
    +++ /dev/null
    @@ -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 '
    '; - }; - tables.td = function(cell) { - return ''; - }; - tables.ths = function(){ - var out = "", i = 0, hs = [].slice.apply(arguments); - for (i;i'); - 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(''); - 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(''); - tbl.push('
    ' + header + '' + converter.makeHtml(cell) + '
    '); - // 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; - } -}()); diff --git a/src/extensions/twitter.js b/src/extensions/twitter.js deleted file mode 100644 index cde3379d..00000000 --- a/src/extensions/twitter.js +++ /dev/null @@ -1,42 +0,0 @@ -// -// Twitter Extension -// @username -> @username -// #hashtag -> #hashtag -// - -(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 '@' + username + ''; - } - }}, - - // #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 '#' + tag + ''; - } - }}, - - // 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; - -}()); diff --git a/src/helpers.js b/src/helpers.js new file mode 100644 index 00000000..9cc87319 --- /dev/null +++ b/src/helpers.js @@ -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; diff --git a/src/loader.js b/src/loader.js new file mode 100644 index 00000000..8c044dfe --- /dev/null +++ b/src/loader.js @@ -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; +} diff --git a/src/showdown..legacy.js b/src/showdown..legacy.js deleted file mode 100644 index 25bed634..00000000 --- a/src/showdown..legacy.js +++ /dev/null @@ -1,1454 +0,0 @@ -// -// showdown.js -- A javascript port of Markdown. -// -// Copyright (c) 2007 John Fraser. -// -// Original Markdown Copyright (c) 2004-2005 John Gruber -// -// -// 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 -// -// -// - -// -// Wherever possible, Showdown is a straight, line-by-line port -// of the Perl version of Markdown. -// -// This is not a normal parser design; it's basically just a -// series of string substitutions. It's hard to read and -// maintain this way, but keeping Showdown close to the original -// design makes it easier to port new features. -// -// More importantly, Showdown behaves like markdown.pl in most -// edge cases. So web applications can do client-side preview -// in Javascript, and then build identical HTML on the server. -// -// This port needs the new RegExp functionality of ECMA 262, -// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers -// should do fine. Even with the new regular expression features, -// We do a lot of work to emulate Perl's regex functionality. -// The tricky changes in this file mostly have the "attacklab:" -// label. Major or self-explanatory changes don't. -// -// Smart diff tools like Araxis Merge will be able to match up -// this file with markdown.pl in a useful way. A little tweaking -// helps: in a copy of markdown.pl, replace "#" with "//" and -// replace "$text" with "text". Be sure to ignore whitespace -// and line endings. -// - - -// -// Showdown usage: -// -// var text = "Markdown *rocks*."; -// -// var converter = new Showdown.converter(); -// var html = converter.makeHtml(text); -// -// alert(html); -// -// Note: move the sample code to the bottom of this -// file before uncommenting it. -// - - -// -// Showdown namespace -// -var Showdown = {extensions: {}}; - -// -// forEach -// -var forEach = Showdown.forEach = function (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); - } - } -}; - -// -// Standard extension naming -// -var stdExtName = function (s) { - return s.replace(/[_-]||\s/g, '').toLowerCase(); -}; - -// -// converter -// -// Wraps all "globals" so that the only thing -// exposed is makeHtml(). -// -Showdown.converter = function (converter_options) { - -// -// Globals: -// - -// Global hashes, used by various utility routines - var g_urls; - var g_titles; - var g_html_blocks; - -// Used to track when we're inside an ordered or unordered list -// (see _ProcessListItems() for details): - var g_list_level = 0; - -// Global extensions - var g_lang_extensions = []; - var g_output_modifiers = []; - - -// -// Automatic Extension Loading (node only): -// - if (typeof module !== 'undefined' && typeof exports !== 'undefined' && typeof require !== 'undefined') { - var fs = require('fs'); - - if (fs) { - // Search extensions folder - var extensions = fs.readdirSync((__dirname || '.') + '/extensions').filter(function (file) { - return ~file.indexOf('.js'); - }).map(function (file) { - return file.replace(/\.js$/, ''); - }); - // Load extensions into Showdown namespace - Showdown.forEach(extensions, function (ext) { - var name = stdExtName(ext); - Showdown.extensions[name] = require('./extensions/' + ext); - }); - } - } - - this.makeHtml = function (text) { -// -// Main function. The order in which other subs are called here is -// essential. Link and image substitutions need to happen before -// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the -// and tags get encoded. -// - - // Clear the global hashes. If we don't clear these, you get conflicts - // from other articles when generating a page which contains more than - // one article (e.g. an index page that shows the N most recent - // articles): - g_urls = {}; - g_titles = {}; - g_html_blocks = []; - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitray; 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"; - - // Convert all tabs to spaces. - text = _Detab(text); - - // Strip any lines consisting only of spaces and tabs. - // This makes subsequent regexen easier to write, because we can - // match consecutive blank lines with /\n+/ instead of something - // contorted like /[ \t]*\n+/ . - text = text.replace(/^[ \t]+$/mg, ""); - - // Run language extensions - Showdown.forEach(g_lang_extensions, function (x) { - text = _ExecuteExtension(x, text); - }); - - // Handle github codeblocks prior to running HashHTML so that - // HTML contained within the codeblock gets escaped propertly - text = _DoGithubCodeBlocks(text); - - // Turn block-level HTML blocks into hash entries - text = _HashHTMLBlocks(text); - - // Strip link definitions, store in hashes. - text = _StripLinkDefinitions(text); - - text = _RunBlockGamut(text); - - text = _UnescapeSpecialChars(text); - - // 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; - }; - - -// -// Options: -// - -// Parse extensions options into separate arrays - if (converter_options && converter_options.extensions) { - - var self = this; - - // Iterate over each plugin - Showdown.forEach(converter_options.extensions, function (plugin) { - - // Assume it's a bundled plugin if a string is given - if (typeof plugin === 'string') { - plugin = Showdown.extensions[stdExtName(plugin)]; - } - - if (typeof plugin === 'function') { - // Iterate over each extension within that plugin - Showdown.forEach(plugin(self), function (ext) { - // Sort extensions by type - if (ext.type) { - if (ext.type === 'language' || ext.type === 'lang') { - g_lang_extensions.push(ext); - } else if (ext.type === 'output' || ext.type === 'html') { - g_output_modifiers.push(ext); - } - } else { - // Assume language extension - g_output_modifiers.push(ext); - } - }); - } else { - throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension."; - } - }); - } - - - var _ExecuteExtension = function (ext, text) { - if (ext.regex) { - var re = new RegExp(ext.regex, 'g'); - return text.replace(re, ext.replace); - } else if (ext.filter) { - return ext.filter(text); - } - }; - - var _StripLinkDefinitions = function (text) { -// -// Strips link definitions from text, stores the URLs and titles in -// hash references. -// - - // Link defs are in the form: ^[id]: url "optional title" - - /* - var text = text.replace(/ - ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - [ \t]* - \n? // maybe *one* newline - [ \t]* - ? // 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(){...}); - */ - - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += "~0"; - - text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm, - function (wholeMatch, m1, m2, m3, m4) { - m1 = m1.toLowerCase(); - g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive - 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) { - g_titles[m1] = m4.replace(/"/g, """); - } - - // Completely remove the definition from the text - return ""; - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - var _HashHTMLBlocks = function (text) { - // 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

    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.: - //

    - //
    - // tags for inner block must be indented. - //
    - //
    - // - // 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 `
    ` and stop at the first `
    `. - - // 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 - // 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, hashElement); - - // - // Now match more liberally, simply from `\n` to `\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 - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); - - // Special case just for
    . 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,hashElement); - */ - text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement); - - // 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 - - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g, hashElement); - - // 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,hashElement); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement); - - // attacklab: Undo double lines (see comment at top of this function) - text = text.replace(/\n\n/g, "\n"); - return text; - } - - var hashElement = 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" + (g_html_blocks.push(blockText) - 1) + "K\n\n"; - - return blockText; - }; - - var _RunBlockGamut = function (text) { -// -// These are all the transformations that form block-level -// tags like paragraphs, headers, and list items. -// - text = _DoHeaders(text); - - // Do Horizontal Rules: - var key = hashBlock("
    "); - 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 = _DoLists(text); - text = _DoCodeBlocks(text); - text = _DoBlockQuotes(text); - - // 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 - //

    tags around block-level tags. - text = _HashHTMLBlocks(text); - text = _FormParagraphs(text); - - return text; - }; - - var _RunSpanGamut = function (text) { -// -// These are all the transformations that occur *within* block-level -// tags like paragraphs, headers, and list items. -// - - text = _DoCodeSpans(text); - text = _EscapeSpecialCharsWithinTagAttributes(text); - text = _EncodeBackslashEscapes(text); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = _DoImages(text); - text = _DoAnchors(text); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = _DoAutoLinks(text); - text = _EncodeAmpsAndAngles(text); - text = _DoItalicsAndBold(text); - - // Do hard breaks: - text = text.replace(/ +\n/g, "
    \n"); - - return text; - } - - var _EscapeSpecialCharsWithinTagAttributes = function (text) { -// -// Within tags -- meaning between < and > -- encode [\ ` * _] so they -// don't conflict with their use in Markdown for code, italics and strong. -// - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; - - text = text.replace(regex, function (wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); - tag = escapeCharacters(tag, "\\`*_"); - return tag; - }); - - return text; - } - - var _DoAnchors = function (text) { -// -// Turn Markdown link shortcuts into XHTML
    tags. -// - // - // 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; - } - - var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { - if (m7 == undefined) m7 = ""; - var whole_match = m1; - var link_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = link_text.toLowerCase().replace(/ ?\n/g, " "); - } - url = "#" + link_id; - - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - if (whole_match.search(/\(\s*\)$/m) > -1) { - // Special case for explicit empty url - url = ""; - } else { - return whole_match; - } - } - } - - url = escapeCharacters(url, "*_"); - var result = ""; - - return result; - } - - var _DoImages = function (text) { -// -// Turn Markdown image shortcuts into tags. -// - - // - // 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 - ? // src url = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // title = $7 - \6 // matching quote - [ \t]* - )? // title is optional - \) - ) - /g,writeImageTag); - */ - text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); - - return text; - } - - var writeImageTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { - var whole_match = m1; - var alt_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (!title) title = ""; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = alt_text.toLowerCase().replace(/ ?\n/g, " "); - } - url = "#" + link_id; - - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - return whole_match; - } - } - - alt_text = alt_text.replace(/"/g, """); - url = escapeCharacters(url, "*_"); - var result = "\""' + _RunSpanGamut(m1) + ""); - }); - - text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, - function (matchFound, m1) { - return hashBlock('

    ' + _RunSpanGamut(m1) + "

    "); - }); - - // 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 h_level = m1.length; - return hashBlock("' + _RunSpanGamut(m2) + ""); - }); - - function headerId(m) { - return m.replace(/[^\w]/g, '').toLowerCase(); - } - - return text; - } - -// This declaration keeps Dojo compressor from outputting garbage: - var _ProcessListItems; - - var _DoLists = function (text) { -// -// Form HTML ordered (numbered) and unordered (bulleted) lists. -// - - // 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 whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - - if (g_list_level) { - text = text.replace(whole_list, function (wholeMatch, m1, m2) { - var list = m1; - var list_type = (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 `` - // 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 = "<" + list_type + ">" + result + "\n"; - return result; - }); - } else { - whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; - text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) { - var runup = m1; - var list = m2; - - var list_type = (m3.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: - var list = list.replace(/\n{2,}/g, "\n\n\n"); - ; - var result = _ProcessListItems(list); - result = runup + "<" + list_type + ">\n" + result + "\n"; - return result; - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - _ProcessListItems = function (list_str) { -// -// Process the contents of a single ordered or unordered list, splitting it -// into individual list items. -// - // 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.". - - g_list_level++; - - // trim trailing blank lines: - list_str = list_str.replace(/\n{2,}$/, "\n"); - - // attacklab: add sentinel to emulate \z - list_str += "~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(){...}); - */ - list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, - function (wholeMatch, m1, m2, m3, m4) { - var item = m4; - var leading_line = m1; - var leading_space = m2; - - if (leading_line || (item.search(/\n{2,}/) > -1)) { - item = _RunBlockGamut(_Outdent(item)); - } - else { - // Recursion for sub-lists: - item = _DoLists(_Outdent(item)); - item = item.replace(/\n$/, ""); // chomp(item) - item = _RunSpanGamut(item); - } - - return "
  • " + item + "
  • \n"; - } - ); - - // attacklab: strip sentinel - list_str = list_str.replace(/~0/g, ""); - - g_list_level--; - return list_str; - } - - var _DoCodeBlocks = function (text) { -// -// Process Markdown `
    ` blocks.
    -//
    -
    -        /*
    -         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;
    -                var nextChar = m2;
    -
    -                codeblock = _EncodeCode(_Outdent(codeblock));
    -                codeblock = _Detab(codeblock);
    -                codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
    -                codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
    -
    -                codeblock = "
    " + codeblock + "\n
    "; - - return hashBlock(codeblock) + nextChar; - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - }; - - var _DoGithubCodeBlocks = function (text) { -// -// Process Github-style code blocks -// Example: -// ```ruby -// def hello_world(x) -// puts "Hello, #{x}" -// end -// ``` -// - - - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += "~0"; - - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, - function (wholeMatch, m1, m2) { - var language = m1; - var codeblock = m2; - - codeblock = _EncodeCode(codeblock); - codeblock = _Detab(codeblock); - codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines - codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace - - codeblock = "
    " + codeblock + "\n
    "; - - return hashBlock(codeblock); - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - var hashBlock = function (text) { - text = text.replace(/(^\n+|\n+$)/g, ""); - return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; - } - - var _DoCodeSpans = function (text) { -// -// * Backtick quotes are used for 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: -// -//

    Just type foo `bar` baz at the prompt.

    -// -// 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 `bar` ... -// - - /* - 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, m4) { - var c = m3; - c = c.replace(/^([ \t]*)/g, ""); // leading whitespace - c = c.replace(/[ \t]*$/g, ""); // trailing whitespace - c = _EncodeCode(c); - return m1 + "" + c + ""; - }); - - return text; - } - - var _EncodeCode = function (text) { -// -// Encode/escape certain characters inside Markdown code runs. -// The point is that in code, these characters are literals, -// and lose their special Markdown meanings. -// - // 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, ">"); - - // Now, escape characters that are magic in Markdown: - text = escapeCharacters(text, "\*_{}[]\\", false); - -// jj the line above breaks this: -//--- - -//* Item - -// 1. Subitem - -// special char: * -//--- - - return text; - } - - var _DoItalicsAndBold = function (text) { - - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, - "$2"); - - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, - "$2"); - - return text; - } - - var _DoBlockQuotes = function (text) { - - /* - 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 = _RunBlockGamut(bq); // recurse - - bq = bq.replace(/(^|\n)/g, "$1 "); - // These leading spaces screw with
     content, so we need to fix that:
    -                bq = bq.replace(
    -                    /(\s*
    [^\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 hashBlock("
    \n" + bq + "\n
    "); - }); - return text; - } - - var _FormParagraphs = function (text) { -// -// Params: -// $text - string to process with html

    tags -// - - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ""); - text = text.replace(/\n+$/g, ""); - - var grafs = text.split(/\n{2,}/g); - var grafsOut = []; - - // - // Wrap

    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 = _RunSpanGamut(str); - str = str.replace(/^([ \t]*)/g, "

    "); - str += "

    " - grafsOut.push(str); - } - - } - - // - // Unhashify HTML blocks - // - end = grafsOut.length; - for (var i = 0; i < end; i++) { - // if this is a marker for an html block... - while (grafsOut[i].search(/~K(\d+)K/) >= 0) { - var blockText = g_html_blocks[RegExp.$1]; - blockText = blockText.replace(/\$/g, "$$$$"); // Escape any dollar signs - grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText); - } - } - - return grafsOut.join("\n\n"); - } - - var _EncodeAmpsAndAngles = function (text) { -// Smart processing for ampersands and angle brackets that need to be encoded. - - // 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; - } - - var _EncodeBackslashEscapes = function (text) { -// -// Parameter: String. -// 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. - - text = text.replace(/\\(\\)/g, escapeCharacters_callback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback); - return text; - } - - var _DoAutoLinks = function (text) { - - text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi, "
    $1"); - - // Email addresses: - - /* - 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) { - return _EncodeEmailAddress(_UnescapeSpecialChars(m1)); - } - ); - - return text; - } - - var _EncodeEmailAddress = function (addr) { -// -// 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.: -// -// foo -// @example.com -// -// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk -// mailing list: -// - - 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 > .9 ? encode[2](ch) : - r > .45 ? encode[1](ch) : - encode[0](ch) - ); - } - return ch; - }); - - addr = "" + addr + ""; - addr = addr.replace(/">.+:/g, "\">"); // strip the mailto: from the visible part - - return addr; - } - - var _UnescapeSpecialChars = function (text) { -// -// Swap back in all the special characters we've hidden. -// - text = text.replace(/~E(\d+)E/g, - function (wholeMatch, m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - } - ); - return text; - } - - var _Outdent = function (text) { -// -// Remove one level of line-leading tabs or spaces -// - - // 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; - } - - var _Detab = function (text) { -// attacklab: Detab's completely rewritten for speed. -// In perl we could fix it by anchoring the regexp with \G. -// In javascript we're less fortunate. - - // expand first n-1 tabs - text = text.replace(/\t(?=\t)/g, " "); // attacklab: 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, m2) { - var leadingText = m1; - var numSpaces = 4 - leadingText.length % 4; // attacklab: 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, " "); // attacklab: g_tab_width - text = text.replace(/~B/g, ""); - - return text; - } - - -// -// attacklab: Utility functions -// - - - 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, escapeCharacters_callback); - - return text; - } - - - var escapeCharacters_callback = function (wholeMatch, m1) { - var charCodeToEscape = m1.charCodeAt(0); - return "~E" + charCodeToEscape + "E"; - } - -} // end of Showdown.converter - - -// export -if (typeof module !== 'undefined') module.exports = Showdown; - -// stolen from AMD branch of underscore -// AMD define happens at the end for compatibility with AMD loaders -// that don't enforce next-turn semantics on modules. -if (typeof define === 'function' && define.amd) { - define('showdown', function () { - return Showdown; - }); -} diff --git a/src/showdown.js b/src/showdown.js new file mode 100644 index 00000000..ef17ba9b --- /dev/null +++ b/src/showdown.js @@ -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 + }; +}; diff --git a/src/subParsers/anchors.js b/src/subParsers/anchors.js new file mode 100644 index 00000000..27610bec --- /dev/null +++ b/src/subParsers/anchors.js @@ -0,0 +1,136 @@ +/** + * Created by Estevao on 11-01-2015. + */ + +/** + * Turn Markdown link shortcuts into XHTML 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 = ''; + + 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; + + +}); diff --git a/src/subParsers/autoLinks.js b/src/subParsers/autoLinks.js new file mode 100644 index 00000000..86a9e347 --- /dev/null +++ b/src/subParsers/autoLinks.js @@ -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, "$1"); + + // Email addresses: + + /* + 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; + +}); diff --git a/src/subParsers/blockGamut.js b/src/subParsers/blockGamut.js new file mode 100644 index 00000000..11f62088 --- /dev/null +++ b/src/subParsers/blockGamut.js @@ -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')('
    ', 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 + //

    tags around block-level tags. + text = showdown.subParser('hashHTMLBlocks')(text, options, globals); + text = showdown.subParser('paragraphs')(text, options, globals); + + return text; +}); + diff --git a/src/subParsers/blockQuotes.js b/src/subParsers/blockQuotes.js new file mode 100644 index 00000000..56418c24 --- /dev/null +++ b/src/subParsers/blockQuotes.js @@ -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

     content, so we need to fix that:
    +            bq = bq.replace(
    +                /(\s*
    [^\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')('
    \n' + bq + '\n
    ', options, globals); + }); + return text; +}); diff --git a/src/subParsers/codeBlocks.js b/src/subParsers/codeBlocks.js new file mode 100644 index 00000000..b11de329 --- /dev/null +++ b/src/subParsers/codeBlocks.js @@ -0,0 +1,48 @@ +/** + * Created by Estevao on 12-01-2015. + */ + +/** + * Process Markdown `
    ` 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 = '
    ' + codeblock + '\n
    '; + + return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; + } + ); + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + return text; +}); diff --git a/src/subParsers/codeSpans.js b/src/subParsers/codeSpans.js new file mode 100644 index 00000000..002eb639 --- /dev/null +++ b/src/subParsers/codeSpans.js @@ -0,0 +1,57 @@ +/** + * Created by Estevao on 11-01-2015. + */ + +/** + * + * * Backtick quotes are used for 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: + * + *

    Just type foo `bar` baz at the prompt.

    + * + * 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 `bar` ... + */ +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 + '' + c + ''; + }); + + return text; + +}); diff --git a/src/subParsers/detab.js b/src/subParsers/detab.js new file mode 100644 index 00000000..8ed13492 --- /dev/null +++ b/src/subParsers/detab.js @@ -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; + +}); diff --git a/src/subParsers/encodeAmpsAndAngles.js b/src/subParsers/encodeAmpsAndAngles.js new file mode 100644 index 00000000..f6123a50 --- /dev/null +++ b/src/subParsers/encodeAmpsAndAngles.js @@ -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; +}); diff --git a/src/subParsers/encodeBackslashEscapes.js b/src/subParsers/encodeBackslashEscapes.js new file mode 100644 index 00000000..c8acee2e --- /dev/null +++ b/src/subParsers/encodeBackslashEscapes.js @@ -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; +}); diff --git a/src/subParsers/encodeCode.js b/src/subParsers/encodeCode.js new file mode 100644 index 00000000..e3a6feaf --- /dev/null +++ b/src/subParsers/encodeCode.js @@ -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, '>'); + + // 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; + +}); diff --git a/src/subParsers/encodeEmailAddress.js b/src/subParsers/encodeEmailAddress.js new file mode 100644 index 00000000..0f231745 --- /dev/null +++ b/src/subParsers/encodeEmailAddress.js @@ -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.: + * + * foo + * @example.com + * + * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk + * mailing list: + * + */ +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 = '' + addr + ''; + addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part + + return addr; + +}); diff --git a/src/subParsers/escapeSpecialCharsWithinTagAttributes.js b/src/subParsers/escapeSpecialCharsWithinTagAttributes.js new file mode 100644 index 00000000..2f95b4a7 --- /dev/null +++ b/src/subParsers/escapeSpecialCharsWithinTagAttributes.js @@ -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\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; + + text = text.replace(regex, function (wholeMatch) { + var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); + tag = showdown.helper.escapeCharacters(tag, '\\`*_'); + return tag; + }); + + return text; +}); diff --git a/src/subParsers/githubCodeBlocks.js b/src/subParsers/githubCodeBlocks.js new file mode 100644 index 00000000..b40cc875 --- /dev/null +++ b/src/subParsers/githubCodeBlocks.js @@ -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 = '
    ' + codeblock + end + '
    '; + + return showdown.subParser('hashBlock')(codeblock, options, globals); + } + ); + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + return text; + +}); diff --git a/src/subParsers/hashBlock.js b/src/subParsers/hashBlock.js new file mode 100644 index 00000000..99cc0dc5 --- /dev/null +++ b/src/subParsers/hashBlock.js @@ -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'; +}); diff --git a/src/subParsers/hashElement.js b/src/subParsers/hashElement.js new file mode 100644 index 00000000..7747fbe9 --- /dev/null +++ b/src/subParsers/hashElement.js @@ -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; + }; +}); diff --git a/src/subParsers/hashHTMLBlocks.js b/src/subParsers/hashHTMLBlocks.js new file mode 100644 index 00000000..03a63723 --- /dev/null +++ b/src/subParsers/hashHTMLBlocks.js @@ -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

    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.: + //

    + //
    + // tags for inner block must be indented. + //
    + //
    + // + // 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 `
    ` and stop at the first `
    `. + + // 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 + // 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` to `\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 + // 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
    . 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 + + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,showdown.subParser('hashElement')(text, options, globals)); + */ + text = text.replace(/(\n\n[ ]{0,3}[ \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; + + +}); diff --git a/src/subParsers/headers.js b/src/subParsers/headers.js new file mode 100644 index 00000000..8af27392 --- /dev/null +++ b/src/subParsers/headers.js @@ -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')('

    ' + + showdown.subParser('spanGamut')(m1, options, globals) + '

    ', options, globals); + }); + + text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, + function (matchFound, m1) { + return showdown.subParser('hashBlock')('

    ' + + showdown.subParser('spanGamut')(m1, options, globals) + '

    ', 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 = '' + span + ''; + + return showdown.subParser('hashBlock')(header, options, globals); + }); + + function headerId(m) { + return m.replace(/[^\w]/g, '').toLowerCase(); + } + + return text; +}); diff --git a/src/subParsers/images.js b/src/subParsers/images.js new file mode 100644 index 00000000..99a78902 --- /dev/null +++ b/src/subParsers/images.js @@ -0,0 +1,109 @@ +/** + * Created by Estevao on 11-01-2015. + */ + + +/** + * Turn Markdown image shortcuts into 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 = '' + altText + '? // src url = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // title = $7 + \6 // matching quote + [ \t]* + )? // title is optional + \) + ) + /g,writeImageTag); + */ + text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); + + return text; +}); diff --git a/src/subParsers/italicsAndBold.js b/src/subParsers/italicsAndBold.js new file mode 100644 index 00000000..77906729 --- /dev/null +++ b/src/subParsers/italicsAndBold.js @@ -0,0 +1,15 @@ +/** + * Created by Estevao on 12-01-2015. + */ + +showdown.subParser('italicsAndBold', function (text) { + 'use strict'; + // must go first: + text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, + '$2'); + + text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, + '$2'); + + return text; +}); diff --git a/src/subParsers/lists.js b/src/subParsers/lists.js new file mode 100644 index 00000000..e216d897 --- /dev/null +++ b/src/subParsers/lists.js @@ -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 '
  • ' + item + '
  • \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 `` + // 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 + '\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 + '\n'; + }); + } + + // attacklab: strip sentinel + text = text.replace(/~0/, ''); + + return text; +}); diff --git a/src/subParsers/outdent.js b/src/subParsers/outdent.js new file mode 100644 index 00000000..a6e37ac6 --- /dev/null +++ b/src/subParsers/outdent.js @@ -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; +}); diff --git a/src/subParsers/paragraphs.js b/src/subParsers/paragraphs.js new file mode 100644 index 00000000..5af839cf --- /dev/null +++ b/src/subParsers/paragraphs.js @@ -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

    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, '

    '); + str += '

    '; + 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'); +}); diff --git a/src/subParsers/spanGamut.js b/src/subParsers/spanGamut.js new file mode 100644 index 00000000..6aae7234 --- /dev/null +++ b/src/subParsers/spanGamut.js @@ -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 `` + // Must come after _DoAnchors(), because you can use < and > + // delimiters in inline links like [this](). + 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, '
    \n'); + + return text; + +}); diff --git a/src/subParsers/stripBlankLines.js b/src/subParsers/stripBlankLines.js new file mode 100644 index 00000000..ef4969b9 --- /dev/null +++ b/src/subParsers/stripBlankLines.js @@ -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, ''); +}); diff --git a/src/subParsers/stripLinkDefinitions.js b/src/subParsers/stripLinkDefinitions.js new file mode 100644 index 00000000..4bb474fb --- /dev/null +++ b/src/subParsers/stripLinkDefinitions.js @@ -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]* + * ? // 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]*?[ \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; +}); diff --git a/src/subParsers/unescapeSpecialChars.js b/src/subParsers/unescapeSpecialChars.js new file mode 100644 index 00000000..28e17c37 --- /dev/null +++ b/src/subParsers/unescapeSpecialChars.js @@ -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; +}); diff --git a/test/cases/anchors-by-reference.html b/test/cases/anchors-by-reference.html index 92652746..6115d50e 100644 --- a/test/cases/anchors-by-reference.html +++ b/test/cases/anchors-by-reference.html @@ -1,5 +1,4 @@ -

    This is an example reference-style link. This is another reference-style link. This is a third reference-style link. -This is a fourth reference-style link.

    \ No newline at end of file +This is a fourth reference-style link.

    diff --git a/test/cases/automatic-anchors.html b/test/cases/automatic-anchors.html index 8d322155..6de71a9d 100644 --- a/test/cases/automatic-anchors.html +++ b/test/cases/automatic-anchors.html @@ -1,2 +1 @@ - -

    http://example.com/

    \ No newline at end of file +

    http://example.com/

    diff --git a/test/cases/blockquote.html b/test/cases/blockquote.html index c31f2842..85cabf47 100644 --- a/test/cases/blockquote.html +++ b/test/cases/blockquote.html @@ -2,4 +2,4 @@

    This is a multi line blockquote test

    With more than one line.

    - \ No newline at end of file + diff --git a/test/cases/code-block-html-escape.html b/test/cases/code-block-html-escape.html index 123e4f3c..028f874d 100644 --- a/test/cases/code-block-html-escape.html +++ b/test/cases/code-block-html-escape.html @@ -1,5 +1,4 @@ -

    This is some HTML:

    <h1>Heading</h1>
    -
    \ No newline at end of file +
    diff --git a/test/cases/code-block.html b/test/cases/code-block.html index bdb50b4e..3e560997 100644 --- a/test/cases/code-block.html +++ b/test/cases/code-block.html @@ -1,5 +1,4 @@ -

    This is a normal paragraph:

    This is a code block.
    -
    \ No newline at end of file +
    diff --git a/test/cases/doubline-list.html b/test/cases/doubline-list.html index da412379..a2c72162 100644 --- a/test/cases/doubline-list.html +++ b/test/cases/doubline-list.html @@ -1,5 +1,4 @@ -
    • Bird

    • Magic

    • -
    \ No newline at end of file + diff --git a/test/cases/emphasis.html b/test/cases/emphasis.html index 056fdeab..36c11352 100644 --- a/test/cases/emphasis.html +++ b/test/cases/emphasis.html @@ -1,8 +1,7 @@ -

    important

    important

    this midimportantsentence

    -

    *not important*

    \ No newline at end of file +

    *not important*

    diff --git a/test/cases/escaped-number-period.html b/test/cases/escaped-number-period.html index f773d21d..0ea83bac 100644 --- a/test/cases/escaped-number-period.html +++ b/test/cases/escaped-number-period.html @@ -1 +1 @@ -

    It happened in 1986. What a great season.

    \ No newline at end of file +

    It happened in 1986. What a great season.

    diff --git a/test/cases/escaping.html b/test/cases/escaping.html index 671a2a64..1ac6a291 100644 --- a/test/cases/escaping.html +++ b/test/cases/escaping.html @@ -1,4 +1,3 @@ -

    These should all be escaped:

    \

    @@ -29,4 +28,4 @@

    .

    -

    !

    \ No newline at end of file +

    !

    diff --git a/test/cases/github-style-at-start.html b/test/cases/github-style-at-start.html index 8f5dd947..4f119a76 100644 --- a/test/cases/github-style-at-start.html +++ b/test/cases/github-style-at-start.html @@ -1,7 +1,6 @@ -
    function MyFunc(a) {
         // ...
     }
     
    -

    That is some code!

    \ No newline at end of file +

    That is some code!

    diff --git a/test/cases/github-style-codeblock.html b/test/cases/github-style-codeblock.html index fc4187e5..5daca2f9 100644 --- a/test/cases/github-style-codeblock.html +++ b/test/cases/github-style-codeblock.html @@ -1,5 +1,3 @@ - -

    Define a function in javascript:

    function MyFunc(a) {
    @@ -10,4 +8,4 @@
     

    And some HTML

    <div>HTML!</div>
    -
    \ No newline at end of file +
    diff --git a/test/cases/github-style-linebreaks.html b/test/cases/github-style-linebreaks.html index d8210a4f..276dbf03 100644 --- a/test/cases/github-style-linebreaks.html +++ b/test/cases/github-style-linebreaks.html @@ -1,4 +1,3 @@ -
    code can go here
     this is rendered on a second line
    -
    \ No newline at end of file +
    diff --git a/test/cases/h1-with-double-hash.html b/test/cases/h1-with-double-hash.html index 2cecc38f..ab5b5555 100644 --- a/test/cases/h1-with-double-hash.html +++ b/test/cases/h1-with-double-hash.html @@ -1 +1 @@ -

    This is an H1

    \ No newline at end of file +

    This is an H1

    diff --git a/test/cases/h1-with-equals.html b/test/cases/h1-with-equals.html index 2cecc38f..ab5b5555 100644 --- a/test/cases/h1-with-equals.html +++ b/test/cases/h1-with-equals.html @@ -1 +1 @@ -

    This is an H1

    \ No newline at end of file +

    This is an H1

    diff --git a/test/cases/h1-with-single-hash.html b/test/cases/h1-with-single-hash.html index 2cecc38f..ab5b5555 100644 --- a/test/cases/h1-with-single-hash.html +++ b/test/cases/h1-with-single-hash.html @@ -1 +1 @@ -

    This is an H1

    \ No newline at end of file +

    This is an H1

    diff --git a/test/cases/h2-with-dashes.html b/test/cases/h2-with-dashes.html index d040d753..375a0d06 100644 --- a/test/cases/h2-with-dashes.html +++ b/test/cases/h2-with-dashes.html @@ -1 +1 @@ -

    This is an H2

    \ No newline at end of file +

    This is an H2

    diff --git a/test/cases/h2-with-double-hash.html b/test/cases/h2-with-double-hash.html index d040d753..375a0d06 100644 --- a/test/cases/h2-with-double-hash.html +++ b/test/cases/h2-with-double-hash.html @@ -1 +1 @@ -

    This is an H2

    \ No newline at end of file +

    This is an H2

    diff --git a/test/cases/h2-with-single-hash.html b/test/cases/h2-with-single-hash.html index d040d753..375a0d06 100644 --- a/test/cases/h2-with-single-hash.html +++ b/test/cases/h2-with-single-hash.html @@ -1 +1 @@ -

    This is an H2

    \ No newline at end of file +

    This is an H2

    diff --git a/test/cases/h3-with-double-hash.html b/test/cases/h3-with-double-hash.html index 082318e5..13f8c9e7 100644 --- a/test/cases/h3-with-double-hash.html +++ b/test/cases/h3-with-double-hash.html @@ -1 +1 @@ -

    This is an H3

    \ No newline at end of file +

    This is an H3

    diff --git a/test/cases/h3-with-single-hash.html b/test/cases/h3-with-single-hash.html index 082318e5..13f8c9e7 100644 --- a/test/cases/h3-with-single-hash.html +++ b/test/cases/h3-with-single-hash.html @@ -1 +1 @@ -

    This is an H3

    \ No newline at end of file +

    This is an H3

    diff --git a/test/cases/h4-with-single-hash.html b/test/cases/h4-with-single-hash.html index cc32aa17..165b4ef2 100644 --- a/test/cases/h4-with-single-hash.html +++ b/test/cases/h4-with-single-hash.html @@ -1 +1 @@ -

    This is an H4

    \ No newline at end of file +

    This is an H4

    diff --git a/test/cases/h5-with-single-hash.html b/test/cases/h5-with-single-hash.html index 96dca787..28eac148 100644 --- a/test/cases/h5-with-single-hash.html +++ b/test/cases/h5-with-single-hash.html @@ -1 +1 @@ -
    This is an H5
    \ No newline at end of file +
    This is an H5
    diff --git a/test/cases/h6-with-single-hash.html b/test/cases/h6-with-single-hash.html index 10de6905..47574cc5 100644 --- a/test/cases/h6-with-single-hash.html +++ b/test/cases/h6-with-single-hash.html @@ -1 +1 @@ -
    This is an H6
    \ No newline at end of file +
    This is an H6
    diff --git a/test/cases/horizontal-rules.html b/test/cases/horizontal-rules.html index 88eb5def..aaef23eb 100644 --- a/test/cases/horizontal-rules.html +++ b/test/cases/horizontal-rules.html @@ -1,4 +1,3 @@ -

    @@ -7,4 +6,4 @@
    -
    \ No newline at end of file +
    diff --git a/test/cases/html5-strutural-tags.html b/test/cases/html5-strutural-tags.html index 98698e8d..528731f5 100644 --- a/test/cases/html5-strutural-tags.html +++ b/test/cases/html5-strutural-tags.html @@ -1,4 +1,3 @@ -

    These HTML5 tags should pass through just fine.

    hello
    @@ -20,4 +19,4 @@ ignore me -

    the end

    \ No newline at end of file +

    the end

    diff --git a/test/cases/images.html b/test/cases/images.html index f214ba11..7df58552 100644 --- a/test/cases/images.html +++ b/test/cases/images.html @@ -1,6 +1,5 @@ -

    Alt text

    Alt text

    -

    Alt text

    \ No newline at end of file +

    Alt text

    diff --git a/test/cases/implicit-anchors.html b/test/cases/implicit-anchors.html index 8bb39fff..01e62d9a 100644 --- a/test/cases/implicit-anchors.html +++ b/test/cases/implicit-anchors.html @@ -1,2 +1 @@ - -

    Search the web at Google or Daring Fireball.

    \ No newline at end of file +

    Search the web at Google or Daring Fireball.

    diff --git a/test/cases/inline-anchors.html b/test/cases/inline-anchors.html index 7be05efe..52f90ed3 100644 --- a/test/cases/inline-anchors.html +++ b/test/cases/inline-anchors.html @@ -1,4 +1,3 @@ -

    This is an example inline link.

    -

    This link has no title attribute.

    \ No newline at end of file +

    This link has no title attribute.

    diff --git a/test/cases/inline-code.html b/test/cases/inline-code.html index e1809e6e..ebb7fdc0 100644 --- a/test/cases/inline-code.html +++ b/test/cases/inline-code.html @@ -1,4 +1,3 @@ -

    Create a new function.

    Use the backtick in MySQL syntax SELECT `column` FROM whatever.

    @@ -9,4 +8,4 @@

    Please don't use any <blink> tags.

    -

    &#8212; is the decimal-encoded equivalent of &mdash;.

    \ No newline at end of file +

    &#8212; is the decimal-encoded equivalent of &mdash;.

    diff --git a/test/cases/inline-escaped-chars.html b/test/cases/inline-escaped-chars.html index ff9226c7..9192f3e4 100644 --- a/test/cases/inline-escaped-chars.html +++ b/test/cases/inline-escaped-chars.html @@ -1,3 +1,2 @@ -

    Hello.this_is_a_variable -and.this.is.another_one

    \ No newline at end of file +and.this.is.another_one

    diff --git a/test/cases/inline-style-tag.html b/test/cases/inline-style-tag.html index 9a6540b5..1006aa8f 100644 --- a/test/cases/inline-style-tag.html +++ b/test/cases/inline-style-tag.html @@ -1,6 +1,5 @@ - -

    An exciting sentence.

    \ No newline at end of file +

    An exciting sentence.

    diff --git a/test/cases/lazy-blockquote.html b/test/cases/lazy-blockquote.html index c31f2842..85cabf47 100644 --- a/test/cases/lazy-blockquote.html +++ b/test/cases/lazy-blockquote.html @@ -2,4 +2,4 @@

    This is a multi line blockquote test

    With more than one line.

    - \ No newline at end of file + diff --git a/test/cases/list-with-blockquote.html b/test/cases/list-with-blockquote.html index e33c04fe..6bfd077c 100644 --- a/test/cases/list-with-blockquote.html +++ b/test/cases/list-with-blockquote.html @@ -5,4 +5,4 @@

    This is a blockquote inside a list item.

    - \ No newline at end of file + diff --git a/test/cases/list-with-code.html b/test/cases/list-with-code.html index 753a2693..dbea279b 100644 --- a/test/cases/list-with-code.html +++ b/test/cases/list-with-code.html @@ -3,4 +3,4 @@
    alert('Hello world!');
         
    - \ No newline at end of file + diff --git a/test/cases/multi-paragraph-list.html b/test/cases/multi-paragraph-list.html index 1afca575..1f08bd0e 100644 --- a/test/cases/multi-paragraph-list.html +++ b/test/cases/multi-paragraph-list.html @@ -3,4 +3,4 @@

    That contains multiple paragraphs.

  • And another line

  • - \ No newline at end of file + diff --git a/test/cases/multiline-unordered-list.html b/test/cases/multiline-unordered-list.html index bb6c06a9..ab56450e 100644 --- a/test/cases/multiline-unordered-list.html +++ b/test/cases/multiline-unordered-list.html @@ -2,4 +2,4 @@
  • This line spans more than one line and is lazy
  • Similar to this line
  • - \ No newline at end of file + diff --git a/test/cases/nested-blockquote.html b/test/cases/nested-blockquote.html index 97ca7d7d..91ae5ea9 100644 --- a/test/cases/nested-blockquote.html +++ b/test/cases/nested-blockquote.html @@ -6,4 +6,4 @@

    With more than one line.

    - \ No newline at end of file + diff --git a/test/cases/ordered-list-same-number.html b/test/cases/ordered-list-same-number.html index 726f66c6..95b3803f 100644 --- a/test/cases/ordered-list-same-number.html +++ b/test/cases/ordered-list-same-number.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/ordered-list-wrong-numbers.html b/test/cases/ordered-list-wrong-numbers.html index 726f66c6..95b3803f 100644 --- a/test/cases/ordered-list-wrong-numbers.html +++ b/test/cases/ordered-list-wrong-numbers.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/ordered-list.html b/test/cases/ordered-list.html index 726f66c6..95b3803f 100644 --- a/test/cases/ordered-list.html +++ b/test/cases/ordered-list.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/relative-anchors.html b/test/cases/relative-anchors.html index a4120831..6db73dc4 100644 --- a/test/cases/relative-anchors.html +++ b/test/cases/relative-anchors.html @@ -1,2 +1 @@ - -

    See my About page for details.

    \ No newline at end of file +

    See my About page for details.

    diff --git a/test/cases/simple-paragraph.html b/test/cases/simple-paragraph.html index 1f19a39b..7ce53543 100644 --- a/test/cases/simple-paragraph.html +++ b/test/cases/simple-paragraph.html @@ -1 +1 @@ -

    Hello, world!

    \ No newline at end of file +

    Hello, world!

    diff --git a/test/cases/strong.html b/test/cases/strong.html index 4938794c..5bcc6756 100644 --- a/test/cases/strong.html +++ b/test/cases/strong.html @@ -1,6 +1,5 @@ -

    important

    important

    -

    really freakingstrong

    \ No newline at end of file +

    really freakingstrong

    diff --git a/test/cases/unordered-list-asterisk.html b/test/cases/unordered-list-asterisk.html index ddc422c7..f02abb7c 100644 --- a/test/cases/unordered-list-asterisk.html +++ b/test/cases/unordered-list-asterisk.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/unordered-list-minus.html b/test/cases/unordered-list-minus.html index ddc422c7..f02abb7c 100644 --- a/test/cases/unordered-list-minus.html +++ b/test/cases/unordered-list-minus.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/unordered-list-plus.html b/test/cases/unordered-list-plus.html index ddc422c7..f02abb7c 100644 --- a/test/cases/unordered-list-plus.html +++ b/test/cases/unordered-list-plus.html @@ -2,4 +2,4 @@
  • Red
  • Green
  • Blue
  • - \ No newline at end of file + diff --git a/test/cases/url-with-parenthesis.html b/test/cases/url-with-parenthesis.html index d42cee8b..9e1e7cc9 100644 --- a/test/cases/url-with-parenthesis.html +++ b/test/cases/url-with-parenthesis.html @@ -1,2 +1 @@ - -

    There's an episode of Star Trek: The Next Generation

    \ No newline at end of file +

    There's an episode of Star Trek: The Next Generation

    diff --git a/test/converter/testMakeHtml.js b/test/converter/testMakeHtml.js new file mode 100644 index 00000000..9095cef9 --- /dev/null +++ b/test/converter/testMakeHtml.js @@ -0,0 +1,80 @@ +/** + * Created by Estevao on 15-01-2015. + */ + +(function () { + 'use strict'; + + require('source-map-support').install(); + require('chai').should(); + + var fs = require('fs'), + dir = 'test/cases/', + showdown = require('../../dist/showdown.js'), + converter = new showdown.Converter(); + + // Load test cases from disk + var cases = fs.readdirSync(dir).filter(function (file) { + var ext = file.slice(-3); + return (ext === '.md'); + }).map(function (file) { + + var name = file.replace('.md', ''), + htmlPath = dir + name + '.html', + html = fs.readFileSync(htmlPath, 'utf8'), + mdPath = dir + name + '.md', + md = fs.readFileSync(mdPath, 'utf8'); + + return { + name: name, + input: md, + expected: html + }; + }); + + //Normalize input/output + function normalize(testCase) { + + // Normalize line returns + testCase.expected = testCase.expected.replace(/\r/g, ''); + testCase.actual = testCase.actual.replace(/\r/g, ''); + + // Ignore all leading/trailing whitespace + testCase.expected = testCase.expected.split('\n').map(function (x) { + return x.trim(); + }).join('\n'); + testCase.actual = testCase.actual.split('\n').map(function (x) { + return x.trim(); + }).join('\n'); + + // Remove extra lines + testCase.expected = testCase.expected.trim(); + + // Convert whitespace to a visible character so that it shows up on error reports + testCase.expected = testCase.expected.replace(/ /g, '·'); + testCase.expected = testCase.expected.replace(/\n/g, '•\n'); + testCase.actual = testCase.actual.replace(/ /g, '·'); + testCase.actual = testCase.actual.replace(/\n/g, '•\n'); + + return testCase; + + } + + function assertion(testCase) { + return function () { + testCase.actual = converter.makeHtml(testCase.input); + testCase = normalize(testCase); + + // Compare + testCase.actual.should.equal(testCase.expected); + }; + } + + //Tests + describe('Converter.makeHtml()', function () { + for (var i = 0; i < cases.length; ++i) { + it(cases[i].name, assertion(cases[i])); + } + }); + +})(); diff --git a/test/extensions/github/basic.html b/test/extensions/github/basic.html deleted file mode 100644 index af2b0696..00000000 --- a/test/extensions/github/basic.html +++ /dev/null @@ -1,5 +0,0 @@ -

    github-flavored markdown adds support for:

    - -
      -
    • strike-through text
    • -
    \ No newline at end of file diff --git a/test/extensions/github/basic.md b/test/extensions/github/basic.md deleted file mode 100644 index 0ef04a30..00000000 --- a/test/extensions/github/basic.md +++ /dev/null @@ -1,3 +0,0 @@ -[github-flavored markdown](http://github.github.com/github-flavored-markdown/) adds support for: - - * ~~strike-through text~~ diff --git a/test/extensions/prettify/basic.html b/test/extensions/prettify/basic.html deleted file mode 100644 index 13c7f404..00000000 --- a/test/extensions/prettify/basic.html +++ /dev/null @@ -1,7 +0,0 @@ - -

    Here's a simple hello world in javascript:

    - -
    alert('Hello World!');
    -
    - -

    The alert function is a build-in global from window.

    \ No newline at end of file diff --git a/test/extensions/prettify/basic.md b/test/extensions/prettify/basic.md deleted file mode 100644 index fba19fa6..00000000 --- a/test/extensions/prettify/basic.md +++ /dev/null @@ -1,6 +0,0 @@ - -Here's a simple hello world in javascript: - - alert('Hello World!'); - -The `alert` function is a build-in global from `window`. \ No newline at end of file diff --git a/test/extensions/table/basic.html b/test/extensions/table/basic.html deleted file mode 100644 index d909e65b..00000000 --- a/test/extensions/table/basic.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
    First Header Second Header

    Row 1 Cell 1

    Row 1 Cell 2

    Row 2 Cell 1

    Row 2 Cell 2

    diff --git a/test/extensions/table/basic.md b/test/extensions/table/basic.md deleted file mode 100644 index d67f8fda..00000000 --- a/test/extensions/table/basic.md +++ /dev/null @@ -1,4 +0,0 @@ -| First Header | Second Header | -| ------------- | ------------- | -| Row 1 Cell 1 | Row 1 Cell 2 | -| Row 2 Cell 1 | Row 2 Cell 2 | diff --git a/test/extensions/table/large.html b/test/extensions/table/large.html deleted file mode 100644 index 332bdab6..00000000 --- a/test/extensions/table/large.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    First Header Second Header Third Header Fourth Header

    Row 1 Cell 1

    Row 1 Cell 2

    Row 1 Cell 3

    Row 1 Cell 4

    Row 2 Cell 1

    Row 2 Cell 2

    Row 2 Cell 3

    Row 2 Cell 4

    Row 3 Cell 1

    Row 3 Cell 2

    Row 3 Cell 3

    Row 3 Cell 4

    Row 4 Cell 1

    Row 4 Cell 2

    Row 4 Cell 3

    Row 4 Cell 4

    Row 5 Cell 1

    Row 5 Cell 2

    Row 5 Cell 3

    Row 5 Cell 4

    diff --git a/test/extensions/table/large.md b/test/extensions/table/large.md deleted file mode 100644 index e18e478d..00000000 --- a/test/extensions/table/large.md +++ /dev/null @@ -1,7 +0,0 @@ -| First Header | Second Header | Third Header | Fourth Header | -| ------------- | ------------- | ------------ | ------------- | -| Row 1 Cell 1 | Row 1 Cell 2 | Row 1 Cell 3 | Row 1 Cell 4 | -| Row 2 Cell 1 | Row 2 Cell 2 | Row 2 Cell 3 | Row 2 Cell 4 | -| Row 3 Cell 1 | Row 3 Cell 2 | Row 3 Cell 3 | Row 3 Cell 4 | -| Row 4 Cell 1 | Row 4 Cell 2 | Row 4 Cell 3 | Row 4 Cell 4 | -| Row 5 Cell 1 | Row 5 Cell 2 | Row 5 Cell 3 | Row 5 Cell 4 | diff --git a/test/extensions/table/multiple-tables.html b/test/extensions/table/multiple-tables.html deleted file mode 100644 index 5611db12..00000000 --- a/test/extensions/table/multiple-tables.html +++ /dev/null @@ -1,43 +0,0 @@ -

    Table Test

    - -

    section 1

    - - - - - - - - - - - - - - - - - - -
    header1 header2 header3

    Value1

    Value2

    Value3

    - -

    section 2

    - - - - - - - - - - - - - - - - - - -
    headerA headerB headerC

    ValueA

    ValueB

    ValueC

    \ No newline at end of file diff --git a/test/extensions/table/multiple-tables.md b/test/extensions/table/multiple-tables.md deleted file mode 100644 index 25bc09ec..00000000 --- a/test/extensions/table/multiple-tables.md +++ /dev/null @@ -1,17 +0,0 @@ -Table Test -============ - -section 1 ------------- - -|header1 |header2 |header3| -|-----------|-----------|---------| -|Value1 |Value2 |Value3 | - - -section 2 ------------ - -|headerA |headerB |headerC| -|-----------|-----------|---------| -|ValueA |ValueB |ValueC | diff --git a/test/extensions/table/with-equals.html b/test/extensions/table/with-equals.html deleted file mode 100644 index d909e65b..00000000 --- a/test/extensions/table/with-equals.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
    First Header Second Header

    Row 1 Cell 1

    Row 1 Cell 2

    Row 2 Cell 1

    Row 2 Cell 2

    diff --git a/test/extensions/table/with-equals.md b/test/extensions/table/with-equals.md deleted file mode 100644 index 744a6db4..00000000 --- a/test/extensions/table/with-equals.md +++ /dev/null @@ -1,4 +0,0 @@ -| First Header | Second Header | -| ============= | ============= | -| Row 1 Cell 1 | Row 1 Cell 2 | -| Row 2 Cell 1 | Row 2 Cell 2 | diff --git a/test/extensions/table/with-surroundings.html b/test/extensions/table/with-surroundings.html deleted file mode 100644 index a8957c33..00000000 --- a/test/extensions/table/with-surroundings.html +++ /dev/null @@ -1,33 +0,0 @@ -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisi est, -ullamcorper euismod iaculis sed, tristique at neque. Nullam metus risus, -malesuada vitae imperdiet ac, tincidunt eget lacus. Proin ullamcorper -vulputate dictum. Vestibulum consequat ultricies nibh, sed tempus nisl mattis a.

    - - - - - - - - - - - - - - - - - - - - - -
    First Header Second Header

    Row 1 Cell 1

    Row 1 Cell 2

    Row 2 Cell 1

    Row 2 Cell 2

    - -

    Phasellus ac porttitor quam. Integer cursus accumsan mauris nec interdum. -Etiam iaculis urna vitae risus facilisis faucibus eu quis risus. Sed aliquet -rutrum dictum. Vivamus pulvinar malesuada ultricies. Pellentesque in commodo -nibh. Maecenas justo erat, sodales vel bibendum a, dignissim in orci. Duis -blandit ornare mi non facilisis. Aliquam rutrum fringilla lacus in semper. -Sed vel pretium lorem.

    diff --git a/test/extensions/table/with-surroundings.md b/test/extensions/table/with-surroundings.md deleted file mode 100644 index b55baa32..00000000 --- a/test/extensions/table/with-surroundings.md +++ /dev/null @@ -1,16 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisi est, -ullamcorper euismod iaculis sed, tristique at neque. Nullam metus risus, -malesuada vitae imperdiet ac, tincidunt eget lacus. Proin ullamcorper -vulputate dictum. Vestibulum consequat ultricies nibh, sed tempus nisl mattis a. - -| First Header | Second Header | -| ------------- | ------------- | -| Row 1 Cell 1 | Row 1 Cell 2 | -| Row 2 Cell 1 | Row 2 Cell 2 | - -Phasellus ac porttitor quam. Integer cursus accumsan mauris nec interdum. -Etiam iaculis urna vitae risus facilisis faucibus eu quis risus. Sed aliquet -rutrum dictum. Vivamus pulvinar malesuada ultricies. Pellentesque in commodo -nibh. Maecenas justo erat, sodales vel bibendum a, dignissim in orci. Duis -blandit ornare mi non facilisis. Aliquam rutrum fringilla lacus in semper. -Sed vel pretium lorem. diff --git a/test/extensions/table/without-body.html b/test/extensions/table/without-body.html deleted file mode 100644 index 5b37e4a5..00000000 --- a/test/extensions/table/without-body.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -
    First Header Second Header
    diff --git a/test/extensions/table/without-body.md b/test/extensions/table/without-body.md deleted file mode 100644 index 78c06f01..00000000 --- a/test/extensions/table/without-body.md +++ /dev/null @@ -1,2 +0,0 @@ -| First Header | Second Header | -| ------------- | ------------- | diff --git a/test/extensions/table/without-header-delimiter.html b/test/extensions/table/without-header-delimiter.html deleted file mode 100644 index 5d1af0fe..00000000 --- a/test/extensions/table/without-header-delimiter.html +++ /dev/null @@ -1 +0,0 @@ -

    | First Header | Second Header |

    diff --git a/test/extensions/table/without-header-delimiter.md b/test/extensions/table/without-header-delimiter.md deleted file mode 100644 index a86f2da8..00000000 --- a/test/extensions/table/without-header-delimiter.md +++ /dev/null @@ -1 +0,0 @@ -| First Header | Second Header | diff --git a/test/extensions/twitter/basic.html b/test/extensions/twitter/basic.html deleted file mode 100644 index 4e9b316a..00000000 --- a/test/extensions/twitter/basic.html +++ /dev/null @@ -1,5 +0,0 @@ -

    Testing of the twitter extension.

    - -

    Ping @andstuff to find out more about #extensions with showdown

    - -

    And @something shouldn't render as a twitter link

    \ No newline at end of file diff --git a/test/extensions/twitter/basic.md b/test/extensions/twitter/basic.md deleted file mode 100644 index 6269c9d1..00000000 --- a/test/extensions/twitter/basic.md +++ /dev/null @@ -1,5 +0,0 @@ -Testing of the twitter extension. - -Ping @andstuff to find out more about #extensions with showdown - -And \@something shouldn't render as a twitter link \ No newline at end of file diff --git a/test/run.js b/test/run.js deleted file mode 100644 index 5e7a677b..00000000 --- a/test/run.js +++ /dev/null @@ -1,86 +0,0 @@ -var showdown = new require('../src/showdown'), - fs = require('fs'), - path = require('path'), - should = require('should'); - - -var runTestsInDir = function(dir, converter) { - - // Load test cases from disk - var cases = fs.readdirSync(dir).filter(function(file){ - return ~file.indexOf('.md'); - }).map(function(file){ - return file.replace('.md', ''); - }); - - // Run each test case (markdown -> html) - showdown.forEach(cases, function(test){ - var name = test.replace(/[-.]/g, ' '); - it (name, function(){ - var mdpath = path.join(dir, test + '.md'), - htmlpath = path.join(dir, test + '.html'), - md = fs.readFileSync(mdpath, 'utf8'), - expected = fs.readFileSync(htmlpath, 'utf8').trim(), - actual = converter.makeHtml(md).trim(); - - // Normalize line returns - expected = expected.replace(/\r/g, ''); - - // Ignore all leading/trailing whitespace - expected = expected.split('\n').map(function(x){ - return x.trim(); - }).join('\n'); - actual = actual.split('\n').map(function(x){ - return x.trim(); - }).join('\n'); - - // Convert whitespace to a visible character so that it shows up on error reports - expected = expected.replace(/ /g, '·'); - expected = expected.replace(/\n/g, '•\n'); - actual = actual.replace(/ /g, '·'); - actual = actual.replace(/\n/g, '•\n'); - - // Compare - actual.should.equal(expected); - }); - }); -}; - - -// -// :: Markdown to HTML testing :: -// - -describe('Markdown', function() { - var converter = new showdown.converter(); - runTestsInDir('test/cases', converter); -}); - - -// -// :: Extensions Testing :: -// - -if (path.existsSync('test/extensions')) { - - describe('extensions', function() { - // Search all sub-folders looking for directory-specific tests - var extensions = fs.readdirSync('test/extensions').filter(function(file){ - return fs.lstatSync('test/extensions/' + file).isDirectory(); - }); - - // Run tests in each extension sub-folder - showdown.forEach(extensions, function(ext){ - // Make sure extension exists - var src = 'src/extensions/' + ext + '.js'; - if (!path.existsSync(src)) { - throw "Attempting tests for '" + ext + "' but source file (" + src + ") was not found."; - } - - var converter = new showdown.converter({ extensions: [ ext ] }); - var dir = 'test/extensions/' + ext; - runTestsInDir(dir, converter); - }); - }); - -} diff --git a/test/testTable.js b/test/testTable.js deleted file mode 100644 index 712f0973..00000000 --- a/test/testTable.js +++ /dev/null @@ -1,24 +0,0 @@ -var Showdown = require('../src/showdown'); -var fs = require('fs'); - -module.exports = { - setUp:function(callback) { - this.showdown = new Showdown.converter({extensions:['table']}); - callback(); - }, - testMakeHtml:function(test) { - var html = this.showdown.makeHtml("**blah**"); - console.log(html); - test.equals(html ,'

    blah

    '); - test.done(); - }, - testMakeTable:function(test) { - var md = fs.readFileSync('test/extensions/table/multiple-tables.md','UTF-8'); - var html = fs.readFileSync('test/extensions/table/multiple-tables.html','UTF-8'); - - var result = this.showdown.makeHtml(md); - console.log(result); - test.equals(result, html); - test.done(); - } -}; \ No newline at end of file