diff --git a/.travis.yml b/.travis.yml index d888943..2245011 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "0.8" + - "4.4.5" git: depth: 3 diff --git a/dev/src/pattern_lexer.js b/dev/src/pattern_lexer.js index f6ad89c..cc00827 100644 --- a/dev/src/pattern_lexer.js +++ b/dev/src/pattern_lexer.js @@ -15,6 +15,27 @@ //params - everything between `{ }` or `: :` PARAMS_REGEXP = /(?:\{|:)([^}:]+)(?:\}|:)/g, + fnReplaceOS = function(match, offset, string) { + var rsIndex = string.lastIndexOf("__CR_RS__"), //last index of required slash + rqIndex = string.indexOf("__CR_RQ__"), //first index of required query + oqIndex = string.indexOf("__CR_OQ__"), //first index of optional query + slashIndex = string.indexOf("\\/", offset), //first index of char slash after the matched offset + qIndex = Math.min(oqIndex === -1 ? string.length : oqIndex, rqIndex === -1 ? string.length : rqIndex), + endPosition = offset + match.length; + + if (endPosition >= qIndex || endPosition < rsIndex || endPosition < slashIndex) { + //regex for optinal slash is returned when the optional slash: + //1. immediately precedes a query (either optional or required) or appears after a query + //2. appears before a required slash + //3. appears before a slash char in the pattern + return TOKENS.OS.res_normal; + } else { + //otherwise the slash isn't fully optional and return the regex defined + //for optional slash "after required slash or before query" + return TOKENS.OS.res_arsbq; + } + }, + //used to save params during compile (avoid escaping things that //shouldn't be escaped). TOKENS = { @@ -23,7 +44,15 @@ //slash between `::` or `}:` or `\w:` or `:{?` or `}{?` or `\w{?` rgx : /([:}]|\w(?=\/))\/?(:|(?:\{\?))/g, save : '$1{{id}}$2', - res : '\\/?' + res : fnReplaceOS, + //the regex for optinal slash + res_normal : '\\/?', + //the regex for slash which isn't fully optional based on the given pattern + //for example, given patterns foo/:bar: and foobar/:bar:, the slash isn't fully + //optional because if the slash is optional, hash "foobar" will also match foo/:bar: + //with parameter bar set to "bar" + //arsbq stands for "after required slash or before query" + res_arsbq : '(?:(?:\\/(?=(?:[^\\/?]+)?))|^\\/?|\\/?$)' }, 'RS' : { //required slashes @@ -238,4 +267,3 @@ }; }()); - diff --git a/dev/tests/spec/match.spec.js b/dev/tests/spec/match.spec.js index ee998bf..be8d5ca 100644 --- a/dev/tests/spec/match.spec.js +++ b/dev/tests/spec/match.spec.js @@ -89,6 +89,12 @@ describe('Match', function(){ expect( d.match('/123/45/ipsum') ).toBe( true ); }); + it('should not take the slash as optional one', function(){ + var s = crossroads.addRoute('/123/:bar:'); + expect( s.match('/1234') ).toBe(false); + expect( s.match('/123/4') ).toBe(true); + }); + it('should support multiple consecutive optional params', function(){ var s = crossroads.addRoute('/123/:bar:/:ipsum:'); expect( s.match('/123') ).toBe( true ); diff --git a/dev/tests/spec/parse.spec.js b/dev/tests/spec/parse.spec.js index 872065f..0aeced0 100644 --- a/dev/tests/spec/parse.spec.js +++ b/dev/tests/spec/parse.spec.js @@ -195,6 +195,23 @@ describe('crossroads.parse()', function(){ expect( calls ).toBe( 1 ); }); + it('should capture optional params when option params in the front are omitted', function(){ + var calls = 0; + + var a = crossroads.addRoute('foo/:lorem:/:ipsum:/:dolor:/:sit:'); + a.matched.add(function(a, b, c, d){ + expect( a ).toBeUndefined(); + expect( b ).toBe( '123' ); + expect( c ).toBeUndefined(); + expect( d ).toBeUndefined(); + calls++; + }); + + crossroads.parse('foo//123'); + + expect( calls ).toBe( 1 ); + }) + });