text: if (typeof jQuery === 'undefined') {
      throw new Error('Metro 4 requires jQuery!');
  }
  
  if ('MutationObserver' in window === false) {
      throw new Error('Metro 4 requires MutationObserver!');
  }
  
  var meta_init = $("meta[name='metro4:init']").attr("content");
  var meta_locale = $("meta[name='metro4:locale']").attr("content");
  var meta_week_start = $("meta[name='metro4:week_start']").attr("content");
  var meta_animation_duration = $("meta[name='metro4:animation_duration']").attr("content");
  var meta_callback_timeout = $("meta[name='metro4:callback_timeout']").attr("content");
  var meta_timeout = $("meta[name='metro4:timeout']").attr("content");
  
  if (window.METRO_INIT === undefined) {
      window.METRO_INIT = meta_init !== undefined ? JSON.parse(meta_init) : true;
  }
  if (window.METRO_DEBUG === undefined) {window.METRO_DEBUG = true;}
  
  if (window.METRO_WEEK_START === undefined) {
      window.METRO_WEEK_START = meta_week_start !== undefined ? parseInt(meta_week_start) : 1;
  }
  if (window.METRO_LOCALE === undefined) {
      window.METRO_LOCALE = meta_locale !== undefined ? meta_locale : 'en-US';
  }
  if (window.METRO_ANIMATION_DURATION === undefined) {
      window.METRO_ANIMATION_DURATION = meta_animation_duration !== undefined ? parseInt(meta_animation_duration) : 300;
  }
  if (window.METRO_CALLBACK_TIMEOUT === undefined) {
      window.METRO_CALLBACK_TIMEOUT = meta_callback_timeout !== undefined ? parseInt(meta_callback_timeout) : 500;
  }
  if (window.METRO_TIMEOUT === undefined) {
      window.METRO_TIMEOUT = meta_timeout !== undefined ? parseInt(meta_timeout) : 2000;
  }
  if (window.METRO_HOTKEYS_FILTER_CONTENT_EDITABLE === undefined) {window.METRO_HOTKEYS_FILTER_CONTENT_EDITABLE = true;}
  if (window.METRO_HOTKEYS_FILTER_INPUT_ACCEPTING_ELEMENTS === undefined) {window.METRO_HOTKEYS_FILTER_INPUT_ACCEPTING_ELEMENTS = true;}
  if (window.METRO_HOTKEYS_FILTER_TEXT_INPUTS === undefined) {window.METRO_HOTKEYS_FILTER_TEXT_INPUTS = true;}
  if (window.METRO_HOTKEYS_BUBBLE_UP === undefined) {window.METRO_HOTKEYS_BUBBLE_UP = false;}
  if (window.METRO_THROWS === undefined) {window.METRO_THROWS = true;}
  
  window.METRO_MEDIA = [];
  
  if ( typeof Object.create !== 'function' ) {
      Object.create = function (o) {
          function F() {}
          F.prototype = o;
          return new F();
      };
  }
  
  if (typeof Object.values !== 'function') {
      Object.values = function(obj) {
          return Object.keys(obj).map(function(e) {
              return obj[e]
          });
      }
  }
  
  var isTouch = (('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
  
  var Metro = {
  
      version: "@@version",
      versionFull: "@@version.@@build @@status",
      isTouchable: isTouch,
      fullScreenEnabled: document.fullscreenEnabled,
      sheet: null,
  
      controlsPosition: {
          INSIDE: "inside",
          OUTSIDE: "outside"
      },
  
      groupMode: {
          ONE: "one",
          MULTI: "multi"
      },
  
      aspectRatio: {
          HD: "hd",
          SD: "sd",
          CINEMA: "cinema"
      },
  
      fullScreenMode: {
          WINDOW: "window",
          DESKTOP: "desktop"
      },
  
      position: {
          TOP: "top",
          BOTTOM: "bottom",
          LEFT: "left",
          RIGHT: "right",
          TOP_RIGHT: "top-right",
          TOP_LEFT: "top-left",
          BOTTOM_LEFT: "bottom-left",
          BOTTOM_RIGHT: "bottom-right",
          LEFT_BOTTOM: "left-bottom",
          LEFT_TOP: "left-top",
          RIGHT_TOP: "right-top",
          RIGHT_BOTTOM: "right-bottom"
      },
  
      popoverEvents: {
          CLICK: "click",
          HOVER: "hover",
          FOCUS: "focus"
      },
  
      stepperView: {
          SQUARE: "square",
          CYCLE: "cycle",
          DIAMOND: "diamond"
      },
  
      listView: {
          LIST: "list",
          CONTENT: "content",
          ICONS: "icons",
          ICONS_MEDIUM: "icons-medium",
          ICONS_LARGE: "icons-large",
          TILES: "tiles",
          TABLE: "table"
      },
  
      events: {
          click: 'click.metro',
          start: 'touchstart.metro mousedown.metro',
          stop: 'touchend.metro mouseup.metro',
          move: 'touchmove.metro mousemove.metro',
          enter: 'touchstart.metro mouseenter.metro',
          leave: 'touchend.metro mouseleave.metro',
          focus: 'focus.metro',
          blur: 'blur.metro',
          resize: 'resize.metro',
          keyup: 'keyup.metro',
          keydown: 'keydown.metro',
          keypress: 'keypredd.metro',
          dblclick: 'dblclick.metro',
          input: 'input.metro',
          change: 'change.metro',
          cut: 'cut.metro',
          paste: 'paste.metro',
          drop: 'drop.metro',
          scroll: 'scroll.metro',
          scrollStart: 'scrollstart.metro',
          scrollStop: 'scrollstop.metro',
          mousewheel: 'mousewheel.metro',
          inputchange: "change.metro input.metro propertychange.metro cut.metro paste.metro copy.metro"
      },
  
      media_queries: {
          FS: "(min-width: 0px)",
          SM: "(min-width: 576px)",
          MD: "(min-width: 768px)",
          LG: "(min-width: 992px)",
          XL: "(min-width: 1200px)",
          XXL: "(min-width: 1452px)"
      },
  
      media_sizes: {
          FS: 0,
          SM: 576,
          MD: 768,
          LG: 992,
          XL: 1200,
          XXL: 1452
      },
  
      media_mode: {
          FS: "fs",
          SM: "sm",
          MD: "md",
          LG: "lg",
          XL: "xl",
          XXL: "xxl"
      },
  
      actions: {
          REMOVE: 1,
          HIDE: 2
      },
  
      hotkeys: [],
  
      about: function(f){
          console.log("Metro 4 Components Library - v" + (f === true ? this.versionFull : this.version));
      },
  
      aboutDlg: function(f){
          alert("Metro 4 Components Library - v" + (f === true ? this.versionFull : this.version));
      },
  
      ver: function(f){
          return (f === true ? this.versionFull : this.version);
      },
  
      observe: function(){
          var observer, observerCallback;
          var observerConfig = {
              childList: true,
              attributes: true,
              subtree: true
          };
          observerCallback = function(mutations){
              mutations.map(function(mutation){
  
                  if (mutation.type === 'attributes' && mutation.attributeName !== "data-role") {
                      var element = $(mutation.target);
                      var mc = element.data('metroComponent');
                      if (mc !== undefined) {
                          $.each(mc, function(){
                              var plug = element.data(this);
                              if (plug) plug.changeAttribute(mutation.attributeName);
                          });
                      }
                  } else
  
                  if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                      var i, obj, widgets = {}, plugins = {};
                      var nodes = mutation.addedNodes;
  
                      for(i = 0; i < nodes.length; i++) {
  
                          var node = mutation.addedNodes[i];
  
                          if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') {
                              return;
                          }
                          obj = $(mutation.addedNodes[i]);
  
                          plugins = obj.find("[data-role]");
                          if (obj.data('role') !== undefined) {
                              widgets = $.merge(plugins, obj);
                          } else {
                              widgets = plugins;
                          }
                          if (widgets.length) {
                              Metro.initWidgets(widgets);
                          }
                      }
  
                  } else  {
                      //console.log(mutation);
                  }
              });
          };
          observer = new MutationObserver(observerCallback);
          observer.observe($("html")[0], observerConfig);
      },
  
      init: function(){
          var widgets = $("[data-role]");
          var hotkeys = $("[data-hotkey]");
          var html = $("html");
  
          if (isTouch === true) {
              html.addClass("metro-touch-device");
          } else {
              html.addClass("metro-no-touch-device");
          }
  
          this.sheet = Utils.newCssSheet();
  
          this.observe();
  
          this.initHotkeys(hotkeys);
          this.initWidgets(widgets);
  
          window.METRO_MEDIA = [];
          $.each(Metro.media_queries, function(key, query){
              if (Utils.media(query)) {
                  METRO_MEDIA.push(Metro.media_mode[key]);
              }
          });
  
          this.about(true);
  
          return this;
      },
  
      initHotkeys: function(hotkeys){
          $.each(hotkeys, function(){
              var element = $(this);
              var hotkey = element.data('hotkey') ? element.data('hotkey').toLowerCase() : false;
  
              if (hotkey === false) {
                  return;
              }
  
              if (element.data('hotKeyBonded') === true ) {
                  return;
              }
  
              Metro.hotkeys.push(hotkey);
  
              $(document).on(Metro.events.keyup, null, hotkey, function(e){
                  if (element === undefined) return;
  
                  if (element[0].tagName === 'A' &&
                      element.attr('href') !== undefined &&
                      element.attr('href').trim() !== '' &&
                      element.attr('href').trim() !== '#') {
                      document.location.href = element.attr('href');
                  } else {
                      element.click();
                  }
                  return METRO_HOTKEYS_BUBBLE_UP;
              });
  
              element.data('hotKeyBonded', true);
          });
      },
  
      initWidgets: function(widgets) {
          var that = this;
  
          $.each(widgets, function () {
              var $this = $(this), w = this;
              var roles = $this.data('role').split(/\s*,\s*/);
              roles.map(function (func) {
                  if ($.fn[func] !== undefined && $this.attr("data-role-"+func) === undefined) {
                      $.fn[func].call($this);
                      $this.attr("data-role-"+func, true);
  
                      var mc = $this.data('metroComponent');
  
                      if (mc === undefined) {
                          mc = [func];
                      } else {
                          mc.push(func);
                      }
                      $this.data('metroComponent', mc);
                  }
              });
          });
      },
  
      plugin: function(name, object){
          $.fn[name] = function( options ) {
              return this.each(function() {
                  $.data( this, name, Object.create(object).init(options, this ));
              });
          };
      },
  
      destroyPlugin: function(element, name){
          var p, mc;
          element = Utils.isJQueryObject(element) ? element[0] : element;
          p = $(element).data(name);
  
          if (!Utils.isValue(p)) {
              throw new Error("Component can not be destroyed: the element is not a Ьуекщ 4 component.");
          }
  
          if (!Utils.isFunc(p['destroy'])) {
              throw new Error("Component can not be destroyed: method destroy not found.");
          }
  
          p['destroy']();
          mc = $(element).data("metroComponent");
          Utils.arrayDelete(mc, name);
          $(element).data("metroComponent", mc);
          $.removeData(element, name);
          $(element).removeAttr("data-role-"+name);
      },
  
      destroyPluginAll: function(element){
          element = Utils.isJQueryObject(element) ? element[0] : element;
          var mc = $(element).data("metroComponent");
  
          if (mc !== undefined && mc.length > 0) $.each(mc, function(){
              Metro.destroyPlugin(element, this);
          });
      },
  
      initPlugin: function(element, name){
          element = $(element);
          try {
              if ($.fn[name] !== undefined && element.attr("data-role-"+name) === undefined) {
                  $.fn[name].call(element);
                  element.attr("data-role-"+name, true);
  
                  var mc = element.data('metroComponent');
  
                  if (mc === undefined) {
                      mc = [name];
                  } else {
                      mc.push(name);
                  }
                  element.data('metroComponent', mc);
              }
          } catch (e) {
              console.log(e.message, e.stack);
          }
      },
  
      reinitPlugin: function(element, name){
          this.destroyPlugin(element, name);
          this.initPlugin(element, name);
      },
  
      reinitPluginAll: function(element){
          var mc = $(element).data("metroComponent");
  
          if (mc !== undefined && mc.length > 0) $.each(mc, function(){
              Metro.reinitPlugin(element, this);
          });
      },
  
      noop: function(){},
      noop_true: function(){return true;},
      noop_false: function(){return false;},
  
      stop: function(e){
          e.stopPropagation();
          e.preventDefault();
      },
  
      requestFullScreen: function(element){
          if (element.mozRequestFullScreen) {
              element.mozRequestFullScreen();
          } else if (element.webkitRequestFullScreen) {
              element.webkitRequestFullScreen();
          } else if (element.msRequestFullscreen) {
              element.msRequestFullscreen();
          } else {
              element.requestFullscreen();
          }
      },
  
      exitFullScreen: function(){
          if (document.mozCancelFullScreen) {
              document.mozCancelFullScreen();
          }
          else if (document.webkitCancelFullScreen) {
              document.webkitCancelFullScreen();
          }
          else if (document.msExitFullscreen) {
              document.msExitFullscreen();
          } else {
              document.exitFullscreen();
          }
      },
  
      inFullScreen: function(){
          var fsm = (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
          return fsm !== undefined;
      }
  };
  
  window['Metro'] = Metro;
  
  $(window).on(Metro.events.resize, function(){
      window.METRO_MEDIA = [];
      $.each(Metro.media_queries, function(key, query){
          if (Utils.media(query)) {
              METRO_MEDIA.push(Metro.media_mode[key]);
          }
      });
  });
  
  