| 1 | | /* |
| 2 | | * @author: Hsiaoming Yang |
| 3 | | */ |
| 4 | | |
| 5 | | |
| 6 | | function parse(code) { |
| 7 | 13 | code = code + '\n'; |
| 8 | 13 | var lines = code.split(/\r\n|\r|\n/); |
| 9 | 13 | var sections = []; |
| 10 | | |
| 11 | 13 | var comments = []; |
| 12 | 13 | var inComment = false, section; |
| 13 | 13 | lines.forEach(function(line) { |
| 14 | | // comment block |
| 15 | 195 | if (/^\/\*/.test(line)) { |
| 16 | 9 | inComment = true; |
| 17 | | } |
| 18 | 195 | if (/\*\/$/.test(line)) { |
| 19 | 9 | inComment = false; |
| 20 | 9 | line = line.replace(/\*\/$/, ''); |
| 21 | 9 | comments.push(cleanLine(line)); |
| 22 | 9 | return; |
| 23 | | } |
| 24 | | |
| 25 | 186 | if (/^\/\//.test(line)) { |
| 26 | 33 | comments.push(cleanLine(line)); |
| 27 | 33 | return; |
| 28 | | } |
| 29 | | |
| 30 | 153 | if (inComment) { |
| 31 | 80 | comments.push(cleanLine(line)); |
| 32 | 80 | return; |
| 33 | | } |
| 34 | | |
| 35 | | // it is not comment now |
| 36 | 73 | if (comments.length) { |
| 37 | 13 | section = parseSection(comments); |
| 38 | 13 | if (section) { |
| 39 | 10 | sections.push(section); |
| 40 | | } |
| 41 | | // reset storage |
| 42 | 13 | comments = []; |
| 43 | | } |
| 44 | | }); |
| 45 | 13 | return sections; |
| 46 | | } |
| 47 | 1 | module.exports = parse; |
| 48 | | |
| 49 | | |
| 50 | | function parseSection(lines) { |
| 51 | 13 | var name, title, description = [], modifiers = [], examples = []; |
| 52 | | |
| 53 | 13 | var parsed, exampleStart, exampleFile; |
| 54 | 13 | lines.forEach(function(line) { |
| 55 | | // example file is the last line of comment |
| 56 | 130 | if (exampleFile) return; |
| 57 | | |
| 58 | 114 | if (!name && line.trim()) { |
| 59 | 13 | parsed = parseTitle(line); |
| 60 | 13 | if (parsed) { |
| 61 | 10 | name = parsed.name; |
| 62 | 10 | title = parsed.title; |
| 63 | 10 | return; |
| 64 | | } |
| 65 | | } |
| 66 | 107 | if (!name) return; |
| 67 | | |
| 68 | | // parse modifier |
| 69 | | // :hover - Button when hover |
| 70 | 101 | if (/^((?:\:|\.)[a-zA-Z0-9\:]+)\s+\-\s+(.*)$/.exec(line)) { |
| 71 | 27 | return modifiers.push({name: RegExp.$1, description: RegExp.$2}); |
| 72 | | } |
| 73 | | |
| 74 | 74 | if (/^Examples\:/.test(line.trim())) { |
| 75 | 9 | if (line.trim() === 'Examples:') { |
| 76 | 6 | exampleStart = true; |
| 77 | | } else { |
| 78 | 3 | exampleFile = line.trim().replace(/^Examples\:/, '').trim() |
| 79 | | } |
| 80 | 9 | return; |
| 81 | | } |
| 82 | | |
| 83 | 65 | if (!exampleStart && /^\s{2,}/.test(line)) { |
| 84 | 1 | exampleStart = true; |
| 85 | | } |
| 86 | 65 | if (exampleStart) { |
| 87 | 16 | examples.push(line); |
| 88 | 16 | return; |
| 89 | | } |
| 90 | | |
| 91 | 49 | description.push(line); |
| 92 | | }); |
| 93 | 16 | if (!name) return null; |
| 94 | | |
| 95 | 10 | var ret = {name: name, title: title, modifiers: modifiers}; |
| 96 | | |
| 97 | 10 | ret.description = description.join('\n').replace(/^\n*\s*/, ''); |
| 98 | | |
| 99 | 10 | if (exampleFile) { |
| 100 | 3 | ret.exampleFile = exampleFile; |
| 101 | | } else { |
| 102 | 7 | ret.html = formatCode(examples); |
| 103 | 7 | ret.examples = parseExamples(ret.html, modifiers); |
| 104 | | } |
| 105 | 10 | return ret; |
| 106 | | } |
| 107 | | |
| 108 | | |
| 109 | | function cleanLine(line) { |
| 110 | | // clean comment line |
| 111 | 122 | line = line.replace(/^\/\//, ''); |
| 112 | 122 | line = line.replace(/^\s*\*/, ''); |
| 113 | 122 | line = line.replace(/^\/\*/, ''); |
| 114 | 122 | line = line.replace(/\*\/$/, ''); |
| 115 | 122 | line = line.replace(/^\s{0,1}/, ''); |
| 116 | 122 | return line.trimRight(); |
| 117 | | } |
| 118 | | |
| 119 | | |
| 120 | | function parseTitle(line) { |
| 121 | | // the first line can be title |
| 122 | 13 | if (/^(\d+\.\d+(?:\.\d+)?)\s+(.*)$/.exec(line)) { |
| 123 | 10 | var name = RegExp.$1; |
| 124 | 10 | var title = RegExp.$2; |
| 125 | 10 | return {name: name, title: title}; |
| 126 | | } |
| 127 | 3 | return null; |
| 128 | | } |
| 129 | | |
| 130 | | function parseExamples(code, modifiers) { |
| 131 | | // <button class="classy {{modifier}}">Button</button> |
| 132 | 7 | var examples = []; |
| 133 | | |
| 134 | | // we should have an example without modifier |
| 135 | 7 | examples.push({ |
| 136 | | name: '', |
| 137 | | code: code.replace(/\{\{modifier\}\}/g, '') |
| 138 | | }); |
| 139 | | |
| 140 | 7 | if (!modifiers) return examples; |
| 141 | | |
| 142 | 7 | var name, value; |
| 143 | 7 | modifiers.forEach(function(modifier) { |
| 144 | 21 | name = modifier.name; |
| 145 | 21 | value = code.replace( |
| 146 | | /\{\{modifier\}\}/g, |
| 147 | | name.replace(':', ' pseudo-class-').replace('.', ' ') |
| 148 | | ); |
| 149 | 21 | examples.push({name: name, code: value}); |
| 150 | | }); |
| 151 | 7 | return examples; |
| 152 | | } |
| 153 | | |
| 154 | | function formatCode(lines) { |
| 155 | 7 | var indent; |
| 156 | 7 | var codes = []; |
| 157 | | |
| 158 | 7 | lines.forEach(function(line) { |
| 159 | 16 | if (indent === undefined && line.trim()) { |
| 160 | 7 | var match = line.match(/\S/); |
| 161 | 7 | if (match) { |
| 162 | 7 | indent = new RegExp('^\\s{' + match.index + '}'); |
| 163 | | } |
| 164 | | } |
| 165 | 16 | if (!indent) return; |
| 166 | 16 | codes.push(line.replace(indent, '')); |
| 167 | | }); |
| 168 | 7 | return codes.join('\n'); |
| 169 | | } |
| 170 | | |