128 lines
3.4 KiB
JavaScript
128 lines
3.4 KiB
JavaScript
/* Hjson http://hjson.org */
|
|
"use strict";
|
|
|
|
function loadDsf(col, type) {
|
|
|
|
if (Object.prototype.toString.apply(col) !== '[object Array]') {
|
|
if (col) throw new Error("dsf option must contain an array!");
|
|
else return nopDsf;
|
|
} else if (col.length === 0) return nopDsf;
|
|
|
|
var dsf = [];
|
|
function isFunction(f) { return {}.toString.call(f) === '[object Function]'; }
|
|
|
|
col.forEach(function(x) {
|
|
if (!x.name || !isFunction(x.parse) || !isFunction(x.stringify))
|
|
throw new Error("extension does not match the DSF interface");
|
|
dsf.push(function() {
|
|
try {
|
|
if (type == "parse") {
|
|
return x.parse.apply(null, arguments);
|
|
} else if (type == "stringify") {
|
|
var res=x.stringify.apply(null, arguments);
|
|
// check result
|
|
if (res !== undefined && (typeof res !== "string" ||
|
|
res.length === 0 ||
|
|
res[0] === '"' ||
|
|
[].some.call(res, function(c) { return isInvalidDsfChar(c); })))
|
|
throw new Error("value may not be empty, start with a quote or contain a punctuator character except colon: " + res);
|
|
return res;
|
|
} else throw new Error("Invalid type");
|
|
} catch (e) {
|
|
throw new Error("DSF-"+x.name+" failed; "+e.message);
|
|
}
|
|
});
|
|
});
|
|
|
|
return runDsf.bind(null, dsf);
|
|
}
|
|
|
|
function runDsf(dsf, value) {
|
|
if (dsf) {
|
|
for (var i = 0; i < dsf.length; i++) {
|
|
var res = dsf[i](value);
|
|
if (res !== undefined) return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
function nopDsf(/*value*/) {
|
|
}
|
|
|
|
function isInvalidDsfChar(c) {
|
|
return c === '{' || c === '}' || c === '[' || c === ']' || c === ',';
|
|
}
|
|
|
|
|
|
function math(/*opt*/) {
|
|
return {
|
|
name: "math",
|
|
parse: function (value) {
|
|
switch (value) {
|
|
case "+inf":
|
|
case "inf":
|
|
case "+Inf":
|
|
case "Inf": return Infinity;
|
|
case "-inf":
|
|
case "-Inf": return -Infinity;
|
|
case "nan":
|
|
case "NaN": return NaN;
|
|
}
|
|
},
|
|
stringify: function (value) {
|
|
if (typeof value !== 'number') return;
|
|
if (1 / value === -Infinity) return "-0"; // 0 === -0
|
|
if (value === Infinity) return "Inf";
|
|
if (value === -Infinity) return "-Inf";
|
|
if (isNaN(value)) return "NaN";
|
|
},
|
|
};
|
|
}
|
|
math.description="support for Inf/inf, -Inf/-inf, Nan/naN and -0";
|
|
|
|
function hex(opt) {
|
|
var out=opt && opt.out;
|
|
return {
|
|
name: "hex",
|
|
parse: function (value) {
|
|
if (/^0x[0-9A-Fa-f]+$/.test(value))
|
|
return parseInt(value, 16);
|
|
},
|
|
stringify: function (value) {
|
|
if (out && Number.isInteger(value))
|
|
return "0x"+value.toString(16);
|
|
},
|
|
};
|
|
}
|
|
hex.description="parse hexadecimal numbers prefixed with 0x";
|
|
|
|
function date(/*opt*/) {
|
|
return {
|
|
name: "date",
|
|
parse: function (value) {
|
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value) ||
|
|
/^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}(?:.\d+)(?:Z|[+-]\d{2}:\d{2})$/.test(value)) {
|
|
var dt = Date.parse(value);
|
|
if (!isNaN(dt)) return new Date(dt);
|
|
}
|
|
},
|
|
stringify: function (value) {
|
|
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
var dt = value.toISOString();
|
|
if (dt.indexOf("T00:00:00.000Z", dt.length - 14) !== -1) return dt.substr(0, 10);
|
|
else return dt;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
date.description="support ISO dates";
|
|
|
|
module.exports = {
|
|
loadDsf: loadDsf,
|
|
std: {
|
|
math: math,
|
|
hex: hex,
|
|
date: date,
|
|
},
|
|
};
|