├── .gitignore ├── Procfile ├── README.md ├── package.json ├── public ├── script.js └── style.css ├── server.js └── views └── pad.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dump.rdb 4 | 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Realtime Markdown Editor 2 | 3 | ### What is this? 4 | 5 | Type your markdown into the box on the left and immediately see if on the box on the right. If you send a friend a link to this URL you both can edit the document at the same time! 6 | 7 | ### How to use this? 8 | 9 | Type anything after the slash in "https://realtimemarkdown.herokuapp.com/" and just start typing. If you don't feel typing in the address bar, feel free to go to one of these markdown pads: 10 | 11 | - [https://realtimemarkdown.herokuapp.com/sample](https://realtimemarkdown.herokuapp.com/sample) 12 | - [https://realtimemarkdown.herokuapp.com/my_project](https://realtimemarkdown.herokuapp.com/my_project) 13 | - [https://realtimemarkdown.herokuapp.com/whatever](https://realtimemarkdown.herokuapp.com/whatever) 14 | 15 | ### How was this built? 16 | 17 | This website uses the following to work: 18 | 19 | - [Showdown](https://github.com/showdownjs/showdown) - Converts markdown text to beautiful HTML 20 | - [ShareJS](http://sharejs.org/) - allows for realtime editing of this textbox 21 | - [Node.js](https://nodejs.org/) - backend framework 22 | - [Redis](http://redis.io/) - where we store our markdown documents 23 | - [Twitter Bootstrap](http://getbootstrap.com/) - makes everything a little prettier 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RealtimeMarkdownViewer", 3 | "description": "Realtime Markdown Viewer", 4 | "main": "server.js", 5 | "version": "1.0.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:sifxtreme/realtime-markdown.git" 9 | }, 10 | "keywords": [ 11 | "markdown", 12 | "realtime", 13 | "sharejs" 14 | ], 15 | "author": "Asif Ahmed ", 16 | "dependencies": { 17 | "express": "^4.12.4", 18 | "ejs": "^2.3.1", 19 | "redis": "^0.10.3", 20 | "share": "0.6.3" 21 | }, 22 | "engines": { 23 | "node": "0.10.x", 24 | "npm": "1.3.x" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /public/script.js: -------------------------------------------------------------------------------- 1 | /* public/script.js */ 2 | 3 | window.onload = function() { 4 | var converter = new showdown.Converter(); 5 | var pad = document.getElementById('pad'); 6 | var markdownArea = document.getElementById('markdown'); 7 | 8 | // make the tab act like a tab 9 | pad.addEventListener('keydown',function(e) { 10 | if(e.keyCode === 9) { // tab was pressed 11 | // get caret position/selection 12 | var start = this.selectionStart; 13 | var end = this.selectionEnd; 14 | 15 | var target = e.target; 16 | var value = target.value; 17 | 18 | // set textarea value to: text before caret + tab + text after caret 19 | target.value = value.substring(0, start) 20 | + "\t" 21 | + value.substring(end); 22 | 23 | // put caret at right position again (add one for the tab) 24 | this.selectionStart = this.selectionEnd = start + 1; 25 | 26 | // prevent the focus lose 27 | e.preventDefault(); 28 | } 29 | }); 30 | 31 | var previousMarkdownValue; 32 | 33 | // convert text area to markdown html 34 | var convertTextAreaToMarkdown = function(){ 35 | var markdownText = pad.value; 36 | previousMarkdownValue = markdownText; 37 | html = converter.makeHtml(markdownText); 38 | markdownArea.innerHTML = html; 39 | }; 40 | 41 | var didChangeOccur = function(){ 42 | if(previousMarkdownValue != pad.value){ 43 | return true; 44 | } 45 | return false; 46 | }; 47 | 48 | // check every second if the text area has changed 49 | setInterval(function(){ 50 | if(didChangeOccur()){ 51 | convertTextAreaToMarkdown(); 52 | } 53 | }, 1000); 54 | 55 | // convert textarea on input change 56 | pad.addEventListener('input', convertTextAreaToMarkdown); 57 | 58 | // ignore if on home page 59 | if(document.location.pathname.length < 2){ 60 | // implement share js 61 | sharejs.open(document.location.pathname, 'text', function(error, doc) { 62 | doc.attach_textarea(pad); 63 | convertTextAreaToMarkdown(); 64 | }); 65 | } 66 | 67 | // convert on page load 68 | convertTextAreaToMarkdown(); 69 | 70 | }; -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | /* public/style.css */ 2 | 3 | html, body, section, .full-height { 4 | height: 100%; 5 | } 6 | 7 | #pad{ 8 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 9 | 10 | border: none; 11 | overflow: auto; 12 | outline: none; 13 | resize: none; 14 | 15 | -webkit-box-shadow: none; 16 | -moz-box-shadow: none; 17 | box-shadow: none; 18 | } 19 | 20 | #markdown { 21 | overflow: auto; 22 | border-left: 1px solid black; 23 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /* server.js */ 2 | 3 | var express = require('express'); 4 | var app = express(); 5 | 6 | // set the view engine to ejs 7 | app.set('view engine', 'ejs'); 8 | 9 | // public folder to store assets 10 | app.use(express.static(__dirname + '/public')); 11 | 12 | // routes for app 13 | app.get('/', function(req, res) { 14 | res.render('pad'); 15 | }); 16 | app.get('/(:id)', function(req, res) { 17 | res.render('pad'); 18 | }); 19 | 20 | // get sharejs dependencies 21 | var sharejs = require('share'); 22 | 23 | // set up redis server 24 | var redisClient; 25 | console.log(process.env.REDISTOGO_URL); 26 | if (process.env.REDISTOGO_URL) { 27 | var rtg = require("url").parse(process.env.REDISTOGO_URL); 28 | redisClient = require("redis").createClient(rtg.port, rtg.hostname); 29 | redisClient.auth(rtg.auth.split(":")[1]); 30 | } else { 31 | redisClient = require("redis").createClient(); 32 | } 33 | 34 | // options for sharejs 35 | var options = { 36 | db: {type: 'redis', client: redisClient} 37 | }; 38 | 39 | // attach the express server to sharejs 40 | sharejs.server.attach(app, options); 41 | 42 | // listen on port 8000 (for localhost) or the port defined for heroku 43 | var port = process.env.PORT || 8000; 44 | app.listen(port); -------------------------------------------------------------------------------- /views/pad.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Realtime Markdown Viewer 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | --------------------------------------------------------------------------------