Skip to content

Commit

Permalink
fixes from the upstream (dmester/sffjs)
Browse files Browse the repository at this point in the history
- Bug fix: single letter object paths were not parsed.
- Bug fix: escaped braces were incorrectly handled when preceding a valid format item.
- Bug fix: escaped braces inside format items were not handled properly.
  • Loading branch information
thorn0 committed Oct 16, 2015
1 parent 7911d49 commit 6bd9eff
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 48 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
33 changes: 11 additions & 22 deletions src/stringformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]];

Expand Down Expand Up @@ -212,14 +212,6 @@
}
}

function unescapeBraces(braces, consumedBraces) {
/// <summary>Replaces escaped brackets ({ and }) with their unescaped representation.</summary>
/// <param name="braces">A string containing braces of a single type only.</param>
/// <param name="consumedBraces">The number of braces that should be ignored when unescaping.</param>
/// <returns>A string of the unescaped braces.</returns>
return braces.substr(0, (braces.length + 1 - (consumedBraces || 0)) / 2);
}

function processFormatItem(pathOrIndex, align, formatString, args) {
/// <summary>Process a single format item in a composite format string</summary>
/// <param name="pathOrIndex" type="String">The raw argument index or path component of the format item.</param>
Expand Down Expand Up @@ -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);
});
}

Expand Down
59 changes: 34 additions & 25 deletions src/stringformat.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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");
Expand All @@ -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;

Expand Down

0 comments on commit 6bd9eff

Please sign in to comment.