Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/yaml.js

Issue 10233013: Crawler, second version (Closed)
Patch Set: Created April 12, 2013, 1:38 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/task.js ('k') | metadata.gecko » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 })();
OLDNEW
« no previous file with comments | « lib/task.js ('k') | metadata.gecko » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld