update Ox.URL

This commit is contained in:
rlx 2011-09-22 03:20:27 +00:00
parent b7100792c5
commit 2cb6d89a0a
3 changed files with 315 additions and 106 deletions

View file

@ -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 (state.page) {
parts.push(state.page);
} else {
if (self.options.types.indexOf(state.type) > 0) { if (self.options.types.indexOf(state.type) > 0) {
parts.push(state.type); parts.push(state.type);
} }
if (state.item) { if (state.item) {
parts.push(item); parts.push(state.item);
} }
if (self.options.views[state.type][ if (self.options.views[state.type][
state.item ? 'item' : 'list' state.item ? 'item' : 'list'
].indexOf(state.view) > 0) { ].indexOf(state.view) > -1) {
parts.push(state.view); parts.push(state.view);
} }
if (state.span) { if (state.span) {
parts.push(constructSpan(state.span)); parts.push(constructSpan(state.span, state));
} }
if (state.sort.length) { if (state.sort && state.sort.length) {
parts.push(constructSort(state.sort)); parts.push(constructSort(state.sort, state));
} }
if (state.find) { if (state.find) {
parts.push(constructFind(state.find)); parts.push(constructFind(state.find));
} }
return parts.join('/'); }
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,
// 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.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 { } else {
self.options.getSpanId(parts[0], function(spanId) { state.span = span;
if (spanId) { parts.shift();
state.span = spanId; }
}
}
}
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);
}; };

View file

@ -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 = [

View file

@ -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