text: var Utils = {
      isUrl: function (val) {
          return /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@\-\/]))?/.test(val);
      },
  
      isTag: function(val){
          return /^<\/?[\w\s="/.':;#-\/\?]+>/gi.test(val);
      },
  
      isColor: function (val) {
          return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(val);
      },
  
      isEmbedObject: function(val){
          var embed = ["iframe", "object", "embed", "video"];
          var result = false;
          $.each(embed, function(){
              if (val.indexOf(this) !== -1) {
                  result = true;
              }
          });
          return result;
      },
  
      isVideoUrl: function(val){
          return /youtu\.be|youtube|vimeo/gi.test(val);
      },
  
      isDate: function(val, format){
          var result;
  
          if (typeof val === "object" && Utils.isFunc(val['getMonth'])) {
              return true;
          }
  
          if (Utils.isValue(format)) {
              result = String(val).toDate(format);
          } else {
              result = String(new Date(val));
          }
  
          return result !== "Invalid Date";
      },
  
      isDateObject: function(v){
          return typeof v === 'object' && v['getMonth'] !== undefined;
      },
  
      isInt: function(n){
          return Number(n) === n && n % 1 === 0;
      },
  
      isFloat: function(n){
          return Number(n) === n && n % 1 !== 0;
      },
  
      isTouchDevice: function() {
          return (('ontouchstart' in window)
              || (navigator.MaxTouchPoints > 0)
              || (navigator.msMaxTouchPoints > 0));
      },
  
      isFunc: function(f){
          return Utils.isType(f, 'function');
      },
  
      isObject: function(o){
          return Utils.isType(o, 'object')
      },
  
      isArray: function(a){
          return Array.isArray(a);
      },
  
      isType: function(o, t){
          if (o === undefined || o === null) {
              return false;
          }
  
          if (typeof o === t) {
              return o;
          }
  
          if (Utils.isTag(o) || Utils.isUrl(o)) {
              return false;
          }
  
          if (typeof window[o] === t) {
              return window[o];
          }
  
          if (typeof o === 'string' && o.indexOf(".") === -1) {
              return false;
          }
  
          if (typeof o === 'string' && o.indexOf(" ") !== -1) {
              return false;
          }
  
          if (typeof o === 'string' && o.indexOf("(") !== -1) {
              return false;
          }
  
          if (typeof o === 'string' && o.indexOf("[") !== -1) {
              return false;
          }
  
          var ns = o.split(".");
          var i, context = window;
  
          for(i = 0; i < ns.length; i++) {
              context = context[ns[i]];
          }
  
          return typeof context === t ? context : false;
      },
  
      isMetroObject: function(el, type){
          var $el = $(el), el_obj = $el.data(type);
          if ($el.length === 0) {
              console.log(type + ' ' + el + ' not found!');
              return false;
          }
  
          if (el_obj === undefined) {
              console.log('Element not contain role '+ type +'! Please add attribute data-role="'+type+'" to element ' + el);
              return false;
          }
  
          return true;
      },
  
      isJQueryObject: function(el){
          return (typeof jQuery === "function" && el instanceof jQuery);
      },
  
      embedObject: function(val){
          if (typeof  val !== "string" ) {
              val = Utils.isJQueryObject(val) ? val.html() : val.innerHTML;
          }
          return "<div class='embed-container'>" + val + "</div>";
      },
  
      embedUrl: function(val){
          if (val.indexOf("youtu.be") !== -1) {
              val = "https://www.youtube.com/embed/" + val.split("/").pop();
          }
          return "<div class='embed-container'><iframe src='"+val+"'></iframe></div>";
      },
  
      secondsToTime: function(secs) {
          var hours = Math.floor(secs / (60 * 60));
  
          var divisor_for_minutes = secs % (60 * 60);
          var minutes = Math.floor(divisor_for_minutes / 60);
  
          var divisor_for_seconds = divisor_for_minutes % 60;
          var seconds = Math.ceil(divisor_for_seconds);
  
          return {
              "h": hours,
              "m": minutes,
              "s": seconds
          };
      },
  
      hex2rgba: function(hex, alpha){
          var c;
          alpha = isNaN(alpha) ? 1 : alpha;
          if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
              c= hex.substring(1).split('');
              if(c.length=== 3){
                  c= [c[0], c[0], c[1], c[1], c[2], c[2]];
              }
              c= '0x'+c.join('');
              return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255].join(',')+','+alpha+')';
          }
          throw new Error('Hex2rgba error. Bad Hex value');
      },
  
      random: function(from, to){
          return Math.floor(Math.random()*(to-from+1)+from);
      },
  
      uniqueId: function () {
          "use strict";
          var d = new Date().getTime();
          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
              var r = (d + Math.random() * 16) % 16 | 0;
              d = Math.floor(d / 16);
              return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
          });
      },
  
      elementId: function(prefix){
          return prefix+"-"+(new Date()).getTime()+Utils.random(1, 1000);
      },
  
      secondsToFormattedString: function(time){
          var sec_num = parseInt(time, 10);
          var hours   = Math.floor(sec_num / 3600);
          var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
          var seconds = sec_num - (hours * 3600) - (minutes * 60);
  
          if (hours   < 10) {hours   = "0"+hours;}
          if (minutes < 10) {minutes = "0"+minutes;}
          if (seconds < 10) {seconds = "0"+seconds;}
  
          return [hours, minutes, seconds].join(":");
      },
  
      callback: function(f, args, context){
          return Utils.exec(f, args, context);
      },
  
      func: function(f){
          return new Function("a", f);
      },
  
      exec: function(f, args, context){
          var result;
          if (f === undefined || f === null) {return false;}
          var func = Utils.isFunc(f);
          if (func === false) {
              func = Utils.func(f);
          }
  
          try {
              result = func.apply(context, args);
          } catch (err) {
              result = null;
              if (METRO_THROWS === true) {
                  throw err;
              }
          }
          return result;
      },
  
      isOutsider: function(el) {
          el = Utils.isJQueryObject(el) ? el : $(el);
          var rect;
          var clone = el.clone();
  
          clone.removeAttr("data-role").css({
              visibility: "hidden",
              position: "absolute",
              display: "block"
          });
          el.parent().append(clone);
  
          rect = clone[0].getBoundingClientRect();
          clone.remove();
  
          return (
              rect.top >= 0 &&
              rect.left >= 0 &&
              rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
              rect.right <= (window.innerWidth || document.documentElement.clientWidth)
          );
      },
  
      inViewport: function(el){
          var rect = Utils.rect(el);
  
          return (
              rect.top >= 0 &&
              rect.left >= 0 &&
              rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
              rect.right <= (window.innerWidth || document.documentElement.clientWidth)
          );
      },
  
      rect: function(el){
          if (typeof jQuery === "function" && el instanceof jQuery) {
              el = el[0];
          }
  
          return el.getBoundingClientRect();
      },
  
      objectLength: function(obj){
          return Object.keys(obj).length;
      },
  
      percent: function(total, part, round_value){
          if (total === 0) {
              return 0;
          }
          var result = part * 100 / total;
          return round_value === true ? Math.round(result) : Math.round(result * 100) / 100;
      },
  
      camelCase: function(str){
          return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
      },
  
      dashedName: function(str){
          return str.replace(/([A-Z])/g, function(u) { return "-" + u.toLowerCase(); });
      },
  
      objectShift: function(obj){
          var min = 0;
          $.each(obj, function(i){
              if (min === 0) {
                  min = i;
              } else {
                  if (min > i) {
                      min = i;
                  }
              }
          });
          delete obj[min];
  
          return obj;
      },
  
      objectDelete: function(obj, key){
          if (obj[key] !== undefined) delete obj[key];
      },
  
      arrayDelete: function(arr, val){
          arr.splice(arr.indexOf(val), 1);
      },
  
      arrayDeleteByKey: function(arr, key){
          arr.splice(key, 1);
      },
  
      nvl: function(data, other){
          return data === undefined || data === null ? other : data;
      },
  
      objectClone: function(obj){
          var copy = {};
          for(var key in obj) {
              if (obj.hasOwnProperty(key)) {
                  copy[key] = obj[key];
              }
          }
          return copy;
      },
  
      github: function(repo, callback){
          var that = this;
          $.ajax({
              url: 'https://api.github.com/repos/' + repo,
              dataType: 'jsonp'
          })
          .done(function(data){
              that.callback(callback, [data.data]);
          });
      },
  
      detectIE: function() {
          var ua = window.navigator.userAgent;
          var msie = ua.indexOf('MSIE ');
          if (msie > 0) {
              // IE 10 or older => return version number
              return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
          }
  
          var trident = ua.indexOf('Trident/');
          if (trident > 0) {
              // IE 11 => return version number
              var rv = ua.indexOf('rv:');
              return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
          }
  
          var edge = ua.indexOf('Edge/');
          if (edge > 0) {
              // Edge (IE 12+) => return version number
              return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
          }
  
          // other browser
          return false;
      },
  
      detectChrome: function(){
          return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
      },
  
      md5: function(s){
          return hex_md5(s);
      },
  
      encodeURI: function(str){
          return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']');
      },
  
      pageHeight: function(){
          var body = document.body,
              html = document.documentElement;
  
          return Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight );
      },
  
      cleanPreCode: function(selector){
          var els = Array.prototype.slice.call(document.querySelectorAll(selector), 0);
  
          els.forEach(function(el){
              var txt = el.textContent
                  .replace(/^[\r\n]+/, "")	// strip leading newline
                  .replace(/\s+$/g, "");
  
              if (/^\S/gm.test(txt)) {
                  el.textContent = txt;
                  return;
              }
  
              var mat, str, re = /^[\t ]+/gm, len, min = 1e3;
  
              while (mat = re.exec(txt)) {
                  len = mat[0].length;
  
                  if (len < min) {
                      min = len;
                      str = mat[0];
                  }
              }
  
              if (min === 1e3)
                  return;
  
              el.textContent = txt.replace(new RegExp("^" + str, 'gm'), "");
          });
      },
  
      coords: function(el){
          if (Utils.isJQueryObject(el)) {
              el = el[0];
          }
  
          var box = el.getBoundingClientRect();
  
          return {
              top: box.top + window.pageYOffset,
              left: box.left + window.pageXOffset
          };
      },
  
      positionXY: function(e, t){
          switch (t) {
              case 'client': return Utils.clientXY(e);
              case 'screen': return Utils.screenXY(e);
              case 'page': return Utils.pageXY(e);
              default: return {x: 0, y: 0}
          }
      },
  
      clientXY: function(e){
          return {
              x: e.changedTouches ? e.changedTouches[0].clientX : e.clientX,
              y: e.changedTouches ? e.changedTouches[0].clientY : e.clientY
          };
      },
  
      screenXY: function(e){
          return {
              x: e.changedTouches ? e.changedTouches[0].screenX : e.screenX,
              y: e.changedTouches ? e.changedTouches[0].screenY : e.screenY
          };
      },
  
      pageXY: function(e){
          return {
              x: e.changedTouches ? e.changedTouches[0].pageX : e.pageX,
              y: e.changedTouches ? e.changedTouches[0].pageY : e.pageY
          };
      },
  
      isRightMouse: function(e){
          return "which" in e ? e.which === 3 : "button" in e ? e.button === 2 : undefined;
      },
  
      hiddenElementSize: function(el, includeMargin){
          var clone = $(el).clone();
          clone.removeAttr("data-role").css({
              visibility: "hidden",
              position: "absolute",
              display: "block"
          });
          $("body").append(clone);
  
          if (includeMargin === undefined) {
              includeMargin = false;
          }
  
          var width = clone.outerWidth(includeMargin);
          var height = clone.outerHeight(includeMargin);
          clone.remove();
          return {
              width: width,
              height: height
          }
      },
  
      getStyle: function(el, pseudo){
          if (Utils.isJQueryObject(el) === true) {
              el  = el[0];
          }
          return window.getComputedStyle(el, pseudo);
      },
  
      getStyleOne: function(el, property){
          return Utils.getStyle(el).getPropertyValue(property);
      },
  
      getTransformMatrix: function(el, returnArray){
          var computedMatrix = Utils.getStyleOne(el, "transform");
          var a = computedMatrix
              .replace("matrix(", '')
              .slice(0, -1)
              .split(',');
          return returnArray !== true ? {
              a: a[0],
              b: a[1],
              c: a[2],
              d: a[3],
              tx: a[4],
              ty: a[5]
          } : a;
      },
  
      computedRgbToHex: function(rgb){
          var a = rgb.replace(/[^\d,]/g, '').split(',');
          var result = "#", i;
  
          for(i = 0; i < 3; i++) {
              var h = parseInt(a[i]).toString(16);
              result += h.length === 1 ? "0" + h : h;
          }
  
          return result;
      },
  
      computedRgbToRgba: function(rgb, alpha){
          var a = rgb.replace(/[^\d,]/g, '').split(',');
          if (alpha === undefined) {
              alpha = 1;
          }
          a.push(alpha);
          return "rgba("+a.join(",")+")";
      },
  
      computedRgbToArray: function(rgb){
          return rgb.replace(/[^\d,]/g, '').split(',');
      },
  
      hexColorToArray: function(hex){
          var c;
          if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
              c= hex.substring(1).split('');
              if(c.length === 3){
                  c= [c[0], c[0], c[1], c[1], c[2], c[2]];
              }
              c= '0x'+c.join('');
              return [(c>>16)&255, (c>>8)&255, c&255];
          }
          return [0,0,0];
      },
  
      hexColorToRgbA: function(hex, alpha){
          var c;
          if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
              c= hex.substring(1).split('');
              if(c.length === 3){
                  c= [c[0], c[0], c[1], c[1], c[2], c[2]];
              }
              c= '0x'+c.join('');
              return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255, alpha ? alpha : 1].join(',')+')';
          }
          return 'rgba(0,0,0,1)';
      },
  
      getInlineStyles: function(el){
          var styles = {};
          if (Utils.isJQueryObject(el)) {
              el = el[0];
          }
          for (var i = 0, l = el.style.length; i < l; i++) {
              var s = el.style[i];
              styles[s] = el.style[s];
          }
  
          return styles;
      },
  
      updateURIParameter: function(uri, key, value) {
          var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
          var separator = uri.indexOf('?') !== -1 ? "&" : "?";
          if (uri.match(re)) {
              return uri.replace(re, '$1' + key + "=" + value + '$2');
          }
          else {
              return uri + separator + key + "=" + value;
          }
      },
  
      getURIParameter: function(url, name){
          if (!url) url = window.location.href;
          name = name.replace(/[\[\]]/g, "\\$&");
          var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
              results = regex.exec(url);
          if (!results) return null;
          if (!results[2]) return '';
          return decodeURIComponent(results[2].replace(/\+/g, " "));
      },
  
      getLocales: function(){
          return Object.keys(Metro.locales);
      },
  
      addLocale: function(locale){
          Metro.locales = $.extend( {}, Metro.locales, locale );
      },
  
      strToArray: function(str, delimiter, type, format){
          var a;
  
          if (!Utils.isValue(delimiter)) {
              delimiter = ",";
          }
  
          if (!Utils.isValue(type)) {
              type = "string";
          }
  
          a = (""+str).split(delimiter);
  
          return a.map(function(s){
              var result;
  
              switch (type) {
                  case "integer": result = parseInt(s); break;
                  case "float": result = parseFloat(s); break;
                  case "date": result = !Utils.isValue(format) ? new Date(s) : s.toDate(format); break;
                  default: result = s.trim();
              }
  
              return result;
          })
      },
  
      aspectRatioH: function(width, a){
          if (a === "16/9") return width * 9 / 16;
          if (a === "21/9") return width * 9 / 21;
          if (a === "4/3") return width * 3 / 4;
      },
  
      aspectRatioW: function(height, a){
          if (a === "16/9") return height * 16 / 9;
          if (a === "21/9") return height * 21 / 9;
          if (a === "4/3") return height * 4 / 3;
      },
  
      valueInObject: function(obj, value){
          return Object.values(obj).indexOf(value) > -1;
      },
  
      keyInObject: function(){
          return Object.keys(obj).indexOf(value) > -1;
      },
  
      inObject: function(obj, key, val){
          return obj[key] !== undefined && obj[key] === val;
      },
  
      newCssSheet: function(media){
          var style = document.createElement("style");
  
          if (media !== undefined) {
              style.setAttribute("media", media);
          }
  
          style.appendChild(document.createTextNode(""));
  
          document.head.appendChild(style);
  
          return style.sheet;
      },
  
      addCssRule: function(sheet, selector, rules, index){
          if("insertRule" in sheet) {
              sheet.insertRule(selector + "{" + rules + "}", index);
          }
          else if("addRule" in sheet) {
              sheet.addRule(selector, rules, index);
          }
      },
  
      media: function(query){
          return window.matchMedia(query).matches
      },
  
      mediaModes: function(){
          return METRO_MEDIA;
      },
  
      mediaExist: function(media){
          return METRO_MEDIA.indexOf(media) > -1;
      },
  
      inMedia: function(media){
          return METRO_MEDIA.indexOf(media) > -1 && METRO_MEDIA.indexOf(media) === METRO_MEDIA.length - 1;
      },
  
      isValue: function(val){
          return val !== undefined && val !== null && val !== "";
      },
  
      isNull: function(val){
          return val === undefined || val === null;
      },
  
      isNegative: function(val){
          return parseFloat(val) < 0;
      },
  
      isPositive: function(val){
          return parseFloat(val) > 0;
      },
  
      isZero: function(val){
          return (parseFloat(val.toFixed(2))) === 0.00;
      },
  
      between: function(val, bottom, top, equals){
          return equals === true ? val >= bottom && val <= top : val > bottom && val < top;
      },
  
      parseMoney: function(val){
          return Number(parseFloat(val.replace(/[^0-9-.]/g, '')));
      },
  
      isVisible: function(el){
          if (Utils.isJQueryObject(el)) {
              el = el[0];
          }
  
          return Utils.getStyleOne(el, "display") !== "none" && Utils.getStyleOne(el, "visibility") !== "hidden" && el.offsetParent !== null;
      },
  
      parseNumber: function(val, thousand, decimal){
          return val.replace(new RegExp('\\'+thousand, "g"), "").replace(new RegExp('\\'+decimal, 'g'), ".");
      },
  
      nearest: function(val, precision, down){
          val /= precision;
          val = Math[down === true ? 'floor' : 'ceil'](val) * precision;
          return val;
      },
  
      bool: function(value){
          switch(value){
              case true:
              case "true":
              case 1:
              case "1":
              case "on":
              case "yes":
                  return true;
              default:
                  return false;
          }
      },
  
      copy: function(el){
          var body = document.body, range, sel;
  
          if (Utils.isJQueryObject(el)) {
              el = el[0];
          }
  
          if (document.createRange && window.getSelection) {
              range = document.createRange();
              sel = window.getSelection();
              sel.removeAllRanges();
              try {
                  range.selectNodeContents(el);
                  sel.addRange(range);
              } catch (e) {
                  range.selectNode(el);
                  sel.addRange(range);
              }
          } else if (body.createTextRange) {
              range = body.createTextRange();
              range.moveToElementText(el);
              range.select();
          }
  
          document.execCommand("Copy");
  
          if (window.getSelection) {
              if (window.getSelection().empty) {  // Chrome
                  window.getSelection().empty();
              } else if (window.getSelection().removeAllRanges) {  // Firefox
                  window.getSelection().removeAllRanges();
              }
          } else if (document.selection) {  // IE?
              document.selection.empty();
          }
      }
  };
  
  Metro['utils'] = Utils;