forked from 0x2620/oxjs
rename Ox.UI source files, remove Ox. prefix
This commit is contained in:
parent
005d50c389
commit
91e1065aab
101 changed files with 0 additions and 0 deletions
726
source/Ox.UI/js/Core/URL.js
Normal file
726
source/Ox.UI/js/Core/URL.js
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.URL <f> URL controller
|
||||
(options) -> <o> URL controller
|
||||
options <o> Options object
|
||||
findKeys <[o]> Find keys
|
||||
id <s> Find key id
|
||||
type <s> Value type (like "string" or "integer")
|
||||
getItem <f> Tests if a string matches an item
|
||||
(string, callback) -> <u> undefined
|
||||
string <s> The string to be tested
|
||||
callback <f> callback function
|
||||
id <s> Matching item id, or empty
|
||||
getSpan <f> Tests if a string matches a span
|
||||
(item, view, string, callback) -> <u> undefined
|
||||
item <s> The item id, or empty
|
||||
view <s> The view, or empty
|
||||
string <s> The string to be tested
|
||||
callback <f> Callback function
|
||||
id <s> Matching span id, or empty
|
||||
view <s> Matching view, or empty
|
||||
pages <[s]> List of pages
|
||||
sortKeys <o> Sort keys for list and item views for all types
|
||||
typeA <o> Sort keys for this type
|
||||
list <o> Sort keys for list views for this type
|
||||
viewA <[o]> Sort keys for this view
|
||||
id <s> Sort key id
|
||||
operator <s> Default sort operator ("+" or "-")
|
||||
item <o> Sort keys for item views for this type
|
||||
viewA <[o]> Sort keys for this view
|
||||
id <s> Sort key id
|
||||
operator <s> Default sort operator ("+" or "-")
|
||||
spanType <o> Span types for list and item views for all types
|
||||
typeA <o> Span types for this type
|
||||
list <o> Span types for list views for this type
|
||||
viewA <s> Span type for this view
|
||||
Can be "date", "duration" or "location"
|
||||
item <o> Span types for item views for this type
|
||||
viewA <s> Span type for this view
|
||||
Can be "date", "duration" or "location"
|
||||
types <[s]> List of types
|
||||
views <o> List and item views for all types
|
||||
typeA <o> Views for type "typeA"
|
||||
list <[s]> List views for this type
|
||||
item <[s]> Item views for this type
|
||||
@*/
|
||||
|
||||
/*
|
||||
|
||||
example.com[/page]
|
||||
or
|
||||
example.com[/type][/item][/view][/span][/sort][/find]
|
||||
|
||||
page Special page, like "about" or "contact"
|
||||
type Section a.k.a. item type, like "movies", "edits", "texts" etc.
|
||||
item Item id or title, like in '/movies/0060304', '/movies/inception' or
|
||||
'texts/ABC'. Testing this is asynchonous.
|
||||
view List or item view, like "clips" or "map". Both list and item views are
|
||||
per type.
|
||||
span Position or selection in a view, either one or two coordinates or one
|
||||
id, like in "video/01:00", "video/-01:00", "video/01:00,-01:00",
|
||||
"video/@annotationABC", "video/@subtitles:23", "text/@chapter42",
|
||||
"map/0,0", "map/-45,-90,45,90", "map/@barcelona", "image/100,100" etc.
|
||||
Testing id is asynchronous.
|
||||
sort Sort, like "title" or "-director" or "country,year,-language,+runtime"
|
||||
find Query, like a=x or a=x&b=y or a=x&(b=y|c=z). A query object has the form
|
||||
{conditions: [], operator: ''} (logical operator), and a condition
|
||||
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.
|
||||
|
||||
String Key Value Operator
|
||||
v * v = any text or string contains or any number is
|
||||
!v * v != no text or string contains and no number is
|
||||
k=v k v = contains (text or string), is (number)
|
||||
k!=v k v != does not contain (text or string), is not (number)
|
||||
k==v k v == is (string)
|
||||
k!==v k v !== is not (string)
|
||||
k=v* k v ^ starts with (string)
|
||||
k!=v* k v !^ does not start with (string)
|
||||
k=*v k v $ ends with (string)
|
||||
k!=*v k v !$ does not end with (string)
|
||||
k<v k v < is less than (number)
|
||||
k!<v k v !< is not less than (number)
|
||||
k>v k v > is more than (number)
|
||||
k!>v k v !> is not more than (number)
|
||||
k=v,w k [v,w] = is between (number), contains (string or text)
|
||||
k!=v,w k [v,w] != is not between (number), does not contain (string or text)
|
||||
|
||||
All parts of the URL can be omitted, as long as the order is preserved.
|
||||
|
||||
example.com/foo
|
||||
If "foo" is not a type, item (of the default type), list view (of the
|
||||
default type), span id (of any list view of the default type) or sort key
|
||||
(of any list view of the default type), then this means find *=foo
|
||||
example.com/title, or example.com/+title or example.com/-title
|
||||
If this neither matches a type or default type item, then this will be sort
|
||||
example.com/clip/+duration/title=foo
|
||||
If "clip" is a default type list view, this will show all clips of items
|
||||
that match title=foo, sorted by item duration in ascending order
|
||||
example.com/clip/+clip.duration/subtitles=foo
|
||||
If "clip" is a default type list view and "subtitles" is an annotation type,
|
||||
this will show all clips that match subtitles=foo, sorted by clip duration
|
||||
in ascending order. (In pan.do/ra's clip view, annotation=foo is always per
|
||||
clip. There is no way to show all clips of all items where any clip matches
|
||||
subtitles=foo, this doesn't seem to be needed.)
|
||||
example.com/map/@paris/duration/title!=london
|
||||
If "map" is a default type list view and "paris" is a place name, this will
|
||||
zoom the map to Paris, show all places of items that match title!=london,
|
||||
and when a place is selected sort matching clips by item duration in
|
||||
default order.
|
||||
example.com/calendar/1900,2000/clip:duration/event=hiroshima
|
||||
If "calendar" is a default type list view, this will zoom the calendar to
|
||||
the 20th century, show all events of all items that match event=hiroshima,
|
||||
and when an event is selected sort matching clips by clip duration in
|
||||
default order. (In pan.do/ra's map and calendar view, annotation=foo is
|
||||
always per item. There is no way to show all events of all clips that match
|
||||
event=hiroshima, this doesn't seem to be needed.)
|
||||
|
||||
example.com/2001/2001 -> example.com/0062622/video/00:33:21
|
||||
2001 matches an item title (word match), the second 2001 is a valid duration
|
||||
example.com/2002/2002 -> example.com/calendar/2002/2002
|
||||
2002 is a valid duration, but no list view supports durations. Then it is
|
||||
read as a year, and we get calendar view with find *=2002
|
||||
example.com/@paris/london -> example.com/map/@paris/london
|
||||
paris matches place ABC (case-insensitive), but (assuming) find *=london
|
||||
does not match place ABC, "paris" becomes the map query
|
||||
example.com/@paris/paris -> example.com/map/ABC/paris
|
||||
paris matches place ABC (case-insensitive), so we get map view, zoomed to
|
||||
ABC/Paris, which is selected, with find *=paris
|
||||
example.com/@renaissance/renaissance -> example.com/calendar/ABC/renaissance
|
||||
renaissaince matches an event name (case-insensitive), so we get calendar
|
||||
view, zoomed to the Renaissance, with find *=renaissance
|
||||
example.com/@foo/foo -> example.com/map/@foo/foo
|
||||
foo doesn't match a place or event name, but getSpan() sets the map query to
|
||||
foo and returns @foo, so we get map view, zoomed to Foo, with find *=foo
|
||||
example.com/clip:duration -> example.com/clip/clip:duration
|
||||
clip:duration is not a sort key of the default view (grid), so the view is
|
||||
set to the first list view that accepts this sort key
|
||||
|
||||
*/
|
||||
|
||||
Ox.URL = function(options) {
|
||||
|
||||
var self = {}, that = {};
|
||||
|
||||
self.options = Ox.extend({
|
||||
// fixme: find keys are also per type/list|item/view
|
||||
// since one can search for layer properties in some item views
|
||||
findKeys: [],
|
||||
getItem: null,
|
||||
getSpan: null,
|
||||
pages: [],
|
||||
spanType: {},
|
||||
sortKeys: {},
|
||||
types: [],
|
||||
views: {}
|
||||
}, options);
|
||||
|
||||
Ox.Log('Core', 'Ox.URL options', self.options)
|
||||
|
||||
self.previousTitle = '';
|
||||
self.previousURL = '';
|
||||
|
||||
window.onpopstate = function() {
|
||||
self.previousTitle = document.title;
|
||||
self.previousURL = document.location.pathname
|
||||
+ document.location.search
|
||||
+ document.location.hash;
|
||||
};
|
||||
|
||||
function constructCondition(condition) {
|
||||
var key = condition.key == '*' ? '' : condition.key,
|
||||
operator = condition.operator,
|
||||
value;
|
||||
value = (
|
||||
Ox.isArray(condition.value) ? condition.value : [condition.value]
|
||||
).map(function(value) {
|
||||
return encodeValue(constructValue(value, condition.key));
|
||||
}).join(',');
|
||||
if (!key) {
|
||||
operator = operator.replace('=', '');
|
||||
} else if (operator.indexOf('^') > -1) {
|
||||
operator = operator.replace('^', '=');
|
||||
value += '*';
|
||||
} else if (operator.indexOf('$') > -1) {
|
||||
operator = operator.replace('$', '=');
|
||||
value = '*' + value;
|
||||
}
|
||||
return [key, operator, value].join('');
|
||||
}
|
||||
|
||||
function constructDate(date) {
|
||||
return Ox.formatDate(date, '%Y-%m-%d', true);
|
||||
}
|
||||
|
||||
function constructDuration(duration) {
|
||||
return Ox.formatDuration(duration, 3).replace(/\.000$/, '');
|
||||
}
|
||||
|
||||
function constructFind(find) {
|
||||
return find.conditions.map(function(condition) {
|
||||
return condition.conditions
|
||||
? '(' + constructFind(condition) + ')'
|
||||
: constructCondition(condition);
|
||||
}).join(find.operator);
|
||||
}
|
||||
|
||||
function constructLocation(location) {
|
||||
return location.join(',');
|
||||
}
|
||||
|
||||
function constructSort(sort, state) {
|
||||
var sortKeys = self.options.sortKeys[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
][state.view];
|
||||
return sortKeys ? sort.map(function(sort) {
|
||||
return (
|
||||
Ox.getObjectById(sortKeys, sort.key).operator == sort.operator
|
||||
? '' : sort.operator
|
||||
) + sort.key;
|
||||
}).join(',') : '';
|
||||
}
|
||||
|
||||
function constructSpan(span, state) {
|
||||
var spanType = self.options.spanType[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
][state.view];
|
||||
return (Ox.isArray(span) ? span : [span]).map(function(point) {
|
||||
return Ox.isNumber(point) ? (
|
||||
spanType == 'date' ? constructDate(point)
|
||||
: spanType == 'duration' ? constructDuration(point)
|
||||
: constructLocation(point)
|
||||
) : point;
|
||||
}).join(',');
|
||||
}
|
||||
|
||||
function constructURL(state) {
|
||||
var parts = [];
|
||||
if (state.page) {
|
||||
parts.push(state.page);
|
||||
} else {
|
||||
if (self.options.types.indexOf(state.type) > 0) {
|
||||
parts.push(state.type);
|
||||
}
|
||||
if (state.item) {
|
||||
parts.push(state.item);
|
||||
}
|
||||
if (state.type && self.options.views[state.type][
|
||||
state.item ? 'item' : 'list'
|
||||
].indexOf(state.view) > -1) {
|
||||
parts.push(state.view);
|
||||
}
|
||||
if (state.span && state.span.length) {
|
||||
parts.push(constructSpan(state.span, state));
|
||||
}
|
||||
if (state.sort && state.sort.length) {
|
||||
parts.push(constructSort(state.sort, state));
|
||||
}
|
||||
if (state.find) {
|
||||
parts.push(constructFind(state.find));
|
||||
}
|
||||
}
|
||||
return '/' + Ox.filter(parts).join('/');
|
||||
}
|
||||
|
||||
function constructValue(str, key) {
|
||||
var findKey = Ox.getObjectById(self.options.findKeys, key),
|
||||
type = Ox.isArray(findKey.type) ? findKey.type[0] : findKey.type,
|
||||
value = str,
|
||||
values = findKey.values;
|
||||
return type == 'enum' ? values[value] : value;
|
||||
}
|
||||
|
||||
function decodeValue(str) {
|
||||
return decodeURIComponent(str);
|
||||
}
|
||||
|
||||
function encodeValue(str) {
|
||||
// var chars = '/&|()=*:';
|
||||
var chars = '&|()=*',
|
||||
ret = '';
|
||||
str.toString().split('').forEach(function(char) {
|
||||
var index = chars.indexOf(char);
|
||||
ret += index > -1
|
||||
? '%' + char.charCodeAt(0).toString(16).toUpperCase()
|
||||
: char;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
function isNumericalSpan(str) {
|
||||
return str.split(',').every(function(str) {
|
||||
return /^[0-9-\.:]+$/.test(str);
|
||||
});
|
||||
}
|
||||
|
||||
function getSpanType(str, types) {
|
||||
Ox.Log('Core', 'getSpanType', str, types)
|
||||
var canBeDate = types.indexOf('date') > -1,
|
||||
canBeDuration = types.indexOf('duration') > -1,
|
||||
canBeLocation = types.indexOf('location') > -1,
|
||||
length = str.split(',').length;
|
||||
return canBeDate && /\d-/.test(str) ? 'date'
|
||||
: canBeDuration && /:/.test(str) ? 'duration'
|
||||
: canBeLocation && length == 4 ? 'location'
|
||||
// leaves us with [-]D[.D][,[-]D[.D]]
|
||||
: canBeDuration ? 'duration'
|
||||
: canBeDate && !/\./.test(str) ? 'date'
|
||||
: canBeLocation && length == 2 ? 'location' : ':'
|
||||
}
|
||||
|
||||
function parseCondition(str) {
|
||||
Ox.Log('Core', 'PARSE COND', str)
|
||||
var condition = {},
|
||||
operators = ['!==', '==', '!=', '=', '!<', '<', '!>', '>'],
|
||||
split;
|
||||
Ox.forEach(operators, function(operator) {
|
||||
if (str.indexOf(operator) > - 1) {
|
||||
split = str.split(operator);
|
||||
condition = {
|
||||
key: split.shift(),
|
||||
value: split.join(operator),
|
||||
operator: operator
|
||||
};
|
||||
Ox.Break();
|
||||
}
|
||||
});
|
||||
if (
|
||||
!condition.operator
|
||||
|| Ox.getIndexById(self.options.findKeys, condition.key) == -1
|
||||
) {
|
||||
// missing operator or unknown key
|
||||
condition = {key: '*', value: str, operator: '='};
|
||||
}
|
||||
if (['=', '!='].indexOf(condition.operator) > -1) {
|
||||
if (condition.value[0] == '*') {
|
||||
condition.value = condition.value.slice(1);
|
||||
condition.operator = condition.operator.replace('=', '$')
|
||||
} else if (condition.value[condition.value.length - 1] == '*') {
|
||||
condition.value = condition.value.slice(0, -1);
|
||||
condition.operator = condition.operator.replace('=', '^')
|
||||
}
|
||||
}
|
||||
if (
|
||||
['date', 'enum', 'float', 'integer', 'time', 'year'].indexOf(
|
||||
Ox.getObjectById(self.options.findKeys, condition.key).type
|
||||
) > -1
|
||||
&& condition.value.indexOf(',') > -1
|
||||
) {
|
||||
condition.value = condition.value.split(',').map(function(value) {
|
||||
return parseValue(decodeValue(value), condition.key);
|
||||
});
|
||||
} else {
|
||||
condition.value = parseValue(decodeValue(condition.value), condition.key);
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
function parseDate(str) {
|
||||
return Ox.formatDate(Ox.parseDate(str, true), '%Y-%m-%d');
|
||||
}
|
||||
|
||||
function parseDuration(str) {
|
||||
return Ox.parseDuration(str);
|
||||
}
|
||||
|
||||
function parseFind(str) {
|
||||
str = (str || '').replace(/%7C/g, '|');
|
||||
var conditions, counter = 0,
|
||||
find = {conditions: [], operator: '&'},
|
||||
subconditions = [];
|
||||
if (str.length) {
|
||||
// replace subconditions with placeholder,
|
||||
// so we can later split by main operator
|
||||
Ox.forEach(str, function(c, i) {
|
||||
if (c == ')') {
|
||||
counter--;
|
||||
}
|
||||
if (counter >= 1) {
|
||||
subconditions[subconditions.length - 1] += c;
|
||||
}
|
||||
if (c == '(') {
|
||||
(++counter == 1) && subconditions.push('');
|
||||
}
|
||||
});
|
||||
subconditions = subconditions.filter(function(subcondition) {
|
||||
// make sure empty brackets don't throw errors
|
||||
return !!subcondition;
|
||||
});
|
||||
subconditions.forEach(function(subcondition, i) {
|
||||
str = str.replace(subcondition, i);
|
||||
});
|
||||
find.operator = str.indexOf('|') > -1 ? '|' : '&'
|
||||
find.conditions = str.split(find.operator).map(function(condition, i) {
|
||||
var ret;
|
||||
if (condition[0] == '(') {
|
||||
// re-insert subcondition
|
||||
ret = parseFind(subconditions[parseInt(condition.slice(1, -1))]);
|
||||
} else {
|
||||
ret = parseCondition(condition);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
return find;
|
||||
}
|
||||
|
||||
function parseLocation(str) {
|
||||
return str.split(',').map(function(str, i) {
|
||||
return Ox.limit(parseInt(str, 10), -90 * (i + 1), 90 * (i + 1));
|
||||
});
|
||||
}
|
||||
|
||||
function parseSort(str, state) {
|
||||
return str.split(',').map(function(str) {
|
||||
var hasOperator = /^[\+-]/.test(str);
|
||||
return {
|
||||
key: hasOperator ? str.slice(1) : str,
|
||||
operator: hasOperator
|
||||
? str[0]
|
||||
: Ox.getObjectById(self.options.sortKeys[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
][state.view], str).operator
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function parseSpan(str, type) {
|
||||
var split = str.split(',');
|
||||
if (split.length == 4) {
|
||||
split = [split[0] + ',' + split[1], split[2] + ',' + split[3]];
|
||||
}
|
||||
return split.map(
|
||||
type == 'date' ? parseDate
|
||||
: type == 'duration' ? parseDuration
|
||||
: parseLocation
|
||||
);
|
||||
}
|
||||
|
||||
function parseURL(str, callback) {
|
||||
// fixme: removing trailing slash makes it impossible to search for '/'
|
||||
str = str.replace(/(^\/|\/$)/g, '');
|
||||
var parts = str.split('/'),
|
||||
state = {};
|
||||
if (parts[0] == '') {
|
||||
// empty URL
|
||||
callback(state);
|
||||
} else if (self.options.pages.indexOf(parts[0]) > -1) {
|
||||
// page
|
||||
state.page = parts[0];
|
||||
callback(state);
|
||||
} else {
|
||||
if (self.options.types.indexOf(parts[0]) > -1) {
|
||||
// type
|
||||
state.type = parts[0];
|
||||
parts.shift();
|
||||
} else {
|
||||
// set to default type
|
||||
state.type = self.options.types[0];
|
||||
}
|
||||
if (parts.length) {
|
||||
Ox.Log('Core', 'ST', state.type, self.options.views)
|
||||
if (self.options.views[state.type].list.indexOf(parts[0]) > -1) {
|
||||
// list view
|
||||
state.item = '';
|
||||
state.view = parts[0];
|
||||
parts.shift();
|
||||
parseBeyondItem();
|
||||
} else {
|
||||
// test for item id or name
|
||||
self.options.getItem(parts[0].replace(/%20/g, ' '), function(item) {
|
||||
state.item = item;
|
||||
if (item) {
|
||||
parts.shift();
|
||||
}
|
||||
parseBeyondItem();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
function parseBeyondItem() {
|
||||
Ox.Log('Core', 'pBI', state, parts.join('/'));
|
||||
var span, spanType, spanTypes;
|
||||
if (
|
||||
parts.length && state.item
|
||||
&& self.options.views[state.type].item.indexOf(parts[0]) > -1
|
||||
) {
|
||||
// item view
|
||||
state.view = parts[0];
|
||||
parts.shift();
|
||||
}
|
||||
if (parts.length) {
|
||||
if (isNumericalSpan(parts[0])) {
|
||||
// test for numerical span
|
||||
spanTypes = self.options.spanType[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
];
|
||||
// if no view is given then parse the span anyway,
|
||||
// but make sure the span type could match a view
|
||||
spanType = state.view
|
||||
? spanTypes[state.view]
|
||||
: getSpanType(parts[0], Ox.unique(Ox.values(spanTypes)));
|
||||
Ox.Log('Core', 'SPAN TYPE', spanType)
|
||||
if (spanType) {
|
||||
span = parseSpan(parts[0], spanType);
|
||||
if (span) {
|
||||
if (state.view) {
|
||||
state.span = span;
|
||||
parts.shift();
|
||||
} else {
|
||||
// if no view is given then switch to the first
|
||||
// view that supports a span of this type
|
||||
Ox.forEach(self.options.views[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
], function(view) {
|
||||
if (spanTypes[view] == spanType) {
|
||||
state.view = view;
|
||||
state.span = span;
|
||||
parts.shift();
|
||||
Ox.Break();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!state.span && /^[A-Z@]/.test(parts[0])) {
|
||||
// test for span id or name
|
||||
self.options.getSpan(state.item, state.view, decodeValue(parts[0]), function(span, view) {
|
||||
Ox.Log('Core', 'span/view', span, view)
|
||||
if (span) {
|
||||
if (!state.view) {
|
||||
// set list or item view
|
||||
state.view = view;
|
||||
}
|
||||
state.span = span;
|
||||
parts.shift();
|
||||
}
|
||||
parseBeyondSpan();
|
||||
});
|
||||
} else {
|
||||
parseBeyondSpan();
|
||||
}
|
||||
} else {
|
||||
if (!state.view) {
|
||||
// set to default item view
|
||||
state.view = self.options.views[state.type].item[0];
|
||||
}
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
function parseBeyondSpan() {
|
||||
Ox.Log('Core', 'pBS', state)
|
||||
var sortKeyIds, sortParts;
|
||||
if (parts.length) {
|
||||
sortParts = parts[0].split(',');
|
||||
sortKeyIds = Ox.map(self.options.sortKeys[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
], function(sortKeys) {
|
||||
return sortKeys.map(function(sortKey) {
|
||||
return sortKey.id;
|
||||
});
|
||||
});
|
||||
// test if sort keys match the given view,
|
||||
// or any view if no view is given
|
||||
Ox.forEach(
|
||||
state.view ? [state.view]
|
||||
: self.options.views[state.type][!state.item ? 'list' : 'item'],
|
||||
function(view) {
|
||||
if (sortKeyIds[view] && sortParts.every(function(part) {
|
||||
return sortKeyIds[view].indexOf(part.replace(/^[\+-]/, '')) > -1;
|
||||
})) {
|
||||
if (!state.view) {
|
||||
// set list or item view
|
||||
state.view = view;
|
||||
}
|
||||
// sort
|
||||
state.sort = parseSort(parts[0], state);
|
||||
parts.shift();
|
||||
Ox.Break();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!state.view) {
|
||||
// set to default list or item view
|
||||
state.view = self.options.views[state.type][
|
||||
!state.item ? 'list' : 'item'
|
||||
][0];
|
||||
}
|
||||
if (parts.length) {
|
||||
// find
|
||||
state.find = parseFind(parts.join('/'));
|
||||
}
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
|
||||
function parseValue(str, key) {
|
||||
var findKey = Ox.getObjectById(self.options.findKeys, key),
|
||||
type = Ox.isArray(findKey.type) ? findKey.type[0] : findKey.type,
|
||||
value = str,
|
||||
values = findKey.values;
|
||||
if (type == 'boolean') {
|
||||
value = ['', 'false'].indexOf(str) == -1;
|
||||
} else if (type == 'date') {
|
||||
value = Ox.formatDate(Ox.parseDate(str, true), '%F', true);
|
||||
} else if (type == 'enum') {
|
||||
value = Math.max(values.map(function(value) {
|
||||
return value.toLowerCase();
|
||||
}).indexOf(str.toLowerCase()), 0);
|
||||
} else if (type == 'float') {
|
||||
value = parseFloat(str) || 0;
|
||||
} else if (type == 'integer') {
|
||||
value = Math.round(str) || 0;
|
||||
} else if (type == 'time') {
|
||||
value = Ox.formatDurarion(Ox.parseDuration(value));
|
||||
} else if (type == 'year') {
|
||||
value = Math.round(str) || 1970;
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
function saveURL() {
|
||||
|
||||
}
|
||||
|
||||
/*@
|
||||
parse <f> parse
|
||||
(callback) -> <o> parse state from document.location
|
||||
(url, callback) -> <o> parse state from passed url
|
||||
@*/
|
||||
that.parse = function() {
|
||||
var str = arguments.length == 2 ? arguments[0]
|
||||
: document.location.pathname
|
||||
+ document.location.search
|
||||
+ document.location.hash,
|
||||
callback = arguments[arguments.length - 1];
|
||||
parseURL(str, callback);
|
||||
return that;
|
||||
}
|
||||
|
||||
/*@
|
||||
pop <f> Sets the URL to the previous URL
|
||||
@*/
|
||||
that.pop = function() {
|
||||
if (self.previousURL) {
|
||||
history.pushState && history.pushState(
|
||||
{}, self.previousTitle, self.previousURL
|
||||
);
|
||||
document.title = self.previousTitle;
|
||||
}
|
||||
return !!self.previousURL;
|
||||
};
|
||||
|
||||
/*@
|
||||
push <f> Pushes a new URL
|
||||
(state, title, url, callback) -> <o> URL controller
|
||||
state <o> State for the new URL
|
||||
If state is null, it will be derived from url
|
||||
title <s> Title for the new URL
|
||||
url <s|o> New URL
|
||||
If url is null, it will be derived from state
|
||||
callback <f> callback function
|
||||
state <o> New state
|
||||
@*/
|
||||
that.push = function(state, title, url, callback) {
|
||||
if (!state) {
|
||||
parseURL(url, function(state) {
|
||||
pushState(state, title, url);
|
||||
});
|
||||
} else {
|
||||
url = url || constructURL(state);
|
||||
pushState(state, title, url);
|
||||
}
|
||||
function pushState(state, title, url) {
|
||||
self.previousTitle = document.title;
|
||||
self.previousURL = document.location.pathname
|
||||
+ document.location.search
|
||||
+ document.location.hash;
|
||||
if (url != self.previousURL) {
|
||||
history.pushState && history.pushState(
|
||||
Ox.extend(state, {title: title}), '', url
|
||||
);
|
||||
document.title = title;
|
||||
callback && callback(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@
|
||||
replace <f> Replaces the URL with a new URL
|
||||
(state, title, url, callback) -> <o> URL controller
|
||||
state <o> State for the new URL
|
||||
If state is null, it will be derived from url
|
||||
title <s> Title for the new URL
|
||||
url <s|o> New URL
|
||||
If url is null, it will be derived from state
|
||||
callback <f> callback function
|
||||
state <o> New state
|
||||
@*/
|
||||
that.replace = function(state, title, url, callback) {
|
||||
if (!state) {
|
||||
parseURL(url, function(state) {
|
||||
replaceState(state, title, url);
|
||||
});
|
||||
} else {
|
||||
url = url || constructURL(state);
|
||||
replaceState(state, title, url);
|
||||
}
|
||||
function replaceState(state, title, url) {
|
||||
history.replaceState && history.replaceState(
|
||||
Ox.extend(state, {title: title}), '', url
|
||||
);
|
||||
document.title = title;
|
||||
callback && callback(state);
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue