├── scss
├── _colors.scss
├── _mixins.scss
├── huh.scss
├── _animations.scss
├── _launcher.scss
└── _container.scss
├── package.json
├── Gruntfile.js
├── README.md
├── huh.php
├── js
├── huh.js
└── marked.js
├── huh.css
└── LICENSE
/scss/_colors.scss:
--------------------------------------------------------------------------------
1 | $main-accent: #009688;
2 |
--------------------------------------------------------------------------------
/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | // huh mixins
2 | @mixin calc( $property, $expression ) {
3 | #{$property}: calc( #{$expression} );
4 | }
5 |
--------------------------------------------------------------------------------
/scss/huh.scss:
--------------------------------------------------------------------------------
1 | // huh
2 | @import "_mixins.scss";
3 | @import "_colors.scss";
4 | @import "_animations.scss";
5 |
6 | // launcher button
7 | @import "_launcher.scss";
8 |
9 | // content container
10 | @import "_container.scss";
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "huh",
3 | "title": "huh",
4 | "version": "1.0.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/secretpizzaparty/huh"
8 | },
9 | "license": "GPL-2.0+",
10 | "main": "Gruntfile.js",
11 | "devDependencies": {
12 | "grunt": "~1.0.1",
13 | "grunt-checktextdomain": "~1.0.0",
14 | "grunt-contrib-clean": "~1.0.0",
15 | "grunt-contrib-cssmin": "~1.0.0",
16 | "grunt-contrib-sass": "~1.0.0",
17 | "grunt-contrib-watch": "~1.0.0",
18 | "grunt-wp-i18n": "~0.5.4",
19 | "node-bourbon": "~4.2.3"
20 | },
21 | "engines": {
22 | "node": ">=0.8.0",
23 | "npm": ">=1.1.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/scss/_animations.scss:
--------------------------------------------------------------------------------
1 | // huh animations
2 |
3 | @keyframes popIn {
4 | 0% {
5 | opacity: 0;
6 | transform: scale( 0 );
7 | }
8 | 55% {
9 | opacity: 0;
10 | transform: scale( 0 );
11 | }
12 | 75% {
13 | opacity: 1;
14 | transform: scale( 1.1 );
15 | }
16 | 90% {
17 | opacity: 1;
18 | transform: scale( 0.9 );
19 | }
20 | 100% {
21 | opacity: 1;
22 | transform: scale( 1 );
23 | }
24 | }
25 |
26 | @keyframes sonarEffect {
27 | 0% {
28 | opacity: 1;
29 | }
30 | 40% {
31 | opacity: 0.5;
32 | box-shadow: 0 0 4px 2px rgba( 0, 0, 0 , 0.2 );
33 | }
34 | 100% {
35 | box-shadow: 0 0 4px 2px rgba( 0, 0, 0 , 0.2 );
36 | transform: scale(1.5);
37 | opacity: 0;
38 | }
39 | }
40 |
41 | @keyframes fadeIn {
42 | 0% {
43 | opacity: 0;
44 | transform: translateY( 20px );
45 | }
46 |
47 | 100% {
48 | opacity: 1;
49 | transform: translateY( 0 );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function( grunt ) {
2 | 'use strict';
3 |
4 | grunt.initConfig({
5 |
6 | // Setting folder templates.
7 | dirs: {
8 | css: 'scss'
9 | },
10 |
11 | // Compile all .scss files.
12 | sass: {
13 | compile: {
14 | options: {
15 | sourcemap: 'none',
16 | loadPath: require( 'node-bourbon' ).includePaths
17 | },
18 | files: [{
19 | expand: true,
20 | cwd: '<%= dirs.css %>/',
21 | src: ['*.scss'],
22 | dest: './',
23 | ext: '.css'
24 | }]
25 | }
26 | },
27 |
28 | // Minify all .css files.
29 | cssmin: {
30 | minify: {
31 | expand: true,
32 | cwd: './',
33 | src: [
34 | '*.css',
35 | '!*.min.css'
36 | ],
37 | dest: './',
38 | ext: '.min.css'
39 | }
40 | },
41 |
42 | // Watch changes for assets.
43 | watch: {
44 | css: {
45 | files: ['<%= dirs.css %>/*.scss'],
46 | tasks: ['sass', 'cssmin']
47 | }
48 | }
49 | });
50 |
51 | // Load NPM tasks to be used here
52 | grunt.loadNpmTasks( 'grunt-contrib-sass' );
53 | grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
54 | grunt.loadNpmTasks( 'grunt-contrib-watch' );
55 |
56 | // Register tasks
57 | grunt.registerTask( 'default', [
58 | 'css'
59 | ]);
60 |
61 | grunt.registerTask( 'css', [
62 | 'sass',
63 | 'cssmin'
64 | ]);
65 | };
66 |
--------------------------------------------------------------------------------
/scss/_launcher.scss:
--------------------------------------------------------------------------------
1 | // huh launcher button
2 | .huh-launcher {
3 | animation: popIn 0.8s ease-in-out;
4 | bottom: 30px;
5 | position: fixed;
6 | right: 20px;
7 | z-index: 500001;
8 | }
9 |
10 | .huh-launcher--button {
11 | -webkit-appearance: none;
12 | background: $main-accent;
13 | border: none;
14 | border-radius: 50px;
15 | box-shadow: 0 2px 10px rgba( 0, 0, 0, 0.2 );
16 | box-sizing: border-box;
17 | cursor: pointer;
18 | height: 50px;
19 | padding: 0;
20 | position: relative;
21 | transition: transform ease-out 0.1s;
22 | width: 50px;
23 | z-index: 1;
24 |
25 | svg {
26 | fill: #fff;
27 | float: right;
28 | height: 50px;
29 | left: 0;
30 | position: absolute;
31 | top: 0;
32 | transition: transform 0.2s;
33 | width: 50px;
34 | }
35 |
36 | .huh-launcher--icon-close {
37 | height: 30px;
38 | opacity: 0;
39 | padding: 10px;
40 | transform: scale( 0.2 );
41 | width: 30px;
42 | }
43 |
44 | &.active {
45 | .huh-launcher--icon-enable {
46 | opacity: 0;
47 | transform: scale( 0.2 );
48 | }
49 |
50 | .huh-launcher--icon-close {
51 | opacity: 1;
52 | transform: scale( 1);
53 | }
54 | }
55 |
56 | &::after,
57 | &::before {
58 | border-radius: 50%;
59 | box-sizing: content-box;
60 | content: '';
61 | left: 0;
62 | height: 100%;
63 | pointer-events: none;
64 | position: absolute;
65 | top: 0;
66 | transform: scale( 0.9 );
67 | width: 100%;
68 | }
69 |
70 | &:hover,
71 | &:focus {
72 | outline: none;
73 | transform: scale( 0.9 );
74 |
75 | &::after {
76 | animation: sonarEffect 1.3s ease-out;
77 | }
78 | }
79 | }
80 |
81 | .huh-launcher--label {
82 | display: block;
83 | font-size: 0;
84 | height: 0;
85 | width: 0;
86 | }
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Who?
2 | _huh_ was dreamt up by [secret pizza party](https://secretpizza.party) and brought to life by the amazing [Dan Hauk](https://danhauk.com/). Seriously, Dan is the best. He took a broad idea and really brought it to life like only he could. The SPP offices are open on Christmas Day but we close on May 5th (Dan's birthday) as it's a company wide holiday.
3 |
4 | # What?
5 | _huh_ is the best way to offer in dashboard documentation for all your WordPress projects. The content is generated from a markdown file which makes it super quick & easy to update your documentation whenever you want. You can learn more from the launch post [here](https://secretpizza.party/huh-making-documentation-easier/).
6 |
7 | # Where?
8 | We think _huh_ is awesome and we really want you to use it in all your projects. It's totally free/open source and you can find it on [github](https://github.com/secretpizzaparty/huh/).
9 |
10 | ## Wanna Contribute?
11 | If you found a bug, [report it here](https://github.com/secretpizzaparty/huh/issues/new). If you're a developer, we welcome pull requests of all types!
12 |
13 | ### Development Workflow
14 | 1. Make sure you have `git`, `node`, and `npm` installed and a working WordPress installation.
15 | 2. Clone this repository inside your theme directory.
16 |
17 | ```
18 | $ git clone https://github.com/secretpizzaparty/huh.git
19 | $ cd huh
20 | ```
21 |
22 | 3. Watch the front-end CSS/Sass for changes and rebuild accordingly with [Grunt](https://github.com/gruntjs/grunt). Please only modify the Sass files to keep the CSS consistent and clean.
23 |
24 | ```
25 | $ npm install
26 | $ grunt watch
27 | ```
28 |
29 | 4. Open `/wp-admin/` in your browser.
30 | 5. Have fun!
31 |
32 |
33 | # Why?
34 | [secret pizza party](https://secretpizza.party) is in the process of developing a bunch of new WordPress themes and while they are quite simple there is still a need for a wee bit of documentation. External documentation is dumb and everything should be contained in the dashboard. We created _huh_ to make that happen.
35 |
36 | # How?
37 | Adding _huh_ to your theme is incredibly easy.
38 |
39 | ## Formatting your markdown
40 | _huh_ pulls all of your `
` tags to use as a table of contents. Each section of your documentation will be contained between these `
` tags. For example:
41 |
42 | ```
43 | # First section
44 | The content of the first section of your documentation would go here. You can include links, bullets, images, anything!
45 |
46 | # Second section
47 | This would be the next section.
48 |
49 | ## You can even use subheadings
50 | It will all be formatted correctly, but only the first-level headings will show on the table of contents.
51 | ```
52 |
53 | ## Adding _huh_ to your theme
54 | Once you have your documentation formatted correctly, adding _huh_ to your theme is simple.
55 |
56 | Just download the zipped plugin and extract it to your theme directory. At the bottom of your theme's `functions.php` file add the following lines:
57 |
58 | ``` php
59 | require get_stylesheet_directory() . '/huh/huh.php';
60 | function secretpizzaparty_huh() {
61 | // Enter the URL of your markdown file below
62 | $markdown_url = 'https://raw.githubusercontent.com/secretpizzaparty/huh/master/README.md';
63 | $huh = new WP_Huh();
64 | $huh->init( $markdown_url );
65 | }
66 | add_action( 'admin_init', 'secretpizzaparty_huh' );
67 | ```
68 |
69 | Make sure you change the URL of the `$markdown_url` variable to point to your markdown file. It's that easy!
70 |
--------------------------------------------------------------------------------
/huh.php:
--------------------------------------------------------------------------------
1 | markdown_doc_url = $markdown_doc_url;
24 |
25 | if ( is_admin() || is_customize_preview() ) {
26 | add_action( 'admin_enqueue_scripts', array( $this, 'huh_load_scripts' ) );
27 | add_action( 'admin_footer', array( $this, 'display_huh' ) );
28 | }
29 | }
30 |
31 | /**
32 | * Enqueue CSS and JS.
33 | */
34 | public function huh_load_scripts() {
35 | wp_register_style( 'huh_admin_css', get_stylesheet_directory_uri().'/huh/huh.css', false );
36 | wp_enqueue_style( 'huh_admin_css' );
37 |
38 | wp_register_script( 'huh_admin_js', get_stylesheet_directory_uri().'/huh/js/huh.js', false );
39 | wp_enqueue_script( 'huh_admin_js' );
40 |
41 | wp_register_script( 'huh_markdown_js', get_stylesheet_directory_uri().'/huh/js/marked.js', false );
42 | wp_enqueue_script( 'huh_markdown_js' );
43 | }
44 |
45 | /**
46 | * Get admin color scheme.
47 | */
48 | public function huh_get_admin_colors() {
49 | global $_wp_admin_css_colors;
50 | $current_color_scheme = get_user_meta( get_current_user_id(), 'admin_color', true );
51 | $colors = $_wp_admin_css_colors[ $current_color_scheme ]->colors;
52 |
53 | return $colors;
54 | }
55 |
56 | /**
57 | * Display the HTML.
58 | * @param $markdown_doc_url URL of the raw markdown file.
59 | */
60 | public function display_huh() {
61 | $colors = $this->huh_get_admin_colors();
62 | $huh_accent_color = $colors[2];
63 |
64 | ?>
65 |
66 |
74 | `;
75 | } ).join( '' );
76 |
77 | return html;
78 | }
79 |
80 | function showHideContainer( e ) {
81 | huhLauncher.classList.toggle( 'active' );
82 | huhContainer.classList.toggle( 'open' );
83 | }
84 |
85 | function showContent( e ) {
86 | // hide all triggers
87 | for ( i = 0; i < huhTocTriggers.length; i++ ) {
88 | huhTocTriggers[i].classList.add( 'hidden' );
89 | huhTocTriggers[i].classList.remove( 'show' );
90 | }
91 |
92 | // add a class to indicate current selection
93 | e.target.classList.add( 'current' );
94 |
95 | // add a class to content block of the current selection
96 | // so we can show just that one
97 | const content = e.target.nextElementSibling;
98 | content.classList.add( 'open' );
99 |
100 | // show back button
101 | huhHeader.classList.add( 'with-content' );
102 | }
103 |
104 | function backToToc() {
105 | // show all triggers
106 | for ( i = 0; i < huhTocTriggers.length; i++ ) {
107 | huhTocTriggers[i].classList.remove( 'hidden', 'current' );
108 | huhTocTriggers[i].classList.add( 'show' );
109 | }
110 |
111 | // hide all content blocks
112 | const contentBlocks = document.querySelectorAll( '.huh-toc--content' );
113 | for ( i = 0; i < contentBlocks.length; i++ ) {
114 | contentBlocks[i].classList.remove( 'open' );
115 | }
116 |
117 | // show main header
118 | huhHeader.classList.remove( 'with-content' );
119 | }
120 |
121 | function applyAccentColor( color ) {
122 | huhLauncher.setAttribute( 'style', 'background:' + color );
123 | huhHeader.setAttribute( 'style', 'background:' + color );
124 | }
125 |
126 | function huhBindEvents() {
127 | huhLauncher.addEventListener( 'click', showHideContainer );
128 | huhMobileClose.addEventListener( 'click', showHideContainer );
129 | huhBackButton.addEventListener( 'click', backToToc );
130 |
131 | huhTocTriggers = document.querySelectorAll( '.huh-toc--trigger' );
132 | for ( i = 0; i < huhTocTriggers.length; i++ ) {
133 | huhTocTriggers[i].addEventListener( 'click', showContent );
134 | }
135 | }
136 |
137 | // init after page has loaded to make sure
138 | // we can find the DOM nodes to modify
139 | window.addEventListener( 'load', huhInit );
140 |
--------------------------------------------------------------------------------
/huh.css:
--------------------------------------------------------------------------------
1 | @keyframes popIn {
2 | 0% {
3 | opacity: 0;
4 | transform: scale(0); }
5 | 55% {
6 | opacity: 0;
7 | transform: scale(0); }
8 | 75% {
9 | opacity: 1;
10 | transform: scale(1.1); }
11 | 90% {
12 | opacity: 1;
13 | transform: scale(0.9); }
14 | 100% {
15 | opacity: 1;
16 | transform: scale(1); } }
17 | @keyframes sonarEffect {
18 | 0% {
19 | opacity: 1; }
20 | 40% {
21 | opacity: 0.5;
22 | box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.2); }
23 | 100% {
24 | box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.2);
25 | transform: scale(1.5);
26 | opacity: 0; } }
27 | @keyframes fadeIn {
28 | 0% {
29 | opacity: 0;
30 | transform: translateY(20px); }
31 | 100% {
32 | opacity: 1;
33 | transform: translateY(0); } }
34 | .huh-launcher {
35 | animation: popIn 0.8s ease-in-out;
36 | bottom: 30px;
37 | position: fixed;
38 | right: 20px;
39 | z-index: 500001; }
40 |
41 | .huh-launcher--button {
42 | -webkit-appearance: none;
43 | background: #009688;
44 | border: none;
45 | border-radius: 50px;
46 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
47 | box-sizing: border-box;
48 | cursor: pointer;
49 | height: 50px;
50 | padding: 0;
51 | position: relative;
52 | transition: transform ease-out 0.1s;
53 | width: 50px;
54 | z-index: 1; }
55 | .huh-launcher--button svg {
56 | fill: #fff;
57 | float: right;
58 | height: 50px;
59 | left: 0;
60 | position: absolute;
61 | top: 0;
62 | transition: transform 0.2s;
63 | width: 50px; }
64 | .huh-launcher--button .huh-launcher--icon-close {
65 | height: 30px;
66 | opacity: 0;
67 | padding: 10px;
68 | transform: scale(0.2);
69 | width: 30px; }
70 | .huh-launcher--button.active .huh-launcher--icon-enable {
71 | opacity: 0;
72 | transform: scale(0.2); }
73 | .huh-launcher--button.active .huh-launcher--icon-close {
74 | opacity: 1;
75 | transform: scale(1); }
76 | .huh-launcher--button::after, .huh-launcher--button::before {
77 | border-radius: 50%;
78 | box-sizing: content-box;
79 | content: '';
80 | left: 0;
81 | height: 100%;
82 | pointer-events: none;
83 | position: absolute;
84 | top: 0;
85 | transform: scale(0.9);
86 | width: 100%; }
87 | .huh-launcher--button:hover, .huh-launcher--button:focus {
88 | outline: none;
89 | transform: scale(0.9); }
90 | .huh-launcher--button:hover::after, .huh-launcher--button:focus::after {
91 | animation: sonarEffect 1.3s ease-out; }
92 |
93 | .huh-launcher--label {
94 | display: block;
95 | font-size: 0;
96 | height: 0;
97 | width: 0; }
98 |
99 | .huh-container {
100 | background: #fff;
101 | border-radius: 4px;
102 | bottom: 90px;
103 | box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
104 | display: none;
105 | height: calc( 100% - 150px );
106 | max-height: 550px;
107 | max-width: 350px;
108 | position: fixed;
109 | right: 20px;
110 | width: 100%;
111 | z-index: 500001; }
112 | .huh-container.open {
113 | animation: fadeIn 0.3s ease-in-out;
114 | display: flex;
115 | flex-direction: column; }
116 | @media screen and (max-width: 600px) {
117 | .huh-container {
118 | bottom: 0;
119 | border-radius: 0;
120 | height: 100%;
121 | max-width: none;
122 | right: 0;
123 | top: 46px; } }
124 |
125 | .huh-container--head {
126 | background: #009688;
127 | border-radius: 4px 4px 0 0;
128 | height: 60px;
129 | flex-shrink: 0;
130 | overflow: hidden;
131 | position: relative;
132 | transition: background 0.3s; }
133 | @media screen and (max-width: 600px) {
134 | .huh-container--head {
135 | border-radius: 0; } }
136 | .huh-container--head.with-content {
137 | background: #f5f5f5 !important;
138 | border-bottom: 1px solid #ddd; }
139 | .huh-container--head.with-content .huh-container--heading {
140 | transform: translateX(-100%); }
141 | .huh-container--head.with-content .huh-container--back {
142 | transform: translateX(0); }
143 |
144 | .huh-container--heading,
145 | .huh-container--back {
146 | box-sizing: border-box;
147 | font-size: 16px;
148 | left: 0;
149 | line-height: 60px;
150 | margin: 0;
151 | padding: 0 20px;
152 | position: absolute;
153 | top: 0;
154 | transition: transform 0.2s;
155 | width: 100%; }
156 |
157 | .huh-container--heading {
158 | color: #fff;
159 | font-weight: 400;
160 | text-align: center;
161 | transform: translateX(0);
162 | width: 100%; }
163 | @media all and (max-width: 600px) {
164 | .huh-container--heading {
165 | text-align: left; } }
166 |
167 | .huh-container--back {
168 | text-decoration: none;
169 | transform: translateX(100%); }
170 | .huh-container--back > svg {
171 | display: inline-block;
172 | fill: #444;
173 | height: 16px;
174 | position: relative;
175 | top: 2px;
176 | width: 16px; }
177 |
178 | .huh-container--content {
179 | overflow: scroll;
180 | padding: 0 20px 20px; }
181 | .huh-container--content::after {
182 | background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) 0%, #fff 55%, #fff 100%);
183 | background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) 0%, #fff 55%, #fff 100%);
184 | background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 55%, #fff 100%);
185 | bottom: 0;
186 | content: '';
187 | display: block;
188 | height: 20px;
189 | left: 0;
190 | position: absolute;
191 | width: 100%; }
192 | .huh-container--content p,
193 | .huh-container--content ul,
194 | .huh-container--content ol {
195 | font-size: 14px; }
196 | .huh-container--content img {
197 | max-width: 100%; }
198 | .huh-container--content code,
199 | .huh-container--content pre {
200 | max-width: 100%;
201 | overflow: scroll;
202 | white-space: pre-wrap;
203 | word-wrap: break-word; }
204 |
205 | .huh-container--close-mobile {
206 | cursor: pointer;
207 | display: none;
208 | fill: #fff;
209 | height: 24px;
210 | padding: 17px 10px;
211 | position: absolute;
212 | right: 0;
213 | top: 0;
214 | width: 24px; }
215 | @media all and (max-width: 600px) {
216 | .huh-container--close-mobile {
217 | display: block; } }
218 |
219 | .huh-toc--trigger {
220 | border-bottom: 1px solid #eee;
221 | cursor: pointer;
222 | display: block;
223 | font-size: 16px;
224 | font-weight: 600;
225 | margin: 0 -20px;
226 | padding: 20px;
227 | position: relative;
228 | transition: background 0.3s, box-shadow 0.1s; }
229 | .huh-toc--trigger > span {
230 | color: #666;
231 | float: right;
232 | opacity: 0;
233 | transform: translateX(10px);
234 | transition: opacity 0.3s, transform 0.3s; }
235 | .huh-toc--trigger:hover {
236 | background: #f9f9f9; }
237 | .huh-toc--trigger:hover > span {
238 | opacity: 1;
239 | transform: translateX(0); }
240 | .huh-toc--trigger.hidden {
241 | display: none; }
242 | .huh-toc--trigger.current {
243 | animation: fadeIn 0.3s ease-in-out;
244 | border: 0;
245 | color: #222;
246 | cursor: auto;
247 | display: block;
248 | font-size: 18px;
249 | padding-bottom: 0; }
250 | .huh-toc--trigger.current > span {
251 | display: none; }
252 | .huh-toc--trigger.current:hover {
253 | background: 0;
254 | box-shadow: none; }
255 | .huh-toc--trigger.show {
256 | animation: fadeIn 0.3s ease-in-out; }
257 |
258 | .huh-toc--content {
259 | display: none; }
260 | .huh-toc--content.open {
261 | animation: fadeIn 0.3s ease-in-out;
262 | display: block; }
263 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/js/marked.js:
--------------------------------------------------------------------------------
1 | /**
2 | * marked - a markdown parser
3 | * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
4 | * https://github.com/chjj/marked
5 | */
6 |
7 | ;(function() {
8 |
9 | /**
10 | * Block-Level Grammar
11 | */
12 |
13 | var block = {
14 | newline: /^\n+/,
15 | code: /^( {4}[^\n]+\n*)+/,
16 | fences: noop,
17 | hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18 | heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19 | nptable: noop,
20 | lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
21 | blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
22 | list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
23 | html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
24 | def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
25 | table: noop,
26 | paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
27 | text: /^[^\n]+/
28 | };
29 |
30 | block.bullet = /(?:[*+-]|\d+\.)/;
31 | block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
32 | block.item = replace(block.item, 'gm')
33 | (/bull/g, block.bullet)
34 | ();
35 |
36 | block.list = replace(block.list)
37 | (/bull/g, block.bullet)
38 | ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
39 | ('def', '\\n+(?=' + block.def.source + ')')
40 | ();
41 |
42 | block.blockquote = replace(block.blockquote)
43 | ('def', block.def)
44 | ();
45 |
46 | block._tag = '(?!(?:'
47 | + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
48 | + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
49 | + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
50 |
51 | block.html = replace(block.html)
52 | ('comment', //)
53 | ('closed', /<(tag)[\s\S]+?<\/\1>/)
54 | ('closing', /])*?>/)
55 | (/tag/g, block._tag)
56 | ();
57 |
58 | block.paragraph = replace(block.paragraph)
59 | ('hr', block.hr)
60 | ('heading', block.heading)
61 | ('lheading', block.lheading)
62 | ('blockquote', block.blockquote)
63 | ('tag', '<' + block._tag)
64 | ('def', block.def)
65 | ();
66 |
67 | /**
68 | * Normal Block Grammar
69 | */
70 |
71 | block.normal = merge({}, block);
72 |
73 | /**
74 | * GFM Block Grammar
75 | */
76 |
77 | block.gfm = merge({}, block.normal, {
78 | fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
79 | paragraph: /^/,
80 | heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
81 | });
82 |
83 | block.gfm.paragraph = replace(block.paragraph)
84 | ('(?!', '(?!'
85 | + block.gfm.fences.source.replace('\\1', '\\2') + '|'
86 | + block.list.source.replace('\\1', '\\3') + '|')
87 | ();
88 |
89 | /**
90 | * GFM + Tables Block Grammar
91 | */
92 |
93 | block.tables = merge({}, block.gfm, {
94 | nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
95 | table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
96 | });
97 |
98 | /**
99 | * Block Lexer
100 | */
101 |
102 | function Lexer(options) {
103 | this.tokens = [];
104 | this.tokens.links = {};
105 | this.options = options || marked.defaults;
106 | this.rules = block.normal;
107 |
108 | if (this.options.gfm) {
109 | if (this.options.tables) {
110 | this.rules = block.tables;
111 | } else {
112 | this.rules = block.gfm;
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * Expose Block Rules
119 | */
120 |
121 | Lexer.rules = block;
122 |
123 | /**
124 | * Static Lex Method
125 | */
126 |
127 | Lexer.lex = function(src, options) {
128 | var lexer = new Lexer(options);
129 | return lexer.lex(src);
130 | };
131 |
132 | /**
133 | * Preprocessing
134 | */
135 |
136 | Lexer.prototype.lex = function(src) {
137 | src = src
138 | .replace(/\r\n|\r/g, '\n')
139 | .replace(/\t/g, ' ')
140 | .replace(/\u00a0/g, ' ')
141 | .replace(/\u2424/g, '\n');
142 |
143 | return this.token(src, true);
144 | };
145 |
146 | /**
147 | * Lexing
148 | */
149 |
150 | Lexer.prototype.token = function(src, top, bq) {
151 | var src = src.replace(/^ +$/gm, '')
152 | , next
153 | , loose
154 | , cap
155 | , bull
156 | , b
157 | , item
158 | , space
159 | , i
160 | , l;
161 |
162 | while (src) {
163 | // newline
164 | if (cap = this.rules.newline.exec(src)) {
165 | src = src.substring(cap[0].length);
166 | if (cap[0].length > 1) {
167 | this.tokens.push({
168 | type: 'space'
169 | });
170 | }
171 | }
172 |
173 | // code
174 | if (cap = this.rules.code.exec(src)) {
175 | src = src.substring(cap[0].length);
176 | cap = cap[0].replace(/^ {4}/gm, '');
177 | this.tokens.push({
178 | type: 'code',
179 | text: !this.options.pedantic
180 | ? cap.replace(/\n+$/, '')
181 | : cap
182 | });
183 | continue;
184 | }
185 |
186 | // fences (gfm)
187 | if (cap = this.rules.fences.exec(src)) {
188 | src = src.substring(cap[0].length);
189 | this.tokens.push({
190 | type: 'code',
191 | lang: cap[2],
192 | text: cap[3] || ''
193 | });
194 | continue;
195 | }
196 |
197 | // heading
198 | if (cap = this.rules.heading.exec(src)) {
199 | src = src.substring(cap[0].length);
200 | this.tokens.push({
201 | type: 'heading',
202 | depth: cap[1].length,
203 | text: cap[2]
204 | });
205 | continue;
206 | }
207 |
208 | // table no leading pipe (gfm)
209 | if (top && (cap = this.rules.nptable.exec(src))) {
210 | src = src.substring(cap[0].length);
211 |
212 | item = {
213 | type: 'table',
214 | header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
215 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
216 | cells: cap[3].replace(/\n$/, '').split('\n')
217 | };
218 |
219 | for (i = 0; i < item.align.length; i++) {
220 | if (/^ *-+: *$/.test(item.align[i])) {
221 | item.align[i] = 'right';
222 | } else if (/^ *:-+: *$/.test(item.align[i])) {
223 | item.align[i] = 'center';
224 | } else if (/^ *:-+ *$/.test(item.align[i])) {
225 | item.align[i] = 'left';
226 | } else {
227 | item.align[i] = null;
228 | }
229 | }
230 |
231 | for (i = 0; i < item.cells.length; i++) {
232 | item.cells[i] = item.cells[i].split(/ *\| */);
233 | }
234 |
235 | this.tokens.push(item);
236 |
237 | continue;
238 | }
239 |
240 | // lheading
241 | if (cap = this.rules.lheading.exec(src)) {
242 | src = src.substring(cap[0].length);
243 | this.tokens.push({
244 | type: 'heading',
245 | depth: cap[2] === '=' ? 1 : 2,
246 | text: cap[1]
247 | });
248 | continue;
249 | }
250 |
251 | // hr
252 | if (cap = this.rules.hr.exec(src)) {
253 | src = src.substring(cap[0].length);
254 | this.tokens.push({
255 | type: 'hr'
256 | });
257 | continue;
258 | }
259 |
260 | // blockquote
261 | if (cap = this.rules.blockquote.exec(src)) {
262 | src = src.substring(cap[0].length);
263 |
264 | this.tokens.push({
265 | type: 'blockquote_start'
266 | });
267 |
268 | cap = cap[0].replace(/^ *> ?/gm, '');
269 |
270 | // Pass `top` to keep the current
271 | // "toplevel" state. This is exactly
272 | // how markdown.pl works.
273 | this.token(cap, top, true);
274 |
275 | this.tokens.push({
276 | type: 'blockquote_end'
277 | });
278 |
279 | continue;
280 | }
281 |
282 | // list
283 | if (cap = this.rules.list.exec(src)) {
284 | src = src.substring(cap[0].length);
285 | bull = cap[2];
286 |
287 | this.tokens.push({
288 | type: 'list_start',
289 | ordered: bull.length > 1
290 | });
291 |
292 | // Get each top-level item.
293 | cap = cap[0].match(this.rules.item);
294 |
295 | next = false;
296 | l = cap.length;
297 | i = 0;
298 |
299 | for (; i < l; i++) {
300 | item = cap[i];
301 |
302 | // Remove the list item's bullet
303 | // so it is seen as the next token.
304 | space = item.length;
305 | item = item.replace(/^ *([*+-]|\d+\.) +/, '');
306 |
307 | // Outdent whatever the
308 | // list item contains. Hacky.
309 | if (~item.indexOf('\n ')) {
310 | space -= item.length;
311 | item = !this.options.pedantic
312 | ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
313 | : item.replace(/^ {1,4}/gm, '');
314 | }
315 |
316 | // Determine whether the next list item belongs here.
317 | // Backpedal if it does not belong in this list.
318 | if (this.options.smartLists && i !== l - 1) {
319 | b = block.bullet.exec(cap[i + 1])[0];
320 | if (bull !== b && !(bull.length > 1 && b.length > 1)) {
321 | src = cap.slice(i + 1).join('\n') + src;
322 | i = l - 1;
323 | }
324 | }
325 |
326 | // Determine whether item is loose or not.
327 | // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
328 | // for discount behavior.
329 | loose = next || /\n\n(?!\s*$)/.test(item);
330 | if (i !== l - 1) {
331 | next = item.charAt(item.length - 1) === '\n';
332 | if (!loose) loose = next;
333 | }
334 |
335 | this.tokens.push({
336 | type: loose
337 | ? 'loose_item_start'
338 | : 'list_item_start'
339 | });
340 |
341 | // Recurse.
342 | this.token(item, false, bq);
343 |
344 | this.tokens.push({
345 | type: 'list_item_end'
346 | });
347 | }
348 |
349 | this.tokens.push({
350 | type: 'list_end'
351 | });
352 |
353 | continue;
354 | }
355 |
356 | // html
357 | if (cap = this.rules.html.exec(src)) {
358 | src = src.substring(cap[0].length);
359 | this.tokens.push({
360 | type: this.options.sanitize
361 | ? 'paragraph'
362 | : 'html',
363 | pre: !this.options.sanitizer
364 | && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
365 | text: cap[0]
366 | });
367 | continue;
368 | }
369 |
370 | // def
371 | if ((!bq && top) && (cap = this.rules.def.exec(src))) {
372 | src = src.substring(cap[0].length);
373 | this.tokens.links[cap[1].toLowerCase()] = {
374 | href: cap[2],
375 | title: cap[3]
376 | };
377 | continue;
378 | }
379 |
380 | // table (gfm)
381 | if (top && (cap = this.rules.table.exec(src))) {
382 | src = src.substring(cap[0].length);
383 |
384 | item = {
385 | type: 'table',
386 | header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
387 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
388 | cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
389 | };
390 |
391 | for (i = 0; i < item.align.length; i++) {
392 | if (/^ *-+: *$/.test(item.align[i])) {
393 | item.align[i] = 'right';
394 | } else if (/^ *:-+: *$/.test(item.align[i])) {
395 | item.align[i] = 'center';
396 | } else if (/^ *:-+ *$/.test(item.align[i])) {
397 | item.align[i] = 'left';
398 | } else {
399 | item.align[i] = null;
400 | }
401 | }
402 |
403 | for (i = 0; i < item.cells.length; i++) {
404 | item.cells[i] = item.cells[i]
405 | .replace(/^ *\| *| *\| *$/g, '')
406 | .split(/ *\| */);
407 | }
408 |
409 | this.tokens.push(item);
410 |
411 | continue;
412 | }
413 |
414 | // top-level paragraph
415 | if (top && (cap = this.rules.paragraph.exec(src))) {
416 | src = src.substring(cap[0].length);
417 | this.tokens.push({
418 | type: 'paragraph',
419 | text: cap[1].charAt(cap[1].length - 1) === '\n'
420 | ? cap[1].slice(0, -1)
421 | : cap[1]
422 | });
423 | continue;
424 | }
425 |
426 | // text
427 | if (cap = this.rules.text.exec(src)) {
428 | // Top-level should never reach here.
429 | src = src.substring(cap[0].length);
430 | this.tokens.push({
431 | type: 'text',
432 | text: cap[0]
433 | });
434 | continue;
435 | }
436 |
437 | if (src) {
438 | throw new
439 | Error('Infinite loop on byte: ' + src.charCodeAt(0));
440 | }
441 | }
442 |
443 | return this.tokens;
444 | };
445 |
446 | /**
447 | * Inline-Level Grammar
448 | */
449 |
450 | var inline = {
451 | escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
452 | autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
453 | url: noop,
454 | tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
455 | link: /^!?\[(inside)\]\(href\)/,
456 | reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
457 | nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
458 | strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
459 | em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
460 | code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
461 | br: /^ {2,}\n(?!\s*$)/,
462 | del: noop,
463 | text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;
468 |
469 | inline.link = replace(inline.link)
470 | ('inside', inline._inside)
471 | ('href', inline._href)
472 | ();
473 |
474 | inline.reflink = replace(inline.reflink)
475 | ('inside', inline._inside)
476 | ();
477 |
478 | /**
479 | * Normal Inline Grammar
480 | */
481 |
482 | inline.normal = merge({}, inline);
483 |
484 | /**
485 | * Pedantic Inline Grammar
486 | */
487 |
488 | inline.pedantic = merge({}, inline.normal, {
489 | strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
490 | em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
491 | });
492 |
493 | /**
494 | * GFM Inline Grammar
495 | */
496 |
497 | inline.gfm = merge({}, inline.normal, {
498 | escape: replace(inline.escape)('])', '~|])')(),
499 | url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
500 | del: /^~~(?=\S)([\s\S]*?\S)~~/,
501 | text: replace(inline.text)
502 | (']|', '~]|')
503 | ('|', '|https?://|')
504 | ()
505 | });
506 |
507 | /**
508 | * GFM + Line Breaks Inline Grammar
509 | */
510 |
511 | inline.breaks = merge({}, inline.gfm, {
512 | br: replace(inline.br)('{2,}', '*')(),
513 | text: replace(inline.gfm.text)('{2,}', '*')()
514 | });
515 |
516 | /**
517 | * Inline Lexer & Compiler
518 | */
519 |
520 | function InlineLexer(links, options) {
521 | this.options = options || marked.defaults;
522 | this.links = links;
523 | this.rules = inline.normal;
524 | this.renderer = this.options.renderer || new Renderer;
525 | this.renderer.options = this.options;
526 |
527 | if (!this.links) {
528 | throw new
529 | Error('Tokens array requires a `links` property.');
530 | }
531 |
532 | if (this.options.gfm) {
533 | if (this.options.breaks) {
534 | this.rules = inline.breaks;
535 | } else {
536 | this.rules = inline.gfm;
537 | }
538 | } else if (this.options.pedantic) {
539 | this.rules = inline.pedantic;
540 | }
541 | }
542 |
543 | /**
544 | * Expose Inline Rules
545 | */
546 |
547 | InlineLexer.rules = inline;
548 |
549 | /**
550 | * Static Lexing/Compiling Method
551 | */
552 |
553 | InlineLexer.output = function(src, links, options) {
554 | var inline = new InlineLexer(links, options);
555 | return inline.output(src);
556 | };
557 |
558 | /**
559 | * Lexing/Compiling
560 | */
561 |
562 | InlineLexer.prototype.output = function(src) {
563 | var out = ''
564 | , link
565 | , text
566 | , href
567 | , cap;
568 |
569 | while (src) {
570 | // escape
571 | if (cap = this.rules.escape.exec(src)) {
572 | src = src.substring(cap[0].length);
573 | out += cap[1];
574 | continue;
575 | }
576 |
577 | // autolink
578 | if (cap = this.rules.autolink.exec(src)) {
579 | src = src.substring(cap[0].length);
580 | if (cap[2] === '@') {
581 | text = cap[1].charAt(6) === ':'
582 | ? this.mangle(cap[1].substring(7))
583 | : this.mangle(cap[1]);
584 | href = this.mangle('mailto:') + text;
585 | } else {
586 | text = escape(cap[1]);
587 | href = text;
588 | }
589 | out += this.renderer.link(href, null, text);
590 | continue;
591 | }
592 |
593 | // url (gfm)
594 | if (!this.inLink && (cap = this.rules.url.exec(src))) {
595 | src = src.substring(cap[0].length);
596 | text = escape(cap[1]);
597 | href = text;
598 | out += this.renderer.link(href, null, text);
599 | continue;
600 | }
601 |
602 | // tag
603 | if (cap = this.rules.tag.exec(src)) {
604 | if (!this.inLink && /^/i.test(cap[0])) {
607 | this.inLink = false;
608 | }
609 | src = src.substring(cap[0].length);
610 | out += this.options.sanitize
611 | ? this.options.sanitizer
612 | ? this.options.sanitizer(cap[0])
613 | : escape(cap[0])
614 | : cap[0]
615 | continue;
616 | }
617 |
618 | // link
619 | if (cap = this.rules.link.exec(src)) {
620 | src = src.substring(cap[0].length);
621 | this.inLink = true;
622 | out += this.outputLink(cap, {
623 | href: cap[2],
624 | title: cap[3]
625 | });
626 | this.inLink = false;
627 | continue;
628 | }
629 |
630 | // reflink, nolink
631 | if ((cap = this.rules.reflink.exec(src))
632 | || (cap = this.rules.nolink.exec(src))) {
633 | src = src.substring(cap[0].length);
634 | link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
635 | link = this.links[link.toLowerCase()];
636 | if (!link || !link.href) {
637 | out += cap[0].charAt(0);
638 | src = cap[0].substring(1) + src;
639 | continue;
640 | }
641 | this.inLink = true;
642 | out += this.outputLink(cap, link);
643 | this.inLink = false;
644 | continue;
645 | }
646 |
647 | // strong
648 | if (cap = this.rules.strong.exec(src)) {
649 | src = src.substring(cap[0].length);
650 | out += this.renderer.strong(this.output(cap[2] || cap[1]));
651 | continue;
652 | }
653 |
654 | // em
655 | if (cap = this.rules.em.exec(src)) {
656 | src = src.substring(cap[0].length);
657 | out += this.renderer.em(this.output(cap[2] || cap[1]));
658 | continue;
659 | }
660 |
661 | // code
662 | if (cap = this.rules.code.exec(src)) {
663 | src = src.substring(cap[0].length);
664 | out += this.renderer.codespan(escape(cap[2], true));
665 | continue;
666 | }
667 |
668 | // br
669 | if (cap = this.rules.br.exec(src)) {
670 | src = src.substring(cap[0].length);
671 | out += this.renderer.br();
672 | continue;
673 | }
674 |
675 | // del (gfm)
676 | if (cap = this.rules.del.exec(src)) {
677 | src = src.substring(cap[0].length);
678 | out += this.renderer.del(this.output(cap[1]));
679 | continue;
680 | }
681 |
682 | // text
683 | if (cap = this.rules.text.exec(src)) {
684 | src = src.substring(cap[0].length);
685 | out += this.renderer.text(escape(this.smartypants(cap[0])));
686 | continue;
687 | }
688 |
689 | if (src) {
690 | throw new
691 | Error('Infinite loop on byte: ' + src.charCodeAt(0));
692 | }
693 | }
694 |
695 | return out;
696 | };
697 |
698 | /**
699 | * Compile Link
700 | */
701 |
702 | InlineLexer.prototype.outputLink = function(cap, link) {
703 | var href = escape(link.href)
704 | , title = link.title ? escape(link.title) : null;
705 |
706 | return cap[0].charAt(0) !== '!'
707 | ? this.renderer.link(href, title, this.output(cap[1]))
708 | : this.renderer.image(href, title, escape(cap[1]));
709 | };
710 |
711 | /**
712 | * Smartypants Transformations
713 | */
714 |
715 | InlineLexer.prototype.smartypants = function(text) {
716 | if (!this.options.smartypants) return text;
717 | return text
718 | // em-dashes
719 | .replace(/---/g, '\u2014')
720 | // en-dashes
721 | .replace(/--/g, '\u2013')
722 | // opening singles
723 | .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
724 | // closing singles & apostrophes
725 | .replace(/'/g, '\u2019')
726 | // opening doubles
727 | .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
728 | // closing doubles
729 | .replace(/"/g, '\u201d')
730 | // ellipses
731 | .replace(/\.{3}/g, '\u2026');
732 | };
733 |
734 | /**
735 | * Mangle Links
736 | */
737 |
738 | InlineLexer.prototype.mangle = function(text) {
739 | if (!this.options.mangle) return text;
740 | var out = ''
741 | , l = text.length
742 | , i = 0
743 | , ch;
744 |
745 | for (; i < l; i++) {
746 | ch = text.charCodeAt(i);
747 | if (Math.random() > 0.5) {
748 | ch = 'x' + ch.toString(16);
749 | }
750 | out += '' + ch + ';';
751 | }
752 |
753 | return out;
754 | };
755 |
756 | /**
757 | * Renderer
758 | */
759 |
760 | function Renderer(options) {
761 | this.options = options || {};
762 | }
763 |
764 | Renderer.prototype.code = function(code, lang, escaped) {
765 | if (this.options.highlight) {
766 | var out = this.options.highlight(code, lang);
767 | if (out != null && out !== code) {
768 | escaped = true;
769 | code = out;
770 | }
771 | }
772 |
773 | if (!lang) {
774 | return '