Navigation Menu

Skip to content

Commit

Permalink
Cleanup SC.routes by putting the Route class and the hashChange/popSt…
Browse files Browse the repository at this point in the history
…ate handlers in the closure
  • Loading branch information
Devin Torres committed Sep 20, 2011
1 parent 49b0ce9 commit abdac87
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 194 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
.bpm/
.spade/
assets/
*.bpkg
313 changes: 150 additions & 163 deletions lib/core.js
Expand Up @@ -17,6 +17,112 @@ var supportsHistory = !!(window.history && window.history.pushState);
*/
var supportsHashChange = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7);

/**
@class
Route is a class used internally by SC.routes. The routes defined by your
application are stored in a tree structure, and this is the class for the
nodes.
*/
var Route = SC.Object.extend(
/** @scope Route.prototype */ {

target: null,

method: null,

staticRoutes: null,

dynamicRoutes: null,

wildcardRoutes: null,

add: function(parts, target, method) {
var part, nextRoute;

// clone the parts array because we are going to alter it
parts = SC.copy(parts);

if (!parts || parts.length === 0) {
this.target = target;
this.method = method;

} else {
part = parts.shift();

// there are 3 types of routes
switch (part.slice(0, 1)) {

// 1. dynamic routes
case ':':
part = part.slice(1, part.length);
if (!this.dynamicRoutes) this.dynamicRoutes = {};
if (!this.dynamicRoutes[part]) this.dynamicRoutes[part] = this.constructor.create();
nextRoute = this.dynamicRoutes[part];
break;

// 2. wildcard routes
case '*':
part = part.slice(1, part.length);
if (!this.wildcardRoutes) this.wildcardRoutes = {};
nextRoute = this.wildcardRoutes[part] = this.constructor.create();
break;

// 3. static routes
default:
if (!this.staticRoutes) this.staticRoutes = {};
if (!this.staticRoutes[part]) this.staticRoutes[part] = this.constructor.create();
nextRoute = this.staticRoutes[part];
}

// recursively add the rest of the route
if (nextRoute) nextRoute.add(parts, target, method);
}
},

routeForParts: function(parts, params) {
var part, key, route;

// clone the parts array because we are going to alter it
parts = SC.copy(parts);

// if parts is empty, we are done
if (!parts || parts.length === 0) {
return this.method ? this : null;

} else {
part = parts.shift();

// try to match a static route
if (this.staticRoutes && this.staticRoutes[part]) {
return this.staticRoutes[part].routeForParts(parts, params);

} else {

// else, try to match a dynamic route
for (key in this.dynamicRoutes) {
route = this.dynamicRoutes[key].routeForParts(parts, params);
if (route) {
params[key] = part;
return route;
}
}

// else, try to match a wilcard route
for (key in this.wildcardRoutes) {
parts.unshift(part);
params[key] = parts.join('/');
return this.wildcardRoutes[key].routeForParts(null, params);
}

// if nothing was found, it means that there is no match
return null;
}
}
}

});

/**
@class
Expand Down Expand Up @@ -49,7 +155,7 @@ var supportsHashChange = ('onhashchange' in window) && (document.documentMode ==
SC.routes also supports HTML5 history, which uses a '/' instead of a '#'
in the URLs, so that all your website's URLs are consistent.
*/
SC.routes = SC.Object.create(
var routes = SC.routes = SC.Object.create(
/** @scope SC.routes.prototype */{

/**
Expand Down Expand Up @@ -132,10 +238,17 @@ SC.routes = SC.Object.create(
Routes are stored in a tree structure, this is the root node.
@property
@type {SC.routes._Route}
@type {Route}
*/
_firstRoute: null,

/** @private
An internal reference to the Route class.
@property
*/
_Route: Route,

/** @private
Internal method used to extract and merge the parameters of a URL.
Expand Down Expand Up @@ -230,25 +343,6 @@ SC.routes = SC.Object.create(
return this._extractLocation(key, value);
}.property(),

/*
Works exactly like 'location' but you usee this property only when
you want to just change the location w/out triggering the routes
*/
informLocation: function(key, value){
this._skipRoute = true;
// This is a very special case where this property
// has a very heavy influence on the 'location' property
// this is a case where you might want to use idempotent
// but you would take a performance hit because it is possible
// call set() multiple time and we don't want to take the extra
// cost, so we just invalidate the cached set() value ourselves
// you shouldn't do this in your own code unless you REALLY
// know what you are doing.
var lsk = this.location.lastSetValueKey;
if (lsk && this._kvo_cache) this._kvo_cache[lsk] = value;
return this._extractLocation(key, value);
}.property(),

_extractLocation: function(key, value) {
var crumbs, encodedValue;

Expand Down Expand Up @@ -297,15 +391,15 @@ SC.routes = SC.Object.create(
if (get(this, 'wantsHistory') && supportsHistory) {
this.usesHistory = true;

this.popState();
jQuery(window).bind('popstate', this.popState);
popState();
jQuery(window).bind('popstate', popState);

} else {
this.usesHistory = false;

if (supportsHashChange) {
this.hashChange();
jQuery(window).bind('hashchange', this.hashChange);
hashChange();
jQuery(window).bind('hashchange', hashChange);

} else {
// we don't use a SC.Timer because we don't want
Expand All @@ -321,43 +415,6 @@ SC.routes = SC.Object.create(
}
},

/**
Event handler for the hashchange event. Called automatically by the browser
if it supports the hashchange event, or by our timer if not.
*/
hashChange: function(event) {
var loc = window.location.hash;

// Remove the '#' prefix
loc = (loc && loc.length > 0) ? loc.slice(1, loc.length) : '';

if (!jQuery.browser.mozilla) {
// because of bug https://bugzilla.mozilla.org/show_bug.cgi?id=483304
loc = decodeURI(loc);
}

if (get(SC.routes, 'location') !== loc && !SC.routes._skipRoute) {
set(SC.routes, 'location', loc);
}
SC.routes._skipRoute = false;
},

popState: function(event) {
var base = get(this, 'baseURI'),
loc = document.location.href;

if (loc.slice(0, base.length) === base) {

// Remove the base prefix and the extra '/'
loc = loc.slice(base.length + 1, loc.length);

if (get(this, 'location') !== loc && !this._skipRoute) {
set(this, 'location', loc);
}
}
this._skipRoute = false;
},

/**
Adds a route handler. Routes have the following format:
Expand Down Expand Up @@ -392,7 +449,7 @@ SC.routes = SC.Object.create(
method = target[method];
}

if (!this._firstRoute) this._firstRoute = this._Route.create();
if (!this._firstRoute) this._firstRoute = Route.create();
this._firstRoute.add(route.split('/'), target, method);

return this;
Expand Down Expand Up @@ -428,113 +485,43 @@ SC.routes = SC.Object.create(
route.method.call(route.target || this, params);
}
}
},

/**
@private
@class
SC.routes._Route is a class used internally by SC.routes. The routes defined by your
application are stored in a tree structure, and this is the class for the
nodes.
*/
_Route: SC.Object.extend(
/** @scope SC.routes._Route.prototype */ {

target: null,

method: null,
}

staticRoutes: null,

dynamicRoutes: null,

wildcardRoutes: null,

add: function(parts, target, method) {
var part, nextRoute;

// clone the parts array because we are going to alter it
parts = SC.copy(parts);

if (!parts || parts.length === 0) {
this.target = target;
this.method = method;

} else {
part = parts.shift();

// there are 3 types of routes
switch (part.slice(0, 1)) {

// 1. dynamic routes
case ':':
part = part.slice(1, part.length);
if (!this.dynamicRoutes) this.dynamicRoutes = {};
if (!this.dynamicRoutes[part]) this.dynamicRoutes[part] = this.constructor.create();
nextRoute = this.dynamicRoutes[part];
break;

// 2. wildcard routes
case '*':
part = part.slice(1, part.length);
if (!this.wildcardRoutes) this.wildcardRoutes = {};
nextRoute = this.wildcardRoutes[part] = this.constructor.create();
break;

// 3. static routes
default:
if (!this.staticRoutes) this.staticRoutes = {};
if (!this.staticRoutes[part]) this.staticRoutes[part] = this.constructor.create();
nextRoute = this.staticRoutes[part];
}

// recursively add the rest of the route
if (nextRoute) nextRoute.add(parts, target, method);
}
},

routeForParts: function(parts, params) {
var part, key, route;
});

// clone the parts array because we are going to alter it
parts = SC.copy(parts);
/**
Event handler for the hashchange event. Called automatically by the browser
if it supports the hashchange event, or by our timer if not.
*/
function hashChange(event) {
var loc = window.location.hash;

// if parts is empty, we are done
if (!parts || parts.length === 0) {
return this.method ? this : null;
// Remove the '#' prefix
loc = (loc && loc.length > 0) ? loc.slice(1, loc.length) : '';

} else {
part = parts.shift();
if (!jQuery.browser.mozilla) {
// because of bug https://bugzilla.mozilla.org/show_bug.cgi?id=483304
loc = decodeURI(loc);
}

// try to match a static route
if (this.staticRoutes && this.staticRoutes[part]) {
return this.staticRoutes[part].routeForParts(parts, params);
if (get(routes, 'location') !== loc && !routes._skipRoute) {
set(routes, 'location', loc);
}
routes._skipRoute = false;
}

} else {
function popState(event) {
var base = get(routes, 'baseURI'),
loc = document.location.href;

// else, try to match a dynamic route
for (key in this.dynamicRoutes) {
route = this.dynamicRoutes[key].routeForParts(parts, params);
if (route) {
params[key] = part;
return route;
}
}
if (loc.slice(0, base.length) === base) {

// else, try to match a wilcard route
for (key in this.wildcardRoutes) {
parts.unshift(part);
params[key] = parts.join('/');
return this.wildcardRoutes[key].routeForParts(null, params);
}
// Remove the base prefix and the extra '/'
loc = loc.slice(base.length + 1, loc.length);

// if nothing was found, it means that there is no match
return null;
}
}
if (get(routes, 'location') !== loc && !routes._skipRoute) {
set(routes, 'location', loc);
}

})

});
}
routes._skipRoute = false;
}
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -4,7 +4,7 @@
"description": "HTML5 History (with hashchange fallback) based routing",
"homepage": "http://sproutcore.com/",
"author": "Strobe Inc., Apple Inc. and contributors",
"version": "2.0.beta.5",
"version": "2.0.beta.6",

"directories": {
"lib": "lib"
Expand Down

0 comments on commit abdac87

Please sign in to comment.