/*
* niViz -- snow profiles visualization
* Copyright (C) 2015 WSL/SLF - Fluelastrasse 11 - 7260 Davos Dorf - Switzerland.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*eslint complexity: [2, 15]*/
(function (niviz) {
'use strict';
var slice = Array.prototype.slice;
var concat = Array.prototype.concat;
var hasOwn = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
/** @module niviz */
/**
* Common utilities.
*
* @class util
* @static
*/
var exports = niviz.util = {};
exports.isUndefinedOrNull = function (obj) {
if (obj === null || obj === undefined) return true;
return false;
};
/**
* nivizNode provides a few convenience functions regarding the
* Node object passed as parameter, i. e. the height/width
* getters and setters as well as the offset function -
* an equivalent to jQuery's offset() function.
*
* @class nivizNode
* @constructor
*
* @param {Node} node
*
* @return {nivizNode}
*/
exports.nivizNode = function (node) {
function nivizNode () {}
function px (val) {
return val + 'px';
}
nivizNode.height = function (val) {
if (val === undefined || val === null)
return node.getBoundingClientRect().height;
node.style.height = px(val);
};
nivizNode.width = function (val) {
if (val === undefined || val === null)
return node.getBoundingClientRect().width;
node.style.width = px(val);
};
nivizNode.empty = function () {
node.innerHTML = '';
};
nivizNode.offset = function () {
var rect = node.getBoundingClientRect();
return {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
};
};
return nivizNode;
};
/**
* Checks if variable is a function
*
* @method isFunction
* @static
*
* @param {Object|Number|Function|Array} v The variable to check
*
* @return {Boolean} true if parameter is a function
*/
exports.isFunction = function (v) {
return v instanceof Function;
};
exports.isArray = function (arr) {
if (typeof Array.isArray === 'function') {
return Array.isArray(arr);
}
return toString.call(arr) === '[object Array]';
};
exports.isObject = function (obj) {
if (!obj || toString.call(obj) !== '[object Object]')
return false;
var hasOwnConstructor = hasOwn.call(obj, 'constructor');
var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype
&& hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
// Not own constructor property must be Object
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf)
return false;
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for (key in obj) { /**/ }
return typeof key === 'undefined' || hasOwn.call(obj, key);
};
/**
* Extends an object with 1..n other objects.
*
* @method extend
* @static
*
* @param {Object} obj The object to extend.
* @param {Object} source* The source objects used to extend the object.
*
* @return {Object} The extended object
*/
exports.extend = function extend() {
var options, name, src, copy, copyIsArray, clone;
var target = arguments[0];
var i = 1;
var length = arguments.length;
var deep = false;
// Handle a deep copy situation
if (typeof target === 'boolean') {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
if (exports.isUndefinedOrNull(target)
|| (typeof target !== 'object' && typeof target !== 'function'))
target = {};
for (; i < length; ++i) {
options = arguments[i];
// Only deal with non-null/undefined values
if (!exports.isUndefinedOrNull(options)) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
// Prevent never-ending loop
if (target !== copy) {
// Recurse if we're merging plain objects or arrays
if (deep && copy && (exports.isObject(copy)
|| (copyIsArray = exports.isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && exports.isArray(src) ? src : [];
} else {
clone = src && exports.isObject(src) ? src : {};
}
// Never move original objects, clone them
target[name] = extend(deep, clone, copy);
// Don't bring in undefined values
} else if (typeof copy !== 'undefined') {
target[name] = copy;
}
}
}
}
}
// Return the modified object
return target;
};
/**
* Format a string using sprintf-like syntax.
*
* @param {String} template The format string.
* @param {String|Number|Object} parameters*
*/
exports.format = function format(template) {
var params;
if (arguments.length === 1 && Array.isArray(template)) {
params = template;
template = params.shift();
} else {
params = slice.call(arguments, 1);
}
return template.replace(/%(s|d|j)/g, function () {
return params.shift();
});
};
/**
* Converts CamelCase into snake_case.
*
* @param {String} string A CamelCased string.
* @returns {String} The snake_cased string.
*/
exports.underscore = function underscore(string) {
return string
.replace(/([a-z])([A-Z])/, '$1_$2')
.replace(/[\s-]/, '_')
.toLowerCase();
};
/**
* Let constructor `C` inherit from `P`.
*
* @method inherits
* @static
*
* @param {Function} C The child constructor.
* @param {Function} P The parent constructor.
*
* @returns {Function} The extended child constructor.
*/
exports.inherits = (function () {
var F = function () {};
return function inherits(C, P) {
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P;
C.prototype.constructor = C;
return C;
};
}());
exports.borrow = function borrow(C, P, props) {
exports.assert(C.prototype);
exports.assert(P.prototype);
for (var i = 0; i < props.length; ++i)
C.prototype[props[i]] = P.prototype[props[i]];
return C;
};
/**
* Combines two or more arrays.
*
* @method zip
* @static
*
* @param {Array*}
*/
exports.zip = function zip() {
var i, ii = 0, j, jj = arguments.length, zipped;
for (i = 0; i < arguments.length; ++i)
ii = Math.max(ii, arguments[i].length);
zipped = new Array(ii);
for (i = 0; i < ii; ++i) {
zipped[i] = [];
for (j = 0; j < jj; ++j)
zipped[i].push(arguments[j][i]);
}
return zipped;
};
/**
* Assert that `condition` holds.
*
* @method assert
* @static
*
* @param {Boolean} condition
* @param {String} [message]
*
* @throws {Error} If the condition is false.
*
* @returns {Boolean} The conditions value.
*/
exports.assert = function assert(condition, message) {
if (!condition)
throw new Error(message || 'assertion failed');
return true;
};
exports.literal = function literal(value) {
if (value === '') return value;
var num = Number(value);
return isNaN(num) ? value : num;
};
exports.alias = function (obj, a, b) {
Object.defineProperty(obj, a, {
get: function () { return this[b]; }
});
return obj;
};
exports.noop = function noop() {};
exports.delay = function delay(action, ms) {
setTimeout(action, ms || 15);
};
/**
* Picks the passed-in properties from an object.
*
* @method pick
* @static
*
* @param {Object} obj
* @param {String|Array<String>} keys* The keys to pick.
*
* @return {Object} A copy of `obj` containing only the
* given `keys`.
*/
exports.pick = function pick(obj) {
var i, ii, key, result = {}, keys = concat.apply([], slice.call(arguments, 1));
for (i = 0, ii = keys.length; i < ii; ++i) {
key = keys[i];
if (key in obj) result[key] = obj[key];
}
return result;
};
/**
* Rounds the passed in value with the specified precision.
*
* @method round
* @static
*
* @param {Number} value
* @param {Number} precision
*
* @return {Number} Rounded value
*/
exports.round = function (value, precision) {
exports.assert(!isNaN(Number(value)), 'Cannot round ' + value);
var m = precision && precision > 0 ? Math.pow(10, precision) : 1;
return Math.round(value * m) / m;
};
/**
* Returns the base 10 logarithm of a number.
*
* @method log10
* @static
*
* @param {Number} y
* @return {Number}
*/
exports.log10 = function (y) {
return Math.log(y) / Math.LN10;
};
//Polyfills
if (!(Object.assign instanceof Function)) {
Object.assign = function(target) {
if (exports.isUndefinedOrNull(target))
throw new TypeError('Cannot convert undefined or null to object');
target = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (!exports.isUndefinedOrNull(source)) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
};
}
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this === null) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method
// of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of
// O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
}
// NodeList.forEach (not present in IE and Edge
(function () {
if (typeof NodeList.prototype.forEach === 'function') return false;
NodeList.prototype.forEach = Array.prototype.forEach;
})();
}(niviz));