/bower_components/yaml.js/src/Escaper.coffee |
@@ -0,0 +1,80 @@ |
|
Pattern = require './Pattern' |
|
# Escaper encapsulates escaping rules for single |
# and double-quoted YAML strings. |
class Escaper |
|
# Mapping arrays for escaping a double quoted string. The backslash is |
# first to ensure proper escaping. |
@LIST_ESCAPEES: ['\\', '\\\\', '\\"', '"', |
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", |
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", |
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", |
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", |
(ch = String.fromCharCode)(0x0085), ch(0x00A0), ch(0x2028), ch(0x2029)] |
@LIST_ESCAPED: ['\\\\', '\\"', '\\"', '\\"', |
"\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", |
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f", |
"\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", |
"\\x18", "\\x19", "\\x1a", "\\e", "\\x1c", "\\x1d", "\\x1e", "\\x1f", |
"\\N", "\\_", "\\L", "\\P"] |
|
@MAPPING_ESCAPEES_TO_ESCAPED: do => |
mapping = {} |
for i in [0...@LIST_ESCAPEES.length] |
mapping[@LIST_ESCAPEES[i]] = @LIST_ESCAPED[i] |
return mapping |
|
# Characters that would cause a dumped string to require double quoting. |
@PATTERN_CHARACTERS_TO_ESCAPE: new Pattern '[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9' |
|
# Other precompiled patterns |
@PATTERN_MAPPING_ESCAPEES: new Pattern @LIST_ESCAPEES.join('|').split('\\').join('\\\\') |
@PATTERN_SINGLE_QUOTING: new Pattern '[\\s\'":{}[\\],&*#?]|^[-?|<>=!%@`]' |
|
|
|
# Determines if a JavaScript value would require double quoting in YAML. |
# |
# @param [String] value A JavaScript value value |
# |
# @return [Boolean] true if the value would require double quotes. |
# |
@requiresDoubleQuoting: (value) -> |
return @PATTERN_CHARACTERS_TO_ESCAPE.test value |
|
|
# Escapes and surrounds a JavaScript value with double quotes. |
# |
# @param [String] value A JavaScript value |
# |
# @return [String] The quoted, escaped string |
# |
@escapeWithDoubleQuotes: (value) -> |
result = @PATTERN_MAPPING_ESCAPEES.replace value, (str) => |
return @MAPPING_ESCAPEES_TO_ESCAPED[str] |
return '"'+result+'"' |
|
|
# Determines if a JavaScript value would require single quoting in YAML. |
# |
# @param [String] value A JavaScript value |
# |
# @return [Boolean] true if the value would require single quotes. |
# |
@requiresSingleQuoting: (value) -> |
return @PATTERN_SINGLE_QUOTING.test value |
|
|
# Escapes and surrounds a JavaScript value with single quotes. |
# |
# @param [String] value A JavaScript value |
# |
# @return [String] The quoted, escaped string |
# |
@escapeWithSingleQuotes: (value) -> |
return "'"+value.replace(/'/g, "''")+"'" |
|
|
module.exports = Escaper |
/bower_components/yaml.js/src/Inline.coffee |
@@ -0,0 +1,487 @@ |
|
Pattern = require './Pattern' |
Unescaper = require './Unescaper' |
Escaper = require './Escaper' |
Utils = require './Utils' |
ParseException = require './Exception/ParseException' |
DumpException = require './Exception/DumpException' |
|
# Inline YAML parsing and dumping |
class Inline |
|
# Quoted string regular expression |
@REGEX_QUOTED_STRING: '(?:"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\']*(?:\'\'[^\']*)*)\')' |
|
# Pre-compiled patterns |
# |
@PATTERN_TRAILING_COMMENTS: new Pattern '^\\s*#.*$' |
@PATTERN_QUOTED_SCALAR: new Pattern '^'+@REGEX_QUOTED_STRING |
@PATTERN_THOUSAND_NUMERIC_SCALAR: new Pattern '^(-|\\+)?[0-9,]+(\\.[0-9]+)?$' |
@PATTERN_SCALAR_BY_DELIMITERS: {} |
|
# Settings |
@settings: {} |
|
|
# Configure YAML inline. |
# |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
@configure: (exceptionOnInvalidType = null, objectDecoder = null) -> |
# Update settings |
@settings.exceptionOnInvalidType = exceptionOnInvalidType |
@settings.objectDecoder = objectDecoder |
return |
|
|
# Converts a YAML string to a JavaScript object. |
# |
# @param [String] value A YAML string |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
# @return [Object] A JavaScript object representing the YAML string |
# |
# @throw [ParseException] |
# |
@parse: (value, exceptionOnInvalidType = false, objectDecoder = null) -> |
# Update settings from last call of Inline.parse() |
@settings.exceptionOnInvalidType = exceptionOnInvalidType |
@settings.objectDecoder = objectDecoder |
|
if not value? |
return '' |
|
value = Utils.trim value |
|
if 0 is value.length |
return '' |
|
# Keep a context object to pass through static methods |
context = {exceptionOnInvalidType, objectDecoder, i: 0} |
|
switch value.charAt(0) |
when '[' |
result = @parseSequence value, context |
++context.i |
when '{' |
result = @parseMapping value, context |
++context.i |
else |
result = @parseScalar value, null, ['"', "'"], context |
|
# Some comments are allowed at the end |
if @PATTERN_TRAILING_COMMENTS.replace(value[context.i..], '') isnt '' |
throw new ParseException 'Unexpected characters near "'+value[context.i..]+'".' |
|
return result |
|
|
# Dumps a given JavaScript variable to a YAML string. |
# |
# @param [Object] value The JavaScript variable to convert |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectEncoder A function to serialize custom objects, null otherwise |
# |
# @return [String] The YAML string representing the JavaScript object |
# |
# @throw [DumpException] |
# |
@dump: (value, exceptionOnInvalidType = false, objectEncoder = null) -> |
if not value? |
return 'null' |
type = typeof value |
if type is 'object' |
if value instanceof Date |
return value.toISOString() |
else if objectEncoder? |
result = objectEncoder value |
if typeof result is 'string' or result? |
return result |
return @dumpObject value |
if type is 'boolean' |
return (if value then 'true' else 'false') |
if Utils.isDigits(value) |
return (if type is 'string' then "'"+value+"'" else String(parseInt(value))) |
if Utils.isNumeric(value) |
return (if type is 'string' then "'"+value+"'" else String(parseFloat(value))) |
if type is 'number' |
return (if value is Infinity then '.Inf' else (if value is -Infinity then '-.Inf' else (if isNaN(value) then '.NaN' else value))) |
if Escaper.requiresDoubleQuoting value |
return Escaper.escapeWithDoubleQuotes value |
if Escaper.requiresSingleQuoting value |
return Escaper.escapeWithSingleQuotes value |
if '' is value |
return '""' |
if Utils.PATTERN_DATE.test value |
return "'"+value+"'"; |
if value.toLowerCase() in ['null','~','true','false'] |
return "'"+value+"'" |
# Default |
return value; |
|
|
# Dumps a JavaScript object to a YAML string. |
# |
# @param [Object] value The JavaScript object to dump |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectEncoder A function do serialize custom objects, null otherwise |
# |
# @return string The YAML string representing the JavaScript object |
# |
@dumpObject: (value, exceptionOnInvalidType, objectSupport = null) -> |
# Array |
if value instanceof Array |
output = [] |
for val in value |
output.push @dump val |
return '['+output.join(', ')+']' |
|
# Mapping |
else |
output = [] |
for key, val of value |
output.push @dump(key)+': '+@dump(val) |
return '{'+output.join(', ')+'}' |
|
|
# Parses a scalar to a YAML string. |
# |
# @param [Object] scalar |
# @param [Array] delimiters |
# @param [Array] stringDelimiters |
# @param [Object] context |
# @param [Boolean] evaluate |
# |
# @return [String] A YAML string |
# |
# @throw [ParseException] When malformed inline YAML string is parsed |
# |
@parseScalar: (scalar, delimiters = null, stringDelimiters = ['"', "'"], context = null, evaluate = true) -> |
unless context? |
context = exceptionOnInvalidType: @settings.exceptionOnInvalidType, objectDecoder: @settings.objectDecoder, i: 0 |
{i} = context |
|
if scalar.charAt(i) in stringDelimiters |
# Quoted scalar |
output = @parseQuotedScalar scalar, context |
{i} = context |
|
if delimiters? |
tmp = Utils.ltrim scalar[i..], ' ' |
if not(tmp.charAt(0) in delimiters) |
throw new ParseException 'Unexpected characters ('+scalar[i..]+').' |
|
else |
# "normal" string |
if not delimiters |
output = scalar[i..] |
i += output.length |
|
# Remove comments |
strpos = output.indexOf ' #' |
if strpos isnt -1 |
output = Utils.rtrim output[0...strpos] |
|
else |
joinedDelimiters = delimiters.join('|') |
pattern = @PATTERN_SCALAR_BY_DELIMITERS[joinedDelimiters] |
unless pattern? |
pattern = new Pattern '^(.+?)('+joinedDelimiters+')' |
@PATTERN_SCALAR_BY_DELIMITERS[joinedDelimiters] = pattern |
if match = pattern.exec scalar[i..] |
output = match[1] |
i += output.length |
else |
throw new ParseException 'Malformed inline YAML string ('+scalar+').' |
|
|
if evaluate |
output = @evaluateScalar output, context |
|
context.i = i |
return output |
|
|
# Parses a quoted scalar to YAML. |
# |
# @param [String] scalar |
# @param [Object] context |
# |
# @return [String] A YAML string |
# |
# @throw [ParseException] When malformed inline YAML string is parsed |
# |
@parseQuotedScalar: (scalar, context) -> |
{i} = context |
|
unless match = @PATTERN_QUOTED_SCALAR.exec scalar[i..] |
throw new ParseException 'Malformed inline YAML string ('+scalar[i..]+').' |
|
output = match[0].substr(1, match[0].length - 2) |
|
if '"' is scalar.charAt(i) |
output = Unescaper.unescapeDoubleQuotedString output |
else |
output = Unescaper.unescapeSingleQuotedString output |
|
i += match[0].length |
|
context.i = i |
return output |
|
|
# Parses a sequence to a YAML string. |
# |
# @param [String] sequence |
# @param [Object] context |
# |
# @return [String] A YAML string |
# |
# @throw [ParseException] When malformed inline YAML string is parsed |
# |
@parseSequence: (sequence, context) -> |
output = [] |
len = sequence.length |
{i} = context |
i += 1 |
|
# [foo, bar, ...] |
while i < len |
context.i = i |
switch sequence.charAt(i) |
when '[' |
# Nested sequence |
output.push @parseSequence sequence, context |
{i} = context |
when '{' |
# Nested mapping |
output.push @parseMapping sequence, context |
{i} = context |
when ']' |
return output |
when ',', ' ', "\n" |
# Do nothing |
else |
isQuoted = (sequence.charAt(i) in ['"', "'"]) |
value = @parseScalar sequence, [',', ']'], ['"', "'"], context |
{i} = context |
|
if not(isQuoted) and typeof(value) is 'string' and (value.indexOf(': ') isnt -1 or value.indexOf(":\n") isnt -1) |
# Embedded mapping? |
try |
value = @parseMapping '{'+value+'}' |
catch e |
# No, it's not |
|
|
output.push value |
|
--i |
|
++i |
|
throw new ParseException 'Malformed inline YAML string '+sequence |
|
|
# Parses a mapping to a YAML string. |
# |
# @param [String] mapping |
# @param [Object] context |
# |
# @return [String] A YAML string |
# |
# @throw [ParseException] When malformed inline YAML string is parsed |
# |
@parseMapping: (mapping, context) -> |
output = {} |
len = mapping.length |
{i} = context |
i += 1 |
|
# {foo: bar, bar:foo, ...} |
shouldContinueWhileLoop = false |
while i < len |
context.i = i |
switch mapping.charAt(i) |
when ' ', ',', "\n" |
++i |
context.i = i |
shouldContinueWhileLoop = true |
when '}' |
return output |
|
if shouldContinueWhileLoop |
shouldContinueWhileLoop = false |
continue |
|
# Key |
key = @parseScalar mapping, [':', ' ', "\n"], ['"', "'"], context, false |
{i} = context |
|
# Value |
done = false |
|
while i < len |
context.i = i |
switch mapping.charAt(i) |
when '[' |
# Nested sequence |
value = @parseSequence mapping, context |
{i} = context |
# Spec: Keys MUST be unique; first one wins. |
# Parser cannot abort this mapping earlier, since lines |
# are processed sequentially. |
if output[key] == undefined |
output[key] = value |
done = true |
when '{' |
# Nested mapping |
value = @parseMapping mapping, context |
{i} = context |
# Spec: Keys MUST be unique; first one wins. |
# Parser cannot abort this mapping earlier, since lines |
# are processed sequentially. |
if output[key] == undefined |
output[key] = value |
done = true |
when ':', ' ', "\n" |
# Do nothing |
else |
value = @parseScalar mapping, [',', '}'], ['"', "'"], context |
{i} = context |
# Spec: Keys MUST be unique; first one wins. |
# Parser cannot abort this mapping earlier, since lines |
# are processed sequentially. |
if output[key] == undefined |
output[key] = value |
done = true |
--i |
|
++i |
|
if done |
break |
|
throw new ParseException 'Malformed inline YAML string '+mapping |
|
|
# Evaluates scalars and replaces magic values. |
# |
# @param [String] scalar |
# |
# @return [String] A YAML string |
# |
@evaluateScalar: (scalar, context) -> |
scalar = Utils.trim(scalar) |
scalarLower = scalar.toLowerCase() |
|
switch scalarLower |
when 'null', '', '~' |
return null |
when 'true' |
return true |
when 'false' |
return false |
when '.inf' |
return Infinity |
when '.nan' |
return NaN |
when '-.inf' |
return Infinity |
else |
firstChar = scalarLower.charAt(0) |
switch firstChar |
when '!' |
firstSpace = scalar.indexOf(' ') |
if firstSpace is -1 |
firstWord = scalarLower |
else |
firstWord = scalarLower[0...firstSpace] |
switch firstWord |
when '!' |
if firstSpace isnt -1 |
return parseInt @parseScalar(scalar[2..]) |
return null |
when '!str' |
return Utils.ltrim scalar[4..] |
when '!!str' |
return Utils.ltrim scalar[5..] |
when '!!int' |
return parseInt(@parseScalar(scalar[5..])) |
when '!!bool' |
return Utils.parseBoolean(@parseScalar(scalar[6..]), false) |
when '!!float' |
return parseFloat(@parseScalar(scalar[7..])) |
when '!!timestamp' |
return Utils.stringToDate(Utils.ltrim(scalar[11..])) |
else |
unless context? |
context = exceptionOnInvalidType: @settings.exceptionOnInvalidType, objectDecoder: @settings.objectDecoder, i: 0 |
{objectDecoder, exceptionOnInvalidType} = context |
|
if objectDecoder |
# If objectDecoder function is given, we can do custom decoding of custom types |
trimmedScalar = Utils.rtrim scalar |
firstSpace = trimmedScalar.indexOf(' ') |
if firstSpace is -1 |
return objectDecoder trimmedScalar, null |
else |
subValue = Utils.ltrim trimmedScalar[firstSpace+1..] |
unless subValue.length > 0 |
subValue = null |
return objectDecoder trimmedScalar[0...firstSpace], subValue |
|
if exceptionOnInvalidType |
throw new ParseException 'Custom object support when parsing a YAML file has been disabled.' |
|
return null |
when '0' |
if '0x' is scalar[0...2] |
return Utils.hexDec scalar |
else if Utils.isDigits scalar |
return Utils.octDec scalar |
else if Utils.isNumeric scalar |
return parseFloat scalar |
else |
return scalar |
when '+' |
if Utils.isDigits scalar |
raw = scalar |
cast = parseInt(raw) |
if raw is String(cast) |
return cast |
else |
return raw |
else if Utils.isNumeric scalar |
return parseFloat scalar |
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar |
return parseFloat(scalar.replace(',', '')) |
return scalar |
when '-' |
if Utils.isDigits(scalar[1..]) |
if '0' is scalar.charAt(1) |
return -Utils.octDec(scalar[1..]) |
else |
raw = scalar[1..] |
cast = parseInt(raw) |
if raw is String(cast) |
return -cast |
else |
return -raw |
else if Utils.isNumeric scalar |
return parseFloat scalar |
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar |
return parseFloat(scalar.replace(',', '')) |
return scalar |
else |
if date = Utils.stringToDate(scalar) |
return date |
else if Utils.isNumeric(scalar) |
return parseFloat scalar |
else if @PATTERN_THOUSAND_NUMERIC_SCALAR.test scalar |
return parseFloat(scalar.replace(',', '')) |
return scalar |
|
module.exports = Inline |
/bower_components/yaml.js/src/Parser.coffee |
@@ -0,0 +1,654 @@ |
|
Inline = require './Inline' |
Pattern = require './Pattern' |
Utils = require './Utils' |
ParseException = require './Exception/ParseException' |
|
# Parser parses YAML strings to convert them to JavaScript objects. |
# |
class Parser |
|
# Pre-compiled patterns |
# |
PATTERN_FOLDED_SCALAR_ALL: new Pattern '^(?:(?<type>![^\\|>]*)\\s+)?(?<separator>\\||>)(?<modifiers>\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(?<comments> +#.*)?$' |
PATTERN_FOLDED_SCALAR_END: new Pattern '(?<separator>\\||>)(?<modifiers>\\+|\\-|\\d+|\\+\\d+|\\-\\d+|\\d+\\+|\\d+\\-)?(?<comments> +#.*)?$' |
PATTERN_SEQUENCE_ITEM: new Pattern '^\\-((?<leadspaces>\\s+)(?<value>.+?))?\\s*$' |
PATTERN_ANCHOR_VALUE: new Pattern '^&(?<ref>[^ ]+) *(?<value>.*)' |
PATTERN_COMPACT_NOTATION: new Pattern '^(?<key>'+Inline.REGEX_QUOTED_STRING+'|[^ \'"\\{\\[].*?) *\\:(\\s+(?<value>.+?))?\\s*$' |
PATTERN_MAPPING_ITEM: new Pattern '^(?<key>'+Inline.REGEX_QUOTED_STRING+'|[^ \'"\\[\\{].*?) *\\:(\\s+(?<value>.+?))?\\s*$' |
PATTERN_DECIMAL: new Pattern '\\d+' |
PATTERN_INDENT_SPACES: new Pattern '^ +' |
PATTERN_TRAILING_LINES: new Pattern '(\n*)$' |
PATTERN_YAML_HEADER: new Pattern '^\\%YAML[: ][\\d\\.]+.*\n' |
PATTERN_LEADING_COMMENTS: new Pattern '^(\\#.*?\n)+' |
PATTERN_DOCUMENT_MARKER_START: new Pattern '^\\-\\-\\-.*?\n' |
PATTERN_DOCUMENT_MARKER_END: new Pattern '^\\.\\.\\.\\s*$' |
PATTERN_FOLDED_SCALAR_BY_INDENTATION: {} |
|
# Context types |
# |
CONTEXT_NONE: 0 |
CONTEXT_SEQUENCE: 1 |
CONTEXT_MAPPING: 2 |
|
|
# Constructor |
# |
# @param [Integer] offset The offset of YAML document (used for line numbers in error messages) |
# |
constructor: (@offset = 0) -> |
@lines = [] |
@currentLineNb = -1 |
@currentLine = '' |
@refs = {} |
|
|
# Parses a YAML string to a JavaScript value. |
# |
# @param [String] value A YAML string |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
# @return [Object] A JavaScript value |
# |
# @throw [ParseException] If the YAML is not valid |
# |
parse: (value, exceptionOnInvalidType = false, objectDecoder = null) -> |
@currentLineNb = -1 |
@currentLine = '' |
@lines = @cleanup(value).split "\n" |
|
data = null |
context = @CONTEXT_NONE |
allowOverwrite = false |
while @moveToNextLine() |
if @isCurrentLineEmpty() |
continue |
|
# Tab? |
if "\t" is @currentLine[0] |
throw new ParseException 'A YAML file cannot contain tabs as indentation.', @getRealCurrentLineNb() + 1, @currentLine |
|
isRef = mergeNode = false |
if values = @PATTERN_SEQUENCE_ITEM.exec @currentLine |
if @CONTEXT_MAPPING is context |
throw new ParseException 'You cannot define a sequence item when in a mapping' |
context = @CONTEXT_SEQUENCE |
data ?= [] |
|
if values.value? and matches = @PATTERN_ANCHOR_VALUE.exec values.value |
isRef = matches.ref |
values.value = matches.value |
|
# Array |
if not(values.value?) or '' is Utils.trim(values.value, ' ') or Utils.ltrim(values.value, ' ').indexOf('#') is 0 |
if @currentLineNb < @lines.length - 1 and not @isNextLineUnIndentedCollection() |
c = @getRealCurrentLineNb() + 1 |
parser = new Parser c |
parser.refs = @refs |
data.push parser.parse(@getNextEmbedBlock(null, true), exceptionOnInvalidType, objectDecoder) |
else |
data.push null |
|
else |
if values.leadspaces?.length and matches = @PATTERN_COMPACT_NOTATION.exec values.value |
|
# This is a compact notation element, add to next block and parse |
c = @getRealCurrentLineNb() |
parser = new Parser c |
parser.refs = @refs |
|
block = values.value |
indent = @getCurrentLineIndentation() |
if @isNextLineIndented(false) |
block += "\n"+@getNextEmbedBlock(indent + values.leadspaces.length + 1, true) |
|
data.push parser.parse block, exceptionOnInvalidType, objectDecoder |
|
else |
data.push @parseValue values.value, exceptionOnInvalidType, objectDecoder |
|
else if (values = @PATTERN_MAPPING_ITEM.exec @currentLine) and values.key.indexOf(' #') is -1 |
if @CONTEXT_SEQUENCE is context |
throw new ParseException 'You cannot define a mapping item when in a sequence' |
context = @CONTEXT_MAPPING |
data ?= {} |
|
# Force correct settings |
Inline.configure exceptionOnInvalidType, objectDecoder |
try |
key = Inline.parseScalar values.key |
catch e |
e.parsedLine = @getRealCurrentLineNb() + 1 |
e.snippet = @currentLine |
|
throw e |
|
if '<<' is key |
mergeNode = true |
allowOverwrite = true |
if values.value?.indexOf('*') is 0 |
refName = values.value[1..] |
unless @refs[refName]? |
throw new ParseException 'Reference "'+refName+'" does not exist.', @getRealCurrentLineNb() + 1, @currentLine |
|
refValue = @refs[refName] |
|
if typeof refValue isnt 'object' |
throw new ParseException 'YAML merge keys used with a scalar value instead of an object.', @getRealCurrentLineNb() + 1, @currentLine |
|
if refValue instanceof Array |
# Merge array with object |
for value, i in refValue |
data[String(i)] ?= value |
else |
# Merge objects |
for key, value of refValue |
data[key] ?= value |
|
else |
if values.value? and values.value isnt '' |
value = values.value |
else |
value = @getNextEmbedBlock() |
|
c = @getRealCurrentLineNb() + 1 |
parser = new Parser c |
parser.refs = @refs |
parsed = parser.parse value, exceptionOnInvalidType |
|
unless typeof parsed is 'object' |
throw new ParseException 'YAML merge keys used with a scalar value instead of an object.', @getRealCurrentLineNb() + 1, @currentLine |
|
if parsed instanceof Array |
# If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes |
# and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier |
# in the sequence override keys specified in later mapping nodes. |
for parsedItem in parsed |
unless typeof parsedItem is 'object' |
throw new ParseException 'Merge items must be objects.', @getRealCurrentLineNb() + 1, parsedItem |
|
if parsedItem instanceof Array |
# Merge array with object |
for value, i in parsedItem |
k = String(i) |
unless data.hasOwnProperty(k) |
data[k] = value |
else |
# Merge objects |
for key, value of parsedItem |
unless data.hasOwnProperty(key) |
data[key] = value |
|
else |
# If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the |
# current mapping, unless the key already exists in it. |
for key, value of parsed |
unless data.hasOwnProperty(key) |
data[key] = value |
|
else if values.value? and matches = @PATTERN_ANCHOR_VALUE.exec values.value |
isRef = matches.ref |
values.value = matches.value |
|
|
if mergeNode |
# Merge keys |
else if not(values.value?) or '' is Utils.trim(values.value, ' ') or Utils.ltrim(values.value, ' ').indexOf('#') is 0 |
# Hash |
# if next line is less indented or equal, then it means that the current value is null |
if not(@isNextLineIndented()) and not(@isNextLineUnIndentedCollection()) |
# Spec: Keys MUST be unique; first one wins. |
# But overwriting is allowed when a merge node is used in current block. |
if allowOverwrite or data[key] is undefined |
data[key] = null |
|
else |
c = @getRealCurrentLineNb() + 1 |
parser = new Parser c |
parser.refs = @refs |
val = parser.parse @getNextEmbedBlock(), exceptionOnInvalidType, objectDecoder |
|
# Spec: Keys MUST be unique; first one wins. |
# But overwriting is allowed when a merge node is used in current block. |
if allowOverwrite or data[key] is undefined |
data[key] = val |
|
else |
val = @parseValue values.value, exceptionOnInvalidType, objectDecoder |
|
# Spec: Keys MUST be unique; first one wins. |
# But overwriting is allowed when a merge node is used in current block. |
if allowOverwrite or data[key] is undefined |
data[key] = val |
|
else |
# 1-liner optionally followed by newline |
lineCount = @lines.length |
if 1 is lineCount or (2 is lineCount and Utils.isEmpty(@lines[1])) |
try |
value = Inline.parse @lines[0], exceptionOnInvalidType, objectDecoder |
catch e |
e.parsedLine = @getRealCurrentLineNb() + 1 |
e.snippet = @currentLine |
|
throw e |
|
if typeof value is 'object' |
if value instanceof Array |
first = value[0] |
else |
for key of value |
first = value[key] |
break |
|
if typeof first is 'string' and first.indexOf('*') is 0 |
data = [] |
for alias in value |
data.push @refs[alias[1..]] |
value = data |
|
return value |
|
else if Utils.ltrim(value).charAt(0) in ['[', '{'] |
try |
return Inline.parse value, exceptionOnInvalidType, objectDecoder |
catch e |
e.parsedLine = @getRealCurrentLineNb() + 1 |
e.snippet = @currentLine |
|
throw e |
|
throw new ParseException 'Unable to parse.', @getRealCurrentLineNb() + 1, @currentLine |
|
if isRef |
if data instanceof Array |
@refs[isRef] = data[data.length-1] |
else |
lastKey = null |
for key of data |
lastKey = key |
@refs[isRef] = data[lastKey] |
|
|
if Utils.isEmpty(data) |
return null |
else |
return data |
|
|
|
# Returns the current line number (takes the offset into account). |
# |
# @return [Integer] The current line number |
# |
getRealCurrentLineNb: -> |
return @currentLineNb + @offset |
|
|
# Returns the current line indentation. |
# |
# @return [Integer] The current line indentation |
# |
getCurrentLineIndentation: -> |
return @currentLine.length - Utils.ltrim(@currentLine, ' ').length |
|
|
# Returns the next embed block of YAML. |
# |
# @param [Integer] indentation The indent level at which the block is to be read, or null for default |
# |
# @return [String] A YAML string |
# |
# @throw [ParseException] When indentation problem are detected |
# |
getNextEmbedBlock: (indentation = null, includeUnindentedCollection = false) -> |
@moveToNextLine() |
|
if not indentation? |
newIndent = @getCurrentLineIndentation() |
|
unindentedEmbedBlock = @isStringUnIndentedCollectionItem @currentLine |
|
if not(@isCurrentLineEmpty()) and 0 is newIndent and not(unindentedEmbedBlock) |
throw new ParseException 'Indentation problem.', @getRealCurrentLineNb() + 1, @currentLine |
|
else |
newIndent = indentation |
|
|
data = [@currentLine[newIndent..]] |
|
unless includeUnindentedCollection |
isItUnindentedCollection = @isStringUnIndentedCollectionItem @currentLine |
|
# Comments must not be removed inside a string block (ie. after a line ending with "|") |
# They must not be removed inside a sub-embedded block as well |
removeCommentsPattern = @PATTERN_FOLDED_SCALAR_END |
removeComments = not removeCommentsPattern.test @currentLine |
|
while @moveToNextLine() |
indent = @getCurrentLineIndentation() |
|
if indent is newIndent |
removeComments = not removeCommentsPattern.test @currentLine |
|
if isItUnindentedCollection and not @isStringUnIndentedCollectionItem(@currentLine) and indent is newIndent |
@moveToPreviousLine() |
break |
|
if @isCurrentLineBlank() |
data.push @currentLine[newIndent..] |
continue |
|
if removeComments and @isCurrentLineComment() |
if indent is newIndent |
continue |
|
if indent >= newIndent |
data.push @currentLine[newIndent..] |
else if Utils.ltrim(@currentLine).charAt(0) is '#' |
# Don't add line with comments |
else if 0 is indent |
@moveToPreviousLine() |
break |
else |
throw new ParseException 'Indentation problem.', @getRealCurrentLineNb() + 1, @currentLine |
|
|
return data.join "\n" |
|
|
# Moves the parser to the next line. |
# |
# @return [Boolean] |
# |
moveToNextLine: -> |
if @currentLineNb >= @lines.length - 1 |
return false |
|
@currentLine = @lines[++@currentLineNb]; |
|
return true |
|
|
# Moves the parser to the previous line. |
# |
moveToPreviousLine: -> |
@currentLine = @lines[--@currentLineNb] |
return |
|
|
# Parses a YAML value. |
# |
# @param [String] value A YAML value |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
# @return [Object] A JavaScript value |
# |
# @throw [ParseException] When reference does not exist |
# |
parseValue: (value, exceptionOnInvalidType, objectDecoder) -> |
if 0 is value.indexOf('*') |
pos = value.indexOf '#' |
if pos isnt -1 |
value = value.substr(1, pos-2) |
else |
value = value[1..] |
|
if @refs[value] is undefined |
throw new ParseException 'Reference "'+value+'" does not exist.', @currentLine |
|
return @refs[value] |
|
|
if matches = @PATTERN_FOLDED_SCALAR_ALL.exec value |
modifiers = matches.modifiers ? '' |
|
foldedIndent = Math.abs(parseInt(modifiers)) |
if isNaN(foldedIndent) then foldedIndent = 0 |
val = @parseFoldedScalar matches.separator, @PATTERN_DECIMAL.replace(modifiers, ''), foldedIndent |
if matches.type? |
# Force correct settings |
Inline.configure exceptionOnInvalidType, objectDecoder |
return Inline.parseScalar matches.type+' '+val |
else |
return val |
|
try |
return Inline.parse value, exceptionOnInvalidType, objectDecoder |
catch e |
# Try to parse multiline compact sequence or mapping |
if value.charAt(0) in ['[', '{'] and e instanceof ParseException and @isNextLineIndented() |
value += "\n" + @getNextEmbedBlock() |
try |
return Inline.parse value, exceptionOnInvalidType, objectDecoder |
catch e |
e.parsedLine = @getRealCurrentLineNb() + 1 |
e.snippet = @currentLine |
|
throw e |
|
else |
e.parsedLine = @getRealCurrentLineNb() + 1 |
e.snippet = @currentLine |
|
throw e |
|
return |
|
|
# Parses a folded scalar. |
# |
# @param [String] separator The separator that was used to begin this folded scalar (| or >) |
# @param [String] indicator The indicator that was used to begin this folded scalar (+ or -) |
# @param [Integer] indentation The indentation that was used to begin this folded scalar |
# |
# @return [String] The text value |
# |
parseFoldedScalar: (separator, indicator = '', indentation = 0) -> |
notEOF = @moveToNextLine() |
if not notEOF |
return '' |
|
isCurrentLineBlank = @isCurrentLineBlank() |
text = '' |
|
# Leading blank lines are consumed before determining indentation |
while notEOF and isCurrentLineBlank |
# newline only if not EOF |
if notEOF = @moveToNextLine() |
text += "\n" |
isCurrentLineBlank = @isCurrentLineBlank() |
|
|
# Determine indentation if not specified |
if 0 is indentation |
if matches = @PATTERN_INDENT_SPACES.exec @currentLine |
indentation = matches[0].length |
|
|
if indentation > 0 |
pattern = @PATTERN_FOLDED_SCALAR_BY_INDENTATION[indentation] |
unless pattern? |
pattern = new Pattern '^ {'+indentation+'}(.*)$' |
Parser::PATTERN_FOLDED_SCALAR_BY_INDENTATION[indentation] = pattern |
|
while notEOF and (isCurrentLineBlank or matches = pattern.exec @currentLine) |
if isCurrentLineBlank |
text += @currentLine[indentation..] |
else |
text += matches[1] |
|
# newline only if not EOF |
if notEOF = @moveToNextLine() |
text += "\n" |
isCurrentLineBlank = @isCurrentLineBlank() |
|
else if notEOF |
text += "\n" |
|
|
if notEOF |
@moveToPreviousLine() |
|
|
# Remove line breaks of each lines except the empty and more indented ones |
if '>' is separator |
newText = '' |
for line in text.split "\n" |
if line.length is 0 or line.charAt(0) is ' ' |
newText = Utils.rtrim(newText, ' ') + line + "\n" |
else |
newText += line + ' ' |
text = newText |
|
if '+' isnt indicator |
# Remove any extra space or new line as we are adding them after |
text = Utils.rtrim(text) |
|
# Deal with trailing newlines as indicated |
if '' is indicator |
text = @PATTERN_TRAILING_LINES.replace text, "\n" |
else if '-' is indicator |
text = @PATTERN_TRAILING_LINES.replace text, '' |
|
return text |
|
|
# Returns true if the next line is indented. |
# |
# @return [Boolean] Returns true if the next line is indented, false otherwise |
# |
isNextLineIndented: (ignoreComments = true) -> |
currentIndentation = @getCurrentLineIndentation() |
EOF = not @moveToNextLine() |
|
if ignoreComments |
while not(EOF) and @isCurrentLineEmpty() |
EOF = not @moveToNextLine() |
else |
while not(EOF) and @isCurrentLineBlank() |
EOF = not @moveToNextLine() |
|
if EOF |
return false |
|
ret = false |
if @getCurrentLineIndentation() > currentIndentation |
ret = true |
|
@moveToPreviousLine() |
|
return ret |
|
|
# Returns true if the current line is blank or if it is a comment line. |
# |
# @return [Boolean] Returns true if the current line is empty or if it is a comment line, false otherwise |
# |
isCurrentLineEmpty: -> |
trimmedLine = Utils.trim(@currentLine, ' ') |
return trimmedLine.length is 0 or trimmedLine.charAt(0) is '#' |
|
|
# Returns true if the current line is blank. |
# |
# @return [Boolean] Returns true if the current line is blank, false otherwise |
# |
isCurrentLineBlank: -> |
return '' is Utils.trim(@currentLine, ' ') |
|
|
# Returns true if the current line is a comment line. |
# |
# @return [Boolean] Returns true if the current line is a comment line, false otherwise |
# |
isCurrentLineComment: -> |
# Checking explicitly the first char of the trim is faster than loops or strpos |
ltrimmedLine = Utils.ltrim(@currentLine, ' ') |
|
return ltrimmedLine.charAt(0) is '#' |
|
|
# Cleanups a YAML string to be parsed. |
# |
# @param [String] value The input YAML string |
# |
# @return [String] A cleaned up YAML string |
# |
cleanup: (value) -> |
if value.indexOf("\r") isnt -1 |
value = value.split("\r\n").join("\n").split("\r").join("\n") |
|
# Strip YAML header |
count = 0 |
[value, count] = @PATTERN_YAML_HEADER.replaceAll value, '' |
@offset += count |
|
# Remove leading comments |
[trimmedValue, count] = @PATTERN_LEADING_COMMENTS.replaceAll value, '', 1 |
if count is 1 |
# Items have been removed, update the offset |
@offset += Utils.subStrCount(value, "\n") - Utils.subStrCount(trimmedValue, "\n") |
value = trimmedValue |
|
# Remove start of the document marker (---) |
[trimmedValue, count] = @PATTERN_DOCUMENT_MARKER_START.replaceAll value, '', 1 |
if count is 1 |
# Items have been removed, update the offset |
@offset += Utils.subStrCount(value, "\n") - Utils.subStrCount(trimmedValue, "\n") |
value = trimmedValue |
|
# Remove end of the document marker (...) |
value = @PATTERN_DOCUMENT_MARKER_END.replace value, '' |
|
# Ensure the block is not indented |
lines = value.split("\n") |
smallestIndent = -1 |
for line in lines |
continue if Utils.trim(line, ' ').length == 0 |
indent = line.length - Utils.ltrim(line).length |
if smallestIndent is -1 or indent < smallestIndent |
smallestIndent = indent |
if smallestIndent > 0 |
for line, i in lines |
lines[i] = line[smallestIndent..] |
value = lines.join("\n") |
|
return value |
|
|
# Returns true if the next line starts unindented collection |
# |
# @return [Boolean] Returns true if the next line starts unindented collection, false otherwise |
# |
isNextLineUnIndentedCollection: (currentIndentation = null) -> |
currentIndentation ?= @getCurrentLineIndentation() |
notEOF = @moveToNextLine() |
|
while notEOF and @isCurrentLineEmpty() |
notEOF = @moveToNextLine() |
|
if false is notEOF |
return false |
|
ret = false |
if @getCurrentLineIndentation() is currentIndentation and @isStringUnIndentedCollectionItem(@currentLine) |
ret = true |
|
@moveToPreviousLine() |
|
return ret |
|
|
# Returns true if the string is un-indented collection item |
# |
# @return [Boolean] Returns true if the string is un-indented collection item, false otherwise |
# |
isStringUnIndentedCollectionItem: -> |
return @currentLine is '-' or @currentLine[0...2] is '- ' |
|
|
module.exports = Parser |
/bower_components/yaml.js/src/Utils.coffee |
@@ -0,0 +1,342 @@ |
|
Pattern = require './Pattern' |
|
# A bunch of utility methods |
# |
class Utils |
|
@REGEX_LEFT_TRIM_BY_CHAR: {} |
@REGEX_RIGHT_TRIM_BY_CHAR: {} |
@REGEX_SPACES: /\s+/g |
@REGEX_DIGITS: /^\d+$/ |
@REGEX_OCTAL: /[^0-7]/gi |
@REGEX_HEXADECIMAL: /[^a-f0-9]/gi |
|
# Precompiled date pattern |
@PATTERN_DATE: new Pattern '^'+ |
'(?<year>[0-9][0-9][0-9][0-9])'+ |
'-(?<month>[0-9][0-9]?)'+ |
'-(?<day>[0-9][0-9]?)'+ |
'(?:(?:[Tt]|[ \t]+)'+ |
'(?<hour>[0-9][0-9]?)'+ |
':(?<minute>[0-9][0-9])'+ |
':(?<second>[0-9][0-9])'+ |
'(?:\.(?<fraction>[0-9]*))?'+ |
'(?:[ \t]*(?<tz>Z|(?<tz_sign>[-+])(?<tz_hour>[0-9][0-9]?)'+ |
'(?::(?<tz_minute>[0-9][0-9]))?))?)?'+ |
'$', 'i' |
|
# Local timezone offset in ms |
@LOCAL_TIMEZONE_OFFSET: new Date().getTimezoneOffset() * 60 * 1000 |
|
# Trims the given string on both sides |
# |
# @param [String] str The string to trim |
# @param [String] _char The character to use for trimming (default: '\\s') |
# |
# @return [String] A trimmed string |
# |
@trim: (str, _char = '\\s') -> |
return str.trim() |
regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char] |
unless regexLeft? |
@REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*' |
regexLeft.lastIndex = 0 |
regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char] |
unless regexRight? |
@REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$' |
regexRight.lastIndex = 0 |
return str.replace(regexLeft, '').replace(regexRight, '') |
|
|
# Trims the given string on the left side |
# |
# @param [String] str The string to trim |
# @param [String] _char The character to use for trimming (default: '\\s') |
# |
# @return [String] A trimmed string |
# |
@ltrim: (str, _char = '\\s') -> |
regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char] |
unless regexLeft? |
@REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*' |
regexLeft.lastIndex = 0 |
return str.replace(regexLeft, '') |
|
|
# Trims the given string on the right side |
# |
# @param [String] str The string to trim |
# @param [String] _char The character to use for trimming (default: '\\s') |
# |
# @return [String] A trimmed string |
# |
@rtrim: (str, _char = '\\s') -> |
regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char] |
unless regexRight? |
@REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$' |
regexRight.lastIndex = 0 |
return str.replace(regexRight, '') |
|
|
# Checks if the given value is empty (null, undefined, empty string, string '0') |
# |
# @param [Object] value The value to check |
# |
# @return [Boolean] true if the value is empty |
# |
@isEmpty: (value) -> |
return not(value) or value is '' or value is '0' or (value instanceof Array and value.length is 0) |
|
|
# Counts the number of occurences of subString inside string |
# |
# @param [String] string The string where to count occurences |
# @param [String] subString The subString to count |
# @param [Integer] start The start index |
# @param [Integer] length The string length until where to count |
# |
# @return [Integer] The number of occurences |
# |
@subStrCount: (string, subString, start, length) -> |
c = 0 |
|
string = '' + string |
subString = '' + subString |
|
if start? |
string = string[start..] |
if length? |
string = string[0...length] |
|
len = string.length |
sublen = subString.length |
for i in [0...len] |
if subString is string[i...sublen] |
c++ |
i += sublen - 1 |
|
return c |
|
|
# Returns true if input is only composed of digits |
# |
# @param [Object] input The value to test |
# |
# @return [Boolean] true if input is only composed of digits |
# |
@isDigits: (input) -> |
@REGEX_DIGITS.lastIndex = 0 |
return @REGEX_DIGITS.test input |
|
|
# Decode octal value |
# |
# @param [String] input The value to decode |
# |
# @return [Integer] The decoded value |
# |
@octDec: (input) -> |
@REGEX_OCTAL.lastIndex = 0 |
return parseInt((input+'').replace(@REGEX_OCTAL, ''), 8) |
|
|
# Decode hexadecimal value |
# |
# @param [String] input The value to decode |
# |
# @return [Integer] The decoded value |
# |
@hexDec: (input) -> |
@REGEX_HEXADECIMAL.lastIndex = 0 |
input = @trim(input) |
if (input+'')[0...2] is '0x' then input = (input+'')[2..] |
return parseInt((input+'').replace(@REGEX_HEXADECIMAL, ''), 16) |
|
|
# Get the UTF-8 character for the given code point. |
# |
# @param [Integer] c The unicode code point |
# |
# @return [String] The corresponding UTF-8 character |
# |
@utf8chr: (c) -> |
ch = String.fromCharCode |
if 0x80 > (c %= 0x200000) |
return ch(c) |
if 0x800 > c |
return ch(0xC0 | c>>6) + ch(0x80 | c & 0x3F) |
if 0x10000 > c |
return ch(0xE0 | c>>12) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F) |
|
return ch(0xF0 | c>>18) + ch(0x80 | c>>12 & 0x3F) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F) |
|
|
# Returns the boolean value equivalent to the given input |
# |
# @param [String|Object] input The input value |
# @param [Boolean] strict If set to false, accept 'yes' and 'no' as boolean values |
# |
# @return [Boolean] the boolean value |
# |
@parseBoolean: (input, strict = true) -> |
if typeof(input) is 'string' |
lowerInput = input.toLowerCase() |
if not strict |
if lowerInput is 'no' then return false |
if lowerInput is '0' then return false |
if lowerInput is 'false' then return false |
if lowerInput is '' then return false |
return true |
return !!input |
|
|
|
# Returns true if input is numeric |
# |
# @param [Object] input The value to test |
# |
# @return [Boolean] true if input is numeric |
# |
@isNumeric: (input) -> |
@REGEX_SPACES.lastIndex = 0 |
return typeof(input) is 'number' or typeof(input) is 'string' and !isNaN(input) and input.replace(@REGEX_SPACES, '') isnt '' |
|
|
# Returns a parsed date from the given string |
# |
# @param [String] str The date string to parse |
# |
# @return [Date] The parsed date or null if parsing failed |
# |
@stringToDate: (str) -> |
unless str?.length |
return null |
|
# Perform regular expression pattern |
info = @PATTERN_DATE.exec str |
unless info |
return null |
|
# Extract year, month, day |
year = parseInt info.year, 10 |
month = parseInt(info.month, 10) - 1 # In javascript, january is 0, february 1, etc... |
day = parseInt info.day, 10 |
|
# If no hour is given, return a date with day precision |
unless info.hour? |
date = new Date Date.UTC(year, month, day) |
return date |
|
# Extract hour, minute, second |
hour = parseInt info.hour, 10 |
minute = parseInt info.minute, 10 |
second = parseInt info.second, 10 |
|
# Extract fraction, if given |
if info.fraction? |
fraction = info.fraction[0...3] |
while fraction.length < 3 |
fraction += '0' |
fraction = parseInt fraction, 10 |
else |
fraction = 0 |
|
# Compute timezone offset if given |
if info.tz? |
tz_hour = parseInt info.tz_hour, 10 |
if info.tz_minute? |
tz_minute = parseInt info.tz_minute, 10 |
else |
tz_minute = 0 |
|
# Compute timezone delta in ms |
tz_offset = (tz_hour * 60 + tz_minute) * 60000 |
if '-' is info.tz_sign |
tz_offset *= -1 |
|
# Compute date |
date = new Date Date.UTC(year, month, day, hour, minute, second, fraction) |
if tz_offset |
date.setTime date.getTime() + tz_offset |
|
return date |
|
|
# Repeats the given string a number of times |
# |
# @param [String] str The string to repeat |
# @param [Integer] number The number of times to repeat the string |
# |
# @return [String] The repeated string |
# |
@strRepeat: (str, number) -> |
res = '' |
i = 0 |
while i < number |
res += str |
i++ |
return res |
|
|
# Reads the data from the given file path and returns the result as string |
# |
# @param [String] path The path to the file |
# @param [Function] callback A callback to read file asynchronously (optional) |
# |
# @return [String] The resulting data as string |
# |
@getStringFromFile: (path, callback = null) -> |
xhr = null |
if window? |
if window.XMLHttpRequest |
xhr = new XMLHttpRequest() |
else if window.ActiveXObject |
for name in ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] |
try |
xhr = new ActiveXObject(name) |
|
if xhr? |
# Browser |
if callback? |
# Async |
xhr.onreadystatechange = -> |
if xhr.readyState is 4 |
if xhr.status is 200 or xhr.status is 0 |
callback(xhr.responseText) |
else |
callback(null) |
xhr.open 'GET', path, true |
xhr.send null |
|
else |
# Sync |
xhr.open 'GET', path, false |
xhr.send null |
|
if xhr.status is 200 or xhr.status == 0 |
return xhr.responseText |
|
return null |
else |
# Node.js-like |
req = require |
fs = req('fs') # Prevent browserify from trying to load 'fs' module |
if callback? |
# Async |
fs.readFile path, (err, data) -> |
if err |
callback null |
else |
callback String(data) |
|
else |
# Sync |
data = fs.readFileSync path |
if data? |
return String(data) |
return null |
|
|
|
module.exports = Utils |
/bower_components/yaml.js/src/Yaml.coffee |
@@ -0,0 +1,118 @@ |
|
Parser = require './Parser' |
Dumper = require './Dumper' |
Utils = require './Utils' |
|
# Yaml offers convenience methods to load and dump YAML. |
# |
class Yaml |
|
# Parses YAML into a JavaScript object. |
# |
# The parse method, when supplied with a YAML string, |
# will do its best to convert YAML in a file into a JavaScript object. |
# |
# Usage: |
# myObject = Yaml.parse('some: yaml'); |
# console.log(myObject); |
# |
# @param [String] input A string containing YAML |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types, false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
# @return [Object] The YAML converted to a JavaScript object |
# |
# @throw [ParseException] If the YAML is not valid |
# |
@parse: (input, exceptionOnInvalidType = false, objectDecoder = null) -> |
return new Parser().parse(input, exceptionOnInvalidType, objectDecoder) |
|
|
# Parses YAML from file path into a JavaScript object. |
# |
# The parseFile method, when supplied with a YAML file, |
# will do its best to convert YAML in a file into a JavaScript object. |
# |
# Usage: |
# myObject = Yaml.parseFile('config.yml'); |
# console.log(myObject); |
# |
# @param [String] path A file path pointing to a valid YAML file |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types, false otherwise |
# @param [Function] objectDecoder A function to deserialize custom objects, null otherwise |
# |
# @return [Object] The YAML converted to a JavaScript object or null if the file doesn't exist. |
# |
# @throw [ParseException] If the YAML is not valid |
# |
@parseFile: (path, callback = null, exceptionOnInvalidType = false, objectDecoder = null) -> |
if callback? |
# Async |
Utils.getStringFromFile path, (input) => |
result = null |
if input? |
result = @parse input, exceptionOnInvalidType, objectDecoder |
callback result |
return |
else |
# Sync |
input = Utils.getStringFromFile path |
if input? |
return @parse input, exceptionOnInvalidType, objectDecoder |
return null |
|
|
# Dumps a JavaScript object to a YAML string. |
# |
# The dump method, when supplied with an object, will do its best |
# to convert the object into friendly YAML. |
# |
# @param [Object] input JavaScript object |
# @param [Integer] inline The level where you switch to inline YAML |
# @param [Integer] indent The amount of spaces to use for indentation of nested nodes. |
# @param [Boolean] exceptionOnInvalidType true if an exception must be thrown on invalid types (a JavaScript resource or object), false otherwise |
# @param [Function] objectEncoder A function to serialize custom objects, null otherwise |
# |
# @return [String] A YAML string representing the original JavaScript object |
# |
@dump: (input, inline = 2, indent = 4, exceptionOnInvalidType = false, objectEncoder = null) -> |
yaml = new Dumper() |
yaml.indentation = indent |
|
return yaml.dump(input, inline, 0, exceptionOnInvalidType, objectEncoder) |
|
|
# Registers .yml extension to work with node's require() function. |
# |
@register: -> |
require_handler = (module, filename) -> |
# Fill in result |
module.exports = YAML.parseFile filename |
|
# Register require extensions only if we're on node.js |
# hack for browserify |
if require?.extensions? |
require.extensions['.yml'] = require_handler |
require.extensions['.yaml'] = require_handler |
|
|
# Alias of dump() method for compatibility reasons. |
# |
@stringify: (input, inline, indent, exceptionOnInvalidType, objectEncoder) -> |
return @dump input, inline, indent, exceptionOnInvalidType, objectEncoder |
|
|
# Alias of parseFile() method for compatibility reasons. |
# |
@load: (path, callback, exceptionOnInvalidType, objectDecoder) -> |
return @parseFile path, callback, exceptionOnInvalidType, objectDecoder |
|
|
# Expose YAML namespace to browser |
window?.YAML = Yaml |
|
# Not in the browser? |
unless window? |
@YAML = Yaml |
|
module.exports = Yaml |