// create namespace var util = {}; /** * Test whether given object is a number * @param {*} object * @return {Boolean} isNumber */ util.isNumber = function isNumber(object) { return (object instanceof Number || typeof object == 'number'); }; /** * Test whether given object is a string * @param {*} object * @return {Boolean} isString */ util.isString = function isString(object) { return (object instanceof String || typeof object == 'string'); }; /** * Test whether given object is a Date, or a String containing a Date * @param {Date | String} object * @return {Boolean} isDate */ util.isDate = function isDate(object) { if (object instanceof Date) { return true; } else if (util.isString(object)) { // test whether this string contains a date var match = ASPDateRegex.exec(object); if (match) { return true; } else if (!isNaN(Date.parse(object))) { return true; } } return false; }; /** * Test whether given object is an instance of google.visualization.DataTable * @param {*} object * @return {Boolean} isDataTable */ util.isDataTable = function isDataTable(object) { return (typeof (google) !== 'undefined') && (google.visualization) && (google.visualization.DataTable) && (object instanceof google.visualization.DataTable); }; /** * Create a semi UUID * source: http://stackoverflow.com/a/105074/1262753 * @return {String} uuid */ util.randomUUID = function randomUUID () { var S4 = function () { return Math.floor( Math.random() * 0x10000 /* 65536 */ ).toString(16); }; return ( S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4() ); }; /** * Extend object a with the properties of object b * @param {Object} a * @param {Object} b * @return {Object} a */ util.extend = function (a, b) { for (var prop in b) { if (b.hasOwnProperty(prop)) { a[prop] = b[prop]; } } return a; }; /** * Cast an object to another type * @param {Boolean | Number | String | Date | Null | undefined} object * @param {String | function | undefined} type Name of the type or a cast * function. Available types: * 'Boolean', 'Number', 'String', * 'Date', ISODate', 'ASPDate'. * @return {*} object * @throws Error */ util.cast = function cast(object, type) { if (object === undefined) { return undefined; } if (object === null) { return null; } if (!type) { return object; } if (typeof type == 'function') { return type(object); } //noinspection FallthroughInSwitchStatementJS switch (type) { case 'boolean': case 'Boolean': return Boolean(object); case 'number': case 'Number': return Number(object); case 'string': case 'String': return String(object); case 'Date': if (util.isNumber(object)) { return new Date(object); } if (object instanceof Date) { return new Date(object.valueOf()); } if (util.isString(object)) { // parse ASP.Net Date pattern, // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/' // code from http://momentjs.com/ var match = ASPDateRegex.exec(object); if (match) { return new Date(Number(match[1])); } else { return moment(object).toDate(); // parse string } } else { throw new Error( 'Cannot cast object of type ' + util.getType(object) + ' to type Date'); } case 'ISODate': if (object instanceof Date) { return object.toISOString(); } else if (util.isNumber(object) || util.isString(object)) { return moment(object).toDate().toISOString(); } else { throw new Error( 'Cannot cast object of type ' + util.getType(object) + ' to type ISODate'); } case 'ASPDate': if (object instanceof Date) { return '/Date(' + object.valueOf() + ')/'; } else if (util.isNumber(object) || util.isString(object)) { return '/Date(' + moment(object).valueOf() + ')/'; } else { throw new Error( 'Cannot cast object of type ' + util.getType(object) + ' to type ASPDate'); } default: throw new Error('Cannot cast object of type ' + util.getType(object) + ' to type "' + type + '"'); } }; var ASPDateRegex = /^\/?Date\((\-?\d+)/i; /** * Get the type of an object, for example util.getType([]) returns 'Array' * @param {*} object * @return {String} type */ util.getType = function getType(object) { var type = typeof object; if (type == 'object') { if (object == null) { return 'null'; } if (object instanceof Boolean) { return 'Boolean'; } if (object instanceof Number) { return 'Number'; } if (object instanceof String) { return 'String'; } if (object instanceof Array) { return 'Array'; } if (object instanceof Date) { return 'Date'; } return 'Object'; } else if (type == 'number') { return 'Number'; } else if (type == 'boolean') { return 'Boolean'; } else if (type == 'string') { return 'String'; } return type; }; /** * Retrieve the absolute left value of a DOM element * @param {Element} elem A dom element, for example a div * @return {number} left The absolute left position of this element * in the browser page. */ util.getAbsoluteLeft = function getAbsoluteLeft (elem) { var doc = document.documentElement; var body = document.body; var left = elem.offsetLeft; var e = elem.offsetParent; while (e != null && e != body && e != doc) { left += e.offsetLeft; left -= e.scrollLeft; e = e.offsetParent; } return left; }; /** * Retrieve the absolute top value of a DOM element * @param {Element} elem A dom element, for example a div * @return {number} top The absolute top position of this element * in the browser page. */ util.getAbsoluteTop = function getAbsoluteTop (elem) { var doc = document.documentElement; var body = document.body; var top = elem.offsetTop; var e = elem.offsetParent; while (e != null && e != body && e != doc) { top += e.offsetTop; top -= e.scrollTop; e = e.offsetParent; } return top; }; /** * Get the absolute, vertical mouse position from an event. * @param {Event} event * @return {Number} pageY */ util.getPageY = function getPageY (event) { if ('pageY' in event) { return event.pageY; } else { var clientY; if (('targetTouches' in event) && event.targetTouches.length) { clientY = event.targetTouches[0].clientY; } else { clientY = event.clientY; } var doc = document.documentElement; var body = document.body; return clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } }; /** * Get the absolute, horizontal mouse position from an event. * @param {Event} event * @return {Number} pageX */ util.getPageX = function getPageX (event) { if ('pageY' in event) { return event.pageX; } else { var clientX; if (('targetTouches' in event) && event.targetTouches.length) { clientX = event.targetTouches[0].clientX; } else { clientX = event.clientX; } var doc = document.documentElement; var body = document.body; return clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); } }; /** * add a className to the given elements style * @param {Element} elem * @param {String} className */ util.addClassName = function addClassName(elem, className) { var classes = elem.className.split(' '); if (classes.indexOf(className) == -1) { classes.push(className); // add the class to the array elem.className = classes.join(' '); } }; /** * add a className to the given elements style * @param {Element} elem * @param {String} className */ util.removeClassName = function removeClassname(elem, className) { var classes = elem.className.split(' '); var index = classes.indexOf(className); if (index != -1) { classes.splice(index, 1); // remove the class from the array elem.className = classes.join(' '); } }; /** * For each method for both arrays and objects. * In case of an array, the built-in Array.forEach() is applied. * In case of an Object, the method loops over all properties of the object. * @param {Object | Array} object An Object or Array * @param {function} callback Callback method, called for each item in * the object or array with three parameters: * callback(value, index, object) */ util.forEach = function forEach (object, callback) { if (object instanceof Array) { // array object.forEach(callback); } else { // object for (var key in object) { if (object.hasOwnProperty(key)) { callback(object[key], key, object); } } } }; /** * Update a property in an object * @param {Object} object * @param {String} key * @param {*} value * @return {Boolean} changed */ util.updateProperty = function updateProp (object, key, value) { if (object[key] !== value) { object[key] = value; return true; } else { return false; } }; /** * Add and event listener. Works for all browsers * @param {Element} element An html element * @param {string} action The action, for example "click", * without the prefix "on" * @param {function} listener The callback function to be executed * @param {boolean} [useCapture] */ util.addEventListener = function addEventListener(element, action, listener, useCapture) { if (element.addEventListener) { if (useCapture === undefined) useCapture = false; if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { action = "DOMMouseScroll"; // For Firefox } element.addEventListener(action, listener, useCapture); } else { element.attachEvent("on" + action, listener); // IE browsers } }; /** * Remove an event listener from an element * @param {Element} element An html dom element * @param {string} action The name of the event, for example "mousedown" * @param {function} listener The listener function * @param {boolean} [useCapture] */ util.removeEventListener = function removeEventListener(element, action, listener, useCapture) { if (element.removeEventListener) { // non-IE browsers if (useCapture === undefined) useCapture = false; if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { action = "DOMMouseScroll"; // For Firefox } element.removeEventListener(action, listener, useCapture); } else { // IE browsers element.detachEvent("on" + action, listener); } }; /** * Get HTML element which is the target of the event * @param {Event} event * @return {Element} target element */ util.getTarget = function getTarget(event) { // code from http://www.quirksmode.org/js/events_properties.html if (!event) { event = window.event; } var target; if (event.target) { target = event.target; } else if (event.srcElement) { target = event.srcElement; } if (target.nodeType != undefined && target.nodeType == 3) { // defeat Safari bug target = target.parentNode; } return target; }; /** * Stop event propagation */ util.stopPropagation = function stopPropagation(event) { if (!event) event = window.event; if (event.stopPropagation) { event.stopPropagation(); // non-IE browsers } else { event.cancelBubble = true; // IE browsers } }; /** * Cancels the event if it is cancelable, without stopping further propagation of the event. */ util.preventDefault = function preventDefault (event) { if (!event) event = window.event; if (event.preventDefault) { event.preventDefault(); // non-IE browsers } else { event.returnValue = false; // IE browsers } }; util.option = {}; /** * Cast a value as boolean * @param {Boolean | function | undefined} value * @param {Boolean} [defaultValue] * @returns {Boolean} bool */ util.option.asBoolean = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } if (value != null) { return (value != false); } return defaultValue || null; }; /** * Cast a value as number * @param {Boolean | function | undefined} value * @param {Number} [defaultValue] * @returns {Number} number */ util.option.asNumber = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } if (value != null) { return Number(value); } return defaultValue || null; }; /** * Cast a value as string * @param {String | function | undefined} value * @param {String} [defaultValue] * @returns {String} str */ util.option.asString = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } if (value != null) { return String(value); } return defaultValue || null; }; /** * Cast a size or location in pixels or a percentage * @param {String | Number | function | undefined} value * @param {String} [defaultValue] * @returns {String} size */ util.option.asSize = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } if (util.isString(value)) { return value; } else if (util.isNumber(value)) { return value + 'px'; } else { return defaultValue || null; } }; /** * Cast a value as DOM element * @param {HTMLElement | function | undefined} value * @param {HTMLElement} [defaultValue] * @returns {HTMLElement | null} dom */ util.option.asElement = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } return value || defaultValue || null; }; // Internet Explorer 8 and older does not support Array.indexOf, so we define // it here in that case. // http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ if(!Array.prototype.indexOf) { Array.prototype.indexOf = function(obj){ for(var i = 0; i < this.length; i++){ if(this[i] == obj){ return i; } } return -1; }; try { console.log("Warning: Ancient browser detected. Please update your browser"); } catch (err) { } } // Internet Explorer 8 and older does not support Array.forEach, so we define // it here in that case. // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function(fn, scope) { for(var i = 0, len = this.length; i < len; ++i) { fn.call(scope || this, this[i], i, this); } } } // Internet Explorer 8 and older does not support Array.map, so we define it // here in that case. // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map // Production steps of ECMA-262, Edition 5, 15.4.4.19 // Reference: http://es5.github.com/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback, thisArg) { var T, A, 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 (thisArg) { T = thisArg; } // 6. Let A be a new array created as if by the expression new Array(len) where Array is // the standard built-in constructor with that name and len is the value of len. A = new Array(len); // 7. Let k be 0 k = 0; // 8. Repeat, while k < len while(k < len) { var kValue, mappedValue; // 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. Let mappedValue be the result of calling the Call internal method of callback // with T as the this value and argument list containing kValue, k, and O. mappedValue = callback.call(T, kValue, k, O); // iii. Call the DefineOwnProperty internal method of A with arguments // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true}, // and false. // In browsers that support Object.defineProperty, use the following: // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); // For best browser support, use the following: A[ k ] = mappedValue; } // d. Increase k by 1. k++; } // 9. return A return A; }; } // Internet Explorer 8 and older does not support Array.filter, so we define it // here in that case. // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp */) { "use strict"; if (this == null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (typeof fun != "function") { throw new TypeError(); } var res = []; var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in t) { var val = t[i]; // in case fun mutates this if (fun.call(thisp, val, i, t)) res.push(val); } } return res; }; } // Internet Explorer 8 and older does not support Object.keys, so we define it // here in that case. // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys if (!Object.keys) { Object.keys = (function () { var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), dontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor' ], dontEnumsLength = dontEnums.length; return function (obj) { if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { throw new TypeError('Object.keys called on non-object'); } var result = []; for (var prop in obj) { if (hasOwnProperty.call(obj, prop)) result.push(prop); } if (hasDontEnumBug) { for (var i=0; i < dontEnumsLength; i++) { if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); } } return result; } })() } // Internet Explorer 8 and older does not support Array.isArray, // so we define it here in that case. // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray if(!Array.isArray) { Array.isArray = function (vArg) { return Object.prototype.toString.call(vArg) === "[object Array]"; }; } // exports module.exports = exports = util;