oxjs/source/Ox/js/Core.js

360 lines
11 KiB
JavaScript
Raw 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';
/*
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
2012-05-25 07:34:50 +00:00
array array
canFoo boolean
callback callback function
2012-05-25 07:34:50 +00:00
collection collection (array, string or object)
date date
2012-05-25 07:34:50 +00:00
iterator iterator function
hasFoo boolean
i index (integer key)
2012-05-25 07:34:50 +00:00
index index (integer key)
isFoo boolean
k key (of a key/value pair)
key key (of a key/value pair)
max maximum value
min minimum value
2012-05-25 07:34:50 +00:00
number number
object object
regexp regexp
ret return value
2012-05-25 07:52:57 +00:00
string string
2012-05-25 07:34:50 +00:00
test test function
v value (of a key/value pair)
2012-05-25 07:34:50 +00:00
value 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;
*/
// 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
(function(global) {
/*@
Ox <f> The <code>Ox</code> object
See <code>Ox.wrap</code> for details.
(value) -> <o> wrapped value
value <*> Any value
@*/
2012-05-25 07:34:50 +00:00
global.Ox = function(value) {
return Ox.wrap(value);
};
})(this);
/*@
Ox.Break <f> Breaks from <code>Ox.forEach</code> and <code>Ox.loop</code>
@*/
Ox.Break = function() {
throw Ox.BreakError;
};
2012-05-25 07:34:50 +00:00
Ox.BreakError = new SyntaxError('Illegal Ox.Break() statement');
/*@
Ox.load <f> Loads a module
2012-03-30 01:41:47 +00:00
A module named "Foo" provides <code>Ox.Foo/Ox.Foo.js</code>, in which it
defines one method, <code>Ox.load.Foo</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
2012-03-30 01:41:47 +00:00
define <code>Ox.Foo</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);
Ox.forEach(modules, function(options, module) {
2012-05-25 07:34:50 +00:00
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
/*@
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
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 = {};
}
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
});
2011-12-30 15:06:55 +00:00
ret = this;
}
return ret;
};
// IE 8 doesn't like `storage.delete`
storage['delete'] = function() {
var keys = arguments.length == 0 ? Object.keys(storage())
: Ox.toArray(arguments)
2011-12-30 15:06:55 +00:00
keys.forEach(function(key) {
delete localStorage[namespace + '.' + key];
});
var keys =
keys
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() {
var args = Ox.toArray(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)
);
2011-11-04 15:52:47 +00:00
window.console && window.console.log.apply(window.console, args);
ret = args.join(' ');
}
return ret;
};
return that;
}());
2012-01-07 07:20:02 +00:00
/*@
Ox.loop <f> For-loop, functional-style
Returning <code>false</code> from the iterator function acts like a
<code>break</code> statement. Unlike a <code>for</code> loop,
<code>Ox.loop</code> 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 <code>for (var i = 0; i < stop; i++) { fn(i); }</code>
(start, stop, fn) -> <n> Next value
equivalent to <code>for (var i = start; i < stop; i++) { fn(i); }</code>
or, if <code>start</code> is larger than <code>stop</code>,
<code>for (var i = start; i > stop; i--) { fn(i); }</code>
(start, stop, step, fn) -> <n> Next value
equivalent to <code>for (var i = start; i < stop; i += step) { fn(i);
}</code> or, if <code>step</code> is negative, <code>for (var i = start;
i > stop; i += step) { fn(i); }</code>
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-05-25 07:34:50 +00:00
> Ox.loop(10, function(i) { i == 4 && Ox.Break() })
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;
try {
for (i = start; step > 0 ? i < stop : i > stop; i += step) {
2012-05-25 07:34:50 +00:00
// iterator(i);
if (iterator(i) === false) {
console.warn('Returning false in Ox.loop is deprecated.');
break;
}
}
2012-05-25 07:34:50 +00:00
} catch (error) {
if (error !== Ox.BreakError) {
throw error;
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').split(' ').pop()
"foo"
@*/
Ox.print = function() {
var args = Ox.toArray(arguments),
date = new Date();
args.unshift(
2012-05-25 07:34:50 +00:00
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().slice(-3)
);
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"
@*/
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;
};