oxjs/source/Ox/js/Core.js

344 lines
12 KiB
JavaScript
Raw Permalink Normal View History

// OxJS (c) 2012 0x2620, dual-licensed GPL/MIT, see http://oxjs.org for details
2011-11-05 16:46:53 +00:00
'use strict';
2012-06-26 01:14:35 +00:00
/*@
Ox <f> The `Ox` object
See `Ox.wrap` for details.
(value) -> <o> wrapped value
value <*> Any value
@*/
this.Ox = function(value) {
return Ox.wrap(value);
};
/*@
Ox.load <f> Loads OxJS and, optionally, one or more modules
2014-09-26 16:57:12 +00:00
To load OxJS, include `/min/Ox.js` (minified version) or `/dev/Ox.js`
(development version), and use `Ox.load(callback)` at the beginning of your
program. The callback will run once OxJS is loaded and the document is
ready. To choose the version programatically (for example: minified version
on production server, development version on localhost), include the
development version and pass `true` as a first parameter to `Ox.load` in
case you want to switch to the minified version. To load one or more
modules, either at the beginning or at a later point, use<br>
<br>
<ul><li>`Ox.load(module, callback)` (one module),</li>
<li>`Ox.load(module, options, callback)` (one module with options),</li>
<li>`Ox.load(['moduleA', 'moduleB', ...], callback)` (multiple modules)</li>
<li>`Ox.load({moduleA: optionsA, moduleB: optionsB, ...}, callback)` (multiple
modules with options) or either</li>
<li>`Ox.load(['moduleA', {moduleB: optionsB}, ...], callback)` or
`Ox.load({moduleA: {}, moduleB: optionsB, ...}, callback)` (multiple modules
without and with options).</li></ul>
<br>
A module named 'Foo' provides `Foo/Foo.js`, in which it defines one method,
`Ox.load.Foo`, that takes two arguments, `options` and `callback`, and calls
`callback` with one argument, `true` for success or `false` if an error
occurred. A third-party module should define `Ox.Foo` and attach its own
methods there.
([min ,]callback) -> <u> undefined
([min ,]module, callback) -> <u> undefined
([min ,]module, options, callback) -> <u> undefined
([min ,]modules, callback) -> <u> undefined
min <b> If true, switch from development version to minified version
module <s> Module name
options <o> Module options
modules <a|o> Multiple modules
`['moduleA', 'moduleB']` (without options) or
`{moduleA: optionsA, moduleB: optionsB, ...}` (with options) or either
`['moduleA', {moduleB: optionsB}, ...]` or
`{moduleA: {}, moduleB: optionsB, ...}` (without and with options)
callback <f> Callback function
success <b> If true, all modules have been loaded successfully
@*/
Ox.load = function() {
var callback = arguments[arguments.length - 1],
length, loaded = 0, localeFiles = [], modules = {}, succeeded = 0,
type = Ox.typeOf(arguments[0]);
if (type == 'string') {
modules = Ox.extend(
{}, arguments[0], Ox.isObject(arguments[1]) ? arguments[1] : {}
);
} else if (type == 'array') {
arguments[0].forEach(function(value) {
if (Ox.isString(value)) {
modules[value] = {};
} else {
Ox.extend(modules, value);
}
});
} else if (type == 'object') {
modules = arguments[0];
}
2011-11-01 14:45:47 +00:00
length = Ox.len(modules);
Ox.documentReady(function() {
if (!length) {
callback(true);
} else {
Ox.forEach(modules, function(options, module) {
Ox.getFile(
Ox.PATH + module + '/' + module + '.js?' + Ox.VERSION,
function() {
Ox.load[module](options, function(success) {
succeeded += success;
if (++loaded == length) {
Ox.setLocale(Ox.LOCALE, function() {
callback(succeeded == length);
});
}
});
}
);
});
}
});
};
2011-12-30 15:06:55 +00:00
/*@
2012-05-29 22:28:52 +00:00
Ox.localStorage <f> localStorage wrapper
(namespace) -> storage <f> localStorage function for a given namespace
2012-04-09 08:42:00 +00:00
() -> <o> returns all key:value pairs
(key) -> <*> returns one value
(key, val) -> <f> sets one value, returns localStorage object
({key: val, ...}) -> <f> sets values, returns localStorage object
<script>
Ox.test.localStorage = Ox.localStorage('test');
</script>
> Ox.test.localStorage({foo: 'bar'})('foo')
'bar'
> Ox.test.localStorage.delete('foo')()
{}
2011-12-30 15:06:55 +00:00
@*/
Ox.localStorage = function(namespace) {
var localStorage;
2012-07-05 17:48:20 +00:00
try {
// this will fail if third party cookies/storage is not allowed
localStorage = window.localStorage || {};
// FF 3.6 can't assign to or iterate over localStorage
2014-08-19 08:19:59 +00:00
for (var key in localStorage) {}
// In Safari (OS X or iOS) is in private browsing mode,
// it appears as though localStorage is available,
// but trying to call .setItem throws an exception.
2016-02-22 09:26:35 +00:00
localStorage.setItem('OxJS.test', '');
2016-02-22 09:19:18 +00:00
localStorage.removeItem('OxJS.test');
2012-07-05 17:48:20 +00:00
} catch (e) {
console.log('localStorage disabled');
2012-07-05 17:48:20 +00:00
localStorage = {};
2012-01-02 08:25:34 +00:00
}
2012-05-25 07:34:50 +00:00
function storage(key, value) {
var ret;
2011-12-30 15:37:41 +00:00
if (arguments.length == 0) {
2011-12-30 15:06:55 +00:00
ret = {};
2012-05-25 07:34:50 +00:00
Ox.forEach(localStorage, function(value, key) {
2011-12-30 15:06:55 +00:00
if (Ox.startsWith(key, namespace + '.')) {
2012-05-25 07:34:50 +00:00
ret[key.slice(namespace.length + 1)] = JSON.parse(value);
2011-12-30 15:06:55 +00:00
}
});
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 {
Ox.forEach(Ox.makeObject(arguments), function(value, key) {
2012-05-25 07:34:50 +00:00
localStorage[namespace + '.' + key] = JSON.stringify(value);
2011-12-30 15:37:41 +00:00
});
ret = storage;
2011-12-30 15:06:55 +00:00
}
return ret;
2014-08-19 08:19:59 +00:00
}
// IE 8 doesn't like `storage.delete`
storage['delete'] = function() {
var keys = arguments.length == 0 ? Object.keys(storage())
2014-08-19 08:19:59 +00:00
: Ox.slice(arguments);
2011-12-30 15:06:55 +00:00
keys.forEach(function(key) {
delete localStorage[namespace + '.' + key];
});
2012-04-09 08:42:00 +00:00
return storage;
2011-12-30 15:06:55 +00:00
};
return storage;
};
2011-11-04 18:11:45 +00:00
/*@
Ox.Log <f> Logging module
@*/
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},
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;
};
2012-05-25 07:34:50 +00:00
that.filter = function(value) {
if (!Ox.isUndefined(value)) {
2011-11-04 18:11:45 +00:00
that.filter.enable();
2012-05-25 07:34:50 +00:00
log.filter = Ox.makeArray(value);
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
};
2012-05-25 07:34:50 +00:00
that.filter.add = function(value) {
return that.filter(Ox.unique(log.filter.concat(Ox.makeArray(value))));
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
};
2012-05-25 07:34:50 +00:00
that.filter.remove = function(value) {
value = Ox.makeArray(value);
2011-11-04 16:04:46 +00:00
return that.filter(log.filter.filter(function(v) {
2012-05-25 07:34:50 +00:00
return value.indexOf(v) == -1;
2011-11-04 15:52:47 +00:00
}));
};
2011-11-04 16:34:24 +00:00
that.log = function() {
2013-12-01 13:57:52 +00:00
var args = Ox.slice(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();
args.unshift(
2012-05-24 09:47:33 +00:00
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().slice(-3)
);
window.console && window.console.log && window.console.log.apply(window.console, args);
2011-11-04 15:52:47 +00:00
ret = args.join(' ');
}
return ret;
};
return that;
}());
2012-01-07 07:20:02 +00:00
/*@
Ox.loop <f> For-loop, functional-style
Returning `false` from the iterator function acts like a `break` statement.
Unlike a `for` loop, `Ox.loop` doesn't leak its counter variable to the
outer scope, but returns it.
2012-04-13 21:19:52 +00:00
(stop, fn) -> <n> Next value
equivalent to `for (var i = 0; i < stop; i++) { fn(i); }`
2012-04-13 21:19:52 +00:00
(start, stop, fn) -> <n> Next value
equivalent to `for (var i = start; i < stop; i++) { fn(i); }` or, if
`start` is larger than `stop`, `for (var i = start; i > stop; i--) {
fn(i); }`
2012-04-13 21:19:52 +00:00
(start, stop, step, fn) -> <n> Next value
equivalent to `for (var i = start; i < stop; i += step) { fn(i); }` or,
if `step` is negative, `for (var i = start; i > stop; i += step) {
fn(i); }`
2012-01-07 07:20:02 +00:00
start <n> Start value
stop <n> Stop value (exclusive)
step <n> Step value
2012-04-13 21:19:52 +00:00
fn <f> Iterator function
2012-01-07 07:20:02 +00:00
i <n> Counter value
2012-07-05 08:58:08 +00:00
> Ox.loop(10, function(i) { if (i == 4) { return false; } })
2012-01-07 07:20:02 +00:00
4
> Ox.loop(0, 3, 2, function() {})
4
@*/
Ox.loop = function() {
2012-05-25 07:34:50 +00:00
var length = arguments.length,
start = length > 2 ? arguments[0] : 0,
stop = arguments[length > 2 ? 1 : 0],
step = length == 4 ? arguments[2] : (start <= stop ? 1 : -1),
iterator = arguments[length - 1],
2012-01-07 07:20:02 +00:00
i;
2012-07-05 08:58:08 +00:00
for (i = start; step > 0 ? i < stop : i > stop; i += step) {
if (iterator(i) === false) {
break;
2012-04-08 12:19:34 +00:00
}
2012-01-07 07:20:02 +00:00
}
return i;
};
/*@
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', 'bar').split(' ').slice(1).join(' ')
'foo bar'
@*/
Ox.print = function() {
var args = Ox.slice(arguments), date = new Date();
args.unshift(
date.toString().split(' ')[4] + '.' + (+date).toString().slice(-3)
);
window.console && window.console.log.apply(window.console, args);
return args.join(' ');
};
/*@
Ox.trace <f> Prints its arguments to the console, followed by a stack trace
(arg, ...) -> <s> String
@*/
Ox.trace = function() {
var args = Ox.slice(arguments);
try {
2014-08-19 08:19:59 +00:00
throw new Error();
} catch (e) {
if (e.stack) {
args.push(
'\n' + Ox.clean(e.stack.split('\n').slice(2).join('\n'))
);
}
}
return Ox.print.apply(null, args);
};
/*@
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
`Ox(value)` is a shorthand for `Ox.wrap(value)`.
(value) -> <o> wrapped value
chain <f> Wrap return values to allow chaining
value <f> Unwrap the value wrapped by `chain()`
value <*> Any value
2012-07-05 17:48:20 +00:00
> Ox('foobar').repeat(2)
'foobarfoobar'
> Ox('foobar').chain().reverse().toTitleCase().value()
'Raboof'
> Ox.wrap('foobar').value()
'foobar'
@*/
2012-05-25 07:34:50 +00:00
Ox.wrap = function(value, chained) {
// somewhat inspired by underscore.js
var wrapper = {
chain: function() {
wrapper.chained = true;
return wrapper;
},
chained: chained || false,
value: function() {
2012-05-25 07:34:50 +00:00
return value;
}
};
2012-05-25 17:46:27 +00:00
Ox.methods(Ox).filter(function(method) {
return method[0] == method[0].toLowerCase();
}).forEach(function(method) {
wrapper[method] = function() {
var args = Array.prototype.slice.call(arguments), ret;
args.unshift(value);
ret = Ox[method].apply(Ox, args);
return wrapper.chained ? Ox.wrap(ret, true) : ret;
};
});
return wrapper;
};