diff --git a/package.json b/package.json
index edff167..4ee30b4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sffjs",
- "version": "1.10.0-alpha.8",
+ "version": "1.10.0",
"description": "String.Format for JavaScript",
"main": "sffjs.js",
"repository": {
diff --git a/src/stringformat.js b/src/stringformat.js
index 99a3932..13bd1ce 100644
--- a/src/stringformat.js
+++ b/src/stringformat.js
@@ -174,8 +174,8 @@
// Parse and evaluate path
if (hasValue(value)) {
- var followingMembers = /(\.([a-zA-Z_$]\w+)|\[(\d+)\])/g,
- match = /^[a-zA-Z_$]\w+/.exec(path);
+ var followingMembers = /(\.([a-zA-Z_$]\w*)|\[(\d+)\])/g,
+ match = /^[a-zA-Z_$]\w*/.exec(path);
value = value[match[0]];
@@ -212,14 +212,6 @@
}
}
- function unescapeBraces(braces, consumedBraces) {
- /// Replaces escaped brackets ({ and }) with their unescaped representation.
- /// A string containing braces of a single type only.
- /// The number of braces that should be ignored when unescaping.
- /// A string of the unescaped braces.
- return braces.substr(0, (braces.length + 1 - (consumedBraces || 0)) / 2);
- }
-
function processFormatItem(pathOrIndex, align, formatString, args) {
/// Process a single format item in a composite format string
/// The raw argument index or path component of the format item.
@@ -741,25 +733,22 @@
var outerArgs = arguments;
- return str.replace(/(\{+)((\d+|[a-zA-Z_$]\w+(?:\.[a-zA-Z_$]\w+|\[\d+\])*)(?:\,(-?\d*))?(?:\:([^\}]*))?)(\}+)|(\{+)|(\}+)/g, function() {
+ return str.replace(/\{((\d+|[a-zA-Z_$]\w*(?:\.[a-zA-Z_$]\w*|\[\d+\])*)(?:\,(-?\d*))?(?:\:([^\}]*(?:(?:\}\})+[^\}]+)*))?)\}|(\{\{)|(\}\})/g, function() {
var innerArgs = arguments;
// Handle escaped {
- return innerArgs[7] ? unescapeBraces(innerArgs[7]) :
+ return innerArgs[5] ? "{" :
// Handle escaped }
- innerArgs[8] ? unescapeBraces(innerArgs[8]) :
-
- // Handle case when both { and } are present, but one or both of them are escaped
- !(innerArgs[1].length % 2 && innerArgs[6].length % 2) ?
- unescapeBraces(innerArgs[1]) +
- innerArgs[2] +
- unescapeBraces(innerArgs[6]) :
+ innerArgs[6] ? "}" :
// Valid format item
- unescapeBraces(innerArgs[1], 1) +
- processFormatItem(innerArgs[3], innerArgs[4], innerArgs[5], outerArgs) +
- unescapeBraces(innerArgs[6], 1);
+ processFormatItem(
+ innerArgs[2],
+ innerArgs[3],
+ // Format string might contain escaped braces
+ innerArgs[4] && innerArgs[4].replace(/\}\}/g, "}").replace(/\{\{/g, "{"),
+ outerArgs);
});
}
diff --git a/src/stringformat.tests.js b/src/stringformat.tests.js
index 08ba203..ad043e6 100644
--- a/src/stringformat.tests.js
+++ b/src/stringformat.tests.js
@@ -37,14 +37,20 @@
sffjs.setCulture("en");
var testObject = {
- authors: [{
+ a: "b",
+ authors: [
+ {
firstname: "John",
lastname: "Doe",
- phonenumbers: [{
+ phonenumbers: [
+ {
+ home: "012",
home: "345"
- }],
+ }
+ ],
age: 27
- }]
+ }
+ ]
};
var undefined;
@@ -53,7 +59,7 @@
assert.formatsTo("Test {with} brackets", "Test {{with}} brackets");
assert.formatsTo("{brackets} in args", "{0} in args", "{brackets}");
assert.formatsTo("{{dblbrackets}} in args", "{0} in args", "{{dblbrackets}}");
- assert.formatsTo("Mismatch {{0}", "Mismatch {{{0}}", "{{brackets}");
+ assert.formatsTo("Mismatch {{{brackets}}", "Mismatch {{{0}}", "{{brackets}");
assert.formatsTo("Double outer {{{brackets}}", "Double outer {{{0}}}", "{{brackets}");
test.section("Index");
@@ -62,9 +68,7 @@
assert.formatsTo("!true!", "!{0}!", true);
assert.formatsTo("null:!!", "null:!{0}!", null);
assert.formatsTo("undefined:!!", "undefined:!{0}!", undefined);
- assert.doesThrow(function() {
- String.format("{1}", 42);
- }, "Missing argument", "Index out of range");
+ assert.doesThrow(function () { String.format("{1}", 42) }, "Missing argument", "Index out of range");
assert.formatsTo("Negative index:!{-1}!", "Negative index:!{-1}!", 42);
test.section("Path");
@@ -78,6 +82,7 @@
assert.formatsTo("Hi, !", "Hi, {authors.fdg}!", testObject);
assert.formatsTo("Hi, 1!", "Hi, {authors.length}!", testObject);
assert.formatsTo("1.00", "{authors.length:0.00}", testObject);
+ assert.formatsTo("After a comes b.", "After a comes {a}.", testObject);
test.section("Invalid paths");
assert.formatsTo("Hi, {fg$}!", "Hi, {fg$}!", undefined);
@@ -87,6 +92,21 @@
assert.formatsTo("Hi, {.fg}!", "Hi, {.fg}!", undefined);
assert.formatsTo("Hi, {a..b}!", "Hi, {a..b}!", undefined);
+ test.section("Escaped braces");
+ assert.formatsTo("a { b", "a {{ b", testObject);
+ assert.formatsTo("a } b", "a }} b", testObject);
+ assert.formatsTo("a{{a}}", "a{{{{a}}}", testObject); // *
+ assert.formatsTo("a{{b}", "a{{{{{a}}}", testObject);
+ assert.formatsTo("a{aba}a", "a{{a{a}a}}a", testObject);
+ assert.formatsTo("a{{aba", "a{{{a{a}a", testObject); // *
+ assert.formatsTo("a{bbb{}a", "a{{b{a}{a}{}a", testObject); // *
+ assert.formatsTo("4}.2", "{0:0}}.0}", 4.2);
+ assert.formatsTo("4{.2", "{0:0{{.0}", 4.2);
+ assert.formatsTo("4}{{}.2", "{0:0}}{{{{}}.0}", 4.2);
+ // * These tests do not produce the same output as in .NET. In .NET these format strings will
+ // generate a FormatException while the JS implementation makes a best effort to finish processing
+ // the format string.
+
var dtam = new Date(1989, 3, 2, 6, 20, 33);
var dtpm = new Date(1989, 3, 2, 18, 20, 33);
var dt2009 = new Date(2009, 3, 2, 18, 20, 33);
@@ -352,22 +372,12 @@
assert.formatsTo("{brackets} in args", "{0} in args", "{brackets}");
assert.formatsTo("{{dblbrackets}} in args", "{0} in args", "{{dblbrackets}}");
- assert.formatsTo("Mismatch {{0}", "Mismatch {{{0}}", "{{brackets}");
- assert.formatsTo("Double outer {{{brackets}}", "Double outer {{{0}}}", "{{brackets}");
test.section("setCulture");
- sffjs.registerCulture({
- name: "__LANG"
- });
- sffjs.registerCulture({
- name: "__LANG-REGION"
- });
- sffjs.registerCulture({
- name: "__LANG2"
- });
- sffjs.registerCulture({
- name: "__LANG3-region"
- });
+ sffjs.registerCulture({ name: "__LANG" });
+ sffjs.registerCulture({ name: "__LANG-REGION" });
+ sffjs.registerCulture({ name: "__LANG2" });
+ sffjs.registerCulture({ name: "__LANG3-region" });
sffjs.setCulture("");
assert.areEqual("", sffjs.LC.name, "Invariant culture");
@@ -387,14 +397,13 @@
sffjs.setCulture("__LANG3");
assert.areEqual("", sffjs.LC.name, "Non-existing neutral");
- sffjs.registerCulture({
- name: "__Lang3"
- });
+ sffjs.registerCulture({ name: "__Lang3" });
assert.areEqual("__Lang3", sffjs.LC.name, "Delayed neutral");
sffjs.setCulture("");
}
+
function Test() {
var t = this;