UNPKG

14.6 kBJavaScriptView Raw
1(function () {
2 /**
3 * Fork https://github.com/egoist/docute/blob/master/src/utils/yaml.js
4 */
5 /* eslint-disable */
6 /*
7 YAML parser for Javascript
8 Author: Diogo Costa
9 This program is released under the MIT License as follows:
10 Copyright (c) 2011 Diogo Costa (costa.h4evr@gmail.com)
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17 The above copyright notice and this permission notice shall be included in
18 all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 */
27
28 /**
29 * @name YAML
30 * @namespace
31 */
32
33 var errors = [],
34 reference_blocks = [],
35 processing_time = 0,
36 regex = {
37 regLevel: new RegExp('^([\\s\\-]+)'),
38 invalidLine: new RegExp('^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$'),
39 dashesString: new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),
40 quotesString: new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),
41 float: new RegExp('^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$'),
42 integer: new RegExp('^[+-]?[0-9]+$'),
43 array: new RegExp('\\[\\s*(.*)\\s*\\]'),
44 map: new RegExp('\\{\\s*(.*)\\s*\\}'),
45 key_value: new RegExp('([a-z0-9_-][ a-z0-9_-]*):( .+)', 'i'),
46 single_key_value: new RegExp('^([a-z0-9_-][ a-z0-9_-]*):( .+?)$', 'i'),
47 key: new RegExp('([a-z0-9_-][ a-z0-9_-]+):( .+)?', 'i'),
48 item: new RegExp('^-\\s+'),
49 trim: new RegExp('^\\s+|\\s+$'),
50 comment: new RegExp('([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?')
51 };
52
53 /**
54 * @class A block of lines of a given level.
55 * @param {int} lvl The block's level.
56 * @private
57 */
58 function Block(lvl) {
59 return {
60 /* The block's parent */
61 parent: null,
62 /* Number of children */
63 length: 0,
64 /* Block's level */
65 level: lvl,
66 /* Lines of code to process */
67 lines: [],
68 /* Blocks with greater level */
69 children: [],
70 /* Add a block to the children collection */
71 addChild: function(obj) {
72 this.children.push(obj);
73 obj.parent = this;
74 ++this.length;
75 }
76 }
77 }
78
79 function parser(str) {
80 var regLevel = regex['regLevel'];
81 var invalidLine = regex['invalidLine'];
82 var lines = str.split('\n');
83 var m;
84 var level = 0,
85 curLevel = 0;
86
87 var blocks = [];
88
89 var result = new Block(-1);
90 var currentBlock = new Block(0);
91 result.addChild(currentBlock);
92 var levels = [];
93 var line = '';
94
95 blocks.push(currentBlock);
96 levels.push(level);
97
98 for (var i = 0, len = lines.length; i < len; ++i) {
99 line = lines[i];
100
101 if (line.match(invalidLine)) {
102 continue
103 }
104
105 if ((m = regLevel.exec(line))) {
106 level = m[1].length;
107 } else { level = 0; }
108
109 if (level > curLevel) {
110 var oldBlock = currentBlock;
111 currentBlock = new Block(level);
112 oldBlock.addChild(currentBlock);
113 blocks.push(currentBlock);
114 levels.push(level);
115 } else if (level < curLevel) {
116 var added = false;
117
118 var k = levels.length - 1;
119 for (; k >= 0; --k) {
120 if (levels[k] == level) {
121 currentBlock = new Block(level);
122 blocks.push(currentBlock);
123 levels.push(level);
124 if (blocks[k].parent != null) { blocks[k].parent.addChild(currentBlock); }
125 added = true;
126 break
127 }
128 }
129
130 if (!added) {
131 errors.push('Error: Invalid indentation at line ' + i + ': ' + line);
132 return
133 }
134 }
135
136 currentBlock.lines.push(line.replace(regex['trim'], ''));
137 curLevel = level;
138 }
139
140 return result
141 }
142
143 function processValue(val) {
144 val = val.replace(regex['trim'], '');
145 var m = null;
146
147 if (val == 'true') {
148 return true
149 } else if (val == 'false') {
150 return false
151 } else if (val == '.NaN') {
152 return Number.NaN
153 } else if (val == 'null') {
154 return null
155 } else if (val == '.inf') {
156 return Number.POSITIVE_INFINITY
157 } else if (val == '-.inf') {
158 return Number.NEGATIVE_INFINITY
159 } else if ((m = val.match(regex['dashesString']))) {
160 return m[1]
161 } else if ((m = val.match(regex['quotesString']))) {
162 return m[1]
163 } else if ((m = val.match(regex['float']))) {
164 return parseFloat(m[0])
165 } else if ((m = val.match(regex['integer']))) {
166 return parseInt(m[0])
167 } else if (!isNaN((m = Date.parse(val)))) {
168 return new Date(m)
169 } else if ((m = val.match(regex['single_key_value']))) {
170 var res = {};
171 res[m[1]] = processValue(m[2]);
172 return res
173 } else if ((m = val.match(regex['array']))) {
174 var count = 0,
175 c = ' ';
176 var res = [];
177 var content = '';
178 var str = false;
179 for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
180 c = m[1][j];
181 if (c == "'" || c == '"') {
182 if (str === false) {
183 str = c;
184 content += c;
185 continue
186 } else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
187 str = false;
188 content += c;
189 continue
190 }
191 } else if (str === false && (c == '[' || c == '{')) {
192 ++count;
193 } else if (str === false && (c == ']' || c == '}')) {
194 --count;
195 } else if (str === false && count == 0 && c == ',') {
196 res.push(processValue(content));
197 content = '';
198 continue
199 }
200
201 content += c;
202 }
203
204 if (content.length > 0) { res.push(processValue(content)); }
205 return res
206 } else if ((m = val.match(regex['map']))) {
207 var count = 0,
208 c = ' ';
209 var res = [];
210 var content = '';
211 var str = false;
212 for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
213 c = m[1][j];
214 if (c == "'" || c == '"') {
215 if (str === false) {
216 str = c;
217 content += c;
218 continue
219 } else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
220 str = false;
221 content += c;
222 continue
223 }
224 } else if (str === false && (c == '[' || c == '{')) {
225 ++count;
226 } else if (str === false && (c == ']' || c == '}')) {
227 --count;
228 } else if (str === false && count == 0 && c == ',') {
229 res.push(content);
230 content = '';
231 continue
232 }
233
234 content += c;
235 }
236
237 if (content.length > 0) { res.push(content); }
238
239 var newRes = {};
240 for (var j = 0, lenJ = res.length; j < lenJ; ++j) {
241 if ((m = res[j].match(regex['key_value']))) {
242 newRes[m[1]] = processValue(m[2]);
243 }
244 }
245
246 return newRes
247 } else { return val }
248 }
249
250 function processFoldedBlock(block) {
251 var lines = block.lines;
252 var children = block.children;
253 var str = lines.join(' ');
254 var chunks = [str];
255 for (var i = 0, len = children.length; i < len; ++i) {
256 chunks.push(processFoldedBlock(children[i]));
257 }
258 return chunks.join('\n')
259 }
260
261 function processLiteralBlock(block) {
262 var lines = block.lines;
263 var children = block.children;
264 var str = lines.join('\n');
265 for (var i = 0, len = children.length; i < len; ++i) {
266 str += processLiteralBlock(children[i]);
267 }
268 return str
269 }
270
271 function processBlock(blocks) {
272 var m = null;
273 var res = {};
274 var lines = null;
275 var children = null;
276 var currentObj = null;
277
278 var level = -1;
279
280 var processedBlocks = [];
281
282 var isMap = true;
283
284 for (var j = 0, lenJ = blocks.length; j < lenJ; ++j) {
285 if (level != -1 && level != blocks[j].level) { continue }
286
287 processedBlocks.push(j);
288
289 level = blocks[j].level;
290 lines = blocks[j].lines;
291 children = blocks[j].children;
292 currentObj = null;
293
294 for (var i = 0, len = lines.length; i < len; ++i) {
295 var line = lines[i];
296
297 if ((m = line.match(regex['key']))) {
298 var key = m[1];
299
300 if (key[0] == '-') {
301 key = key.replace(regex['item'], '');
302 if (isMap) {
303 isMap = false;
304 if (typeof res.length === 'undefined') {
305 res = [];
306 }
307 }
308 if (currentObj != null) { res.push(currentObj); }
309 currentObj = {};
310 isMap = true;
311 }
312
313 if (typeof m[2] != 'undefined') {
314 var value = m[2].replace(regex['trim'], '');
315 if (value[0] == '&') {
316 var nb = processBlock(children);
317 if (currentObj != null) { currentObj[key] = nb; }
318 else { res[key] = nb; }
319 reference_blocks[value.substr(1)] = nb;
320 } else if (value[0] == '|') {
321 if (currentObj != null)
322 { currentObj[key] = processLiteralBlock(children.shift()); }
323 else { res[key] = processLiteralBlock(children.shift()); }
324 } else if (value[0] == '*') {
325 var v = value.substr(1);
326 var no = {};
327
328 if (typeof reference_blocks[v] == 'undefined') {
329 errors.push("Reference '" + v + "' not found!");
330 } else {
331 for (var k in reference_blocks[v]) {
332 no[k] = reference_blocks[v][k];
333 }
334
335 if (currentObj != null) { currentObj[key] = no; }
336 else { res[key] = no; }
337 }
338 } else if (value[0] == '>') {
339 if (currentObj != null)
340 { currentObj[key] = processFoldedBlock(children.shift()); }
341 else { res[key] = processFoldedBlock(children.shift()); }
342 } else {
343 if (currentObj != null) { currentObj[key] = processValue(value); }
344 else { res[key] = processValue(value); }
345 }
346 } else {
347 if (currentObj != null) { currentObj[key] = processBlock(children); }
348 else { res[key] = processBlock(children); }
349 }
350 } else if (line.match(/^-\s*$/)) {
351 if (isMap) {
352 isMap = false;
353 if (typeof res.length === 'undefined') {
354 res = [];
355 }
356 }
357 if (currentObj != null) { res.push(currentObj); }
358 currentObj = {};
359 isMap = true;
360 continue
361 } else if ((m = line.match(/^-\s*(.*)/))) {
362 if (currentObj != null) { currentObj.push(processValue(m[1])); }
363 else {
364 if (isMap) {
365 isMap = false;
366 if (typeof res.length === 'undefined') {
367 res = [];
368 }
369 }
370 res.push(processValue(m[1]));
371 }
372 continue
373 }
374 }
375
376 if (currentObj != null) {
377 if (isMap) {
378 isMap = false;
379 if (typeof res.length === 'undefined') {
380 res = [];
381 }
382 }
383 res.push(currentObj);
384 }
385 }
386
387 for (var j = processedBlocks.length - 1; j >= 0; --j) {
388 blocks.splice.call(blocks, processedBlocks[j], 1);
389 }
390
391 return res
392 }
393
394 function semanticAnalysis(blocks) {
395 var res = processBlock(blocks.children);
396 return res
397 }
398
399 function preProcess(src) {
400 var m;
401 var lines = src.split('\n');
402
403 var r = regex['comment'];
404
405 for (var i in lines) {
406 if ((m = lines[i].match(r))) {
407 /* var cmt = "";
408 if(typeof m[3] != "undefined")
409 lines[i] = m[1];
410 else if(typeof m[3] != "undefined")
411 lines[i] = m[3];
412 else
413 lines[i] = "";
414 */
415 if (typeof m[3] !== 'undefined') {
416 lines[i] = m[0].substr(0, m[0].length - m[3].length);
417 }
418 }
419 }
420
421 return lines.join('\n')
422 }
423
424 function load(str) {
425 errors = [];
426 reference_blocks = [];
427 processing_time = new Date().getTime();
428 var pre = preProcess(str);
429 var doc = parser(pre);
430 var res = semanticAnalysis(doc);
431 processing_time = new Date().getTime() - processing_time;
432
433 return res
434 }
435
436 /**
437 * Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js
438 */
439
440 var optionalByteOrderMark = '\\ufeff?';
441 var pattern =
442 '^(' +
443 optionalByteOrderMark +
444 '(= yaml =|---)' +
445 '$([\\s\\S]*?)' +
446 '(?:\\2|\\.\\.\\.)' +
447 '$' +
448 '' +
449 '(?:\\n)?)';
450 // NOTE: If this pattern uses the 'g' flag the `regex` variable definition will
451 // need to be moved down into the functions that use it.
452 var regex$1 = new RegExp(pattern, 'm');
453
454 function extractor(string) {
455 string = string || '';
456
457 var lines = string.split(/(\r?\n)/);
458 if (lines[0] && /= yaml =|---/.test(lines[0])) {
459 return parse(string)
460 } else {
461 return { attributes: {}, body: string }
462 }
463 }
464
465 function parse(string) {
466 var match = regex$1.exec(string);
467
468 if (!match) {
469 return {
470 attributes: {},
471 body: string
472 }
473 }
474
475 var yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '');
476 var attributes = load(yaml) || {};
477 var body = string.replace(match[0], '');
478
479 return { attributes: attributes, body: body, frontmatter: yaml }
480 }
481
482 var install = function (hook, vm) {
483 // Used to remove front matter from embedded pages if installed.
484 vm.config.frontMatter = {};
485 vm.config.frontMatter.installed = true;
486 vm.config.frontMatter.parseMarkdown = function (content) {
487 var ref = extractor(content);
488 var body = ref.body;
489 return body;
490 };
491
492 hook.beforeEach(function (content) {
493 var ref = extractor(content);
494 var attributes = ref.attributes;
495 var body = ref.body;
496
497 vm.frontmatter = attributes;
498
499 return body;
500 });
501 };
502
503 $docsify.plugins = [].concat(install, $docsify.plugins);
504
505}());