scratch – Blame information for rev 133
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
75 | office | 1 | /* |
2 | Copyright (c) 2010 Jeremy Faivre |
||
3 | |||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy |
||
5 | of this software and associated documentation files (the "Software"), to deal |
||
6 | in the Software without restriction, including without limitation the rights |
||
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
8 | copies of the Software, and to permit persons to whom the Software is furnished |
||
9 | to do so, subject to the following conditions: |
||
10 | |||
11 | The above copyright notice and this permission notice shall be included in all |
||
12 | copies or substantial portions of the Software. |
||
13 | |||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||
20 | THE SOFTWARE. |
||
21 | */ |
||
22 | (function(){ |
||
23 | /** |
||
24 | * Exception class thrown when an error occurs during parsing. |
||
25 | * |
||
26 | * @author Fabien Potencier <fabien@symfony.com> |
||
27 | * |
||
28 | * @api |
||
29 | */ |
||
30 | |||
31 | /** |
||
32 | * Constructor. |
||
33 | * |
||
34 | * @param string message The error message |
||
35 | * @param integer parsedLine The line where the error occurred |
||
36 | * @param integer snippet The snippet of code near the problem |
||
37 | * @param string parsedFile The file name where the error occurred |
||
38 | */ |
||
39 | |||
40 | var YamlParseException = function(message, parsedLine, snippet, parsedFile){ |
||
41 | |||
42 | this.rawMessage = message; |
||
43 | this.parsedLine = (parsedLine !== undefined) ? parsedLine : -1; |
||
44 | this.snippet = (snippet !== undefined) ? snippet : null; |
||
45 | this.parsedFile = (parsedFile !== undefined) ? parsedFile : null; |
||
46 | |||
47 | this.updateRepr(); |
||
48 | |||
49 | this.message = message; |
||
50 | |||
51 | }; |
||
52 | YamlParseException.prototype = |
||
53 | { |
||
54 | |||
55 | name: 'YamlParseException', |
||
56 | message: null, |
||
57 | |||
58 | parsedFile: null, |
||
59 | parsedLine: -1, |
||
60 | snippet: null, |
||
61 | rawMessage: null, |
||
62 | |||
63 | isDefined: function(input) |
||
64 | { |
||
65 | return input != undefined && input != null; |
||
66 | }, |
||
67 | |||
68 | /** |
||
69 | * Gets the snippet of code near the error. |
||
70 | * |
||
71 | * @return string The snippet of code |
||
72 | */ |
||
73 | getSnippet: function() |
||
74 | { |
||
75 | return this.snippet; |
||
76 | }, |
||
77 | |||
78 | /** |
||
79 | * Sets the snippet of code near the error. |
||
80 | * |
||
81 | * @param string snippet The code snippet |
||
82 | */ |
||
83 | setSnippet: function(snippet) |
||
84 | { |
||
85 | this.snippet = snippet; |
||
86 | |||
87 | this.updateRepr(); |
||
88 | }, |
||
89 | |||
90 | /** |
||
91 | * Gets the filename where the error occurred. |
||
92 | * |
||
93 | * This method returns null if a string is parsed. |
||
94 | * |
||
95 | * @return string The filename |
||
96 | */ |
||
97 | getParsedFile: function() |
||
98 | { |
||
99 | return this.parsedFile; |
||
100 | }, |
||
101 | |||
102 | /** |
||
103 | * Sets the filename where the error occurred. |
||
104 | * |
||
105 | * @param string parsedFile The filename |
||
106 | */ |
||
107 | setParsedFile: function(parsedFile) |
||
108 | { |
||
109 | this.parsedFile = parsedFile; |
||
110 | |||
111 | this.updateRepr(); |
||
112 | }, |
||
113 | |||
114 | /** |
||
115 | * Gets the line where the error occurred. |
||
116 | * |
||
117 | * @return integer The file line |
||
118 | */ |
||
119 | getParsedLine: function() |
||
120 | { |
||
121 | return this.parsedLine; |
||
122 | }, |
||
123 | |||
124 | /** |
||
125 | * Sets the line where the error occurred. |
||
126 | * |
||
127 | * @param integer parsedLine The file line |
||
128 | */ |
||
129 | setParsedLine: function(parsedLine) |
||
130 | { |
||
131 | this.parsedLine = parsedLine; |
||
132 | |||
133 | this.updateRepr(); |
||
134 | }, |
||
135 | |||
136 | updateRepr: function() |
||
137 | { |
||
138 | this.message = this.rawMessage; |
||
139 | |||
140 | var dot = false; |
||
141 | if ('.' === this.message.charAt(this.message.length - 1)) { |
||
142 | this.message = this.message.substring(0, this.message.length - 1); |
||
143 | dot = true; |
||
144 | } |
||
145 | |||
146 | if (null !== this.parsedFile) { |
||
147 | this.message += ' in ' + JSON.stringify(this.parsedFile); |
||
148 | } |
||
149 | |||
150 | if (this.parsedLine >= 0) { |
||
151 | this.message += ' at line ' + this.parsedLine; |
||
152 | } |
||
153 | |||
154 | if (this.snippet) { |
||
155 | this.message += ' (near "' + this.snippet + '")'; |
||
156 | } |
||
157 | |||
158 | if (dot) { |
||
159 | this.message += '.'; |
||
160 | } |
||
161 | } |
||
162 | } |
||
163 | /** |
||
164 | * Yaml offers convenience methods to parse and dump YAML. |
||
165 | * |
||
166 | * @author Fabien Potencier <fabien@symfony.com> |
||
167 | * |
||
168 | * @api |
||
169 | */ |
||
170 | |||
171 | var YamlRunningUnderNode = false; |
||
172 | var Yaml = function(){}; |
||
173 | Yaml.prototype = |
||
174 | { |
||
175 | |||
176 | /** |
||
177 | * Parses YAML into a JS representation. |
||
178 | * |
||
179 | * The parse method, when supplied with a YAML stream (file), |
||
180 | * will do its best to convert YAML in a file into a JS representation. |
||
181 | * |
||
182 | * Usage: |
||
183 | * <code> |
||
184 | * obj = yaml.parseFile('config.yml'); |
||
185 | * </code> |
||
186 | * |
||
187 | * @param string input Path of YAML file |
||
188 | * |
||
189 | * @return array The YAML converted to a JS representation |
||
190 | * |
||
191 | * @throws YamlParseException If the YAML is not valid |
||
192 | */ |
||
193 | parseFile: function(file /* String */, callback /* Function */) |
||
194 | { |
||
195 | if ( callback == null ) |
||
196 | { |
||
197 | var input = this.getFileContents(file); |
||
198 | var ret = null; |
||
199 | try |
||
200 | { |
||
201 | ret = this.parse(input); |
||
202 | } |
||
203 | catch ( e ) |
||
204 | { |
||
205 | if ( e instanceof YamlParseException ) { |
||
206 | e.setParsedFile(file); |
||
207 | } |
||
208 | throw e; |
||
209 | } |
||
210 | return ret; |
||
211 | } |
||
212 | |||
213 | this.getFileContents(file, function(data) |
||
214 | { |
||
215 | callback(new Yaml().parse(data)); |
||
216 | }); |
||
217 | }, |
||
218 | |||
219 | /** |
||
220 | * Parses YAML into a JS representation. |
||
221 | * |
||
222 | * The parse method, when supplied with a YAML stream (string), |
||
223 | * will do its best to convert YAML into a JS representation. |
||
224 | * |
||
225 | * Usage: |
||
226 | * <code> |
||
227 | * obj = yaml.parse(...); |
||
228 | * </code> |
||
229 | * |
||
230 | * @param string input string containing YAML |
||
231 | * |
||
232 | * @return array The YAML converted to a JS representation |
||
233 | * |
||
234 | * @throws YamlParseException If the YAML is not valid |
||
235 | */ |
||
236 | parse: function(input /* String */) |
||
237 | { |
||
238 | var yaml = new YamlParser(); |
||
239 | |||
240 | return yaml.parse(input); |
||
241 | }, |
||
242 | |||
243 | /** |
||
244 | * Dumps a JS representation to a YAML string. |
||
245 | * |
||
246 | * The dump method, when supplied with an array, will do its best |
||
247 | * to convert the array into friendly YAML. |
||
248 | * |
||
249 | * @param array array JS representation |
||
250 | * @param integer inline The level where you switch to inline YAML |
||
251 | * |
||
252 | * @return string A YAML string representing the original JS representation |
||
253 | * |
||
254 | * @api |
||
255 | */ |
||
256 | dump: function(array, inline, spaces) |
||
257 | { |
||
258 | if ( inline == null ) inline = 2; |
||
259 | |||
260 | var yaml = new YamlDumper(); |
||
261 | if (spaces) { |
||
262 | yaml.numSpacesForIndentation = spaces; |
||
263 | } |
||
264 | |||
265 | return yaml.dump(array, inline); |
||
266 | }, |
||
267 | |||
268 | getXHR: function() |
||
269 | { |
||
270 | if ( window.XMLHttpRequest ) |
||
271 | return new XMLHttpRequest(); |
||
272 | |||
273 | if ( window.ActiveXObject ) |
||
274 | { |
||
275 | var names = [ |
||
276 | "Msxml2.XMLHTTP.6.0", |
||
277 | "Msxml2.XMLHTTP.3.0", |
||
278 | "Msxml2.XMLHTTP", |
||
279 | "Microsoft.XMLHTTP" |
||
280 | ]; |
||
281 | |||
282 | for ( var i = 0; i < 4; i++ ) |
||
283 | { |
||
284 | try{ return new ActiveXObject(names[i]); } |
||
285 | catch(e){} |
||
286 | } |
||
287 | } |
||
288 | return null; |
||
289 | }, |
||
290 | |||
291 | getFileContents: function(file, callback) |
||
292 | { |
||
293 | if ( YamlRunningUnderNode ) |
||
294 | { |
||
295 | var fs = require('fs'); |
||
296 | if ( callback == null ) |
||
297 | { |
||
298 | var data = fs.readFileSync(file); |
||
299 | if (data == null) return null; |
||
300 | return ''+data; |
||
301 | } |
||
302 | else |
||
303 | { |
||
304 | fs.readFile(file, function(err, data) |
||
305 | { |
||
306 | if (err) |
||
307 | callback(null); |
||
308 | else |
||
309 | callback(data); |
||
310 | }); |
||
311 | } |
||
312 | } |
||
313 | else |
||
314 | { |
||
315 | var request = this.getXHR(); |
||
316 | |||
317 | // Sync |
||
318 | if ( callback == null ) |
||
319 | { |
||
320 | request.open('GET', file, false); |
||
321 | request.send(null); |
||
322 | |||
323 | if ( request.status == 200 || request.status == 0 ) |
||
324 | return request.responseText; |
||
325 | |||
326 | return null; |
||
327 | } |
||
328 | |||
329 | // Async |
||
330 | request.onreadystatechange = function() |
||
331 | { |
||
332 | if ( request.readyState == 4 ) |
||
333 | if ( request.status == 200 || request.status == 0 ) |
||
334 | callback(request.responseText); |
||
335 | else |
||
336 | callback(null); |
||
337 | }; |
||
338 | request.open('GET', file, true); |
||
339 | request.send(null); |
||
340 | } |
||
341 | } |
||
342 | }; |
||
343 | |||
344 | var YAML = |
||
345 | { |
||
346 | /* |
||
347 | * @param integer inline The level where you switch to inline YAML |
||
348 | */ |
||
349 | |||
350 | stringify: function(input, inline, spaces) |
||
351 | { |
||
352 | return new Yaml().dump(input, inline, spaces); |
||
353 | }, |
||
354 | |||
355 | parse: function(input) |
||
356 | { |
||
357 | return new Yaml().parse(input); |
||
358 | }, |
||
359 | |||
360 | load: function(file, callback) |
||
361 | { |
||
362 | return new Yaml().parseFile(file, callback); |
||
363 | } |
||
364 | }; |
||
365 | |||
366 | // Handle node.js case |
||
367 | if (typeof exports !== 'undefined') { |
||
368 | if (typeof module !== 'undefined' && module.exports) { |
||
369 | exports = module.exports = YAML; |
||
370 | YamlRunningUnderNode = true; |
||
371 | |||
372 | // Add require handler |
||
373 | (function () { |
||
374 | var require_handler = function (module, filename) { |
||
375 | // fill in result |
||
376 | module.exports = YAML.load(filename); |
||
377 | }; |
||
378 | |||
379 | // register require extensions only if we're on node.js |
||
380 | // hack for browserify |
||
381 | if ( undefined !== require.extensions ) { |
||
382 | require.extensions['.yml'] = require_handler; |
||
383 | require.extensions['.yaml'] = require_handler; |
||
384 | } |
||
385 | }()); |
||
386 | } |
||
387 | } |
||
388 | |||
389 | // Handle browser case |
||
390 | if ( typeof(window) != "undefined" ) |
||
391 | { |
||
392 | window.YAML = YAML; |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * YamlInline implements a YAML parser/dumper for the YAML inline syntax. |
||
397 | */ |
||
398 | var YamlInline = function(){}; |
||
399 | YamlInline.prototype = |
||
400 | { |
||
401 | i: null, |
||
402 | |||
403 | /** |
||
404 | * Convert a YAML string to a JS object. |
||
405 | * |
||
406 | * @param string value A YAML string |
||
407 | * |
||
408 | * @return object A JS object representing the YAML string |
||
409 | */ |
||
410 | parse: function(value) |
||
411 | { |
||
412 | var result = null; |
||
413 | value = this.trim(value); |
||
414 | |||
415 | if ( 0 == value.length ) |
||
416 | { |
||
417 | return ''; |
||
418 | } |
||
419 | |||
420 | switch ( value.charAt(0) ) |
||
421 | { |
||
422 | case '[': |
||
423 | result = this.parseSequence(value); |
||
424 | break; |
||
425 | case '{': |
||
426 | result = this.parseMapping(value); |
||
427 | break; |
||
428 | default: |
||
429 | result = this.parseScalar(value); |
||
430 | } |
||
431 | |||
432 | // some comment can end the scalar |
||
433 | if ( value.substr(this.i+1).replace(/^\s*#.*$/, '') != '' ) { |
||
434 | console.log("oups "+value.substr(this.i+1)); |
||
435 | throw new YamlParseException('Unexpected characters near "'+value.substr(this.i)+'".'); |
||
436 | } |
||
437 | |||
438 | return result; |
||
439 | }, |
||
440 | |||
441 | /** |
||
442 | * Dumps a given JS variable to a YAML string. |
||
443 | * |
||
444 | * @param mixed value The JS variable to convert |
||
445 | * |
||
446 | * @return string The YAML string representing the JS object |
||
447 | */ |
||
448 | dump: function(value) |
||
449 | { |
||
450 | if ( undefined == value || null == value ) |
||
451 | return 'null'; |
||
452 | if ( value instanceof Date) |
||
453 | return value.toISOString(); |
||
454 | if ( typeof(value) == 'object') |
||
455 | return this.dumpObject(value); |
||
456 | if ( typeof(value) == 'boolean' ) |
||
457 | return value ? 'true' : 'false'; |
||
458 | if ( /^\d+$/.test(value) ) |
||
459 | return typeof(value) == 'string' ? "'"+value+"'" : parseInt(value); |
||
460 | if ( this.isNumeric(value) ) |
||
461 | return typeof(value) == 'string' ? "'"+value+"'" : parseFloat(value); |
||
462 | if ( typeof(value) == 'number' ) |
||
463 | return value == Infinity ? '.Inf' : ( value == -Infinity ? '-.Inf' : ( isNaN(value) ? '.NAN' : value ) ); |
||
464 | var yaml = new YamlEscaper(); |
||
465 | if ( yaml.requiresDoubleQuoting(value) ) |
||
466 | return yaml.escapeWithDoubleQuotes(value); |
||
467 | if ( yaml.requiresSingleQuoting(value) ) |
||
468 | return yaml.escapeWithSingleQuotes(value); |
||
469 | if ( '' == value ) |
||
470 | return '""'; |
||
471 | if ( this.getTimestampRegex().test(value) ) |
||
472 | return "'"+value+"'"; |
||
473 | if ( this.inArray(value.toLowerCase(), ['null','~','true','false']) ) |
||
474 | return "'"+value+"'"; |
||
475 | // default |
||
476 | return value; |
||
477 | }, |
||
478 | |||
479 | /** |
||
480 | * Dumps a JS object to a YAML string. |
||
481 | * |
||
482 | * @param object value The JS array to dump |
||
483 | * |
||
484 | * @return string The YAML string representing the JS object |
||
485 | */ |
||
486 | dumpObject: function(value) |
||
487 | { |
||
488 | var keys = this.getKeys(value); |
||
489 | var output = null; |
||
490 | var i; |
||
491 | var len = keys.length; |
||
492 | |||
493 | // array |
||
494 | if ( value instanceof Array ) |
||
495 | /*( 1 == len && '0' == keys[0] ) |
||
496 | || |
||
497 | ( len > 1 && this.reduceArray(keys, function(v,w){return Math.floor(v+w);}, 0) == len * (len - 1) / 2) )*/ |
||
498 | { |
||
499 | output = []; |
||
500 | for ( i = 0; i < len; i++ ) |
||
501 | { |
||
502 | output.push(this.dump(value[keys[i]])); |
||
503 | } |
||
504 | |||
505 | return '['+output.join(', ')+']'; |
||
506 | } |
||
507 | |||
508 | // mapping |
||
509 | output = []; |
||
510 | for ( i = 0; i < len; i++ ) |
||
511 | { |
||
512 | output.push(this.dump(keys[i])+': '+this.dump(value[keys[i]])); |
||
513 | } |
||
514 | |||
515 | return '{ '+output.join(', ')+' }'; |
||
516 | }, |
||
517 | |||
518 | /** |
||
519 | * Parses a scalar to a YAML string. |
||
520 | * |
||
521 | * @param scalar scalar |
||
522 | * @param string delimiters |
||
523 | * @param object stringDelimiters |
||
524 | * @param integer i |
||
525 | * @param boolean evaluate |
||
526 | * |
||
527 | * @return string A YAML string |
||
528 | * |
||
529 | * @throws YamlParseException When malformed inline YAML string is parsed |
||
530 | */ |
||
531 | parseScalar: function(scalar, delimiters, stringDelimiters, i, evaluate) |
||
532 | { |
||
533 | if ( delimiters == undefined ) delimiters = null; |
||
534 | if ( stringDelimiters == undefined ) stringDelimiters = ['"', "'"]; |
||
535 | if ( i == undefined ) i = 0; |
||
536 | if ( evaluate == undefined ) evaluate = true; |
||
537 | |||
538 | var output = null; |
||
539 | var pos = null; |
||
540 | var matches = null; |
||
541 | |||
542 | if ( this.inArray(scalar[i], stringDelimiters) ) |
||
543 | { |
||
544 | // quoted scalar |
||
545 | output = this.parseQuotedScalar(scalar, i); |
||
546 | i = this.i; |
||
547 | if (null !== delimiters) { |
||
548 | var tmp = scalar.substr(i).replace(/^\s+/, ''); |
||
549 | if (!this.inArray(tmp.charAt(0), delimiters)) { |
||
550 | throw new YamlParseException('Unexpected characters ('+scalar.substr(i)+').'); |
||
551 | } |
||
552 | } |
||
553 | } |
||
554 | else |
||
555 | { |
||
556 | // "normal" string |
||
557 | if ( !delimiters ) |
||
558 | { |
||
559 | output = (scalar+'').substring(i); |
||
560 | |||
561 | i += output.length; |
||
562 | |||
563 | // remove comments |
||
564 | pos = output.indexOf(' #'); |
||
565 | if ( pos != -1 ) |
||
566 | { |
||
567 | output = output.substr(0, pos).replace(/\s+$/g,''); |
||
568 | } |
||
569 | } |
||
570 | else if ( matches = new RegExp('^(.+?)('+delimiters.join('|')+')').exec((scalar+'').substring(i)) ) |
||
571 | { |
||
572 | output = matches[1]; |
||
573 | i += output.length; |
||
574 | } |
||
575 | else |
||
576 | { |
||
577 | throw new YamlParseException('Malformed inline YAML string ('+scalar+').'); |
||
578 | } |
||
579 | output = evaluate ? this.evaluateScalar(output) : output; |
||
580 | } |
||
581 | |||
582 | this.i = i; |
||
583 | |||
584 | return output; |
||
585 | }, |
||
586 | |||
587 | /** |
||
588 | * Parses a quoted scalar to YAML. |
||
589 | * |
||
590 | * @param string scalar |
||
591 | * @param integer i |
||
592 | * |
||
593 | * @return string A YAML string |
||
594 | * |
||
595 | * @throws YamlParseException When malformed inline YAML string is parsed |
||
596 | */ |
||
597 | parseQuotedScalar: function(scalar, i) |
||
598 | { |
||
599 | var matches = null; |
||
600 | //var item = /^(.*?)['"]\s*(?:[,:]|[}\]]\s*,)/.exec((scalar+'').substring(i))[1]; |
||
601 | |||
602 | if ( !(matches = new RegExp('^'+YamlInline.REGEX_QUOTED_STRING).exec((scalar+'').substring(i))) ) |
||
603 | { |
||
604 | throw new YamlParseException('Malformed inline YAML string ('+(scalar+'').substring(i)+').'); |
||
605 | } |
||
606 | |||
607 | var output = matches[0].substr(1, matches[0].length - 2); |
||
608 | |||
609 | var unescaper = new YamlUnescaper(); |
||
610 | |||
611 | if ( '"' == (scalar+'').charAt(i) ) |
||
612 | { |
||
613 | output = unescaper.unescapeDoubleQuotedString(output); |
||
614 | } |
||
615 | else |
||
616 | { |
||
617 | output = unescaper.unescapeSingleQuotedString(output); |
||
618 | } |
||
619 | |||
620 | i += matches[0].length; |
||
621 | |||
622 | this.i = i; |
||
623 | return output; |
||
624 | }, |
||
625 | |||
626 | /** |
||
627 | * Parses a sequence to a YAML string. |
||
628 | * |
||
629 | * @param string sequence |
||
630 | * @param integer i |
||
631 | * |
||
632 | * @return string A YAML string |
||
633 | * |
||
634 | * @throws YamlParseException When malformed inline YAML string is parsed |
||
635 | */ |
||
636 | parseSequence: function(sequence, i) |
||
637 | { |
||
638 | if ( i == undefined ) i = 0; |
||
639 | |||
640 | var output = []; |
||
641 | var len = sequence.length; |
||
642 | i += 1; |
||
643 | |||
644 | // [foo, bar, ...] |
||
645 | while ( i < len ) |
||
646 | { |
||
647 | switch ( sequence.charAt(i) ) |
||
648 | { |
||
649 | case '[': |
||
650 | // nested sequence |
||
651 | output.push(this.parseSequence(sequence, i)); |
||
652 | i = this.i; |
||
653 | break; |
||
654 | case '{': |
||
655 | // nested mapping |
||
656 | output.push(this.parseMapping(sequence, i)); |
||
657 | i = this.i; |
||
658 | break; |
||
659 | case ']': |
||
660 | this.i = i; |
||
661 | return output; |
||
662 | case ',': |
||
663 | case ' ': |
||
664 | break; |
||
665 | default: |
||
666 | var isQuoted = this.inArray(sequence.charAt(i), ['"', "'"]); |
||
667 | var value = this.parseScalar(sequence, [',', ']'], ['"', "'"], i); |
||
668 | i = this.i; |
||
669 | |||
670 | if ( !isQuoted && (value+'').indexOf(': ') != -1 ) |
||
671 | { |
||
672 | // embedded mapping? |
||
673 | try |
||
674 | { |
||
675 | value = this.parseMapping('{'+value+'}'); |
||
676 | } |
||
677 | catch ( e ) |
||
678 | { |
||
679 | if ( !(e instanceof YamlParseException ) ) throw e; |
||
680 | // no, it's not |
||
681 | } |
||
682 | } |
||
683 | |||
684 | output.push(value); |
||
685 | |||
686 | i--; |
||
687 | } |
||
688 | |||
689 | i++; |
||
690 | } |
||
691 | |||
692 | throw new YamlParseException('Malformed inline YAML string "'+sequence+'"'); |
||
693 | }, |
||
694 | |||
695 | /** |
||
696 | * Parses a mapping to a YAML string. |
||
697 | * |
||
698 | * @param string mapping |
||
699 | * @param integer i |
||
700 | * |
||
701 | * @return string A YAML string |
||
702 | * |
||
703 | * @throws YamlParseException When malformed inline YAML string is parsed |
||
704 | */ |
||
705 | parseMapping: function(mapping, i) |
||
706 | { |
||
707 | if ( i == undefined ) i = 0; |
||
708 | var output = {}; |
||
709 | var len = mapping.length; |
||
710 | i += 1; |
||
711 | var done = false; |
||
712 | var doContinue = false; |
||
713 | |||
714 | // {foo: bar, bar:foo, ...} |
||
715 | while ( i < len ) |
||
716 | { |
||
717 | doContinue = false; |
||
718 | |||
719 | switch ( mapping.charAt(i) ) |
||
720 | { |
||
721 | case ' ': |
||
722 | case ',': |
||
723 | i++; |
||
724 | doContinue = true; |
||
725 | break; |
||
726 | case '}': |
||
727 | this.i = i; |
||
728 | return output; |
||
729 | } |
||
730 | |||
731 | if ( doContinue ) continue; |
||
732 | |||
733 | // key |
||
734 | var key = this.parseScalar(mapping, [':', ' '], ['"', "'"], i, false); |
||
735 | i = this.i; |
||
736 | |||
737 | // value |
||
738 | done = false; |
||
739 | while ( i < len ) |
||
740 | { |
||
741 | switch ( mapping.charAt(i) ) |
||
742 | { |
||
743 | case '[': |
||
744 | // nested sequence |
||
745 | output[key] = this.parseSequence(mapping, i); |
||
746 | i = this.i; |
||
747 | done = true; |
||
748 | break; |
||
749 | case '{': |
||
750 | // nested mapping |
||
751 | output[key] = this.parseMapping(mapping, i); |
||
752 | i = this.i; |
||
753 | done = true; |
||
754 | break; |
||
755 | case ':': |
||
756 | case ' ': |
||
757 | break; |
||
758 | default: |
||
759 | output[key] = this.parseScalar(mapping, [',', '}'], ['"', "'"], i); |
||
760 | i = this.i; |
||
761 | done = true; |
||
762 | i--; |
||
763 | } |
||
764 | |||
765 | ++i; |
||
766 | |||
767 | if ( done ) |
||
768 | { |
||
769 | doContinue = true; |
||
770 | break; |
||
771 | } |
||
772 | } |
||
773 | |||
774 | if ( doContinue ) continue; |
||
775 | } |
||
776 | |||
777 | throw new YamlParseException('Malformed inline YAML string "'+mapping+'"'); |
||
778 | }, |
||
779 | |||
780 | /** |
||
781 | * Evaluates scalars and replaces magic values. |
||
782 | * |
||
783 | * @param string scalar |
||
784 | * |
||
785 | * @return string A YAML string |
||
786 | */ |
||
787 | evaluateScalar: function(scalar) |
||
788 | { |
||
789 | scalar = this.trim(scalar); |
||
790 | |||
791 | var raw = null; |
||
792 | var cast = null; |
||
793 | |||
794 | if ( ( 'null' == scalar.toLowerCase() ) || |
||
795 | ( '' == scalar ) || |
||
796 | ( '~' == scalar ) ) |
||
797 | return null; |
||
798 | if ( (scalar+'').indexOf('!str ') == 0 ) |
||
799 | return (''+scalar).substring(5); |
||
800 | if ( (scalar+'').indexOf('! ') == 0 ) |
||
801 | return parseInt(this.parseScalar((scalar+'').substr(2))); |
||
802 | if ( /^\d+$/.test(scalar) ) |
||
803 | { |
||
804 | raw = scalar; |
||
805 | cast = parseInt(scalar); |
||
806 | return '0' == scalar.charAt(0) ? this.octdec(scalar) : (( ''+raw == ''+cast ) ? cast : raw); |
||
807 | } |
||
808 | if ( 'true' == (scalar+'').toLowerCase() ) |
||
809 | return true; |
||
810 | if ( 'false' == (scalar+'').toLowerCase() ) |
||
811 | return false; |
||
812 | if ( this.isNumeric(scalar) ) |
||
813 | return '0x' == (scalar+'').substr(0, 2) ? this.hexdec(scalar) : parseFloat(scalar); |
||
814 | if ( scalar.toLowerCase() == '.inf' ) |
||
815 | return Infinity; |
||
816 | if ( scalar.toLowerCase() == '.nan' ) |
||
817 | return NaN; |
||
818 | if ( scalar.toLowerCase() == '-.inf' ) |
||
819 | return -Infinity; |
||
820 | if ( /^(-|\+)?[0-9,]+(\.[0-9]+)?$/.test(scalar) ) |
||
821 | return parseFloat(scalar.split(',').join('')); |
||
822 | if ( this.getTimestampRegex().test(scalar) ) |
||
823 | return new Date(this.strtotime(scalar)); |
||
824 | //else |
||
825 | return ''+scalar; |
||
826 | }, |
||
827 | |||
828 | /** |
||
829 | * Gets a regex that matches an unix timestamp |
||
830 | * |
||
831 | * @return string The regular expression |
||
832 | */ |
||
833 | getTimestampRegex: function() |
||
834 | { |
||
835 | return new RegExp('^'+ |
||
836 | '([0-9][0-9][0-9][0-9])'+ |
||
837 | '-([0-9][0-9]?)'+ |
||
838 | '-([0-9][0-9]?)'+ |
||
839 | '(?:(?:[Tt]|[ \t]+)'+ |
||
840 | '([0-9][0-9]?)'+ |
||
841 | ':([0-9][0-9])'+ |
||
842 | ':([0-9][0-9])'+ |
||
843 | '(?:\.([0-9]*))?'+ |
||
844 | '(?:[ \t]*(Z|([-+])([0-9][0-9]?)'+ |
||
845 | '(?::([0-9][0-9]))?))?)?'+ |
||
846 | '$','gi'); |
||
847 | }, |
||
848 | |||
849 | trim: function(str /* String */) |
||
850 | { |
||
851 | return (str+'').replace(/^\s+/,'').replace(/\s+$/,''); |
||
852 | }, |
||
853 | |||
854 | isNumeric: function(input) |
||
855 | { |
||
856 | return (input - 0) == input && input.length > 0 && input.replace(/\s+/g,'') != ''; |
||
857 | }, |
||
858 | |||
859 | inArray: function(key, tab) |
||
860 | { |
||
861 | var i; |
||
862 | var len = tab.length; |
||
863 | for ( i = 0; i < len; i++ ) |
||
864 | { |
||
865 | if ( key == tab[i] ) return true; |
||
866 | } |
||
867 | return false; |
||
868 | }, |
||
869 | |||
870 | getKeys: function(tab) |
||
871 | { |
||
872 | var ret = []; |
||
873 | |||
874 | for ( var name in tab ) |
||
875 | { |
||
876 | if ( tab.hasOwnProperty(name) ) |
||
877 | { |
||
878 | ret.push(name); |
||
879 | } |
||
880 | } |
||
881 | |||
882 | return ret; |
||
883 | }, |
||
884 | |||
885 | /*reduceArray: function(tab, fun) |
||
886 | { |
||
887 | var len = tab.length; |
||
888 | if (typeof fun != "function") |
||
889 | throw new YamlParseException("fun is not a function"); |
||
890 | |||
891 | // no value to return if no initial value and an empty array |
||
892 | if (len == 0 && arguments.length == 1) |
||
893 | throw new YamlParseException("empty array"); |
||
894 | |||
895 | var i = 0; |
||
896 | if (arguments.length >= 2) |
||
897 | { |
||
898 | var rv = arguments[1]; |
||
899 | } |
||
900 | else |
||
901 | { |
||
902 | do |
||
903 | { |
||
904 | if (i in tab) |
||
905 | { |
||
906 | rv = tab[i++]; |
||
907 | break; |
||
908 | } |
||
909 | |||
910 | // if array contains no values, no initial value to return |
||
911 | if (++i >= len) |
||
912 | throw new YamlParseException("no initial value to return"); |
||
913 | } |
||
914 | while (true); |
||
915 | } |
||
916 | |||
917 | for (; i < len; i++) |
||
918 | { |
||
919 | if (i in tab) |
||
920 | rv = fun.call(null, rv, tab[i], i, tab); |
||
921 | } |
||
922 | |||
923 | return rv; |
||
924 | },*/ |
||
925 | |||
926 | octdec: function(input) |
||
927 | { |
||
928 | return parseInt((input+'').replace(/[^0-7]/gi, ''), 8); |
||
929 | }, |
||
930 | |||
931 | hexdec: function(input) |
||
932 | { |
||
933 | input = this.trim(input); |
||
934 | if ( (input+'').substr(0, 2) == '0x' ) input = (input+'').substring(2); |
||
935 | return parseInt((input+'').replace(/[^a-f0-9]/gi, ''), 16); |
||
936 | }, |
||
937 | |||
938 | /** |
||
939 | * @see http://phpjs.org/functions/strtotime |
||
940 | * @note we need timestamp with msecs so /1000 removed |
||
941 | * @note original contained binary | 0 (wtf?!) everywhere, which messes everything up |
||
942 | */ |
||
943 | strtotime: function (h,b){var f,c,g,k,d="";h=(h+"").replace(/\s{2,}|^\s|\s$/g," ").replace(/[\t\r\n]/g,"");if(h==="now"){return b===null||isNaN(b)?new Date().getTime()||0:b||0}else{if(!isNaN(d=Date.parse(h))){return d||0}else{if(b){b=new Date(b)}else{b=new Date()}}}h=h.toLowerCase();var e={day:{sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},mon:["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]};var a=function(i){var o=(i[2]&&i[2]==="ago");var n=(n=i[0]==="last"?-1:1)*(o?-1:1);switch(i[0]){case"last":case"next":switch(i[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+n);break;case"wee":b.setDate(b.getDate()+(n*7));break;case"day":b.setDate(b.getDate()+n);break;case"hou":b.setHours(b.getHours()+n);break;case"min":b.setMinutes(b.getMinutes()+n);break;case"sec":b.setSeconds(b.getSeconds()+n);break;case"mon":if(i[1]==="month"){b.setMonth(b.getMonth()+n);break}default:var l=e.day[i[1].substring(0,3)];if(typeof l!=="undefined"){var p=l-b.getDay();if(p===0){p=7*n}else{if(p>0){if(i[0]==="last"){p-=7}}else{if(i[0]==="next"){p+=7}}}b.setDate(b.getDate()+p);b.setHours(0,0,0,0)}}break;default:if(/\d+/.test(i[0])){n*=parseInt(i[0],10);switch(i[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+n);break;case"mon":b.setMonth(b.getMonth()+n);break;case"wee":b.setDate(b.getDate()+(n*7));break;case"day":b.setDate(b.getDate()+n);break;case"hou":b.setHours(b.getHours()+n);break;case"min":b.setMinutes(b.getMinutes()+n);break;case"sec":b.setSeconds(b.getSeconds()+n);break}}else{return false}break}return true};g=h.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);if(g!==null){if(!g[2]){g[2]="00:00:00"}else{if(!g[3]){g[2]+=":00"}}k=g[1].split(/-/g);k[1]=e.mon[k[1]-1]||k[1];k[0]=+k[0];k[0]=(k[0]>=0&&k[0]<=69)?"20"+(k[0]<10?"0"+k[0]:k[0]+""):(k[0]>=70&&k[0]<=99)?"19"+k[0]:k[0]+"";return parseInt(this.strtotime(k[2]+" "+k[1]+" "+k[0]+" "+g[2])+(g[4]?g[4]:""),10)}var j="([+-]?\\d+\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)|(last|next)\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))(\\sago)?";g=h.match(new RegExp(j,"gi"));if(g===null){return false}for(f=0,c=g.length;f<c;f++){if(!a(g[f].split(" "))){return false}}return b.getTime()||0} |
||
944 | |||
945 | }; |
||
946 | |||
947 | /* |
||
948 | * @note uses only non-capturing sub-patterns (unlike PHP original) |
||
949 | */ |
||
950 | YamlInline.REGEX_QUOTED_STRING = '(?:"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\']*(?:\'\'[^\']*)*)\')'; |
||
951 | |||
952 | |||
953 | /** |
||
954 | * YamlParser parses YAML strings to convert them to JS objects |
||
955 | * (port of Yaml Symfony Component) |
||
956 | */ |
||
957 | var YamlParser = function(offset /* Integer */) |
||
958 | { |
||
959 | this.offset = (offset !== undefined) ? offset : 0; |
||
960 | }; |
||
961 | YamlParser.prototype = |
||
962 | { |
||
963 | offset: 0, |
||
964 | lines: [], |
||
965 | currentLineNb: -1, |
||
966 | currentLine: '', |
||
967 | refs: {}, |
||
968 | |||
969 | /** |
||
970 | * Parses a YAML string to a JS value. |
||
971 | * |
||
972 | * @param String value A YAML string |
||
973 | * |
||
974 | * @return mixed A JS value |
||
975 | */ |
||
976 | parse: function(value /* String */) |
||
977 | { |
||
978 | this.currentLineNb = -1; |
||
979 | this.currentLine = ''; |
||
980 | this.lines = this.cleanup(value).split("\n"); |
||
981 | |||
982 | var data = null; |
||
983 | var context = null; |
||
984 | |||
985 | while ( this.moveToNextLine() ) |
||
986 | { |
||
987 | if ( this.isCurrentLineEmpty() ) |
||
988 | { |
||
989 | continue; |
||
990 | } |
||
991 | |||
992 | // tab? |
||
993 | if ( this.currentLine.charAt(0) == '\t' ) |
||
994 | { |
||
995 | throw new YamlParseException('A YAML file cannot contain tabs as indentation.', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
996 | } |
||
997 | |||
998 | var isRef = false; |
||
999 | var isInPlace = false; |
||
1000 | var isProcessed = false; |
||
1001 | var values = null; |
||
1002 | var matches = null; |
||
1003 | var c = null; |
||
1004 | var parser = null; |
||
1005 | var block = null; |
||
1006 | var key = null; |
||
1007 | var parsed = null; |
||
1008 | var len = null; |
||
1009 | var reverse = null; |
||
1010 | |||
1011 | if ( values = /^\-((\s+)(.+?))?\s*$/.exec(this.currentLine) ) |
||
1012 | { |
||
1013 | |||
1014 | if (context && 'mapping' == context) { |
||
1015 | throw new YamlParseException('You cannot define a sequence item when in a mapping', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1016 | } |
||
1017 | context = 'sequence'; |
||
1018 | |||
1019 | if ( !this.isDefined(data) ) data = []; |
||
1020 | //if ( !(data instanceof Array) ) throw new YamlParseException("Non array entry", this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1021 | |||
1022 | values = {leadspaces: values[2], value: values[3]}; |
||
1023 | |||
1024 | if ( this.isDefined(values.value) && ( matches = /^&([^ ]+) *(.*)/.exec(values.value) ) ) |
||
1025 | { |
||
1026 | matches = {ref: matches[1], value: matches[2]}; |
||
1027 | isRef = matches.ref; |
||
1028 | values.value = matches.value; |
||
1029 | } |
||
1030 | |||
1031 | // array |
||
1032 | if ( !this.isDefined(values.value) || '' == this.trim(values.value) || values.value.replace(/^ +/,'').charAt(0) == '#' ) |
||
1033 | { |
||
1034 | c = this.getRealCurrentLineNb() + 1; |
||
1035 | parser = new YamlParser(c); |
||
1036 | parser.refs = this.refs; |
||
1037 | data.push(parser.parse(this.getNextEmbedBlock())); |
||
1038 | this.refs = parser.refs; |
||
1039 | } |
||
1040 | else |
||
1041 | { |
||
1042 | if ( this.isDefined(values.leadspaces) && |
||
1043 | ' ' == values.leadspaces && |
||
1044 | ( matches = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"\{\[].*?) *\:(\\s+(.+?))?\\s*$').exec(values.value) ) |
||
1045 | ) { |
||
1046 | matches = {key: matches[1], value: matches[3]}; |
||
1047 | // this is a compact notation element, add to next block and parse |
||
1048 | c = this.getRealCurrentLineNb(); |
||
1049 | parser = new YamlParser(c); |
||
1050 | parser.refs = this.refs; |
||
1051 | block = values.value; |
||
1052 | |||
1053 | if ( !this.isNextLineIndented() ) |
||
1054 | { |
||
1055 | block += "\n"+this.getNextEmbedBlock(this.getCurrentLineIndentation() + 2); |
||
1056 | } |
||
1057 | |||
1058 | data.push(parser.parse(block)); |
||
1059 | this.refs = parser.refs; |
||
1060 | } |
||
1061 | else |
||
1062 | { |
||
1063 | data.push(this.parseValue(values.value)); |
||
1064 | } |
||
1065 | } |
||
1066 | } |
||
1067 | else if ( values = new RegExp('^('+YamlInline.REGEX_QUOTED_STRING+'|[^ \'"\[\{].*?) *\:(\\s+(.+?))?\\s*$').exec(this.currentLine) ) |
||
1068 | { |
||
1069 | if ( !this.isDefined(data) ) data = {}; |
||
1070 | if (context && 'sequence' == context) { |
||
1071 | throw new YamlParseException('You cannot define a mapping item when in a sequence', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1072 | } |
||
1073 | context = 'mapping'; |
||
1074 | //if ( data instanceof Array ) throw new YamlParseException("Non mapped entry", this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1075 | |||
1076 | values = {key: values[1], value: values[3]}; |
||
1077 | |||
1078 | try { |
||
1079 | key = new YamlInline().parseScalar(values.key); |
||
1080 | } catch (e) { |
||
1081 | if ( e instanceof YamlParseException ) { |
||
1082 | e.setParsedLine(this.getRealCurrentLineNb() + 1); |
||
1083 | e.setSnippet(this.currentLine); |
||
1084 | } |
||
1085 | throw e; |
||
1086 | } |
||
1087 | |||
1088 | |||
1089 | if ( '<<' == key ) |
||
1090 | { |
||
1091 | if ( this.isDefined(values.value) && '*' == (values.value+'').charAt(0) ) |
||
1092 | { |
||
1093 | isInPlace = values.value.substr(1); |
||
1094 | if ( this.refs[isInPlace] == undefined ) |
||
1095 | { |
||
1096 | throw new YamlParseException('Reference "'+value+'" does not exist', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1097 | } |
||
1098 | } |
||
1099 | else |
||
1100 | { |
||
1101 | if ( this.isDefined(values.value) && values.value != '' ) |
||
1102 | { |
||
1103 | value = values.value; |
||
1104 | } |
||
1105 | else |
||
1106 | { |
||
1107 | value = this.getNextEmbedBlock(); |
||
1108 | } |
||
1109 | |||
1110 | c = this.getRealCurrentLineNb() + 1; |
||
1111 | parser = new YamlParser(c); |
||
1112 | parser.refs = this.refs; |
||
1113 | parsed = parser.parse(value); |
||
1114 | this.refs = parser.refs; |
||
1115 | |||
1116 | var merged = []; |
||
1117 | if ( !this.isObject(parsed) ) |
||
1118 | { |
||
1119 | throw new YamlParseException("YAML merge keys used with a scalar value instead of an array", this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1120 | } |
||
1121 | else if ( this.isDefined(parsed[0]) ) |
||
1122 | { |
||
1123 | // Numeric array, merge individual elements |
||
1124 | reverse = this.reverseArray(parsed); |
||
1125 | len = reverse.length; |
||
1126 | for ( var i = 0; i < len; i++ ) |
||
1127 | { |
||
1128 | var parsedItem = reverse[i]; |
||
1129 | if ( !this.isObject(reverse[i]) ) |
||
1130 | { |
||
1131 | throw new YamlParseException("Merge items must be arrays", this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1132 | } |
||
1133 | merged = this.mergeObject(reverse[i], merged); |
||
1134 | } |
||
1135 | } |
||
1136 | else |
||
1137 | { |
||
1138 | // Associative array, merge |
||
1139 | merged = this.mergeObject(merged, parsed); |
||
1140 | } |
||
1141 | |||
1142 | isProcessed = merged; |
||
1143 | } |
||
1144 | } |
||
1145 | else if ( this.isDefined(values.value) && (matches = /^&([^ ]+) *(.*)/.exec(values.value) ) ) |
||
1146 | { |
||
1147 | matches = {ref: matches[1], value: matches[2]}; |
||
1148 | isRef = matches.ref; |
||
1149 | values.value = matches.value; |
||
1150 | } |
||
1151 | |||
1152 | if ( isProcessed ) |
||
1153 | { |
||
1154 | // Merge keys |
||
1155 | data = isProcessed; |
||
1156 | } |
||
1157 | // hash |
||
1158 | else if ( !this.isDefined(values.value) || '' == this.trim(values.value) || this.trim(values.value).charAt(0) == '#' ) |
||
1159 | { |
||
1160 | // if next line is less indented or equal, then it means that the current value is null |
||
1161 | if ( this.isNextLineIndented() && !this.isNextLineUnIndentedCollection() ) |
||
1162 | { |
||
1163 | data[key] = null; |
||
1164 | } |
||
1165 | else |
||
1166 | { |
||
1167 | c = this.getRealCurrentLineNb() + 1; |
||
1168 | parser = new YamlParser(c); |
||
1169 | parser.refs = this.refs; |
||
1170 | data[key] = parser.parse(this.getNextEmbedBlock()); |
||
1171 | this.refs = parser.refs; |
||
1172 | } |
||
1173 | } |
||
1174 | else |
||
1175 | { |
||
1176 | if ( isInPlace ) |
||
1177 | { |
||
1178 | data = this.refs[isInPlace]; |
||
1179 | } |
||
1180 | else |
||
1181 | { |
||
1182 | data[key] = this.parseValue(values.value); |
||
1183 | } |
||
1184 | } |
||
1185 | } |
||
1186 | else |
||
1187 | { |
||
1188 | // 1-liner followed by newline |
||
1189 | if ( 2 == this.lines.length && this.isEmpty(this.lines[1]) ) |
||
1190 | { |
||
1191 | try { |
||
1192 | value = new YamlInline().parse(this.lines[0]); |
||
1193 | } catch (e) { |
||
1194 | if ( e instanceof YamlParseException ) { |
||
1195 | e.setParsedLine(this.getRealCurrentLineNb() + 1); |
||
1196 | e.setSnippet(this.currentLine); |
||
1197 | } |
||
1198 | throw e; |
||
1199 | } |
||
1200 | |||
1201 | if ( this.isObject(value) ) |
||
1202 | { |
||
1203 | var first = value[0]; |
||
1204 | if ( typeof(value) == 'string' && '*' == first.charAt(0) ) |
||
1205 | { |
||
1206 | data = []; |
||
1207 | len = value.length; |
||
1208 | for ( var i = 0; i < len; i++ ) |
||
1209 | { |
||
1210 | data.push(this.refs[value[i].substr(1)]); |
||
1211 | } |
||
1212 | value = data; |
||
1213 | } |
||
1214 | } |
||
1215 | |||
1216 | return value; |
||
1217 | } |
||
1218 | |||
1219 | throw new YamlParseException('Unable to parse.', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1220 | } |
||
1221 | |||
1222 | if ( isRef ) |
||
1223 | { |
||
1224 | if ( data instanceof Array ) |
||
1225 | this.refs[isRef] = data[data.length-1]; |
||
1226 | else |
||
1227 | { |
||
1228 | var lastKey = null; |
||
1229 | for ( var k in data ) |
||
1230 | { |
||
1231 | if ( data.hasOwnProperty(k) ) lastKey = k; |
||
1232 | } |
||
1233 | this.refs[isRef] = data[k]; |
||
1234 | } |
||
1235 | } |
||
1236 | } |
||
1237 | |||
1238 | return this.isEmpty(data) ? null : data; |
||
1239 | }, |
||
1240 | |||
1241 | /** |
||
1242 | * Returns the current line number (takes the offset into account). |
||
1243 | * |
||
1244 | * @return integer The current line number |
||
1245 | */ |
||
1246 | getRealCurrentLineNb: function() |
||
1247 | { |
||
1248 | return this.currentLineNb + this.offset; |
||
1249 | }, |
||
1250 | |||
1251 | /** |
||
1252 | * Returns the current line indentation. |
||
1253 | * |
||
1254 | * @return integer The current line indentation |
||
1255 | */ |
||
1256 | getCurrentLineIndentation: function() |
||
1257 | { |
||
1258 | return this.currentLine.length - this.currentLine.replace(/^ +/g, '').length; |
||
1259 | }, |
||
1260 | |||
1261 | /** |
||
1262 | * Returns the next embed block of YAML. |
||
1263 | * |
||
1264 | * @param integer indentation The indent level at which the block is to be read, or null for default |
||
1265 | * |
||
1266 | * @return string A YAML string |
||
1267 | * |
||
1268 | * @throws YamlParseException When indentation problem are detected |
||
1269 | */ |
||
1270 | getNextEmbedBlock: function(indentation) |
||
1271 | { |
||
1272 | this.moveToNextLine(); |
||
1273 | var newIndent = null; |
||
1274 | var indent = null; |
||
1275 | |||
1276 | if ( !this.isDefined(indentation) ) |
||
1277 | { |
||
1278 | newIndent = this.getCurrentLineIndentation(); |
||
1279 | |||
1280 | var unindentedEmbedBlock = this.isStringUnIndentedCollectionItem(this.currentLine); |
||
1281 | |||
1282 | if ( !this.isCurrentLineEmpty() && 0 == newIndent && !unindentedEmbedBlock ) |
||
1283 | { |
||
1284 | throw new YamlParseException('Indentation problem A', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1285 | } |
||
1286 | } |
||
1287 | else |
||
1288 | { |
||
1289 | newIndent = indentation; |
||
1290 | } |
||
1291 | |||
1292 | var data = [this.currentLine.substr(newIndent)]; |
||
1293 | |||
1294 | var isUnindentedCollection = this.isStringUnIndentedCollectionItem(this.currentLine); |
||
1295 | |||
1296 | var continuationIndent = -1; |
||
1297 | if (isUnindentedCollection === true) { |
||
1298 | continuationIndent = 1 + /^\-((\s+)(.+?))?\s*$/.exec(this.currentLine)[2].length; |
||
1299 | } |
||
1300 | |||
1301 | while ( this.moveToNextLine() ) |
||
1302 | { |
||
1303 | |||
1304 | if (isUnindentedCollection && !this.isStringUnIndentedCollectionItem(this.currentLine) && this.getCurrentLineIndentation() != continuationIndent) { |
||
1305 | this.moveToPreviousLine(); |
||
1306 | break; |
||
1307 | } |
||
1308 | |||
1309 | if ( this.isCurrentLineEmpty() ) |
||
1310 | { |
||
1311 | if ( this.isCurrentLineBlank() ) |
||
1312 | { |
||
1313 | data.push(this.currentLine.substr(newIndent)); |
||
1314 | } |
||
1315 | |||
1316 | continue; |
||
1317 | } |
||
1318 | |||
1319 | indent = this.getCurrentLineIndentation(); |
||
1320 | var matches; |
||
1321 | if ( matches = /^( *)$/.exec(this.currentLine) ) |
||
1322 | { |
||
1323 | // empty line |
||
1324 | data.push(matches[1]); |
||
1325 | } |
||
1326 | else if ( indent >= newIndent ) |
||
1327 | { |
||
1328 | data.push(this.currentLine.substr(newIndent)); |
||
1329 | } |
||
1330 | else if ( 0 == indent ) |
||
1331 | { |
||
1332 | this.moveToPreviousLine(); |
||
1333 | |||
1334 | break; |
||
1335 | } |
||
1336 | else |
||
1337 | { |
||
1338 | throw new YamlParseException('Indentation problem B', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1339 | } |
||
1340 | } |
||
1341 | |||
1342 | return data.join("\n"); |
||
1343 | }, |
||
1344 | |||
1345 | /** |
||
1346 | * Moves the parser to the next line. |
||
1347 | * |
||
1348 | * @return Boolean |
||
1349 | */ |
||
1350 | moveToNextLine: function() |
||
1351 | { |
||
1352 | if ( this.currentLineNb >= this.lines.length - 1 ) |
||
1353 | { |
||
1354 | return false; |
||
1355 | } |
||
1356 | |||
1357 | this.currentLineNb++; |
||
1358 | this.currentLine = this.lines[this.currentLineNb]; |
||
1359 | |||
1360 | return true; |
||
1361 | }, |
||
1362 | |||
1363 | /** |
||
1364 | * Moves the parser to the previous line. |
||
1365 | */ |
||
1366 | moveToPreviousLine: function() |
||
1367 | { |
||
1368 | this.currentLineNb--; |
||
1369 | this.currentLine = this.lines[this.currentLineNb]; |
||
1370 | }, |
||
1371 | |||
1372 | /** |
||
1373 | * Parses a YAML value. |
||
1374 | * |
||
1375 | * @param string value A YAML value |
||
1376 | * |
||
1377 | * @return mixed A JS value |
||
1378 | * |
||
1379 | * @throws YamlParseException When reference does not exist |
||
1380 | */ |
||
1381 | parseValue: function(value) |
||
1382 | { |
||
1383 | if ( '*' == (value+'').charAt(0) ) |
||
1384 | { |
||
1385 | if ( this.trim(value).charAt(0) == '#' ) |
||
1386 | { |
||
1387 | value = (value+'').substr(1, value.indexOf('#') - 2); |
||
1388 | } |
||
1389 | else |
||
1390 | { |
||
1391 | value = (value+'').substr(1); |
||
1392 | } |
||
1393 | |||
1394 | if ( this.refs[value] == undefined ) |
||
1395 | { |
||
1396 | throw new YamlParseException('Reference "'+value+'" does not exist', this.getRealCurrentLineNb() + 1, this.currentLine); |
||
1397 | } |
||
1398 | return this.refs[value]; |
||
1399 | } |
||
1400 | |||
1401 | var matches = null; |
||
1402 | if ( matches = /^(\||>)(\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?( +#.*)?$/.exec(value) ) |
||
1403 | { |
||
1404 | matches = {separator: matches[1], modifiers: matches[2], comments: matches[3]}; |
||
1405 | var modifiers = this.isDefined(matches.modifiers) ? matches.modifiers : ''; |
||
1406 | |||
1407 | return this.parseFoldedScalar(matches.separator, modifiers.replace(/\d+/g, ''), Math.abs(parseInt(modifiers))); |
||
1408 | } |
||
1409 | try { |
||
1410 | return new YamlInline().parse(value); |
||
1411 | } catch (e) { |
||
1412 | if ( e instanceof YamlParseException ) { |
||
1413 | e.setParsedLine(this.getRealCurrentLineNb() + 1); |
||
1414 | e.setSnippet(this.currentLine); |
||
1415 | } |
||
1416 | throw e; |
||
1417 | } |
||
1418 | }, |
||
1419 | |||
1420 | /** |
||
1421 | * Parses a folded scalar. |
||
1422 | * |
||
1423 | * @param string separator The separator that was used to begin this folded scalar (| or >) |
||
1424 | * @param string indicator The indicator that was used to begin this folded scalar (+ or -) |
||
1425 | * @param integer indentation The indentation that was used to begin this folded scalar |
||
1426 | * |
||
1427 | * @return string The text value |
||
1428 | */ |
||
1429 | parseFoldedScalar: function(separator, indicator, indentation) |
||
1430 | { |
||
1431 | if ( indicator == undefined ) indicator = ''; |
||
1432 | if ( indentation == undefined ) indentation = 0; |
||
1433 | |||
1434 | separator = '|' == separator ? "\n" : ' '; |
||
1435 | var text = ''; |
||
1436 | var diff = null; |
||
1437 | |||
1438 | var notEOF = this.moveToNextLine(); |
||
1439 | |||
1440 | while ( notEOF && this.isCurrentLineBlank() ) |
||
1441 | { |
||
1442 | text += "\n"; |
||
1443 | |||
1444 | notEOF = this.moveToNextLine(); |
||
1445 | } |
||
1446 | |||
1447 | if ( !notEOF ) |
||
1448 | { |
||
1449 | return ''; |
||
1450 | } |
||
1451 | |||
1452 | var matches = null; |
||
1453 | if ( !(matches = new RegExp('^('+(indentation ? this.strRepeat(' ', indentation) : ' +')+')(.*)$').exec(this.currentLine)) ) |
||
1454 | { |
||
1455 | this.moveToPreviousLine(); |
||
1456 | |||
1457 | return ''; |
||
1458 | } |
||
1459 | |||
1460 | matches = {indent: matches[1], text: matches[2]}; |
||
1461 | |||
1462 | var textIndent = matches.indent; |
||
1463 | var previousIndent = 0; |
||
1464 | |||
1465 | text += matches.text + separator; |
||
1466 | while ( this.currentLineNb + 1 < this.lines.length ) |
||
1467 | { |
||
1468 | this.moveToNextLine(); |
||
1469 | |||
1470 | if ( matches = new RegExp('^( {'+textIndent.length+',})(.+)$').exec(this.currentLine) ) |
||
1471 | { |
||
1472 | matches = {indent: matches[1], text: matches[2]}; |
||
1473 | |||
1474 | if ( ' ' == separator && previousIndent != matches.indent ) |
||
1475 | { |
||
1476 | text = text.substr(0, text.length - 1)+"\n"; |
||
1477 | } |
||
1478 | |||
1479 | previousIndent = matches.indent; |
||
1480 | |||
1481 | diff = matches.indent.length - textIndent.length; |
||
1482 | text += this.strRepeat(' ', diff) + matches.text + (diff != 0 ? "\n" : separator); |
||
1483 | } |
||
1484 | else if ( matches = /^( *)$/.exec(this.currentLine) ) |
||
1485 | { |
||
1486 | text += matches[1].replace(new RegExp('^ {1,'+textIndent.length+'}','g'), '')+"\n"; |
||
1487 | } |
||
1488 | else |
||
1489 | { |
||
1490 | this.moveToPreviousLine(); |
||
1491 | |||
1492 | break; |
||
1493 | } |
||
1494 | } |
||
1495 | |||
1496 | if ( ' ' == separator ) |
||
1497 | { |
||
1498 | // replace last separator by a newline |
||
1499 | text = text.replace(/ (\n*)$/g, "\n$1"); |
||
1500 | } |
||
1501 | |||
1502 | switch ( indicator ) |
||
1503 | { |
||
1504 | case '': |
||
1505 | text = text.replace(/\n+$/g, "\n"); |
||
1506 | break; |
||
1507 | case '+': |
||
1508 | break; |
||
1509 | case '-': |
||
1510 | text = text.replace(/\n+$/g, ''); |
||
1511 | break; |
||
1512 | } |
||
1513 | |||
1514 | return text; |
||
1515 | }, |
||
1516 | |||
1517 | /** |
||
1518 | * Returns true if the next line is indented. |
||
1519 | * |
||
1520 | * @return Boolean Returns true if the next line is indented, false otherwise |
||
1521 | */ |
||
1522 | isNextLineIndented: function() |
||
1523 | { |
||
1524 | var currentIndentation = this.getCurrentLineIndentation(); |
||
1525 | var notEOF = this.moveToNextLine(); |
||
1526 | |||
1527 | while ( notEOF && this.isCurrentLineEmpty() ) |
||
1528 | { |
||
1529 | notEOF = this.moveToNextLine(); |
||
1530 | } |
||
1531 | |||
1532 | if ( false == notEOF ) |
||
1533 | { |
||
1534 | return false; |
||
1535 | } |
||
1536 | |||
1537 | var ret = false; |
||
1538 | if ( this.getCurrentLineIndentation() <= currentIndentation ) |
||
1539 | { |
||
1540 | ret = true; |
||
1541 | } |
||
1542 | |||
1543 | this.moveToPreviousLine(); |
||
1544 | |||
1545 | return ret; |
||
1546 | }, |
||
1547 | |||
1548 | /** |
||
1549 | * Returns true if the current line is blank or if it is a comment line. |
||
1550 | * |
||
1551 | * @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise |
||
1552 | */ |
||
1553 | isCurrentLineEmpty: function() |
||
1554 | { |
||
1555 | return this.isCurrentLineBlank() || this.isCurrentLineComment(); |
||
1556 | }, |
||
1557 | |||
1558 | /** |
||
1559 | * Returns true if the current line is blank. |
||
1560 | * |
||
1561 | * @return Boolean Returns true if the current line is blank, false otherwise |
||
1562 | */ |
||
1563 | isCurrentLineBlank: function() |
||
1564 | { |
||
1565 | return '' == this.trim(this.currentLine); |
||
1566 | }, |
||
1567 | |||
1568 | /** |
||
1569 | * Returns true if the current line is a comment line. |
||
1570 | * |
||
1571 | * @return Boolean Returns true if the current line is a comment line, false otherwise |
||
1572 | */ |
||
1573 | isCurrentLineComment: function() |
||
1574 | { |
||
1575 | //checking explicitly the first char of the trim is faster than loops or strpos |
||
1576 | var ltrimmedLine = this.currentLine.replace(/^ +/g, ''); |
||
1577 | return ltrimmedLine.charAt(0) == '#'; |
||
1578 | }, |
||
1579 | |||
1580 | /** |
||
1581 | * Cleanups a YAML string to be parsed. |
||
1582 | * |
||
1583 | * @param string value The input YAML string |
||
1584 | * |
||
1585 | * @return string A cleaned up YAML string |
||
1586 | */ |
||
1587 | cleanup: function(value) |
||
1588 | { |
||
1589 | value = value.split("\r\n").join("\n").split("\r").join("\n"); |
||
1590 | |||
1591 | if ( !/\n$/.test(value) ) |
||
1592 | { |
||
1593 | value += "\n"; |
||
1594 | } |
||
1595 | |||
1596 | // strip YAML header |
||
1597 | var count = 0; |
||
1598 | var regex = /^\%YAML[: ][\d\.]+.*\n/; |
||
1599 | while ( regex.test(value) ) |
||
1600 | { |
||
1601 | value = value.replace(regex, ''); |
||
1602 | count++; |
||
1603 | } |
||
1604 | this.offset += count; |
||
1605 | |||
1606 | // remove leading comments |
||
1607 | regex = /^(#.*?\n)+/; |
||
1608 | if ( regex.test(value) ) |
||
1609 | { |
||
1610 | var trimmedValue = value.replace(regex, ''); |
||
1611 | |||
1612 | // items have been removed, update the offset |
||
1613 | this.offset += this.subStrCount(value, "\n") - this.subStrCount(trimmedValue, "\n"); |
||
1614 | value = trimmedValue; |
||
1615 | } |
||
1616 | |||
1617 | // remove start of the document marker (---) |
||
1618 | regex = /^\-\-\-.*?\n/; |
||
1619 | if ( regex.test(value) ) |
||
1620 | { |
||
1621 | trimmedValue = value.replace(regex, ''); |
||
1622 | |||
1623 | // items have been removed, update the offset |
||
1624 | this.offset += this.subStrCount(value, "\n") - this.subStrCount(trimmedValue, "\n"); |
||
1625 | value = trimmedValue; |
||
1626 | |||
1627 | // remove end of the document marker (...) |
||
1628 | value = value.replace(/\.\.\.\s*$/g, ''); |
||
1629 | } |
||
1630 | |||
1631 | return value; |
||
1632 | }, |
||
1633 | |||
1634 | /** |
||
1635 | * Returns true if the next line starts unindented collection |
||
1636 | * |
||
1637 | * @return Boolean Returns true if the next line starts unindented collection, false otherwise |
||
1638 | */ |
||
1639 | isNextLineUnIndentedCollection: function() |
||
1640 | { |
||
1641 | var currentIndentation = this.getCurrentLineIndentation(); |
||
1642 | var notEOF = this.moveToNextLine(); |
||
1643 | |||
1644 | while (notEOF && this.isCurrentLineEmpty()) { |
||
1645 | notEOF = this.moveToNextLine(); |
||
1646 | } |
||
1647 | |||
1648 | if (false === notEOF) { |
||
1649 | return false; |
||
1650 | } |
||
1651 | |||
1652 | var ret = false; |
||
1653 | if ( |
||
1654 | this.getCurrentLineIndentation() == currentIndentation |
||
1655 | && |
||
1656 | this.isStringUnIndentedCollectionItem(this.currentLine) |
||
1657 | ) { |
||
1658 | ret = true; |
||
1659 | } |
||
1660 | |||
1661 | this.moveToPreviousLine(); |
||
1662 | |||
1663 | return ret; |
||
1664 | }, |
||
1665 | |||
1666 | /** |
||
1667 | * Returns true if the string is unindented collection item |
||
1668 | * |
||
1669 | * @return Boolean Returns true if the string is unindented collection item, false otherwise |
||
1670 | */ |
||
1671 | isStringUnIndentedCollectionItem: function(string) |
||
1672 | { |
||
1673 | return (0 === this.currentLine.indexOf('- ')); |
||
1674 | }, |
||
1675 | |||
1676 | isObject: function(input) |
||
1677 | { |
||
1678 | return typeof(input) == 'object' && this.isDefined(input); |
||
1679 | }, |
||
1680 | |||
1681 | isEmpty: function(input) |
||
1682 | { |
||
1683 | return input == undefined || input == null || input == '' || input == 0 || input == "0" || input == false; |
||
1684 | }, |
||
1685 | |||
1686 | isDefined: function(input) |
||
1687 | { |
||
1688 | return input != undefined && input != null; |
||
1689 | }, |
||
1690 | |||
1691 | reverseArray: function(input /* Array */) |
||
1692 | { |
||
1693 | var result = []; |
||
1694 | var len = input.length; |
||
1695 | for ( var i = len-1; i >= 0; i-- ) |
||
1696 | { |
||
1697 | result.push(input[i]); |
||
1698 | } |
||
1699 | |||
1700 | return result; |
||
1701 | }, |
||
1702 | |||
1703 | merge: function(a /* Object */, b /* Object */) |
||
1704 | { |
||
1705 | var c = {}; |
||
1706 | var i; |
||
1707 | |||
1708 | for ( i in a ) |
||
1709 | { |
||
1710 | if ( a.hasOwnProperty(i) ) |
||
1711 | if ( /^\d+$/.test(i) ) c.push(a); |
||
1712 | else c[i] = a[i]; |
||
1713 | } |
||
1714 | for ( i in b ) |
||
1715 | { |
||
1716 | if ( b.hasOwnProperty(i) ) |
||
1717 | if ( /^\d+$/.test(i) ) c.push(b); |
||
1718 | else c[i] = b[i]; |
||
1719 | } |
||
1720 | |||
1721 | return c; |
||
1722 | }, |
||
1723 | |||
1724 | strRepeat: function(str /* String */, count /* Integer */) |
||
1725 | { |
||
1726 | var i; |
||
1727 | var result = ''; |
||
1728 | for ( i = 0; i < count; i++ ) result += str; |
||
1729 | return result; |
||
1730 | }, |
||
1731 | |||
1732 | subStrCount: function(string, subString, start, length) |
||
1733 | { |
||
1734 | var c = 0; |
||
1735 | |||
1736 | string = '' + string; |
||
1737 | subString = '' + subString; |
||
1738 | |||
1739 | if ( start != undefined ) string = string.substr(start); |
||
1740 | if ( length != undefined ) string = string.substr(0, length); |
||
1741 | |||
1742 | var len = string.length; |
||
1743 | var sublen = subString.length; |
||
1744 | for ( var i = 0; i < len; i++ ) |
||
1745 | { |
||
1746 | if ( subString == string.substr(i, sublen) ) |
||
1747 | c++; |
||
1748 | i += sublen - 1; |
||
1749 | } |
||
1750 | |||
1751 | return c; |
||
1752 | }, |
||
1753 | |||
1754 | trim: function(str /* String */) |
||
1755 | { |
||
1756 | return (str+'').replace(/^ +/,'').replace(/ +$/,''); |
||
1757 | } |
||
1758 | }; |
||
1759 | /** |
||
1760 | * YamlEscaper encapsulates escaping rules for single and double-quoted |
||
1761 | * YAML strings. |
||
1762 | * |
||
1763 | * @author Matthew Lewinski <matthew@lewinski.org> |
||
1764 | */ |
||
1765 | YamlEscaper = function(){}; |
||
1766 | YamlEscaper.prototype = |
||
1767 | { |
||
1768 | /** |
||
1769 | * Determines if a JS value would require double quoting in YAML. |
||
1770 | * |
||
1771 | * @param string value A JS value |
||
1772 | * |
||
1773 | * @return Boolean True if the value would require double quotes. |
||
1774 | */ |
||
1775 | requiresDoubleQuoting: function(value) |
||
1776 | { |
||
1777 | return new RegExp(YamlEscaper.REGEX_CHARACTER_TO_ESCAPE).test(value); |
||
1778 | }, |
||
1779 | |||
1780 | /** |
||
1781 | * Escapes and surrounds a JS value with double quotes. |
||
1782 | * |
||
1783 | * @param string value A JS value |
||
1784 | * |
||
1785 | * @return string The quoted, escaped string |
||
1786 | */ |
||
1787 | escapeWithDoubleQuotes: function(value) |
||
1788 | { |
||
1789 | value = value + ''; |
||
1790 | var len = YamlEscaper.escapees.length; |
||
1791 | var maxlen = YamlEscaper.escaped.length; |
||
1792 | var esc = YamlEscaper.escaped; |
||
1793 | for (var i = 0; i < len; ++i) |
||
1794 | if ( i >= maxlen ) esc.push(''); |
||
1795 | |||
1796 | var ret = ''; |
||
1797 | ret = value.replace(new RegExp(YamlEscaper.escapees.join('|'),'g'), function(str){ |
||
1798 | for(var i = 0; i < len; ++i){ |
||
1799 | if( str == YamlEscaper.escapees[i] ) |
||
1800 | return esc[i]; |
||
1801 | } |
||
1802 | }); |
||
1803 | return '"' + ret + '"'; |
||
1804 | }, |
||
1805 | |||
1806 | /** |
||
1807 | * Determines if a JS value would require single quoting in YAML. |
||
1808 | * |
||
1809 | * @param string value A JS value |
||
1810 | * |
||
1811 | * @return Boolean True if the value would require single quotes. |
||
1812 | */ |
||
1813 | requiresSingleQuoting: function(value) |
||
1814 | { |
||
1815 | return /[\s'":{}[\],&*#?]|^[-?|<>=!%@`]/.test(value);> |
||
1816 | <> },> |
||
1817 | |||
1818 | <> /**> |
||
1819 | <> * Escapes and surrounds a JS value with single quotes.> |
||
1820 | <> *> |
||
1821 | <> * @param string value A JS value> |
||
1822 | <> *> |
||
1823 | <> * @return string The quoted, escaped string> |
||
1824 | <> */> |
||
1825 | <> escapeWithSingleQuotes : function(value)> |
||
1826 | <> {> |
||
1827 | <> return "'" + value.replace(/'/g, "''") + "'";> |
||
1828 | <> }> |
||
1829 | <>};> |
||
1830 | |||
1831 | <>// Characters that would cause a dumped string to require double quoting.> |
||
1832 | <>YamlEscaper.REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";> |
||
1833 | |||
1834 | <>// Mapping arrays for escaping a double quoted string. The backslash is> |
||
1835 | <>// first to ensure proper escaping. > |
||
1836 | <>YamlEscaper.escapees = ['\\\\', '\\"', '"',> |
||
1837 | <> "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",> |
||
1838 | <> "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",> |
||
1839 | <> "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",> |
||
1840 | <> "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",> |
||
1841 | <> "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"];> |
||
1842 | <>YamlEscaper.escaped = ['\\"', '\\\\', '\\"',> |
||
1843 | <> "\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a",> |
||
1844 | <> "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f",> |
||
1845 | <> "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",> |
||
1846 | <> "\\x18", "\\x19", "\\x1a", "\\e", "\\x1c", "\\x1d", "\\x1e", "\\x1f",> |
||
1847 | <> "\\N", "\\_", "\\L", "\\P"];> |
||
1848 | <>/**> |
||
1849 | <> * YamlUnescaper encapsulates unescaping rules for single and double-quoted> |
||
1850 | <> * YAML strings.> |
||
1851 | <> *> |
||
1852 | <> * @author Matthew Lewinski <matthew@lewinski.org>> |
||
1853 | <> */> |
||
1854 | <>var YamlUnescaper = function(){};> |
||
1855 | <>YamlUnescaper.prototype => |
||
1856 | <>{> |
||
1857 | <> /**> |
||
1858 | <> * Unescapes a single quoted string.> |
||
1859 | <> *> |
||
1860 | <> * @param string value A single quoted string.> |
||
1861 | <> *> |
||
1862 | <> * @return string The unescaped string.> |
||
1863 | <> */> |
||
1864 | <> unescapeSingleQuotedString: function(value)> |
||
1865 | <> {> |
||
1866 | <> return value.replace(/''/g, "'");> |
||
1867 | <> },> |
||
1868 | |||
1869 | <> /**> |
||
1870 | <> * Unescapes a double quoted string.> |
||
1871 | <> *> |
||
1872 | <> * @param string value A double quoted string.> |
||
1873 | <> *> |
||
1874 | <> * @return string The unescaped string.> |
||
1875 | <> */> |
||
1876 | <> unescapeDoubleQuotedString: function(value)> |
||
1877 | <> {> |
||
1878 | <> var callback = function(m) {> |
||
1879 | <> return new YamlUnescaper().unescapeCharacter(m);> |
||
1880 | <> };> |
||
1881 | |||
1882 | <> // evaluate the string> |
||
1883 | <> return value.replace(new RegExp(YamlUnescaper.REGEX_ESCAPED_CHARACTER, 'g'), callback);> |
||
1884 | <> },> |
||
1885 | |||
1886 | <> /**> |
||
1887 | <> * Unescapes a character that was found in a double-quoted string> |
||
1888 | <> *> |
||
1889 | <> * @param string value An escaped character> |
||
1890 | <> *> |
||
1891 | <> * @return string The unescaped character> |
||
1892 | <> */> |
||
1893 | <> unescapeCharacter: function(value)> |
||
1894 | <> {> |
||
1895 | <> switch (value.charAt(1)) {> |
||
1896 | <> case '0':> |
||
1897 | <> return String.fromCharCode(0);> |
||
1898 | <> case 'a':> |
||
1899 | <> return String.fromCharCode(7);> |
||
1900 | <> case 'b':> |
||
1901 | <> return String.fromCharCode(8);> |
||
1902 | <> case 't':> |
||
1903 | <> return "\t";> |
||
1904 | <> case "\t":> |
||
1905 | <> return "\t";> |
||
1906 | <> case 'n':> |
||
1907 | <> return "\n";> |
||
1908 | <> case 'v':> |
||
1909 | <> return String.fromCharCode(11);> |
||
1910 | <> case 'f':> |
||
1911 | <> return String.fromCharCode(12);> |
||
1912 | <> case 'r':> |
||
1913 | <> return String.fromCharCode(13);> |
||
1914 | <> case 'e':> |
||
1915 | <> return "\x1b";> |
||
1916 | <> case ' ':> |
||
1917 | <> return ' ';> |
||
1918 | <> case '"':> |
||
1919 | <> return '"';> |
||
1920 | <> case '/':> |
||
1921 | <> return '/';> |
||
1922 | <> case '\\':> |
||
1923 | <> return '\\';> |
||
1924 | <> case 'N':> |
||
1925 | <> // U+0085 NEXT LINE> |
||
1926 | <> return "\x00\x85";> |
||
1927 | <> case '_':> |
||
1928 | <> // U+00A0 NO-BREAK SPACE> |
||
1929 | <> return "\x00\xA0";> |
||
1930 | <> case 'L':> |
||
1931 | <> // U+2028 LINE SEPARATOR> |
||
1932 | <> return "\x20\x28";> |
||
1933 | <> case 'P':> |
||
1934 | <> // U+2029 PARAGRAPH SEPARATOR> |
||
1935 | <> return "\x20\x29";> |
||
1936 | <> case 'x':> |
||
1937 | <> return this.pack('n', new YamlInline().hexdec(value.substr(2, 2)));> |
||
1938 | <> case 'u':> |
||
1939 | <> return this.pack('n', new YamlInline().hexdec(value.substr(2, 4)));> |
||
1940 | <> case 'U':> |
||
1941 | <> return this.pack('N', new YamlInline().hexdec(value.substr(2, 8)));> |
||
1942 | <> }> |
||
1943 | <> },> |
||
1944 | |||
1945 | <> /**> |
||
1946 | <> * @see http://phpjs.org/functions/pack> |
||
1947 | <> * @warning only modes used above copied> |
||
1948 | <> */> |
||
1949 | <> pack: function(B){var g=0,o=1,m="",l="",z=0,p=[],E,s,C,I,h,c;var d,b,x,H,u,e,A,q,D,t,w,a,G,F,y,v,f;while(g<B.length){E=B.charAt(g);s="";g++;while((g<B.length)&&(B.charAt(g).match(/[\d\*]/)!==null)){s+=B.charAt(g);g++}if(s===""){s="1"}switch(E){case"n":if(s==="*"){s=arguments.length-o}if(s>(arguments.length-o)){throw new Error("Warning: pack() Type "+E+": too few arguments")}for(z=0;z<s;z++){m+=String.fromCharCode(arguments[o]>>8&255);m+=String.fromCharCode(arguments[o]&255);o++}break;case"N":if(s==="*"){s=arguments.length-o}if(s>(arguments.length-o)){throw new Error("Warning: pack() Type "+E+": too few arguments")}for(z=0;z<s;z++){m+=String.fromCharCode(arguments[o]>>24&255);m+=String.fromCharCode(arguments[o]>>16&255);m+=String.fromCharCode(arguments[o]>>8&255);m+=String.fromCharCode(arguments[o]&255);o++}break;default:throw new Error("Warning: pack() Type "+E+": unknown format code")}}if(o<arguments.length){throw new Error("Warning: pack(): "+(arguments.length-o)+" arguments unused")}return m}> |
||
1950 | <>}> |
||
1951 | |||
1952 | <>// Regex fragment that matches an escaped character in a double quoted> |
||
1953 | <>// string.> |
||
1954 | <>// why escape quotes, ffs!> |
||
1955 | <>YamlUnescaper.REGEX_ESCAPED_CHARACTER = '\\\\([0abt\tnvfre "\\/\\\\N_LP]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})';> |
||
1956 | |||
1957 | <>/**> |
||
1958 | <> * YamlDumper dumps JS variables to YAML strings.> |
||
1959 | <> *> |
||
1960 | <> * @author Fabien Potencier <fabien@symfony.com>> |
||
1961 | <> */> |
||
1962 | <>var YamlDumper = function(){};> |
||
1963 | <>YamlDumper.prototype => |
||
1964 | <>{> |
||
1965 | <> /**> |
||
1966 | <> * Dumps a JS value to YAML.> |
||
1967 | <> *> |
||
1968 | <> * @param mixed input The JS value> |
||
1969 | <> * @param integer inline The level where you switch to inline YAML> |
||
1970 | <> * @param integer indent The level o indentation indentation (used internally)> |
||
1971 | <> *> |
||
1972 | <> * @return string The YAML representation of the JS value> |
||
1973 | <> */> |
||
1974 | <> dump: function(input, inline, indent)> |
||
1975 | <> {> |
||
1976 | <> if ( inline == null ) inline = 0;> |
||
1977 | <> if ( indent == null ) indent = 0;> |
||
1978 | <> var output = '';> |
||
1979 | <> var prefix = indent ? this.strRepeat(' ', indent) : '';> |
||
1980 | <> var yaml;> |
||
1981 | <> if (!this.numSpacesForIndentation) this.numSpacesForIndentation = 2;> |
||
1982 | |||
1983 | <> if ( inline <= 0 || !this.isObject(input) || this.isEmpty(input) )> |
||
1984 | <> {> |
||
1985 | <> yaml = new YamlInline();> |
||
1986 | <> output += prefix + yaml.dump(input);> |
||
1987 | <> }> |
||
1988 | <> else> |
||
1989 | <> {> |
||
1990 | <> var isAHash = !this.arrayEquals(this.getKeys(input), this.range(0,input.length - 1));> |
||
1991 | <> var willBeInlined;> |
||
1992 | |||
1993 | <> for ( var key in input )> |
||
1994 | <> {> |
||
1995 | <> if ( input.hasOwnProperty(key) )> |
||
1996 | <> {> |
||
1997 | <> willBeInlined = inline - 1 <= 0 || !this.isObject(input[key]) || this.isEmpty(input[key]);> |
||
1998 | |||
1999 | <> if ( isAHash ) yaml = new YamlInline();> |
||
2000 | |||
2001 | <> output += > |
||
2002 | <> prefix + '' +> |
||
2003 | <> (isAHash ? yaml.dump(key)+':' : '-') + '' +> |
||
2004 | <> (willBeInlined ? ' ' : "\n") + '' +> |
||
2005 | <> this.dump(input[key], inline - 1, (willBeInlined ? 0 : indent + this.numSpacesForIndentation)) + '' +> |
||
2006 | <> (willBeInlined ? "\n" : '');> |
||
2007 | <> }> |
||
2008 | <> }> |
||
2009 | <> }> |
||
2010 | |||
2011 | <> return output;> |
||
2012 | <> },> |
||
2013 | |||
2014 | <> strRepeat: function(str /* String */, count /* Integer */)> |
||
2015 | <> {> |
||
2016 | <> var i;> |
||
2017 | <> var result = '';> |
||
2018 | <> for ( i = 0; i < count; i++ ) result += str;> |
||
2019 | <> return result;> |
||
2020 | <> },> |
||
2021 | |||
2022 | <> isObject: function(input)> |
||
2023 | <> {> |
||
2024 | <> return this.isDefined(input) && typeof(input) == 'object';> |
||
2025 | <> },> |
||
2026 | |||
2027 | <> isEmpty: function(input)> |
||
2028 | <> {> |
||
2029 | <> var ret = input == undefined || input == null || input == '' || input == 0 || input == "0" || input == false;> |
||
2030 | <> if ( !ret && typeof(input) == "object" && !(input instanceof Array)){> |
||
2031 | <> var propCount = 0;> |
||
2032 | <> for ( var key in input )> |
||
2033 | <> if ( input.hasOwnProperty(key) ) propCount++;> |
||
2034 | <> ret = !propCount;> |
||
2035 | <> }> |
||
2036 | <> return ret;> |
||
2037 | <> },> |
||
2038 | |||
2039 | <> isDefined: function(input)> |
||
2040 | <> {> |
||
2041 | <> return input != undefined && input != null;> |
||
2042 | <> },> |
||
2043 | |||
2044 | <> getKeys: function(tab)> |
||
2045 | <> {> |
||
2046 | <> var ret = [];> |
||
2047 | |||
2048 | <> for ( var name in tab )> |
||
2049 | <> {> |
||
2050 | <> if ( tab.hasOwnProperty(name) )> |
||
2051 | <> {> |
||
2052 | <> ret.push(name);> |
||
2053 | <> }> |
||
2054 | <> }> |
||
2055 | |||
2056 | <> return ret;> |
||
2057 | <> },> |
||
2058 | |||
2059 | <> range: function(start, end)> |
||
2060 | <> {> |
||
2061 | <> if ( start > end ) return [];> |
||
2062 | |||
2063 | <> var ret = [];> |
||
2064 | |||
2065 | <> for ( var i = start; i <= end; i++ )> |
||
2066 | <> {> |
||
2067 | <> ret.push(i);> |
||
2068 | <> }> |
||
2069 | |||
2070 | <> return ret;> |
||
2071 | <> },> |
||
2072 | |||
2073 | <> arrayEquals: function(a,b)> |
||
2074 | <> {> |
||
2075 | <> if ( a.length != b.length ) return false;> |
||
2076 | |||
2077 | <> var len = a.length;> |
||
2078 | |||
2079 | <> for ( var i = 0; i < len; i++ )> |
||
2080 | <> {> |
||
2081 | <> if ( a[i] != b[i] ) return false;> |
||
2082 | <> }> |
||
2083 | |||
2084 | <> return true;> |
||
2085 | <> }> |
||
2086 | <>};> |
||
2087 | <>})();> |