diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..94cc540 Binary files /dev/null and b/.DS_Store differ diff --git a/path.js b/path.js index d56a2d8..dc4b652 100644 --- a/path.js +++ b/path.js @@ -1,192 +1,219 @@ -var Path = { - 'version': "0.8.4", - 'map': function (path) { - if (Path.routes.defined.hasOwnProperty(path)) { - return Path.routes.defined[path]; - } else { - return new Path.core.route(path); - } - }, - 'root': function (path) { - Path.routes.root = path; - }, - 'rescue': function (fn) { - Path.routes.rescue = fn; - }, - 'history': { - 'initial':{}, // Empty container for "Initial Popstate" checking variables. - 'pushState': function(state, title, path){ - if(Path.history.supported){ - if(Path.dispatch(path)){ - history.pushState(state, title, path); - } +!function (ctx) { + + var Path = { + 'version': "0.8.4", + 'map': function (path) { + if (Path.routes.defined.hasOwnProperty(path)) { + return Path.routes.defined[path]; } else { - if(Path.history.fallback){ - window.location.hash = "#" + path; - } + return new Path.core.route(path); } }, - 'popState': function(event){ - var initialPop = !Path.history.initial.popped && location.href == Path.history.initial.URL; - Path.history.initial.popped = true; - if(initialPop) return; - Path.dispatch(document.location.pathname); + 'root': function (path) { + Path.routes.root = path; }, - 'listen': function(fallback){ - Path.history.supported = !!(window.history && window.history.pushState); - Path.history.fallback = fallback; + 'rescue': function (fn) { + Path.routes.rescue = fn; + }, + 'history': { + 'initial':{}, // Empty container for "Initial Popstate" checking variables. + 'pushState': function(state, title, path){ + if(Path.history.supported){ + if(Path.dispatch(path)){ + history.pushState(state, title, path); + } + } else { + if(Path.history.fallback){ + window.location.hash = "#" + path; + } + } + }, + 'popState': function(event){ + var initialPop = !Path.history.initial.popped && location.href == Path.history.initial.URL; + Path.history.initial.popped = true; + if(initialPop) return; + Path.dispatch(document.location.pathname); + }, + 'listen': function(fallback){ + Path.history.supported = !!(window.history && window.history.pushState); + Path.history.fallback = fallback; - if(Path.history.supported){ - Path.history.initial.popped = ('state' in window.history), Path.history.initial.URL = location.href; - window.onpopstate = Path.history.popState; - } else { - if(Path.history.fallback){ - for(route in Path.routes.defined){ - if(route.charAt(0) != "#"){ - Path.routes.defined["#"+route] = Path.routes.defined[route]; - Path.routes.defined["#"+route].path = "#"+route; + if(Path.history.supported){ + Path.history.initial.popped = ('state' in window.history), Path.history.initial.URL = location.href; + window.onpopstate = Path.history.popState; + } else { + if(Path.history.fallback){ + for(route in Path.routes.defined){ + if(route.charAt(0) != "#"){ + Path.routes.defined["#"+route] = Path.routes.defined[route]; + Path.routes.defined["#"+route].path = "#"+route; + } } + Path.listen(); } - Path.listen(); } } - } - }, - 'match': function (path, parameterize) { - var params = {}, route = null, possible_routes, slice, i, j, compare; - for (route in Path.routes.defined) { - if (route !== null && route !== undefined) { - route = Path.routes.defined[route]; - possible_routes = route.partition(); - for (j = 0; j < possible_routes.length; j++) { - slice = possible_routes[j]; - compare = path; - if (slice.search(/:/) > 0) { - for (i = 0; i < slice.split("/").length; i++) { - if ((i < compare.split("/").length) && (slice.split("/")[i].charAt(0) === ":")) { - params[slice.split('/')[i].replace(/:/, '')] = compare.split("/")[i]; - compare = compare.replace(compare.split("/")[i], slice.split("/")[i]); - } + }, + 'match': function (path, parameterize) { + var params = {}, route = null, possible_routes, slice, i, j, compare; + for (route in Path.routes.defined) { + if (route !== null && route !== undefined) { + route = Path.routes.defined[route]; + possible_routes = route.partition(); + + // if a ? is present... + if (path.search(/\?[a-z0-9\-_=&]{1,}/i) > 0) { + var url_params = path.match(/\?[a-z0-9\-_=&]{1,}/i), + param; + + // remove the ?, split params on & if applicable + param = url_params[0].substring(1).split('&'); + + // add params to route params array, allowing fully qualified params (like :id in URL match) + // to overwrite query string params + for(var u in param) { + params[ param[u].split('=')[0] || param[u] ] = param[u].split('=')[1] || true; } + + // remove url query params from path & re-assign + path = path.substring(0, path.search(/\?[a-z0-9\-_=&]+/gi)); } - if (slice === compare) { - if (parameterize) { - route.params = params; + + for (j = 0; j < possible_routes.length; j++) { + slice = possible_routes[j]; + compare = path; + if (slice.search(/:/) > 0) { + for (i = 0; i < slice.split("/").length; i++) { + if ((i < compare.split("/").length) && (slice.split("/")[i].charAt(0) === ":")) { + params[slice.split('/')[i].replace(/:/, '')] = compare.split("/")[i]; + compare = compare.replace(compare.split("/")[i], slice.split("/")[i]); + } + } + } + if (slice === compare) { + if (parameterize) { + route.params = params; + } + return route; } - return route; } } } - } - return null; - }, - 'dispatch': function (passed_route) { - var previous_route, matched_route; - if (Path.routes.current !== passed_route) { - Path.routes.previous = Path.routes.current; - Path.routes.current = passed_route; - matched_route = Path.match(passed_route, true); + return null; + }, + 'dispatch': function (passed_route) { + var previous_route, matched_route; + if (Path.routes.current !== passed_route) { + Path.routes.previous = Path.routes.current; + Path.routes.current = passed_route; + matched_route = Path.match(passed_route, true); - if (Path.routes.previous) { - previous_route = Path.match(Path.routes.previous); - if (previous_route !== null && previous_route.do_exit !== null) { - previous_route.do_exit(); + if (Path.routes.previous) { + previous_route = Path.match(Path.routes.previous); + if (previous_route !== null && previous_route.do_exit !== null) { + previous_route.do_exit(); + } } - } - if (matched_route !== null) { - matched_route.run(); - return true; - } else { - if (Path.routes.rescue !== null) { - Path.routes.rescue(); + if (matched_route !== null) { + matched_route.run(); + return true; + } else { + if (Path.routes.rescue !== null) { + Path.routes.rescue(); + } } } - } - }, - 'listen': function () { - var fn = function(){ Path.dispatch(location.hash); } + }, + 'listen': function () { + var fn = function(){ Path.dispatch(location.hash); } - if (location.hash === "") { - if (Path.routes.root !== null) { - location.hash = Path.routes.root; + if (location.hash === "") { + if (Path.routes.root !== null) { + location.hash = Path.routes.root; + } } - } - // The 'document.documentMode' checks below ensure that PathJS fires the right events - // even in IE "Quirks Mode". - if ("onhashchange" in window && (!document.documentMode || document.documentMode >= 8)) { - window.onhashchange = fn; - } else { - setInterval(fn, 50); - } + // The 'document.documentMode' checks below ensure that PathJS fires the right events + // even in IE "Quirks Mode". + if ("onhashchange" in window && (!document.documentMode || document.documentMode >= 8)) { + window.addEventListener("hashchange", fn, false); + } else { + setInterval(fn, 50); + } - if(location.hash !== "") { - Path.dispatch(location.hash); - } - }, - 'core': { - 'route': function (path) { - this.path = path; - this.action = null; - this.do_enter = []; - this.do_exit = null; - this.params = {}; - Path.routes.defined[path] = this; - } - }, - 'routes': { - 'current': null, - 'root': null, - 'rescue': null, - 'previous': null, - 'defined': {} - } -}; -Path.core.route.prototype = { - 'to': function (fn) { - this.action = fn; - return this; - }, - 'enter': function (fns) { - if (fns instanceof Array) { - this.do_enter = this.do_enter.concat(fns); - } else { - this.do_enter.push(fns); - } - return this; - }, - 'exit': function (fn) { - this.do_exit = fn; - return this; - }, - 'partition': function () { - var parts = [], options = [], re = /\(([^}]+?)\)/g, text, i; - while (text = re.exec(this.path)) { - parts.push(text[1]); - } - options.push(this.path.split("(")[0]); - for (i = 0; i < parts.length; i++) { - options.push(options[options.length - 1] + parts[i]); + if(location.hash !== "") { + Path.dispatch(location.hash); + } + }, + 'core': { + 'route': function (path) { + this.path = path; + this.action = null; + this.do_enter = []; + this.do_exit = null; + this.params = {}; + Path.routes.defined[path] = this; + } + }, + 'routes': { + 'current': null, + 'root': null, + 'rescue': null, + 'previous': null, + 'defined': {} } - return options; - }, - 'run': function () { - var halt_execution = false, i, result, previous; + }; + Path.core.route.prototype = { + 'to': function (fn) { + this.action = fn; + return this; + }, + 'enter': function (fns) { + if (fns instanceof Array) { + this.do_enter = this.do_enter.concat(fns); + } else { + this.do_enter.push(fns); + } + return this; + }, + 'exit': function (fn) { + this.do_exit = fn; + return this; + }, + 'partition': function () { + var parts = [], options = [], re = /\(([^}]+?)\)/g, text, i; + while (text = re.exec(this.path)) { + parts.push(text[1]); + } + options.push(this.path.split("(")[0]); + for (i = 0; i < parts.length; i++) { + options.push(options[options.length - 1] + parts[i]); + } + return options; + }, + 'run': function () { + var halt_execution = false, i, result, previous; - if (Path.routes.defined[this.path].hasOwnProperty("do_enter")) { - if (Path.routes.defined[this.path].do_enter.length > 0) { - for (i = 0; i < Path.routes.defined[this.path].do_enter.length; i++) { - result = Path.routes.defined[this.path].do_enter[i].apply(this, null); - if (result === false) { - halt_execution = true; - break; + if (Path.routes.defined[this.path].hasOwnProperty("do_enter")) { + if (Path.routes.defined[this.path].do_enter.length > 0) { + for (i = 0; i < Path.routes.defined[this.path].do_enter.length; i++) { + result = Path.routes.defined[this.path].do_enter[i].apply(this, null); + if (result === false) { + halt_execution = true; + break; + } } } } + if (!halt_execution) { + Path.routes.defined[this.path].action(); + } } - if (!halt_execution) { - Path.routes.defined[this.path].action(); - } - } -}; \ No newline at end of file + }; + + typeof module !== 'undefined' && module.exports ? + (module.exports.pathjs = Path) : + (ctx.pathjs = Path); + +}(this); \ No newline at end of file diff --git a/path.min.js b/path.min.js index f44e647..46dd099 100644 --- a/path.min.js +++ b/path.min.js @@ -1 +1 @@ -var Path={version:"0.8.4",map:function(a){if(Path.routes.defined.hasOwnProperty(a)){return Path.routes.defined[a]}else{return new Path.core.route(a)}},root:function(a){Path.routes.root=a},rescue:function(a){Path.routes.rescue=a},history:{initial:{},pushState:function(a,b,c){if(Path.history.supported){if(Path.dispatch(c)){history.pushState(a,b,c)}}else{if(Path.history.fallback){window.location.hash="#"+c}}},popState:function(a){var b=!Path.history.initial.popped&&location.href==Path.history.initial.URL;Path.history.initial.popped=true;if(b)return;Path.dispatch(document.location.pathname)},listen:function(a){Path.history.supported=!!(window.history&&window.history.pushState);Path.history.fallback=a;if(Path.history.supported){Path.history.initial.popped="state"in window.history,Path.history.initial.URL=location.href;window.onpopstate=Path.history.popState}else{if(Path.history.fallback){for(route in Path.routes.defined){if(route.charAt(0)!="#"){Path.routes.defined["#"+route]=Path.routes.defined[route];Path.routes.defined["#"+route].path="#"+route}}Path.listen()}}}},match:function(a,b){var c={},d=null,e,f,g,h,i;for(d in Path.routes.defined){if(d!==null&&d!==undefined){d=Path.routes.defined[d];e=d.partition();for(h=0;h0){for(g=0;g=8)){window.onhashchange=a}else{setInterval(a,50)}if(location.hash!==""){Path.dispatch(location.hash)}},core:{route:function(a){this.path=a;this.action=null;this.do_enter=[];this.do_exit=null;this.params={};Path.routes.defined[a]=this}},routes:{current:null,root:null,rescue:null,previous:null,defined:{}}};Path.core.route.prototype={to:function(a){this.action=a;return this},enter:function(a){if(a instanceof Array){this.do_enter=this.do_enter.concat(a)}else{this.do_enter.push(a)}return this},exit:function(a){this.do_exit=a;return this},partition:function(){var a=[],b=[],c=/\(([^}]+?)\)/g,d,e;while(d=c.exec(this.path)){a.push(d[1])}b.push(this.path.split("(")[0]);for(e=0;e0){for(b=0;b0){var l=e.match(/\?[a-z0-9\-_=&]{1,}/i),c;c=l[0].substring(1).split("&");for(var h in c){r[c[h].split("=")[0]||c[h]]=c[h].split("=")[1]||true}e=e.substring(0,e.search(/\?[a-z0-9\-_=&]+/gi))}for(a=0;a0){for(u=0;u=8)){window.addEventListener("hashchange",e,false)}else{setInterval(e,50)}if(location.hash!==""){t.dispatch(location.hash)}},core:{route:function(e){this.path=e;this.action=null;this.do_enter=[];this.do_exit=null;this.params={};t.routes.defined[e]=this}},routes:{current:null,root:null,rescue:null,previous:null,defined:{}}};t.core.route.prototype={to:function(e){this.action=e;return this},enter:function(e){if(e instanceof Array){this.do_enter=this.do_enter.concat(e)}else{this.do_enter.push(e)}return this},exit:function(e){this.do_exit=e;return this},partition:function(){var e=[],t=[],n=/\(([^}]+?)\)/g,r,i;while(r=n.exec(this.path)){e.push(r[1])}t.push(this.path.split("(")[0]);for(i=0;i0){for(n=0;n -
-

Test Suite

+
+

Test Suite

- Path.js uses a very straightforward method of testing. We manually construct + Path.js uses a very straightforward method of testing. We manually construct a series of method calls that the library should execute under normal working conditions. We then use JavaScript to simulate the URL changes, and compare the final result with what the result should actually be. If the end result @@ -149,39 +164,43 @@

Test Suite

is not suitable for use. The expected test results are as follows:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Token Reason
F[enter] Enter method of F, as it is root
F[action] True action of F, as it is root
A[enter] Enter method of A, as it is looped
A[action] True action of A, as it is looped
A[exit] Exit method of A, as we move to next route
B[enter] Enter method of B, as it is looped
B[action] True action of B, as it is looped
C[action] True action of C
C[exit] Exit method of C, as we move to next route
RESCUE Rescue a route that wasn't found (D1)
RESCUE Rescue a route that wasn't found (D2)
E[enter](parse id=1) Enter method of a param parsed route
E[action](parse id=1) True action of the route, with param of id=1
E[enter](parse id=2) Enter method of the same route again
E[action](parse id=2) True action of the route, with param of id=2
E[action](check id=3) True action of the next route, with param id=3
E[exit](check id=3) Exit method of parameterized route
F[enter] Enter method of F again, our final route
F[action] True action of F, our final route
G[enter 1] First enter method of G
G[enter 2] Second enter method of G
G[enter 3] Third enter method of G
G[enter 4] Last enter method of G - Returns false, stops execution
H(one=N/A, two=N/A) Optional parameters with only the require part submitted
H(one=10, two=N/A) Optional parameters with one optional part submitted
H(one=10, two=20) Optional parameters two levels deep
H(one=10, two=N/A) Testing "back" functionality
Token Reason
F[enter] Enter method of F, as it is root
F[action] True action of F, as it is root
A[enter] Enter method of A, as it is looped
A[action] True action of A, as it is looped
A[exit] Exit method of A, as we move to next route
B[enter] Enter method of B, as it is looped
B[action] True action of B, as it is looped
C[action] True action of C
C[exit] Exit method of C, as we move to next route
RESCUE Rescue a route that wasn't found (D1)
RESCUE Rescue a route that wasn't found (D2)
E[enter](parse id=1) Enter method of a param parsed route
E[action](parse id=1) True action of the route, with param of id=1
E[enter](parse id=2) Enter method of the same route again
E[action](parse id=2) True action of the route, with param of id=2
E[action](check id=3) True action of the next route, with param id=3
E[exit](check id=3) Exit method of parameterized route
F[enter] Enter method of F again, our final route
F[action] True action of F, our final route
G[enter 1] First enter method of G
G[enter 2] Second enter method of G
G[enter 3] Third enter method of G
G[enter 4] Last enter method of G - Returns false, stops execution
H(one=N/A, two=N/A) Optional parameters with only the require part submitted
H(one=10, two=N/A) Optional parameters with one optional part submitted
H(one=10, two=20) Optional parameters two levels deep
I(debug=true) Query string parameter present, but not set
I(debug=false) Query string parameter set
I(debug=true, id=10) Multiple query string parameters, one present (but unset), one set
RESCUE Empty query string with ? should fail to match
H(one=10, two=N/A) Testing "back" functionality


-

Expected

-
F[enter]::F[action]::A[enter]::A[action]::A[exit]::B[enter]::B[action]::C[action]::C[exit]::RESCUE::RESCUE::E[enter](parse id=1)::E[action](parse id=1)::E[enter](parse id=2)::E[action](parse id=2)::E[action](check id=3)::E[exit](check id=3)::F[enter]::F[action]::G[enter 1]::G[enter 2]::G[enter 3]::G[enter 4]::H(one=N/A, two=N/A)::H(one=10, two=N/A)::H(one=10, two=20)::H(one=10, two=N/A)
+

Expected

+
F[enter]::F[action]::A[enter]::A[action]::A[exit]::B[enter]::B[action]::C[action]::C[exit]::RESCUE::RESCUE::E[enter](parse id=1)::E[action](parse id=1)::E[enter](parse id=2)::E[action](parse id=2)::E[action](check id=3)::E[exit](check id=3)::F[enter]::F[action]::G[enter 1]::G[enter 2]::G[enter 3]::G[enter 4]::H(one=N/A, two=N/A)::H(one=10, two=N/A)::H(one=10, two=20)::I(debug=true)::I(debug=false)::I(debug=true, id=10)::RESCUE::H(one=10, two=N/A)

Actual

Grade

diff --git a/tests/path.min.js.test.html b/tests/path.min.js.test.html index bdcb2e1..9649b6c 100644 --- a/tests/path.min.js.test.html +++ b/tests/path.min.js.test.html @@ -5,143 +5,158 @@ -
-

Test Suite

+
+

Test Suite

- Path.js uses a very straightforward method of testing. We manually construct + Path.js uses a very straightforward method of testing. We manually construct a series of method calls that the library should execute under normal working conditions. We then use JavaScript to simulate the URL changes, and compare the final result with what the result should actually be. If the end result @@ -149,39 +164,43 @@

Test Suite

is not suitable for use. The expected test results are as follows:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Token Reason
F[enter] Enter method of F, as it is root
F[action] True action of F, as it is root
A[enter] Enter method of A, as it is looped
A[action] True action of A, as it is looped
A[exit] Exit method of A, as we move to next route
B[enter] Enter method of B, as it is looped
B[action] True action of B, as it is looped
C[action] True action of C
C[exit] Exit method of C, as we move to next route
RESCUE Rescue a route that wasn't found (D1)
RESCUE Rescue a route that wasn't found (D2)
E[enter](parse) Enter method of a param parsed route
E[action](parse id=1) True action of the route, with param of id=1
E[enter](parse) Enter method of the same route again
E[action](parse id=2) True action of the route, with param of id=2
E[action](check id=3) True action of the next route, with param id=3
E[exit](check) Exit method of parameterized route
F[enter] Enter method of F again, our final route
F[action] True action of F, our final route
G[enter 1] First enter method of G
G[enter 2] Second enter method of G
G[enter 3] Third enter method of G
G[enter 4] Last enter method of G - Returns false, stops execution
H(one=N/A, two=N/A) Optional parameters with only the require part submitted
H(one=10, two=N/A) Optional parameters with one optional part submitted
H(one=10, two=20) Optional parameters two levels deep
H(one=10, two=N/A) Testing "back" functionality
Token Reason
F[enter] Enter method of F, as it is root
F[action] True action of F, as it is root
A[enter] Enter method of A, as it is looped
A[action] True action of A, as it is looped
A[exit] Exit method of A, as we move to next route
B[enter] Enter method of B, as it is looped
B[action] True action of B, as it is looped
C[action] True action of C
C[exit] Exit method of C, as we move to next route
RESCUE Rescue a route that wasn't found (D1)
RESCUE Rescue a route that wasn't found (D2)
E[enter](parse id=1) Enter method of a param parsed route
E[action](parse id=1) True action of the route, with param of id=1
E[enter](parse id=2) Enter method of the same route again
E[action](parse id=2) True action of the route, with param of id=2
E[action](check id=3) True action of the next route, with param id=3
E[exit](check id=3) Exit method of parameterized route
F[enter] Enter method of F again, our final route
F[action] True action of F, our final route
G[enter 1] First enter method of G
G[enter 2] Second enter method of G
G[enter 3] Third enter method of G
G[enter 4] Last enter method of G - Returns false, stops execution
H(one=N/A, two=N/A) Optional parameters with only the require part submitted
H(one=10, two=N/A) Optional parameters with one optional part submitted
H(one=10, two=20) Optional parameters two levels deep
I(debug=true) Query string parameter present, but not set
I(debug=false) Query string parameter set
I(debug=true, id=10) Multiple query string parameters, one present (but unset), one set
RESCUE Empty query string with ? should fail to match
H(one=10, two=N/A) Testing "back" functionality


-

Expected

-
F[enter]::F[action]::A[enter]::A[action]::A[exit]::B[enter]::B[action]::C[action]::C[exit]::RESCUE::RESCUE::E[enter](parse)::E[action](parse id=1)::E[enter](parse)::E[action](parse id=2)::E[action](check id=3)::E[exit](check)::F[enter]::F[action]::G[enter 1]::G[enter 2]::G[enter 3]::G[enter 4]::H(one=N/A, two=N/A)::H(one=10, two=N/A)::H(one=10, two=20)::H(one=10, two=N/A)
+

Expected

+
F[enter]::F[action]::A[enter]::A[action]::A[exit]::B[enter]::B[action]::C[action]::C[exit]::RESCUE::RESCUE::E[enter](parse id=1)::E[action](parse id=1)::E[enter](parse id=2)::E[action](parse id=2)::E[action](check id=3)::E[exit](check id=3)::F[enter]::F[action]::G[enter 1]::G[enter 2]::G[enter 3]::G[enter 4]::H(one=N/A, two=N/A)::H(one=10, two=N/A)::H(one=10, two=20)::I(debug=true)::I(debug=false)::I(debug=true, id=10)::RESCUE::H(one=10, two=N/A)

Actual

Grade