add some more documentation, pass some more tests
This commit is contained in:
parent
c6d67420a8
commit
f2dbfbd1e5
9 changed files with 589 additions and 494 deletions
240
source/Ox.js
240
source/Ox.js
|
|
@ -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>')
|
||||
'<a href="javascript:alert()">foo</a>'
|
||||
'<a href="javascript:alert()">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>')
|
||||
'<script>alert()</script>'
|
||||
'<script>alert()</script>'
|
||||
> Ox.parseHTML('\'foo\' < \'bar\' && "foo" > "bar"')
|
||||
'\'foo\' < \'bar\' && "foo" > "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 '"' to '"'
|
||||
// note: this converts '"' 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);
|
||||
};
|
||||
|
||||
/*@
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue