Ox.URL: add hash parser, add tests
This commit is contained in:
parent
64085cdc04
commit
ed4dd70c8e
1 changed files with 244 additions and 7 deletions
|
@ -44,13 +44,217 @@ Ox.URL <f> URL controller
|
|||
typeA <o> Views for type "typeA"
|
||||
list <[s]> List 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
|
||||
example.com[/type][/item][/view][/span][/sort][/find]
|
||||
example.com[/type][/item][/view][/span][/sort][/find][#hash]
|
||||
|
||||
page Special page, like "about" or "contact"
|
||||
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: ''}
|
||||
(comparison operator) or {conditions: [], operator: ''} (logical
|
||||
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
|
||||
v * v = any text or string contains or any number is
|
||||
|
@ -158,7 +363,11 @@ Ox.URL = function(options) {
|
|||
views: {}
|
||||
}, 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.previousURL = '';
|
||||
|
@ -207,6 +416,13 @@ Ox.URL = function(options) {
|
|||
}).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) {
|
||||
return location.join(',');
|
||||
}
|
||||
|
@ -262,7 +478,8 @@ Ox.URL = function(options) {
|
|||
parts.push(constructFind(state.find));
|
||||
}
|
||||
}
|
||||
return '/' + Ox.filter(parts).join('/');
|
||||
return '/' + Ox.filter(parts).join('/')
|
||||
+ (state.hash ? '#' + constructHash(state.hash) : '');
|
||||
}
|
||||
|
||||
function constructValue(str, key) {
|
||||
|
@ -407,6 +624,23 @@ Ox.URL = function(options) {
|
|||
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) {
|
||||
return str.split(',').map(function(str, i) {
|
||||
return Ox.limit(parseInt(str, 10), -90 * (i + 1), 90 * (i + 1));
|
||||
|
@ -441,9 +675,9 @@ Ox.URL = function(options) {
|
|||
|
||||
function parseURL(str, callback) {
|
||||
// fixme: removing trailing slash makes it impossible to search for '/'
|
||||
str = str.replace(/(^\/|\/$)/g, '');
|
||||
var parts = str.split('/'),
|
||||
state = {};
|
||||
var split = str.split('#'),
|
||||
parts = split.shift().replace(/(^\/|\/$)/g, '').split('/'),
|
||||
state = split.length ? {hash: parseHash(split.join('#'))} : {};
|
||||
if (parts[0] == '') {
|
||||
// empty URL
|
||||
callback(state);
|
||||
|
@ -479,6 +713,9 @@ Ox.URL = function(options) {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
state.item = '';
|
||||
// set to default view
|
||||
state.view = self.options.views[state.type].list[0];
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue