praxis 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +15 -2
  4. data/CHANGELOG.md +54 -1
  5. data/bin/praxis +49 -2
  6. data/lib/api_browser/Gruntfile.js +247 -90
  7. data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
  8. data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
  9. data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
  10. data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
  11. data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
  12. data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
  13. data/lib/api_browser/app/bower_components/angular/README.md +23 -4
  14. data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
  15. data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
  16. data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
  17. data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
  18. data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
  19. data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
  20. data/lib/api_browser/app/bower_components/angular/package.json +25 -0
  21. data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
  22. data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
  23. data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
  24. data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
  25. data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
  26. data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
  27. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
  28. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
  29. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
  30. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
  31. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
  32. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
  33. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
  34. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
  35. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
  36. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
  37. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
  38. data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
  39. data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
  40. data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
  41. data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
  42. data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
  43. data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
  44. data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
  45. data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
  46. data/lib/api_browser/app/index.html +6 -4
  47. data/lib/api_browser/app/js/app.js +1 -2
  48. data/lib/api_browser/app/js/controllers/action.js +4 -4
  49. data/lib/api_browser/app/js/controllers/controller.js +1 -1
  50. data/lib/api_browser/app/js/controllers/menu.js +5 -3
  51. data/lib/api_browser/app/js/controllers/type.js +5 -5
  52. data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
  53. data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
  54. data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
  55. data/lib/api_browser/app/js/directives/no_container.js +1 -1
  56. data/lib/api_browser/app/js/directives/request_body.js +5 -5
  57. data/lib/api_browser/app/js/directives/request_headers.js +3 -6
  58. data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
  59. data/lib/api_browser/app/js/directives/type_label.js +4 -5
  60. data/lib/api_browser/app/js/factories/Documentation.js +4 -4
  61. data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
  62. data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
  63. data/lib/api_browser/app/js/filters/markdown.js +6 -0
  64. data/lib/api_browser/app/js/filters/resource_name.js +2 -2
  65. data/lib/api_browser/app/sass/modules/_header.scss +2 -7
  66. data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
  67. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
  68. data/lib/api_browser/app/views/action.html +2 -2
  69. data/lib/api_browser/app/views/controller.html +2 -2
  70. data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
  71. data/lib/api_browser/app/views/layout.html +2 -11
  72. data/lib/api_browser/app/views/navbar.html +9 -0
  73. data/lib/api_browser/app/views/resource/_actions.html +1 -1
  74. data/lib/api_browser/app/views/type.html +2 -2
  75. data/lib/api_browser/app/views/type/_details.html +2 -1
  76. data/lib/api_browser/bower.json +5 -0
  77. data/lib/api_browser/package.json +18 -7
  78. data/lib/praxis.rb +8 -3
  79. data/lib/praxis/action_definition.rb +28 -6
  80. data/lib/praxis/api_definition.rb +30 -2
  81. data/lib/praxis/api_general_info.rb +36 -0
  82. data/lib/praxis/bootloader.rb +1 -0
  83. data/lib/praxis/collection.rb +34 -0
  84. data/lib/praxis/controller.rb +7 -0
  85. data/lib/praxis/dispatcher.rb +3 -0
  86. data/lib/praxis/links.rb +2 -8
  87. data/lib/praxis/media_type.rb +6 -24
  88. data/lib/praxis/media_type_collection.rb +6 -2
  89. data/lib/praxis/plugin_concern.rb +2 -1
  90. data/lib/praxis/request.rb +24 -15
  91. data/lib/praxis/request_stages/request_stage.rb +19 -4
  92. data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
  93. data/lib/praxis/request_stages/validate_payload.rb +1 -1
  94. data/lib/praxis/resource_definition.rb +45 -10
  95. data/lib/praxis/response_definition.rb +46 -27
  96. data/lib/praxis/restful_doc_generator.rb +94 -7
  97. data/lib/praxis/simple_media_type.rb +2 -9
  98. data/lib/praxis/stage.rb +1 -4
  99. data/lib/praxis/tasks/api_docs.rb +51 -19
  100. data/lib/praxis/tasks/routes.rb +19 -15
  101. data/lib/praxis/types/media_type_common.rb +31 -0
  102. data/lib/praxis/types/multipart.rb +4 -4
  103. data/lib/praxis/version.rb +1 -1
  104. data/praxis.gemspec +2 -2
  105. data/spec/api_browser/factories/documentation_spec.js +50 -0
  106. data/spec/api_browser/filters/attribute_name_spec.js +23 -0
  107. data/spec/functional_spec.rb +62 -10
  108. data/spec/praxis/action_definition_spec.rb +12 -4
  109. data/spec/praxis/api_definition_spec.rb +159 -0
  110. data/spec/praxis/api_general_info_spec.rb +36 -0
  111. data/spec/praxis/bootloader_spec.rb +10 -1
  112. data/spec/praxis/media_type_collection_spec.rb +46 -53
  113. data/spec/praxis/media_type_spec.rb +6 -6
  114. data/spec/praxis/request_stage_spec.rb +7 -2
  115. data/spec/praxis/request_stages_validate_spec.rb +12 -7
  116. data/spec/praxis/resource_definition_spec.rb +62 -0
  117. data/spec/praxis/response_definition_spec.rb +26 -16
  118. data/spec/praxis/stage_spec.rb +4 -8
  119. data/spec/praxis/types/collection_spec.rb +144 -0
  120. data/spec/spec_app/app/controllers/instances.rb +8 -2
  121. data/spec/spec_app/design/api.rb +11 -0
  122. data/spec/spec_app/design/media_types/instance.rb +12 -0
  123. data/spec/spec_app/design/media_types/volume.rb +9 -2
  124. data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
  125. data/spec/spec_app/design/resources/instances.rb +25 -10
  126. data/spec/support/spec_media_types.rb +1 -1
  127. data/spec/support/spec_resource_definitions.rb +2 -0
  128. data/tasks/thor/app.rb +15 -10
  129. data/tasks/thor/example.rb +115 -115
  130. data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
  131. data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
  132. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
  133. metadata +50 -9
  134. data/lib/api_browser/app/css/main.css +0 -4511
  135. data/lib/praxis/types/collection.rb +0 -17
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "showdown",
3
+ "description": "JavaScript port of Markdown",
4
+ "homepage": "https://github.com/showdownjs/showdown",
5
+ "authors": [
6
+ "John Fraser",
7
+ "Corey Innis (https://github.com/coreyti)",
8
+ "Pascal Deschênes (https://github.com/pdeschen)",
9
+ "Estevão Santos (https://github.com/tivie)"
10
+ ],
11
+ "main": [
12
+ "src/showdown.js"
13
+ ],
14
+ "ignore": [
15
+ ".jshintrc",
16
+ "perlMarkdown/*"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/showdownjs/showdown.git"
21
+ },
22
+ "keywords": [
23
+ "markdown",
24
+ "md",
25
+ "mdown"
26
+ ],
27
+ "license": "https://github.com/showdownjs/showdown/blob/master/license.txt",
28
+ "version": "0.4.0",
29
+ "_release": "0.4.0",
30
+ "_resolution": {
31
+ "type": "version",
32
+ "tag": "0.4.0",
33
+ "commit": "e7a4a4084257d788b7c039c2252d9e00eb749e41"
34
+ },
35
+ "_source": "git://github.com/showdownjs/showdown.git",
36
+ "_target": "~0.4.0",
37
+ "_originalSource": "showdown",
38
+ "_direct": true
39
+ }
@@ -0,0 +1,2 @@
1
+ Gruntfile.js
2
+ dist/**/*.js
@@ -0,0 +1,8 @@
1
+ language: node_js
2
+ node_js:
3
+ - "0.8"
4
+ - "0.10"
5
+
6
+ before_install:
7
+ - '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm@1.4.28'
8
+ - npm install -g npm@latest
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Created by Tivie on 12-11-2014.
3
+ */
4
+
5
+ module.exports = function (grunt) {
6
+
7
+ // Project configuration.
8
+ grunt.initConfig({
9
+ pkg: grunt.file.readJSON('package.json'),
10
+ concat: {
11
+ options: {
12
+ separator: ';',
13
+ sourceMap: true
14
+ },
15
+ dist: {
16
+ src: ['src/showdown.js', 'src/*.js'],
17
+ dest: 'compressed/<%= pkg.name %>.js'
18
+ },
19
+ github_ext: {
20
+ src: ['src/extensions/github.js'],
21
+ dest: 'compressed/extensions/github.min.js'
22
+ },
23
+ prettify_ext: {
24
+ src: ['src/extensions/prettify.js'],
25
+ dest: 'compressed/extensions/prettify.min.js'
26
+ },
27
+ table_ext: {
28
+ src: ['src/extensions/table.js'],
29
+ dest: 'compressed/extensions/table.min.js'
30
+ },
31
+ twitter_ext: {
32
+ src: ['src/extensions/twitter.js'],
33
+ dest: 'compressed/extensions/twitter.min.js'
34
+ }
35
+ },
36
+ uglify: {
37
+ options: {
38
+ banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
39
+ },
40
+ dist: {
41
+ files: {
42
+ 'compressed/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
43
+ }
44
+ },
45
+ github_ext: {
46
+ files: {
47
+ 'compressed/extensions/github.min.js': ['<%= concat.github_ext.dest %>']
48
+ }
49
+ },
50
+ prettify_ext: {
51
+ files: {
52
+ 'compressed/extensions/prettify.min.js': ['<%= concat.prettify_ext.dest %>']
53
+ }
54
+ },
55
+ table_ext: {
56
+ files: {
57
+ 'compressed/extensions/table.min.js': ['<%= concat.table_ext.dest %>']
58
+ }
59
+ },
60
+ twitter_ext: {
61
+ files: {
62
+ 'compressed/extensions/twitter.min.js': ['<%= concat.twitter_ext.dest %>']
63
+ }
64
+ }
65
+ },
66
+ jshint: {
67
+ files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js']
68
+ },
69
+ simplemocha: {
70
+ all: {
71
+ src: 'test/run.js',
72
+ options: {
73
+ globals: ['should'],
74
+ timeout: 3000,
75
+ ignoreLeaks: false,
76
+ ui: 'bdd'
77
+ }
78
+ }
79
+ }
80
+ });
81
+
82
+ grunt.loadNpmTasks('grunt-contrib-concat');
83
+ grunt.loadNpmTasks('grunt-contrib-uglify');
84
+ grunt.loadNpmTasks('grunt-contrib-jshint');
85
+ grunt.loadNpmTasks('grunt-simple-mocha');
86
+
87
+ // test
88
+ grunt.registerTask('lint', ['jshint']);
89
+ grunt.registerTask('test', ['simplemocha']);
90
+
91
+ // build with uglify
92
+ grunt.registerTask('build', ['concat', 'uglify']);
93
+
94
+ // Build with closure compiler
95
+ grunt.registerTask('build-with-closure', ['test', 'concat', 'closure-compiler']);
96
+
97
+ // Default task(s).
98
+ grunt.registerTask('default', []);
99
+
100
+ };
@@ -0,0 +1,317 @@
1
+ # Showdown
2
+
3
+ A JavaScript port of Markdown
4
+
5
+ ## Note
6
+
7
+ > Showdown is now maintained by the [showdownjs](https://github.com/showdownjs) organization on Github.
8
+ >
9
+ > The organization needs members to maintain Showdown.
10
+ >
11
+ > Please see [this issue](https://github.com/showdownjs/showdown/issues/114) to express interest or comment on this note.
12
+
13
+ ## Original Attributions
14
+
15
+ Showdown Copyright (c) 2007 John Fraser.
16
+ <http://www.attacklab.net/>
17
+
18
+ Original Markdown Copyright (c) 2004-2005 John Gruber
19
+ <http://daringfireball.net/projects/markdown/>
20
+
21
+ Redistributable under a BSD-style open source license.
22
+ See license.txt for more information.
23
+
24
+ ## Quick Example
25
+
26
+ ```js
27
+ var Showdown = require('showdown');
28
+ var converter = new Showdown.converter();
29
+
30
+ converter.makeHtml('#hello markdown!');
31
+
32
+ // <h1 id="hellomarkdown">hello, markdown</h1>
33
+ ```
34
+
35
+ ## What's it for?
36
+
37
+ Developers can use Showdown to:
38
+
39
+ * Add in-browser preview to existing Markdown apps
40
+
41
+ Showdown's output is (almost always) identical to
42
+ markdown.pl's, so the server can reproduce exactly
43
+ the output that the user saw. (See below for
44
+ exceptions.)
45
+
46
+ * Add Markdown input to programs that don't support it
47
+
48
+ Any app that accepts HTML input can now be made to speak
49
+ Markdown by modifying the input pages's HTML. If your
50
+ application lets users edit documents again later,
51
+ then they won't have access to the original Markdown
52
+ text. But this should be good enough for many
53
+ uses -- and you can do it with just a two-line
54
+ `onsubmit` function!
55
+
56
+ * Add Markdown input to closed-source web apps
57
+
58
+ You can write bookmarklets or userscripts to extend
59
+ any standard textarea on the web so that it accepts
60
+ Markdown instead of HTML. With a little more hacking,
61
+ the same can probably be done with many rich edit
62
+ controls.
63
+
64
+ * Build new web apps from scratch
65
+
66
+ A Showdown front-end can send back text in Markdown,
67
+ HTML or both, so you can trade bandwidth for server
68
+ load to reduce your cost of operation. If your app
69
+ requires JavaScript, you won't need to do any
70
+ Markdown processing on the server at all. (For most
71
+ uses, you'll still need to sanitize the HTML before
72
+ showing it to other users -- but you'd need to do
73
+ that anyway if you're allowing raw HTML in your
74
+ Markdown.)
75
+
76
+
77
+ ## Browser Compatibility
78
+
79
+ Showdown has been tested successfully with:
80
+
81
+ * Firefox 1.5 and 2.0
82
+ * Internet Explorer 6 and 7
83
+ * Safari 2.0.4
84
+ * Opera 8.54 and 9.10
85
+ * Netscape 8.1.2
86
+ * Konqueror 3.5.4
87
+
88
+ 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.
89
+
90
+
91
+ ## Extensions
92
+
93
+ Showdown allows additional functionality to be loaded via extensions.
94
+
95
+ ### Client-side Extension Usage
96
+
97
+ ```js
98
+ <script src="src/showdown.js" />
99
+ <script src="src/extensions/twitter.js" />
100
+
101
+ var converter = new Showdown.converter({ extensions: ['twitter'] });
102
+ ```
103
+
104
+ ### Server-side Extension Usage
105
+
106
+ ```js
107
+ // Using a bundled extension
108
+ var Showdown = require('showdown');
109
+ var converter = new Showdown.converter({ extensions: ['twitter'] });
110
+
111
+ // Using a custom extension
112
+ var mine = require('./custom-extensions/mine');
113
+ var converter = new Showdown.converter({ extensions: ['twitter', mine] });
114
+ ```
115
+
116
+
117
+ ## Known Differences in Output
118
+
119
+ 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.
120
+
121
+ * This release uses the HTML parser from Markdown 1.0.2b2,
122
+ which means it fails `Inline HTML (Advanced).text` from
123
+ the Markdown test suite:
124
+
125
+ <div>
126
+ <div>
127
+ unindented == broken
128
+ </div>
129
+ </div>
130
+
131
+ * Showdown doesn't support the markdown="1" attribute:
132
+
133
+ <div markdown="1">
134
+ Markdown does *not* work in here.
135
+ </div>
136
+
137
+ This is half laziness on my part and half stubbornness.
138
+ Markdown is smart enough to process the contents of span-
139
+ level tags without screwing things up; shouldn't it be
140
+ able to do the same inside block elements? Let's find a
141
+ way to make markdown="1" the default.
142
+
143
+
144
+ * You can only nest square brackets in link titles to a
145
+ depth of two levels:
146
+
147
+ [[fine]](http://www.attacklab.net/)
148
+ [[[broken]]](http://www.attacklab.net/)
149
+
150
+ If you need more, you can escape them with backslashes.
151
+
152
+
153
+ * When sublists have paragraphs, Showdown produces equivalent
154
+ HTML with a slightly different arrangement of newlines:
155
+
156
+ + item
157
+
158
+ - subitem
159
+
160
+ The HTML has a superfluous newline before this
161
+ paragraph.
162
+
163
+ - subitem
164
+
165
+ The HTML here is unchanged.
166
+
167
+ - subitem
168
+
169
+ The HTML is missing a newline after this
170
+ list subitem.
171
+
172
+
173
+
174
+ * Markdown.pl creates empty title attributes for
175
+ inline-style images:
176
+
177
+ Here's an empty title on an inline-style
178
+ ![image](http://w3.org/Icons/valid-xhtml10).
179
+
180
+ I tried to replicate this to clean up my diffs during
181
+ testing, but I went too far: now Showdown also makes
182
+ empty titles for reference-style images:
183
+
184
+ Showdown makes an empty title for
185
+ reference-style ![images][] too.
186
+
187
+ [images]: http://w3.org/Icons/valid-xhtml10
188
+
189
+
190
+ * With crazy input, Markdown will mistakenly put
191
+ `<strong>` or `<em>` tags in URLs:
192
+
193
+ <a href="<*Markdown adds em tags in here*>">
194
+ improbable URL
195
+ </a>
196
+
197
+ Showdown won't. But still, don't do that.
198
+
199
+
200
+ ## Tests
201
+
202
+ 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:
203
+
204
+ npm install --dev
205
+
206
+ Once installed the tests can be run from the project root using:
207
+
208
+ npm test
209
+
210
+ 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`.
211
+
212
+
213
+ ## Creating Markdown Extensions
214
+
215
+ A showdown extension is simply a function which returns an array of language extensions and/or output modifiers:
216
+
217
+ * 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.
218
+ * 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.
219
+
220
+ Each showdown extension can provide language extensions and/or output modifiers.
221
+
222
+ ### Regex/Replace
223
+
224
+ 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.
225
+
226
+ **Example:**
227
+
228
+ ```js
229
+ var demo = function(converter) {
230
+ return [
231
+ // Replace escaped @ symbols
232
+ { type: 'lang', regex: '\\@', replace: '@' }
233
+ ];
234
+ }
235
+ ```
236
+
237
+ ### Filter
238
+
239
+ 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).
240
+
241
+ **Example:**
242
+
243
+ ```js
244
+ var demo = function(converter) {
245
+ return [
246
+ // Replace escaped @ symbols
247
+ { type: 'lang', filter: function(text) {
248
+ return text.replace(/\\@/g, '@');
249
+ }}
250
+ ];
251
+ }
252
+ ```
253
+
254
+ ### Implementation Concerns
255
+
256
+ 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.
257
+
258
+ ```js
259
+ (function(){
260
+ // Your extension here
261
+ }());
262
+ ```
263
+
264
+ 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.
265
+
266
+ ```js
267
+ (function(){
268
+ var demo = function(converter) {
269
+ // ... extension code here ...
270
+ };
271
+
272
+ // Client-side export
273
+ if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.demo = demo; }
274
+ // Server-side export
275
+ if (typeof module !== 'undefined') module.exports = demo;
276
+ }());
277
+ ```
278
+
279
+ ### Testing Extensions
280
+
281
+ 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.
282
+
283
+
284
+ ## Credits
285
+
286
+ * Origins
287
+ * [John Fraser](http://attacklab.net/):<br/>
288
+ Author of Showdown
289
+ * [John Gruber](http://daringfireball.net/projects/markdown/):<br/>
290
+ Author of Markdown
291
+ * Maintenance/Contributions (roughly chronologically)
292
+ * [Corey Innis](http://github.com/coreyti):<br/>
293
+ GitHub project maintainer
294
+ * [Remy Sharp](https://github.com/remy/):<br/>
295
+ CommonJS-compatibility and more
296
+ * [Konstantin Käfer](https://github.com/kkaefer/):<br/>
297
+ CommonJS packaging
298
+ * [Roger Braun](https://github.com/rogerbraun):<br/>
299
+ Github-style code blocks
300
+ * [Dominic Tarr](https://github.com/dominictarr):<br/>
301
+ Documentation
302
+ * [Cat Chen](https://github.com/CatChen):<br/>
303
+ Export fix
304
+ * [Titus Stone](https://github.com/tstone):<br/>
305
+ Mocha tests, extension mechanism, and bug fixes
306
+ * [Rob Sutherland](https://github.com/roberocity):<br/>
307
+ The idea that lead to extensions
308
+ * [Pavel Lang](https://github.com/langpavel):<br/>
309
+ Code cleanup
310
+ * [Ben Combee](https://github.com/unwiredben):<br/>
311
+ Regex optimization
312
+ * [Adam Backstrom](https://github.com/abackstrom):<br/>
313
+ WebKit bugfix
314
+ * [Pascal Deschênes](https://github.com/pdeschen):<br/>
315
+ Grunt support, extension fixes + additions, packaging improvements, documentation
316
+ * [Estevão Santos](http://github.com/coreyti):<br/>
317
+ GitHub project maintainer