2011-10-07 01:04:47 +00:00
|
|
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
|
|
|
|
2012-01-04 09:50:07 +00:00
|
|
|
// OxJS (c) 2012 0x2620, dual-licensed GPL/MIT, see http://oxjs.org for details
|
2011-10-07 01:04:47 +00:00
|
|
|
|
2011-11-05 16:46:53 +00:00
|
|
|
'use strict';
|
|
|
|
|
2011-10-07 01:04:47 +00:00
|
|
|
/*
|
|
|
|
Some conventions:
|
|
|
|
Functions
|
|
|
|
- only one var statement, in the first line of the function
|
|
|
|
- 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
|
|
|
|
fn function
|
|
|
|
hasFoo boolean
|
|
|
|
i index (integer key)
|
|
|
|
isFoo boolean
|
|
|
|
k key (of a key/value pair)
|
|
|
|
key key (of a key/value pair)
|
|
|
|
max maximum value
|
|
|
|
min minumum value
|
|
|
|
num number
|
|
|
|
obj object
|
|
|
|
re regexp
|
|
|
|
ret return value
|
|
|
|
v value (of a key/value pair)
|
|
|
|
val value (of a key/value pair)
|
|
|
|
Indentation
|
|
|
|
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;
|
|
|
|
*/
|
|
|
|
|
2011-11-30 14:56:01 +00:00
|
|
|
// FIXME: add memoize
|
|
|
|
|
2011-10-07 01:04:47 +00:00
|
|
|
// 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
|
|
|
|
|
|
|
|
/*@
|
|
|
|
Ox <f> The <code>Ox</code> object
|
|
|
|
See <code>Ox.wrap</code> for details.
|
|
|
|
(value) -> <o> wrapped value
|
|
|
|
value <*> Any value
|
|
|
|
@*/
|
|
|
|
|
2011-11-05 17:37:56 +00:00
|
|
|
window.Ox = function(val) {
|
2011-10-07 01:04:47 +00:00
|
|
|
return Ox.wrap(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, ...}
|
|
|
|
options <o> Module options
|
|
|
|
callback <f> Callback function
|
|
|
|
success <b> If true, the module has been loaded successfully
|
|
|
|
@*/
|
|
|
|
|
|
|
|
Ox.load = function() {
|
|
|
|
var callback = arguments[arguments.length - 1],
|
|
|
|
counter = 0,
|
|
|
|
isObject = Ox.isObject(arguments[0]),
|
|
|
|
length,
|
|
|
|
modules = isObject ? arguments[0] : {},
|
|
|
|
success = 0;
|
|
|
|
if (!isObject) {
|
|
|
|
modules[arguments[0]] = Ox.isObject(arguments[1]) ? arguments[1] : {};
|
|
|
|
}
|
2011-11-01 14:45:47 +00:00
|
|
|
length = Ox.len(modules);
|
2011-10-07 01:04:47 +00:00
|
|
|
Ox.forEach(modules, function(options, module) {
|
|
|
|
Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() {
|
|
|
|
Ox.load[module](options, function(s) {
|
|
|
|
success += s;
|
|
|
|
++counter == length && callback(success == counter);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2011-12-30 15:06:55 +00:00
|
|
|
/*@
|
|
|
|
Ox.localStorage <o> localStorage wrapper
|
2011-12-30 15:37:41 +00:00
|
|
|
() -> <o> key:value pairs
|
|
|
|
(key) -> <*> value
|
|
|
|
(key, val) -> <f> localStorage object
|
|
|
|
({key, val}) -> <f> localStorage object
|
2011-12-30 15:06:55 +00:00
|
|
|
@*/
|
|
|
|
Ox.localStorage = function(namespace) {
|
2012-01-02 08:25:34 +00:00
|
|
|
if (!window.localStorage) {
|
|
|
|
window.localStorage = {};
|
|
|
|
}
|
2011-12-30 15:06:55 +00:00
|
|
|
function storage(key, val) {
|
2011-12-30 20:41:54 +00:00
|
|
|
var args, ret, value;
|
2011-12-30 15:37:41 +00:00
|
|
|
if (arguments.length == 0) {
|
2011-12-30 15:06:55 +00:00
|
|
|
ret = {};
|
|
|
|
Ox.forEach(localStorage, function(val, key) {
|
|
|
|
if (Ox.startsWith(key, namespace + '.')) {
|
|
|
|
ret[key.substr(namespace.length + 1)] = JSON.parse(val);
|
|
|
|
}
|
|
|
|
});
|
2011-12-30 15:51:33 +00:00
|
|
|
} else if (arguments.length == 1 && typeof key == 'string') {
|
|
|
|
// This gets called by Ox.Log before Type.js has loaded
|
2011-12-30 15:06:55 +00:00
|
|
|
value = localStorage[namespace + '.' + key];
|
2011-12-30 15:51:33 +00:00
|
|
|
ret = value === void 0 ? void 0 : JSON.parse(value);
|
2011-12-30 15:06:55 +00:00
|
|
|
} else {
|
2011-12-30 15:37:41 +00:00
|
|
|
args = Ox.makeObject(arguments);
|
|
|
|
Ox.forEach(args, function(val, key) {
|
|
|
|
localStorage[namespace + '.' + key] = JSON.stringify(val);
|
|
|
|
});
|
2011-12-30 15:06:55 +00:00
|
|
|
ret = this;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
storage.delete = function(key) {
|
2011-12-30 15:37:41 +00:00
|
|
|
var keys = arguments.length == 0
|
2011-12-30 15:06:55 +00:00
|
|
|
? Object.keys(storage())
|
|
|
|
: [key];
|
|
|
|
keys.forEach(function(key) {
|
|
|
|
delete localStorage[namespace + '.' + key];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
return storage;
|
|
|
|
};
|
|
|
|
|
2011-11-04 18:11:45 +00:00
|
|
|
/*@
|
|
|
|
Ox.Log <f> Logging module
|
|
|
|
@*/
|
2011-10-23 14:26:37 +00:00
|
|
|
Ox.Log = (function() {
|
2011-12-30 15:06:55 +00:00
|
|
|
var storage = Ox.localStorage('Ox'),
|
2011-12-30 15:51:33 +00:00
|
|
|
log = storage('log') || {filter: [], filterEnabled: true},
|
2011-10-23 14:26:37 +00:00
|
|
|
that = function() {
|
2011-11-04 16:13:56 +00:00
|
|
|
var ret;
|
|
|
|
if (arguments.length == 0) {
|
2011-11-04 16:34:24 +00:00
|
|
|
ret = log;
|
2011-11-04 16:13:56 +00:00
|
|
|
} else {
|
|
|
|
ret = that.log.apply(null, arguments);
|
|
|
|
}
|
|
|
|
return ret;
|
2011-10-23 14:26:37 +00:00
|
|
|
};
|
2011-11-04 15:52:47 +00:00
|
|
|
that.filter = function(val) {
|
2011-11-04 16:34:24 +00:00
|
|
|
if (!Ox.isUndefined(val)) {
|
2011-11-04 18:11:45 +00:00
|
|
|
that.filter.enable();
|
2011-11-04 16:04:46 +00:00
|
|
|
log.filter = Ox.toArray(val);
|
2011-12-30 15:51:33 +00:00
|
|
|
storage('log', log);
|
2011-11-04 15:52:47 +00:00
|
|
|
}
|
2011-11-04 16:34:24 +00:00
|
|
|
return log.filter;
|
2011-11-04 15:52:47 +00:00
|
|
|
};
|
|
|
|
that.filter.add = function(val) {
|
2011-11-04 16:04:46 +00:00
|
|
|
return that.filter(Ox.unique(Ox.merge(log.filter, Ox.toArray(val))));
|
2011-11-04 15:52:47 +00:00
|
|
|
};
|
2011-11-04 16:34:24 +00:00
|
|
|
that.filter.disable = function() {
|
|
|
|
log.filterEnabled = false;
|
2011-12-30 15:51:33 +00:00
|
|
|
storage('log', log);
|
2011-11-04 16:34:24 +00:00
|
|
|
};
|
|
|
|
that.filter.enable = function() {
|
|
|
|
log.filterEnabled = true;
|
2011-12-30 15:51:33 +00:00
|
|
|
storage('log', log);
|
2011-11-04 16:34:24 +00:00
|
|
|
};
|
2011-11-04 15:52:47 +00:00
|
|
|
that.filter.remove = function(val) {
|
|
|
|
val = Ox.toArray(val);
|
2011-11-04 16:04:46 +00:00
|
|
|
return that.filter(log.filter.filter(function(v) {
|
2011-11-04 15:52:47 +00:00
|
|
|
return val.indexOf(v) == -1;
|
|
|
|
}));
|
2011-10-23 14:26:37 +00:00
|
|
|
};
|
2011-11-04 16:34:24 +00:00
|
|
|
that.log = function() {
|
2011-11-04 15:52:47 +00:00
|
|
|
var args = Ox.makeArray(arguments), date, ret;
|
2011-11-04 16:34:24 +00:00
|
|
|
if (!log.filterEnabled || log.filter.indexOf(args[0]) > -1) {
|
2011-11-04 15:52:47 +00:00
|
|
|
date = new Date();
|
2011-10-29 12:32:55 +00:00
|
|
|
args.unshift(
|
2011-12-30 15:06:55 +00:00
|
|
|
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().substr(-3)
|
2011-10-29 12:32:55 +00:00
|
|
|
);
|
2011-11-04 15:52:47 +00:00
|
|
|
window.console && window.console.log.apply(window.console, args);
|
|
|
|
ret = args.join(' ');
|
2011-10-23 14:26:37 +00:00
|
|
|
}
|
2011-10-29 12:32:55 +00:00
|
|
|
return ret;
|
2011-10-23 14:26:37 +00:00
|
|
|
};
|
|
|
|
return that;
|
|
|
|
}());
|
|
|
|
|
2011-10-07 01:04:47 +00:00
|
|
|
/*@
|
|
|
|
Ox.print <f> Prints its arguments to the console
|
|
|
|
(arg, ...) -> <s> String
|
|
|
|
The string contains the timestamp, the name of the caller function, and
|
|
|
|
any arguments, separated by spaces
|
|
|
|
arg <*> any value
|
|
|
|
> Ox.print('foo').split(' ').pop()
|
|
|
|
"foo"
|
|
|
|
@*/
|
|
|
|
|
|
|
|
Ox.print = function() {
|
|
|
|
var args = Ox.makeArray(arguments),
|
|
|
|
date = new Date();
|
|
|
|
args.unshift(
|
2011-11-05 16:46:53 +00:00
|
|
|
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().substr(-3)/*,
|
2011-10-07 01:04:47 +00:00
|
|
|
(arguments.callee.caller && arguments.callee.caller.name)
|
2011-11-05 16:46:53 +00:00
|
|
|
|| '(anonymous)'*/
|
2011-10-07 01:04:47 +00:00
|
|
|
);
|
|
|
|
window.console && window.console.log.apply(window.console, args);
|
|
|
|
return args.join(' ');
|
|
|
|
};
|
|
|
|
|
|
|
|
/*@
|
|
|
|
Ox.uid <f> Returns a unique id
|
|
|
|
() -> <n> Unique id
|
|
|
|
> Ox.uid() != Ox.uid()
|
|
|
|
true
|
|
|
|
@*/
|
|
|
|
|
|
|
|
Ox.uid = (function() {
|
|
|
|
var uid = 0;
|
|
|
|
return function() {
|
|
|
|
return uid++;
|
|
|
|
};
|
|
|
|
}());
|
|
|
|
|
|
|
|
/*@
|
|
|
|
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 return values to allow chaining
|
|
|
|
value <f> Unwrap the value wrapped by <code>chain()</chain>
|
|
|
|
value <*> Any value
|
|
|
|
> Ox("foobar").repeat(2)
|
|
|
|
"foobarfoobar"
|
|
|
|
> Ox("foobar").chain().reverse().toTitleCase().value()
|
|
|
|
"Raboof"
|
|
|
|
> Ox.wrap("foobar").value()
|
|
|
|
"foobar"
|
|
|
|
@*/
|
|
|
|
|
|
|
|
Ox.wrap = function(val, chained) {
|
|
|
|
// somewhat inspired by underscore.js
|
|
|
|
var wrapper = {
|
|
|
|
chain: function() {
|
|
|
|
wrapper.chained = true;
|
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
chained: chained || false,
|
|
|
|
value: function() {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Object.getOwnPropertyNames(Ox).forEach(function(name) {
|
2011-11-05 16:46:53 +00:00
|
|
|
if (
|
|
|
|
['arguments', 'callee', 'caller', 'length'].indexOf(name) == -1
|
|
|
|
&& name[0] == name[0].toLowerCase()
|
|
|
|
&& Ox.isFunction(Ox[name])
|
|
|
|
) {
|
2011-10-07 01:04:47 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return wrapper;
|
|
|
|
};
|