oxjs/source/js/Ox.js

2901 lines
79 KiB
JavaScript
Raw Normal View History

2011-04-22 22:54:53 +00:00
//vim: et:ts=4:sw=4:sts=4:ft=js
2010-01-07 20:21:07 +00:00
// todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
Ox = function(val) {
return Ox.wrap(val);
};
2010-01-07 20:21:07 +00:00
2010-09-03 08:47:40 +00:00
/*
================================================================================
Constants
================================================================================
*/
2011-02-09 17:56:35 +00:00
Ox.AMPM = ['AM', 'PM'];
2011-02-22 10:02:28 +00:00
//Ox.DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
Ox.DURATIONS = ['year', 'month', 'day', 'minute', 'second'];
2010-11-28 15:06:47 +00:00
Ox.EARTH_RADIUS = 6378137;
Ox.EARTH_CIRCUMFERENCE = Ox.EARTH_RADIUS * 2 * Math.PI;
2011-02-22 10:02:28 +00:00
Ox.HTML_ENTITIES = {
'"': '&quot;', '&': '&amp;', "'": '&apos;', '<': '&lt;', '>': '&gt;'
};
Ox.KEYS = {
SECTION: 0, BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13,
SHIFT: 16, CONTROL: 17, OPTION: 18, PAUSE: 19, CAPSLOCK: 20,
ESCAPE: 27, SPACE: 32, PAGEUP: 33, PAGEDOWN: 34, END: 35, HOME: 36,
LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, INSERT: 45, DELETE: 46, HELP: 47,
0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57,
A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74,
K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84,
U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90,
META_LEFT: 91, META_RIGHT: 92, SELECT: 93,
'0_NUMPAD': 96, '1_NUMPAD': 97, '2_NUMPAD': 98, '3_NUMPAD': 99,
'4_NUMPAD': 100, '5_NUMPAD': 101, '6_NUMPAD': 102, '7_NUMPAD': 103,
'8_NUMPAD': 104, '9_NUMPAD': 105, '*_NUMPAD': 106, '+_NUMPAD': 107,
'\n_NUMPAD': 108, '-_NUMPAD': 109, '._NUMPAD': 110, '/_NUMPAD': 111,
F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118,
F8: 110, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125,
F15: 126, F16: 127, NUMLOCK: 144, SCROLLLOCK: 145,
';': 186, '=': 187, ',': 188, '-': 189, '.': 190, '/': 191, '`': 192,
'(': 219, '\\': 220, ')': 221, '\'': 222
};
2011-03-05 02:08:30 +00:00
Ox.MAP_TILE_SIZE = 256;
2011-02-22 10:02:28 +00:00
Ox.MONTHS = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
2011-04-20 11:29:34 +00:00
Ox.SHORT_MONTHS = Ox.MONTHS.map(function(val) {
return val.substr(0, 3);
});
2011-02-09 17:56:35 +00:00
Ox.PREFIXES = ['K', 'M', 'G', 'T', 'P'];
2011-02-22 10:02:28 +00:00
Ox.SYMBOLS = {
DOLLAR: '\u0024',
CENT: '\u00A2', POUND: '\u00A3', CURRENCY: '\u00A4', YEN: '\u00A5',
BULLET: '\u2022', ELLIPSIS: '\u2026', PERMILLE: '\u2030',
COLON: '\u20A1', CRUZEIRO: '\u20A2', FRANC: '\u20A3', LIRA: '\u20A4',
NAIRA: '\u20A6', PESETA: '\u20A7', WON: '\u20A9', SHEQEL: '\u20AA',
DONG: '\u20AB', EURO: '\u20AC', KIP: '\u20AD', TUGRIK: '\u20AE',
DRACHMA: '\u20AF', PESO: '\u20B1', GUARANI: '\u20B2', AUSTRAL: '\u20B3',
HRYVNIA: '\u20B4', CEDI: '\u20B5', TENGE: '\u20B8', RUPEE: '\u20B9',
CELSIUS: '\u2103', FAHRENHEIT: '\u2109', POUNDS: '\u2114', OUNCE: '\u2125',
OHM: '\u2126', KELVIN: '\u212A', ANGSTROM: '\u212B', INFO: '\u2139',
LEFT: '\u2190', UP: '\u2191', RIGHT: '\u2192', DOWN: '\u2193',
HOME: '\u2196', END: '\u2198', RETURN: '\u21A9',
REDO: '\u21BA', UNDO: '\u21BB', PAGEUP: '\u21DE', PAGEDOWN: '\u21DF',
CAPSLOCK: '\u21EA', TAB: '\u21E5', SHIFT: '\u21E7', INFINITY: '\u221E',
CONTROL: '\u2303', COMMAND: '\u2318', ENTER: '\u2324', ALT: '\u2325',
DELETE: '\u2326', CLEAR:'\u2327',BACKSPACE: '\u232B', OPTION: '\u2387',
NAVIGATE: '\u2388', ESCAPE: '\u238B', EJECT: '\u23CF',
SPACE: '\u2423', DIAMOND: '\u25C6',
STAR: '\u2605', SOUND: '\u266B', TRASH: '\u267A', FLAG: '\u2691',
ANCHOR: '\u2693', GEAR: '\u2699', ATOM: '\u269B', WARNING: '\u26A0',
CUT: '\u2702', BACKUP: '\u2707', FLY: '\u2708', CHECK: '\u2713',
CLOSE: '\u2715', BALLOT: '\u2717', WINDOWS: '\u2756',
EDIT: '\uF802', CLICK: '\uF803', APPLE: '\uF8FF'
};
2011-04-21 11:50:08 +00:00
// local timezone offset in milliseconds
Ox.TIMEZONE_OFFSET = +new Date().getTimezoneOffset() * 60000;
2011-02-22 10:02:28 +00:00
Ox.TYPES = [
'Arguments', 'Array', 'Boolean', 'Date', 'Element', 'Function', 'Infinity',
'NaN', 'Null', 'Number', 'Object', 'RegExp', 'String', 'Undefined'
];
2011-03-03 21:02:35 +00:00
Ox.VERSION = '0.1.2';
2011-02-22 10:02:28 +00:00
Ox.WEEKDAYS = [
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
];
2011-04-20 11:29:34 +00:00
Ox.SHORT_WEEKDAYS = Ox.WEEKDAYS.map(function(val) {
return val.substr(0, 3);
});
2010-09-03 08:47:40 +00:00
2010-01-07 20:21:07 +00:00
/*
================================================================================
Core functions
================================================================================
*/
2010-01-27 12:30:00 +00:00
Ox.getset = function(obj, args, callback, context) {
2011-01-23 04:59:16 +00:00
/***
Generic getter and setter function
2011-02-22 10:02:28 +00:00
can be implemented like this:
that.options = function() {
return Ox.getset(options, arguments, setOption(key, val), that);
}
Ox.getset(obj, []) returns obj
2010-09-05 00:31:58 +00:00
Ox.getset(obj, [key]) returns obj.key
Ox.getset(obj, [key, val], callback, context)
Ox.getset(obj, [{key: val, ...}], callback, context) sets obj.key to val,
2011-02-22 10:02:28 +00:00
calls callback(key, val)
2011-03-03 21:02:35 +00:00
for each changed value,
2010-09-05 00:31:58 +00:00
returns context
2011-02-22 10:02:28 +00:00
(for chaining)
>>> o = new function() { var o = {}, s = function() {}, t = this; t.o = function() { return Ox['getset'](o, arguments, s, t); }; return t; }
true
>>> Ox.getset({}, []) && o.o('key', 'val').o('key')
'val'
>>> Ox.getset({}, []) && o.o({key: 'val', foo: 'bar'}).o().foo
'bar'
>>> Ox.getset({}, []) && typeof o.o({foo: undefined}).o('foo') == 'undefined'
true
>>> delete o
true
2011-01-23 04:59:16 +00:00
***/
2011-02-22 10:02:28 +00:00
var obj_ = Ox.clone(obj), ret;
if (args.length == 0) {
// getset([])
2010-01-27 12:30:00 +00:00
ret = obj;
2011-02-22 10:02:28 +00:00
} else if (args.length == 1 && !Ox.isObject(args[0])) {
// getset([key])
2011-04-19 21:21:08 +00:00
ret = obj[args[0]];
2010-01-27 12:30:00 +00:00
} else {
2011-02-22 10:02:28 +00:00
// getset([key, val]) or getset([{key: val, ...}])
args = Ox.makeObject(Ox.isObject(args[0]) ? args[0] : args);
obj = Ox.extend(obj, args);
Ox.forEach(args, function(val, key) {
if (!obj_ || !Ox.isEqual(obj_[key], val)) {
callback && callback(key, val);
2010-02-06 08:23:42 +00:00
}
2010-01-27 12:30:00 +00:00
});
ret = context;
}
return ret;
}
2010-01-07 20:21:07 +00:00
Ox.print = function() {
/*
*/
2011-02-09 17:56:35 +00:00
if (window.console) {
2011-02-22 10:02:28 +00:00
var args = Ox.makeArray(arguments),
2010-01-07 20:21:07 +00:00
date = new Date;
2011-02-09 17:56:35 +00:00
args.unshift(Ox.formatDate(date, '%H:%M:%S') + '.' +
2010-01-07 20:21:07 +00:00
(Ox.pad(+date % 1000, 3)));
2011-02-09 17:56:35 +00:00
window.console.log.apply(window.console, args);
2010-01-07 20:21:07 +00:00
}
2011-04-19 21:21:08 +00:00
};
2010-01-07 20:21:07 +00:00
2011-02-09 17:56:35 +00:00
Ox.uid = (function() {
2010-09-03 08:47:40 +00:00
/***
2011-01-23 04:59:16 +00:00
returns a unique id
>>> Ox.uid() != Ox.uid()
true
2010-09-03 08:47:40 +00:00
***/
2010-01-07 20:21:07 +00:00
var uid = 0;
return function() {
return uid++;
};
2011-02-09 17:56:35 +00:00
}());
2010-01-07 20:21:07 +00:00
Ox.wrap = function(val, chained) {
/***
>>> Ox.wrap('foobar').reverse()
'raboof'
>>> Ox.wrap('foobar').chain().reverse().reverse().value()
'foobar'
***/
var wrapper = {
chain: function() {
wrapper.chained = true;
return wrapper;
},
chained: chained || false,
value: function() {
return val;
}
};
Object.getOwnPropertyNames(Ox).forEach(function(name) {
if (name[0] == name[0].toLowerCase() && Ox.isFunction(Ox[name])) {
wrapper[name] = function() {
var args = Array.prototype.slice.call(arguments), ret;
args.unshift(val);
ret = Ox[name].apply(Ox, args);
return wrapper.chained ? Ox.wrap(ret, true) : ret;
2011-04-19 21:21:08 +00:00
};
}
});
return wrapper;
};
2010-01-07 20:21:07 +00:00
/*
================================================================================
Array and Object functions
================================================================================
*/
Ox.avg = function(obj) {
2010-09-03 08:47:40 +00:00
/***
2011-01-23 04:59:16 +00:00
returns the average of an array's values, or an object's properties
2010-01-07 20:21:07 +00:00
>>> Ox.avg([-1, 0, 1])
0
2011-02-09 17:56:35 +00:00
>>> Ox.avg({a: 1, b: 2, c: 3})
2010-01-07 20:21:07 +00:00
2
2010-09-03 08:47:40 +00:00
***/
return Ox.sum(obj) / Ox.len(obj);
2010-01-07 20:21:07 +00:00
};
2011-01-23 04:59:16 +00:00
Ox.clone = function(obj) {
2011-04-19 14:11:04 +00:00
/***
returns a copy of an array or object
2011-02-22 10:02:28 +00:00
>>> (function() { a = ['val']; b = Ox.clone(a); a[0] = null; return b[0]; }())
'val'
>>> (function() { a = {key: 'val'}; b = Ox.clone(a); a.key = null; return b.key; }())
'val'
2011-04-19 14:11:04 +00:00
***/
2011-01-23 04:59:16 +00:00
return Ox.isArray(obj) ? obj.slice() : Ox.extend({}, obj);
};
2011-02-28 10:03:01 +00:00
Ox.compact = function(arr) {
2011-04-19 14:11:04 +00:00
/***
2011-02-28 10:03:01 +00:00
returns an array without null or undefined values
>>> Ox.compact([null,,1,,2,,3])
[1, 2, 3]
2011-04-19 14:11:04 +00:00
***/
2011-02-28 10:03:01 +00:00
return Ox.map(arr, function(val) {
return Ox.isUndefined(val) ? null : val;
});
2011-04-19 21:21:08 +00:00
};
2011-02-28 10:03:01 +00:00
2011-02-09 17:56:35 +00:00
Ox.count = function(arr) {
2011-04-19 14:11:04 +00:00
/***
>>> Ox.count(['foo', 'bar', 'foo']).foo
2011-02-09 17:56:35 +00:00
2
2011-04-19 14:11:04 +00:00
***/
2011-02-09 17:56:35 +00:00
var obj = {};
arr.forEach(function(v) {
obj[v] = (obj[v] || 0) + 1;
});
return obj;
};
2010-01-07 20:21:07 +00:00
Ox.each = function(obj, fn) {
2011-02-09 17:56:35 +00:00
// fixme: deprecate!
2010-01-07 20:21:07 +00:00
/*
Ox.each() works for arrays, objects and strings,
like $.each(), unlike [].forEach()
>>> Ox.each([0, 1, 2], function(i, v) {})
[0, 1, 2]
>>> Ox.each({a: 1, b: 2, c: 3}, function(k, v) {}).a
1
2011-02-09 17:56:35 +00:00
>>> Ox.each('foo', function(i, v) {})
'foo'
2010-01-07 20:21:07 +00:00
*/
2011-02-09 17:56:35 +00:00
var i, isArray = Ox.isArray(obj);
2010-01-07 20:21:07 +00:00
for (i in obj) {
2011-02-09 17:56:35 +00:00
i = isArray ? parseInt(i) : i;
2011-01-23 04:59:16 +00:00
// fixme: should be (v, k), like [].forEach()
2010-01-07 20:21:07 +00:00
if (fn(i, obj[i]) === false) {
break;
}
}
return obj;
};
Ox.every = function(obj, fn) {
/*
Ox.every() works for arrays, objects and strings, unlike [].every()
2010-01-07 20:21:07 +00:00
>>> Ox.every([0, 1, 2], function(v, i) { return i == v; })
true
>>> Ox.every({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
false
2011-02-09 17:56:35 +00:00
>>> Ox.every("foo", function(v) { return v == 'f'; })
2010-01-07 20:21:07 +00:00
false
2010-07-20 20:04:13 +00:00
>>> Ox.every([true, true, true])
true
2010-01-07 20:21:07 +00:00
*/
2010-07-20 20:04:13 +00:00
return Ox.filter(Ox.values(obj), fn || function(v) {
return v;
}).length == Ox.len(obj);
2010-01-07 20:21:07 +00:00
};
2011-02-09 17:56:35 +00:00
Ox.extend = function() {
/*
2011-02-22 10:02:28 +00:00
>>> Ox.extend({a: 1, b: 1, c: 1}, {b: 2, c: 2}, {c: 3}).c
2011-02-09 17:56:35 +00:00
3
*/
2011-02-22 10:02:28 +00:00
var obj = arguments[0];
2011-02-09 17:56:35 +00:00
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg, i) {
Ox.forEach(arg, function(val, key) {
obj[key] = val;
2011-01-23 04:59:16 +00:00
});
});
2011-02-09 17:56:35 +00:00
return obj;
2011-01-23 04:59:16 +00:00
};
2011-04-19 21:21:08 +00:00
Ox.filter = function(obj, fn) {
/***
Ox.filter works for arrays, objects and strings, unlike [].filter()
2010-01-07 20:21:07 +00:00
>>> Ox.filter([2, 1, 0], function(v, i) { return v == i; })
[1]
2011-04-19 21:21:08 +00:00
>>> Ox.keys(Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; }))
['b']
>>> Ox.filter(' hello world ', function(v) { return v != ' '; })
'helloworld'
***/
var type = Ox.typeOf(obj),
ret = type == 'array' ? [] : type == 'object' ? {} : '';
Ox.forEach(obj, function(v, k) {
if (fn(v, k)) {
if (type == 'array') {
ret.push(v);
} else if (type == 'object') {
ret[k] = v;
} else {
ret += v;
}
2010-01-07 20:21:07 +00:00
}
2011-04-19 21:21:08 +00:00
});
2010-01-07 20:21:07 +00:00
return ret;
};
2010-09-03 08:47:40 +00:00
Ox.flatten = function(arr) {
/*
>>> Ox.flatten([1, [2, [3], 4], 5])
[1, 2, 3, 4, 5]
*/
var ret = [];
arr.forEach(function(v) {
if (Ox.isArray(v)) {
Ox.flatten(v).forEach(function(v) {
ret.push(v);
});
} else {
ret.push(v);
}
});
return ret;
}
Ox.find = function(arr, str) {
/*
2011-01-23 04:59:16 +00:00
returns an array with two arrays as elements:
an array of elements of arr that begin with str,
and an array of elements of arr that contain,
but do not begin with str
2010-09-03 08:47:40 +00:00
>>> Ox.find(["foo", "bar", "foobar", "barfoo"], "foo")
[["foo", "foobar"], ["barfoo"]]
*/
var arrLowerCase = arr.map(function(v) {
return v.toLowerCase();
}),
ret = [[], []];
str && arrLowerCase.forEach(function(v, i) {
var index = v.indexOf(str.toLowerCase());
index > -1 && ret[index == 0 ? 0 : 1].push(arr[i]);
});
return ret;
2011-04-19 21:21:08 +00:00
};
2010-09-03 08:47:40 +00:00
2011-02-09 17:56:35 +00:00
Ox.forEach = function(obj, fn) {
/*
Ox.forEach() works for arrays, objects and strings,
like $.each(), unlike [].forEach()
The arguments of the iterator function are (value, key),
like [].forEach(), unlike $.each()
2011-02-28 10:03:01 +00:00
>>> Ox.forEach('foo', function(v, i) {})
'foo'
2011-02-09 17:56:35 +00:00
>>> Ox.forEach([0, 1, 2], function(v, i) {})
[0, 1, 2]
>>> Ox.forEach({a: 1, b: 2, c: 3}, function(v, k) {}).a
1
*/
2011-02-28 10:03:01 +00:00
var isObject = Ox.isObject(obj), key;
2011-02-09 17:56:35 +00:00
for (key in obj) {
2011-02-28 10:03:01 +00:00
key = isObject ? key : parseInt(key);
2011-04-19 14:11:04 +00:00
if (/*hasOwnProperty.call(obj, key) && */fn(obj[key], key) === false) {
2011-02-09 17:56:35 +00:00
break;
}
}
return obj;
};
2010-07-05 16:52:12 +00:00
Ox.getObjectById = function(arr, id) {
2011-01-23 04:59:16 +00:00
/***
>>> Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo").title
"Foo"
***/
2010-07-05 16:52:12 +00:00
var ret = null;
2011-02-09 17:56:35 +00:00
Ox.forEach(arr, function(v) {
2010-07-05 16:52:12 +00:00
if (v.id == id) {
ret = v;
return false;
}
});
return ret;
};
Ox.getPositionById = function(arr, id) {
2011-01-23 04:59:16 +00:00
/***
>>> Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "bar")
1
***/
2010-07-05 16:52:12 +00:00
var ret = -1;
2011-02-09 17:56:35 +00:00
Ox.forEach(arr, function(v, i) {
2010-07-05 16:52:12 +00:00
if (v.id == id) {
ret = i;
return false;
}
});
return ret;
};
2011-02-22 10:02:28 +00:00
Ox.isEmpty = function(val) {
return Ox.len(val) == 0;
2011-02-22 10:02:28 +00:00
};
Ox.isEqual = function(obj0, obj1) {
/*
>>> Ox.isEqual(false, false)
true
>>> Ox.isEqual(0, 0)
true
>>> Ox.isEqual(NaN, NaN)
false
2011-02-28 10:03:01 +00:00
>>> Ox.isEqual('', '')
true
2011-02-22 10:02:28 +00:00
>>> Ox.isEqual([1, 2, 3], [1, 2, 3])
true
>>> Ox.isEqual({a: 1, b: [2, 3], c: {d: '4'}}, {a: 1, b: [2, 3], c: {d: '4'}})
true
2011-02-28 10:03:01 +00:00
>>> Ox.isEqual(function(arg) { return arg; }, function(arg) { return arg; });
2011-02-22 10:02:28 +00:00
true
*/
var ret = false;
if (obj0 === obj1) {
ret = true;
} else if (typeof(obj0) == typeof(obj1)) {
if (obj0 == obj1) {
ret = true;
} else if (Ox.isArray(obj0) && obj0.length == obj1.length) {
2011-02-25 10:23:33 +00:00
ret = true;
2011-02-22 10:02:28 +00:00
Ox.forEach(obj0, function(v, i) {
ret = Ox.isEqual(v, obj1[i]);
return ret;
});
} else if (Ox.isDate(obj0)) {
ret = obj0.getTime() == obj1.getTime();
} else if (Ox.isObject(obj0)) {
ret = Ox.isEqual(Ox.keys(obj0), Ox.keys(obj1)) &&
Ox.isEqual(Ox.values(obj0), Ox.values(obj1));
} else if (Ox.isFunction(obj0)) {
ret = obj0.toString() == obj1.toString();
}
}
return ret;
2011-04-19 21:21:08 +00:00
};
2011-02-22 10:02:28 +00:00
2010-01-07 20:21:07 +00:00
Ox.keys = function(obj) {
/*
works for arrays, objects and strings, unlike Object.keys()
2011-02-28 10:03:01 +00:00
>>> Ox.keys([1, 2, 3])
[0, 1, 2]
2011-02-09 17:56:35 +00:00
>>> Ox.keys({a: 1, b: 2, c: 3})
2010-01-07 20:21:07 +00:00
["a", "b", "c"]
>>> Ox.keys('abc')
[0, 1, 2]
2011-02-28 10:03:01 +00:00
>>> Ox.keys([,])
[0]
2010-01-07 20:21:07 +00:00
*/
var keys = [];
2011-02-09 17:56:35 +00:00
Ox.forEach(obj, function(v, k) {
2010-01-07 20:21:07 +00:00
keys.push(k);
});
2011-02-28 10:03:01 +00:00
return keys.sort();
2010-01-07 20:21:07 +00:00
};
Ox.len = function(obj) {
2010-01-07 20:21:07 +00:00
/*
>>> Ox.len([1, 2, 3])
3
>>> Ox.len({a: 1, b: 2, c: 3})
2011-02-22 10:02:28 +00:00
3
>>> Ox.len('abc')
2010-01-07 20:21:07 +00:00
3
2011-04-19 21:21:08 +00:00
>>> Ox.len(function(x, y) { return x + y; })
2
>>> Ox.len([,])
2011-02-28 10:03:01 +00:00
1
2010-01-07 20:21:07 +00:00
*/
return Ox.isObject(obj) ? Ox.values(obj).length : obj.length;
2010-01-07 20:21:07 +00:00
};
2011-04-20 11:29:34 +00:00
Ox.loop = function() {
var length = arguments.length,
fn = arguments[length - 1],
step = length == 4 ? arguments[2] : 1,
stop = arguments[length > 2 ? 1 : 0],
start = length > 2 ? arguments[0] : 0,
i;
for (i = start; i < stop; i += step) {
2011-04-19 21:21:08 +00:00
fn(i);
}
};
2011-02-22 10:02:28 +00:00
Ox.makeArray = function(arg) {
2010-01-07 20:21:07 +00:00
/*
like $.makeArray()
2011-02-22 10:02:28 +00:00
>>> Ox.makeArray('foo', 'bar')
['foo', 'bar']
>>> (function() { return Ox.makeArray(arguments); }('foo', 'bar'))
['foo', 'bar']
*/
return Array.prototype.slice.call(
Ox.isArguments(arg) ? arg : arguments
);
2010-01-07 20:21:07 +00:00
};
2011-02-22 10:02:28 +00:00
Ox.makeObject = function() {
2010-01-07 20:21:07 +00:00
/*
>>> Ox.makeObject("foo", "bar").foo
"bar"
2011-02-22 10:02:28 +00:00
>>> Ox.makeObject(["foo", "bar"]).foo
2010-01-07 20:21:07 +00:00
"bar"
2011-02-22 10:02:28 +00:00
>>> Ox.makeObject({foo: "bar"}).foo
2010-01-07 20:21:07 +00:00
"bar"
2011-02-22 10:02:28 +00:00
>>> (function() { return Ox.makeObject(arguments); }("foo", "bar")).foo
2010-01-07 20:21:07 +00:00
"bar"
*/
2011-04-19 21:21:08 +00:00
var obj = {};
if (arguments.length == 1) {
if (Ox.isObject(arguments[0])) {
2011-02-22 10:02:28 +00:00
// ({foo: 'bar'})
2011-04-19 21:21:08 +00:00
obj = arguments[0];
2011-02-22 10:02:28 +00:00
} else {
// (['foo', 'bar'])
2011-04-19 21:21:08 +00:00
obj[arguments[0][0]] = arguments[0][1];
2011-02-22 10:02:28 +00:00
}
2010-01-07 20:21:07 +00:00
} else {
2011-02-22 10:02:28 +00:00
// ('foo', 'bar')
2011-04-19 21:21:08 +00:00
obj[arguments[0]] = arguments[1];
2010-01-07 20:21:07 +00:00
}
return obj;
};
2011-02-28 10:03:01 +00:00
Ox.map = function(obj, fn) {
2010-01-07 20:21:07 +00:00
/*
2011-02-28 10:03:01 +00:00
Ox.map() works for arrays, objects and strings,
unlike [].map()
2010-01-07 20:21:07 +00:00
>>> Ox.map([1, 1, 1], function(v, i) { return v == i; })
[false, true, false]
2011-02-28 10:03:01 +00:00
>>> Ox.map({a: 'a', b: 'a', c: 'a'}, function(v, k) { return v == k; }).a
true
2010-01-07 20:21:07 +00:00
>>> Ox.map("111", function(v, i) { return v == i; })
[false, true, false]
2011-02-28 10:03:01 +00:00
>>> Ox.map([,], function(v, i) { return i; })
[0]
2010-01-07 20:21:07 +00:00
*/
2011-02-28 10:03:01 +00:00
var isObject = Ox.isObject(obj),
2011-04-19 21:21:08 +00:00
ret = isObject ? {} : [];
2011-02-28 10:03:01 +00:00
Ox.forEach(obj, function(val, key) {
if ((v = fn(val, key)) !== null) {
ret[isObject ? key : ret.length] = v;
2010-01-07 20:21:07 +00:00
}
2011-02-28 10:03:01 +00:00
});
2010-01-07 20:21:07 +00:00
return ret;
};
Ox.max = function(obj) {
/*
>>> Ox.max([-1, 0, 1])
1
2011-02-09 17:56:35 +00:00
>>> Ox.max({a: 1, b: 2, c: 3})
2010-01-07 20:21:07 +00:00
3
*/
return Math.max.apply(Math, Ox.values(obj));
};
Ox.min = function(obj) {
/*
>>> Ox.min([-1, 0, 1])
-1
2011-02-09 17:56:35 +00:00
>>> Ox.min({a: 1, b: 2, c: 3})
2010-01-07 20:21:07 +00:00
1
*/
return Math.min.apply(Math, Ox.values(obj));
};
2011-01-23 04:59:16 +00:00
Ox.merge = function(arr) {
2011-02-09 17:56:35 +00:00
/*
>>> Ox.merge(['foo'], ['bar'], ['baz'])
['foo', 'bar', 'baz']
*/
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) {
Ox.forEach(arg, function(val) {
2011-01-23 04:59:16 +00:00
arr.push(val);
});
});
return arr;
};
2010-01-07 20:21:07 +00:00
Ox.range = function(start, stop, step) {
/*
>>> Ox.range(3)
[0, 1, 2]
>>> Ox.range(3, 0)
[3, 2, 1]
>>> Ox.range(1, 2, 0.5)
[1, 1.5]
*/
stop = arguments.length > 1 ? stop : arguments[0];
start = arguments.length > 1 ? start : 0;
step = step || (start <= stop ? 1 : -1);
2011-02-22 10:02:28 +00:00
var arr = [], i;
2010-01-07 20:21:07 +00:00
for (i = start; step > 0 ? i < stop : i > stop; i += step) {
2011-02-22 10:02:28 +00:00
arr.push(i);
2010-01-07 20:21:07 +00:00
}
2011-02-22 10:02:28 +00:00
return arr;
2010-01-07 20:21:07 +00:00
};
2010-07-13 22:38:53 +00:00
Ox.serialize = function(obj) {
2010-09-03 08:47:40 +00:00
/*
2011-02-09 17:56:35 +00:00
>>> Ox.serialize({a: 1, b: 2, c: 3})
'a=1&b=2&c=3'
2010-09-03 08:47:40 +00:00
*/
2010-07-13 22:38:53 +00:00
var arr = [];
2011-02-09 17:56:35 +00:00
Ox.forEach(obj, function(val, key) {
val !== '' && arr.push(key + '=' + val);
2010-07-13 22:38:53 +00:00
});
2011-01-03 12:01:38 +00:00
return arr.join('&');
2010-07-13 22:38:53 +00:00
};
2010-09-03 08:47:40 +00:00
Ox.setPropertyOnce = function(arr, str) {
2011-02-09 17:56:35 +00:00
/*
2011-02-22 10:02:28 +00:00
>>> Ox.setPropertyOnce([{selected: false}, {selected: false}], 'selected')
2011-02-09 17:56:35 +00:00
0
2011-02-22 10:02:28 +00:00
>>> Ox.setPropertyOnce([{selected: false}, {selected: true}], 'selected')
1
>>> Ox.setPropertyOnce([{selected: true}, {selected: true}], 'selected')
2011-02-09 17:56:35 +00:00
0
*/
2010-09-03 08:47:40 +00:00
var pos = -1;
2011-02-09 17:56:35 +00:00
Ox.forEach(arr, function(v, i) {
2010-09-03 08:47:40 +00:00
if (pos == -1 && arr[i][str]) {
pos = i;
} else if (pos > -1 && arr[i][str]) {
delete arr[i][str];
}
});
if (pos == -1) {
arr[0][str] = true;
pos = 0;
}
return pos;
2011-04-19 21:21:08 +00:00
};
2010-09-03 08:47:40 +00:00
2010-01-07 20:21:07 +00:00
Ox.shuffle = function(arr) {
/*
>>> Ox.shuffle([1, 2, 3]).length
3
*/
var shuffle = arr;
return shuffle.sort(function() {
return Math.random() - 0.5;
});
};
Ox.some = function(obj, fn) {
/*
2010-07-05 16:52:12 +00:00
Ox.some() works for arrays, objects and strings, unlike [].some()
2010-01-07 20:21:07 +00:00
>>> Ox.some([2, 1, 0], function(i, v) { return i == v; })
true
>>> Ox.some({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
true
2011-02-09 17:56:35 +00:00
>>> Ox.some("foo", function(v) { return v == 'f'; })
2010-01-07 20:21:07 +00:00
true
*/
return Ox.filter(Ox.values(obj), fn).length > 0;
};
2011-02-28 10:03:01 +00:00
Ox.sort = function(arr) {
/*
>>> Ox.sort(['10', '9', 'B', 'a'])
['9', '10', 'a', 'B']
*/
var len, matches = {}, sort = {};
arr.forEach(function(val, i) {
var match = /^\d+/(val);
matches[val] = match ? match[0] : '';
});
len = Ox.max(Ox.map(matches, function(val) {
return val.length;
}));
arr.forEach(function(val) {
sort[val] = (
matches[val] ?
Ox.pad(matches[val], len) + val.toString().substr(matches[val].length) :
val
).toLowerCase();
});
return arr.sort(function(a, b) {
var ret = 0;
if (sort[a] < sort[b]) {
ret = -1;
} else if (sort[a] > sort[b]) {
ret = 1;
}
return ret;
});
};
2010-01-07 20:21:07 +00:00
Ox.sum = function(obj) {
/*
>>> Ox.sum([-1, 0, 1])
0
2011-02-09 17:56:35 +00:00
>>> Ox.sum({a: 1, b: 2, c: 3})
2010-01-07 20:21:07 +00:00
6
*/
var sum = 0;
2011-02-09 17:56:35 +00:00
Ox.forEach(obj, function(val) {
sum += val;
2010-01-07 20:21:07 +00:00
});
return sum;
};
2011-02-22 10:02:28 +00:00
Ox.toArray = function(obj) {
/*
>>> Ox.toArray('foo')
['foo']
>>> Ox.toArray(['foo'])
['foo']
*/
var arr;
if (Ox.isArray(obj)) {
arr = obj;
} else if (Ox.isArguments(obj)) {
arr = Ox.makeArray(obj);
} else {
arr = [obj];
}
return arr;
};
2010-09-06 23:44:37 +00:00
Ox.unique = function(arr) {
/*
>>> Ox.unique([1, 2, 3, 1])
[1, 2, 3]
*/
var unique = [];
2011-02-22 10:02:28 +00:00
Ox.forEach(arr, function(val) {
unique.indexOf(val) == -1 && unique.push(val);
2010-09-06 23:44:37 +00:00
});
return unique;
};
2010-07-13 22:38:53 +00:00
Ox.unserialize = function(str) {
2011-02-09 17:56:35 +00:00
/*
>>> Ox.unserialize('a=1&b=2&c=3').c
'3'
*/
2010-07-13 22:38:53 +00:00
var arr, obj = {};
2011-02-09 17:56:35 +00:00
Ox.forEach(str.split('&'), function(val) {
arr = val.split('=');
2010-07-13 22:38:53 +00:00
obj[arr[0]] = arr[1];
});
return obj;
};
2010-01-07 20:21:07 +00:00
Ox.values = function(obj) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.values([1, 2, 3])
2010-01-07 20:21:07 +00:00
[1, 2, 3]
2011-02-28 10:03:01 +00:00
>>> Ox.values({a: 1, b: 2, c: 3})
[1, 2, 3]
>>> Ox.values('abc')
['a', 'b', 'c']
2011-02-28 10:03:01 +00:00
>>> Ox.values([1,])
[1]
2010-01-07 20:21:07 +00:00
*/
var values = [];
2011-02-09 17:56:35 +00:00
Ox.forEach(obj, function(val) {
values.push(val);
2010-01-07 20:21:07 +00:00
});
return values;
};
Ox.zip = function() {
/*
>>> Ox.zip([[0, 1], [2, 3], [4, 5]])
[[0, 2, 4], [1, 3, 5]]
>>> Ox.zip([0, 1, 2], [3, 4, 5])
[[0, 3], [1, 4], [2, 5]]
*/
var args = arguments.length == 1 ? arguments[0] : Ox.makeArray(arguments),
arr = [];
args[0].forEach(function(v, i) {
arr[i] = [];
args.forEach(function(v_, i_) {
arr[i].push(v_[i]);
});
});
return arr;
};
/*
================================================================================
Color functions
================================================================================
*/
Ox.hsl = function(rgb) {
/*
>>> Ox.hsl([0, 0, 0])
[0, 0, 0]
>>> Ox.hsl([255, 255, 255])
[0, 0, 1]
>>> Ox.hsl([0, 255, 0])
[120, 1, 0.5]
*/
rgb = rgb.map(function(v) {
return v / 255;
});
var max = Ox.max(rgb),
min = Ox.min(rgb),
hsl = [0, 0, 0];
hsl[2] = 0.5 * (max + min);
if (max == min) {
hsl[0] = 0;
hsl[1] = 0;
} else {
if (max == rgb[0]) {
hsl[0] = (60 * (rgb[1] - rgb[2]) / (max - min) + 360) % 360;
} else if (max == rgb[1]) {
hsl[0] = 60 * (rgb[2] - rgb[0]) / (max - min) + 120;
} else if (max == rgb[2]) {
hsl[0] = 60 * (rgb[0] - rgb[1]) / (max - min) + 240;
}
if (hsl[2] <= 0.5) {
hsl[1] = (max - min) / (2 * hsl[2]);
} else {
hsl[1] = (max - min) / (2 - 2 * hsl[2]);
}
}
return hsl;
};
Ox.rgb = function(hsl) {
/*
>>> Ox.rgb([0, 0, 0])
[0, 0, 0]
>>> Ox.rgb([0, 0, 1])
[255, 255, 255]
>>> Ox.rgb([120, 1, 0.5])
[0, 255, 0]
*/
hsl[0] /= 360;
var rgb = [0, 0, 0],
v1, v2, v3;
if (hsl[1] == 0) {
rgb = [hsl[2], hsl[2], hsl[2]];
} else {
if (hsl[2] < 0.5) {
v2 = hsl[2] * (1 + hsl[1]);
} else {
v2 = hsl[1] + hsl[2] - (hsl[1] * hsl[2]);
}
v1 = 2 * hsl[2] - v2;
rgb.forEach(function(v, i) {
v3 = hsl[0] + (1 - i) * 1/3;
if (v3 < 0) {
v3++;
} else if (v3 > 1) {
v3--;
}
if (v3 < 1/6) {
rgb[i] = v1 + ((v2 - v1) * 6 * v3);
} else if (v3 < 0.5) {
rgb[i] = v2;
} else if (v3 < 2/3) {
rgb[i] = v1 + ((v2 - v1) * 6 * (2/3 - v3));
} else {
rgb[i] = v1;
}
});
}
return rgb.map(function(v) {
return v * 255;
});
};
/*
================================================================================
Date functions
================================================================================
*/
2010-09-03 08:47:40 +00:00
Ox.getDateInWeek = function(date, weekday) {
2010-01-07 20:21:07 +00:00
/*
2010-09-03 08:47:40 +00:00
>>> Ox.formatDate(Ox.getDateInWeek(new Date("January 1 2000"), "Sunday"), "%A, %B %e, %Y")
"Sunday, January 2, 2000"
>>> Ox.formatDate(Ox.getDateInWeek(new Date("Jan 1 2000"), "Fri"), "%A, %B %e, %Y")
"Friday, December 31, 1999"
>>> Ox.formatDate(Ox.getDateInWeek(new Date("1/1/2000"), 1), "%A, %B %e, %Y")
"Monday, December 27, 1999"
2010-01-07 20:21:07 +00:00
*/
2010-09-03 08:47:40 +00:00
var date = date || new Date(),
sourceWeekday = Ox.formatDate(date, "%u");
targetWeekday = Ox.isNumber(weekday) ? weekday :
Ox.map(Ox.WEEKDAYS, function(v, i) {
return v.substr(0, 3) == weekday.substr(0, 3) ? i + 1 : null;
})[0];
date.setDate(date.getDate() - sourceWeekday + targetWeekday);
return date;
}
2010-01-07 20:21:07 +00:00
2010-09-03 08:47:40 +00:00
Ox.getDayOfTheYear = function(date) {
2010-01-07 20:21:07 +00:00
/*
2011-02-22 10:02:28 +00:00
>>> Ox.getDayOfTheYear(new Date("12/31/2000"))
366
>>> Ox.getDayOfTheYear(new Date("12/31/2002"))
365
2010-01-07 20:21:07 +00:00
>>> Ox.getDayOfTheYear(new Date("12/31/2004"))
366
*/
return function(date) {
date = date || new Date();
2011-02-22 10:02:28 +00:00
var month = date.getMonth(),
year = date.getFullYear();
return Ox.sum(Ox.map(Ox.range(month), function(i) {
return Ox.getDaysInMonth(year, i + 1);
})) + date.getDate();
/*
2010-01-07 20:21:07 +00:00
var day = date.getDate(),
2011-02-22 10:02:28 +00:00
month = date.getMonth();
2010-01-07 20:21:07 +00:00
i;
for (i = 0; i < month; i++) {
2011-02-22 10:02:28 +00:00
day += Ox.DAYS[i];
2010-01-07 20:21:07 +00:00
}
if (month >= 2 && Ox.isLeapYear(date.getFullYear())) {
day++;
}
return day;
2011-02-22 10:02:28 +00:00
*/
2010-01-07 20:21:07 +00:00
};
}();
2010-09-03 08:47:40 +00:00
Ox.getDaysInMonth = function(year, month) {
/*
>>> Ox.getDaysInMonth(2000, 2)
2011-02-22 10:02:28 +00:00
29
2010-09-03 08:47:40 +00:00
>>> Ox.getDaysInMonth("2002", "Feb")
28
>>> Ox.getDaysInMonth("2004", "February")
29
*/
var year = parseInt(year),
month = Ox.isNumber(month) ? month :
Ox.map(Ox.MONTHS, function(v, i) {
return v.substr(0, 3) == month.substr(0, 3) ? i + 1 : null;
})[0];
2011-02-22 10:02:28 +00:00
return new Date(year, month, 0).getDate()
//return Ox.DAYS[month - 1] + (month == 2 && Ox.isLeapYear(year));
2010-09-03 08:47:40 +00:00
}
2011-04-20 11:29:34 +00:00
Ox.getDaysInYear = function(year) {
return 365 + Ox.isLeapYear(year);
};
2010-09-03 08:47:40 +00:00
Ox.getFirstDayOfTheYear = function(date) {
/*
Decimal weekday of January 1 (0-6, Sunday as first day)
>>> Ox.getFirstDayOfTheYear(new Date("01/01/00"))
6
*/
var date_ = date ? new Date(date.valueOf()) : new Date();
date_.setMonth(0);
date_.setDate(1);
return date_.getDay();
};
2010-01-07 20:21:07 +00:00
Ox.getISODate = function(date) {
/*
>>> Ox.getISODate(new Date("01/01/2000"))
"2000-01-01T00:00:00Z"
*/
return Ox.formatDate(date || new Date(), '%FT%TZ');
};
Ox.getISODay = function(date) {
/*
Decimal weekday (1-7, Monday as first day)
>>> Ox.getISODay(new Date("01/01/2000"))
6
>>> Ox.getISODay(new Date("01/02/2000"))
7
>>> Ox.getISODay(new Date("01/03/2000"))
1
*/
return (date || new Date()).getDay() || 7;
};
Ox.getISOWeek = function(date) {
/*
see http://en.wikipedia.org/wiki/ISO_8601
>>> Ox.getISOWeek(new Date("01/01/2000"))
52
>>> Ox.getISOWeek(new Date("01/02/2000"))
52
>>> Ox.getISOWeek(new Date("01/03/2000"))
1
*/
date = date || new Date();
var date_ = new Date(date.valueOf());
// set date to Thursday of the same week
date_.setDate(date.getDate() - Ox.getISODay(date) + 4);
return Math.floor((Ox.getDayOfTheYear(date_) - 1) / 7) + 1;
};
Ox.getISOYear = function(date) {
/*
see http://en.wikipedia.org/wiki/ISO_8601
>>> Ox.getISOYear(new Date("01/01/2000"))
1999
>>> Ox.getISOYear(new Date("01/02/2000"))
1999
>>> Ox.getISOYear(new Date("01/03/2000"))
2000
*/
date = date || new Date();
var date_ = new Date(date.valueOf());
// set date to Thursday of the same week
date_.setDate(date.getDate() - Ox.getISODay(date) + 4);
return date_.getFullYear();
};
2010-01-27 12:30:00 +00:00
Ox.getTime = function() {
return +new Date();
}
2010-01-07 20:21:07 +00:00
Ox.getTimezoneOffsetString = function(date) {
/*
2011-02-22 10:02:28 +00:00
Time zone offset string ('-1200' - '+1200')
>>> Ox.getTimezoneOffsetString(new Date('01/01/2000')).length
5
2010-01-07 20:21:07 +00:00
*/
var offset = (date || new Date()).getTimezoneOffset();
return (offset < 0 ? '+' : '-') +
Ox.pad(Math.floor(Math.abs(offset) / 60), 2) +
Ox.pad(Math.abs(offset) % 60, 2);
};
Ox.getWeek = function(date) {
/*
Week of the year (0-53, Sunday as first day)
>>> Ox.getWeek(new Date("01/01/2000"))
0
>>> Ox.getWeek(new Date("01/02/2000"))
1
>>> Ox.getWeek(new Date("01/03/2000"))
1
*/
date = date || new Date();
return Math.floor((Ox.getDayOfTheYear(date) +
Ox.getFirstDayOfTheYear(date) - 1) / 7);
};
Ox.isLeapYear = function(year) {
/*
2011-02-22 10:02:28 +00:00
>>> Ox.isLeapYear(1900)
2010-01-07 20:21:07 +00:00
false
2011-02-22 10:02:28 +00:00
>>> Ox.isLeapYear(2000)
true
>>> Ox.isLeapYear(2004)
true
2010-01-07 20:21:07 +00:00
*/
2011-02-22 10:02:28 +00:00
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
2010-01-07 20:21:07 +00:00
};
2011-04-21 11:50:08 +00:00
Ox.makeDate = function(date) {
return Ox.isDate(date) ? date :
Ox.isUndefined(date) ? new Date() : new Date(date);
};
2010-01-07 20:21:07 +00:00
/*
================================================================================
DOM functions
================================================================================
*/
Ox.canvas = function() {
// Ox.canvas(img) or Ox.canvas(width, height)
var c = {}, isImage = arguments.length == 1,
image = isImage ? arguments[0] : {
width: arguments[0], height: arguments[1]
};
2011-02-09 17:56:35 +00:00
c.context = (c.canvas = Ox.element('canvas').attr({
2010-01-07 20:21:07 +00:00
width: image.width, height: image.height
2011-02-09 17:56:35 +00:00
})[0]).getContext('2d');
2010-09-03 08:47:40 +00:00
isImage && c.context.drawImage(image, 0, 0);
2011-02-09 17:56:35 +00:00
c.data = (c.imageData = c.context.getImageData(
0, 0, image.width, image.height
)).data;
2010-01-07 20:21:07 +00:00
return c;
};
Ox.element = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.element('div').attr({id: 'foo'}).attr('id')
'foo'
>>> Ox.element('div').html('foo').html()
'foo'
2010-01-07 20:21:07 +00:00
*/
return {
2011-02-09 17:56:35 +00:00
0: str[0] == '#' ? document.getElementById(str.substr(1)) :
2010-01-07 20:21:07 +00:00
document.createElement(str),
attr: function() {
var args, ret, that = this;
if (arguments.length == 1 && Ox.isString(arguments[0])) {
ret = this[0].getAttribute(arguments[0]);
} else {
2011-02-09 17:56:35 +00:00
Ox.forEach(Ox.makeObject.apply(this, arguments), function(v, k) {
2010-01-07 20:21:07 +00:00
that[0].setAttribute(k, v);
});
ret = this;
}
return ret;
},
html: function(str) {
var ret;
if (Ox.isUndefined(str)) {
ret = this[0].innerHTML;
} else {
this[0].innerHTML = str;
ret = this;
}
return ret;
}
}
};
/*
================================================================================
Encoding functions
================================================================================
*/
(function() {
2011-02-09 17:56:35 +00:00
var aliases = {'I': '1', 'L': '1', 'O': '0', 'U': 'V'},
digits = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2010-01-07 20:21:07 +00:00
2011-02-09 17:56:35 +00:00
function cap(width, height) {
2010-01-07 20:21:07 +00:00
// returns maximum encoding capacity of an image
return parseInt(width * height * 3/8) - 4;
}
function seek(data, px) {
// returns this, or the next, opaque pixel
while (data[px * 4 + 3] < 255) {
if (++px * 4 == data.length) {
2011-02-09 17:56:35 +00:00
throwPNGError('de');
2010-01-07 20:21:07 +00:00
}
}
return px;
}
function xor(byte) {
// returns "1"-bits-in-byte % 2
var xor = 0;
2011-02-09 17:56:35 +00:00
Ox.range(8).forEach(function(i) {
2010-01-07 20:21:07 +00:00
xor ^= byte >> i & 1;
});
return xor;
}
function throwPNGError(str) {
2011-02-09 17:56:35 +00:00
throw new RangeError(
'PNG codec can\'t ' +
(str == 'en' ? 'encode data' : 'decode image')
);
2010-01-07 20:21:07 +00:00
}
function throwUTF8Error(byte, pos) {
2011-02-09 17:56:35 +00:00
throw new RangeError(
'UTF-8 codec can\'t decode byte 0x' +
byte.toString(16).toUpperCase() + ' at position ' + pos
);
2010-01-07 20:21:07 +00:00
}
Ox.encodeBase32 = function(num) {
// see http://www.crockford.com/wrmg/base32.html
/*
>>> Ox.encodeBase32(15360)
2011-02-09 17:56:35 +00:00
'F00'
2010-01-07 20:21:07 +00:00
>>> Ox.encodeBase32(33819)
2011-02-09 17:56:35 +00:00
'110V'
2010-01-07 20:21:07 +00:00
*/
return Ox.map(num.toString(32), function(char) {
return digits[parseInt(char, 32)];
2011-02-09 17:56:35 +00:00
}).join('');
2010-01-07 20:21:07 +00:00
}
Ox.decodeBase32 = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase32('foo')
2010-01-07 20:21:07 +00:00
15360
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase32('ilou')
2010-01-07 20:21:07 +00:00
33819
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase32('?').toString()
'NaN'
2010-01-07 20:21:07 +00:00
*/
2011-02-22 10:02:28 +00:00
return parseInt(Ox.map(str.toUpperCase(), function(char) {
2010-01-07 20:21:07 +00:00
var index = digits.indexOf(aliases[char] || char);
2011-02-09 17:56:35 +00:00
return (index == -1 ? ' ' : index).toString(32);
}).join(''), 32);
2010-01-07 20:21:07 +00:00
}
Ox.encodeBase64 = function(num) {
/*
>>> Ox.encodeBase64(32394)
2011-02-09 17:56:35 +00:00
'foo'
2010-01-07 20:21:07 +00:00
*/
return btoa(Ox.encodeBase256(num)).replace(/=/g, "");
}
Ox.decodeBase64 = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase64('foo')
2010-01-07 20:21:07 +00:00
32394
*/
return Ox.decodeBase256(atob(str));
}
Ox.encodeBase128 = function(num) {
/*
>>> Ox.encodeBase128(1685487)
2011-02-09 17:56:35 +00:00
'foo'
2010-01-07 20:21:07 +00:00
*/
2011-02-09 17:56:35 +00:00
var str = '';
2010-01-07 20:21:07 +00:00
while (num) {
str = Ox.char(num & 127) + str;
num >>= 7;
}
return str;
}
Ox.decodeBase128 = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase128('foo')
2010-01-07 20:21:07 +00:00
1685487
*/
var num = 0, len = str.length;
2011-02-09 17:56:35 +00:00
Ox.forEach(str, function(char, i) {
2010-01-07 20:21:07 +00:00
num += char.charCodeAt(0) << (len - i - 1) * 7;
});
return num;
}
Ox.encodeBase256 = function(num) {
/*
>>> Ox.encodeBase256(6713199)
2011-02-09 17:56:35 +00:00
'foo'
2010-01-07 20:21:07 +00:00
*/
2011-02-09 17:56:35 +00:00
var str = '';
2010-01-07 20:21:07 +00:00
while (num) {
str = Ox.char(num & 255) + str;
num >>= 8;
}
return str;
}
Ox.decodeBase256 = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeBase256('foo')
2010-01-07 20:21:07 +00:00
6713199
*/
var num = 0, len = str.length;
2011-02-09 17:56:35 +00:00
Ox.forEach(str, function(char, i) {
2010-01-07 20:21:07 +00:00
num += char.charCodeAt(0) << (len - i - 1) * 8;
});
return num;
}
Ox.encodeDeflate = function(str) {
// encodes string, using deflate
/*
in fact, the string is written to the rgb channels of a canvas element,
then the dataURL is decoded from base64, and some head and tail cut off
*/
str = Ox.encodeUTF8(str);
var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1), data;
str = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)) + str +
2011-02-09 17:56:35 +00:00
Ox.repeat('\u00FF', (4 - len % 4) % 4); // simpler? Ox.pad()?
2010-01-07 20:21:07 +00:00
/* fixme: why does map not work here?
c.data = $.map(c.data, function(v, i) {
return i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
});
*/
for (i = 0; i < c.data.length; i += 1) {
c.data[i] = i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
}
c.context.putImageData(c.imageData, 0, 0);
2011-02-09 17:56:35 +00:00
Ox.print(c.canvas.toDataURL())
data = atob(c.canvas.toDataURL().split(',')[1]);
Ox.print('data', data);
2010-01-07 20:21:07 +00:00
return data.substr(8, data.length - 20);
}
Ox.decodeDeflate = function(str) {
var image = new Image();
2011-02-09 17:56:35 +00:00
image.src = 'data:image/png;base64,' + btoa('\u0089PNG\r\n\u001A\n' +
str + Ox.repeat('\u0000', 4) + 'IEND\u00AEB`\u0082');
Ox.print(image.src);
while (!image.width) {} // block until image data is available
2010-01-07 20:21:07 +00:00
str = Ox.map(Ox.canvas(image).data, function(v, i) {
2011-02-09 17:56:35 +00:00
return i % 4 < 3 ? Ox.char(v) : '';
}).join('');
2010-01-07 20:21:07 +00:00
return Ox.decodeUTF8(str.substr(4, Ox.decodeBase256(str.substr(0, 4))));
}
2011-02-09 17:56:35 +00:00
Ox.encodeHTML = function(str) {
2010-01-07 20:21:07 +00:00
/*
2011-02-09 17:56:35 +00:00
>>> Ox.encodeHTML('\'<"&">\'')
'&apos;&lt;&quot;&amp;&quot;&gt;&apos;'
>>> Ox.encodeHTML('äbçdê')
'&#x00E4;b&#x00E7;d&#x00EA;'
2010-01-07 20:21:07 +00:00
*/
2011-02-22 10:02:28 +00:00
return Ox.map(str, function(v) {
2011-02-09 17:56:35 +00:00
var code = v.charCodeAt(0);
return code < 128 ? (v in Ox.HTML_ENTITIES ? Ox.HTML_ENTITIES[v] : v) :
'&#x' + Ox.pad(code.toString(16).toUpperCase(), 4) + ';';
}).join('');
};
2010-01-07 20:21:07 +00:00
Ox.decodeHTML = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeHTML('&apos;&lt;&quot;&amp;&quot;&gt;&apos;')
'\'<"&">\''
>>> Ox.decodeHTML('&#x0027;&#x003C;&#x0022;&#x0026;&#x0022;&#x003E;&#x0027;')
'\'<"&">\''
>>> Ox.decodeHTML('&auml;b&ccedil;d&ecirc;')
'äbçdê'
>>> Ox.decodeHTML('&#x00E4;b&#x00E7;d&#x00EA;')
'äbçdê'
2010-01-07 20:21:07 +00:00
*/
// relies on dom, but shorter than using this:
2011-02-22 10:02:28 +00:00
// http://www.w3.org/TR/html5/named-character-references.html
return Ox.element('div').html(str)[0].childNodes[0].nodeValue;
//return $('<div/>').html(str)[0].childNodes[0].nodeValue;
2010-01-07 20:21:07 +00:00
};
Ox.encodePNG = function(img, str) {
// encodes string into image, returns new image url
/*
the message is compressed with deflate (by proxy of canvas),
then the string (four bytes length) + (length bytes message)
is encoded bitwise into the r/g/b bytes of all opaque pixels
by flipping, if necessary, the least significant bit, so that
(number of "1"-bits of the byte) % 2 is the bit of the string
wishlist:
- only use deflate if it actually shortens the message
- in deflate, strip and later re-insert the chunk types
- encode a decoy message into the least significant bit
(and flip the second least significant bit, if at all)
- write an extra png chunk containing some key
*/
2011-02-09 17:56:35 +00:00
//str = Ox.encodeDeflate(str); currently broken
str = Ox.encodeUTF8(str);
2010-01-07 20:21:07 +00:00
var c = Ox.canvas(img), len = str.length, px = 0;
2011-02-09 17:56:35 +00:00
if (len == 0 || len > cap(img.width, img.height)) {
throwPNGError('en')
2010-01-07 20:21:07 +00:00
}
len = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0));
2011-02-09 17:56:35 +00:00
Ox.forEach(Ox.map(len + str, function(byte) {
return Ox.map(Ox.range(8), function(i) {
2010-01-07 20:21:07 +00:00
return byte.charCodeAt(0) >> 7 - i & 1;
2011-02-09 17:56:35 +00:00
}).join('');
}).join(''), function(bit, i) {
2010-01-07 20:21:07 +00:00
var index = parseInt((px = seek(c.data, px)) * 4 + i % 3),
byte = c.data[index];
c.data[index] = bit == xor(byte) ? byte :
byte & 254 | !(byte & 1);
px += i % 3 == 2;
});
c.context.putImageData(c.imageData, 0, 0);
return c.canvas.toDataURL();
}
Ox.decodePNG = function(img) {
// decodes image, returns string
2011-02-09 17:56:35 +00:00
var bits = '', data = Ox.canvas(img).data, flag = false, i = 0,
len = 4, max = cap(img.width, img.height), px = 0, str = '';
2010-01-07 20:21:07 +00:00
do {
bits += xor(data[parseInt((px = seek(data, px)) * 4 + i % 3)]);
px += i % 3 == 2;
if (++i % 8 == 0) {
str += Ox.char(parseInt(bits, 2));
2011-02-09 17:56:35 +00:00
bits = '';
2010-01-07 20:21:07 +00:00
len--;
if (len == 0 && !flag) {
len = Ox.decodeBase256(str);
2011-02-09 17:56:35 +00:00
if (len <= 0 || len > max) {
2010-02-06 10:29:27 +00:00
Ox.print(len);
2011-02-09 17:56:35 +00:00
throwPNGError('de');
2010-01-07 20:21:07 +00:00
}
2011-02-09 17:56:35 +00:00
str = '';
2010-01-07 20:21:07 +00:00
flag = true;
}
}
} while (len);
try {
2011-02-09 17:56:35 +00:00
//return Ox.decodeDeflate(str); currently broken
return Ox.decodeUTF8(str);
2010-01-07 20:21:07 +00:00
} catch(e) {
2010-02-06 10:29:27 +00:00
Ox.print(e.toString());
2011-02-09 17:56:35 +00:00
throwPNGError('de');
2010-01-07 20:21:07 +00:00
}
}
Ox.encodeUTF8 = function(str) {
/*
see http://en.wikipedia.org/wiki/UTF-8
2011-02-09 17:56:35 +00:00
>>> Ox.encodeUTF8('foo')
'foo'
>>> Ox.encodeUTF8('¥€$')
'\u00C2\u00A5\u00E2\u0082\u00AC\u0024'
2010-01-07 20:21:07 +00:00
*/
2011-02-25 10:23:33 +00:00
return Ox.map(str, function(chr) {
2011-02-09 17:56:35 +00:00
var code = chr.charCodeAt(0),
str = '';
2010-01-07 20:21:07 +00:00
if (code < 128) {
2011-02-09 17:56:35 +00:00
str = chr;
2010-01-07 20:21:07 +00:00
} else if (code < 2048) {
str = String.fromCharCode(code >> 6 | 192) +
String.fromCharCode(code & 63 | 128);
} else {
str = String.fromCharCode(code >> 12 | 224) +
String.fromCharCode(code >> 6 & 63 | 128) +
String.fromCharCode(code & 63 | 128);
}
return str;
2011-02-09 17:56:35 +00:00
}).join('');
2010-01-07 20:21:07 +00:00
}
Ox.decodeUTF8 = function(str) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.decodeUTF8('foo')
'foo'
>>> Ox.decodeUTF8('\u00C2\u00A5\u00E2\u0082\u00AC\u0024')
'¥€$'
2010-01-07 20:21:07 +00:00
*/
2011-02-22 10:02:28 +00:00
var bytes = Ox.map(str, function(v) {
2010-01-07 20:21:07 +00:00
return v.charCodeAt(0);
}),
i = 0,
len = str.length,
2011-02-09 17:56:35 +00:00
str = '';
2010-01-07 20:21:07 +00:00
while (i < len) {
if (bytes[i] <= 128) {
str += String.fromCharCode(bytes[i]);
i++;
2011-02-09 17:56:35 +00:00
} else if (
bytes[i] >= 192 && bytes[i] < 240 &&
i < len - (bytes[i] < 224 ? 1 : 2)
) {
2010-01-07 20:21:07 +00:00
if (bytes[i + 1] >= 128 && bytes[i + 1] < 192) {
if (bytes[i] < 224) {
str += String.fromCharCode((bytes[i] & 31) << 6 |
bytes[i + 1] & 63);
i += 2;
} else if (bytes[i + 2] >= 128 && bytes[i + 2] < 192) {
str += String.fromCharCode((bytes[i] & 15) << 12 |
(bytes[i + 1] & 63) << 6 | bytes[i + 2] & 63);
i += 3;
} else {
throwUTF8Error(bytes[i + 2], i + 2);
}
} else {
throwUTF8Error(bytes[i + 1], i + 1);
}
} else {
throwUTF8Error(bytes[i], i);
}
}
return str;
};
})();
/*
================================================================================
Format functions
================================================================================
*/
2011-02-25 10:23:33 +00:00
Ox.formatArea = function(num, dec) {
return Ox.formatNumber(Ox.round(num / 1000000, dec)) + ' km²';
}
2011-01-24 04:08:19 +00:00
Ox.formatColor = function() {
};
Ox.formatCurrency = function(num, str, dec) {
2011-02-22 10:02:28 +00:00
/*
>>> Ox.formatCurrency(1000, '$', 2)
'$1,000.00'
*/
return str + Ox.formatNumber(num, dec);
};
2010-01-07 20:21:07 +00:00
Ox.formatDate = function() {
/*
See http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/strftime.3.html
and http://en.wikipedia.org/wiki/ISO_8601
>>> _date = new Date("01/02/05 00:03:04")
"Sun Jan 02 2005 00:03:04 GMT+0100 (CET)"
>>> Ox.formatDate(_date, "%A") // Full weekday
"Sunday"
>>> Ox.formatDate(_date, "%a") // Abbreviated weekday
"Sun"
>>> Ox.formatDate(_date, "%B") // Full month
"January"
>>> Ox.formatDate(_date, "%b") // Abbreviated month
"Jan"
>>> Ox.formatDate(_date, "%C") // Century
"20"
>>> Ox.formatDate(_date, "%c") // US time and date
"01/02/05 12:03:04 AM"
>>> Ox.formatDate(_date, "%D") // US date
"01/02/05"
>>> Ox.formatDate(_date, "%d") // Zero-padded day of the month
"02"
>>> Ox.formatDate(_date, "%e") // Space-padded day of the month
" 2"
>>> Ox.formatDate(_date, "%F") // Date
"2005-01-02"
>>> Ox.formatDate(_date, "%G") // Full ISO-8601 year
"2004"
>>> Ox.formatDate(_date, "%g") // Abbreviated ISO-8601 year
"04"
>>> Ox.formatDate(_date, "%H") // Zero-padded hour (24-hour clock)
"00"
>>> Ox.formatDate(_date, "%h") // Abbreviated month
"Jan"
>>> Ox.formatDate(_date, "%I") // Zero-padded hour (12-hour clock)
"12"
>>> Ox.formatDate(_date, "%j") // Zero-padded day of the year
"002"
>>> Ox.formatDate(_date, "%k") // Space-padded hour (24-hour clock)
" 0"
>>> Ox.formatDate(_date, "%l") // Space-padded hour (12-hour clock)
"12"
>>> Ox.formatDate(_date, "%M") // Zero-padded minute
"03"
>>> Ox.formatDate(_date, "%m") // Zero-padded month
"01"
>>> Ox.formatDate(_date, "%n") // Newline
"\n"
>>> Ox.formatDate(_date, "%p") // AM or PM
"AM"
>>> Ox.formatDate(_date, "%Q") // Quarter of the year
"1"
>>> Ox.formatDate(_date, "%R") // Zero-padded hour and minute
"00:03"
>>> Ox.formatDate(_date, "%r") // US time
"12:03:04 AM"
>>> Ox.formatDate(_date, "%S") // Zero-padded second
"04"
2011-02-22 10:02:28 +00:00
>/> Ox.formatDate(_date, "%s") // Number of seconds since the Epoch
2010-01-07 20:21:07 +00:00
"1104620584"
>>> Ox.formatDate(_date, "%T") // Time
"00:03:04"
>>> Ox.formatDate(_date, "%t") // Tab
"\t"
>>> Ox.formatDate(_date, "%U") // Zero-padded week of the year (00-53, Sunday as first day)
"01"
>>> Ox.formatDate(_date, "%u") // Decimal weekday (1-7, Monday as first day)
"7"
>>> Ox.formatDate(_date, "%V") // Zero-padded ISO-8601 week of the year
"53"
>>> Ox.formatDate(_date, "%v") // Formatted date
" 2-Jan-2005"
>>> Ox.formatDate(_date, "%W") // Zero-padded week of the year (00-53, Monday as first day)
"00"
>>> Ox.formatDate(_date, "%w") // Decimal weekday (0-6, Sunday as first day)
"0"
>>> Ox.formatDate(_date, "%X") // US time
"12:03:04 AM"
>>> Ox.formatDate(_date, "%x") // US date
"01/02/05"
>>> Ox.formatDate(_date, "%Y") // Full year
"2005"
>>> Ox.formatDate(_date, "%y") // Abbreviated year
"05"
2011-02-22 10:02:28 +00:00
>/> Ox.formatDate(_date, "%Z") // Time zone name
2010-01-07 20:21:07 +00:00
"CET"
2011-02-22 10:02:28 +00:00
>/> Ox.formatDate(_date, "%z") // Time zone offset
2010-01-07 20:21:07 +00:00
"+0100"
2011-02-22 10:02:28 +00:00
>/> Ox.formatDate(_date, "%+") // Formatted date and time
2010-01-07 20:21:07 +00:00
"Sun Jan 2 00:03:04 CET 2005"
>>> Ox.formatDate(_date, "%%")
"%"
>>> delete _date
true
>>> Ox.formatDate(new Date("01/01/2000"), "%W")
"00"
>>> Ox.formatDate(new Date("01/02/2000"), "%W")
"00"
>>> Ox.formatDate(new Date("01/03/2000"), "%W")
"01"
*/
2011-04-21 11:50:08 +00:00
/*
var date, fn, format, utc
Array.prototype.slice.call(arguments).forEach(function(arg) {
});
var fn = {}, format;
[
'getFullYear', 'getMonth', 'getDate',
'getHours', 'getMinutes', 'getSeconds', 'toString'
].forEach(function(f) {
});
*/
2011-02-22 10:02:28 +00:00
var format = [
2010-01-07 20:21:07 +00:00
["%", function() {return "%{%}";}],
["c", function() {return "%x %X";}],
["X", function() {return "%r";}],
["x", function() {return "%D";}],
["D", function() {return "%m/%d/%y";}],
["F", function() {return "%Y-%m-%d";}],
["h", function() {return "%b";}],
["R", function() {return "%H:%M";}],
["r", function() {return "%I:%M:%S %p";}],
["T", function() {return "%H:%M:%S";}],
["v", function() {return "%e-%b-%Y";}],
["\\+", function() {return "%a %b %e %H:%M:%S %Z %Y";}],
2011-02-22 10:02:28 +00:00
["A", function(d) {return Ox.WEEKDAYS[(d.getDay() + 6) % 7];}],
2011-04-20 11:29:34 +00:00
["a", function(d) {return Ox.SHORT_WEEKDAYS[(d.getDay() + 6) % 7];}],
2011-02-22 10:02:28 +00:00
["B", function(d) {return Ox.MONTHS[d.getMonth()];}],
2011-04-20 11:29:34 +00:00
["b", function(d) {return Ox.SHORT_MONTHS[d.getMonth()];}],
["C", function(d) {return (d.getFullYear() / 100).toString();}],
2010-01-07 20:21:07 +00:00
["d", function(d) {return Ox.pad(d.getDate(), 2);}],
["e", function(d) {return Ox.pad(d.getDate(), 2, " ");}],
["G", function(d) {return Ox.getISOYear(d);}],
["g", function(d) {return Ox.getISOYear(d).toString().substr(-2);}],
["H", function(d) {return Ox.pad(d.getHours(), 2);}],
["I", function(d) {return Ox.pad((d.getHours() + 11) % 12 + 1, 2);}],
["j", function(d) {return Ox.pad(Ox.getDayOfTheYear(d), 3);}],
["k", function(d) {return Ox.pad(d.getHours(), 2, " ");}],
["l", function(d) {return Ox.pad(((d.getHours() + 11) % 12 + 1), 2, " ");}],
["M", function(d) {return Ox.pad(d.getMinutes(), 2);}],
["m", function(d) {return Ox.pad((d.getMonth() + 1), 2);}],
2011-02-22 10:02:28 +00:00
["p", function(d) {return Ox.AMPM[Math.floor(d.getHours() / 12)];}],
2010-01-07 20:21:07 +00:00
["Q", function(d) {return Math.floor(d.getMonth() / 4) + 1;}],
["S", function(d) {return Ox.pad(d.getSeconds(), 2);}],
["s", function(d) {return Math.floor(d.getTime() / 1000);}],
["U", function(d) {return Ox.pad(Ox.getWeek(d), 2);}],
["u", function(d) {return Ox.getISODay(d);}],
["V", function(d) {return Ox.pad(Ox.getISOWeek(d), 2);}],
["W", function(d) {return Ox.pad(Math.floor((Ox.getDayOfTheYear(d) +
(Ox.getFirstDayOfTheYear(d) || 7) - 2) / 7), 2);}],
["w", function(d) {return d.getDay();}],
["Y", function(d) {return d.getFullYear();}],
["y", function(d) {return d.getFullYear().toString().substr(-2);}],
["Z", function(d) {return d.toString().split("(")[1].replace(")", "");}],
["z", function(d) {return Ox.getTimezoneOffsetString(d);}],
["n", function() {return "\n";}],
["t", function() {return "\t";}],
["\\{%\\}", function() {return "%";}]
];
2011-02-22 10:02:28 +00:00
format.forEach(function(v, i) {
v[0] = new RegExp('%' + v[0] + '', 'g');
2010-01-07 20:21:07 +00:00
});
return function(date, str) {
str = str || date;
date = arguments.length == 2 ? date : new Date();
var split;
if (typeof date == 'string') {
// support YYYY-MM-DD
split = date.substr(0, 10).split('-');
if (split.length == 3) {
date = [split[1], split[2], split[0]].join('/') + date.substr(10);
}
}
if (Ox.isNumber(date) || Ox.isString(date)) {
date = new Date(date);
}
if (Ox.isDate(date) && date.toString() != 'Invalid Date') {
2011-02-22 10:02:28 +00:00
Ox.forEach(format, function(v) {
str = str.replace(v[0], v[1](date));
});
} else {
str = '';
}
2010-01-07 20:21:07 +00:00
return str;
};
}();
2010-07-01 15:06:04 +00:00
Ox.formatDuration = function(sec, dec, format) {
2010-07-01 12:26:32 +00:00
/*
>>> Ox.formatDuration(123456.789, 3)
2011-02-09 17:56:35 +00:00
"1:10:17:36.789"
2010-07-01 12:26:32 +00:00
>>> Ox.formatDuration(12345.6789)
2011-02-09 17:56:35 +00:00
"03:25:46"
2010-07-01 12:26:32 +00:00
>>> Ox.formatDuration(12345.6789, true)
2011-02-09 17:56:35 +00:00
"0:03:25:46"
2010-07-01 12:26:32 +00:00
>>> Ox.formatDuration(3599.999, 3)
2011-02-09 17:56:35 +00:00
"00:59:59.999"
2010-07-01 12:26:32 +00:00
>>> Ox.formatDuration(3599.999)
2011-02-09 17:56:35 +00:00
"01:00:00"
2010-07-01 12:26:32 +00:00
*/
2010-07-01 15:06:04 +00:00
var format = arguments.length == 3 ? format : (Ox.isString(dec) ? dec : "short"),
dec = (arguments.length == 3 || Ox.isNumber(dec)) ? dec : 0,
2010-07-01 12:26:32 +00:00
sec = dec ? sec : Math.round(sec),
2010-07-01 15:06:04 +00:00
val = [
Math.floor(sec / 31536000),
Math.floor(sec % 31536000 / 86400),
Math.floor(sec % 86400 / 3600),
Math.floor(sec % 3600 / 60),
format == "short" ? Ox.formatNumber(sec % 60, dec) : sec % 60
],
str = {
medium: ["y", "d", "h", "m", "s"],
long: ["year", "day", "hour", "minute", "second"]
},
pad = [0, 3, 2, 2, dec ? dec + 3 : 2];
while (!val[0] && val.length > (format == "short" ? 3 : 1)) {
val.shift();
str.medium.shift();
str.long.shift();
pad.shift();
}
while (format != "short" && !val[val.length - 1] && val.length > 1) {
val.pop();
str.medium.pop();
str.long.pop();
}
2011-02-22 10:02:28 +00:00
return Ox.map(val, function(v, i) {
2010-07-01 15:06:04 +00:00
return format == "short" ? Ox.pad(v, pad[i]) :
v + (format == "long" ? " " : "") + str[format][i] +
(format == "long" && v != 1 ? "s" : "");
}).join(format == "short" ? ":" : " ");
2010-07-01 12:26:32 +00:00
};
2010-01-07 20:21:07 +00:00
Ox.formatNumber = function(num, dec) {
/*
>>> Ox.formatNumber(123456789, 3)
"123,456,789.000"
>>> Ox.formatNumber(-2000000 / 3, 3)
"-666,666.667"
>>> Ox.formatNumber(666666.666)
"666,667"
*/
var str = Math.abs(num).toFixed(dec || 0),
spl = str.split('.'),
2010-01-07 20:21:07 +00:00
arr = [];
while (spl[0]) {
arr.unshift(spl[0].substr(-3));
spl[0] = spl[0].substr(0, spl[0].length - 3);
}
spl[0] = arr.join(',');
return (num < 0 ? '-' : '') + spl.join('.');
};
2011-04-20 11:29:34 +00:00
Ox.formatOrdinal = function(num) {
/*
>>> Ox.formatOrdinal(1)
"1st"
>>> Ox.formatOrdinal(2)
"2nd"
>>> Ox.formatOrdinal(3)
"3rd"
>>> Ox.formatOrdinal(4)
"4th"
>>> Ox.formatOrdinal(11)
"11th"
>>> Ox.formatOrdinal(12)
"12th"
>>> Ox.formatOrdinal(13)
"13th"
*/
var str = num.toString(),
end = str[str.length - 1],
ten = str.length > 1 && str[str.length - 2] == '1';
if (end == '1' && !ten) {
str += 'st';
} else if (end == '2' && !ten) {
str += 'nd';
} else if (end == '3' && !ten) {
str += 'rd';
} else {
str += 'th';
}
return str;
};
Ox.formatPercent = function(num, total, dec) {
2011-02-09 17:56:35 +00:00
/*
>>> Ox.formatPercent(1, 1000, 2)
"0.10%"
*/
return Ox.formatNumber(num / total * 100, dec) + '%'
2010-01-07 20:21:07 +00:00
};
2011-02-01 09:56:16 +00:00
Ox.formatResolution = function(arr, str) {
2011-02-09 17:56:35 +00:00
/*
>>> Ox.formatResolution([1920, 1080], 'px')
"1920 x 1080 px"
*/
return arr[0] + ' x ' + arr[1] + (str ? ' ' + str : '');
2011-02-01 09:56:16 +00:00
}
2011-02-09 17:56:35 +00:00
Ox.formatString = function (str, obj) {
/*
>>> Ox.formatString('{0}{1}', ['foo', 'bar'])
"foobar"
>>> Ox.formatString('{a}{b}', {a: 'foo', b: 'bar'})
"foobar"
*/
return str.replace(/\{([^}]+)\}/g, function(str, match) {
return obj[match];
});
}
2011-02-09 17:56:35 +00:00
Ox.formatValue = function(num, str, bin) {
2010-07-01 12:26:32 +00:00
/*
>>> Ox.formatValue(0, "B")
2011-02-09 17:56:35 +00:00
"0 KB"
2010-07-01 12:26:32 +00:00
>>> Ox.formatValue(123456789, "B")
2011-02-09 17:56:35 +00:00
"123.5 MB"
>>> Ox.formatValue(1234567890, "B", true)
"1.15 GiB"
*/
var base = bin ? 1024 : 1000,
len = Ox.PREFIXES.length,
val;
Ox.forEach(Ox.PREFIXES, function(chr, i) {
if (num < Math.pow(base, i + 2) || i == len - 1) {
val = Ox.formatNumber(num / Math.pow(base, i + 1), i) +
' ' + chr + (bin ? 'i' : '') + str;
2010-07-01 12:26:32 +00:00
return false;
}
});
return val;
};
2011-01-24 04:08:19 +00:00
Ox.formatUnit = function(num, str) {
return num + ' ' + str;
};
2010-11-28 15:06:47 +00:00
/*
================================================================================
Geo functions
================================================================================
*/
2011-02-25 10:23:33 +00:00
(function() {
function rad(point) {
return {
lat: Ox.rad(point.lat),
lng: Ox.rad(point.lng)
};
}
Ox.crossesDateline = function(point0, point1) {
return point0.lng > point1.lng;
}
Ox.getArea = function(point0, point1) {
/*
area of a ring between two latitudes:
2 * PI * r^2 * abs(sin(lat0) - sin(lat1))
see http://mathforum.org/library/drmath/view/63767.html
*/
/*
2 * Math.PI *
Math.pow(Ox.EARTH_RADIUS, 2) *
Math.abs(Math.sin(Ox.rad(0)) - Math.sin(Ox.rad(1))) *
Math.abs(Ox.rad(0) - Ox.rad(1)) /
(2 * Math.PI)
*/
if (Ox.crossesDateline(point0, point1)) {
point1.lng += 360;
}
var point0 = rad(point0),
point1 = rad(point1);
return Math.pow(Ox.EARTH_RADIUS, 2) *
Math.abs(Math.sin(point0.lat) - Math.sin(point1.lat)) *
Math.abs(point0.lng - point1.lng);
};
Ox.getBearing = function(point0, point1) {
/*
>>> Ox.getBearing({lat: -45, lng: 0}, {lat: 45, lng: 0})
0
*/
var point0 = rad(point0),
point1 = rad(point1),
x = Math.cos(point0.lat) * Math.sin(point1.lat) -
Math.sin(point0.lat) * Math.cos(point1.lat) *
Math.cos(point1.lng - point0.lng),
y = Math.sin(point1.lng - point0.lng) *
Math.cos(point1.lat);
return (Ox.deg(Math.atan2(y, x)) + 360) % 360;
};
Ox.getCenter = function(point0, point1) {
/*
>>> Ox.values(Ox.getCenter({lat: -45, lng: -90}, {lat: 45, lng: 90}))
[0, 0]
*/
var point0 = rad(point0),
point1 = rad(point1),
x = Math.cos(point1.lat) *
Math.cos(point1.lng - point0.lng),
y = Math.cos(point1.lat) *
Math.sin(point1.lng - point0.lng),
d = Math.sqrt(
Math.pow(Math.cos(point0.lat) + x, 2) + Math.pow(y, 2)
),
lat = Ox.deg(
Math.atan2(Math.sin(point0.lat) + Math.sin(point1.lat), d)
),
lng = Ox.deg(
point0.lng + Math.atan2(y, Math.cos(point0.lat) + x)
);
return {lat: lat, lng: lng};
};
2011-03-04 16:24:54 +00:00
Ox.getDegreesPerMeter = function(lat) {
/***
return degrees per meter at a given latitude
>>> Ox.EARTH_CIRCUMFERENCE == 360 / Ox.getDegreesPerMeter(0)
2011-03-04 16:24:54 +00:00
true
***/
2011-03-04 16:24:54 +00:00
return 360 / Ox.EARTH_CIRCUMFERENCE / Math.cos(lat * Math.PI / 180);
};
2011-02-25 10:23:33 +00:00
Ox.getDistance = function(point0, point1) {
/*
>>> Ox.EARTH_CIRCUMFERENCE == Ox.getDistance({lat: -45, lng: -90}, {lat: 45, lng: 90}) * 2
true
*/
var point0 = rad(point0),
point1 = rad(point1);
return Math.acos(
Math.sin(point0.lat) * Math.sin(point1.lat) +
Math.cos(point0.lat) * Math.cos(point1.lat) *
Math.cos(point1.lng - point0.lng)
) * Ox.EARTH_RADIUS;
};
Ox.getLatLngByXY = function(xy) {
/*
>>> Ox.values(Ox.getLatLngByXY({x: 0.5, y: 0.5}))
[0, 0]
*/
function getVal(val) {
return (val - 0.5) * 2 * Math.PI;
}
return {
lat: -Ox.deg(Math.atan(Ox.sinh(getVal(xy.y)))),
lng: Ox.deg(getVal(xy.x))
}
};
Ox.getMetersPerDegree = function(lat) {
/***
returns meters per degree at a given latitude
2011-02-25 10:23:33 +00:00
>>> Ox.EARTH_CIRCUMFERENCE == Ox.getMetersPerDegree(0) * 360
true
***/
2011-02-25 10:23:33 +00:00
return Math.cos(lat * Math.PI / 180) * Ox.EARTH_CIRCUMFERENCE / 360;
};
Ox.getXYByLatLng = function(latlng) {
/*
>>> Ox.values(Ox.getXYByLatLng({lat: 0, lng: 0}))
[0.5, 0.5]
*/
function getVal(val) {
return (val / (2 * Math.PI) + 0.5)
}
return {
x: getVal(Ox.rad(latlng.lng)),
y: getVal(Ox.asinh(Math.tan(Ox.rad(-latlng.lat))))
};
};
}());
2011-02-22 10:02:28 +00:00
Ox.Line = function(point0, point1) {
var self = {
points: [point0, point1]
},
that = this;
function rad() {
return self.points.map(function(point) {
return {
lat: Ox.rad(point.lat()),
lng: Ox.rad(point.lng())
};
});
}
that.getArea = function() {
};
that.getBearing = function() {
};
that.getDistance = function() {
var points = rad();
return Math.acos(
Math.sin(point[0].lat) * Math.sin(point[1].lat) +
Math.cos(point[0].lat) * Math.cos(point[1].lat) *
Math.cos(point[1].lng - point[0].lng)
) * Ox.EARTH_RADIUS;
};
that.getMidpoint = function() {
var points = rad(),
x = Math.cos(point[1].lat) *
Math.cos(point[1].lng - point[0].lng),
y = Math.cos(point[1].lat) *
Math.sin(point[1].lng - point[0].lng),
d = Math.sqrt(
Math.pow(Math.cos(point[0].lat) + x, 2) + Math.pow(y, 2)
),
lat = Ox.deg(
Math.atan2(Math.sin(points[0].lat) + Math.sin(points[1].lat), d)
),
lng = Ox.deg(
points[0].lng + Math.atan2(y, math.cos(points[0].lat) + x)
);
return new Point(lat, lng);
};
that.points = function() {
};
return that;
2010-11-28 15:06:47 +00:00
};
2011-02-22 10:02:28 +00:00
Ox.Point = function(lat, lng) {
var self = {lat: lat, lng: lng},
that = this;
that.lat = function() {
};
that.latlng = function() {
};
that.lng = function() {
};
that.getMetersPerDegree = function() {
return Math.cos(self.lat * Math.PI / 180) *
Ox.EARTH_CIRCUMFERENCE / 360;
}
that.getXY = function() {
return [
getXY(Ox.rad(self.lng)),
getXY(Ox.asinh(Math.tan(Ox.rad(-self.lat))))
];
};
return that;
2010-11-28 15:06:47 +00:00
};
2011-02-22 10:02:28 +00:00
Ox.Rectangle = function(point0, point1) {
var self = {
points: [
new Point(
Math.min(point0.lat(), point1.lat()),
point0.lng()
),
new Point(
Math.max(point0.lat(), point1.lat()),
point1.lng()
)
]
},
that = this;
that.contains = function(rectangle) {
}
that.crossesDateline = function() {
return self.points[0].lng > self.points[1].lng;
}
that.getCenter = function() {
return new Ox.Line(self.points[0], self.points[1]).getMidpoint();
};
that.intersects = function(rectangle) {
};
return that;
2010-11-28 15:06:47 +00:00
};
2011-02-22 10:02:28 +00:00
2011-02-09 17:56:35 +00:00
/*
================================================================================
HTML functions
================================================================================
*/
Ox.parseEmailAddresses = function(html) {
return html.replace(
/\b([0-9A-Z\.\+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6})\b/gi,
'<a href="mailto:$1" title="mailto:$1">$1</a>'
);
};
Ox.parseHTML = (function() {
/*
>>> Ox.parseHTML('http://foo.com, bar')
'<a href="http://foo.com" title="http://foo.com">foo.com</a>, bar'
>>> Ox.parseHTML('(see: www.foo.com)')
'(see: <a href="http://www.foo.com" title="http://www.foo.com">www.foo.com</a>)'
>>> Ox.parseHTML('foo@bar.com')
'<a href="mailto:foo@bar.com" title="mailto:foo@bar.com">foo@bar.com</a>'
>>> Ox.parseHTML('<a href="http://foo.com" onmouseover="alert()">foo</a>')
'<a href="http://foo.com" title="http://foo.com">foo</a>'
>>> Ox.parseHTML('<a href="javascript:alert()">foo</a>')
'&lt;a href=&quot;javascript:alert()&quot;&gt;foo&lt;/a&gt;'
>>> Ox.parseHTML('[http://foo.com foo]')
'<a href="http://foo.com" title="http://foo.com">foo</a>'
>>> Ox.parseHTML('<rtl>foo</rtl>')
'<div style="direction: rtl">foo</div>'
>>> Ox.parseHTML('<script>alert()</script>')
'&lt;script&gt;alert()&lt;/script&gt;'
*/
var defaultTags = [
'a', 'b', 'blockquote', 'cite', 'code', 'del',
'em', 'i', 'img', 'ins', 'li', 'ol', 'q', 'rtl',
's', 'strong', 'sub', 'sup', 'ul', '[]'
],
parse = {
a: {
'<a [^<>]*?href="(https?:\/\/.+?)".*?>': '<a href="{1}" title="{1}">',
'<\/a>': '</a>'
},
img: {
'<img [^<>]*?src="(https?:\/\/.+?)".*?>': '<img src="{1}">'
},
rtl: {
'<rtl>': '<div style="direction: rtl">',
'<\/rtl>': '</div>'
},
'*': function(tag) {
var ret = {};
ret['<(/?' + tag + ')>'] = '<{1}>';
return ret;
}
},
tab = '\t';
return function(html, tags, wikilinks) {
var matches = [],
tags = tags || defaultTags;
html = Ox.clean(html);
if (tags.indexOf('[]') > -1) {
html = html.replace(/\[(https?:\/\/.+?) (.+?)\]/gi, '<a href="$1">$2</a>');
tags = tags.filter(function(tag) {
return tag != '[]';
});
}
tags.forEach(function(tag) {
var p = parse[tag] || parse['*'](tag);
Ox.forEach(p, function(replace, regexp) {
html = html.replace(new RegExp(regexp, 'gi'), function() {
matches.push(Ox.formatString(replace, arguments));
return tab + (matches.length - 1) + tab;
});
});
});
html = Ox.encodeHTML(html);
html = Ox.parseURLs(html);
html = Ox.parseEmailAddresses(html);
matches.forEach(function(match, i) {
html = html.replace(new RegExp(tab + i + tab, 'gi'), match);
});
html = html.replace(/\n/g, '<br/>\n')
// close extra opening (and remove extra closing) tags
2011-02-22 10:02:28 +00:00
// return $('<div>').html(html).html();
// fixme: this converts '&quot;' to '"'
return Ox.element('div').html(html).html();
2011-02-09 17:56:35 +00:00
}
}());
Ox.parseURL = (function() {
// fixme: leak memory, like now, or create every time? ... benchmark??
var a = document.createElement('a'),
keys = ['hash', 'host', 'hostname', 'origin',
'pathname', 'port', 'protocol', 'search'];
return function(str) {
var ret = {};
a.href = str;
keys.forEach(function(key) {
ret[key] = a[key];
});
return ret;
};
}());
Ox.parseURLs = function(html) {
return html.replace(
/\b((https?:\/\/|www\.).+?)([\.,:;!\?\)\]]*?(\s|$))/gi,
function(str, url, pre, end) {
url = (pre == 'www.' ? 'http://' : '' ) + url;
return Ox.formatString(
'<a href="{url}" title="{url}">{host}</a>{end}',
{
end: end,
host: Ox.parseURL(url).hostname,
url: url
}
);
}
);
};
2010-01-07 20:21:07 +00:00
/*
================================================================================
Math functions
================================================================================
*/
Ox.asinh = function(x) {
/*
fixme: no test
*/
return Math.log(x + Math.sqrt(x * x + 1));
};
Ox.deg = function(rad) {
/*
>>> Ox.deg(2 * Math.PI)
360
*/
return rad * 180 / Math.PI;
};
2010-09-03 08:47:40 +00:00
Ox.divideInt = function(num, by) {
/*
>>> Ox.divideInt(100, 3)
[33, 33, 34]
>>> Ox.divideInt(100, 6)
[16, 16, 17, 17, 17, 17]
*/
var arr = [],
div = parseInt(num / by),
mod = num % by,
i;
for (i = 0; i < by; i++) {
arr[i] = div + (i > by - 1 - mod);
}
return arr;
}
2010-01-07 20:21:07 +00:00
Ox.limit = function(num, min, max) {
/*
>>> Ox.limit(1, 2, 3)
2
>>> Ox.limit(2, 1)
1
*/
var len = arguments.length;
max = arguments[len - 1];
min = len == 3 ? min : 0;
return Math.min(Math.max(num, min), max);
};
2011-03-05 02:08:30 +00:00
Ox.log = function(num, base) {
2010-01-07 20:21:07 +00:00
/*
>>> Ox.log(100, 10)
2
>>> Ox.log(Math.E)
1
*/
2011-03-05 02:08:30 +00:00
return Math.log(num) / Math.log(base || Math.E);
2010-01-07 20:21:07 +00:00
};
2011-04-20 11:29:34 +00:00
Ox.mod = function(num, by) {
/*
>>> Ox.mod(11, 10)
1
>>> Ox.mod(-11, 10)
9
*/
var mod = num % by;
return mod >= 0 ? mod : mod + by;
};
2010-01-07 20:21:07 +00:00
Ox.rad = function(deg) {
/*
>>> Ox.rad(360)
2 * Math.PI
*/
return deg * Math.PI / 180;
};
Ox.random = function() {
/*
>>> Ox.random(3) in {0: 0, 1: 0, 2: 0, 3: 0}
true
>>> Ox.random(1, 2) in {1: 0, 2: 0}
true
*/
var len = arguments.length,
min = len == 1 ? 0 : arguments[0],
max = arguments[len - 1];
return min + parseInt(Math.random() * (max - min + 1));
};
Ox.round = function(num, dec) {
/*
>>> Ox.round(2 / 3, 6)
0.666667
>>> Ox.round(1 / 2, 3)
0.5
*/
var pow = Math.pow(10, dec || 0);
return Math.round(num * pow) / pow;
};
Ox.sinh = function(x) {
/*
fixme: no test
*/
return (Math.exp(x) - Math.exp(-x)) / 2;
};
2011-03-04 19:07:41 +00:00
Ox.MAX_LATITUDE = Ox.deg(Math.atan(Ox.sinh(Math.PI)));
Ox.MIN_LATITUDE = -Ox.MAX_LATITUDE;
2011-01-15 06:09:22 +00:00
/*
================================================================================
RegExp functions
================================================================================
*/
Ox.regexp = {
'accents': '¨´`ˆ˜',
'letters': 'a-z¨´`ˆ˜äåáàâãæçëéèèñïíìîöóòôõøœßúùûÿ'
};
2010-01-07 20:21:07 +00:00
/*
================================================================================
String functions
================================================================================
*/
Ox.basename = function(str) {
/*
fixme: this should go into Path functions
>>> Ox.basename("foo/bar/foo.bar")
"foo.bar"
>>> Ox.basename("foo.bar")
"foo.bar"
*/
2011-02-09 17:56:35 +00:00
return str.replace(/^.*[\/\\]/g, '');
2010-01-07 20:21:07 +00:00
};
Ox.char = String.fromCharCode;
Ox.clean = function(str) {
/*
>>> Ox.clean("foo bar")
"foo bar"
>>> Ox.clean(" foo bar ")
"foo bar"
2011-02-09 17:56:35 +00:00
>>> Ox.clean(" foo \n bar ")
"foo\nbar"
2010-01-07 20:21:07 +00:00
*/
2011-02-09 17:56:35 +00:00
return Ox.map(str.split('\n'), function(str) {
return Ox.trim(str.replace(/\s+/g, ' '));
}).join('\n');
2010-01-07 20:21:07 +00:00
};
2010-09-03 08:47:40 +00:00
Ox.contains = function(str, chr) {
/*
>>> Ox.contains("foo", "bar")
false
>>> Ox.contains("foobar", "bar")
true
*/
return str.indexOf(chr) > -1;
};
2010-01-07 20:21:07 +00:00
Ox.endsWith = function(str, sub) {
/*
>>> Ox.endsWith("foobar", "bar")
true
*/
2011-03-03 21:02:35 +00:00
return str.toString().substr(-sub.length) === sub;
2010-01-07 20:21:07 +00:00
};
2010-09-17 22:10:07 +00:00
Ox.highlight = function(txt, str) {
// fixme: move to ox.ui
return str ? txt.replace(
new RegExp('(' + str + ')', 'ig'),
'<span class="OxHighlight">$1</span>'
) : txt;
2010-12-26 20:16:35 +00:00
};
Ox.isValidEmail = function(str) {
2011-02-09 17:56:35 +00:00
/*
>>> Ox.isValidEmail("foo@bar.com")
true
>>> Ox.isValidEmail("foo.bar@foobar.co.uk")
true
>>> Ox.isValidEmail("foo@bar")
false
*/
2010-12-26 20:16:35 +00:00
return !!/^[0-9A-Z\.\+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6}$/i(str);
2010-09-17 22:10:07 +00:00
}
2010-01-07 20:21:07 +00:00
Ox.pad = function(str, len, pad, pos) {
/*
>>> Ox.pad(1, 2)
"01"
>>> Ox.pad("abc", 6, ".", "right")
"abc..."
>>> Ox.pad("foobar", 3, ".", "right")
"foo"
>>> Ox.pad("abc", 6, "123456", "right")
"abc123"
>>> Ox.pad("abc", 6, "123456", "left")
"456abc"
*/
str = str.toString().substr(0, len);
pad = Ox.repeat(pad || "0", len - str.length);
pos = pos || "left";
str = pos == "left" ? pad + str : str + pad;
2011-02-09 17:56:35 +00:00
str = pos == "left" ?
str.substr(str.length - len, str.length) :
2010-01-07 20:21:07 +00:00
str.substr(0, len);
return str;
};
Ox.repeat = function(str, num) {
/*
>>> Ox.repeat(1, 3)
"111"
>>> Ox.repeat("foo", 3)
"foofoofoo"
*/
2011-01-23 04:59:16 +00:00
return num >= 1 ? new Array(num + 1).join(str.toString()) : '';
2010-01-07 20:21:07 +00:00
};
Ox.reverse = function(str) {
/*
Ox.reverse("foo")
oof
*/
2011-03-03 21:02:35 +00:00
return str.toString().split('').reverse().join('');
2010-01-07 20:21:07 +00:00
};
Ox.startsWith = function(str, sub) {
/*
>>> Ox.startsWith("foobar", "foo")
true
*/
2011-03-03 21:02:35 +00:00
return str.toString().substr(0, sub.length) === sub;
2010-01-07 20:21:07 +00:00
};
2010-02-18 17:45:37 +00:00
Ox.stripTags = function(str) {
/*
>>> Ox.stripTags("f<span>o</span>o")
2011-02-09 17:56:35 +00:00
"foo"
2010-02-18 17:45:37 +00:00
*/
2011-02-09 17:56:35 +00:00
return str.replace(/(<.*?>)/gi, '');
2010-02-18 17:45:37 +00:00
};
Ox.substr = function(str, start, stop) {
/***
Ox.substr behaves like str[start:stop] in Python
(or like str.substring() with negative values for stop)
not implemented
>>> Ox.substr('foobar', 1)
"oobar"
>>> Ox.substr('foobar', -1)
"r"
>>> Ox.substr('foobar', 1, 3)
"oo"
>>> Ox.substr('foobar', -3, 5)
"ba"
>>> Ox.substr('foobar', 1, -2)
"oob"
>>> Ox.substr('foobar', -4, -1)
"oba"
***/
stop = Ox.isUndefined(stop) ? str.length : stop;
return str.substring(
start < 0 ? str.length + start : start,
stop < 0 ? str.length + stop : stop
);
};
2010-01-07 20:21:07 +00:00
Ox.toCamelCase = function(str) {
/*
>>> Ox.toCamelCase("foo-bar-baz")
"fooBarBaz"
>>> Ox.toCamelCase("foo/bar/baz")
"fooBarBaz"
>>> Ox.toCamelCase("foo_bar_baz")
"fooBarBaz"
*/
return str.replace(/[\-_\/][a-z]/g, function(str) {
return str[1].toUpperCase();
});
};
Ox.toDashes = function(str) {
/*
>>> Ox.toDashes("fooBarBaz")
"foo-bar-baz"
*/
return str.replace(/[A-Z]/g, function(str) {
return '-' + str.toLowerCase();
});
};
Ox.toSlashes = function(str) {
/*
>>> Ox.toSlashes("fooBarBaz")
"foo/bar/baz"
*/
return str.replace(/[A-Z]/g, function(str) {
return '/' + str.toLowerCase();
});
};
Ox.toTitleCase = function(str) {
/*
>>> Ox.toTitleCase("foo")
"Foo"
>>> Ox.toTitleCase("Apple releases iPhone, IBM stock plummets")
"Apple Releases iPhone, IBM Stock Plummets"
*/
2011-02-22 10:02:28 +00:00
return Ox.map(str.split(' '), function(v) {
2010-01-07 20:21:07 +00:00
var sub = v.substr(1),
low = sub.toLowerCase();
if (sub == low) {
v = v.substr(0, 1).toUpperCase() + low;
}
return v;
}).join(" ");
};
Ox.toUnderscores = function(str) {
/*
>>> Ox.toUnderscores("fooBarBaz")
"foo_bar_baz"
*/
return str.replace(/[A-Z]/g, function(str) {
return '_' + str.toLowerCase();
});
};
Ox.trim = function(str) { // is in jQuery
/*
Ox.trim(" foo ")
"foo"
*/
return str.replace(/^\s+|\s+$/g, "");
2010-07-05 07:09:34 +00:00
};
Ox.truncate = function(str, len, pad, pos) {
/*
>>> Ox.truncate("anticonstitutionellement", 16, "...", "center")
2011-02-09 17:56:35 +00:00
"anticon...lement"
2010-07-05 07:09:34 +00:00
*/
var pad = pad || {},
pos = pos || "right",
strlen = str.length,
padlen = pad.length,
left, right;
if (strlen > len) {
if (pos == "left") {
str = pad + str.substr(padlen + strlen - len);
} else if (pos == "center") {
left = Math.ceil((len - padlen) / 2);
right = Math.floor((len - padlen) / 2);
str = str.substr(0, left) + pad + str.substr(-right);
} else if (pos == "right") {
str = str.substr(0, len - padlen) + pad;
}
}
return str;
};
2011-02-09 17:56:35 +00:00
Ox.words = function(str) {
/*
>>> Ox.words('He\'s referring to the "ill-conceived" AOL/TimeWarner merger--didn\'t you know?')
2011-02-22 10:02:28 +00:00
['he\'s', 'referring', 'to' , 'the' , 'ill-conceived' , 'aol', 'timewarner' , 'merger' , 'didn\'t', 'you', 'know']
2011-02-09 17:56:35 +00:00
*/
var arr = str.toLowerCase().split(/\b/),
chr = "-'",
len = arr.length,
startsWithWord = !!/\w/(arr[0]);
arr.forEach(function(v, i) {
// find single occurrences of chars in chr
// that are not at the beginning or end of str
// and join the surrounding words with them
if (
i > 0 && i < len - 1 &&
v.length == 1 && chr.indexOf(v) > -1
) {
arr[i + 1] = arr[i - 1] + arr[i] + arr[i + 1];
arr[i - 1] = arr[i] = '';
}
});
// remove elements that have been emptied above
arr = arr.filter(function(v) {
return v.length;
});
// return words, not spaces or punctuation
return arr.filter(function(v, i) {
return i % 2 == !startsWithWord;
});
}
2010-07-05 07:09:34 +00:00
Ox.wordwrap = function(str, len, sep, bal, spa) {
/*
2011-02-09 17:56:35 +00:00
>>> Ox.wordwrap("Anticonstitutionellement, Paris s'eveille", 25, "<br/>")
"Anticonstitutionellement, <br/>Paris s'eveille"
2010-07-05 07:09:34 +00:00
>>> Ox.wordwrap("Anticonstitutionellement, Paris s'eveille", 16, "<br/>")
2011-02-09 17:56:35 +00:00
"Anticonstitution<br/>ellement, Paris <br/>s'eveille"
2010-07-05 07:09:34 +00:00
>>> Ox.wordwrap("These are short words", 16, "<br/>", true)
2011-02-09 17:56:35 +00:00
"These are <br/>short words"
2010-07-05 07:09:34 +00:00
*/
2010-09-11 08:59:40 +00:00
var str = str === null ? '' : str.toString(),
2010-09-08 16:35:34 +00:00
len = len || 80,
2010-07-05 07:09:34 +00:00
sep = sep || "<br/>",
bal = bal || false,
2010-09-06 23:44:37 +00:00
spa = Ox.isUndefined(spa) ? true : spa,
2010-07-05 07:09:34 +00:00
words = str.split(" "),
lines;
if (bal) {
// balance lines: test if same number of lines
// can be achieved with a shorter line length
lines = Ox.wordwrap(str, len, sep, false).split(sep);
if (lines.length > 1) {
// test shorter line, unless
// that means cutting a word
2011-02-22 10:02:28 +00:00
var max = Ox.max(Ox.map(words, function(word) {
2010-07-05 07:09:34 +00:00
return word.length;
}));
while (len > max) {
len--;
if (Ox.wordwrap(str, len, sep, false).split(sep).length > lines.length) {
len++;
break;
}
}
}
}
lines = [""];
2011-02-22 10:02:28 +00:00
Ox.forEach(words, function(word) {
2010-07-05 07:09:34 +00:00
if ((lines[lines.length - 1] + word + " ").length <= len + 1) {
// word fits in current line
lines[lines.length - 1] += word + " ";
} else {
if (word.length <= len) {
// word fits in next line
lines.push(word + " ");
} else {
// word is longer than line
var chr = len - lines[lines.length - 1].length;
lines[lines.length - 1] += word.substr(0, chr);
for (var pos = chr; pos < word.length; pos += len) {
lines.push(word.substr(pos, len));
}
lines[lines.length - 1] += " ";
}
}
});
if (!spa) {
2011-02-22 10:02:28 +00:00
lines = Ox.map(lines, function(line) {
2010-07-05 07:09:34 +00:00
return Ox.trim(line);
});
}
return Ox.trim(lines.join(sep));
};
2010-01-07 20:21:07 +00:00
/*
================================================================================
Type functions
================================================================================
*/
2011-02-22 10:02:28 +00:00
Ox.isArguments = function(val) {
/*
>>> Ox.isArguments((function() { return arguments; }()))
true
*/
return !!(val && val.toString() == '[object Arguments]');
}
2010-01-07 20:21:07 +00:00
Ox.isArray = function(val) { // is in jQuery
/*
>>> Ox.isArray([])
true
*/
return val instanceof Array;
}
Ox.isBoolean = function(val) {
/*
>>> Ox.isBoolean(false)
true
*/
2010-12-31 11:01:35 +00:00
return typeof val == 'boolean';
2010-01-07 20:21:07 +00:00
};
Ox.isDate = function(val) {
/*
>>> Ox.isDate(new Date())
true
*/
return val instanceof Date;
};
2011-01-23 04:59:16 +00:00
Ox.isElement = function(val) {
2011-02-22 10:02:28 +00:00
/*
>>> Ox.isElement(document.createElement())
true
*/
2011-01-23 04:59:16 +00:00
return !!(val && val.nodeType == 1);
2011-02-22 10:02:28 +00:00
};
2011-01-23 04:59:16 +00:00
2010-01-07 20:21:07 +00:00
Ox.isFunction = function(val) { // is in jQuery
/*
>>> Ox.isFunction(function() {})
true
>>> Ox.isFunction(/ /)
false
*/
2010-12-31 11:01:35 +00:00
return typeof val == 'function' && !Ox.isRegExp(val);
2010-01-07 20:21:07 +00:00
};
2011-02-22 10:02:28 +00:00
Ox.isInfinity = function(val) {
/*
>>> Ox.isInfinity(Infinity)
true
>>> Ox.isInfinity(-Infinity)
true
>>> Ox.isInfinity(NaN)
false
*/
return typeof val == 'number' && !isFinite(val) && !Ox.isNaN(val);
};
2011-01-23 04:59:16 +00:00
Ox.isNaN = function(val) {
/*
>>> Ox.isNaN(NaN)
true
*/
return val !== val;
}
2010-01-07 20:21:07 +00:00
Ox.isNull = function(val) {
/*
>>> Ox.isNull(null)
true
*/
return val === null;
};
Ox.isNumber = function(val) {
/*
>>> Ox.isNumber(0)
true
>>> Ox.isNumber(Infinity)
false
2011-02-22 10:02:28 +00:00
>>> Ox.isNumber(-Infinity)
false
2010-01-07 20:21:07 +00:00
>>> Ox.isNumber(NaN)
false
*/
2010-12-31 11:01:35 +00:00
return typeof val == 'number' && isFinite(val);
2010-01-07 20:21:07 +00:00
};
Ox.isObject = function(val) {
/*
>>> Ox.isObject({})
true
>>> Ox.isObject([])
false
>>> Ox.isObject(new Date())
false
>>> Ox.isObject(null)
false
*/
2011-02-22 10:02:28 +00:00
return typeof val == 'object' && !Ox.isArguments(val) &&
!Ox.isArray(val) && !Ox.isDate(val) && !Ox.isNull(val);
2010-01-07 20:21:07 +00:00
};
Ox.isRegExp = function(val) {
/*
>>> Ox.isRegExp(/ /)
true
*/
return val instanceof RegExp;
};
Ox.isString = function(val) {
/*
2010-12-31 11:01:35 +00:00
>>> Ox.isString('')
2010-01-07 20:21:07 +00:00
true
*/
2010-12-31 11:01:35 +00:00
return typeof val == 'string';
2010-01-07 20:21:07 +00:00
};
Ox.isUndefined = function(val) {
2011-04-22 22:03:10 +00:00
// fixme: void 0 is also nice
2010-01-07 20:21:07 +00:00
/*
>>> Ox.isUndefined()
true
*/
2010-12-31 11:01:35 +00:00
return typeof val == 'undefined';
2010-11-25 11:47:42 +00:00
};
2011-02-22 10:02:28 +00:00
Ox.typeOf = function(val) {
/*
>>> (function() { return Ox.typeOf(arguments); }())
'arguments'
>>> Ox.typeOf([])
'array'
>>> Ox.typeOf(false)
'boolean'
>>> Ox.typeOf(new Date())
'date'
>>> Ox.typeOf(document.createElement())
'element'
>>> Ox.typeOf(function() {})
'function'
>>> Ox.typeOf(Infinity)
'infinity'
>>> Ox.typeOf(NaN)
'nan'
>>> Ox.typeOf(null)
'null'
>>> Ox.typeOf(0)
'number'
>>> Ox.typeOf({})
'object'
>>> Ox.typeOf(/ /)
'regexp'
>>> Ox.typeOf('')
'string'
>>> Ox.typeOf()
'undefined'
*/
var ret;
Ox.forEach(Ox.TYPES, function(type) {
if (Ox['is' + type](val)) {
ret = type.toLowerCase();
return false;
}
});
return ret;
};