├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── cli.js ├── example ├── example.html └── example.md ├── gulpfile.js ├── lib ├── iframe.ejs ├── index.js └── template.ejs └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - 2018-12-08 增加 MathJax 支持(附 [一个生成 LaTex 公式的网站](https://webdemo.myscript.com/views/math/index.html)) 2 | - 2018-12-16 修改 api,release 2.0.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 子迟 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codedog 2 | 3 | ## 初衷 4 | 5 | 个人比较习惯用 markdown 记一些东西,有时候需要插入一些在线小 demo,最好还能在线编辑,用类似 [codepen](https://codepen.io/) 引入的话显得有些大材小用,而且第三方不保险,便希望有一种方式,能在 markdown 中写 html,并且不会相互干扰,遂有此项目 6 | 7 | 我们可以比较下原始的 markdown 文件和用 codedog 二次生成后的文件: 8 | 9 | - [原始的 markdown 文件](https://github.com/hanzichi/codedog/blob/master/example/example.md) 10 | - [codedog 二次生成后的 html 文件](https://hanzichi.github.io/codedog/example/example.html) 11 | 12 | 另外,这个项目是我为 [css-secrets](https://github.com/hanzichi/css-secrets) 特意创造的,更多应用可以点 [这里](https://github.com/hanzichi/css-secrets/blob/master/README.md) 查看 13 | 14 | ## 用法 15 | 16 | 该项目使用非常简单,支持模块引用和全局引用,个人推荐全局引用方式 17 | 18 | ### 本地安装 19 | 20 | ```bash 21 | $ git clone git@github.com:hanzichi/codedog.git 22 | $ npm install 23 | $ npm link 24 | ``` 25 | 26 | ### npm 安装 27 | 28 | ```bash 29 | $ npm install codedog -g 30 | ``` 31 | 32 | ### 应用 33 | 34 | ```bash 35 | $ codedog xx.md 36 | $ codedog xx.md width height 37 | ``` 38 | 39 | 没错,codedog 几乎不提供任何参数,唯二提供的两个可选参数是在线 demo 的宽度和高度: 40 | 41 | - `width` the width of the code editor as well as other code blocks, default value is 100% of its parent node's width (`0` is the same as default) 42 | - `height` the height of the code editor, default value is `270px` 43 | 44 | ## 开发 45 | 46 | ```bash 47 | $ npm install 48 | $ npm run dev 49 | ``` 50 | 51 | run dev 后会自动打开浏览器,打开的 url 后跟上 example.html 即可 52 | 53 | 接下去在 `lib` 和 `example` 两个文件夹中修改文件,codedog 会自动编译 example.md 文件,生成新的 example.html 文件并且自动刷新打开的页面 54 | 55 | ## 注意事项 56 | 57 | - 该项目用于从 markdown 生成 html,并提供某些在线编辑/预览,如果没有这个需求,说明你不需要 58 | - **markdown 中标注为 `html` 的代码块会被 codedog 解析**,并生成在线 demo,其余代码块不会解析 59 | - 被解析的代码块,支持内联 css 和 js,支持绝对路径的外链 css 和 js 60 | - 点击新页面打开,会在新的页面打开在线 demo 61 | - 页面 title 默认为第一次出现的一级标题,且 markdown 文件中一级标题只允许出现一次,且默认一级标题肯定早于其他二级标题、三级标题等出现 62 | - 不支持移动端,个人觉得移动端上编辑代码&预览效果不方便,故不提供支持 63 | 64 | ## lisence 65 | 66 | MIT 67 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs') 3 | const codedog = require('../lib') 4 | 5 | let filePath = process.argv[2] 6 | let outPath = filePath.replace('md', 'html') 7 | 8 | let editorWidth = ~~process.argv[3] // default is 100% width 9 | let editorHeight = ~~process.argv[4] || 270 // default is 270px as the height 10 | 11 | let mdString = fs.readFileSync(filePath, 'utf-8') 12 | let html = codedog(mdString, editorWidth, editorHeight) 13 | fs.writeFileSync(outPath, html) -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 条纹背景 6 | 7 | 8 | 9 | 10 | 100 | 101 | 102 |

条纹背景

103 |

难题

104 |

以前想要在网页中实现条纹图案,可能需要创建一个单独的位图文件,然后每次需要做些调整时,都用图像编辑器来修改它。现在我们可以直接在 CSS 中创建条纹图案

105 |

水平条纹

106 |

该法能实现水平条纹的核心原因是:如果多个色标具有相同的位置,它们会产生一个无限小的过渡区域

107 |
108 |
109 | 110 |
111 |
112 |
113 |
114 | 115 |
116 |
117 |
118 | 151 |

以上 demo 中每个条纹的高度是 15px(它有两个渐变色,分别占据 50%,总共 30px)

152 |

为了避免每次改动数值时需要修改两个数字,我们可以再次从规范那里找到捷径:

153 |
154 |

如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值

155 |
156 |
157 |
158 | 159 |
160 |
161 |
162 |
163 | 164 |
165 |
166 |
167 | 196 |

垂直条纹

197 |
198 |
199 | 200 |
201 |
202 |
203 |
204 | 205 |
206 |
207 |
208 | 237 |

与水平条纹实现的主要差别在于:我们需要在开头加上一个额外的参数来指定渐变的方向。在水平条纹的代码中,我们其实也可以加上这个参数,只不过它的默认值 to bottom 本来就跟我们的意图一致,所以可省略。最后,我们还需要把 background-size 的值颠倒一下

238 |

斜向条纹

239 |
240 |
241 | 242 |
243 |
244 |
245 |
246 | 247 |
248 |
249 |
250 | 279 |

这个方法行不通。原因在于 单个切片需要包含四条条纹,而不是两条,只有这样才能做到无缝拼接

280 |
281 |
282 | 283 |
284 |
285 |
286 |
287 | 288 |
289 |
290 |
291 | 324 |

但是这样的结果条纹很明显比之前的细,根据计算,我们需要设置单个切片的宽度是之前的 \(\sqrt2\) 倍

325 |
326 |
327 | 328 |
329 |
330 |
331 |
332 | 333 |
334 |
335 |
336 | 369 |

更好的斜向条纹

370 |

我们将上面的代码修改角度,会发现条纹完全错乱了(try it!)

371 |

幸运的是,我们还有更好的方法来创建斜向条纹。linear-gradient() 和 radial-gradient() 还各有一个循环式的加强版:repeating-linear-gradient() 和 repeating-radial-gradient()。它们的工作方式和前两者类似,只有一点不同:色标是无限循环重复的,直到填满整个背景

372 |
373 |
374 | 375 |
376 |
377 |
378 |
379 | 380 |
381 |
382 |
383 | 412 | 417 |

灵活的同色系条纹

418 |

大多数情况下,我们想要的条纹图案并不是由差异极大的几种颜色组成的,这些颜色往往属于同一色系,只是在明度方面有着轻微的差异

419 |
420 |
421 | 422 |
423 |
424 |
425 |
426 | 427 |
428 |
429 |
430 | 459 |

条纹是由一个主色调(#58a)和它的浅色变体所组成的。但是,这两种颜色之间的关系在代码中并没有体现出来。如果我们想要改变这个条纹的主色调,甚至需要修改四处!

460 |

幸运的是,还有一种更好的方法:不再为每种条纹单独指定颜色,而是把最深的颜色指定为背景色,同时把半透明白色的条纹叠加在背景色之上来得到浅色条纹

461 |
462 |
463 | 464 |
465 |
466 |
467 |
468 | 469 |
470 |
471 |
472 | 507 |
508 | 509 | 510 | 511 | 530 | 531 | -------------------------------------------------------------------------------- /example/example.md: -------------------------------------------------------------------------------- 1 | # 条纹背景 2 | 3 | ## 难题 4 | 5 | 以前想要在网页中实现条纹图案,可能需要创建一个单独的位图文件,然后每次需要做些调整时,都用图像编辑器来修改它。现在我们可以直接在 CSS 中创建条纹图案 6 | 7 | ## 水平条纹 8 | 9 | 该法能实现水平条纹的核心原因是:如果多个色标具有相同的位置,它们会产生一个无限小的过渡区域 10 | 11 | ```html 12 | 13 | 21 | 22 | ``` 23 | 24 | 以上 demo 中每个条纹的高度是 15px(它有两个渐变色,分别占据 50%,总共 30px) 25 | 26 | 为了避免每次改动数值时需要修改两个数字,我们可以再次从规范那里找到捷径: 27 | 28 | > 如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值 29 | 30 | ```html 31 | 32 | 38 | 39 | ``` 40 | 41 | ## 垂直条纹 42 | 43 | ```html 44 | 45 | 51 | 52 | ``` 53 | 54 | 与水平条纹实现的主要差别在于:我们需要在开头加上一个额外的参数来指定渐变的方向。在水平条纹的代码中,我们其实也可以加上这个参数,只不过它的默认值 to bottom 本来就跟我们的意图一致,所以可省略。最后,我们还需要把 background-size 的值颠倒一下 55 | 56 | ## 斜向条纹 57 | 58 | ```html 59 | 60 | 66 | 67 | ``` 68 | 69 | 这个方法行不通。原因在于 **单个切片需要包含四条条纹**,而不是两条,只有这样才能做到无缝拼接 70 | 71 | ```html 72 | 73 | 81 | 82 | ``` 83 | 84 | 但是这样的结果条纹很明显比之前的细,根据计算,**我们需要设置单个切片的宽度是之前的 \\(\sqrt2\\) 倍** 85 | 86 | ```html 87 | 88 | 96 | 97 | ``` 98 | 99 | ## 更好的斜向条纹 100 | 101 | 我们将上面的代码修改角度,会发现条纹完全错乱了(try it!) 102 | 103 | 幸运的是,我们还有更好的方法来创建斜向条纹。linear-gradient() 和 radial-gradient() 还各有一个循环式的加强版:repeating-linear-gradient() 和 repeating-radial-gradient()。它们的工作方式和前两者类似,只有一点不同:**色标是无限循环重复的,直到填满整个背景**。 104 | 105 | ```html 106 | 107 | 113 | 114 | ``` 115 | 116 | - 可以随意变换角度(try it!) 117 | - 15px 是指渐变轴上度量到的距离,再也不用去乘以 \\(\sqrt2\\) 了 118 | - 同理可以生成水平和垂直条纹(但是请注意,在这个方法中,无论条纹的角度如何,我们在创建双色条纹时都需要用到四个色标,这意味着,**我们最好用前面的方法来实现水平条纹或者垂直条纹,而用这种方法实现斜向条纹**) 119 | 120 | ## 灵活的同色系条纹 121 | 122 | 大多数情况下,我们想要的条纹图案并不是由差异极大的几种颜色组成的,**这些颜色往往属于同一色系,只是在明度方面有着轻微的差异** 123 | 124 | ```html 125 | 126 | 132 | 133 | ``` 134 | 135 | 条纹是由一个主色调(#58a)和它的浅色变体所组成的。但是,这两种颜色之间的关系在代码中并没有体现出来。如果我们想要改变这个条纹的主色调,甚至需要修改四处! 136 | 137 | 幸运的是,还有一种更好的方法:不再为每种条纹单独指定颜色,**而是把最深的颜色指定为背景色**,同时把半透明白色的条纹叠加在背景色之上来得到浅色条纹 138 | 139 | ```html 140 | 141 | 150 | 151 | ``` 152 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const browserSync = require('browser-sync').create() 3 | const reload = browserSync.reload 4 | const fs = require('fs') 5 | // const decache = require('decache') 6 | 7 | let codedog = require('./lib') 8 | 9 | // 将 example.md 重新编译,自动刷新浏览器 10 | gulp.task('compile', () => { 11 | let filePath = './example/example.md' 12 | let outPath = filePath.replace('md', 'html') 13 | let mdString = fs.readFileSync(filePath, 'utf-8') 14 | // 消除 require node_module 时的缓存 15 | // decache('./lib') 16 | delete require.cache[require.resolve('./lib')] 17 | codedog = require('./lib') // fresh start 18 | let html = codedog(mdString, 0, 270) 19 | fs.writeFileSync(outPath, html) 20 | }) 21 | 22 | gulp.task('reloadBrowser', () => { 23 | reload() 24 | }) 25 | 26 | gulp.task('server', ['compile'], () => { 27 | browserSync.init({ 28 | server: { 29 | baseDir: './example' // example.html 文件所在的目录 30 | } 31 | }) 32 | 33 | gulp.watch("./example/*.html", ['reloadBrowser']) 34 | gulp.watch("./example/*.md", ['compile']) 35 | gulp.watch("./lib/*", ['compile']) 36 | }) 37 | 38 | gulp.task('default', ['server']) -------------------------------------------------------------------------------- /lib/iframe.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 |
8 | 9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const marked = require('marked') 2 | const ejs = require('ejs') 3 | const fs = require('fs') 4 | 5 | /** 6 | * markdown to html with `xx` replaced with a live demo 7 | * 8 | * @param {string} mdString 9 | * @param {number} editorWidth 10 | * @param {number} editorHeight 11 | * @returns {string} htmlString 12 | */ 13 | function markdown2html(mdString, editorWidth=0, editorHeight=270) { 14 | const unescapeMap = { 15 | '&': '&', 16 | '<': '<', 17 | '>': '>', 18 | '"': '"', 19 | ''': "'", 20 | } 21 | 22 | // get title 23 | let title = '' 24 | let p = /(#+)(.*)/ 25 | let matches = p.exec(mdString) 26 | if (matches && matches[1] === '#') { 27 | title = matches[2].trim() 28 | } 29 | 30 | let html = marked(mdString) 31 | 32 | // unescape 33 | Object.keys(unescapeMap).forEach(item => { 34 | let [from, to] = [item, unescapeMap[item]] 35 | html = html // only works for `lang=html` 36 | .replace(new RegExp(`(
)([\\s\\S]+?)(<\/code>)`, 'g'), (a, b, c, d) => {
37 |         return b + c.replace(new RegExp(`${from}`, 'g'), to) + d
38 |       })
39 |   })
40 | 
41 |   // escape and unescape
42 |   html = html // only works for `lang=html`
43 |     .replace(new RegExp(`(
)([\\s\\S]+?)(<\/code>)`, 'g'), (a, b, c, d) => {
44 |       // unescape
45 |       Object.keys(unescapeMap).forEach(item => {
46 |         let [from, to] = [item, unescapeMap[item]]
47 |         c = c.replace(new RegExp(`${from}`, 'g'), to)
48 |       })
49 | 
50 |       // escape for `\` and `<\script>`
51 |       // e.g. content: '\a0'
52 |       c = c
53 |         .replace(/\\/g, '\\\\')
54 |         .replace(/<\\script>/, '')
55 |         
56 |       return b + c + d
57 |     })
58 | 
59 |   // `xx` replaced with a live demo
60 |   let codedogCnt = -1
61 | 
62 |   html = html.replace(/
([\s\S]+?)<\/code><\/pre>/g, (origin, code) => {
63 |     // only works for `lang=html`
64 |     code = code.trim()
65 |     codedogCnt += 1
66 | 
67 |     const iframeStr = fs.readFileSync(__dirname + '/iframe.ejs', 'utf-8')
68 |     return ejs.render(iframeStr, {codedogCnt, code})
69 |   })
70 | 
71 |   const htmlStr = fs.readFileSync(__dirname + '/template.ejs', 'utf-8')
72 |   return ejs.render(htmlStr, {html, editorWidth, editorHeight, title})
73 | }
74 | 
75 | module.exports = exports = markdown2html


--------------------------------------------------------------------------------
/lib/template.ejs:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 |   
  5 |   <%=title%>
  6 |   
  7 |   
  8 |   
  9 |   
 10 |   
104 | 
105 | 
106 | 
<%-html%>
107 | 108 | 109 | 110 | 129 | 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codedog", 3 | "version": "2.0.4", 4 | "author": "hanzichi", 5 | "description": "markdown to html with online demo preview", 6 | "homepage": "https://github.com/hanzichi/codedog", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/hanzichi/codedog" 10 | }, 11 | "main": "./lib/index.js", 12 | "bin": { 13 | "codedog": "./bin/cli.js" 14 | }, 15 | "scripts": { 16 | "dev": "gulp" 17 | }, 18 | "dependencies": { 19 | "ejs": "^2.6.1", 20 | "marked": "^0.3.6" 21 | }, 22 | "devDependencies": { 23 | "browser-sync": "^2.24.5", 24 | "gulp": "^3.9.1" 25 | }, 26 | "license": "MIT" 27 | } 28 | --------------------------------------------------------------------------------