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