Ox.URL: add hash parser, add tests

This commit is contained in:
rolux 2012-10-31 12:57:55 +01:00
parent 64085cdc04
commit ed4dd70c8e

View file

@ -44,13 +44,217 @@ Ox.URL <f> URL controller
typeA <o> Views for type "typeA" typeA <o> Views for type "typeA"
list <[s]> List views for this type list <[s]> List views for this type
item <[s]> Item views for this type item <[s]> Item views for this type
<script>
Ox.test.url = Ox.URL({
findKeys: [
{id: 'name', type: 'string'},
{id: 'population', type: 'integer'}
],
getItem: function(str, callback) {
callback(/^\d+$/.test(str) ? str : '');
},
getSpan: function(item, view, str, callback) {
if (!item && (!view || view == 'map')) {
callback(str.replace(/^@/, ''), 'map');
} else {
callback();
}
},
pages: ['about', 'faq', 'help'],
sortKeys: {
countries: {
list: {
grid: [
{id: 'name', operator: '+'},
{id: 'population', operator: '-'},
{id: 'cities', operator: '-'}
]
},
item: {
cities: [
{id: 'name', operator: '+'},
{id: 'population', operator: '-'}
]
}
},
cities: {
list: {
grid: [
{id: 'name', operator: '+'},
{id: 'population', operator: '-'}
]
}
}
},
spanType: {
countries: {
list: {map: 'location'},
item: {map: 'location'}
},
cities: {
list: {map: 'location'},
item: {map: 'location'}
}
},
types: ['countries', 'cities'],
views: {
countries: {
list: ['grid', 'map'],
item: ['info', 'map', 'cities']
},
cities: {
list: ['grid', 'map'],
item: ['info', 'map']
}
}
});
Ox.test.result = {
'/': {},
'/faq#1': {
page: 'faq',
hash: {anchor: '1'}
},
'/cities': {
type: 'cities',
item: '',
view: 'grid'
},
'/map': {
type: 'countries',
item: '',
view: 'map'
},
'/-45,-90,45,90': {
type: 'countries',
item: '',
view: 'map',
span: [[-45, -90], [45, 90]]
},
'/@New%20York': {
type: 'countries',
item: '',
view: 'map',
span: 'New York'
},
'/name': {
type: 'countries',
item: '',
view: 'grid',
sort: [{key: 'name', operator: '+'}]
},
'/-name,population': {
type: 'countries',
item: '',
view: 'grid',
sort: [
{key: 'name', operator: '-'},
{key: 'population', operator: '-'}
]
},
'/2342': {
type: 'countries',
item: '2342',
view: 'info'
},
'/2342/map': {
type: 'countries',
item: '2342',
view: 'map'
},
'/2342/name': {
type: 'countries',
item: '2342',
view: 'cities',
sort: [{key: 'name', operator: '+'}]
},
'/foo': {
type: 'countries',
item: '',
view: 'grid',
find: {
conditions: [
{key: '*', operator: '=', value: 'foo'}
],
operator: '&'
}
},
'/population=1000,2000': {
type: 'countries',
item: '',
view: 'grid',
find: {
conditions: [
{key: 'population', operator: '=', value: ['1000', '2000']}
],
operator: '&'
}
},
'/population>0&(name=a*|name=*z)': {
type: 'countries',
item: '',
view: 'grid',
find: {
conditions: [
{key: 'population', operator: '>', value: '0'},
{
conditions: [
{key: 'name', operator: '^', value: 'a'},
{key: 'name', operator: '$', value: 'z'}
],
operator: '|'
}
],
operator: '&'
}
},
'/#a?k=v&l=w': {
hash: {
anchor: 'a',
query: [
{key: 'k', value: 'v'},
{key: 'l', value: 'w'}
]
}
}
};
</script>
> !!Ox.test.url.parse('/', function(o) { Ox.test(o, Ox.test.result['/']); })
true
> !!Ox.test.url.parse('/faq#1', function(o) { Ox.test(o, Ox.test.result['/faq#1']); })
true
> !!Ox.test.url.parse('/cities', function(o) { Ox.test(o, Ox.test.result['/cities']); })
true
> !!Ox.test.url.parse('/map', function(o) { Ox.test(o, Ox.test.result['/map']); })
true
> !!Ox.test.url.parse('/-45,-90,45,90', function(o) { Ox.test(o, Ox.test.result['/-45,-90,45,90']); })
true
> !!Ox.test.url.parse('/@New%20York', function(o) { Ox.test(o, Ox.test.result['/@New%20York']); })
true
> !!Ox.test.url.parse('/name', function(o) { Ox.test(o, Ox.test.result['/name']); })
true
> !!Ox.test.url.parse('/-name,population', function(o) { Ox.test(o, Ox.test.result['/-name,population']); })
true
> !!Ox.test.url.parse('/2342', function(o) { Ox.test(o, Ox.test.result['/2342']); })
true
> !!Ox.test.url.parse('/2342/map', function(o) { Ox.test(o, Ox.test.result['/2342/map']); })
true
> !!Ox.test.url.parse('/2342/name', function(o) { Ox.test(o, Ox.test.result['/2342/name']); })
true
> !!Ox.test.url.parse('/foo', function(o) { Ox.test(o, Ox.test.result['/foo']); })
true
> !!Ox.test.url.parse('/population=1000,2000', function(o) { Ox.test(o, Ox.test.result['/population=1000,2000']); })
true
> !!Ox.test.url.parse('/population>0&(name=a*|name=*z)', function(o) { Ox.test(o, Ox.test.result['/population>0&(name=a*|name=*z)']); })
true
> !!Ox.test.url.parse('/#a?k=v&l=w', function(o) { Ox.test(o, Ox.test.result['/#a?k=v&l=w']); })
true
@*/ @*/
/* /*
example.com[/page] example.com[/page][#hash]
or or
example.com[/type][/item][/view][/span][/sort][/find] example.com[/type][/item][/view][/span][/sort][/find][#hash]
page Special page, like "about" or "contact" page Special page, like "about" or "contact"
type Section a.k.a. item type, like "movies", "edits", "texts" etc. type Section a.k.a. item type, like "movies", "edits", "texts" etc.
@ -69,6 +273,7 @@ find Query, like a=x or a=x&b=y or a=x&(b=y|c=z). A query object has the form
object has the form {key: '', value: '' or ['', ''], operator: ''} object has the form {key: '', value: '' or ['', ''], operator: ''}
(comparison operator) or {conditions: [], operator: ''} (logical (comparison operator) or {conditions: [], operator: ''} (logical
operator). Condition strings can be more than just "k=v", see below. operator). Condition strings can be more than just "k=v", see below.
hash Anchor and/or query, like 'a' or '?k=v' or 'a?k=v&l=w'.
String Key Value Operator String Key Value Operator
v * v = any text or string contains or any number is v * v = any text or string contains or any number is
@ -158,7 +363,11 @@ Ox.URL = function(options) {
views: {} views: {}
}, options); }, options);
Ox.Log('Core', 'Ox.URL options', self.options) if (Ox.every(self.options.findKeys, function(findKey) {
return findKey.id != '*';
})) {
self.options.findKeys.push({id: '*', type: 'string'});
}
self.previousTitle = ''; self.previousTitle = '';
self.previousURL = ''; self.previousURL = '';
@ -207,6 +416,13 @@ Ox.URL = function(options) {
}).join(find.operator); }).join(find.operator);
} }
function constructHash(hash) {
return (hash.anchor || '')
+ hash.query ? '?' + hash.query.map(function(query) {
return encodeValue(query.key) + '=' + encodeValue(query.value);
}) : '';
}
function constructLocation(location) { function constructLocation(location) {
return location.join(','); return location.join(',');
} }
@ -262,7 +478,8 @@ Ox.URL = function(options) {
parts.push(constructFind(state.find)); parts.push(constructFind(state.find));
} }
} }
return '/' + Ox.filter(parts).join('/'); return '/' + Ox.filter(parts).join('/')
+ (state.hash ? '#' + constructHash(state.hash) : '');
} }
function constructValue(str, key) { function constructValue(str, key) {
@ -407,6 +624,23 @@ Ox.URL = function(options) {
return find; return find;
} }
function parseHash(str) {
var split = str.split('?');
return Ox.extend({
anchor: decodeValue(split[0])
}, split[1] ? {
query: split[1].split('&').filter(function(kv) {
return kv.indexOf('=') > -1;
}).map(function(kv) {
var split = kv.split('=');
return {
key: decodeValue(split[0]),
value: decodeValue(split[1])
};
})
} : {});
}
function parseLocation(str) { function parseLocation(str) {
return str.split(',').map(function(str, i) { return str.split(',').map(function(str, i) {
return Ox.limit(parseInt(str, 10), -90 * (i + 1), 90 * (i + 1)); return Ox.limit(parseInt(str, 10), -90 * (i + 1), 90 * (i + 1));
@ -441,9 +675,9 @@ Ox.URL = function(options) {
function parseURL(str, callback) { function parseURL(str, callback) {
// fixme: removing trailing slash makes it impossible to search for '/' // fixme: removing trailing slash makes it impossible to search for '/'
str = str.replace(/(^\/|\/$)/g, ''); var split = str.split('#'),
var parts = str.split('/'), parts = split.shift().replace(/(^\/|\/$)/g, '').split('/'),
state = {}; state = split.length ? {hash: parseHash(split.join('#'))} : {};
if (parts[0] == '') { if (parts[0] == '') {
// empty URL // empty URL
callback(state); callback(state);
@ -479,6 +713,9 @@ Ox.URL = function(options) {
}); });
} }
} else { } else {
state.item = '';
// set to default view
state.view = self.options.views[state.type].list[0];
callback(state); callback(state);
} }
} }