add some more documentation, pass some more tests

This commit is contained in:
rlx 2011-10-01 02:21:55 +00:00
commit f2dbfbd1e5
9 changed files with 589 additions and 494 deletions

View file

@ -6,11 +6,12 @@
Some conventions:
Functions
- only one var statement, in the first line of the function
- return only once, from the last line of the function body
- return only once, from the last line of the function
Variable names
arg argument
args arguments
arr array
canFoo boolean
callback callback function
col collection (array, string or object)
date date
@ -29,18 +30,48 @@ Some conventions:
v value (of a key/value pair)
val value (of a key/value pair)
Indentation
var a = 1,
b = 2,
c = 3;
Obj.fn1()
.fn2()
.fn3();
Variable definitions
var a = {
key: value,
key: value,
key: value
},
b = {key: value},
c = {key: value};
Method chaining
Obj.fnA({
key: value,
key: value,
key: value
})
.fnB({key: val})
.fnC({key: val});
Simple conditionals
condition && expression;
Conditionals
if (condition) {
expression;
}
Conditionals with long conditions
if (
condition
&& condition
&& condition
) {
expression;
}
Ternary operator
condition ? expression : expression;
Ternary operator with long conditions or expressions
condition ? expression
: condition ? expression
: expression;
*/
// todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
// also see https://github.com/tlrobinson/narwhal/blob/master/lib/util.js
// Fallbacks -------------------------------------------------------------------
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
@ -176,11 +207,17 @@ Ox = function(val) {
/*@
Ox.load <f> Loads a module
A module named "Test" provides <code>Ox.Test/Ox.Test.js</code>, in which it
defines one method, <code>Ox.load.Test</code>, that takes two arguments,
<code>options</code> and <code>callback</code>, and calls
<code>callback</code> with one argument, <code>true</code> for success or
<code>false</code> if an error occurred. Generally, the module should
define <code>Ox.Test</code> and attach its own methods there.
(module, callback) -> <u> undefined
(module, options, callback) -> <u> undefined
(modules, callback) -> <u> undefined
module <s> Module name
modules <o> Multiple modules {name: options}
modules <o> Multiple modules {name: options, ...}
options <o> Module options
callback <f> Callback function
success <b> If true, the module has been loaded successfully
@ -235,6 +272,7 @@ Ox.uid <f> Returns a unique id
> Ox.uid() != Ox.uid()
true
@*/
Ox.uid = (function() {
var uid = 0;
return function() {
@ -244,13 +282,14 @@ Ox.uid = (function() {
/*@
Ox.wrap <f> Wraps a value so that one can directly call any Ox function on it
<code>Ox(value)</code> is a shorthand for <code>Ox.wrap(value)</code>.
(value) -> <o> wrapped value
chain <f> wrap the return value to allow chaining
value <f> unwrap the value wrapped by <code>chain()</chain>
chain <f> Wrap return values to allow chaining
value <f> Unwrap the value wrapped by <code>chain()</chain>
value <*> Any value
> Ox.wrap("foobar").repeat(2)
> Ox("foobar").repeat(2)
"foobarfoobar"
> Ox.wrap("foobar").chain().reverse().toTitleCase().value()
> Ox("foobar").chain().reverse().toTitleCase().value()
"Raboof"
> Ox.wrap("foobar").value()
"foobar"
@ -425,32 +464,6 @@ Ox.avg = function(obj) {
return Ox.sum(obj) / Ox.len(obj);
};
/*@
Ox.clone <f> Returns a (shallow or deep) copy of an object or array
> (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'
> Ox.clone(0)
0
@*/
Ox.clone = function(col, deep) {
// fixme: is there any use case for shallow copy?
var ret = Ox.isArray(col) ? [] : {};
if (deep) {
Ox.forEach(col, function(val, key) {
ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1
? Ox.clone(val, true) : val;
});
} else {
ret = Ox.isArray(col) ? col.slice()
: Ox.isObject(col) ? Ox.extend({}, col)
: col;
}
return ret;
};
/*@
Ox.contains <f> Tests if a collection contains a value
> Ox.contains(['foo', 'bar'], 'foo')
@ -470,6 +483,33 @@ Ox.contains = function(col, val) {
return (Ox.isObject(col) ? Ox.values(col) : col).indexOf(val) > -1;
};
/*@
Ox.copy <f> Returns a (shallow or deep) copy of an object or array
> (function() { a = ['v']; b = Ox.copy(a); a[0] = null; return b[0]; }())
'v'
> (function() { a = {k: 'v'}; b = Ox.copy(a); a.k = null; return b.k; }())
'v'
> Ox.clone(0)
0
@*/
Ox.copy = Ox.clone = function(col, deep) {
// fixme: remove references to Ox.clone
// fixme: is there any use case for shallow copy?
var ret = Ox.isArray(col) ? [] : {};
if (deep) {
Ox.forEach(col, function(val, key) {
ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1
? Ox.clone(val, true) : val;
});
} else {
ret = Ox.isArray(col) ? col.slice()
: Ox.isObject(col) ? Ox.extend({}, col)
: col;
}
return ret;
};
/*@
Ox.count <f> Counts the occurences of values in a collection
> Ox.count(['f', 'o', 'o'])
@ -600,7 +640,7 @@ Ox.getObjectById <f> Returns an array element with a given id
> Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
{id: "foo", title: "Foo"}
@*/
// fixme: shouldn't this be getElementById() ?
// fixme: should this be getElementById() ?
Ox.getObjectById = function(arr, id) {
var ret = null;
Ox.forEach(arr, function(v) {
@ -617,7 +657,7 @@ Ox.getPositionById <f> Returns the index of an array element with a given id
> Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
0
@*/
// fixme: shouldn't this be getIndexById() ?
// fixme: this should be getIndexById()
Ox.getPositionById = function(arr, id) {
var ret = -1;
Ox.forEach(arr, function(v, i) {
@ -656,7 +696,9 @@ Ox.getset <f> Generic getter and setter function
<script>
Ox.test.object = new function() {
var options = {},
setOption = function(key, value) {},
setOption = function(key, value) {
// handle added or modified options
},
that = this;
that.options = function() {
return Ox.getset(options, arguments, setOption, that);
@ -673,13 +715,13 @@ Ox.getset <f> Generic getter and setter function
Ox.getset = function(obj, args, callback, context) {
var obj_ = Ox.clone(obj), ret;
if (args.length == 0) {
// getset([])
// []
ret = obj_;
} else if (args.length == 1 && !Ox.isObject(args[0])) {
// getset([key])
// [key]
ret = Ox.clone(obj[args[0]]);
} else {
// getset([key, val]) or getset([{key: val, ...}])
// [key, val] or [{key: val, ...}]
args = Ox.makeObject(args);
obj = Ox.extend(obj, args);
Ox.forEach(args, function(val, key) {
@ -710,7 +752,6 @@ Ox.isEmpty <f> Returns true if a collection is empty
false
@*/
Ox.isEmpty = function(val) {
// fixme: what about deep isEmpty?
return Ox.len(val) == 0;
};
@ -783,8 +824,7 @@ Ox.len <f> Returns the length of an array, function, object or string
@*/
Ox.len = function(col) {
var type = Ox.typeOf(col);
return ['array', 'function', 'string'].indexOf(type) > -1
? col.length
return ['array', 'function', 'string'].indexOf(type) > -1 ? col.length
: type == 'object' ? Ox.values(col).length : void 0;
};
@ -809,10 +849,10 @@ Ox.loop <f> For-loop, functional-style
step <n> Step value
callback <f> Iterator function
i <n> Counter value
> Ox.loop(0, 3, 2, function() {})
4
> Ox.loop(10, function(i) { return i != 4; })
4
> Ox.loop(0, 3, 2, function() {})
4
@*/
Ox.loop = function() {
var len = arguments.length,
@ -842,21 +882,23 @@ Ox.makeArray <f> Takes an array-like object and returns a true array
["f", "o", "o"]
@*/
Ox.makeArray = /MSIE/.test(navigator.userAgent) ? function(col) {
var i, len, ret = [];
try {
ret = Array.prototype.slice.call(col);
} catch(e) {
// handle MSIE NodeLists
len = col.length;
for (i = 0; i < len; i++) {
ret[i] = col[i];
Ox.makeArray = /MSIE/.test(navigator.userAgent)
? function(col) {
var i, len, ret = [];
try {
ret = Array.prototype.slice.call(col);
} catch(e) {
// handle MSIE NodeLists
len = col.length;
for (i = 0; i < len; i++) {
ret[i] = col[i];
}
}
}
return ret;
} : function(col) {
return Array.prototype.slice.call(col);
}
return ret;
}
: function(col) {
return Array.prototype.slice.call(col);
};
/*@
Ox.makeObject <f> Takes an array and returns an object
@ -867,7 +909,7 @@ Ox.makeObject <f> Takes an array and returns an object
> (function() { return Ox.makeObject(arguments); }('foo', 1))
{foo: 1}
> (function() { return Ox.makeObject(arguments); }('foo'))
{}
{foo: void 0}
> (function() { return Ox.makeObject(arguments); }())
{}
@*/
@ -902,7 +944,7 @@ Ox.map <f> Transforms the values of an array, object or string
@*/
Ox.map = function(obj, fn) {
// fixme: return null to filter out is a bit esoteric
// fixme: return null to filter out may be a bit esoteric
var isObject = Ox.isObject(obj),
ret = isObject ? {} : [];
Ox.forEach(obj, function(val, key) {
@ -3300,16 +3342,24 @@ Ox.parseHTML <f> Takes HTML from an untrusted source and returns something sane
> 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;'
'&lt;a href="javascript:alert()"&gt;foo'
> 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;'
'&lt;script&gt;alert()&lt;/script&gt;'
> Ox.parseHTML('\'foo\' < \'bar\' && "foo" > "bar"')
'\'foo\' &lt; \'bar\' &amp;&amp; "foo" &gt; "bar"'
> Ox.parseHTML('<b>foo')
'<b>foo</b>'
> Ox.parseHTML('<b>foo</b></b>')
'<b>foo</b>'
@*/
Ox.parseHTML = (function() {
var defaultTags = [
// fixme: why not p?
'a', 'b', 'blockquote', 'cite', 'code',
'del', 'em', 'i', 'img', 'ins',
'li', 'ol', 'q', 'rtl', 's',
@ -3357,13 +3407,13 @@ Ox.parseHTML = (function() {
html = Ox.encodeHTML(html);
html = Ox.parseURLs(html);
html = Ox.parseEmailAddresses(html);
Ox.print(html, 'matches', matches);
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
// return $('<div>').html(html).html();
// fixme: this converts '&quot;' to '"'
// note: this converts '&quot;' to '"'
return Ox.element('<div>').html(html).html();
}
@ -3952,7 +4002,7 @@ Ox.tokenize = (function() {
'global', 'ignoreCase', 'lastIndex', 'multiline', 'source',
// Window
'applicationCache',
'closed', 'content', 'crypto',
'closed', 'console', 'content', 'crypto',
'defaultStatus', 'document',
'frameElement', 'frames',
'history',
@ -3966,7 +4016,6 @@ Ox.tokenize = (function() {
'self', 'sessionStorage', 'status', 'statusbar',
'toolbar', 'top'
]
// Window stuff? 'atob', 'btoa', 'console', 'document' ...
};
return function(source) {
@ -4269,10 +4318,10 @@ Ox.extend = function() {
Ox.serialize <f> Parses an object into query parameters
> Ox.serialize({a: 1, b: 2, c: 3})
'a=1&b=2&c=3'
> Ox.serialize({a: 1, b: 2.3, c: [4, 5]})
'a=1&b=2.3&c=4,5'
> Ox.serialize({a: -1, b: 2.3, c: [4, 5]})
'a=-1&b=2.3&c=4,5'
> Ox.serialize({string: 'foo', empty: {}, null: null, undefined: void 0})
'string=bar'
'string=foo'
@*/
Ox.serialize = function(obj) {
var arr = [];
@ -4288,8 +4337,8 @@ Ox.serialize = function(obj) {
Ox.unserialize <f> Parses query parameters into an object
> Ox.unserialize('a=1&b=2&c=3')
{a: '1', b: '2', c: '3'}
> Ox.unserialize('a=1&b=2.3&c=4,5', true)
{a: 1, b: 2.3, c: [4, 5]}
> Ox.unserialize('a=-1&b=2.3&c=4,5', true)
{a: -1, b: 2.3, c: [4, 5]}
@*/
Ox.unserialize = function(str, toNumber) {
var obj = {};
@ -4774,8 +4823,8 @@ Ox.truncate = function(str, len, pad, pos) {
Ox.words <f> Splits a string into words, removing punctuation
(string) -> <[s]> Array of words
string <s> Any string
> Ox.words('Let\'s "walk" a tree-like key/value store--okay?')
["let's", "walk", "a", "tree-like", "key", "value", "store", "okay"]
> Ox.words('Let\'s "split" array-likes into key/value pairs--okay?')
["let's", "split", "array-likes", "into", "key", "value", "pairs", "okay"]
@*/
Ox.words = function(str) {
var arr = str.toLowerCase().split(/\b/),
@ -4783,12 +4832,11 @@ Ox.words = function(str) {
len = arr.length,
startsWithWord = /\w/.test(arr[0]);
arr.forEach(function(v, i) {
// find single occurrences of "-" or "-"
// that are not at the beginning or end of the string
// and join the surrounding words with them
// find single occurrences of "-" or "'" that are not at the beginning
// or end of the string, and join the surrounding words with them
if (
i > 0 && i < len - 1 &&
v.length == 1 && chr.indexOf(v) > -1
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] = '';
@ -4986,18 +5034,17 @@ Ox.isEqual = function(a, b) {
isEqual = true;
} else if (type == 'date') {
isEqual = a.getTime() == b.getTime();
/* toString doesn't do it
} else if (['element', 'function'].indexOf(type) > -1) {
} else if (type == 'element') {
isEqual = a.isEqualNode(b);
} else if (type == 'function') {
// fixme: this doesn't do it
isEqual = a.toString() == b.toString();
*/
} else if (type == 'regexp') {
isEqual = a.global == b.global &&
a.ignore == b.ignore &&
a.multiline == b.multiline &&
a.source == b.source;
isEqual = a.global == b.global && a.ignore == b.ignore
&& a.multiline == b.multiline && a.source == b.source;
} else if (
['arguments', 'array', 'object'].indexOf(type) > -1 &&
Ox.len(a) == Ox.len(b)
['arguments', 'array', 'object'].indexOf(type) > -1
&& Ox.len(a) == Ox.len(b)
) {
isEqual = true;
Ox.forEach(a, function(v, k) {
@ -5018,7 +5065,7 @@ Ox.isFunction <f> Tests if a value is a function
> Ox.isFunction(/ /)
false
@*/
Ox.isFunction = function(val) { // is in jQuery
Ox.isFunction = function(val) {
return typeof val == 'function' && !Ox.isRegExp(val);
};
@ -5090,11 +5137,14 @@ Ox.isObject <f> Tests if a value is a an object
false
> Ox.isObject(null)
false
> Ox.isObject(/ /)
false
@*/
Ox.isObject = function(val) {
return typeof val == 'object' && !Ox.isArguments(val)
&& !Ox.isArray(val) && !Ox.isDate(val) && !Ox.isNull(val);
&& !Ox.isArray(val) && !Ox.isDate(val)
&& !Ox.isNull(val) && !Ox.isRegExp(val);
};
/*@