├── LICENSE ├── README.md ├── index.html └── stdm-react.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Christopher Chedeau 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 | markdown-react 2 | ============== 3 | 4 | React Render for Standard Markdown 5 | 6 | ```javascript 7 | /** @jsx React.DOM */ 8 | var parser = new stmd.DocParser(); 9 | var renderer = new stmdReact.ReactRenderer(); 10 | 11 | React.renderComponent( 12 |
{renderer.render(parser.parse('Hello *world*'))}
, 13 | document.body 14 | ); 15 | ``` 16 | 17 | 18 | Demo: http://vjeux.github.io/markdown-react/ 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 12 | 13 | 60 | -------------------------------------------------------------------------------- /stdm-react.js: -------------------------------------------------------------------------------- 1 | // Basic usage: 2 | // 3 | // var stmd = require('stmd'); 4 | // var parser = new stmd.DocParser(); 5 | // var renderer = new stmdReact.ReactRenderer(); 6 | // console.log(renderer.render(parser.parse('Hello *world*'))); 7 | 8 | (function(exports) { 9 | 10 | // Helper function to produce content in a pair of HTML tags. 11 | var inTags = function(tag, attribs, contents, selfclosing) { 12 | var props = {}; 13 | if (attribs) { 14 | var i = 0; 15 | var attrib; 16 | while ((attrib = attribs[i]) !== undefined) { 17 | props[attrib[0]] = attrib[1]; 18 | i++; 19 | } 20 | } 21 | 22 | return React.DOM[tag](props, contents); 23 | }; 24 | 25 | // Render an inline element as HTML. 26 | var renderInline = function(inline) { 27 | var attrs; 28 | switch (inline.t) { 29 | case 'Str': 30 | return this.escape(inline.c); 31 | case 'Softbreak': 32 | return this.softbreak; 33 | case 'Hardbreak': 34 | return [inTags('br',[],null,true), '\n']; 35 | case 'Emph': 36 | return inTags('em', [], this.renderInlines(inline.c)); 37 | case 'Strong': 38 | return inTags('strong', [], this.renderInlines(inline.c)); 39 | case 'Html': 40 | return inline.c; 41 | case 'Entity': 42 | return inline.c; 43 | case 'Link': 44 | attrs = [['href', this.escape(inline.destination, true)]]; 45 | if (inline.title) { 46 | attrs.push(['title', this.escape(inline.title, true)]); 47 | } 48 | return inTags('a', attrs, this.renderInlines(inline.label)); 49 | case 'Image': 50 | attrs = [['src', this.escape(inline.destination, true)], 51 | ['alt', this.escape(this.renderInlines(inline.label))]]; 52 | if (inline.title) { 53 | attrs.push(['title', this.escape(inline.title, true)]); 54 | } 55 | return inTags('img', attrs, null, true); 56 | case 'Code': 57 | return inTags('code', [], this.escape(inline.c)); 58 | default: 59 | console.log("Uknown inline type " + inline.t); 60 | return null; 61 | } 62 | }; 63 | 64 | // Render a list of inlines. 65 | var renderInlines = function(inlines) { 66 | var result = []; 67 | for (var i=0; i < inlines.length; i++) { 68 | result.push(this.renderInline(inlines[i])); 69 | } 70 | return result; 71 | }; 72 | 73 | // Render a single block element. 74 | var renderBlock = function(block, in_tight_list) { 75 | var tag; 76 | var attr; 77 | var info_words; 78 | switch (block.t) { 79 | case 'Document': 80 | var whole_doc = this.renderBlocks(block.children); 81 | return (whole_doc === '' ? null : [whole_doc, '\n']); 82 | case 'Paragraph': 83 | if (in_tight_list) { 84 | return this.renderInlines(block.inline_content); 85 | } else { 86 | return inTags('p', [], this.renderInlines(block.inline_content));; 87 | } 88 | break; 89 | case 'BlockQuote': 90 | var filling = this.renderBlocks(block.children); 91 | return inTags('blockquote', [], filling === '' ? this.innersep : 92 | [this.innersep, this.renderBlocks(block.children), this.innersep]); 93 | case 'ListItem': 94 | return inTags('li', [], this.renderBlocks(block.children, in_tight_list));//.trim()); 95 | case 'List': 96 | tag = block.list_data.type == 'Bullet' ? 'ul' : 'ol'; 97 | attr = (!block.list_data.start || block.list_data.start == 1) ? 98 | [] : [['start', block.list_data.start.toString()]]; 99 | return inTags(tag, attr, [this.innersep, 100 | this.renderBlocks(block.children, block.tight), 101 | this.innersep]); 102 | case 'ATXHeader': 103 | case 'SetextHeader': 104 | tag = 'h' + block.level; 105 | return inTags(tag, [], this.renderInlines(block.inline_content)); 106 | case 'IndentedCode': 107 | return inTags('pre', [], 108 | inTags('code', [], this.escape(block.string_content))); 109 | case 'FencedCode': 110 | info_words = block.info.split(/ +/); 111 | attr = info_words.length === 0 || info_words[0].length === 0 ? 112 | [] : [['class',this.escape(info_words[0],true)]]; 113 | return inTags('pre', attr, 114 | inTags('code', [], this.escape(block.string_content))); 115 | case 'HtmlBlock': 116 | return block.string_content; 117 | case 'ReferenceDef': 118 | return null; 119 | case 'HorizontalRule': 120 | return inTags('hr',[],null,true); 121 | default: 122 | console.log("Uknown block type " + block.t); 123 | return null; 124 | } 125 | }; 126 | 127 | // Render a list of block elements, separated by this.blocksep. 128 | var renderBlocks = function(blocks, in_tight_list) { 129 | var result = []; 130 | for (var i=0; i < blocks.length; i++) { 131 | if (blocks[i].t !== 'ReferenceDef') { 132 | result.push(this.renderBlock(blocks[i], in_tight_list)); 133 | if (i !== blocks.length - 1) { 134 | result.push(this.blocksep); 135 | } 136 | } 137 | } 138 | return result; 139 | }; 140 | 141 | // The ReactRenderer object. 142 | function ReactRenderer(){ 143 | return { 144 | // default options: 145 | blocksep: '\n', // space between blocks 146 | innersep: '\n', // space between block container tag and contents 147 | softbreak: '\n', // by default, soft breaks are rendered as newlines in HTML 148 | // set to "
" to make them hard breaks 149 | // set to " " if you want to ignore line wrapping in source 150 | escape: function(s, preserve_entities) { 151 | return s; 152 | }, 153 | renderInline: renderInline, 154 | renderInlines: renderInlines, 155 | renderBlock: renderBlock, 156 | renderBlocks: renderBlocks, 157 | render: renderBlock 158 | }; 159 | } 160 | 161 | exports.ReactRenderer = ReactRenderer; 162 | 163 | })(typeof exports === 'undefined' ? this.stmdReact = {} : exports); 164 | --------------------------------------------------------------------------------