/** * The MIT License (MIT) * * Copyright (c) 2014 Gabriel Llamas * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *
*/
var parse = function (data, options, handlers, control){ var c; var code; var escape; var skipSpace = true; var isCommentLine; var isSectionLine; var newLine = true; var multiLine; var isKey = true; var key = ""; var value = ""; var section; var unicode; var unicodeRemaining; var escapingUnicode; var keySpace; var sep; var ignoreLine;
var line = function (){ if (key || value || sep){
handlers.line (key, value);
key = "";
value = "";
sep = false;
}
};
var escapeString = function (key, c, code){ if (escapingUnicode && unicodeRemaining){
unicode = (unicode << 4) + hex (c); if (--unicodeRemaining) return key;
escape = false;
escapingUnicode = false; return key + String.fromCharCode (unicode);
}
//code 117: u if (code === 117){
unicode = 0;
escapingUnicode = true;
unicodeRemaining = 4; return key;
}
escape = false;
//code 116: t //code 114: r //code 110: n //code 102: f if (code === 116) return key + "\t"; elseif (code === 114) return key + "\r"; elseif (code === 110) return key + "\n"; elseif (code === 102) return key + "\f";
return key + c;
};
var isComment; var isSeparator;
if (options._strict){
isComment = function (c, code, options){ return options._comments[c];
};
for (var i=~~control.resume; i<data.length; i++){ if (control.abort) return; if (control.pause){ //The next index is always the start of a new line, it's a like a fresh //start, there's no need to save the current state
control.resume = i; return;
}
//code 93: ] if (isSectionLine && code === 93){
handlers.section (section); //Ignore chars after the section in the same line
ignoreLine = true; continue;
}
if (skipSpace){ //code 32: " " (space) //code 9: \t //code 12: \f if (code === 32 || code === 9 || code === 12){ continue;
} //code 10: \n if (!multiLine && code === 10){ //Empty line or key w/ separator and w/o value
isKey = true;
keySpace = false;
newLine = true;
line (); continue;
}
skipSpace = false;
multiLine = false;
}
//code 10: \n if (code !== 10){ if (control.skipSection || ignoreLine) continue;
if (!isSectionLine){ if (!escape && isKey && isSeparator (c, code, options)){ //sep is needed to detect empty key and empty value with a //non-whitespace separator
sep = true;
isKey = false;
keySpace = false; //Skip whitespace between separator and value
skipSpace = true; continue;
}
}
//code 92: "\" (backslash) if (code === 92){ if (escape){ if (escapingUnicode) continue;
if (keySpace){ //Line with whitespace separator
keySpace = false;
isKey = false;
}
if (isSectionLine) section += "\\"; elseif (isKey) key += "\\"; else value += "\\";
}
escape = !escape;
}else{ if (keySpace){ //Line with whitespace separator
keySpace = false;
isKey = false;
}
if (isSectionLine){ if (escape) section = escapeString (section, c, code); else section += c;
}elseif (isKey){ if (escape){
key = escapeString (key, c, code);
}else{ //code 32: " " (space) //code 9: \t //code 12: \f if (code === 32 || code === 9 || code === 12){
keySpace = true; //Skip whitespace between key and separator
skipSpace = true; continue;
}
key += c;
}
}else{ if (escape) value = escapeString (value, c, code); else value += c;
}
}
}else{ if (escape){ if (!escapingUnicode){
escape = false;
}
skipSpace = true;
multiLine = true;
}else{ if (isSectionLine){
isSectionLine = false; if (!ignoreLine){ //The section doesn't end with ], it's a key
control.error = new Error ("The section line \"" + section + "\" must end with \"]\""); return;
}
ignoreLine = false;
}
newLine = true;
skipSpace = true;
isKey = true;
line ();
}
}
}
control.parsed = true;
if (isSectionLine && !ignoreLine){ //The section doesn't end with ], it's a key
control.error = new Error ("The section line \"" + section + "\" must end" + "with \"]\""); return;
}
line ();
};
var INCLUDE_KEY = "include"; var INDEX_FILE = "index.properties";
var cast = function (value){ if (value === null || value === "null") returnnull; if (value === "undefined") return undefined; if (value === "true") returntrue; if (value === "false") returnfalse; var v = Number (value); return isNaN (v) ? value : v;
};
var expand = function (o, str, options, cb){ if (!options.variables || !str) return cb (null, str);
var stack = []; var c; var cp; var key = ""; var section = null; var v; var holder; var t; var n;
for (var i=0; i<str.length; i++){
c = str[i];
if (cp === "$" && c === "{"){
key = key.substring (0, key.length - 1);
stack.push ({
key: key,
section: section
});
key = "";
section = null; continue;
}elseif (stack.length){ if (options.sections && c === "|"){
section = key;
key = ""; continue;
}elseif (c === "}"){
holder = section !== null ? searchValue (o, section, true) : o; if (!holder){ return cb (new Error ("The section \"" + section + "\" does not " + "exist"));
}
v = options.namespaces ? searchValue (holder, key) : holder[key]; if (v === undefined){ //Read the external vars
v = options.namespaces
? searchValue (options._vars, key)
: options._vars[key]
if (v === undefined){ return cb (new Error ("The property \"" + key + "\" does not " + "exist"));
}
}
if (stack.length !== 0){ return cb (new Error ("Malformed variable: " + str));
}
cb (null, key);
};
var searchValue = function (o, chain, section){ var n = chain.split ("."); var str;
for (var i=0; i<n.length-1; i++){
str = n[i]; if (o[str] === undefined) return;
o = o[str];
}
var v = o[n[n.length - 1]]; if (section){ if (typeof v !== "object") return; return v;
}else{ if (typeof v === "object") return; return v;
}
};
var namespaceKey = function (o, key, value){ var n = key.split ("."); var str;
for (var i=0; i<n.length-1; i++){
str = n[i]; if (o[str] === undefined){
o[str] = {};
}elseif (typeof o[str] !== "object"){ thrownew Error ("Invalid namespace chain in the property name '" +
key + "' ('" + str + "' has already a value)");
}
o = o[str];
}
o[n[n.length - 1]] = value;
};
var namespaceSection = function (o, section){ var n = section.split ("."); var str;
for (var i=0; i<n.length; i++){
str = n[i]; if (o[str] === undefined){
o[str] = {};
}elseif (typeof o[str] !== "object"){ thrownew Error ("Invalid namespace chain in the section name '" +
section + "' ('" + str + "' has already a value)");
}
o = o[str];
}
return o;
};
var merge = function (o1, o2){ for (var p in o2){ try{ if (o1[p].constructor === Object){
o1[p] = merge (o1[p], o2[p]);
}else{
o1[p] = o2[p];
}
}catch (e){
o1[p] = o2[p];
}
} return o1;
}
var build = function (data, options, dirname, cb){ var o = {};
if (options.namespaces){ var n = {};
}
var control = {
abort: false,
skipSection: false
};
if (options.include){ var remainingIncluded = 0;
var include = function (value){ if (currentSection !== null){ return abort (new Error ("Cannot include files from inside a " + "section: " + currentSection));
}
var p = path.resolve (dirname, value); if (options._included[p]) return;
read (p, options, function (error, included){ if (error) return abort (error);
remainingIncluded--;
merge (options.namespaces ? n : o, included);
control.pause = false;
if (!control.parsed){
parse (data, options, handlers, control); if (control.error) return abort (control.error);
}
if (!remainingIncluded) cb (null, options.namespaces ? n : o);
});
};
}
if (!data){ if (cb) return cb (null, o); return o;
}
var currentSection = null; var currentSectionStr = null;
var abort = function (error){
control.abort = true; if (cb) return cb (error); throw error;
};
var handlers = {}; var reviver = { assert: function (){ returnthis.isProperty ? reviverLine.value : true;
}
}; var reviverLine = {};
//Line handler //For speed reasons, if "namespaces" is enabled, the old object is still //populated, e.g.: ${a.b} reads the "a.b" property from { "a.b": 1 }, instead //of having a unique object { a: { b: 1 } } which is slower to search for //the "a.b" value //If "a.b" is not found, then the external vars are read. If "namespaces" is //enabled, the var "a.b" is split and it searches the a.b value. If it is not //enabled, then the var "a.b" searches the "a.b" value
var line; var error;
if (options.reviver){ if (options.sections){
line = function (key, value){ if (options.include && key === INCLUDE_KEY) return include (value);
//Variables if (options.variables){
handlers.line = function (key, value){
expand (options.namespaces ? n : o, key, options, function (error, key){ if (error) return abort (error);
expand (options.namespaces ? n : o, value, options, function (error, value){ if (error) return abort (error);
line (key, cast (value || null));
});
});
};
if (options.sections){
handlers.section = function (s){
expand (options.namespaces ? n : o, s, options, function (error, s){ if (error) return abort (error);
section (s);
});
};
}
}else{
handlers.line = function (key, value){
line (key, cast (value || null));
};
if (options.sections){
handlers.section = section;
}
}
parse (data, options, handlers, control); if (control.error) return abort (control.error);
if (control.abort || control.pause) return;
if (cb) return cb (null, options.namespaces ? n : o); return options.namespaces ? n : o;
};
var read = function (f, options, cb){
fs.stat (f, function (error, stats){ if (error) return cb (error);
var dirname;
if (stats.isDirectory ()){
dirname = f;
f = path.join (f, INDEX_FILE);
}else{
dirname = path.dirname (f);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.