function isDefined(val)
{
  return typeof val !== "undefined";
}

function isUndefined(val)
{
  return typeof val === "undefined";
}

function isUndefinedOrNull(val)
{
  return !isDefined(val) || val === null || val === '';
}

function isDefinedAndNotNull(val)
{
  return isDefined(val) && val !== null;
}

function isDefinedAndNull(val)
{
  return isDefined(val) && val === null;
}

function isDefinedAndTrue(val)
{
  return isDefined(val) && val !== null && val === true;
}

angular.isUndefinedOrNull = isUndefinedOrNull;
angular.isDefinedAndNotNull = isDefinedAndNotNull;
angular.isDefinedAndNull = isDefinedAndNull;
angular.isDefinedAndTrue = isDefinedAndTrue;

var GameOn = window.GameOn || {};
GameOn.utils = {};

(function () {
  if (window.console && window.console.log && GameOn.is_uiwebview) {
    window.console.log = function () {
      window.NativeBridge.call('consoleLog', {args: arguments});
    };
  }
})();

// prevent console.log errors on IE
window.console = window.console || {
  log: function () {
  }
};

GameOn.utils.uuidgen = function () {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
  }

  return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
};

GameOn.utils.median = function (values) {
  if (values.length === 0) {
    return 0;
  }

  values.sort(function (a, b) {
    return a - b;
  });

  var half = Math.floor(values.length / 2);

  if (values.length % 2) {
    return values[half];
  }

  return (values[half - 1] + values[half]) / 2.0;
};

GameOn.utils.ucfirst = function (string) {
  var capitalizeFirstLetter = function () {
    return string.charAt(0).toUpperCase() + string.slice(1);
  };
  return capitalizeFirstLetter(string);
};

GameOn.utils.getFunctionArgs = function (func) {

  // First match everything inside the function argument parens.
  var matches = func.toString().match(/function\s*.*?\(([^)]*)\)/);
  if (matches) {
    var args = matches[1];
    // Split the arguments string into an array comma delimited.
    return args.split(',').map(function (arg) {
      // Ensure no inline comments are parsed and trim the whitespace.
      return arg.replace(/\/\*.*\*\//, '').trim();
    }).filter(function (arg) {
      // Ensure no undefined values are added.
      return arg;
    });
  } else {
    console.log(func.toString());
    return [];
  }
};

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (val) {
    var i = this.length;
    while (i--) {
      if (this[i] === val) {
        return i;
      }
    }
    return -1;
  };
}

GameOn.utils.pad = function (n, width, z) {
  z = z || '0';
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
};

GameOn.utils.getInvalidEmailAddressChars = function (email_address) {
  const invalid_char_re = /[^\x21-\x7E]/;
  const value = String(email_address || '');
  const invalid_chars = [];
  const seen = new Set();

  for (let i = 0; i < value.length; i++) {
    const ch = value[i];
    if (invalid_char_re.test(ch) && !seen.has(ch)) {
      seen.add(ch);
      invalid_chars.push(ch);
    }
  }

  return invalid_chars;
};

GameOn.utils.validateEmailAddress = function (email_address) {
  const email_re = /^(?=.{1,254}$)(?=.{1,64}@)(?:[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+(?:\.[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+)*|"(?:[\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|\\[\x01-\x09\x0b\x0c\x0d-\x7f])*")@(?:(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)+[a-z]{2,}|\[(?:\d{1,3}\.){3}\d{1,3}\])$/i;

  return email_re.test(String(email_address).trim());
};

GameOn.utils.htmlDecode = function (input) {
  var e = document.createElement('div');
  e.innerHTML = input;
  return e.childNodes[0].nodeValue;
};

GameOn.utils.mergeWithDefaults = function (defaults, options) {
  'use strict';
  if (options === 'undefined' || options === null) {
    return defaults;
  }
  return angular.extend(defaults, options);
};

GameOn.utils.isPromise = function (promise) {
  'use strict';
  return promise &&
      angular.isFunction(promise.then) &&
      angular.isFunction(promise['finally']);
};

GameOn.utils.videoTutorialsAllowed = function () {
  var allowed_hosts = [
    'barca.game-on.eu',
    'cloud.game-on.eu',
    'mfc.game-on.eu'
  ];

  return allowed_hosts.indexOf(window.location.hostname.toLowerCase()) !== -1;
};

GameOn.utils.isLogoRequired = function () {
  var required_hosts = [
    'mfc.game-on.eu',
    '192.168.100.51' // MFC local address
  ];

  return required_hosts.indexOf(window.location.hostname.toLowerCase()) !== -1;
};

GameOn.utils.logoCorners = [
  { value: 'topright', label: 'Top Right' },
  { value: 'bottomright', label: 'Bottom Right' },
  { value: 'bottomleft', label: 'Bottom Left' }
];

GameOn.utils.matchTypes = [
  { name: "2 x 15'", type: '2x15' },
  { name: "2 x 20'", type: '2x20' },
  { name: "2 x 25'", type: '2x25' },
  { name: "2 x 30'", type: '2x30' },
  { name: "2 x 35'", type: '2x35' },
  { name: "2 x 40'", type: '2x40' },
  { name: "2 x 40' + 2 x 10' extra time", type: '2x40+2x10' },
  { name: "2 x 45'", type: '2x45' },
  { name: "2 x 45' + 2 x 15' extra time", type: '2x45+2x15' },
  { name: "2 x 50'", type: '2x50' },
  { name: "3 x 15'", type: '3x15' },
  { name: "3 x 20'", type: '3x20' },
  { name: "3 x 25'", type: '3x25' },
  { name: "3 x 30'", type: '3x30' },
  { name: "3 x 35'", type: '3x35' },
  { name: "3 x 45'", type: '3x45' },
  { name: "4 x 10'", type: '4x10' },
  { name: "4 x 15'", type: '4x15' },
  { name: "4 x 17.5'", type: '4x17.5' },
  { name: "4 x 20'", type: '4x20' },
  { name: "4 x 25'", type: '4x25' },
  { name: "4 x 30'", type: '4x30' },
  { name: "5 x 15'", type: '5x15' },
  { name: "6 x 15'", type: '6x15' }
];

GameOn.utils.getKickOffs = function (match_type) {
  var match_types_with_extra_time = ['2x45+2x15', '2x40+2x10'];

  if (match_types_with_extra_time.includes(match_type)) {
    return [
      'Kick-off 1st half',
      'Kick-off 2nd half',
      'Kick-off 1st half extra time',
      'Kick-off 2nd half extra time',
    ];
  }

  if (match_type.match(/^2x/)) {
    return ['Kick-off 1st half', 'Kick-off 2nd half'];
  }

  var stages = match_type.match(/(\d+(\.\d+)?)x/);
  if (stages) {
    var count = parseFloat(stages[1]);
    return Array.from({ length: count }, (_, i) => `Kick-off ${i + 1}/${count}`);
  }
};

GameOn.utils.getAutofollowPresets = function() {
  return [{
    name: 'Tactical Wide'
  }, {
    name: 'Tactical Close'
  }, {
    name: 'TV'
  }];
};

GameOn.utils.getFollowItemForAF = function (autofollow) {
  const presets = GameOn.utils.getAutofollowPresets();
  if (autofollow === presets[0].name) {
    return "overview_ai";
  }
  else if (autofollow === presets[1].name) {
    return "overview_ai_adaptive_tilt_fix";
  }
  else if (autofollow === presets[2].name) {
    return "overview_ai_no_tilt_fix";
  }
  return "overview_ai_adaptive_tilt_fix";
};

GameOn.utils.tryGetFromLocalStorage = function (key, validOptions = [], fallback) {
  try {
    const rawValue = localStorage.getItem(key);
    if (!rawValue) return fallback;
    // Parse JSON if it was stored as an object/stringified value
    const value = JSON.parse(rawValue);
    return validOptions.some(opt => opt.value === value) ? value : fallback;
  } catch {
    return fallback;
  }
};

GameOn.utils.isMultiSensorPano = function (source) {
  return source.model?.type?.includes('pano') ?? false;
};

GameOn.utils.isBehindGoalPano = function (view) {
  const name = view.name.toLowerCase();
  return view.cameras.length >= 2 && (
      (
          (view.icon.y >= 0.25 && view.icon.y <= 0.75) &&
          (view.icon.x <= 0.25 || view.icon.x >= 0.75)
      ) ||
      (
          name.indexOf('nord') > -1  ||
          name.indexOf('sud') > -1 ||
          name.indexOf('gauche') > -1 ||
          name.indexOf('droite') > -1
      ));
};

GameOn.utils.isHalfwayPano = function (view) {
  const name = view.name.toLowerCase();
  return view.cameras.length >= 2 && (
          (view.icon.y <= 0.25 || view.icon.y >= 0.75) &&
          (view.icon.x >= 0.25 && view.icon.x <= 0.75)
      );
};

GameOn.utils.findViewForSourceForCourt = function (source, court_id, views) {
  return _.find(views, function (view) {
    const courtMatches = court_id == null || view.court_id === court_id; // ignore if null or undefined
    if (GameOn.utils.isMultiSensorPano(source)) {
      return (
          view.cameras.length > 1 && view.cameras[0].name === source.name + '_0' && courtMatches
      );
    } else {
      return (
          view.cameras.length === 1 && view.cameras[0].name === source.name && courtMatches
      );
    }
  });
};

if (window.GameOn.is_uiwebview) {
  var console = {};
  console.log = function (text) {
    location.href = "log://" + text;
  };
  window.console = console;
}

GameOn.utils.convertToSlug = function (text) {
  'use strict';
  return text
      .toLowerCase()
      .replace(/ /g, '-')
      .replace(/[^\w-]+/g, '');
};

GameOn.utils.stripTrailingSlash = function (str) {
  if (str.substr(-1) === '/') {
    return str.substr(0, str.length - 1);
  }
  return str;
};

Object.byString = function (o, s) {
  'use strict';
  s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
  s = s.replace(/^\./, '');           // strip a leading dot
  var a = s.split('.');
  for (var i = 0, n = a.length; i < n; ++i) {
    var k = a[i];
    if (k in o) {
      o = o[k];
    } else {
      return;
    }
  }
  return o;
};

GameOn.utils.hashFromObj = function (obj) {
  'use strict';
  return GameOn.utils.sha1(JSON.stringify(obj));
};

GameOn.utils.sha1 = function (str) {
  //  discuss at: http://phpjs.org/functions/sha1/
  // original by: Webtoolkit.info (http://www.webtoolkit.info/)
  // improved by: Michael White (http://getsprink.com)
  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  //    input by: Brett Zamir (http://brett-zamir.me)
  //   example 1: sha1('Kevin van Zonneveld');
  //   returns 1: '54916d2e62f65b3afa6e192e6a601cdbe5cb5897'
  'use strict';

  var rotate_left = function (n, s) {
    var t4 = (n << s) | (n >>> (32 - s));
    return t4;
  };

  /*var lsb_hex = function (val) {
   // Not in use; needed?
   var str="";
   var i;
   var vh;
   var vl;

   for ( i=0; i<=6; i+=2 ) {
   vh = (val>>>(i*4+4))&0x0f;
   vl = (val>>>(i*4))&0x0f;
   str += vh.toString(16) + vl.toString(16);
   }
   return str;
   };*/

  var cvt_hex = function (val) {
    var str = '';
    var i;
    var v;

    for (i = 7; i >= 0; i--) {
      v = (val >>> (i * 4)) & 0x0f;
      str += v.toString(16);
    }
    return str;
  };

  var blockstart;
  var i, j;
  var W = new Array(80);
  var H0 = 0x67452301;
  var H1 = 0xEFCDAB89;
  var H2 = 0x98BADCFE;
  var H3 = 0x10325476;
  var H4 = 0xC3D2E1F0;
  var A, B, C, D, E;
  var temp;

  // utf8_encode
  str = unescape(encodeURIComponent(str));
  var str_len = str.length;

  var word_array = [];
  for (i = 0; i < str_len - 3; i += 4) {
    j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 | str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3);
    word_array.push(j);
  }

  switch (str_len % 4) {
    case 0:
      i = 0x080000000;
      break;
    case 1:
      i = str.charCodeAt(str_len - 1) << 24 | 0x0800000;
      break;
    case 2:
      i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000;
      break;
    case 3:
      i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) <<
          8 | 0x80;
      break;
  }

  word_array.push(i);

  while ((word_array.length % 16) !== 14) {
    word_array.push(0);
  }

  word_array.push(str_len >>> 29);
  word_array.push((str_len << 3) & 0x0ffffffff);

  for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
    for (i = 0; i < 16; i++) {
      W[i] = word_array[blockstart + i];
    }
    for (i = 16; i <= 79; i++) {
      W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
    }

    A = H0;
    B = H1;
    C = H2;
    D = H3;
    E = H4;

    for (i = 0; i <= 19; i++) {
      temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B, 30);
      B = A;
      A = temp;
    }

    for (i = 20; i <= 39; i++) {
      temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B, 30);
      B = A;
      A = temp;
    }

    for (i = 40; i <= 59; i++) {
      temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B, 30);
      B = A;
      A = temp;
    }

    for (i = 60; i <= 79; i++) {
      temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B, 30);
      B = A;
      A = temp;
    }

    H0 = (H0 + A) & 0x0ffffffff;
    H1 = (H1 + B) & 0x0ffffffff;
    H2 = (H2 + C) & 0x0ffffffff;
    H3 = (H3 + D) & 0x0ffffffff;
    H4 = (H4 + E) & 0x0ffffffff;
  }

  temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
  return temp.toLowerCase();
};

//Scroll to top with a optional y offset and with a 75ms timeout.
GameOn.utils.scrollToTop = function (y) {
  setTimeout(function () {
    $('html,body').animate({scrollTop: y || 0}, 500);
  }, 75);
};

GameOn.utils.hexToRGBA = function (hex, alpha) {
  hex = parseInt(hex.replace("#", ""), 16);
  var r = hex >> 16;
  var g = hex >> 8 & 0xFF;
  var b = hex & 0xFF;
  return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
};

GameOn.utils.findActiveCameraIndex = function (cameraName, cameras) {
  for (var i = 0; i < cameras.length; i++) {
    if (cameras[i].name === cameraName) {
      return i;
    }
  }
  return -1;
};

GameOn.utils.xmlToJson = function (xml) {

  // Create the return object
  var obj = {};

  if (xml.nodeType === 1) { // element
    // do attributes
    if (xml.attributes.length > 0) {
      obj["@attributes"] = {};
      for (var j = 0; j < xml.attributes.length; j++) {
        var attribute = xml.attributes.item(j);
        obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
      }
    }
  } else if (xml.nodeType === 3) { // text
    obj = xml.nodeValue;
  }

  // do children
  if (xml.hasChildNodes()) {
    for (var i = 0; i < xml.childNodes.length; i++) {
      var item = xml.childNodes.item(i);
      var nodeName = item.nodeName;
      if (typeof (obj[nodeName]) === "undefined") {
        obj[nodeName] = xmlToJson(item);
      } else {
        if (typeof (obj[nodeName].push) === "undefined") {
          var old = obj[nodeName];
          obj[nodeName] = [];
          obj[nodeName].push(old);
        }
        obj[nodeName].push(xmlToJson(item));
      }
    }
  }
  return obj;
};

GameOn.utils.findKey = function (obj, value) {
  var key;

  _.each(_.keys(obj), function (k) {
    var v = obj[k];
    if (v === value) {
      key = k;
    }
  });

  return key;
};

GameOn.utils.isDialogOpen = function () {
  return $('body').hasClass('modal-open');
};

GameOn.utils.getRecordingTimeElapsedInSeconds = function (startTimestampUTC) {
  return Math.floor((Date.now() - startTimestampUTC) / 1000);
};

GameOn.utils.calcEpochInSeconds = function (timestamp) {
  var a = $.map(timestamp.split(/[^0-9]/), function(x) {
    return parseInt(x, 10);
  });
  var formatted_date = new Date(a[0], a[1]-1 || 0, a[2] || 1, a[3] || 0, a[4] || 0, a[5] || 0, a[6] || 0);
  return Date.parse(formatted_date) / 1000;
};

GameOn.utils.formatSecondsToMMSS = function (value) {
  var minutes = Math.floor(value / 60);
  var seconds = value - minutes * 60;
  return GameOn.utils.pad(minutes, 2) + ":" + GameOn.utils.pad(seconds, 2);
};

/*
 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
 */
function naturalSort(a, b) {
  var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g;
  // trim pre-post whitespace
  var sre = /^\s+|\s+$/g;
  // normalize all whitespace to single ' ' character
  var snre = /\s+/g;
  var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
  var hre = /^0x[0-9a-f]+$/i;
  var ore = /^0/;
  var i = function (s) {
    return (naturalSort.insensitive && ('' + s).toLowerCase() || '' + s).replace(sre, '');
  };
  // convert all to strings strip whitespace
  var x = i(a);
  var y = i(b);
  // chunk/tokenize
  var xN = x.replace(re, '\0$1\0').replace(/0$/, '').replace(/^0/, '').split('\0');
  var yN = y.replace(re, '\0$1\0').replace(/0$/, '').replace(/^0/, '').split('\0');
  // numeric, hex or date detection
  var xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x));
  var yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null;
  var normChunk = function (s, l) {
    // normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
    return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
  };

  // first try and sort Hex codes or Dates
  if (yD) {
    if (xD < yD) {
      return -1;
    } else if (xD > yD) {
      return 1;
    }
  }

  // natural sorting through split numeric strings and default strings
  for (var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
    var oFxNcL = normChunk(xN[cLoc] || '', xNl);
    var oFyNcL = normChunk(yN[cLoc] || '', yNl);
    // handle numeric vs string comparison - number < string - (Kyle Adams)
    if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
      return isNaN(oFxNcL) ? 1 : -1;
    }
    // if unicode use locale comparison
    if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
      var comp = oFxNcL.localeCompare(oFyNcL);
      return comp / Math.abs(comp);
    }
    if (oFxNcL < oFyNcL) {
      return -1;
    } else if (oFxNcL > oFyNcL) {
      return 1;
    }
  }
}

GameOn.utils.hexToRGB = function (H) {
  var r = 0, g = 0, b = 0;
  if (H.length === 4) {
    r = "0x" + H[1] + H[1];
    g = "0x" + H[2] + H[2];
    b = "0x" + H[3] + H[3];
  } else if (H.length === 7) {
    r = "0x" + H[1] + H[2];
    g = "0x" + H[3] + H[4];
    b = "0x" + H[5] + H[6];
  }
  return [r, g, b];
};

GameOn.utils.RGBToHSL = function (r, g, b) {
  // Make r, g, and b fractions of 1
  r /= 255;
  g /= 255;
  b /= 255;

  // Find greatest and smallest channel values
  var cmin = Math.min(r, g, b);
  var cmax = Math.max(r, g, b);
  var delta = cmax - cmin;
  var h = 0;
  var s = 0;
  var l = 0;

  // Calculate hue, no difference
  if (delta === 0) {
    h = 0;
    // Red is max
  } else if (cmax === r) {
    h = ((g - b) / delta) % 6;
    // Green is max
  } else if (cmax === g) {
    h = (b - r) / delta + 2;
    // Blue is max
  } else {
    h = (r - g) / delta + 4;
  }

  h = Math.round(h * 60);

  // Make negative hues positive behind 360°
  if (h < 0) {
    h += 360;
  }

  // Calculate lightness
  l = (cmax + cmin) / 2;

  // Calculate saturation
  s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));

  // Multiply l and s by 100
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return [h, s, l];
};

GameOn.utils.hexToHSL = function (hex_color) {
  var rgb = GameOn.utils.hexToRGB(hex_color);
  return GameOn.utils.RGBToHSL(rgb[0], rgb[1], rgb[2]);
};

GameOn.utils.HSLToHex = function (h, s, l) {
  s /= 100;
  l /= 100;

  var c = (1 - Math.abs(2 * l - 1)) * s,
      x = c * (1 - Math.abs((h / 60) % 2 - 1)),
      m = l - c / 2,
      r = 0,
      g = 0,
      b = 0;

  if (0 <= h && h < 60) {
    r = c;
    g = x;
    b = 0;
  } else if (60 <= h && h < 120) {
    r = x;
    g = c;
    b = 0;
  } else if (120 <= h && h < 180) {
    r = 0;
    g = c;
    b = x;
  } else if (180 <= h && h < 240) {
    r = 0;
    g = x;
    b = c;
  } else if (240 <= h && h < 300) {
    r = x;
    g = 0;
    b = c;
  } else if (300 <= h && h < 360) {
    r = c;
    g = 0;
    b = x;
  }
  // Having obtained RGB, convert channels to hex
  r = Math.round((r + m) * 255).toString(16);
  g = Math.round((g + m) * 255).toString(16);
  b = Math.round((b + m) * 255).toString(16);

  // Prepend 0s, if necessary
  if (r.length === 1) {
    r = "0" + r;
  }

  if (g.length === 1) {
    g = "0" + g;
  }

  if (b.length === 1) {
    b = "0" + b;
  }

  return "#" + r + g + b;
};

GameOn.utils.remap = function(oldValue, oldMin, oldMax, newMin, newMax) {
  var oldRange = oldMax - oldMin;
  var newRange = newMax - newMin;

  return ((oldValue - oldMin) * newRange / oldRange) + newMin;
};

GameOn.utils.compute_overlap = function (start1, end1, start2, end2) {
  return Math.max(0, Math.min(end1, end2) - Math.max(start1, start2));
};

String.prototype.getInitials = function(glue){
  if (typeof glue === "undefined") {
    glue = true;
  }

  var initials = this.replace(/[^a-zA-Z- ]/g, "").match(/\b\w/g);

  if (glue) {
    return initials.join('');
  }

  return  initials;
};

String.prototype.capitalize = function(){
  return this.toLowerCase().replace( /\b\w/g, function (m) {
    return m.toUpperCase();
  });
};
