hig
Version:
HiG server
190 lines (157 loc) • 4.56 kB
JavaScript
var HTML_CHARS = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`'
};
/**
Iterates over all items in _obj_ if _obj_ is an array, or over all enumerable
properties if _obj_ is an object, calling the _callback_ for each one.
@method each
@param {Array|Object} obj Array or object to iterate over.
@param {callback}
@param {mixed} value Value of the current array item or property.
@param {Number|String} key Index (if _obj_ is an array) or key (if _obj_ is an
object).
**/
function each(obj, callback) {
if (Array.isArray(obj)) {
obj.forEach(callback);
} else {
Object.keys(obj).forEach(function (key) {
callback(obj[key], key);
});
}
}
exports.each = each;
/**
Escapes HTML characters in _html_.
@method escapeHTML
@param {String} html String to escape.
@return {String} Escaped string.
**/
function escapeHTML(html) {
return html.replace(/[&<>"'\/`]/g, function (match) {
return HTML_CHARS[match];
});
}
exports.escapeHTML = escapeHTML;
/**
Returns a new object containing a deep merge of the enumerable properties of all
passed objects. Properties in later arguments take precedence over properties
with the same name in earlier arguments. Object values are deep-cloned. Array
values are _not_ deep-cloned.
@method merge
@param {object} obj* One or more objects to merge.
@return {object} New object with merged values from all other objects.
**/
function merge() {
var args = Array.prototype.slice.call(arguments),
target = {};
args.unshift(target);
mix.apply(this, args);
return target;
}
exports.merge = merge;
/**
Like `merge()`, but augments the first passed object with a deep merge of the
enumerable properties of all other passed objects, rather than returning a
brand new object.
@method mix
@param {object} target Object to receive mixed-in properties.
@param {object} obj* One or more objects to mix into _target_.
@return {object} Reference to the same _target_ object that was passed in.
**/
function mix() {
var args = Array.prototype.slice.call(arguments),
target = args.shift(),
i, key, keys, len, source, value;
while ((source = args.shift())) {
keys = Object.keys(source);
for (i = 0, len = keys.length; i < len; ++i) {
key = keys[i];
value = source[key];
if (value && typeof value === 'object' && !Array.isArray(value)) {
typeof target[key] === 'object' || (target[key] = {});
mix(target[key], value);
} else {
target[key] = value;
}
}
}
return target;
}
exports.mix = mix;
/**
If _obj_ is an array, returns `obj.length`. If _obj_ is an object, returns the
number of enumerable properties.
@method size
@param {array|object} obj Array or object.
@return {int} Size.
**/
function size(obj) {
return Array.isArray(obj) ? obj.length : Object.keys(obj).length;
}
exports.size = size;
/**
Returns an array of values in _obj_.
@method values
@param {Array|Object} obj Array or object.
@return {Array} Values.
**/
function values(obj) {
var result = [];
each(obj, function (value) {
result.push(value);
});
return result;
}
exports.values = values;
/**
Creates a stack for multiple callback management:
var s = new util.Stack();
asyncMethod(s.add(fn));
asyncMethod(s.add(fn));
asyncMethod(s.add(fn));
asyncMethod(s.add(fn));
s.done(function() {
// Called when all async methods are done.
});
@class Stack
@return {Stack} Stack instance
@constructor
**/
var Stack = function () {
this.errors = [];
this.finished = 0;
this.results = [];
this.total = 0;
};
Stack.prototype = {
add: function (fn) {
var self = this,
index = self.total;
self.total += 1;
return function (err) {
if (err) { self.errors[index] = err; }
self.finished += 1;
self.results[index] = fn.apply(null, arguments);
self.test();
};
},
test: function () {
if (this.finished >= this.total && this.callback) {
this.callback.call(null, this.errors.length ? this.errors : null,
this.results, this.data);
}
},
done: function (callback, data) {
this.callback = callback;
this.data = data;
this.test();
}
};
exports.Stack = Stack;