|
|
- // utility functions
-
- // first check if moment.js is already loaded in the browser window, if so,
- // use this instance. Else, load via commonjs.
- var moment = require('./module/moment');
-
- /**
- * Test whether given object is a number
- * @param {*} object
- * @return {Boolean} isNumber
- */
- exports.isNumber = function(object) {
- return (object instanceof Number || typeof object == 'number');
- };
-
- /**
- * Test whether given object is a string
- * @param {*} object
- * @return {Boolean} isString
- */
- exports.isString = function(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
- */
- exports.isDate = function(object) {
- if (object instanceof Date) {
- return true;
- }
- else if (exports.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
- */
- exports.isDataTable = function(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
- */
- exports.randomUUID = function() {
- 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 or a series of objects
- * Only properties with defined values are copied
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.extend = function (a, b) {
- for (var i = 1, len = arguments.length; i < len; i++) {
- var other = arguments[i];
- for (var prop in other) {
- if (other.hasOwnProperty(prop)) {
- a[prop] = other[prop];
- }
- }
- }
-
- return a;
- };
-
- /**
- * Extend object a with selected properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Array.<String>} props
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.selectiveExtend = function (props, a, b) {
- if (!Array.isArray(props)) {
- throw new Error('Array with property names expected as first argument');
- }
-
- for (var i = 2; i < arguments.length; i++) {
- var other = arguments[i];
-
- for (var p = 0; p < props.length; p++) {
- var prop = props[p];
- if (other.hasOwnProperty(prop)) {
- a[prop] = other[prop];
- }
- }
- }
- return a;
- };
-
- /**
- * Extend object a with selected properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Array.<String>} props
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.selectiveDeepExtend = function (props, a, b) {
- // TODO: add support for Arrays to deepExtend
- if (Array.isArray(b)) {
- throw new TypeError('Arrays are not supported by deepExtend');
- }
- for (var i = 2; i < arguments.length; i++) {
- var other = arguments[i];
- for (var p = 0; p < props.length; p++) {
- var prop = props[p];
- if (other.hasOwnProperty(prop)) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop].constructor === Object) {
- exports.deepExtend(a[prop], b[prop]);
- }
- else {
- a[prop] = b[prop];
- }
- } else if (Array.isArray(b[prop])) {
- throw new TypeError('Arrays are not supported by deepExtend');
- } else {
- a[prop] = b[prop];
- }
-
- }
- }
- }
- return a;
- };
-
- /**
- * Deep extend an object a with the properties of object b
- * @param {Object} a
- * @param {Object} b
- * @returns {Object}
- */
- exports.deepExtend = function(a, b) {
- // TODO: add support for Arrays to deepExtend
- if (Array.isArray(b)) {
- throw new TypeError('Arrays are not supported by deepExtend');
- }
-
- for (var prop in b) {
- if (b.hasOwnProperty(prop)) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop].constructor === Object) {
- exports.deepExtend(a[prop], b[prop]);
- }
- else {
- a[prop] = b[prop];
- }
- } else if (Array.isArray(b[prop])) {
- throw new TypeError('Arrays are not supported by deepExtend');
- } else {
- a[prop] = b[prop];
- }
- }
- }
- return a;
- };
-
- /**
- * Test whether all elements in two arrays are equal.
- * @param {Array} a
- * @param {Array} b
- * @return {boolean} Returns true if both arrays have the same length and same
- * elements.
- */
- exports.equalArray = function (a, b) {
- if (a.length != b.length) return false;
-
- for (var i = 0, len = a.length; i < len; i++) {
- if (a[i] != b[i]) return false;
- }
-
- return true;
- };
-
- /**
- * Convert an object to another type
- * @param {Boolean | Number | String | Date | Moment | Null | undefined} object
- * @param {String | undefined} type Name of the type. Available types:
- * 'Boolean', 'Number', 'String',
- * 'Date', 'Moment', ISODate', 'ASPDate'.
- * @return {*} object
- * @throws Error
- */
- exports.convert = function(object, type) {
- var match;
-
- if (object === undefined) {
- return undefined;
- }
- if (object === null) {
- return null;
- }
-
- if (!type) {
- return object;
- }
- if (!(typeof type === 'string') && !(type instanceof String)) {
- throw new Error('Type must be a string');
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (type) {
- case 'boolean':
- case 'Boolean':
- return Boolean(object);
-
- case 'number':
- case 'Number':
- return Number(object.valueOf());
-
- case 'string':
- case 'String':
- return String(object);
-
- case 'Date':
- if (exports.isNumber(object)) {
- return new Date(object);
- }
- if (object instanceof Date) {
- return new Date(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return new Date(object.valueOf());
- }
- if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])); // parse number
- }
- else {
- return moment(object).toDate(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type Date');
- }
-
- case 'Moment':
- if (exports.isNumber(object)) {
- return moment(object);
- }
- if (object instanceof Date) {
- return moment(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return moment(object);
- }
- if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return moment(Number(match[1])); // parse number
- }
- else {
- return moment(object); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type Date');
- }
-
- case 'ISODate':
- if (exports.isNumber(object)) {
- return new Date(object);
- }
- else if (object instanceof Date) {
- return object.toISOString();
- }
- else if (moment.isMoment(object)) {
- return object.toDate().toISOString();
- }
- else if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])).toISOString(); // parse number
- }
- else {
- return new Date(object).toISOString(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type ISODate');
- }
-
- case 'ASPDate':
- if (exports.isNumber(object)) {
- return '/Date(' + object + ')/';
- }
- else if (object instanceof Date) {
- return '/Date(' + object.valueOf() + ')/';
- }
- else if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- var value;
- if (match) {
- // object is an ASP date
- value = new Date(Number(match[1])).valueOf(); // parse number
- }
- else {
- value = new Date(object).valueOf(); // parse string
- }
- return '/Date(' + value + ')/';
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type ASPDate');
- }
-
- default:
- throw new Error('Unknown type "' + type + '"');
- }
- };
-
- // parse ASP.Net Date pattern,
- // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
- // code from http://momentjs.com/
- var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
-
- /**
- * Get the type of an object, for example exports.getType([]) returns 'Array'
- * @param {*} object
- * @return {String} type
- */
- exports.getType = function(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.
- */
- exports.getAbsoluteLeft = function(elem) {
- return elem.getBoundingClientRect().left + window.pageXOffset;
- };
-
- /**
- * 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.
- */
- exports.getAbsoluteTop = function(elem) {
- return elem.getBoundingClientRect().top + window.pageYOffset;
- };
-
- /**
- * add a className to the given elements style
- * @param {Element} elem
- * @param {String} className
- */
- exports.addClassName = function(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
- */
- exports.removeClassName = function(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)
- */
- exports.forEach = function(object, callback) {
- var i,
- len;
- if (object instanceof Array) {
- // array
- for (i = 0, len = object.length; i < len; i++) {
- callback(object[i], i, object);
- }
- }
- else {
- // object
- for (i in object) {
- if (object.hasOwnProperty(i)) {
- callback(object[i], i, object);
- }
- }
- }
- };
-
- /**
- * Convert an object into an array: all objects properties are put into the
- * array. The resulting array is unordered.
- * @param {Object} object
- * @param {Array} array
- */
- exports.toArray = function(object) {
- var array = [];
-
- for (var prop in object) {
- if (object.hasOwnProperty(prop)) array.push(object[prop]);
- }
-
- return array;
- }
-
- /**
- * Update a property in an object
- * @param {Object} object
- * @param {String} key
- * @param {*} value
- * @return {Boolean} changed
- */
- exports.updateProperty = function(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]
- */
- exports.addEventListener = function(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]
- */
- exports.removeEventListener = function(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);
- }
- };
-
- /**
- * Cancels the event if it is cancelable, without stopping further propagation of the event.
- */
- exports.preventDefault = function (event) {
- if (!event)
- event = window.event;
-
- if (event.preventDefault) {
- event.preventDefault(); // non-IE browsers
- }
- else {
- event.returnValue = false; // IE browsers
- }
- };
-
- /**
- * Get HTML element which is the target of the event
- * @param {Event} event
- * @return {Element} target element
- */
- exports.getTarget = function(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;
- };
-
- exports.option = {};
-
- /**
- * Convert a value into a boolean
- * @param {Boolean | function | undefined} value
- * @param {Boolean} [defaultValue]
- * @returns {Boolean} bool
- */
- exports.option.asBoolean = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return (value != false);
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a value into a number
- * @param {Boolean | function | undefined} value
- * @param {Number} [defaultValue]
- * @returns {Number} number
- */
- exports.option.asNumber = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return Number(value) || defaultValue || null;
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a value into a string
- * @param {String | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} str
- */
- exports.option.asString = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return String(value);
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a size or location into a string with pixels or a percentage
- * @param {String | Number | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} size
- */
- exports.option.asSize = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (exports.isString(value)) {
- return value;
- }
- else if (exports.isNumber(value)) {
- return value + 'px';
- }
- else {
- return defaultValue || null;
- }
- };
-
- /**
- * Convert a value into a DOM element
- * @param {HTMLElement | function | undefined} value
- * @param {HTMLElement} [defaultValue]
- * @returns {HTMLElement | null} dom
- */
- exports.option.asElement = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- return value || defaultValue || null;
- };
-
-
-
- exports.GiveDec = function(Hex) {
- var Value;
-
- if (Hex == "A")
- Value = 10;
- else if (Hex == "B")
- Value = 11;
- else if (Hex == "C")
- Value = 12;
- else if (Hex == "D")
- Value = 13;
- else if (Hex == "E")
- Value = 14;
- else if (Hex == "F")
- Value = 15;
- else
- Value = eval(Hex);
-
- return Value;
- };
-
- exports.GiveHex = function(Dec) {
- var Value;
-
- if(Dec == 10)
- Value = "A";
- else if (Dec == 11)
- Value = "B";
- else if (Dec == 12)
- Value = "C";
- else if (Dec == 13)
- Value = "D";
- else if (Dec == 14)
- Value = "E";
- else if (Dec == 15)
- Value = "F";
- else
- Value = "" + Dec;
-
- return Value;
- };
-
- /**
- * Parse a color property into an object with border, background, and
- * highlight colors
- * @param {Object | String} color
- * @return {Object} colorObject
- */
- exports.parseColor = function(color) {
- var c;
- if (exports.isString(color)) {
- if (exports.isValidRGB(color)) {
- var rgb = color.substr(4).substr(0,color.length-5).split(',');
- color = exports.RGBToHex(rgb[0],rgb[1],rgb[2]);
- }
- if (exports.isValidHex(color)) {
- var hsv = exports.hexToHSV(color);
- var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)};
- var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6};
- var darkerColorHex = exports.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v);
- var lighterColorHex = exports.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v);
-
- c = {
- background: color,
- border:darkerColorHex,
- highlight: {
- background:lighterColorHex,
- border:darkerColorHex
- },
- hover: {
- background:lighterColorHex,
- border:darkerColorHex
- }
- };
- }
- else {
- c = {
- background:color,
- border:color,
- highlight: {
- background:color,
- border:color
- },
- hover: {
- background:color,
- border:color
- }
- };
- }
- }
- else {
- c = {};
- c.background = color.background || 'white';
- c.border = color.border || c.background;
-
- if (exports.isString(color.highlight)) {
- c.highlight = {
- border: color.highlight,
- background: color.highlight
- }
- }
- else {
- c.highlight = {};
- c.highlight.background = color.highlight && color.highlight.background || c.background;
- c.highlight.border = color.highlight && color.highlight.border || c.border;
- }
-
- if (exports.isString(color.hover)) {
- c.hover = {
- border: color.hover,
- background: color.hover
- }
- }
- else {
- c.hover = {};
- c.hover.background = color.hover && color.hover.background || c.background;
- c.hover.border = color.hover && color.hover.border || c.border;
- }
- }
-
- return c;
- };
-
- /**
- * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
- *
- * @param {String} hex
- * @returns {{r: *, g: *, b: *}}
- */
- exports.hexToRGB = function(hex) {
- hex = hex.replace("#","").toUpperCase();
-
- var a = exports.GiveDec(hex.substring(0, 1));
- var b = exports.GiveDec(hex.substring(1, 2));
- var c = exports.GiveDec(hex.substring(2, 3));
- var d = exports.GiveDec(hex.substring(3, 4));
- var e = exports.GiveDec(hex.substring(4, 5));
- var f = exports.GiveDec(hex.substring(5, 6));
-
- var r = (a * 16) + b;
- var g = (c * 16) + d;
- var b = (e * 16) + f;
-
- return {r:r,g:g,b:b};
- };
-
- exports.RGBToHex = function(red,green,blue) {
- var a = exports.GiveHex(Math.floor(red / 16));
- var b = exports.GiveHex(red % 16);
- var c = exports.GiveHex(Math.floor(green / 16));
- var d = exports.GiveHex(green % 16);
- var e = exports.GiveHex(Math.floor(blue / 16));
- var f = exports.GiveHex(blue % 16);
-
- var hex = a + b + c + d + e + f;
- return "#" + hex;
- };
-
-
- /**
- * http://www.javascripter.net/faq/rgb2hsv.htm
- *
- * @param red
- * @param green
- * @param blue
- * @returns {*}
- * @constructor
- */
- exports.RGBToHSV = function(red,green,blue) {
- red=red/255; green=green/255; blue=blue/255;
- var minRGB = Math.min(red,Math.min(green,blue));
- var maxRGB = Math.max(red,Math.max(green,blue));
-
- // Black-gray-white
- if (minRGB == maxRGB) {
- return {h:0,s:0,v:minRGB};
- }
-
- // Colors other than black-gray-white:
- var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red);
- var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5);
- var hue = 60*(h - d/(maxRGB - minRGB))/360;
- var saturation = (maxRGB - minRGB)/maxRGB;
- var value = maxRGB;
- return {h:hue,s:saturation,v:value};
- };
-
-
- /**
- * https://gist.github.com/mjijackson/5311256
- * @param h
- * @param s
- * @param v
- * @returns {{r: number, g: number, b: number}}
- * @constructor
- */
- exports.HSVToRGB = function(h, s, v) {
- var r, g, b;
-
- var i = Math.floor(h * 6);
- var f = h * 6 - i;
- var p = v * (1 - s);
- var q = v * (1 - f * s);
- var t = v * (1 - (1 - f) * s);
-
- switch (i % 6) {
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
- }
-
- return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
- };
-
- exports.HSVToHex = function(h, s, v) {
- var rgb = exports.HSVToRGB(h, s, v);
- return exports.RGBToHex(rgb.r, rgb.g, rgb.b);
- };
-
- exports.hexToHSV = function(hex) {
- var rgb = exports.hexToRGB(hex);
- return exports.RGBToHSV(rgb.r, rgb.g, rgb.b);
- };
-
- exports.isValidHex = function(hex) {
- var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
- return isOk;
- };
-
- exports.isValidRGB = function(rgb) {
- rgb = rgb.replace(" ","");
- var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb);
- return isOk;
- }
-
- /**
- * This recursively redirects the prototype of JSON objects to the referenceObject
- * This is used for default options.
- *
- * @param referenceObject
- * @returns {*}
- */
- exports.selectiveBridgeObject = function(fields, referenceObject) {
- if (typeof referenceObject == "object") {
- var objectTo = Object.create(referenceObject);
- for (var i = 0; i < fields.length; i++) {
- if (referenceObject.hasOwnProperty(fields[i])) {
- if (typeof referenceObject[fields[i]] == "object") {
- objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]);
- }
- }
- }
- return objectTo;
- }
- else {
- return null;
- }
- };
-
- /**
- * This recursively redirects the prototype of JSON objects to the referenceObject
- * This is used for default options.
- *
- * @param referenceObject
- * @returns {*}
- */
- exports.bridgeObject = function(referenceObject) {
- if (typeof referenceObject == "object") {
- var objectTo = Object.create(referenceObject);
- for (var i in referenceObject) {
- if (referenceObject.hasOwnProperty(i)) {
- if (typeof referenceObject[i] == "object") {
- objectTo[i] = exports.bridgeObject(referenceObject[i]);
- }
- }
- }
- return objectTo;
- }
- else {
- return null;
- }
- };
-
-
- /**
- * this is used to set the options of subobjects in the options object. A requirement of these subobjects
- * is that they have an 'enabled' element which is optional for the user but mandatory for the program.
- *
- * @param [object] mergeTarget | this is either this.options or the options used for the groups.
- * @param [object] options | options
- * @param [String] option | this is the option key in the options argument
- * @private
- */
- exports.mergeOptions = function (mergeTarget, options, option) {
- if (options[option] !== undefined) {
- if (typeof options[option] == 'boolean') {
- mergeTarget[option].enabled = options[option];
- }
- else {
- mergeTarget[option].enabled = true;
- for (prop in options[option]) {
- if (options[option].hasOwnProperty(prop)) {
- mergeTarget[option][prop] = options[option][prop];
- }
- }
- }
- }
- }
-
-
- /**
- * this is used to set the options of subobjects in the options object. A requirement of these subobjects
- * is that they have an 'enabled' element which is optional for the user but mandatory for the program.
- *
- * @param [object] mergeTarget | this is either this.options or the options used for the groups.
- * @param [object] options | options
- * @param [String] option | this is the option key in the options argument
- * @private
- */
- exports.mergeOptions = function (mergeTarget, options, option) {
- if (options[option] !== undefined) {
- if (typeof options[option] == 'boolean') {
- mergeTarget[option].enabled = options[option];
- }
- else {
- mergeTarget[option].enabled = true;
- for (prop in options[option]) {
- if (options[option].hasOwnProperty(prop)) {
- mergeTarget[option][prop] = options[option][prop];
- }
- }
- }
- }
- }
-
-
-
-
- /**
- * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd
- * arrays. This is done by giving a boolean value true if you want to use the byEnd.
- * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check
- * if the time we selected (start or end) is within the current range).
- *
- * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is
- * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest,
- * either the start OR end time has to be in the range.
- *
- * @param {Item[]} orderedItems Items ordered by start
- * @param {{start: number, end: number}} range
- * @param {String} field
- * @param {String} field2
- * @returns {number}
- * @private
- */
- exports.binarySearch = function(orderedItems, range, field, field2) {
- var array = orderedItems;
-
- var maxIterations = 10000;
- var iteration = 0;
- var found = false;
- var low = 0;
- var high = array.length;
- var newLow = low;
- var newHigh = high;
- var guess = Math.floor(0.5*(high+low));
- var value;
-
- if (high == 0) {
- guess = -1;
- }
- else if (high == 1) {
- if (array[guess].isVisible(range)) {
- guess = 0;
- }
- else {
- guess = -1;
- }
- }
- else {
- high -= 1;
-
- while (found == false && iteration < maxIterations) {
- value = field2 === undefined ? array[guess][field] : array[guess][field][field2];
-
- if (array[guess].isVisible(range)) {
- found = true;
- }
- else {
- if (value < range.start) { // it is too small --> increase low
- newLow = Math.floor(0.5*(high+low));
- }
- else { // it is too big --> decrease high
- newHigh = Math.floor(0.5*(high+low));
- }
- // not in list;
- if (low == newLow && high == newHigh) {
- guess = -1;
- found = true;
- }
- else {
- high = newHigh; low = newLow;
- guess = Math.floor(0.5*(high+low));
- }
- }
- iteration++;
- }
- if (iteration >= maxIterations) {
- console.log("BinarySearch too many iterations. Aborting.");
- }
- }
- return guess;
- };
-
- /**
- * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd
- * arrays. This is done by giving a boolean value true if you want to use the byEnd.
- * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check
- * if the time we selected (start or end) is within the current range).
- *
- * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is
- * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest,
- * either the start OR end time has to be in the range.
- *
- * @param {Array} orderedItems
- * @param {{start: number, end: number}} target
- * @param {String} field
- * @param {String} sidePreference 'before' or 'after'
- * @returns {number}
- * @private
- */
- exports.binarySearchGeneric = function(orderedItems, target, field, sidePreference) {
- var maxIterations = 10000;
- var iteration = 0;
- var array = orderedItems;
- var found = false;
- var low = 0;
- var high = array.length;
- var newLow = low;
- var newHigh = high;
- var guess = Math.floor(0.5*(high+low));
- var newGuess;
- var prevValue, value, nextValue;
-
- if (high == 0) {guess = -1;}
- else if (high == 1) {
- value = array[guess][field];
- if (value == target) {
- guess = 0;
- }
- else {
- guess = -1;
- }
- }
- else {
- high -= 1;
- while (found == false && iteration < maxIterations) {
- prevValue = array[Math.max(0,guess - 1)][field];
- value = array[guess][field];
- nextValue = array[Math.min(array.length-1,guess + 1)][field];
-
- if (value == target || prevValue < target && value > target || value < target && nextValue > target) {
- found = true;
- if (value != target) {
- if (sidePreference == 'before') {
- if (prevValue < target && value > target) {
- guess = Math.max(0,guess - 1);
- }
- }
- else {
- if (value < target && nextValue > target) {
- guess = Math.min(array.length-1,guess + 1);
- }
- }
- }
- }
- else {
- if (value < target) { // it is too small --> increase low
- newLow = Math.floor(0.5*(high+low));
- }
- else { // it is too big --> decrease high
- newHigh = Math.floor(0.5*(high+low));
- }
- newGuess = Math.floor(0.5*(high+low));
- // not in list;
- if (low == newLow && high == newHigh) {
- guess = -1;
- found = true;
- }
- else {
- high = newHigh; low = newLow;
- guess = Math.floor(0.5*(high+low));
- }
- }
- iteration++;
- }
- if (iteration >= maxIterations) {
- console.log("BinarySearch too many iterations. Aborting.");
- }
- }
- return guess;
- };
|