update Ox.URL
This commit is contained in:
parent
b7100792c5
commit
2cb6d89a0a
3 changed files with 315 additions and 106 deletions
|
@ -4,23 +4,46 @@
|
||||||
Ox.URL <f> URL controller
|
Ox.URL <f> URL controller
|
||||||
(options) -> <o> URL controller
|
(options) -> <o> URL controller
|
||||||
options <o> Options object
|
options <o> Options object
|
||||||
findKeys <[o]> Find keys {id: "", type: ""}
|
findKeys <[o]> Find keys
|
||||||
type can be "string" or "number"
|
id <s> Find key id
|
||||||
getItemId <f> Tests if a string matches an item
|
type <s> Value type ("string" or "number")
|
||||||
|
getItem <f> Tests if a string matches an item
|
||||||
(string, callback) -> <u> undefined
|
(string, callback) -> <u> undefined
|
||||||
string <s> The string to be tested
|
string <s> The string to be tested
|
||||||
callback <f> callback function
|
callback <f> callback function
|
||||||
id <s> Matching item id, or empty
|
id <s> Matching item id, or empty
|
||||||
getSpanId <f> Tests if a string matches a span
|
getSpan <f> Tests if a string matches a span
|
||||||
(string, callback) -> <u> undefined
|
(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
|
string <s> The string to be tested
|
||||||
callback <f> callback function
|
callback <f> Callback function
|
||||||
id <s> Matching span id, or empty
|
id <s> Matching span id, or empty
|
||||||
|
view <s> Matching view, or empty
|
||||||
pages <[s]> List of pages
|
pages <[s]> List of pages
|
||||||
sortKeys <[o]> Sort keys {id: "", operator: ""}
|
sortKeys <o> Sort keys for list and item views for all types
|
||||||
operator is the default operator ("+" or "-")
|
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
|
types <[s]> List of types
|
||||||
views <o> List of views {type: {'list': [...], 'item': [...]}}
|
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
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -82,12 +105,12 @@ example.com/clip/+clip.duration/subtitles=foo
|
||||||
in ascending order. (In pan.do/ra's clip view, annotation=foo is always per
|
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
|
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.)
|
subtitles=foo, this doesn't seem to be needed.)
|
||||||
example.com/map/Paris/duration/title!=london
|
example.com/map/@paris/duration/title!=london
|
||||||
Ff "map" is a default type list view and "Paris" is a place id, this will
|
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,
|
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
|
and when a place is selected sort matching clips by item duration in
|
||||||
default order.
|
default order.
|
||||||
example.com/calendar/1900,2000/clip.duration/event=hiroshima
|
example.com/calendar/1900,2000/clip:duration/event=hiroshima
|
||||||
If "calendar" is a default type list view, this will zoom the calendar to
|
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,
|
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
|
and when an event is selected sort matching clips by clip duration in
|
||||||
|
@ -95,6 +118,21 @@ example.com/calendar/1900,2000/clip.duration/event=hiroshima
|
||||||
always per item. There is no way to show all events of all clips that match
|
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.)
|
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/paris -> example.com/map/ABC/paris
|
||||||
|
paris matches a place name (case-insensitive), so we get map view, zoomed to
|
||||||
|
Paris, 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
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Ox.URL = function(options) {
|
Ox.URL = function(options) {
|
||||||
|
@ -102,18 +140,19 @@ Ox.URL = function(options) {
|
||||||
var self = {}, that = {};
|
var self = {}, that = {};
|
||||||
|
|
||||||
self.options = Ox.extend({
|
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: [],
|
findKeys: [],
|
||||||
getItemId: null,
|
getItem: null,
|
||||||
getSpanId: null,
|
getSpan: null,
|
||||||
pages: [],
|
pages: [],
|
||||||
sortKeys: [],
|
spanType: {},
|
||||||
|
sortKeys: {},
|
||||||
types: [],
|
types: [],
|
||||||
views: {}
|
views: {}
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
self.sortKeyIds = self.options.sortKeys.map(function(sortKey) {
|
Ox.print('Ox.URL options', self.options)
|
||||||
return sortKey.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
function constructCondition(condition) {
|
function constructCondition(condition) {
|
||||||
var key = condition.key == '*' ? '' : condition.key,
|
var key = condition.key == '*' ? '' : condition.key,
|
||||||
|
@ -133,6 +172,10 @@ Ox.URL = function(options) {
|
||||||
return [key, operator, value].join('');
|
return [key, operator, value].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function constructDate(date) {
|
||||||
|
return Ox.formatDate(date, '%Y-%m-%d', true);
|
||||||
|
}
|
||||||
|
|
||||||
function constructDuration(duration) {
|
function constructDuration(duration) {
|
||||||
return Ox.formatDuration(duration, 3).replace(/\.000$/, '');
|
return Ox.formatDuration(duration, 3).replace(/\.000$/, '');
|
||||||
}
|
}
|
||||||
|
@ -149,44 +192,60 @@ Ox.URL = function(options) {
|
||||||
}).join(find.operator);
|
}).join(find.operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructSort(sort) {
|
function constructLocation(location) {
|
||||||
|
return location.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructSort(sort, state) {
|
||||||
return sort.map(function(sort) {
|
return sort.map(function(sort) {
|
||||||
return (
|
return (
|
||||||
sort.operator == Ox.getObjectById(self.options.sortKeys, sort.key).operator
|
sort.operator == Ox.getObjectById(self.options.sortKeys[state.type][
|
||||||
? '' : sort.operator
|
!state.item ? 'list' : 'item'
|
||||||
|
][state.view], sort.key).operator ? '' : sort.operator
|
||||||
) + sort.key;
|
) + sort.key;
|
||||||
}).join(',')
|
}).join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructSpan(span) {
|
function constructSpan(span, state) {
|
||||||
return span.map(function(point) {
|
var spanType = self.options.spanType[state.type][
|
||||||
return /^[0-9-\.:]+$/.test(str) ? constuctDuration(point) : point;
|
!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(',');
|
}).join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructURL(state) {
|
function constructURL(state) {
|
||||||
var parts = [];
|
var parts = [];
|
||||||
if (self.options.types.indexOf(state.type) > 0) {
|
if (state.page) {
|
||||||
parts.push(state.type);
|
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 (self.options.views[state.type][
|
||||||
|
state.item ? 'item' : 'list'
|
||||||
|
].indexOf(state.view) > -1) {
|
||||||
|
parts.push(state.view);
|
||||||
|
}
|
||||||
|
if (state.span) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (state.item) {
|
return '/' + parts.join('/');
|
||||||
parts.push(item);
|
|
||||||
}
|
|
||||||
if (self.options.views[state.type][
|
|
||||||
state.item ? 'item' : 'list'
|
|
||||||
].indexOf(state.view) > 0) {
|
|
||||||
parts.push(state.view);
|
|
||||||
}
|
|
||||||
if (state.span) {
|
|
||||||
parts.push(constructSpan(state.span));
|
|
||||||
}
|
|
||||||
if (state.sort.length) {
|
|
||||||
parts.push(constructSort(state.sort));
|
|
||||||
}
|
|
||||||
if (state.find) {
|
|
||||||
parts.push(constructFind(state.find));
|
|
||||||
}
|
|
||||||
return parts.join('/');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeValue(str) {
|
function decodeValue(str) {
|
||||||
|
@ -205,8 +264,29 @@ Ox.URL = function(options) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNumericalSpan(str) {
|
||||||
|
return str.split(',').every(function(str) {
|
||||||
|
return /^[0-9-\.:]+$/.test(str);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSpanType(str, types) {
|
||||||
|
Ox.print('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) {
|
function parseCondition(str) {
|
||||||
var condition,
|
var condition = {},
|
||||||
operators = ['!==', '==', '!=', '=', '!<', '<', '!>', '>'],
|
operators = ['!==', '==', '!=', '=', '!<', '<', '!>', '>'],
|
||||||
split;
|
split;
|
||||||
Ox.forEach(operators, function(operator) {
|
Ox.forEach(operators, function(operator) {
|
||||||
|
@ -220,7 +300,11 @@ Ox.URL = function(options) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!condition.operator) {
|
if (
|
||||||
|
!condition.operator
|
||||||
|
|| Ox.getPositionById(self.options.findKeys, condition.key) == -1
|
||||||
|
) {
|
||||||
|
// missing operator or unknown key
|
||||||
condition = {key: '*', value: str, operator: '='};
|
condition = {key: '*', value: str, operator: '='};
|
||||||
}
|
}
|
||||||
if (['=', '!='].indexOf(condition.operator) > -1) {
|
if (['=', '!='].indexOf(condition.operator) > -1) {
|
||||||
|
@ -233,11 +317,17 @@ Ox.URL = function(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (condition.value.indexOf(':') > -1) {
|
if (condition.value.indexOf(':') > -1) {
|
||||||
condition.value = condition.value.split(':')
|
condition.value = condition.value.split(':').map(decodeValue);
|
||||||
|
} else {
|
||||||
|
condition.value = decodeValue(condition.value);
|
||||||
}
|
}
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseDate(str) {
|
||||||
|
return Ox.formatDate(Ox.parseDate(str, true), '%Y-%m-%d');
|
||||||
|
}
|
||||||
|
|
||||||
function parseDuration(str) {
|
function parseDuration(str) {
|
||||||
var parts = str.split(':').reverse();
|
var parts = str.split(':').reverse();
|
||||||
while (parts.length > 3) {
|
while (parts.length > 3) {
|
||||||
|
@ -284,18 +374,36 @@ Ox.URL = function(options) {
|
||||||
return find;
|
return find;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSort(str) {
|
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) {
|
return str.split(',').map(function(str) {
|
||||||
var hasOperator = /^[\+-]/.test(str);
|
var hasOperator = /^[\+-]/.test(str);
|
||||||
return {
|
return {
|
||||||
key: hasOperator ? str.substr(1) : str,
|
key: hasOperator ? str.substr(1) : str,
|
||||||
operator: hasOperator ? str[0] : Ox.getObjectById(self.options.sortKeys, str).operator
|
operator: hasOperator
|
||||||
|
? str[0]
|
||||||
|
: Ox.getObjectById(self.options.sortKeys[state.type][
|
||||||
|
!state.item ? 'list' : 'item'
|
||||||
|
][state.view], str).operator
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSpan(str, callback) {
|
function parseSpan(str, type) {
|
||||||
return str.split(',').map(parseDuration);
|
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) {
|
function parseURL(str, callback) {
|
||||||
|
@ -305,72 +413,135 @@ Ox.URL = function(options) {
|
||||||
state = {};
|
state = {};
|
||||||
if (parts.length == 0) {
|
if (parts.length == 0) {
|
||||||
state.page = '';
|
state.page = '';
|
||||||
|
callback(state);
|
||||||
} else if (self.options.pages.indexOf(parts[0]) > -1) {
|
} else if (self.options.pages.indexOf(parts[0]) > -1) {
|
||||||
state.page = parts[0];
|
state.page = parts[0];
|
||||||
|
callback(state);
|
||||||
} else {
|
} else {
|
||||||
if (self.options.types.indexOf(parts[0]) > -1) {
|
if (self.options.types.indexOf(parts[0]) > -1) {
|
||||||
|
// type
|
||||||
state.type = parts[0];
|
state.type = parts[0];
|
||||||
parts.shift();
|
parts.shift();
|
||||||
|
} else {
|
||||||
|
// set to default type
|
||||||
|
state.type = self.options.types[0];
|
||||||
}
|
}
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
if (self.options.views.list.indexOf(parts[0]) > -1) {
|
if (self.options.views[state.type].list.indexOf(parts[0]) > -1) {
|
||||||
|
// list view
|
||||||
state.item = '';
|
state.item = '';
|
||||||
state.view = parts[0];
|
state.view = parts[0];
|
||||||
parts.shift();
|
parts.shift();
|
||||||
parseBeyondItem(false);
|
parseBeyondItem();
|
||||||
} else {
|
} else {
|
||||||
self.options.getItemId(parts[0], function(itemId) {
|
// test for item id or name
|
||||||
if (itemId) {
|
self.options.getItem(parts[0], function(item) {
|
||||||
state.item = itemId;
|
state.item = item;
|
||||||
|
if (item) {
|
||||||
parts.shift();
|
parts.shift();
|
||||||
}
|
}
|
||||||
parseBeyondItem(!!itemId);
|
parseBeyondItem();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback(state);
|
callback(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function parseBeyondItem(itemId) {
|
function parseBeyondItem() {
|
||||||
if (parts.length && itemId && self.options.views.item.indexOf(parts[0]) > -1) {
|
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];
|
state.view = parts[0];
|
||||||
parts.shift();
|
parts.shift();
|
||||||
}
|
}
|
||||||
|
Ox.print('pBI', state, parts.join('/'));
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
if (parts.split(',').every(function(str) {
|
if (isNumericalSpan(parts[0])) {
|
||||||
return /^[0-9-\.:]+$/.test(str);
|
// test for numerical span
|
||||||
})) {
|
spanTypes = self.options.spanType[state.type][
|
||||||
state.span = parseSpan(parts[0]);
|
!state.item ? 'list' : 'item'
|
||||||
parts.shift()
|
];
|
||||||
parseBeyondSpan();
|
// if no view is given then parse the span anyway,
|
||||||
} else {
|
// but make sure the span type could match a view
|
||||||
self.options.getSpanId(parts[0], function(spanId) {
|
spanType = state.view
|
||||||
if (spanId) {
|
? spanTypes[state.view]
|
||||||
state.span = spanId;
|
: getSpanType(parts[0], Ox.unique(Ox.values(spanTypes)));
|
||||||
|
Ox.print('SPAN TYPE', spanType)
|
||||||
|
if (spanType) {
|
||||||
|
span = parseSpan(parts[0], spanType);
|
||||||
|
if (span) {
|
||||||
|
if (!state.view) {
|
||||||
|
// 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();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
state.span = span;
|
||||||
|
parts.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!state.span && /^[A-Z@]/.test(parts[0])) {
|
||||||
|
// test for span id or name
|
||||||
|
self.options.getSpan(state.item, state.view, parts[0], function(span, view) {
|
||||||
|
if (span) {
|
||||||
|
if (!state.view) {
|
||||||
|
// set list or item view
|
||||||
|
state.view = view;
|
||||||
|
}
|
||||||
|
state.span = span;
|
||||||
parts.shift();
|
parts.shift();
|
||||||
}
|
}
|
||||||
parseBeyondSpan();
|
parseBeyondSpan();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
parseBeyondSpan();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback(state);
|
callback(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function parseBeyondSpan() {
|
function parseBeyondSpan() {
|
||||||
|
if (!state.view) {
|
||||||
|
// set to default list or item view
|
||||||
|
state.view = self.options.views[state.type][
|
||||||
|
!state.item ? 'list' : 'item'
|
||||||
|
][0];
|
||||||
|
}
|
||||||
|
Ox.print('pBS', state)
|
||||||
|
var sortKeyIds = (self.options.sortKeys[state.type][
|
||||||
|
!state.item ? 'list' : 'item'
|
||||||
|
][state.view] || []).map(function(sortKey) {
|
||||||
|
return sortKey.id;
|
||||||
|
});
|
||||||
if (parts.length && parts[0].split(',').every(function(str) {
|
if (parts.length && parts[0].split(',').every(function(str) {
|
||||||
return self.sortKeyIds.indexOf(str.replace(/^[\+-]/, '')) > -1;
|
return sortKeyIds.indexOf(str.replace(/^[\+-]/, '')) > -1;
|
||||||
})) {
|
})) {
|
||||||
state.sort = parseSort(parts[0]);
|
// sort
|
||||||
|
state.sort = parseSort(parts[0], state);
|
||||||
parts.shift();
|
parts.shift();
|
||||||
}
|
}
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
|
// find
|
||||||
state.find = parseFind(parts.join('/'));
|
state.find = parseFind(parts.join('/'));
|
||||||
}
|
}
|
||||||
callback(state);
|
callback(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
that._constructURL = function(state) {
|
that._construct = function(state) {
|
||||||
return constructURL(state);
|
return constructURL(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,22 +49,26 @@ Ox.Filter = function(options, self) {
|
||||||
|
|
||||||
self.conditionOperators = {
|
self.conditionOperators = {
|
||||||
date: [
|
date: [
|
||||||
{id: '', title: 'is'},
|
{id: '=', title: 'is'},
|
||||||
{id: '!', title: 'is not'},
|
{id: '!=', title: 'is not'},
|
||||||
{id: '<', title: 'is before'},
|
{id: '<', title: 'is before'},
|
||||||
|
{id: '!<', title: 'is not before'},
|
||||||
{id: '>', title: 'is after'},
|
{id: '>', title: 'is after'},
|
||||||
|
{id: '!>', title: 'is not after'},
|
||||||
{id: '-', title: 'is between'},
|
{id: '-', title: 'is between'},
|
||||||
{id: '!-', title: 'is not between'}
|
{id: '!-', title: 'is not between'}
|
||||||
],
|
],
|
||||||
list: [
|
list: [
|
||||||
{id: '', title: 'is'},
|
{id: '=', title: 'is'},
|
||||||
{id: '!', title: 'is not'}
|
{id: '!=', title: 'is not'}
|
||||||
],
|
],
|
||||||
number: [
|
number: [
|
||||||
{id: '', title: 'is'},
|
{id: '=', title: 'is'},
|
||||||
{id: '!', title: 'is not'},
|
{id: '!=', title: 'is not'},
|
||||||
{id: '<', title: 'is less than'},
|
{id: '<', title: 'is less than'},
|
||||||
|
{id: '!<', title: 'is not less than'},
|
||||||
{id: '>', title: 'is greater than'},
|
{id: '>', title: 'is greater than'},
|
||||||
|
{id: '!>', title: 'is not greater than'},
|
||||||
{id: '-', title: 'is between'},
|
{id: '-', title: 'is between'},
|
||||||
{id: '!-', title: 'is not between'}/*,
|
{id: '!-', title: 'is not between'}/*,
|
||||||
{id: '^', title: 'starts with'},
|
{id: '^', title: 'starts with'},
|
||||||
|
@ -73,18 +77,18 @@ Ox.Filter = function(options, self) {
|
||||||
{id: '!$', title: 'does not end with'}*/
|
{id: '!$', title: 'does not end with'}*/
|
||||||
],
|
],
|
||||||
string: [
|
string: [
|
||||||
{id: '=', title: 'is'},
|
{id: '==', title: 'is'},
|
||||||
{id: '!=', title: 'is not'},
|
{id: '!==', title: 'is not'},
|
||||||
{id: '', title: 'contains'},
|
{id: '=', title: 'contains'},
|
||||||
{id: '!', title: 'does not contain'},
|
{id: '!=', title: 'does not contain'},
|
||||||
{id: '^', title: 'starts with'},
|
{id: '^', title: 'starts with'},
|
||||||
{id: '!^', title: 'does not start with'},
|
{id: '!^', title: 'does not start with'},
|
||||||
{id: '$', title: 'ends with'},
|
{id: '$', title: 'ends with'},
|
||||||
{id: '!$', title: 'does not end with'}
|
{id: '!$', title: 'does not end with'}
|
||||||
],
|
],
|
||||||
text: [
|
text: [
|
||||||
{id: '', title: 'contains'},
|
{id: '=', title: 'contains'},
|
||||||
{id: '!', title: 'does not contain'}
|
{id: '!=', title: 'does not contain'}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
self.operators = [
|
self.operators = [
|
||||||
|
|
78
source/Ox.js
78
source/Ox.js
|
@ -698,6 +698,12 @@ Ox.isEmpty <f> Returns true if a collection is empty
|
||||||
true
|
true
|
||||||
> Ox.isEmpty(function() {})
|
> Ox.isEmpty(function() {})
|
||||||
true
|
true
|
||||||
|
> Ox.isEmpty(function(a) {})
|
||||||
|
false
|
||||||
|
> Ox.isEmpty(null)
|
||||||
|
false
|
||||||
|
> Ox.isEmpty()
|
||||||
|
false
|
||||||
@*/
|
@*/
|
||||||
Ox.isEmpty = function(val) {
|
Ox.isEmpty = function(val) {
|
||||||
// fixme: what about deep isEmpty?
|
// fixme: what about deep isEmpty?
|
||||||
|
@ -771,8 +777,11 @@ Ox.len <f> Returns the length of an array, function, object or string
|
||||||
> Ox.len('abc')
|
> Ox.len('abc')
|
||||||
3
|
3
|
||||||
@*/
|
@*/
|
||||||
Ox.len = function(obj) {
|
Ox.len = function(col) {
|
||||||
return (Ox.isObject(obj) ? Ox.values(obj) : obj).length;
|
var type = Ox.typeOf(col);
|
||||||
|
return ['array', 'function', 'string'].indexOf(type) > -1
|
||||||
|
? col.length
|
||||||
|
: type == 'object' ? Ox.values(col).length : void 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -1253,6 +1262,10 @@ Ox.rgb = function(hsl) {
|
||||||
|
|
||||||
//@ Ox.AMPM <[str]> ['AM', 'PM']
|
//@ Ox.AMPM <[str]> ['AM', 'PM']
|
||||||
Ox.AMPM = ['AM', 'PM'];
|
Ox.AMPM = ['AM', 'PM'];
|
||||||
|
//@ Ox.BASE_32_ALIASES <o> Base 32 aliases
|
||||||
|
Ox.BASE_32_ALIASES = {'I': '1', 'L': '1', 'O': '0', 'U': 'V'},
|
||||||
|
//@ Ox.BASE_32_DIGITS <o> Base 32 digits
|
||||||
|
Ox.BASE_32_DIGITS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||||
//@ Ox.BCAD <[str]> ['BC', 'AD']
|
//@ Ox.BCAD <[str]> ['BC', 'AD']
|
||||||
Ox.BCAD = ['BC', 'AD'];
|
Ox.BCAD = ['BC', 'AD'];
|
||||||
// fixme: this is unused, and probably unneeded
|
// fixme: this is unused, and probably unneeded
|
||||||
|
@ -1292,7 +1305,8 @@ Ox.KEYS = {
|
||||||
108: 'enter.numpad', 110: 'dot.numpad', 111: 'slash.numpad',
|
108: 'enter.numpad', 110: 'dot.numpad', 111: 'slash.numpad',
|
||||||
112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5',
|
112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5',
|
||||||
117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10',
|
117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10',
|
||||||
122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15', 127: 'f16',
|
122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15',
|
||||||
|
127: 'f16', 128: 'f17', 129: 'f18', 130: 'f19', 131: 'f20',
|
||||||
144: 'numlock', 145: 'scrolllock',
|
144: 'numlock', 145: 'scrolllock',
|
||||||
186: 'semicolon', 187: 'equal', 188: 'comma', 189: 'minus',
|
186: 'semicolon', 187: 'equal', 188: 'comma', 189: 'minus',
|
||||||
190: 'dot', 191: 'slash', 192: 'backtick', 219: 'openbracket',
|
190: 'dot', 191: 'slash', 192: 'backtick', 219: 'openbracket',
|
||||||
|
@ -1938,9 +1952,6 @@ Ox.element = function(str) {
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
var aliases = {'I': '1', 'L': '1', 'O': '0', 'U': 'V'},
|
|
||||||
digits = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
||||||
|
|
||||||
function cap(width, height) {
|
function cap(width, height) {
|
||||||
// returns maximum encoding capacity of an image
|
// returns maximum encoding capacity of an image
|
||||||
return parseInt(width * height * 3/8) - 4;
|
return parseInt(width * height * 3/8) - 4;
|
||||||
|
@ -1980,6 +1991,29 @@ Ox.element = function(str) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*@
|
||||||
|
Ox.encodeBase26 <b> Encode a number as base26
|
||||||
|
> Ox.encodeBase26(3758)
|
||||||
|
'FOO'
|
||||||
|
@*/
|
||||||
|
Ox.encodeBase26 = function(num) {
|
||||||
|
return Ox.map(num.toString(26), function(char) {
|
||||||
|
return Ox.char(65 + parseInt(char, 26));
|
||||||
|
}).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
/*@
|
||||||
|
Ox.decodeBase26 <f> Decodes a base26-encoded number
|
||||||
|
See <a href="http://www.crockford.com/wrmg/base32.html">Base 32</a>.
|
||||||
|
> Ox.decodeBase26('foo')
|
||||||
|
3758
|
||||||
|
@*/
|
||||||
|
Ox.decodeBase26 = function(str) {
|
||||||
|
return parseInt(Ox.map(str.toUpperCase(), function(char) {
|
||||||
|
return (char.charCodeAt(0) - 65).toString(26);
|
||||||
|
}).join(''), 26);
|
||||||
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeBase32 <b> Encode a number as base32
|
Ox.encodeBase32 <b> Encode a number as base32
|
||||||
See <a href="http://www.crockford.com/wrmg/base32.html">Base 32</a>.
|
See <a href="http://www.crockford.com/wrmg/base32.html">Base 32</a>.
|
||||||
|
@ -1990,9 +2024,9 @@ Ox.element = function(str) {
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase32 = function(num) {
|
Ox.encodeBase32 = function(num) {
|
||||||
return Ox.map(num.toString(32), function(char) {
|
return Ox.map(num.toString(32), function(char) {
|
||||||
return digits[parseInt(char, 32)];
|
return Ox.BASE_32_DIGITS[parseInt(char, 32)];
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeBase32 <f> Decodes a base32-encoded number
|
Ox.decodeBase32 <f> Decodes a base32-encoded number
|
||||||
|
@ -2006,10 +2040,10 @@ Ox.element = function(str) {
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase32 = function(str) {
|
Ox.decodeBase32 = function(str) {
|
||||||
return parseInt(Ox.map(str.toUpperCase(), function(char) {
|
return parseInt(Ox.map(str.toUpperCase(), function(char) {
|
||||||
var index = digits.indexOf(aliases[char] || char);
|
var index = Ox.BASE_32_DIGITS.indexOf(Ox.BASE_32_ALIASES[char] || char);
|
||||||
return (index == -1 ? ' ' : index).toString(32);
|
return (index == -1 ? ' ' : index).toString(32);
|
||||||
}).join(''), 32);
|
}).join(''), 32);
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeBase64 <f> Encode a number as base64
|
Ox.encodeBase64 <f> Encode a number as base64
|
||||||
|
@ -2018,7 +2052,7 @@ Ox.element = function(str) {
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase64 = function(num) {
|
Ox.encodeBase64 = function(num) {
|
||||||
return btoa(Ox.encodeBase256(num)).replace(/=/g, '');
|
return btoa(Ox.encodeBase256(num)).replace(/=/g, '');
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeBase64 <f> Decodes a base64-encoded number
|
Ox.decodeBase64 <f> Decodes a base64-encoded number
|
||||||
|
@ -2027,7 +2061,7 @@ Ox.element = function(str) {
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase64 = function(str) {
|
Ox.decodeBase64 = function(str) {
|
||||||
return Ox.decodeBase256(atob(str));
|
return Ox.decodeBase256(atob(str));
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeBase128 <f> Encode a number as base128
|
Ox.encodeBase128 <f> Encode a number as base128
|
||||||
|
@ -2040,8 +2074,8 @@ Ox.element = function(str) {
|
||||||
str = Ox.char(num & 127) + str;
|
str = Ox.char(num & 127) + str;
|
||||||
num >>= 7;
|
num >>= 7;
|
||||||
}
|
}
|
||||||
return str;
|
return str || '0';
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeBase128 <f> Decode a base128-encoded number
|
Ox.decodeBase128 <f> Decode a base128-encoded number
|
||||||
|
@ -2054,7 +2088,7 @@ Ox.element = function(str) {
|
||||||
num += char.charCodeAt(0) << (len - i - 1) * 7;
|
num += char.charCodeAt(0) << (len - i - 1) * 7;
|
||||||
});
|
});
|
||||||
return num;
|
return num;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeBase256 <f> Encode a number as base256
|
Ox.encodeBase256 <f> Encode a number as base256
|
||||||
|
@ -2068,7 +2102,7 @@ Ox.element = function(str) {
|
||||||
num >>= 8;
|
num >>= 8;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeBase256 <f> Decode a base256-encoded number
|
Ox.decodeBase256 <f> Decode a base256-encoded number
|
||||||
|
@ -2081,7 +2115,7 @@ Ox.element = function(str) {
|
||||||
num += char.charCodeAt(0) << (len - i - 1) * 8;
|
num += char.charCodeAt(0) << (len - i - 1) * 8;
|
||||||
});
|
});
|
||||||
return num;
|
return num;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeDeflate <f> Encodes a string, using deflate
|
Ox.encodeDeflate <f> Encodes a string, using deflate
|
||||||
|
@ -2127,7 +2161,7 @@ Ox.element = function(str) {
|
||||||
}
|
}
|
||||||
callback && callback(data);
|
callback && callback(data);
|
||||||
return data;
|
return data;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeDeflate <f> Decodes an deflate-encoded string
|
Ox.decodeDeflate <f> Decodes an deflate-encoded string
|
||||||
|
@ -2179,7 +2213,7 @@ Ox.element = function(str) {
|
||||||
}
|
}
|
||||||
image.onerror = error;
|
image.onerror = error;
|
||||||
image.src = 'data:image/png;base64,' + btoa(data);
|
image.src = 'data:image/png;base64,' + btoa(data);
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeHTML <f> HTML-encodes a string
|
Ox.encodeHTML <f> HTML-encodes a string
|
||||||
|
@ -2261,7 +2295,7 @@ Ox.element = function(str) {
|
||||||
(and flip the second least significant bit, if at all)
|
(and flip the second least significant bit, if at all)
|
||||||
- write an extra png chunk containing some key
|
- write an extra png chunk containing some key
|
||||||
*/
|
*/
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodePNG <f> Decodes an image, returns a string
|
Ox.decodePNG <f> Decodes an image, returns a string
|
||||||
|
@ -2306,7 +2340,7 @@ Ox.element = function(str) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new RangeError('PNG codec can\'t decode image');
|
throw new RangeError('PNG codec can\'t decode image');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeUTF8 <f> Encodes a string as UTF-8
|
Ox.encodeUTF8 <f> Encodes a string as UTF-8
|
||||||
|
@ -2334,7 +2368,7 @@ Ox.element = function(str) {
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.decodeUTF8 <f> Decodes an UTF-8-encoded string
|
Ox.decodeUTF8 <f> Decodes an UTF-8-encoded string
|
||||||
|
|
Loading…
Reference in a new issue