/* 
 * vidim v1.0.1
 * 2017-03-13T17:00:04.439Z
 * https://github.com/OriginalEXE/vidim 
 * 
 * Made by Ante Sepic 
 */
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global.vidim = factory());
}(this, (function () { 'use strict';

function createCommonjsModule(fn, module) {
	return module = { exports: {} }, fn(module, module.exports), module.exports;
}

var index$1 = createCommonjsModule(function (module) {
/**
 * Expose `Emitter`.
 */

{
  module.exports = Emitter;
}

/**
 * Initialize a new `Emitter`.
 *
 * @api public
 */

function Emitter(obj) {
  if (obj) return mixin(obj);
}

/**
 * Mixin the emitter properties.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

function mixin(obj) {
  for (var key in Emitter.prototype) {
    obj[key] = Emitter.prototype[key];
  }
  return obj;
}

/**
 * Listen on the given `event` with `fn`.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
  this._callbacks = this._callbacks || {};
  (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
    .push(fn);
  return this;
};

/**
 * Adds an `event` listener that will be invoked a single
 * time then automatically removed.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.once = function(event, fn){
  function on() {
    this.off(event, on);
    fn.apply(this, arguments);
  }

  on.fn = fn;
  this.on(event, on);
  return this;
};

/**
 * Remove the given callback for `event` or all
 * registered callbacks.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
  this._callbacks = this._callbacks || {};

  // all
  if (0 == arguments.length) {
    this._callbacks = {};
    return this;
  }

  // specific event
  var callbacks = this._callbacks['$' + event];
  if (!callbacks) return this;

  // remove all handlers
  if (1 == arguments.length) {
    delete this._callbacks['$' + event];
    return this;
  }

  // remove specific handler
  var cb;
  for (var i = 0; i < callbacks.length; i++) {
    cb = callbacks[i];
    if (cb === fn || cb.fn === fn) {
      callbacks.splice(i, 1);
      break;
    }
  }
  return this;
};

/**
 * Emit `event` with the given args.
 *
 * @param {String} event
 * @param {Mixed} ...
 * @return {Emitter}
 */

Emitter.prototype.emit = function(event){
  this._callbacks = this._callbacks || {};
  var args = [].slice.call(arguments, 1)
    , callbacks = this._callbacks['$' + event];

  if (callbacks) {
    callbacks = callbacks.slice(0);
    for (var i = 0, len = callbacks.length; i < len; ++i) {
      callbacks[i].apply(this, args);
    }
  }

  return this;
};

/**
 * Return array of callbacks for `event`.
 *
 * @param {String} event
 * @return {Array}
 * @api public
 */

Emitter.prototype.listeners = function(event){
  this._callbacks = this._callbacks || {};
  return this._callbacks['$' + event] || [];
};

/**
 * Check if this emitter has `event` handlers.
 *
 * @param {String} event
 * @return {Boolean}
 * @api public
 */

Emitter.prototype.hasListeners = function(event){
  return !! this.listeners(event).length;
};
});

// Simple utility function for setting default values
function setDefaults(object, defaults) {

  var defaultsKeys = Object.keys(defaults);

  defaultsKeys.forEach(function (key) {

    if (!object.hasOwnProperty(key)) {

      object[key] = defaults[key];
    }
  });
}

// Utility function for when we need to accept the DOM element
function getElement(query) {
  var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;


  if ('string' === typeof query) {

    return parent.querySelector(query);
  }

  return query;
}

// Thanks underscore.js :)
function throttle(func, wait) {
  var timeout, context, args, result;
  var previous = 0;

  var later = function later() {
    previous = Date.now();
    timeout = null;
    result = func.apply(context, args);
    if (!timeout) context = args = null;
  };

  var throttled = function throttled() {
    var now = Date.now();
    if (!previous) previous = now;
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    } else if (!timeout) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };

  throttled.cancel = function () {
    clearTimeout(timeout);
    previous = 0;
    timeout = context = args = null;
  };

  return throttled;
}

// Fires once the DOM is ready
function ready(fn) {

  if (document.readyState != 'loading') {

    fn();
  } else {

    document.addEventListener('DOMContentLoaded', fn);
  }
}

var utility = Object.freeze({
	setDefaults: setDefaults,
	getElement: getElement,
	throttle: throttle,
	ready: ready
});

var html5Provider = function (vidim) {

  /**
   * Defaults specific to this provider
   * @type {Object}
   */
  var defaults = {
    preload: 'auto'
  };

  /**
   * Provider definition object
   * @type {Object}
   */
  var vidimHTML5Provider = {

    /**
     * Initial provider setup
     */
    _initializeProvider: function _initializeProvider() {

      setDefaults(this._options, defaults);

      this._constructPlayer();
      this._dispatch();
      this._listen();
    },

    /**
     * Sets up the actual player
     */
    _constructPlayer: function _constructPlayer() {

      this.el = document.createElement('video');

      this.el.setAttribute('playsinline', '');

      if (this._options.loop) {

        this.el.setAttribute('loop', '');
      }

      if (this._options.autoplay) {

        this.el.setAttribute('autoplay', '');
      }

      if (this._options.preload) {

        this.el.setAttribute('preload', this._options.preload);
      }

      if (this._options.muted) {

        this.el.muted = true;
      }

      this.el.style.position = 'absolute';
      this.el.style.left = '50%';
      this.el.style.top = '50%';
      this.el.style.transform = 'translate(-50%, -50%)';
      this.el.style.webkitTransform = 'translate(-50%, -50%)';
      this.el.style.msTransform = 'translate(-50%, -50%)';
      this.el.style.oTransform = 'translate(-50%, -50%)';
      this.el.style.mozTransform = 'translate(-50%, -50%)';
      this.el.style.minWidth = 'calc( 100% + 2px )';
      this.el.style.minHeight = 'calc( 100% + 2px )';
      this.el.style.opacity = '0';

      var src = this._options.src;

      if (src.length && 'string' !== typeof src) {

        for (var i = 0; i < src.length; i++) {

          var source = document.createElement('source');

          source.setAttribute('type', src[i].type);
          source.setAttribute('src', src[i].src);

          this.el.appendChild(source);
        }
      }

      this.wrapper.appendChild(this.el);

      if (this._options.startAt) {

        this.el.currentTime = this._options.startAt;
      }

      if ('function' === typeof this._options.onReady) {

        this._options.onReady(this);
      }

      this.emit('ready');

      this._constructOverlay();

      this.resize();
    },

    /**
     * Dispatches normalized events
     */
    _dispatch: function _dispatch() {
      var _this = this;

      this.el.addEventListener('play', function () {

        _this.emit('play', _this);
      }, false);

      this.el.addEventListener('pause', function () {

        _this.emit('pause', _this);
      }, false);

      this.el.addEventListener('ended', function () {

        _this.emit('end', _this);
      }, false);

      this.el.addEventListener('canplay', function () {

        _this.emit('canplay', _this);
      }, false);

      this.el.addEventListener('canplaythrough', function () {

        _this.emit('canplaythrough', _this);
      }, false);
    },

    /**
     * Any event listeners that are important to this provider
     */
    _listen: function _listen() {
      var _this2 = this;

      // Make sure we show the player once playback starts
      this.on('play', function () {

        _this2.el.style.opacity = '1';
      });

      // If the poster is hidden initially, set it once the play event happens
      if (!this._options.showPosterBeforePlay) {

        this.once('play', function () {

          _this2.wrapper.style.backgroundImage = 'url(\'' + _this2._options.poster + '\')';
        });
      }

      // Show poster on playback end
      if (this._options.showPosterOnEnd) {

        this.on('end', function () {

          _this2.el.style.opacity = '0';
        });
      }

      // Show poster on playback pause
      if (this._options.showPosterOnPause) {

        this.on('pause', function () {

          _this2.el.style.opacity = '0';
        });
      }
    },

    /**
     * Starts the video playback
     */
    play: function play() {

      this.el.play();

      return this;
    },

    /**
     * Pauses the video playback
     */
    pause: function pause() {

      this.el.pause();

      return this;
    },

    /**
     * Sets the player volume
     * @param {Number} [volume=100] Min = 0, Max = 100
     */
    setVolume: function setVolume() {
      var volume = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 100;


      this.el.volume = parseFloat(volume / 100);

      return this;
    },

    /**
     * Gets the player volume
     * @returns {Number} Current player volume
     */
    getVolume: function getVolume() {

      return this.el.volume * 100;
    },

    /**
     * Mutes the player
     */
    mute: function mute() {

      this.el.muted = true;

      return this;
    },

    /**
     * Unmutes the server
     */
    unMute: function unMute() {

      this.el.muted = false;

      return this;
    },

    /**
     * Sets the time player should seek to
     * @param {Number} [time=0] Amount of seconds in the video to seek to
     */
    setTime: function setTime() {
      var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;


      this.el.currentTime = time;

      return this;
    },

    /**
     * Gets the time video playback is currently at
     * @returns {Number} Number of seconds from start to the current position
     */
    getTime: function getTime() {

      return this.el.currentTime;
    },

    /**
     * Gets video duration in seconds
     * @returns {Number} Length of the video in seconds
     */
    getDuration: function getDuration() {

      return this.el.duration;
    },

    /**
     * Hides the media and shows the poster behind it
     */
    showPoster: function showPoster() {

      this.pause().el.style.opacity = '0';
    },

    /**
     * Changes the source of the current video
     * @param {Object} src Hash of video types and sources
     */
    changeSource: function changeSource(src) {
      var newPoster = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;


      this.el.innerHTML = '';

      if (src.length && 'string' !== typeof src) {

        for (var i = 0; i < src.length; i++) {

          var source = document.createElement('source');

          source.setAttribute('type', src[i].type);
          source.setAttribute('src', src[i].src);

          this.el.appendChild(source);
        }
      }

      this.el.load && this.el.load();

      if (this._options.startAt) {

        this.el.currentTime = this._options.startAt;
      }

      if (newPoster) {

        var oldPoster = this._options.poster;

        if (-1 !== this.wrapper.style.backgroundImage.indexOf(oldPoster)) {

          this.wrapper.style.backgroundImage = 'url(\'' + newPoster + '\')';
        }

        this._options.poster = newPoster;
      }
    },

    /**
     * Destroys and unloads the player
     */
    destroy: function destroy() {

      this.emit('destroy');

      this.el.pause && this.el.pause();
      this.el.src = '';
      this.el.load && this.el.load();

      // We wrap this next part in try...catch in case the element is already
      // gone for some reason
      try {
        this.wrapper.parentNode.removeChild(this.wrapper);
      } catch (e) {

        // do nothing here

      }

      vidim.deleteInstance(this.vidimID);

      delete this.container.vidimID;

      this.off();
    },

    /**
     * Resizes the player to provide the best viewing experience
     */
    resize: function resize() {

      if (!this.el) {

        return;
      }

      var containerHeight = this.container.offsetHeight,
          containerWidth = this.container.offsetWidth;

      if ('BODY' === this.container.nodeName) {

        containerWidth = window.innerWidth;
        containerHeight = window.innerHeight;
      }

      if (1 < this._options.ratio && containerWidth / containerHeight < this._options.ratio || 1 > this._options.ratio && containerHeight / containerWidth < this._options.ratio) {

        this.el.style.maxHeight = 'calc( 100% + 2px )';
        this.el.style.maxWidth = '';
      } else {

        this.el.style.maxHeight = '';
        this.el.style.maxWidth = 'calc( 100% + 2px )';
      }

      this.emit('resize');
    }
  };

  vidim.registerProvider('HTML5', vidimHTML5Provider);
};

/* global YT:false */

/**
 * Will be set to true once YouTube API is ready
 * @type {Boolean}
 */
var isAPIReady = false;

var YouTubeProvider = function (vidim) {

  /**
   * Defaults specific to this provider
   * @type {Object}
   */
  var defaults = {
    quality: 'hd1080'
  };

  /**
   * Provider definition object
   * @type {Object}
   */
  var vidimYouTubeProvider = {

    /**
     * Initial provider setup
     */
    _initializeProvider: function _initializeProvider() {
      var _this = this;

      if (isAPIReady) {

        setDefaults(this._options, defaults);

        this._constructPlayer();
        this._listen();
      } else {

        window.addEventListener('vidimYouTubeAPIReady', function () {

          setDefaults(_this._options, defaults);

          _this._constructPlayer();
          _this._listen();
        }, false);
      }
    },

    /**
     * Sets up the actual player
     */
    _constructPlayer: function _constructPlayer() {
      var _this2 = this;

      // Check if YouTube ID was given, and if not, try to extract it from url
      if (11 === this._options.src.length) {

        this.videoID = this._options.src;
      } else {

        var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;

        var videoMatch = this._options.src.match(regex);

        if (!videoMatch || 11 !== videoMatch[2].length) {

          return new Error('Provided source is not a valid YouTube url');
        }

        this.videoID = videoMatch[2];
      }

      // Create a temporary dom element that will be replaced by the
      // YouTube iframe
      var toBeReplaced = document.createElement('div');

      this.wrapper.appendChild(toBeReplaced);

      var playerParams = {
        videoId: this.videoID,
        playerVars: {
          allowfullscreen: false,
          controls: 0,
          enablejsapi: 1,
          disablekb: 1,
          fs: 0,
          iv_load_policy: 3,
          modestbranding: 1,
          origin: window.location.host,
          playsinline: 1,
          rel: 0,
          showinfo: 0,
          start: this._options.startAt,
          autoplay: +this._options.autoplay
        },
        events: {
          'onReady': function onReady() {

            if (_this2._options.muted) {

              _this2.mute();
            }

            _this2.player.setPlaybackQuality(_this2._options.quality);

            if ('function' === typeof _this2._options.onReady) {

              _this2._options.onReady(_this2);
            }

            _this2.emit('ready');

            if (_this2._options.loop) {

              var loopInterval = void 0;

              _this2.on('play', function () {

                loopInterval = setInterval(function () {

                  if (_this2.getTime() === 0 || _this2.getTime() + 0.15 > _this2.getDuration()) {

                    _this2.setTime(0);
                    _this2.play();
                  }
                }, 100);
              });

              _this2.on('pause', function () {

                clearInterval(loopInterval);
              });

              _this2.on('destroy', function () {

                clearInterval(loopInterval);
              });
            }
          },
          'onStateChange': function onStateChange(e) {

            switch (e.data) {

              case 0:
                _this2.emit('end', _this2);
                break;

              case 1:
                _this2.emit('play', _this2);
                break;

              case 2:
                _this2.emit('pause', _this2);
                break;

              case 3:
                _this2.emit('buffering', _this2);
                break;

            }
          }
        }
      };

      if (this._options.loop) {

        playerParams.playerVars.playlist = this.videoID;
        playerParams.playerVars.loop = 1;
      }

      this.player = new YT.Player(toBeReplaced, playerParams);

      this.el = this.player.getIframe();

      this.el.style.position = 'absolute';
      this.el.style.left = '50%';
      this.el.style.top = '50%';
      this.el.style.transform = 'translate(-50%, -50%)';
      this.el.style.webkitTransform = 'translate(-50%, -50%)';
      this.el.style.msTransform = 'translate(-50%, -50%)';
      this.el.style.oTransform = 'translate(-50%, -50%)';
      this.el.style.mozTransform = 'translate(-50%, -50%)';
      this.el.style.minWidth = 'calc( 100% + 2px )';
      this.el.style.minHeight = 'calc( 100% + 2px )';
      this.el.style.opacity = '0';

      this._constructOverlay();

      this.resize();
    },

    /**
     * Any event listeners that are important to this provider
     */
    _listen: function _listen() {
      var _this3 = this;

      // Make sure we show the player once playback starts
      this.on('play', function () {

        _this3.el.style.opacity = '1';
      });

      // If the poster is hidden initially, set it once the play event happens
      if (!this._options.showPosterBeforePlay) {

        this.once('play', function () {

          _this3.wrapper.style.backgroundImage = 'url(\'' + _this3._options.poster + '\')';
        });
      }

      // Show poster on playback end
      if (this._options.showPosterOnEnd) {

        this.on('end', function () {

          _this3.el.style.opacity = '0';
        });
      } else {

        // We don't want to display YouTube play button over our video,
        // this is a hacky way to stop that from happening
        this.on('end', function () {

          _this3.player.seekTo(_this3.player.getDuration());
          _this3.player.playVideo();
          _this3.player.pauseVideo();
        });
      }

      // Show poster on playback pause
      if (this._options.showPosterOnPause) {

        this.on('pause', function () {

          _this3.el.style.opacity = '0';
        });
      }
    },

    /**
     * Starts the video playback
     */
    play: function play() {

      this.player.playVideo();

      return this;
    },

    /**
     * Pauses the video playback
     */
    pause: function pause() {

      this.player.pauseVideo();

      return this;
    },

    /**
     * Sets the player volume
     * @param {Number} [volume=100] Min = 0, Max = 100
     */
    setVolume: function setVolume() {
      var volume = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 100;


      this.player.setVolume(volume);

      return this;
    },

    /**
     * Gets the player volume
     * @returns {Number} Current player volume
     */
    getVolume: function getVolume() {

      return this.getVolume();
    },

    /**
     * Mutes the player
     */
    mute: function mute() {

      this.player.mute();

      return this;
    },

    /**
     * Unmutes the server
     */
    unMute: function unMute() {

      this.player.unMute();

      return this;
    },

    /**
     * Sets the time player should seek to
     * @param {Number} [time=0] Amount of seconds in the video to seek to
     */
    setTime: function setTime() {
      var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;


      this.player.seekTo(time, true);

      return this;
    },

    /**
     * Gets the time video playback is currently at
     * @returns {Number} Number of seconds from start to the current position
     */
    getTime: function getTime() {

      return this.player.getCurrentTime();
    },

    /**
     * Gets video duration in seconds
     * @returns {Number} Length of the video in seconds
     */
    getDuration: function getDuration() {

      return this.player.getDuration();
    },

    /**
     * Hides the media and shows the poster behind it
     */
    showPoster: function showPoster() {

      this.pause().el.style.opacity = '0';
    },

    /**
     * Changes the source of the current video
     * @param {Object} src Hash of video types and sources
     */
    changeSource: function changeSource(src) {
      var newPoster = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;


      if (11 === src.length) {

        this.videoID = src;
      } else {

        var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;

        var videoMatch = src.match(regex);

        if (!videoMatch || 11 !== videoMatch[2].length) {

          return new Error('Provided source is not a valid YouTube url');
        }

        this.videoID = videoMatch[2];
      }

      this.player.loadPlaylist(this.videoID, 0, this._options.startAt, this._options.quality);

      this.player.setLoop(this._options.loop);

      if (newPoster) {

        var oldPoster = this._options.poster;

        if (-1 !== this.wrapper.style.backgroundImage.indexOf(oldPoster)) {

          this.wrapper.style.backgroundImage = 'url(\'' + newPoster + '\')';
        }

        this._options.poster = newPoster;
      }
    },

    /**
     * Destroys and unloads the player
     */
    destroy: function destroy() {

      this.emit('destroy');

      this.player.destroy();

      // We wrap this next part in try...catch in case the element is already
      // gone for some reason
      try {
        this.wrapper.parentNode.removeChild(this.wrapper);
      } catch (e) {

        // do nothing here

      }

      vidim.deleteInstance(this.vidimID);

      delete this.container.vidimID;

      this.off();
    },

    /**
     * Resizes the player to provide the best viewing experience
     */
    resize: function resize() {

      if (!this.el) {

        return;
      }

      var containerHeight = this.container.offsetHeight,
          containerWidth = this.container.offsetWidth;

      if ('BODY' === this.container.nodeName) {

        containerWidth = window.innerWidth;
        containerHeight = window.innerHeight;
      }

      if (1 < this._options.ratio && containerWidth / containerHeight < this._options.ratio || 1 > this._options.ratio && containerHeight / containerWidth < this._options.ratio) {

        this.el.style.maxHeight = 'calc( 100% + 2px )';
        this.el.style.maxWidth = '';

        this.el.style.height = '';
        this.el.style.width = this.el.offsetHeight * this._options.ratio + 200 + 'px';
      } else {

        this.el.style.maxHeight = '';
        this.el.style.maxWidth = 'calc( 100% + 2px )';

        this.el.style.height = this.el.offsetWidth / this._options.ratio + 'px';
        this.el.style.width = '';

        if (this.el.offsetHeight < this.wrapper.offsetHeight + 140) {

          this.el.style.height = this.el.offsetWidth / this._options.ratio + 140 + 'px';
        }
      }

      this.emit('resize');
    }
  };

  vidim.registerProvider('YouTube', vidimYouTubeProvider);
};

// Load the YouTube API
var tag = document.createElement('script');

tag.src = 'https://www.youtube.com/iframe_api';

document.querySelector('body').appendChild(tag);

// Create our custom event
var event = document.createEvent('Event');

event.initEvent('vidimYouTubeAPIReady', true, true);

// Finaly, make sure we trigger that event once the API is ready
if ('undefined' === typeof window.onYouTubeIframeAPIReady) {

  window.onYouTubeIframeAPIReady = function () {

    window.vidimYouTubeAPIReady = true;

    isAPIReady = true;

    window.dispatchEvent(event);
  };
} else {

  var oldOnYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady;

  window.onYouTubeIframeAPIReady = function () {

    oldOnYouTubeIframeAPIReady();

    window.vidimYouTubeAPIReady = true;

    isAPIReady = true;

    window.dispatchEvent(event);
  };
}

var index = ((function factory(global) {

  if ('undefined' === typeof global.document) {

    return;
  }

  /**
   * Instance ID counter
   * @type {Number}
   */
  var ID = 0;

  /**
   * Hashmap of instances
   * @type {Object}
   */
  var instances = {};

  /**
   * Default options
   * @type {Object}
   */
  var defaults = {
    wrapperClass: '',
    overlayClass: '',
    src: false,
    type: 'HTML5',
    ratio: 1.7778,
    autoplay: true,
    loop: true,
    poster: '',
    showPosterBeforePlay: true,
    showPosterOnEnd: false,
    showPosterOnPause: false,
    zIndex: 0,
    autoResize: true,
    muted: true,
    startAt: 0,
    onReady: false
  };

  /**
   * Stores a list of option keys that the user provided, used for setting more
   * sensible defaults
   * @type {Array}
   */
  var optionsProvided = [];

  /**
   * Hashmap of providers
   * @type {Object}
   */
  var providers = {};

  /**
   * Register a provider for handling media embeds
   * @param  {String} type Short identifier for the media type handled
   * @param  {Function|Object} Object or a function that returns object
   */
  function registerProvider(type, provider) {

    if ('function' === typeof provider) {

      providers[type] = provider.call(global, defaults);
    } else {

      providers[type] = provider;
    }
  }

  /**
   * Get instance by its ID
   * @param  {integer} id Instance ID
   * @returns {null|Object} null or instance if found
   */
  function getInstanceFromID(id) {

    if (!instances[id]) {

      return null;
    }

    return instances[id];
  }

  /**
   * Get instance from the container element
   * @param   {Object|string} element DOM element or a query string
   * @returns {null|Object} null or instance if found
   */
  function getInstanceFromElement(element) {

    element = getElement(element);

    if ('undefined' === typeof element.vidimID || !instances[element.vidimID]) {

      return null;
    }

    return instances[element.vidimID];
  }

  /**
   * Destroys all active instances
   */
  function destroyAllInstances() {

    for (var key in instances) {

      instances[key].hasOwnProperty('destroy') && instances[key].destroy();
    }

    instances = {};
  }

  /**
   * Delete instance from the instances hash
   * NOTE: this will not destroy the instance, only remove it from the
   * instances hash. Call the destroy() method on the instance itself if you
   * want to destroy it
   * @param {string} id Instance ID
   */
  function deleteInstance(id) {

    delete instances[id];
  }

  // API method for scanning the DOM and initializing vide instances from
  // data-vide attribute

  /**
   * Scan the DOM for elements that have data-vidim attribute and initialize
   * new vidim instance based on that attribute
   */
  function scanDOM() {

    var vidimElements = document.querySelectorAll('[data-vidim]');

    if (!vidimElements.length) {

      return;
    }

    for (var i = 0; i < vidimElements.length; i++) {

      var element = vidimElements[i];
      var options = element.getAttribute('data-vidim');

      if ('undefined' !== typeof element.vidimID) {

        // this element already has an instance of vidim
        continue;
      }

      try {

        // The things we do to avoid eval...
        var regex = /({|,)(?:\s*)(?:')?([A-Za-z_$\.][A-Za-z0-9_ \-\.$]*)(?:')?(?:\s*):/g;

        var fixedOptions = options.replace(regex, '$1"$2":').replace(/:\s?'/g, ':"').replace(/'\s?}/g, '"}').replace(/',"/g, '","');

        var parsedOptions = JSON.parse(fixedOptions);

        new vidim(element, parsedOptions);
      } catch (e) {

        // do nothing

      }
    }
  }

  /**
   * Used for instantiating new instances
   * @param {Object|string} element DOM element or a query string
   * @param {Object} [options={}] Options hash
   */
  function vidim(element) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


    Object.keys(options).forEach(function (key) {

      optionsProvided.push(key);
    });

    // Set defaults
    setDefaults(options, defaults);

    if (options.src && !providers[options.type]) {

      return new Error('No provider can handle type: \'' + options.type + '\'');
    }

    if ('string' === typeof options.ratio) {

      if ('4/3' === options.ratio) {

        options.ratio = 4 / 3;
      } else {

        options.ratio = 16 / 9;
      }
    }

    this._options = options;
    this.container = getElement(element);

    if (!this.container) {

      return new Error('Could not find the container: ' + element);
    }

    // Set more sensitive defaults
    if (-1 === optionsProvided.indexOf('zIndex') && 'BODY' === this.container.nodeName) {

      this._options.zIndex = -1;
    }

    this._initialize();
  }

  var prototype = vidim.prototype;

  /**
   * Initializes the instance and does the work common to all providers
   */
  prototype._initialize = function () {

    // Assign a unique ID to this instance, and make sure we store it on the
    // container for convenience
    this.vidimID = ID++;
    this.container.vidimID = this.vidimID;

    instances[this.vidimID] = this;

    // Add emitter interface to our instance
    index$1(this);

    // Check if poster was provided but the source was not,
    // in that case we fake a simple provider interface that only displays a
    // poster
    if (!this._options.src && this._options.poster) {

      this._constructWrapper();

      this.destroy = function () {

        this.emit('destroy');

        // We wrap this next part in try...catch in case the element is already
        // gone for some reason
        try {
          this.wrapper.parentNode.removeChild(this.wrapper);
        } catch (e) {

          // do nothing here

        }

        vidim.deleteInstance(this.vidimID);

        delete this.container.vidimID;

        this.off();
      };

      return;
    }

    var provider = providers[this._options.type];

    for (var key in provider) {

      this[key] = provider[key];
    }

    if ('function' !== typeof this._initializeProvider) {

      return new Error('Provider is missing method: _initializeProvider');
    }

    this._constructWrapper();
    this._initializeProvider();

    if (this._options.autoResize) {

      window.addEventListener('resize', throttle(this.resize, 200).bind(this), false);
    }

    this.resize();
  };

  /**
   * Generates a wrapper that is used for holding the media
   */
  prototype._constructWrapper = function () {

    var containerStyle = getComputedStyle(this.container, null);

    if ('static' === containerStyle.getPropertyValue('position')) {

      this.container.style.position = 'relative';
    }

    this.wrapper = document.createElement('div');

    if ('BODY' === this.container.nodeName) {

      this.wrapper.style.position = 'fixed';
    } else {

      this.wrapper.style.position = 'absolute';
    }

    if (this._options.wrapperClass) {

      if ('function' === typeof this._options.wrapperClass) {

        this.wrapper.classList.add(this._options.wrapperClass.call(this));
      } else {

        this.wrapper.classList.add(this._options.wrapperClass);
      }
    }

    this.wrapper.style.left = 0;
    this.wrapper.style.top = 0;
    this.wrapper.style.height = '100%';
    this.wrapper.style.width = '100%';
    this.wrapper.style.overflow = 'hidden';
    this.wrapper.style.zIndex = parseInt(this._options.zIndex, 10);

    if (this._options.poster) {

      this.wrapper.style.backgroundSize = 'cover';
      this.wrapper.style.backgroundPosition = 'center center';
      this.wrapper.style.backgroundRepear = 'no-repeat';

      if (this._options.showPosterBeforePlay) {

        this.wrapper.style.backgroundImage = 'url(\'' + this._options.poster + '\')';
      }
    }

    this.container.insertBefore(this.wrapper, this.container.firstChild);
  };

  /**
   * Generates an overlay that is placed above the media to prevent interaction
   */
  prototype._constructOverlay = function () {

    this.overlay = document.createElement('div');

    this.overlay.style.position = 'absolute';

    if (this._options.overlayClass) {

      if ('function' === typeof this._options.overlayClass) {

        this.overlay.classList.add(this._options.overlayClass.call(this));
      } else {

        this.overlay.classList.add(this._options.overlayClass);
      }
    }

    this.overlay.style.left = 0;
    this.overlay.style.top = 0;
    this.overlay.style.height = '100%';
    this.overlay.style.width = '100%';

    this.wrapper.appendChild(this.overlay);
  };

  // Provide method for retrieving vidim instance by its id
  vidim.getInstanceFromID = getInstanceFromID;

  // Provide method for retrieving vidim instance from the container element
  vidim.getInstanceFromElement = getInstanceFromElement;

  // Provide method for registering new providers
  vidim.registerProvider = registerProvider;

  // Provide method for destroying all instances
  vidim.destroyAllInstances = destroyAllInstances;

  // Provide method for deleting the instance from instances hash
  vidim.deleteInstance = deleteInstance;

  // Export our utils functions as well so providers can make use of them
  vidim.utility = utility;

  // Provide method for scanning the DOM and initializing vidim from attribute
  vidim.scanDOM = scanDOM;

  // Initialize providers
  html5Provider(vidim);
  YouTubeProvider(vidim);

  // Parse the DOM an auto initialize based on the [data-vidim] attribute
  ready(scanDOM);

  return vidim;
}))('undefined' !== typeof window ? window : undefined);

return index;

})));