/** * Contains calculation methods * * Copyright (C) 2017 R-T Specialty, LLC. * * This file is part of the Liza Data Collection Framework. * * liza is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ function _each( data, value, callback ) { var data_len = data.length, result = [], cur_val = null; for ( var i = 0; i < data_len; i++ ) { cur_val = ( value[ i ] !== undefined ) ? value[ i ] : cur_val; result.push( callback( data[ i ], cur_val, i ) ); } return result; } exports.append = function( data, value ) { return _each( data, value, function( arr1, arr2 ) { if( !( arr1 instanceof Array ) ) { arr1 = [ arr1 ]; } return arr1.concat( arr2 ); }); }; exports.join = function( data, value ) { return _each( data, value, function( arr, delimiter ) { return arr.join( delimiter ); }); }; exports[ 'if' ] = function( data, value ) { var result = []; for ( var i = 0; i < data.length; i++ ) { result.push( ( ( data[ i ] === "1" ) ? value[ i ] : '' ) ); } return result; }; exports.sum = function( data ) { var data_len = data.length; // calculate and return the sum for ( var i = 0, sum = 0; i < data_len; sum += +data[i++] ); return [ sum ]; }; /** * Return an object containing a range of elements * * @param data The first value of the sequence of numbers * @param value The ending value of the sequence * * @example range( 1, 3 ) will yield [ "1", "2", "3" ] * range( 5, 2 ) will yield [ "5", "4", "3", "2" ] */ exports.range = function( data, value ) { var result = [], range_from = data[ 0 ], range_to = value[ 0 ], low = Math.min( range_from, range_to ), high = Math.max( range_from, range_to ), index = 0; for ( var i = low; i < high; i++ ) { result[ index ] = ( ''+i ); index++; } return ( range_from < range_to ) ? result : result.reverse(); }; exports.length = function( data ) { var result = [], item = '', i = 0; while ( true ) { item = data[ i++ ]; if ( ( item === null ) || ( item === undefined ) ) { break; } result.push( item.length ); } return result; }; /** * Return the number of elements in the set that optionally match the given * value */ exports.count = function( data, value ) { var len = data.length; if ( !( value ) || value.length === 0 ) { return [ ''+( len ) ]; } var count = 0; for ( var i = 0; i < len; i++ ) { // intentional lazy cmp if ( data[ i ] == value[ 0 ] ) { count++; } } return [ ''+( count ) ]; }; exports.countNonEmpty = function( data, value ) { var count = 0; for ( var i in data ) { if ( data[ i ] !== '' ) { count++; } } return [ ''+count ]; }; exports.divide = function( data, value ) { return _each( data, value, function( val1, val2 ) { return ( +val1 / +val2 ); } ); }; exports.multiply = function( data, value ) { return _each( data, value, function( val1, val2 ) { return ( +val1 * +val2 ); } ); }; exports.add = function( data, value ) { return _each( data, value, function( val1, val2 ) { return ( +val1 + +val2 ); } ); }; exports.subtract = function( data, value ) { return _each( data, value, function( val1, val2 ) { return ( +val1 - +val2 ); } ); }; exports.date = function() { var now = new Date(); // return in the YYYY-MM-DD format, since that's what our fields are // formatted as return [ now.getFullYear() + '-' + ( now.getMonth() + 1 ) + '-' + now.getDate() ]; }; exports.userAgent = function() { return ( typeof window !== 'undefined' ) ? [ window.navigator.userAgent ] : [ '' ]; }; exports.relativeDate = function( data, value ) { return _each( data, value, function( curdate, format ) { var type = format.substr( -1 ), tval = format.substr( 0, format.length - 1 ), ms = 0; var now = ( curdate ) ? new Date( curdate ) : new Date(), now_year = now.getUTCFullYear(), now_month = now.getUTCMonth() + 1, now_day = now.getUTCDate(), date_new = null; switch ( type ) { // years case 'y': date_new = new Date( ( now_year + +tval ) + '/' + now_month + '/' + now_day ); break; // months case 'm': date_new = new Date( now_year + '/' + ( now_month + +tval ) + '/' + now_day ); break; // days case 'd': date_new = new Date( now_year + '/' + now_month + '/' + ( now_day + +tval ) ); break; // seconds case 's': date_new = new Date( now.getTime() + ( tval * 1000 ) ); break; default: return ''; } // return in the YYYY-MM-DD format, since that's what our fields are // formatted as return date_new.getFullYear() + '-' + ( date_new.getMonth() + 1 ) + '-' + date_new.getDate(); } ); }; exports.copy = function( data ) { return data; }; exports.month = function( data ) { // if no reference was provided, return the current month if ( data.length === 0 ) { return [ new Date().getMonth() + 1 ]; } // otherwise, get the month from each of the provided dates var len = data.length, result = []; for ( var i = 0; i < len; i++ ) { result.push( new Date( Date.parse( data[i].replace( /-/g, '/' ) ) ).getMonth() + 1 ); } return result; }; exports.year = function( data ) { // if no reference was provided, return the current year if ( data.length === 0 ) { return [ new Date().getFullYear() ]; } // otherwise, get the year from each of the provided dates var len = data.length, result = []; for ( var i = 0; i < len; i++ ) { if ( data[ i ] === '' ) { result.push( new Date().getFullYear() ); continue; } result.push( new Date( Date.parse( data[i].replace( /-/g, '/' ) ) ).getFullYear() ); } return result; }; // tests if a value(s) exists in data exports.isIn = function( data, value ) { var all_values_found = false; for ( var v_key = 0; v_key < value.length; v_key++ ) { var value_found = false; for ( var d_key = 0; d_key < data.length; d_key++ ) { // check to see if this value has a match if ( +value[ v_key ] === +data[ d_key ] ) { value_found = true; } } // all values sent in must have a match for isIn to be true all_values_found = ( value_found ) ? true : all_values_found; } return ( all_values_found === true ) ? [ '1' ] : [ '0' ]; }; exports.identical = function( data, value ) { // true if all values in data are the same var len = data.length, cmp_val = data[ 0 ]; for ( var i = 0; i < len; i++ ) { // null indicates that the location is marked for deletion if ( data[ i ] !== cmp_val && data[ i ] !== null ) { // something doesn't match return [ '0' ]; } } return [ '1' ]; }; exports.dateDiff = function( data, value ) { var data_len = data.length, result = [], cmp_ts = 0; for ( var i = 0; i < data_len; i++ ) { // use the last available value for comparison if ( value[i] ) { // convert it to a timestamp cmp_ts = ( Date.parse( value[i].replace( /-/g, '/' ) ) / 1000 ); } // convert data to timestamp var data_ts = ( Date.parse( data[i].replace( /-/g, '/' ) ) / 1000 ); // if the given date is higher than the comparison value, then a // positive number will result, otherwise a negative var diff = data_ts - cmp_ts; result.push( isNaN( diff ) ? 0 : diff ); } return result; }; exports.split = function( data, value ) { return _each( data, value, function( item, delim ) { // split using the provided delimiter return ( ( ''+( item ) ).split( delim ) ); }); }; exports.keyValue = function( data, value ) { return _each( data, value, function( item, index ) { if ( !( item instanceof Array ) ) { return null; } // return requested index return item[ index ]; }); }; exports.match = function( data, value ) { return _each( data, value, function( item, regex ) { return ( ( ''+item ).match( new RegExp( regex ) ) ); }); }; /** * Return min numeric value in data set */ exports.min = function( data ) { var min_val = +data[ 0 ], i = data.length; while ( i-- ) { var cur_val = +data[ i ]; min_val = ( min_val < cur_val ) ? min_val : cur_val; } return [ ''+( min_val ) ]; }; /** * Finds min numeric value in data set and * returns its position */ exports.minPos = function( data ) { var min_val = +data[ 0 ], min_pos = 0; for ( var i = 0; i < data.length; i++ ) { var cur_val = +data[ i ]; if ( cur_val < min_val ) { min_val = cur_val; min_pos = i; } } return [ ''+min_pos ]; }; /** * Finds max numeric value in data set and * returns its position */ exports.maxPos = function( data ) { var max_val = +data[ 0 ], max_pos = 0; for ( var i = 0; i < data.length; i++ ) { var cur_val = +data[ i ]; if ( cur_val > max_val ) { max_val = cur_val; max_pos = i; } } return [ ''+max_pos ]; }; /** * Return max numeric value in data set */ exports.max = function( data ) { var max_val = +data[ 0 ], i = data.length, result = []; while ( i-- ) { var cur_val = +data[ i ]; max_val = ( max_val > cur_val ) ? max_val : cur_val; } return [ ''+( max_val ) ]; }; /** * Return array of each unique element in the data set */ exports.uniq = function( data ) { var found = {}, uniq = [], i = data.length; while ( i-- ) { var val = data[ i ]; if ( found[ val ] ) { continue; } found[ val ] = true; uniq.push( val ); } return uniq; }; /** * Join array of elements with a delimiter * * @param data The array of values to join * @param value The delimiter * * @example implode( [ "foo", "bar" ], "," ) will yield [ "foo,bar" ] */ exports.implode = function( data, value ) { return [ ( data.join( value || '' ) ) ]; }; /** * Given a regular expression, returns '1' for positive test and '0' for * negative. */ exports.test = function( data, value ) { return _each( data, value, function( item, regex ) { // return '1' for true, '0' for false return ''+( +( ( new RegExp( regex ) ).test( item ) ) ); } ); }; /** * Opposite of test */ exports.testNot = function( data, value ) { return _each( data, value, function( item, regex ) { // return '1' for true, '0' for false return ''+( +( !( ( new RegExp( regex ) ).test( item ) ) ) ); } ); }; /** * N-based index */ exports.position = function( data, value ) { return _each( data, value, function( item, offset, i ) { return +i + ( +offset || 0 ); } ); }; /** * Given a set of indexes, return array of values */ exports.value = function( data, indexes ) { var len = indexes.length, values = [], key = 0; for ( var i = 0; i < len; i++ ) { key = indexes[ i ]; if ( data[ key ] !== null ) { // found a value values.push( data[ key ] ); } else { values.push( null ); } } return values; }; exports[ 'void' ] = function() { return []; };