├── .gitignore ├── .jshintrc ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets └── small-view.png ├── build.js ├── bundle.js ├── css └── index.css ├── index.html ├── index.js ├── init-brace.js ├── init.js ├── main.js ├── package.json └── render-md.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | bundle.js 14 | 15 | npm-debug.log 16 | node_modules 17 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "laxcomma" : true 3 | , "laxbreak" : true 4 | , "sub" : true 5 | , "onecase" : true 6 | , "node" : true 7 | , "expr" : true 8 | , "strict" : false 9 | , "validthis": true 10 | , "asi" : true 11 | , "globals" : { 12 | "setImmediate": true 13 | } 14 | , "browser": true 15 | } 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | assets 2 | .npmignore 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 011-finished-product 2 | 3 | - checking in `bundle.js` to allow running this in github pages 4 | - removing hyperwatch 5 | - removing line numbers from rendered code 6 | - minor fixes to make the app work statically 7 | 8 | ### 010-finished-dev-version 9 | 10 | - replacing `textarea` with `brace` editor and adjusting styles 11 | 12 | ### 009-improved-styling 13 | 14 | - editing `index.css` and adding `npm start` script in `package.json` 15 | 16 | ### 008-updating-on-edit-in-realtime 17 | 18 | - updating rendered markdown when user edits textarea (`init.js` **lines: 13-21**) 19 | - removed `require('fs')` workaround that had been there due to a bug in `browser-builtins` -- not important 20 | 21 | ### 007-rendering-md-client-side 22 | 23 | - removing `{MARKDOWN}` placeholder and simply serving `index.html` as-is server side 24 | - adding `init.js` which uses `fs.readFileSync` to load the `README.md` on the client side 25 | - installing `brfs` and adding the transform to inline `README.md` text 26 | - it then inserts the rendered markdown into the `div.render` and the original text into `textarea.edit` 27 | - **`render-md.js` didn't have to change at all** to move the render step client side! 28 | 29 | - installing `hyperwatch` to get serverside message feedback in the browser 30 | - initializing hyperwatch client side (`main.js` **lines: 5**) and server side (`index.js` **lines: 46**) 31 | 32 | - removed test code from `render-md.js` 33 | 34 | ### 006-dynamic-bundle 35 | 36 | - installed local `browserify` 37 | - added a `build.js` script that can be run via `node build` to see resulting bundle piped to `stdout` 38 | - interesting to note that `.bundle()` returns a stream 39 | - serving bundle now by calling `build` and piping the resulting stream into the response `index.js` **lines: 33-=35** 40 | - added `textarea` in `index.html` for next step 41 | 42 | ### 005-styled 43 | 44 | - added `css/index.css` which contains styling for the generated markdown 45 | - added a route for it in `index.js` to serve it via `createReadStream` **lines: 37-40** 46 | 47 | ### 004-rendering-markdown-on-server 48 | 49 | - updated readme 50 | - installed `peacock` and `marked` and added `render-md.js` which takes a markdown string and returns rendered html 51 | - inserted a `{MARKDOWN}` placeholder in `index.html` 52 | - updated the server (`index.js`) to render our `README.md` and replace `{MARKDOWN}` with the resulting html before 53 | serving our `index.html` **lines: 13-30** 54 | - we now launch our app via: `node index.js` 55 | 56 | ### 003-static-server 57 | 58 | - created simple http server (`index.js`) that serves `index.html` and `bundle.js` 59 | - interesting to note that both are piped from `createReadStream` **lines: 7-15** 60 | 61 | ### 002-main 62 | 63 | Good place to start following the steps 64 | 65 | - removed scripts from package.json 66 | - index.html file that references bundle.js that needs to get genereated from main.js via: `browserify main.js > 67 | bundle.js` 68 | 69 | ### 001-stat 70 | 71 | -cleaned up package.json 72 | 73 | ### 000-nstarted 74 | 75 | - ran nstart script to generate repo 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Thorsten Lorenz. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # browserify-markdown-editor 2 | 3 | [![pic](https://github.com/thlorenz/browserify-markdown-editor/raw/master/assets/small-view.png)](http://thlorenz.github.io/browserify-markdown-editor/) 4 | 5 | A demo showing how to build a markdown editor with browserify and marked. 6 | 7 | You can play with [the finished product](http://thlorenz.github.io/browserify-markdown-editor/), it may even prove 8 | useful ;) 9 | 10 | ```js 11 | var a = 3; 12 | console.log('Nice demo!'); 13 | ``` 14 | 15 | ## Installation 16 | 17 | npm install thlorenz/browserify-markdown-editor 18 | 19 | ## How to use this demo 20 | 21 | I created a tag for every major step numbered in order, so you can checkout each tag and look at the code. 22 | 23 | - review all tags and what changed from one to the next in the 24 | [Changelog](https://github.com/thlorenz/browserify-markdown-editor/blob/master/CHANGELOG.md) 25 | 26 | - list all tags: `git tag -n` 27 | - checking out a tag example: `git checkout 003-static-server` 28 | 29 | Playing with the code: 30 | 31 | - after checking out a new tag do `npm install` in order to update added dependencies 32 | - up til tag `003-static-server`: 33 | - `browserify main.js > bundle.js` 34 | - then `open index.html` in the browser 35 | - then just `node index` and for the later part `npm start` will also work 36 | 37 | Note that you can inspect each file separately as long as you enable source maps in your browser, find more information 38 | about browserify sourcemaps [here](http://thlorenz.com/blog/browserify-sourcemaps) 39 | 40 | ## License 41 | 42 | MIT 43 | -------------------------------------------------------------------------------- /assets/small-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thlorenz/browserify-markdown-editor/02c9e97708842bb52ae81eac53f28a002304285f/assets/small-view.png -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var browserify = require('browserify'); 3 | 4 | var go = module.exports = function () { 5 | return browserify({ debug: true }) 6 | .require(require.resolve('./main'), { entry: true }) 7 | .transform('brfs') 8 | .bundle(); 9 | }; 10 | 11 | // Test 12 | if (!module.parent) { 13 | go().pipe(process.stdout); 14 | } 15 | -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | #editor { 2 | width: 45%; 3 | height: 90%; 4 | 5 | position: absolute; 6 | } 7 | 8 | .rendered { 9 | position: absolute; 10 | left: 50%; 11 | width: 45%; 12 | height: 100%; 13 | 14 | } 15 | 16 | /* 17 | Some basic Github styles and syntax highlighting CSS via Pygments. 18 | */ 19 | html { 20 | font-size: 14px; 21 | line-height: 1.6; 22 | font-family: helvetica,arial,freesans,clean,sans-serif; 23 | color: black; 24 | } 25 | body { 26 | padding: 5px; 27 | margin-top: 20px; 28 | } 29 | h1 { 30 | margin: 15px 0; 31 | padding-bottom: 2px; 32 | font-size: 24px; 33 | border-bottom: 1px solid #EEE; 34 | } 35 | h2 { 36 | margin: 20px 0 10px 0; 37 | font-size: 18px; 38 | } 39 | h3 { 40 | margin: 20px 0 10px 0; 41 | padding-bottom: 2px; 42 | font-size: 14px; 43 | border-bottom: 1px solid #DDD; 44 | } 45 | h4 { 46 | font-size: 14px; 47 | line-height: 26px; 48 | padding: 18px 0 4px; 49 | font-weight: bold; 50 | text-transform: uppercase; 51 | } 52 | h5 { 53 | font-size: 13px; 54 | line-height: 26px; 55 | padding: 14px 0 0; 56 | font-weight: bold; 57 | text-transform: uppercase; 58 | } 59 | h6 { 60 | color: #666; 61 | font-size: 14px; 62 | line-height: 26px; 63 | padding: 18px 0 0; 64 | font-weight: normal; 65 | font-variant: italic; 66 | } 67 | br+br { 68 | line-height:0; 69 | height:0; 70 | display:none; 71 | } 72 | p { 73 | margin: 1em 0; 74 | } 75 | blockquote { 76 | margin: 14px 0; 77 | border-left: 4px solid #DDD; 78 | padding-left: 11px; 79 | color: #555; 80 | } 81 | pre, code { 82 | font-family: 'Bitstream Vera Sans Mono','Courier',monospace; 83 | } 84 | pre { 85 | background-color: #F8F8F8; 86 | font-size: 13px; 87 | line-height: 19px; 88 | overflow: auto; 89 | padding: 0px 5px; 90 | border-radius: 3px; 91 | color: black; 92 | } 93 | code { 94 | margin: 0 2px; 95 | padding: 2px 5px; 96 | white-space: nowrap; 97 | border: 1px solid #CCC; 98 | background-color: #F8F8F8; 99 | border-radius: 3px; 100 | font-size: 12px !important; 101 | } 102 | pre > code { 103 | margin: 0px; 104 | padding: 0px; 105 | white-space: pre; 106 | 107 | border: none; 108 | background-color: transparent; 109 | border-radius: 0; 110 | } 111 | a, a code { 112 | color: #4183C4; 113 | text-decoration:none; 114 | } 115 | a:hover, a code:hover { 116 | text-decoration:underline; 117 | } 118 | table { 119 | border-collapse: collapse; 120 | margin: 10px 0 0; 121 | padding: 0; 122 | width: 100%; 123 | } 124 | table tr th, table tr td { 125 | border: 1px solid #CCC; 126 | text-align: left; 127 | margin: 0; 128 | padding: 4px 4px; 129 | } 130 | table tbody tr:nth-child(2n-1) { 131 | background-color: #F8F8F8; 132 | } 133 | 134 | .c{color:#998;font-style:italic;} 135 | .err{color:#a61717;background-color:#e3d2d2;} 136 | .k{font-weight:bold;} 137 | .o{font-weight:bold;} 138 | .cm{color:#998;font-style:italic;} 139 | .cp{color:#999;font-weight:bold;} 140 | .c1{color:#998;font-style:italic;} 141 | .cs{color:#999;font-weight:bold;font-style:italic;} 142 | .gd{color:#000;background-color:#fdd;} 143 | .gd .x{color:#000;background-color:#faa;} 144 | .ge{font-style:italic;} 145 | .gr{color:#a00;} 146 | .gh{color:#999;} 147 | .gi{color:#000;background-color:#dfd;} 148 | .gi .x{color:#000;background-color:#afa;} 149 | .go{color:#888;} 150 | .gp{color:#555;} 151 | .gs{font-weight:bold;} 152 | .gu{color:#800080;font-weight:bold;} 153 | .gt{color:#a00;} 154 | .kc{font-weight:bold;} 155 | .kd{font-weight:bold;} 156 | .kn{font-weight:bold;} 157 | .kp{font-weight:bold;} 158 | .kr{font-weight:bold;} 159 | .kt{color:#458;font-weight:bold;} 160 | .m{color:#099;} 161 | .s{color:#d14;} 162 | .na{color:#008080;} 163 | .nb{color:#0086B3;} 164 | .nc{color:#458;font-weight:bold;} 165 | .no{color:#008080;} 166 | .ni{color:#800080;} 167 | .ne{color:#900;font-weight:bold;} 168 | .nf{color:#900;font-weight:bold;} 169 | .nn{color:#555;} 170 | .nt{color:#000080;} 171 | .nv{color:#008080;} 172 | .ow{font-weight:bold;} 173 | .w{color:#bbb;} 174 | .mf{color:#099;} 175 | .mh{color:#099;} 176 | .mi{color:#099;} 177 | .mo{color:#099;} 178 | .sb{color:#d14;} 179 | .sc{color:#d14;} 180 | .sd{color:#d14;} 181 | .s2{color:#d14;} 182 | .se{color:#d14;} 183 | .sh{color:#d14;} 184 | .si{color:#d14;} 185 | .sx{color:#d14;} 186 | .sr{color:#009926;} 187 | .s1{color:#d14;} 188 | .ss{color:#990073;} 189 | .bp{color:#999;} 190 | .vc{color:#008080;} 191 | .vg{color:#008080;} 192 | .vi{color:#008080;} 193 | .il{color:#099;} 194 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index 6 | 7 | 8 | 9 |
10 |
11 | Fork me on GitHub 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var http = require('http'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var renderMd = require('./render-md'); 7 | var build = require('./build'); 8 | var hyperwatch = require('hyperwatch'); 9 | 10 | console.error(process.pid); 11 | process.on('SIGTERM', onSIGTERM); 12 | 13 | function onSIGTERM() { 14 | console.error('Caught SIGTERM, exiting'); 15 | server.close(); 16 | process.exit(0); 17 | } 18 | 19 | function serveError (res, err) { 20 | console.error(err); 21 | res.writeHead(500, { 'Content-Type': 'text/plain' }); 22 | res.end(err.toString()); 23 | } 24 | 25 | function serveIndex (res) { 26 | res.writeHead(200, { 'Content-Type': 'text/html' }); 27 | fs.createReadStream(path.join(__dirname, 'index.html')).pipe(res); 28 | } 29 | 30 | function serveBundle (res) { 31 | res.writeHead(200, { 'Content-Type': 'application/javascript' }); 32 | build().pipe(res); 33 | } 34 | 35 | function serveCss (res) { 36 | res.writeHead(200, { 'Content-Type': 'text/css' }); 37 | fs.createReadStream(path.join(__dirname, 'css', 'index.css')).pipe(res); 38 | } 39 | 40 | var server = http.createServer(function (req, res) { 41 | console.error('%s %s', req.method, req.url); 42 | if (req.url === '/') return serveIndex(res); 43 | if (req.url === '/bundle.js') return serveBundle(res); 44 | if (req.url === '/css/index.css') return serveCss(res); 45 | 46 | res.writeHead(404); 47 | res.end(); 48 | }); 49 | 50 | server.on('listening', function (address) { 51 | var a = server.address(); 52 | console.error('listening: http://%s:%d', a.address, a.port); 53 | }); 54 | server.listen(3000); 55 | 56 | hyperwatch(server); 57 | 58 | process.on('SIGTERM', function onsigterm() { 59 | // support cleanly exiting process in order to have all buffers flushed 60 | // this is important in situations when --perf-basic-prof is used to write symbol information 61 | server.close(); 62 | process.exit(0); 63 | }) 64 | -------------------------------------------------------------------------------- /init-brace.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ace = require('brace'); 4 | require('brace/mode/markdown'); 5 | require('brace/theme/monokai'); 6 | 7 | var go = module.exports = function (md) { 8 | var editor = ace.edit('editor'); 9 | editor.getSession().setMode('ace/mode/markdown'); 10 | editor.setTheme('ace/theme/monokai'); 11 | editor.setValue(md); 12 | editor.clearSelection(); 13 | return editor; 14 | }; 15 | -------------------------------------------------------------------------------- /init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var renderMd = require('./render-md'); 4 | var fs = require('fs'); 5 | var initEditor = require('./init-brace'); 6 | 7 | 8 | var go = module.exports = function () { 9 | var md = fs.readFileSync(__dirname + '/README.md').toString(); 10 | var rendered = document.getElementsByClassName('rendered')[0]; 11 | 12 | var editor = initEditor(md); 13 | 14 | function renderEdits () { 15 | var md = editor.getValue(); 16 | var html = renderMd(md); 17 | rendered.innerHTML = html; 18 | } 19 | 20 | renderEdits(); 21 | 22 | editor.on('change', renderEdits); 23 | }; 24 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | var hyperwatch = require('hyperwatch'); 2 | var init = require('./init'); 3 | 4 | init(); 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browserify-markdown-editor", 3 | "version": "0.0.0", 4 | "description": "A demo showing how to build a markdown editor with browserify and marked.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/thlorenz/browserify-markdown-editor.git" 12 | }, 13 | "homepage": "https://github.com/thlorenz/browserify-markdown-editor", 14 | "dependencies": { 15 | "brace": "^0.4.0", 16 | "brfs": "~1.2.0", 17 | "browserify": "~7.0.0", 18 | "hyperwatch": "~0.5.0", 19 | "marked": "~0.2.9", 20 | "peacock": "~0.2.0" 21 | }, 22 | "devDependencies": {}, 23 | "keywords": [], 24 | "author": { 25 | "name": "Thorsten Lorenz", 26 | "email": "thlorenz@gmx.de", 27 | "url": "http://thlorenz.com" 28 | }, 29 | "license": { 30 | "type": "MIT", 31 | "url": "https://github.com/thlorenz/browserify-markdown-editor/blob/master/LICENSE" 32 | }, 33 | "engine": { 34 | "node": ">=0.6" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /render-md.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var marked = require('marked'); 3 | var peacock = require('peacock'); 4 | 5 | marked.setOptions({ 6 | gfm : true 7 | , pedantic : false 8 | , sanitize : true 9 | , highlight : function (code, lang) { 10 | if (!lang) return code; 11 | try { 12 | return peacock.highlight(code); 13 | } catch (e) { 14 | return code; 15 | } 16 | } 17 | }) 18 | 19 | var go = module.exports = function (md) { 20 | return marked(md); 21 | }; 22 | --------------------------------------------------------------------------------