flatiron / director Goto Github PK
View Code? Open in Web Editor NEWa tiny and isomorphic URL router for JavaScript
Home Page: http://github.com/flatiron/director
License: MIT License
a tiny and isomorphic URL router for JavaScript
Home Page: http://github.com/flatiron/director
License: MIT License
a demo and page needs to get created.
The Router init() method executes a call to route() without passing a parameter. This causes the route to be 'undefined' which ultimately causes a call to the notfound handler upon initialization.
// init method
this.init = function() {
listener.init(route);
route();
return this;
};
//in parse method
if(route === undefined && path.length === 0) {
self.noroute(partialpath);
return false;
};
Is the initial route() call necessary? Should the 'notfound' handler be added only initialization? That seems clunky.
Thanks!
Say I have two routes: /blogpost and /blogpost/edit. Currently, if /blogpost/edit is hit, the "on" function for both the routes are called. There might be use-cases for both to be called, but the majority of use-cases will be entirely different functionality, so recursing the child urls is not expected. /blogpost will show the blog post, and /edit will show a form, for example.
If there already is a way to control if the recursion will happen, I'm not aware of it. If there isn't, there should be. :)
Let me know if I wasn't clear enough.
What I'm trying to do is match against the root directory ("/").
It's entirely possible that this shouldn't work, but if so the way to match against a root directory (using .get and friends) is non-obvious.
If you have overlapping route that include simple tokens, only the routes that END in a simple token will be routed. No errors are displayed or callbacks are hit.
The routes are listed below:
'/foo/:id' : function(){
console.log("foo2 ", arguments);
},
'/foo/:id/bar' : function(){
console.log("bar", arguments);
},
'/foo/:id/bar/:id2' : function(){
console.log("bar2", arguments);
},
'/foo/:id/bar/:id2/baz' : function(){
console.log("baz", arguments);
},
'/foo/:id/bar/:id2/baz/:id3' : function(){
console.log("baz2", arguments);
}
Back and forth navigation through browser history buttons is broken in IE7. Routes in the URL bar are changing but content stays the same.
Is it currently possible to use director entirely in memory? Without needing any external interface?
We were attempting to use an API that looked like this:
var router = new director.Router();
router.on(/foo/, function () {
console.log('sup');
});
router.dispatch('GET', '/foo', function (err) {
console.log('sup', err);
});
First, let me say, great library. Exactly what I think this world needs :)
Im having a little issue. when I navigate away from a certain page, i have leave event, and it just won't fire. Is there something specific I need to do?
Code example:
router.get(/sup/meh/, function () {
this.res.writeHead(200, { 'Content-Type': 'text/plain' })
this.res.end('hello world\n');
});
If I try to access: /sup/meh, I get this error:
/usr/local/lib/node_modules/director/lib/director/router.js:420
match = path.match(new RegExp('^' + exact));
^
SyntaxError: Invalid regular expression: /^/sup/: \ at end of pattern
at new RegExp (unknown source)
at [object Object].traverse (/usr/local/lib/node_modules/director/lib/director/router.js:420:26)
I've debugged a little bit and came to the conclusion that the problem is:
if (path.source) { // ~ line 200
path = path.source
}
The problem is when we split based on the '/' delimiter, the parts become:
'sup', 'meh'.. and when later on it tries to match 'sup' it obviously says there's a regex error.
My current quick fix: (Replacing / by /)
if (path.source) {
path = path.source.replace('/', '/');
}
Hope it helps
Line 366 in bottom of Router.on
Either this is a bug in the code or in the docs. Should the route name be available to the registered function? Right now it's coming out as undefined.
From the docs:
function helloWorld(route) {
this.res.writeHead(200, { 'Content-Type': 'text/plain' })
this.res.end('hello world from (' + route + ')');
}
When running the demo code and hitting curl http://localhost:8080/hello
, the print statement comes out as:
hello world from (undefined)
Director can not match these characters because regifyString
will replace route wildcards with another regexp which does not match these characters.
The following server responds to /foo/bar
with "hello world", but will 404 for /foo/bar;baz
or /foo/bar/baz
.
var union = require('union');
var director = require('director');
var router = new director.http.Router();
router.get(/\/foo\/(.*)/, function () {
this.res.writeHead(200, { 'Content-Type': 'text/plain' })
this.res.end('hello world\n');
});
var server = union.createServer({
before: [
function (req, res) {
var found = router.dispatch(req, res);
if (!found) {
res.emit('next');
}
}
]
});
server.listen(9090);
Looks like if I try to make multiple routers (like in Sammy.js) the second one overrides/disables the first. Any chance of adding support for multiple concurrent routers?
Either settings 'backword' or 'forward' don't work in http routing.
My env:
$ node -v
v0.6.6
$ npm list
├── [email protected]
Example code:
var http = require('http'),
director = require('director');
var router = new director.http.Router(
{
'/hello':
{
'/hello2':
{
get: function(route)
{
this.res.write('hello2\n');
}
},
get: function(route)
{
this.res.write('hello\n');
}
}
}).configure(
{
recurse: 'backward',
before: function(){ this.res.writeHead(200, { 'Content-Type': 'text/plain' }); },
on: function(){ this.res.end(); }
});
var server = http.createServer(function (req, res) {
router.dispatch(req, res, function (err) {
if (err) {
res.writeHead(404);
res.end();
}
});
});
server.listen(8080);
Result:
$ curl http://localhost:8080/hello/hello2
hello2
Need to write some better examples of how SugarSkull works.
For example, i have a tag like: <a href="#/something">
.
Trying to dispatch that results in a lookup for '/#/something' which is never found.
First of all: A happy and good coding new year! :-)
And 2nd, is there a possibility for someone to bump the version on master and publish that new version to NPM? Need the query parameter fix thats already committed two weeks ago but not published yet.... ;-)
Thanks !!
In theory, these two URLs are equally valid, and will generally mean the same thing:
http://localhost:7000/api/
http://localhost:7000/api
However, it seems that number 1 is not currently possible with Director.
I have this setup in my Flatiron app:
// In app.js
var flatiron = require('flatiron'),
app = flatiron.app;
app.use(flatiron.plugins.http);
app.router.path('/api', require('./api').router);
// In api/index.js
var router = function () {
this.get('/', function () {
this.res.writeHead(200, { 'Content-Type': 'text/plain' });
this.res.end('This is the entry point for the Ardon API.');
});
};
module.exports = {
router: router
};
I've tried different variations of that with slash or not slash in the app.router.path()
and this.get()
invocations, but the result is invariably the same. http://localhost:7000/api
works as it should, http://localhost:7000/api/
returns the text “undefined” with a 404 not found header.
You can find a browser-specific build of director here which has all of the server code stripped away.
The text here
links to the front page of the repo and not to the alluded to browser-specific version
It looks like IE7 history navigation is broken again I guess because of code migration to flatiron.
I have the following in my application:
new Router({
'/(\\w+)\\/' : selectRepository
}).init();
function selectRepository(repo) {
...
}
I would like to match #/AAA, or #/BBB,.... However, if I put no hash, to hit the main page originally, as in http://localhost:8080/context/ it goes through the router, and in the parse method of SS.js, the first line (parse.shift) returns undefined. Then the method continues on and undefined becomes /undefined/ in leftovers, which matches my regular expression, but of course the routing will not work. I could of course check for undefined in my selectRepository function, but I really think this case should be handled internally and /undefined should not be handled as an acceptable case.
To fix this locally, I just added:
if (partialpath === undefined) {
return false;
}
after
var partialpath = path.shift();
of the parse method.
That said, this is probably just treating the symptom. I have not really delved in to understand all of the code, so, maybe something like:
leftovers = (partialpath ? ('/' + partialpath + '/') : '/') + path.join('/');
would be more correct.
Anyway, I am not a regex guru and so I could just have something wrong with my regex, or just be missing something in the usage entirely, as I just barely started using SugarSkull, so if there is a better way, please let me know.
All tests should be organized into a single file and described better.
Router({
'/:page': ':page'
}).use({
resource: {
pizza: function() { ... },
taco: function() { ... },
muffins: function() { ... }
}
})
this would be handy as it would save me from doing something redundant like this:
var pages = {
pizza: function() { ... },
taco: function() { ... },
muffins: function() { ... }
}
Router({
'/:page': {
on: function(page) {
pages[page]()
}
}
}).use({
resource: pages
})
so the following two routes work in FF, Chrome, IE9, etc.
'/test\\/': function(){console.log('test hit')},
'/test\\/([a-z0-9-]+)': function(x){console.log('test hit with '+x)}
but only the first one works when my browser is put into "Document Mode: IE8 Standards"
i am partially suspecting regex differences ( http://stevenlevithan.com/demo/split.cfm )
my current project has an IE8+ requirement. is this something you would be willing to look into? i do understand that coding for old quirks is sometimes more hassle than its worth.
SIDENOTE: as i was testing different patterns i learned that this route works in all browsers:
'/foo': { '/bar': function(){console.log('FUBAR')} }
but this one does not work in any browsers:
'/foo/bar': function(){console.log('FUBAR')}
is this the designed/desired behavior?
I construct & init the router.
Set a route using .on().
Then dispatch it using .dispatch().
I can see there's a match when debugging but it doesn't return the set callbacks and doesn't dispatch.
encountered many problems, trying what was in the readme.
Router({})
TypeError: Object #<Object> has no method 'configure'
at /home/dominic/source/dev/experiement/node_modules/sugarskull/lib/sugarskull/router.js:124:8
at /home/dominic/source/dev/experiement/node_modules/sugarskull/lib/sugarskull/http/index.js:14:21
at Object.<anonymous> (/home/dominic/source/dev/experiement/ss.js:4:9)
at Module._compile (module.js:411:26)
at Object..js (module.js:417:10)
at Module.load (module.js:343:31)
at Function._load (module.js:302:12)
at Array.<anonymous> (module.js:430:10)
at EventEmitter._tickCallback (node.js:126:26)
new Router({}).init()
TypeError: Object [object Object] has no method 'init'
at Object.<anonymous> (/home/dominic/source/dev/experiement/ss.js:9:4)
at Module._compile (module.js:411:26)
at Object..js (module.js:417:10)
at Module.load (module.js:343:31)
at Function._load (module.js:302:12)
at Array.<anonymous> (module.js:430:10)
at EventEmitter._tickCallback (node.js:126:26)
var Router = require('sugarskull').http.Router
, http = require('http')
function respond () {
console.error('it worked')
this.res.writeHeader(200); this.res.end('aoneuhaoenuhoneiurkpborkbropkbr')
}
var r = new Router({
on: respond,
'/\//': respond,
'/': respond,
})
http.createServer(function (req, res) {
r.dispatch(req, res, function (err) { throw err })
}).listen(8080)
^
Error: Could not find path: /
at [object Object].dispatch (/home/dominic/source/dev/experiement/node_modules/sugarskull/lib/sugarskull/http/index.js:60:16)
at Server.<anonymous> (/home/dominic/source/dev/experiement/ss.js:21:5)
at Server.emit (events.js:67:17)
at HTTPParser.onIncoming (http.js:1134:12)
at HTTPParser.onHeadersComplete (http.js:108:31)
at Socket.ondata (http.js:1029:22)
at Socket._onReadable (net.js:677:27)
at IOWatcher.onReadable [as callback] (net.js:177:10)
this needs working examples!
Wanted to implement http basic auth checking as a filter and I'm not able to using routing events.
Request and response objects are not available to routing events and according to the comments in the routing events code, we should not return anything from a routing event function.
When used alongwith webshims we get the error "too much recursion", specifically on firefox 3.6.23 .
Here is jsfiddle http://jsfiddle.net/zfTBS/1/ .
I have tried with latest versions of all libs but no luck. jquery(1.6.4), webshims (1.8.2) and SugarSkull latest.
I am not sure whether webshims/SugarSkull so might add same issue for webshims too.
Same thing works well on Chrome.
Thanks for this wonderful library. We love it.
I didnt test this with options.notfound
, which probably catches the error (rather than silently failing) but I think . and _ are pretty normal for URLs
I think I'd like to use state for something I'm working on, but it's not very clear how to do this from the documentation. Basically I have a small app with two routes:
'/builds': { '/:buildid': { on: function(buildid) { ... } '/jobs': { '/:jobid': { on: function(buildid, jobid) { ... } } } } }
The second is a superset of the first (that is, the first one should be loaded before the second). I can make this happen by recursing forward. So far so good. The problem is that when going backward (e.g. transitioning from /builds/foo/jobs/bar to /builds/foo), SugarSkull think it needs to call the "on" function for /builds/foo (when in fact it's already loaded). I'm gathering that this is something that I can use state for, but it's not clear to me how from the documentation.
Problem 1: How do I access the state dictionary from inside my router functions?
Problem 2: How do I assign route-specific information (e.g. a buildid in the example above) to a state dictionary?
see journey
Hi,
I've got a question on how to handle this in the following (browser) scenario.
var MyApp = function(){
}
MyApp.prototype.start = function() {
console.log(this);
};
var app = new MyApplication();
routes = {
'/': 'start',
}
Router(routes).configure({resource:app}).init();
When I point my browser to "#/" start gets called, but the console.log(this) prints the a.Router object.
Is there a way to get this' set to the resource?
Thanks
I wanted to use this adhoc routing but it seems te be broken... How is this possible btw? We're creating builds that are not working correctly :S
See commit ef3b3bf.
Adhoc routing results in Uncaught TypeError: Cannot call method 'concat' of undefined
"notfound" is not firing any more.
"once" throws an error.
"on" and "after" do not pass values.
The ability to add global routes to be fired whenever any part of the hash contains the fragment
e.g.
router = Router({
'*/global/*: {
on: killDog
},
'/home': {
on: goHome,
'/users': {
on: loadUser
}
}
}).init();
would match...
http://someapp.com/#/global/
http://someapp.com/#/home/users/global/
http://someapp.com/#/home/global/users/
similarly, if you could define it as global part of a subsequent route
router = Router({
'/home': {
on: goHome,
'/users': {
on: loadUser
},
'*/global/*: {
on: killDog
}
}
}).init();
Would match
http://someapp.com/#/home/users/global/
http://someapp.com/#/home/global/users/
http://someapp.com/#/home/users/:token/global
but not
http://someapp.com/#/global/
http://someapp.com/#/some-other-route/global/
It would be beneficial to pass the before and after fragments via arrays
e.g.
function killDog([ before, match, fragments], [after, match, fragments] ){
}
Not sure if I'm missing something but I have tried to achieve a similar result using regex and recurse, to no avail.
Original URL that worked:
http://localhost:3201/games/PS3/MostPopular/All%20PS3%20Games
'/:platform/:filter/([^.]*)': {
get: function (platform, filter, category) {
renderer.getGameslist(filter, platform, category.replace(/&/g, '%26'), 1, 20, this.req, this.res);
}
},
When I add query string parameters like in the following URL , the above route no longer is triggered.
http://localhost:3201/games/PS3/MostPopular/All%20PS3%20Games?pageNumber=2&pageSize=20
I think this should be something optional. For integration purposes with other servers I want to have a Head route.
the docs are out of date.
Feature Request: Support for HTML5 History API on an opt-in basis, with graceful fallback to hash version in lame browsers. Similar to the backbone and sammy routers. The main use case is for web apps that also want SEO (think twitter) and that build a server side solution for it. With Node being able to run your front end's same backbone models and templating it's not much more work. Having the option to handle the routing in sugarskull would be nice.
When testing with: http://development:9090/test/foo?test=test
the attached route function to /test/foo
isn't executed. But if I change the request to http://development:9090/test/foo
it is.
I debugged the code and found that the whole URL (+ the parameters after ?) is fed into the traverse
function by the dispatch
function. If I change that to only the part before ?
then it works. Can make a patch for it but don't know if the current behavior is the correct one. If this is the correct behavior then what do I need to do to get URLs with parameters working with Director?
for the router setup
router =Router( {
'foo':{
on: function(){},
after: function(){}
}
}).use({ recourse: forward }).init();
and the url:
#/foo
After will fired when the url is navigated to, and once the url has left.
So as I mentioned (too briefly) on twitter the other day, I think it
would be really neat if SugarSkull supported parameters to its URLs.
The main motivation for this is that the application I'm currently
working on (http://brasstacks.mozilla.com/gofaster/#/) has some views
that can take many parameters, for example:
http://brasstacks.mozilla.com/gofaster/#/executiontime/build/30
http://brasstacks.mozilla.com/gofaster/#/executiontime/test/60
In this example, only really the first part of the path
(executiontime) is really modelling a resource in my application. The
other two are more like parameters to the request. Obviously putting
them as fully fledged paths works (as that's what I'm doing right
now), but has the following disadvantages:
I can think of a few ways around the above but they seem excessively
complicated. What I'm thinking of is adding the option of setting
parameters to sugar skull "URLs", so you could make queries like this:
http://brasstacks.mozilla.com/gofaster/#/executiontime
http://brasstacks.mozilla.com/gofaster/#/executiontime?type=build
http://brasstacks.mozilla.com/gofaster/#/executiontime?type=build&range=30
and the router would only parse the section up to the '?' character,
passing the set of parameters to the function as a dictionary called
params (if there are no parameters, the dictionary would obviously be
empty).
I guess this would break backwards compatibility since we'd now be
expecting the parameters variable to appear in everybody's functions
(but at least all people would have to do is update their code:
existing URLs would still work on the client side).
If I were to add something like that to a fork of your project, would
you accept it back into mainline?
I've copied the sample code from the docs and added a little logging.
https://gist.github.com/1367702
After starting the server, http://localhost:8080/hello, the route passed to the constructor on lines 20 and 21, returns a 404.
The logging statement on line 47 prints the routing table which is broken for the routes passed to the constructor. For a method it seems to have "et" instead of "get".
data: {
data: bonjour: { get: [Function] },
data: hello: {
data: et: { on: [Function] }
data: },
data: hola: { get: [Function] }
data: }
This is using Node 0.6.1 and SugarSkull version 1.0.1 from npm.
Is there a way to handle multiple hash parameters?
eg.
...index.html#/roadmap/7/2010
I tried to define it this way:
'/roadmap': {
'/(\d+)': {
on: displayRoadmap,
'/(\d+)': {
on: displayRoadmap
}
}
}
Hoping that it would call my function with one or two parameters based on matches:
function displayRoadmap(id, year) {
...
}
Thanks,
tvalletta
Would like to be able to define nested routes that support simple-tokens or regexps, pass all values forward.
'/a': {
'/(\d+)': {
on: function(b) { ... },
'/(\d+)': {
on: function(b, c) { ... }
}
}
}
Error in Line 17, Character: 3, Code: 0, URL: http://hij1nx.github.com/SugarSkull/
=> "Bezeichner, Zeichenfolge oder Zahl erwartet"
using: director-1.0.7.min.js
In chrome v16:
var router = new Router().init();
router.on('/:some', function () {...}); //=> TypeError: Cannot call method 'concat' of undefined
In FF v9
var router = new Router().init();
router.on('/:some', function () {...}); //=> TypeError: this.scope is undefined
In IE v9 does not work.
@hij1nx I'm wondering if you'd be open to the idea of simple tokens. Here's what I mean:
Say you want to match the group name and project name from this URL: /project/mobile-team/iphone-app
'/project\\/([a-z0-9-]+)\\/([a-z0-9-]+)': viewProject
'/project/:group/:project': viewProject
Your readme mentions that simple tokens like this can't handle every situation, and that's true. But they handle probably 95% of the use cases for routing, and they're very easy to use. I'm sure that's why Express, Sammy, and CouchDB use them. Actually the only thing you gain from regex routes is the ability to distinguish from /articles/4
and /articles/four
for example. But the increase in power comes at the cost of being more difficult to use.
I have an idea for a simple syntax that would allow both simple params AND regex distinction where you need it. Just include the regex filter after the simple param. So for example using the same URL /project/mobile-team/iphone-app
you could:
'/project/:group/:project': viewProject
'/project/:group/:project(\\d+)': viewProject
//would match /project/mobile-team/43 but not /project/mobile-team/iphone-app
The (\\d+)
isn't a capture, but rather a definition of what type the simple param must be in order to match. What do you think? This lets your routes be simple and concise, and only use regular expressions in the rare cases where you need them. This would make sugarskull.js easier to use for developers (like me) who aren't experts with regular expressions, without losing any of the flexibility that sugarskull.js currently has.
If you're interested but don't have the time would you be open to a pull request for this?
Thanks!
My mini-router here:
https://gist.github.com/1248332
Having 2 issues:
Maybe I'm just defining something wrong?
ps. can you also specify what SS.min.js is? doesn't seem to just be a minified version of SS.js, or am I wrong?
Currently client-side director only supports hash-routing. It would be a nice sugar feature if it could support regular routing too.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.