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"
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue