Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seeking Maintainers for Showdown #114

Closed
coreyti opened this issue Oct 5, 2014 · 22 comments
Closed

Seeking Maintainers for Showdown #114

coreyti opened this issue Oct 5, 2014 · 22 comments

Comments

@coreyti
Copy link

coreyti commented Oct 5, 2014

With apologies, I've long been unable to stay up with maintaining Showdown on Github. As such, I've moved the repository to the "showdownjs" organization. I'm seeking maintainers to take over ownership of that organization and the Showdown project.

Please respond here if interested.

@pdeschen
Copy link
Contributor

pdeschen commented Oct 5, 2014

@coreyti now that repo as been moved to organization, I'm in!

@coreyti
Copy link
Author

coreyti commented Oct 5, 2014

@pdeschen great! Sorry for the long delay... I've added you to the organization's owners.

@sirakoff
Copy link

@coreyti I am also interested.

@coreyti
Copy link
Author

coreyti commented Oct 11, 2014

Hi @sirakoff, thanks for raising your hand. I'm glad to hear of your interest. I feel somewhat inclined to see if any of the other previous contributors, such as @tstone and @remy, would want to form the initial core. @pdeschen, thoughts?

@tivie
Copy link
Member

tivie commented Oct 13, 2014

@coreyti, I'm also interested. Been using Showdown for a while, fixing some
bugs and adding support for some frameworks, such as AngularJS, in my local
repo.


Estevão Soares dos Santos


2014-10-11 18:49 GMT+01:00 Corey Innis [email protected]:

Hi @sirakoff https://github.com/sirakoff, thanks for raising your hand.
I'm glad to hear of your interest. I feel somewhat inclined to see if any
of the other previous contributors, such as @tstone
https://github.com/tstone and @remy https://github.com/remy, would
want to form the core. @pdeschen https://github.com/pdeschen, thoughts?


Reply to this email directly or view it on GitHub
#114 (comment).

@coreyti
Copy link
Author

coreyti commented Nov 4, 2014

@tivie just added you.

@tivie
Copy link
Member

tivie commented Nov 4, 2014

@coreyti Thanks. I have a couple of bug fixes and features (such as AngularJS integration) that should be ready to push. This repo only has a master branch though. What is the model you guys adopt? The standard Feature/Hotfix -> Develop -> Master -> Release? Or Fork(and branch) -> Push request?

@florenthobein
Copy link

@tivie I'm interested in your AngularJS integration, when do you think you'll be able to publish it on a dev branch?

@tivie
Copy link
Member

tivie commented Nov 17, 2014

@florenthobein It's already available in the feature branch Angular_Integration. I haven't merged it in the development branch yet because I'm waiting for some feedback from @coreyti or @pdeschen.

Regardless, you can download the source file ng-angular.js and include it in your page after angular and showdown.

Some feedback and testing is appreciated =)

@al-the-x
Copy link

@tivie Is there a PR open for your work to date? Love to help you refactor a little...

@tracend
Copy link

tracend commented Dec 5, 2014

👍 would love to help

@tivie
Copy link
Member

tivie commented Jan 15, 2015

@al-the-x I should be able to release a very alpha version of the re-factored version of showdown in the next couple of days. I would really appreciate your help and @tracend too, if @coreyti agrees.

@tivie
Copy link
Member

tivie commented Jan 16, 2015

I've just pushed a very very alpha version of the showdown refactoring. Love if you could have a look.

https://github.com/showdownjs/showdown/tree/showdown2

@pdeschen
Copy link
Contributor

@tivie would you mind opening a PR? I'll give it a shot at reviewing

@pdeschen
Copy link
Contributor

@tivie @ALL maybe it's time we open a google group for general discussion (both dev and user)?

@SyntaxRules
Copy link
Member

I'd like to help out as well- specifically on the AngularJs integration side.

@SyntaxRules
Copy link
Member

I'd also be happy to help migrating from grunt to gulp(v4.0 when its released) task automation.

@tivie
Copy link
Member

tivie commented Mar 2, 2015

@SyntaxRules I'm fine with you coming aboard, if @pdeschen and @coreyti agree.

Regarding migrating to gulp, my question is: why?! The best thing would be dropping grunt (or gulp, or brocolli, or whatever) altogether and switch to pure NPM based tasks. =P

@SyntaxRules
Copy link
Member

@tivie Thanks. An advantage of gulp over grunt is speed. Its hard for me to say how much of a speed up you would get by migrating (if any) because I don't know all the specifics of how this project uses grunt.

I can see the argument for just using npm modules and I totaly agree this works out well for simple projects. So maybe that applies here.

LimeBlast added a commit to LimeBlast/EliteScheduleApp that referenced this issue May 2, 2015
diff --git a/www/app/directives/markdown.js b/www/app/directives/markdown.js
new file mode 100755
index 0000000..7f184f4
--- /dev/null
+++ b/www/app/directives/markdown.js
@@ -0,0 +1,25 @@
+(function () {
+  'use strict';
+
+  angular.module('eliteApp').directive('markdown', [markdown]);
+
+  function markdown() {
+    // Usage:
+    // <div data-markdown="{{vm.content}}"></div>
+
+    var converter = new Showdown.converter();
+
+    var directive = {
+      link: link,
+      restrict: 'A'
+    };
+    return directive;
+
+    function link(scope, element, attrs) {
+      attrs.$observe('markdown', function (value) {
+        var markup = converter.makeHtml(value);
+        element.html(markup);
+      });
+    }
+  }
+})();
diff --git a/www/app/rules/rules-ctrl.js b/www/app/rules/rules-ctrl.js
new file mode 100755
index 0000000..79fd81b
--- /dev/null
+++ b/www/app/rules/rules-ctrl.js
@@ -0,0 +1,16 @@
+(function () {
+  'use strict';
+
+  angular.module('eliteApp').controller('RulesCtrl', ['eliteApi', RulesCtrl]);
+
+  function RulesCtrl(eliteApi) {
+    var vm = this;
+
+    eliteApi.getLeagueData().then(function (data) {
+      console.log("***rulesctrl", data);
+      vm.mainContent = data.league.rulesScreen;
+      console.log("***rulesctrl", data, vm.mainContent);
+    });
+
+  }
+})();
diff --git a/www/app/rules/rules.html b/www/app/rules/rules.html
old mode 100644
new mode 100755
index 328eaec..cdfb16a
--- a/www/app/rules/rules.html
+++ b/www/app/rules/rules.html
@@ -1,5 +1,5 @@
-<ion-view>
-  <ion-content class="has-header">
-    <h1>Rules</h1>
-  </ion-content>
-</ion-view>
+<ion-view title="Rules" ng-controller="RulesCtrl as vm">
+  <ion-content class="has-header padding">
+    <div class="allow-bullets" data-markdown="{{vm.mainContent}}"></div>
+  </ion-content>
+</ion-view>
\ No newline at end of file
diff --git a/www/index.html b/www/index.html
index 47cbd88..e65a30e 100644
--- a/www/index.html
+++ b/www/index.html
@@ -20,6 +20,8 @@
     <script src="lib/lodash/dist/lodash.min.js"></script>
     <script src="lib/angular-cache/dist/angular-cache.min.js"></script>

+    <script src="lib/showdown/compressed/showdown.js"></script>
+
     <script src='https://maps.googleapis.com/maps/api/js?sensor=false'></script>
     <script src="lib/angular-google-maps/dist/angular-google-maps.min.js"></script>

@@ -28,6 +30,7 @@

     <script src="app/services/eliteApi.js"></script>
     <script src="app/services/myTeamsService.js"></script>
+    <script src="app/directives/markdown.js"></script>

     <script src="app/teams/team-detail-ctrl.js"></script>
     <script src="app/home/leagues-ctrl.js"></script>
@@ -37,6 +40,7 @@
     <script src="app/game/game-ctrl.js"></script>
     <script src="app/home/my-teams-ctrl.js"></script>
     <script src="app/locations/location-map-crtl.js"></script>
+    <script src="app/rules/rules-ctrl.js"></script>
   </head>

   <body ng-app="eliteApp">
diff --git a/www/lib/showdown/.bower.json b/www/lib/showdown/.bower.json
new file mode 100644
index 0000000..4d6efac
--- /dev/null
+++ b/www/lib/showdown/.bower.json
@@ -0,0 +1,39 @@
+{
+  "name": "showdown",
+  "description": "JavaScript port of Markdown",
+  "homepage": "https://github.com/showdownjs/showdown",
+  "authors": [
+    "John Fraser",
+    "Corey Innis (https://github.com/coreyti)",
+    "Pascal Deschênes (https://github.com/pdeschen)",
+    "Estevão Santos (https://github.com/tivie)"
+  ],
+  "main": [
+    "src/showdown.js"
+  ],
+  "ignore": [
+    ".jshintrc",
+    "perlMarkdown/*"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/showdownjs/showdown.git"
+  },
+  "keywords": [
+    "markdown",
+    "md",
+    "mdown"
+  ],
+  "license": "https://github.com/showdownjs/showdown/blob/master/license.txt",
+  "version": "0.5.0",
+  "_release": "0.5.0",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.0",
+    "commit": "1840f9bbb72c5500c3b36eeec1fc2f045f8f432b"
+  },
+  "_source": "git://github.com/showdownjs/showdown.git",
+  "_target": "~0.5.0",
+  "_originalSource": "showdown",
+  "_direct": true
+}
\ No newline at end of file
diff --git a/www/lib/showdown/.jshintignore b/www/lib/showdown/.jshintignore
new file mode 100644
index 0000000..62aa808
--- /dev/null
+++ b/www/lib/showdown/.jshintignore
@@ -0,0 +1,2 @@
+Gruntfile.js
+dist/**/*.js
\ No newline at end of file
diff --git a/www/lib/showdown/.travis.yml b/www/lib/showdown/.travis.yml
new file mode 100644
index 0000000..9e341d1
--- /dev/null
+++ b/www/lib/showdown/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+  - "0.8"
+  - "0.10"
+
+before_install:
+  - '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g [email protected]'
+  - npm install -g npm@latest
diff --git a/www/lib/showdown/Gruntfile.js b/www/lib/showdown/Gruntfile.js
new file mode 100644
index 0000000..47d9262
--- /dev/null
+++ b/www/lib/showdown/Gruntfile.js
@@ -0,0 +1,100 @@
+/**
+ * Created by Tivie on 12-11-2014.
+ */
+
+module.exports = function (grunt) {
+
+    // Project configuration.
+    grunt.initConfig({
+        pkg: grunt.file.readJSON('package.json'),
+        concat: {
+            options: {
+                separator: ';',
+                sourceMap: true
+            },
+            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'
+            }
+        },
+        uglify: {
+            options: {
+                banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
+            },
+            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 %>']
+                }
+            }
+        },
+        jshint: {
+            files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js']
+        },
+        simplemocha: {
+            all: {
+                src: 'test/run.js',
+                options: {
+                    globals: ['should'],
+                    timeout: 3000,
+                    ignoreLeaks: false,
+                    ui: 'bdd'
+                }
+            }
+        }
+    });
+
+    grunt.loadNpmTasks('grunt-contrib-concat');
+    grunt.loadNpmTasks('grunt-contrib-uglify');
+    grunt.loadNpmTasks('grunt-contrib-jshint');
+    grunt.loadNpmTasks('grunt-simple-mocha');
+
+    // test
+    grunt.registerTask('lint', ['jshint']);
+    grunt.registerTask('test', ['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/www/lib/showdown/README.md b/www/lib/showdown/README.md
new file mode 100644
index 0000000..bbb13db
--- /dev/null
+++ b/www/lib/showdown/README.md
@@ -0,0 +1,317 @@
+# Showdown
+
+A JavaScript port of Markdown
+
+## Note
+
+  > Showdown is now maintained by the [showdownjs](https://github.com/showdownjs) organization on Github.
+  >
+  > The organization needs members to maintain Showdown.
+  >
+  > Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.
+
+## Original Attributions
+
+Showdown Copyright (c) 2007 John Fraser.
+<http://www.attacklab.net/>
+
+Original Markdown Copyright (c) 2004-2005 John Gruber
+<http://daringfireball.net/projects/markdown/>
+
+Redistributable under a BSD-style open source license.
+See license.txt for more information.
+
+## Quick Example
+
+```js
+var Showdown = require('showdown');
+var converter = new Showdown.converter();
+
+converter.makeHtml('#hello markdown!');
+
+// <h1 id="hellomarkdown">hello markdown!</h1>
+```
+
+## What's it for?
+
+Developers can use Showdown to:
+
+  * Add in-browser preview to existing Markdown apps
+
+    Showdown's output is (almost always) identical to
+    markdown.pl's, so the server can reproduce exactly
+    the output that the user saw.  (See below for
+    exceptions.)
+
+  * Add Markdown input to programs that don't support it
+
+    Any app that accepts HTML input can now be made to speak
+    Markdown by modifying the input pages's HTML.  If your
+    application lets users edit documents again later,
+    then they won't have access to the original Markdown
+    text.  But this should be good enough for many
+    uses -- and you can do it with just a two-line
+    `onsubmit` function!
+
+  * Add Markdown input to closed-source web apps
+
+    You can write bookmarklets or userscripts to extend
+    any standard textarea on the web so that it accepts
+    Markdown instead of HTML.  With a little more hacking,
+    the same can probably be done with  many rich edit
+    controls.
+
+  * Build new web apps from scratch
+
+    A Showdown front-end can send back text in Markdown,
+    HTML or both, so you can trade bandwidth for server
+    load to reduce your cost of operation.  If your app
+    requires JavaScript, you won't need to do any
+    Markdown processing on the server at all.  (For most
+    uses, you'll still need to sanitize the HTML before
+    showing it to other users -- but you'd need to do
+    that anyway if you're allowing raw HTML in your
+    Markdown.)
+
+
+## Browser Compatibility
+
+Showdown has been tested successfully with:
+
+  * Firefox 1.5 and 2.0
+  * Internet Explorer 6 and 7
+  * Safari 2.0.4
+  * Opera 8.54 and 9.10
+  * Netscape 8.1.2
+  * Konqueror 3.5.4
+
+In theory, Showdown will work in any browser that supports ECMA 262 3rd Edition (JavaScript 1.5).  The converter itself might even work in things that aren't web browsers, like Acrobat.  No promises.
+
+
+## Extensions
+
+Showdown allows additional functionality to be loaded via extensions.
+
+### Client-side Extension Usage
+
+```js
+<script src="src/showdown.js" />
+<script src="src/extensions/twitter.js" />
+
+var converter = new Showdown.converter({ extensions: ['twitter'] });
+```
+
+### Server-side Extension Usage
+
+```js
+// Using a bundled extension
+var Showdown = require('showdown');
+var converter = new Showdown.converter({ extensions: ['twitter'] });
+
+// Using a custom extension
+var mine = require('./custom-extensions/mine');
+var converter = new Showdown.converter({ extensions: ['twitter', mine] });
+```
+
+
+## Known Differences in Output
+
+In most cases, Showdown's output is identical to that of Perl Markdown v1.0.2b7.  What follows is a list of all known deviations.  Please file an issue if you find more.
+
+  * This release uses the HTML parser from Markdown 1.0.2b2,
+    which means it fails `Inline HTML (Advanced).text` from
+    the Markdown test suite:
+
+        <div>
+        <div>
+        unindented == broken
+        </div>
+        </div>
+
+  * Showdown doesn't support the markdown="1" attribute:
+
+        <div markdown="1">
+             Markdown does *not* work in here.
+        </div>
+
+    This is half laziness on my part and half stubbornness.
+    Markdown is smart enough to process the contents of span-
+    level tags without screwing things up; shouldn't it be
+    able to do the same inside block elements?  Let's find a
+    way to make markdown="1" the default.
+
+
+  * You can only nest square brackets in link titles to a
+    depth of two levels:
+
+        [[fine]](http://www.attacklab.net/)
+        [[[broken]]](http://www.attacklab.net/)
+
+    If you need more, you can escape them with backslashes.
+
+
+  * When sublists have paragraphs, Showdown produces equivalent
+    HTML with a slightly different arrangement of newlines:
+
+        + item
+
+             - subitem
+
+               The HTML has a superfluous newline before this
+               paragraph.
+
+             - subitem
+
+               The HTML here is unchanged.
+
+             - subitem
+
+               The HTML is missing a newline after this
+               list subitem.
+
+
+
+  * Markdown.pl creates empty title attributes for
+    inline-style images:
+
+        Here's an empty title on an inline-style
+        ![image](http://w3.org/Icons/valid-xhtml10).
+
+    I tried to replicate this to clean up my diffs during
+    testing, but I went too far: now Showdown also makes
+    empty titles for reference-style images:
+
+        Showdown  makes an empty title for
+        reference-style ![images][] too.
+
+        [images]: http://w3.org/Icons/valid-xhtml10
+
+
+  * With crazy input, Markdown will mistakenly put
+    `<strong>` or `<em>` tags in URLs:
+
+        <a href="<*Markdown adds em tags in here*>">
+           improbable URL
+        </a>
+
+    Showdown won't.  But still, don't do that.
+
+
+## Tests
+
+A suite of tests is available which require node.js.  Once node is installed, run the following command from the project root to install the development dependencies:
+
+    npm install --dev
+
+Once installed the tests can be run from the project root using:
+
+    npm test
+
+New test cases can easily be added.  Create a markdown file (ending in `.md`) which contains the markdown to test.  Create a `.html` file of the exact same name.  It will automatically be tested when the tests are executed with `mocha`.
+
+
+## Creating Markdown Extensions
+
+A showdown extension is simply a function which returns an array of language extensions and/or output modifiers:
+
+  * Language Extension -- Language extensions are specified with the `lang` type, and add new markdown syntax to showdown.  For example, say you wanted `^^youtube http://www.youtube.com/watch?v=oHg5SJYRHA0` to automatically render as an embedded YouTube video, that would be a language extension.
+  * Output Modifiers -- Output Modifiers are specified with the `output` type. After showdown has generated HTML, an output modifier can make changes to the generated HTML.  For example, if you wanted to change `<div class="header">` to be `<header>`, you could implement an output modifier.
+
+Each showdown extension can provide language extensions and/or output modifiers.
+
+### Regex/Replace
+
+Regex/replace style extensions are very similar to javascripts `string.replace` function.  Two properties are given, `regex` and `replace`.  `regex` is a string and `replace` can be either a string or a function.  If `replace` is a string, it can use the `$1` syntax for group substitution, exactly as if it were making use of `string.replace` (internally it does this actually);  The value of `regex` is assumed to be a global replacement.
+
+**Example:**
+
+```js
+var demo = function(converter) {
+  return [
+    // Replace escaped @ symbols
+    { type: 'lang', regex: '\\@', replace: '@' }
+  ];
+}
+```
+
+### Filter
+
+Alternately, if you'd just like to do everything yourself, you can specify a filter which is a callback with a single input parameter, text (the current source text within the showdown engine).
+
+**Example:**
+
+```js
+var demo = function(converter) {
+  return [
+    // Replace escaped @ symbols
+    { type: 'lang', filter: function(text) {
+      return text.replace(/\\@/g, '@');
+    }}
+  ];
+}
+```
+
+### Implementation Concerns
+
+One bit which should be taken into account is maintaining both client-side and server-side compatibility.  This can be achieved with a few lines of boilerplate code.  First, to prevent polluting the global scope for client-side code, the extension definition should be wrapped in a self-executing function.
+
+```js
+(function(){
+  // Your extension here
+}());
+```
+
+Second, client-side extensions should add a property onto `Showdown.extensions` which matches the name of the file.  As an example, a file named `demo.js` should then add `Showdown.extensions.demo`.  Server-side extensions can simply export themselves.
+
+```js
+(function(){
+  var demo = function(converter) {
+    // ... extension code here ...
+  };
+
+  // Client-side export
+  if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.demo = demo; }
+  // Server-side export
+  if (typeof module !== 'undefined') module.exports = demo;
+}());
+```
+
+### Testing Extensions
+
+The showdown test runner is setup to automatically test cases for extensions.  To add test cases for an extension, create a new folder under `./test/extensions` which matches the name of the `.js` file in `./src/extensions`.  Place any test cases into the filder using the md/html format and they will automatically be run when tests are run.
+
+
+## Credits
+
+  * Origins
+    * [John Fraser](http://attacklab.net/):<br/>
+      Author of Showdown
+    * [John Gruber](http://daringfireball.net/projects/markdown/):<br/>
+      Author of Markdown
+  * Maintenance/Contributions (roughly chronologically)
+    * [Corey Innis](http://github.com/coreyti):<br/>
+      GitHub project maintainer
+    * [Remy Sharp](https://github.com/remy/):<br/>
+      CommonJS-compatibility and more
+    * [Konstantin Käfer](https://github.com/kkaefer/):<br/>
+      CommonJS packaging
+    * [Roger Braun](https://github.com/rogerbraun):<br/>
+      Github-style code blocks
+    * [Dominic Tarr](https://github.com/dominictarr):<br/>
+      Documentation
+    * [Cat Chen](https://github.com/CatChen):<br/>
+      Export fix
+    * [Titus Stone](https://github.com/tstone):<br/>
+      Mocha tests, extension mechanism, and bug fixes
+    * [Rob Sutherland](https://github.com/roberocity):<br/>
+      The idea that lead to extensions
+    * [Pavel Lang](https://github.com/langpavel):<br/>
+      Code cleanup
+    * [Ben Combee](https://github.com/unwiredben):<br/>
+      Regex optimization
+    * [Adam Backstrom](https://github.com/abackstrom):<br/>
+      WebKit bugfix
+    * [Pascal Deschênes](https://github.com/pdeschen):<br/>
+      Grunt support, extension fixes + additions, packaging improvements, documentation
+    * [Estevão Santos](http://github.com/coreyti):<br/>
+      GitHub project maintainer
diff --git a/www/lib/showdown/bower.json b/www/lib/showdown/bower.json
new file mode 100644
index 0000000..f3fa718
--- /dev/null
+++ b/www/lib/showdown/bower.json
@@ -0,0 +1,26 @@
+{
+    "name": "showdown",
+    "description": "JavaScript port of Markdown",
+    "homepage": "https://github.com/showdownjs/showdown",
+    "authors": [
+        "John Fraser",
+        "Corey Innis (https://github.com/coreyti)",
+        "Pascal Deschênes (https://github.com/pdeschen)",
+        "Estevão Santos (https://github.com/tivie)"
+    ],
+    "main": ["src/showdown.js"],
+    "ignore": [
+        ".jshintrc",
+        "perlMarkdown/*"
+    ],
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/showdownjs/showdown.git"
+    },
+    "keywords": [
+        "markdown",
+        "md",
+        "mdown"
+    ],
+    "license": "https://github.com/showdownjs/showdown/blob/master/license.txt"
+}
diff --git a/www/lib/showdown/compressed/extensions/github.min.js b/www/lib/showdown/compressed/extensions/github.min.js
new file mode 100644
index 0000000..442e06e
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/github.min.js
@@ -0,0 +1,2 @@
+/*! showdown 22-04-2015 */
+!function(){var a=function(){return[{type:"lang",regex:"(~T){2}([^~]+)(~T){2}",replace:function(a,b,c){return"<del>"+c+"</del>"}}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.github=a),"undefined"!=typeof module&&(module.exports=a)}();
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/github.min.js.map b/www/lib/showdown/compressed/extensions/github.min.js.map
new file mode 100644
index 0000000..81eac82
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/github.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["..\\..\\src\\extensions\\github.js"],"names":[],"mappings":"AAAA;AAAA,IACI,MAAM,CAAC,SAAS,EAAE,GAAG;AAAA,MACnB,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG;AAAA;AAAA;AAAA,CAGpD,QAAQ;AAAA,IACL,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC3B,MAAM;AAAA;AAAA,iBAEG,MAAM,CAAC,OAAO;AAAA,iBACd,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,KAAK,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW;AAAA,cAClF,IAAI,OAAO,IAAI;AAAA,cACf,KAAK,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;AAAA,cAC/B,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;AAAA,kBAC7C,MAAM,GAAG,GAAG,KAAK,OAAO,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAM5C,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,MAAM;AAAA,OAC7H,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM;AAAA","file":"github.min.js","sourcesContent":["//\r\n//  Github Extension (WIP)\r\n//  ~~strike-through~~   ->  <del>strike-through</del>\r\n//\r\n\r\n(function(){\r\n    var github = function(converter) {\r\n        return [\r\n            {\r\n              // strike-through\r\n              // NOTE: showdown already replaced \"~\" with \"~T\", so we need to adjust accordingly.\r\n              type    : 'lang',\r\n              regex   : '(~T){2}([^~]+)(~T){2}',\r\n              replace : function(match, prefix, content, suffix) {\r\n                  return '<del>' + content + '</del>';\r\n              }\r\n            }\r\n        ];\r\n    };\r\n\r\n    // Client-side export\r\n    if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.github = github; }\r\n    // Server-side export\r\n    if (typeof module !== 'undefined') module.exports = github;\r\n}());\r\n"]}
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/prettify.min.js b/www/lib/showdown/compressed/extensions/prettify.min.js
new file mode 100644
index 0000000..be71b4a
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/prettify.min.js
@@ -0,0 +1,2 @@
+/*! showdown 22-04-2015 */
+!function(){var a=function(){return[{type:"output",filter:function(a){return a.replace(/(<pre>)?<code>/gi,function(a,b){return b?'<pre class="prettyprint linenums" tabIndex="0"><code data-inner="1">':'<code class="prettyprint">'})}}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.prettify=a),"undefined"!=typeof module&&(module.exports=a)}();
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/prettify.min.js.map b/www/lib/showdown/compressed/extensions/prettify.min.js.map
new file mode 100644
index 0000000..b73852a
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/prettify.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["..\\..\\src\\extensions\\prettify.js"],"names":[],"mappings":"AAAA;AAAA,IACI,MAAM,CAAC,QAAQ;AAAA,IACf,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;AAAA,IAC1F,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;AAAA;AAAA;AAAA,CAGlC,QAAQ;AAAA;AAAA,IAEL,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC7B,MAAM;AAAA,cACA,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM;AAAA;AAAA,gBAErC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,GAAG,IAAI,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG;AAAA,oBACzD,EAAE,EAAE,GAAG;AAAA,wBACH,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AAAA,sBAC5E,IAAI;AAAA,wBACF,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAOjD,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG,QAAQ;AAAA,OACjI,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,QAAQ;AAAA;AAAA","file":"prettify.min.js","sourcesContent":["//\r\n//  Google Prettify\r\n//  A showdown extension to add Google Prettify (http://code.google.com/p/google-code-prettify/)\r\n//  hints to showdown's HTML output.\r\n//\r\n\r\n(function(){\r\n\r\n    var prettify = function(converter) {\r\n        return [\r\n            { type: 'output', filter: function(source){\r\n\r\n                return source.replace(/(<pre>)?<code>/gi, function(match, pre) {\r\n                    if (pre) {\r\n                        return '<pre class=\"prettyprint linenums\" tabIndex=\"0\"><code data-inner=\"1\">';\r\n                    } else {\r\n                        return '<code class=\"prettyprint\">';\r\n                    }\r\n                });\r\n            }}\r\n        ];\r\n    };\r\n\r\n    // Client-side export\r\n    if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.prettify = prettify; }\r\n    // Server-side export\r\n    if (typeof module !== 'undefined') module.exports = prettify;\r\n\r\n}());\r\n"]}
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/table.min.js b/www/lib/showdown/compressed/extensions/table.min.js
new file mode 100644
index 0000000..986eb03
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/table.min.js
@@ -0,0 +1,2 @@
+/*! showdown 22-04-2015 */
+!function(){var a=function(a){var b,c={},d="text-align:left;";return c.th=function(a){if(""===a.trim())return"";var b=a.trim().replace(/ /g,"_").toLowerCase();return'<th id="'+b+'" style="'+d+'">'+a+"</th>"},c.td=function(b){return'<td style="'+d+'">'+a.makeHtml(b)+"</td>"},c.ths=function(){var a="",b=0,d=[].slice.apply(arguments);for(b;b<d.length;b+=1)a+=c.th(d[b])+"\n";return a},c.tds=function(){var a="",b=0,d=[].slice.apply(arguments);for(b;b<d.length;b+=1)a+=c.td(d[b])+"\n";return a},c.thead=function(){var a,b=[].slice.apply(arguments);return a="<thead>\n",a+="<tr>\n",a+=c.ths.apply(this,b),a+="</tr>\n",a+="</thead>\n"},c.tr=function(){var a,b=[].slice.apply(arguments);return a="<tr>\n",a+=c.tds.apply(this,b),a+="</tr>\n"},b=function(a){var b,d,e=0,f=a.split("\n"),g=[];for(e;e<f.length;e+=1){if(b=f[e],b.trim().match(/^[|]{1}.*[|]{1}$/)){b=b.trim();var h=[];if(h.push("<table>"),d=b.substring(1,b.length-1).split("|"),h.push(c.thead.apply(this,d)),b=f[++e],b.trim().match(/^[|]{1}[-=|: ]+[|]{1}$/)){for(b=f[++e],h.push("<tbody>");b.trim().match(/^[|]{1}.*[|]{1}$/);)b=b.trim(),h.push(c.tr.apply(this,b.substring(1,b.length-1).split("|"))),b=f[++e];h.push("</tbody>"),h.push("</table>"),g.push(h.join("\n"));continue}b=f[--e]}g.push(b)}return g.join("\n")},[{type:"lang",filter:b}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.table=a),"undefined"!=typeof module&&(module.exports=a)}();
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/table.min.js.map b/www/lib/showdown/compressed/extensions/table.min.js.map
new file mode 100644
index 0000000..ba076a1
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/table.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["..\\..\\src\\extensions\\table.js"],"names":[],"mappings":"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","file":"table.min.js","sourcesContent":["/*global module:true*/\r\n/*\r\n * Basic table support with re-entrant parsing, where cell content\r\n * can also specify markdown.\r\n *\r\n * Tables\r\n * ======\r\n *\r\n * | Col 1   | Col 2                                              |\r\n * |======== |====================================================|\r\n * |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |\r\n * | Plain   | Value                                              |\r\n *\r\n */\r\n\r\n(function(){\r\n  var table = function(converter) {\r\n    var tables = {}, style = 'text-align:left;', filter;\r\n    tables.th = function(header){\r\n      if (header.trim() === \"\") { return \"\";}\r\n      var id = header.trim().replace(/ /g, '_').toLowerCase();\r\n      return '<th id=\"' + id + '\" style=\"'+style+'\">' + header + '</th>';\r\n    };\r\n    tables.td = function(cell) {\r\n      return '<td style=\"'+style+'\">' + converter.makeHtml(cell) + '</td>';\r\n    };\r\n    tables.ths = function(){\r\n      var out = \"\", i = 0, hs = [].slice.apply(arguments);\r\n      for (i;i<hs.length;i+=1) {\r\n        out += tables.th(hs[i]) + '\\n';\r\n      }\r\n      return out;\r\n    };\r\n    tables.tds = function(){\r\n      var out = \"\", i = 0, ds = [].slice.apply(arguments);\r\n      for (i;i<ds.length;i+=1) {\r\n        out += tables.td(ds[i]) + '\\n';\r\n      }\r\n      return out;\r\n    };\r\n    tables.thead = function() {\r\n      var out, i = 0, hs = [].slice.apply(arguments);\r\n      out = \"<thead>\\n\";\r\n      out += \"<tr>\\n\";\r\n      out += tables.ths.apply(this, hs);\r\n      out += \"</tr>\\n\";\r\n      out += \"</thead>\\n\";\r\n      return out;\r\n    };\r\n    tables.tr = function() {\r\n      var out, i = 0, cs = [].slice.apply(arguments);\r\n      out = \"<tr>\\n\";\r\n      out += tables.tds.apply(this, cs);\r\n      out += \"</tr>\\n\";\r\n      return out;\r\n    };\r\n    filter = function(text) {\r\n      var i=0, lines = text.split('\\n'), line, hs, rows, out = [];\r\n      for (i; i<lines.length;i+=1) {\r\n        line = lines[i];\r\n        // looks like a table heading\r\n        if (line.trim().match(/^[|]{1}.*[|]{1}$/)) {\r\n          line = line.trim();\r\n          var tbl = [];\r\n          tbl.push('<table>');\r\n          hs = line.substring(1, line.length -1).split('|');\r\n          tbl.push(tables.thead.apply(this, hs));\r\n          line = lines[++i];\r\n          if (!line.trim().match(/^[|]{1}[-=|: ]+[|]{1}$/)) {\r\n            // not a table rolling back\r\n            line = lines[--i];\r\n          }\r\n          else {\r\n            line = lines[++i];\r\n            tbl.push('<tbody>');\r\n            while (line.trim().match(/^[|]{1}.*[|]{1}$/)) {\r\n              line = line.trim();\r\n              tbl.push(tables.tr.apply(this, line.substring(1, line.length -1).split('|')));\r\n              line = lines[++i];\r\n            }\r\n            tbl.push('</tbody>');\r\n            tbl.push('</table>');\r\n            // we are done with this table and we move along\r\n            out.push(tbl.join('\\n'));\r\n            continue;\r\n          }\r\n        }\r\n        out.push(line);\r\n      }\r\n      return out.join('\\n');\r\n    };\r\n    return [\r\n    {\r\n      type: 'lang',\r\n      filter: filter\r\n    }\r\n    ];\r\n  };\r\n\r\n  // Client-side export\r\n  if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.table = table; }\r\n  // Server-side export\r\n  if (typeof module !== 'undefined') {\r\n    module.exports = table;\r\n  }\r\n}());\r\n"]}
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/twitter.min.js b/www/lib/showdown/compressed/extensions/twitter.min.js
new file mode 100644
index 0000000..56dc72c
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/twitter.min.js
@@ -0,0 +1,2 @@
+/*! showdown 22-04-2015 */
+!function(){var a=function(){return[{type:"lang",regex:"\\B(\\\\)?@([\\S]+)\\b",replace:function(a,b,c){return"\\"===b?a:'<a href="http://twitter.com/'+c+'">@'+c+"</a>"}},{type:"lang",regex:"\\B(\\\\)?#([\\S]+)\\b",replace:function(a,b,c){return"\\"===b?a:'<a href="http://twitter.com/search/%23'+c+'">#'+c+"</a>"}},{type:"lang",regex:"\\\\@",replace:"@"}]};"undefined"!=typeof window&&window.Showdown&&window.Showdown.extensions&&(window.Showdown.extensions.twitter=a),"undefined"!=typeof module&&(module.exports=a)}();
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/extensions/twitter.min.js.map b/www/lib/showdown/compressed/extensions/twitter.min.js.map
new file mode 100644
index 0000000..f6a1f69
--- /dev/null
+++ b/www/lib/showdown/compressed/extensions/twitter.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["..\\..\\src\\extensions\\twitter.js"],"names":[],"mappings":"AAAA;AAAA,IACI,OAAO,CAAC,SAAS;AAAA,KAChB,QAAQ,QAAQ,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC;AAAA,KACjE,OAAO,SAAS,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,EAAE,CAAC;AAAA;AAAA;AAAA,CAG7E,QAAQ;AAAA;AAAA,IAEL,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,SAAS;AAAA,QAC5B,MAAM;AAAA;AAAA,gBAEE,QAAQ,CAAC,MAAM;AAAA,cACjB,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ;AAAA,mBACzF,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;AAAA,gBACrE,EAAE,EAAE,YAAY;AAAA,oBACZ,MAAM,CAAC,KAAK;AAAA,kBACd,IAAI;AAAA,oBACF,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,KAAK,QAAQ,WAAW,QAAQ,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,gBAI9E,OAAO,CAAC,MAAM;AAAA,cAChB,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG;AAAA,mBACpF,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;AAAA,gBACrE,EAAE,EAAE,YAAY;AAAA,oBACZ,MAAM,CAAC,KAAK;AAAA,kBACd,IAAI;AAAA,oBACF,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,GAAG,WAAW,GAAG,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,eAI/E,OAAO,GAAG,CAAC;AAAA,cACZ,IAAI,GAAG,IAAI,GAAG,KAAK,WAAW,OAAO;AAAA;AAAA;AAAA;AAAA,OAI5C,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,OAAO;AAAA,OAC/H,MAAM,CAAC,IAAI,CAAC,MAAM;AAAA,IACrB,EAAE,EAAE,MAAM,CAAC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,OAAO;AAAA;AAAA","file":"twitter.min.js","sourcesContent":["//\r\n//  Twitter Extension\r\n//  @username   ->  <a href=\"http://twitter.com/username\">@username</a>\r\n//  #hashtag    ->  <a href=\"http://twitter.com/search/%23hashtag\">#hashtag</a>\r\n//\r\n\r\n(function(){\r\n\r\n    var twitter = function(converter) {\r\n        return [\r\n\r\n            // @username syntax\r\n            { type: 'lang', regex: '\\\\B(\\\\\\\\)?@([\\\\S]+)\\\\b', replace: function(match, leadingSlash, username) {\r\n                // Check if we matched the leading \\ and return nothing changed if so\r\n                if (leadingSlash === '\\\\') {\r\n                    return match;\r\n                } else {\r\n                    return '<a href=\"http://twitter.com/' + username + '\">@' + username + '</a>';\r\n                }\r\n            }},\r\n\r\n            // #hashtag syntax\r\n            { type: 'lang', regex: '\\\\B(\\\\\\\\)?#([\\\\S]+)\\\\b', replace: function(match, leadingSlash, tag) {\r\n                // Check if we matched the leading \\ and return nothing changed if so\r\n                if (leadingSlash === '\\\\') {\r\n                    return match;\r\n                } else {\r\n                    return '<a href=\"http://twitter.com/search/%23' + tag + '\">#' + tag + '</a>';\r\n                }\r\n            }},\r\n\r\n            // Escaped @'s\r\n            { type: 'lang', regex: '\\\\\\\\@', replace: '@' }\r\n        ];\r\n    };\r\n\r\n    // Client-side export\r\n    if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.twitter = twitter; }\r\n    // Server-side export\r\n    if (typeof module !== 'undefined') module.exports = twitter;\r\n\r\n}());\r\n"]}
\ No newline at end of file
diff --git a/www/lib/showdown/compressed/showdown.js b/www/lib/showdown/compressed/showdown.js
new file mode 100644
index 0000000..3527ace
--- /dev/null
+++ b/www/lib/showdown/compressed/showdown.js
@@ -0,0 +1,1606 @@
+//
+// showdown.js -- A javascript port of Markdown.
+//
+// Copyright (c) 2007 John Fraser.
+//
+// Original Markdown Copyright (c) 2004-2005 John Gruber
+//   <http://daringfireball.net/projects/markdown/>
+//
+// Redistributable under a BSD-style open source license.
+// See license.txt for more information.
+//
+// The full source distribution is at:
+//
+//				A A L
+//				T C A
+//				T K B
+//
+//   <http://www.attacklab.net/>
+//
+
+//
+// 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 <a>
+// and <img> 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]*
+         <?(\S+?)>?			// url = $2
+         [ \t]*
+         \n?				// maybe one newline
+         [ \t]*
+         (?:
+         (\n*)				// any lines skipped = $3 attacklab: lookbehind removed
+         ["(]
+         (.+?)				// title = $4
+         [")]
+         [ \t]*
+         )?					// title is optional
+         (?:\n+|$)
+         /gm,
+         function(){...});
+         */
+
+        // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+        text += "~0";
+
+        text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,
+            function (wholeMatch, m1, m2, m3, m4) {
+                m1 = m1.toLowerCase();
+                g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive
+                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, "&quot;");
+                }
+
+                // 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 <p>s around
+        // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+        // phrase emphasis, and spans. The list of tags we're looking for is
+        // hard-coded:
+        var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside";
+        var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside";
+
+        // First, look for nested blocks, e.g.:
+        //   <div>
+        //     <div>
+        //     tags for inner block must be indented.
+        //     </div>
+        //   </div>
+        //
+        // The outermost tags must start at the left margin for this to match, and
+        // the inner nested divs must be indented.
+        // We need to do this before the next, more liberal match, because the next
+        // match will start at the first `<div>` and stop at the first `</div>`.
+
+        // attacklab: This regex can be expensive when it fails.
+        /*
+         var text = text.replace(/
+         (						// save in $1
+         ^					// start of line  (with /m)
+         <($block_tags_a)	// start tag = $2
+         \b					// word break
+         // attacklab: hack around khtml/pcre bug...
+         [^\r]*?\n			// any number of lines, minimally matching
+         </\2>				// the matching end tag
+         [ \t]*				// trailing spaces/tabs
+         (?=\n+)				// followed by a newline
+         )						// attacklab: there are sentinel newlines at end of document
+         /gm,function(){...}};
+         */
+        text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement);
+
+        //
+        // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
+        //
+
+        /*
+         var text = text.replace(/
+         (						// save in $1
+         ^					// start of line  (with /m)
+         <($block_tags_b)	// start tag = $2
+         \b					// word break
+         // attacklab: hack around khtml/pcre bug...
+         [^\r]*?				// any number of lines, minimally matching
+         </\2>				// the matching end tag
+         [ \t]*				// trailing spaces/tabs
+         (?=\n+)				// followed by a newline
+         )						// attacklab: there are sentinel newlines at end of document
+         /gm,function(){...}};
+         */
+        text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement);
+
+        // Special case just for <hr />. It was easier to make a special case than
+        // to make the other regex more complicated.
+
+        /*
+         text = text.replace(/
+         (						// save in $1
+         \n\n				// Starting after a blank line
+         [ ]{0,3}
+         (<(hr)				// start tag = $2
+         \b					// word break
+         ([^<>])*?			//
+         \/?>)				// the matching end tag
+         [ \t]*
+         (?=\n{2,})			// followed by a blank line
+         )
+         /g,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
+         <!
+         (--[^\r]*?--\s*)+
+         >
+         [ \t]*
+         (?=\n{2,})			// followed by a blank line
+         )
+         /g,hashElement);
+         */
+        text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \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("<hr />");
+        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
+        // <p> 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 `<http://example.com/>`
+        // Must come after _DoAnchors(), because you can use < and >
+        // delimiters in inline links like [this](<url>).
+        text = _DoAutoLinks(text);
+        text = _EncodeAmpsAndAngles(text);
+        text = _DoItalicsAndBold(text);
+
+        // Do hard breaks:
+        text = text.replace(/  +\n/g, " <br />\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\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/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 <a> 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 = "<a href=\"" + url + "\"";
+
+        if (title != "") {
+            title = title.replace(/"/g, "&quot;");
+            title = escapeCharacters(title, "*_");
+            result += " title=\"" + title + "\"";
+        }
+
+        result += ">" + link_text + "</a>";
+
+        return result;
+    }
+
+    var _DoImages = function (text) {
+//
+// Turn Markdown image shortcuts into <img> 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
+         <?(\S+?)>?			// src url = $4
+         [ \t]*
+         (					// $5
+         (['"])			// quote char = $6
+         (.*?)			// title = $7
+         \6				// matching quote
+         [ \t]*
+         )?					// title is optional
+         \)
+         )
+         /g,writeImageTag);
+         */
+        text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
+
+        return text;
+    }
+
+    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, "&quot;");
+        url = escapeChar…
@insidewhy
Copy link

switch to pure NPM based tasks

Not enough power, no incremental rebuilds etc. etc.

@tivie tivie closed this as completed Jul 20, 2015
@easycoder
Copy link

After I used Showdown on my own project I've written a Dev.to article about extending it:
https://dev.to/gtanyware/how-to-customize-markdown-4geo

@FridayCandour
Copy link

Am also interested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests