onejgordon/flow-dashboard

View on GitHub
src/js/utils/util.js

Summary

Maintainability
F
4 days
Test Coverage
var $ = require('jquery');
var moment = require('moment-timezone');
import {findIndexById} from 'utils/store-utils';

var util = {

    ListPop: function(keyval, list, _key) {
        var key = _key || "id";
        for (var i=0; i<list.length; i++) {
            var li_el = list[i];
            if (li_el[key] == keyval) {
                list.pop(i);
            }
        }
    },

    play_audio(name) {
        let audio = new Audio('/static/sounds/' + name);
        audio.volume = 0.2;
        audio.play();
    },

    goal_id(year, month) {
        if (month == null) return year
        if (month < 10) month = '0' + month
        return `${year}-${month}`
    },

    notify(message, body, tag, icon) {
      let opts = {
        body: body,
        icon: icon || '/images/logo_128.png',
      };
      let notification;
      if (tag) opts.tag = tag;
      if (!("Notification" in window)) {
        console.warning("This browser does not support desktop notification");
      } else if (Notification.permission === "granted") {
        notification = new Notification(message, opts);
      }
      else if (Notification.permission !== "denied") {
        Notification.requestPermission(function (permission) {
          if (permission === "granted") {
            notification = new Notification(message, opts);
          }
        });
      }
      if (notification) {
        notification.onclick = function(){
            window.focus();
            if (this.cancel) this.cancel();
        };
      }
    },

    colorInterpolate: function(opts) {
        // Takes opts
        // color1, color2 - hex without # e.g. 'FF0000'
        // min, max, value, from which ratio is calculated
        // OR
        // ratio
        var color1 = opts.color1;
        var color2 = opts.color2;
        var min = opts.min || 0;
        var max = opts.max || 100;
        var value = opts.value || 0;
        if (value < min) value = min;
        if (value > max) value = max;
        var ratio = 0.0;
        if (opts.value!=null) {
            ratio = (value - min) / (max - min);
        } else if (opts.ratio) {
            ratio = opts.ratio;
        }
        var hex = function(x) {
            x = x.toString(16);
            return (x.length == 1) ? '0' + x : x;
        };

        var r = Math.ceil(parseInt(color2.substring(0,2), 16) * ratio + parseInt(color1.substring(0,2), 16) * (1-ratio));
        var g = Math.ceil(parseInt(color2.substring(2,4), 16) * ratio + parseInt(color1.substring(2,4), 16) * (1-ratio));
        var b = Math.ceil(parseInt(color2.substring(4,6), 16) * ratio + parseInt(color1.substring(4,6), 16) * (1-ratio));
        var res_c = hex(r) + hex(g) + hex(b);
        return res_c;
    },

    url_summary(url) {
        url = url.replace('http://','');
        url = url.replace('https://','');
        url = url.replace('www.','');
        return util.truncate(url, 25);
    },


    updateByKey: function(item, items, _keyattr, _do_delete) {
        let success = false;
        var do_delete = _do_delete || false;
        var keyattr = _keyattr || "key";
        let i = findIndexById(items, item[keyattr], keyattr);
        if (i > -1) {
            if (do_delete) items.splice(i, 1);
            else items[i] = item;
            success = true
        } else {
            items.push(item)
            success = true
        }
        return success;
    },

    _render: function(html, directive, data) {
        compiled = $(html).compile(directive);
        var el = $(html).render(data, compiled);
        return el;
    },

    contains: function(list, val) {
        for (k = 0; k < list.length; k++) {
            if (val == list[k]) {
                return 1;
            }
        }
        return 0;
    },

    baseUrl: function() {
        var base_url = location.protocol + '//' + location.host + location.pathname;
        return base_url;
    },

    nowTimestamp: function() {
        // Millis
        return Date.now();
    },

    printDate: function(ms, _format) {
        if (ms == null) return "";
        // Using moment.js to print local date/times
        let format = _format == null ? "YYYY-MM-DD" : _format;
        var dt = moment(parseInt(ms));
        return dt.format(format);
    },

    printTime: function(date_object) {
        var dt = moment(date_object);
        return dt.format("HH:mm");
    },

    daysInMonth: function(month,year) {
        return new Date(year, month, 0).getDate();
    },

    dayOfYear: function(now) {
        let start = new Date(now.getFullYear(), 0, 0);
        let diff = now - start;
        let oneDay = 1000 * 60 * 60 * 24;
        return Math.floor(diff / oneDay);
    },

    iso_from_date(d) {
        let year = d.getFullYear();
        let day = d.getDate();
        let month = d.getMonth() + 1;
        if (month < 10) month = '0'+month;
        if (day < 10) day = '0'+day;
        return year+'-'+month+'-'+day;
    },

    printDateObj: function(date, _timezone, opts) {
        if (_timezone && moment) {
            // Using moment.js to print local date/times
            let dt = moment.tz(date.getTime(), _timezone);
            let format = "YYYY-MM-DD";
            if (opts) {
                if (opts.format) format = opts.format;
                else if (opts['_with_time']) format = "YYYY-MM-DD HH:mm";
            }
            return dt.format(format);
        } else {
            if (date != null) {
                return util.iso_from_date(date);
            } else return "--";
        }
    },

    printISODate: function(ts) {
        let newDate = new Date();
        newDate.setTime(ts*1000);
        return util.iso_from_date(newDate);
    },

    date_from_iso(iso_str) {
        return new Date(iso_str + 'T00:00:00'); // Force interpretation as local timezone
    },

    timestamp: function() {
        // Seconds
        return parseInt(new Date().getTime() / 1000);
    },

    printMonthDay: function(ts) {
        var newDate = new Date();
        newDate.setTime(ts*1000);
        var month = newDate.getMonth()+1;
        var day = newDate.getDate();
        return day+'/'+month;
    },

    startAutomaticTimestamps: function(_tz, _interval) {
        var tz = _tz || "UTC";
        var interval = _interval || 20; // Secs
        util.printTimestampsNow(null, null, null, tz);
        var interval_id = setInterval(function() {
            util.printTimestampsNow(null, null, null, tz);
        }, 1000*interval);
        return interval_id;
    },

    from_now(ms) {
        return moment(ms).fromNow();
    },

    hours_until(ms) {
        let now = new Date().getTime();
        let secs_until = Math.round((ms - now)/1000);
        return parseInt(secs_until / 60.0 / 60.0);
    },

    timesince(ms) {
        let LEVELS = [
            { label: "second", cutoff: 60, recent: true, seconds: 1 },
            { label: "minute", cutoff: 60, seconds: 60 },
            { label: "hour", cutoff: 24, seconds: 60*60 },
            { label: "day", cutoff: 30, seconds: 60*60*24 }
        ];
        let text;
        let recent = false;
        let very_old = false;
        let now = new Date().getTime();
        let secs_since = Math.round((now - ms)/1000);
        let handled = false;
        let full_date = util.printDate(ms);
        let past = secs_since > 0;
        let diff_label = past ? "ago" : "from now";
        for (let i=0; i<LEVELS.length; i++) {
            let level = LEVELS[i];
            let units_diff = Math.abs(secs_since / level.seconds);
            if (units_diff < level.cutoff) {
                if (level.recent) recent = true;
                text = parseInt(units_diff) + " " + level.label + "(s) " + diff_label;
                handled = true;
                break;
            }
        }
        if (!handled) {
            very_old = true;
            text = full_date;
        }
        return { very_old, text, full_date, recent };
    },

    printTimestampsNow: function(_smart, _row_sel, _recent_class, _timezone) {
        var row_sel = _row_sel || 'li';
        var recent_class = _recent_class || 'list-group-item-info';
        var smart = smart == null ? true : _smart;
        $('[data-ts]').each(function() {
            var ts = $(this).attr('data-ts');
            if (smart) {
                let {very_old, text} = util.timesince(ts);
                if (!handled) {
                    // Remove _ts since this is too old for relative time
                    $(this).removeAttr('data-ts');
                }
            } else text = full_date;
            $(this).text(text).attr('title', full_date);
        });
    },

    printPercent: function(dec, opts) {
        if (dec == Infinity || isNaN(dec)) {
            if (opts && opts.default) return opts.default;
            else return "N/A";
        }
        return parseInt(dec*100) + "%";
    },

    uppercaseSlug: function(str) {
        return str.replace(/[^A-Z0-9]+/ig, "_").toUpperCase();
    },

    truncate: function(s, _chars) {
        var chars = _chars || 30;
        if (s.length > chars) return s.substring(0, _chars) + '...';
        else return s;
    },

    getParameterByName: function(name, _default) {
        name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
            results = regex.exec(location.search);
        return results == null ? _default || "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    },

    getHash: function(default_value) {
        return window.location.hash.substr(1) || default_value
    },

    randomId: function(length) {
        var text = "";
        var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
        for( var i=0; i < length; i++ )
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        return text;
    },

    doOnKeypress: function(keycodes, fn) {
        if (!(keycodes instanceof Array)) keycodes = [keycodes];
        $(document).keyup(function(e) {
            if (keycodes.indexOf(e.keyCode) > -1 && fn) { fn(); }
        });
    },

    mergeObject: function(obj1, obj2) {
        // Merge obj2 into obj1
        for (var key in obj2) {
            if (obj2.hasOwnProperty(key)) {
                obj1[key] = obj2[key];
            }
        }
    },

    arrToObj: function(arr, keyname) {
        var obj = {};
        arr.forEach(function(item, i, arr) {
            obj[item[keyname]] = item;
        });
        return obj;
    },

    printFilesize: function(bytes) {
        var MB = 1000000, KB = 1000;
        if (bytes != null) {
            if (bytes > MB) return (bytes/MB).toFixed(1) + ' MiB';
            else if (bytes > KB) return (bytes/KB).toFixed(1) + ' KiB';
            else return (bytes).toFixed(1) + ' bytes';
        } else return "--";
    },

    dateToTimestamp: function(date_string) {
        var dc = date_string.split('/');
        var date = new Date(dc[2], dc[0], dc[1]);
        console.log(date.getTime());
        return date.getTime();
    },

    addEvent: function(element, eventName, callback) {
        if (element.addEventListener) {
            element.addEventListener(eventName, callback, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + eventName, callback);
        } else {
            element["on" + eventName] = callback;
        }
    },

    applySentenceCase: function(str) {
        return str.replace(/.+?[\.\?\!](\s|$)/g, function (txt) {
            return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
        });
    },

    float2rat: function(x) {
        var tolerance = 1.0E-6;
        var h1=1; var h2=0;
        var k1=0; var k2=1;
        var b = x;
        do {
            var a = Math.floor(b);
            var aux = h1; h1 = a*h1+h2; h2 = aux;
            aux = k1; k1 = a*k1+k2; k2 = aux;
            b = 1/(b-a);
        } while (Math.abs(x-h1/k1) > x*tolerance);

        return h1+":"+k1;
    },

    stripNonNumbers: function(text) {
        return text.replace(/[^0-9]*/g, '');
    },

    stripSpaces: function(text) {
        return text.replace(/ /g,'');
    },

    strip: function(text) {
        return String(text).replace(/^\s+|\s+$/g, '');
    },

    replaceAt: function(index, s, character) {
        return s.substr(0, index) + character + s.substr(index+character.length);
    },

    countChars: function(s, character) {
        return s.split(character).length - 1;
    },

    initAppCache: function() {
        appCache = window.applicationCache;
        appCache.addEventListener('updateready', function(e) {
            if (appCache.status == appCache.UPDATEREADY) {
                // Browser downloaded a new app cache.
                // Swap it in and reload the page to get the new hotness.
                appCache.swapCache();
                var r = confirm('A new version of this site is available... Please reload now');
                if (r) location.reload(true);
            }
        }, false);
        var status;
        switch (appCache.status) {
            case appCache.UNCACHED: // UNCACHED == 0
                status = 'UNCACHED';
                break;
            case appCache.IDLE: // IDLE == 1
                status = 'IDLE';
                break;
            case appCache.CHECKING: // CHECKING == 2
                status = 'CHECKING';
                break;
            case appCache.DOWNLOADING: // DOWNLOADING == 3
                status = 'DOWNLOADING';
                break;
            case appCache.UPDATEREADY: // UPDATEREADY == 4
                status = 'UPDATEREADY';
                break;
            case appCache.OBSOLETE: // OBSOLETE == 5
                status = 'OBSOLETE';
                break;
            default:
                status = 'UKNOWN CACHE STATUS';
                break;
        };
        console.log("[ AppCache ] Status: " + status);
    },

    countWithCeiling: function(count, ceiling) {
        if (count == ceiling) return count + "+";
        else return count;
    },

    arrEquals: function(array, array2) {
        // if the other array is a falsy value, return
        if (!array)
            return false;

        // compare lengths - can save a lot of time
        if (array2.length != array.length)
            return false;

        for (var i = 0, l=array2.length; i < l; i++) {
            // Check if we have nested arrays
            if (array2[i] instanceof Array && array[i] instanceof Array) {
                // recurse into the nested arrays
                if (!array2[i].equals(array[i]))
                    return false;
            }
            else if (array2[i] != array[i]) {
                // Warning - two different object instances will never be equal: {x:20} != {x:20}
                return false;
            }
        }
        return true;
    },

    stripSymbols: function(text) {
        return text.replace(/[^A-Za-z 0-9]*/g, '');
    },

    randomInt: function(min, max) {
        return Math.floor((Math.random() * max) + min);
    },

    emptyArray: function(len, item) {
        var item = item === undefined ? null : item;
        var arr = [];
        for (var i=0; i<len; i++) {
            arr.push(item);
        }
        return arr;
    },

    clone: function(obj) {
        var o2 = {};
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                o2[key] = obj[key];
            }
        }
        return o2;
    },

    getRandomColor: function() {
        var letters = '0123456789ABCDEF'.split('');
        var color = '#';
        for (var i = 0; i < 6; i++ ) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    },

    average: function(arr) {
        let {sum, count} = util.sum(arr);
        return count > 0 ? sum / count : 0;
    },

    sum: function(arr) {
        // Of non-null
        let sum = 0;
        let count = 0;
        if (arr.length > 0) {
            for (let i = 0; i < arr.length; i++) {
                if (arr[i] != null) {
                    sum += arr[i];
                    count += 1;
                }
            }
        }
        return {sum, count};
    },

    capitalize: function(s) {
        if (s==null) return null;
        else {
            s = s.toLowerCase();
            return s.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
        }
    },

    dayDiff: function(firstDate, secondDate) {
        let oneDay = 24*60*60*1000; // hours*minutes*seconds*milliseconds
        let diffDays = Math.round((firstDate.getTime() - secondDate.getTime())/(oneDay));
        return diffDays;
    },

    dateOffset: function(oldDate, _days, _months, _years) {
        var days = _days || 0;
        var months = _months || 0;
        var years = _years || 0;
        return new Date(oldDate.getFullYear()+years,oldDate.getMonth()+months,oldDate.getDate()+days);
    },

    catchJSErrors: function() {
        window.onerror = function(msg, url, line, col, error) {
           // Note that col & error are new to the HTML 5 spec and may not be
           // supported in every browser.  It worked for me in Chrome.
           var extra = !col ? '' : '\ncolumn: ' + col;
           extra += !error ? '' : '\nerror: ' + error;

           // You can view the information in an alert to see things working like this:
           alert("An error has occurred. Share this with the Echo Development team for assistance: " + msg + "\nurl: " + url + "\nline: " + line + extra);

           var suppressErrorAlert = true;
           // If you return true, then error alerts (like in older versions of
           // Internet Explorer) will be suppressed.
           return suppressErrorAlert;
        };
    },

    toggleInList: function(list, item) {
        var i = list.indexOf(item);
        if (i > -1) list.splice(i, 1);
        else list.push(item);
        return list;
    },

    stringToColor: function(str) {
        // str to hash
        for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
        // int/hash to hex
        for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2));
        return colour;
    },

    lookupDict: function(itemlist, _keyprop) {
        var keyprop = _keyprop || 'id';
        var lookup = {}
        itemlist.forEach(function(item, i, arr) {
            lookup[item[keyprop]] = item;
        });
        return lookup;
    },

    flattenDict: function(dict) {
        let list = [];
        for (var key in dict) {
            if (dict.hasOwnProperty(key)) {
                list.push(dict[key]);
            }
        }
        return list;
    },

    fromCents: function(cents) {
        return cents / 100.0;
    },

    toCents: function(units) {
        units = units.replace(',','');
        return parseFloat(units) * 100.0;
    },

    fixedNumber: function(num, _decimals) {
        var decimals = _decimals == null ? 2 : _decimals;
        return parseFloat(Math.round(num * 100) / 100).toFixed(decimals);
    },

    numberWithCommas: function(x) {
        var parts = x.toString().split(".");
        parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        return parts.join(".");
    },

    serializeObject: function(jqel) {
        var o = {};
        var a = jqel.serializeArray();
        $.each(a, function() {
            if (o[this.name] !== undefined) {
                if (!o[this.name].push) {
                    o[this.name] = [o[this.name]];
                }
                o[this.name].push(this.value || '');
            } else {
                o[this.name] = this.value || '';
            }
        });
        return o;
    },

    type_check(value, type) {
        // Type is a string matching google visualization types
        // Returns value standardized to given type
        if (type == "number") value = parseFloat(value);
        return value;
    },

    set_title(title) {
        if (title != null) title = title + " | Flow";
        else title = "Flow";
        document.title = title;
    },

    spread_array(obj, from_prop, to_prop, n) {
        if (obj[from_prop]) {
            for (let i=0; i<n; i++) {
                let key = to_prop + (i+1);
                obj[key] = obj[from_prop][i];
            }
        }
        return obj;
    },

    transp_color(hex_color, brightness) {
        let opacity_prefix = (parseInt(255*brightness)).toString(16).toUpperCase();
        if (hex_color.startsWith('#')) hex_color = hex_color.slice(1).toUpperCase();
        return `#${opacity_prefix}${hex_color}`;
    },

    hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null;
    },

    secsToDuration: function(secs, opts) {
        let labels = ["hour", "minute", "second"];
        let d = moment.duration(secs, "seconds");
        let hours = parseInt(d.asHours());
        let mins = parseInt(d.minutes());
        let _secs = parseInt(d.seconds());
        let s = [];
        let levels = [hours, mins];
        let no_seconds = opts && opts.no_seconds
        let zero_text = opts && opts.zero_text;
        if (!no_seconds) levels.push(_secs);
        levels.forEach(function(p, i) {
            let label = labels[i];
            if (p > 0) {
                let piece = p + " " + label;
                if (p > 1) piece += "s";
                s.push(piece);
            }
        });
        if (s.length > 0) return s.join(', ');
        else return zero_text || "0 seconds";
    },

    user_agent_mobile: function() {
        var check = false;
        (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
        return check;
    }

}

module.exports = util;