docs: function Popup (opts) {
  	if (!(this instanceof Popup)) return new Popup(opts);
  
  	var typeOpts = this.types[opts.type || this.type] || {};
  
  	//hook up type events and options events
  	if (typeOpts.onInit) this.on('init', typeOpts.onInit);
  	if (typeOpts.onShow) this.on('show', typeOpts.onShow);
  	if (typeOpts.onHide) this.on('hide', typeOpts.onHide);
  	if (typeOpts.onAfterShow) this.on('afterShow', typeOpts.onAfterShow);
  	if (typeOpts.onAfterHide) this.on('afterHide', typeOpts.onAfterHide);
  	if (opts.onInit) this.on('init', opts.onInit);
  	if (opts.onShow) this.on('show', opts.onShow);
  	if (opts.onHide) this.on('hide', opts.onHide);
  	if (opts.onAfterShow) this.on('afterShow', opts.onAfterShow);
  	if (opts.onAfterHide) this.on('afterHide', opts.onAfterHide);
  
  
  	//generate unique id
  	this.id = uid();
  
  	//FIXME: :'(
  	this.update = this.update.bind(this);
  
  	//ensure element
  	if (!this.element) this.element = document.createElement('div');
  	this.element.classList.add('popoff');
  	this.element.classList.add('popoff-popup');
  	this.element.classList.add('popoff-hidden');
  
  	//take over type’s options.
  	//should be after element creation to init `content` property
  	extend(this, typeOpts, opts);
  
  	this.element.classList.add(`popoff-${this.type}`);
  
  	//take over a target first
  	if (!this.container) {
  		this.container = document.body || document.documentElement;
  	}
  	this.container.classList.add('popoff-container');
  
  	//create close element
  	this.closeElement = document.createElement('div');
  	this.closeElement.classList.add('popoff-close');
  	if (this.closable) {
  		this.closeElement.addEventListener('click', e => {
  			this.hide();
  		});
  		this.element.appendChild(this.closeElement);
  	}
  
  	//create tip
  	this.tipElement = document.createElement('div');
  	this.tipElement.classList.add('popoff-tip');
  	this.tipElement.classList.add('popoff-hidden');
  	if (this.tip) {
  		this.container.appendChild(this.tipElement);
  		this.element.classList.add('popoff-popup-tip');
  	}
  
  	//apply custom style
  	if (this.style) {
  		for (var name in this.style) {
  			let value = this.style[name];
  			if (typeof value === 'number' && !/z/.test(name)) value += 'px';
  			this.element.style[name] = value;
  		}
  	}
  
  	//create overflow for tall content
  	this.overflowElement = document.createElement('div');
  	this.overflowElement.classList.add('popoff-overflow');
  
  	this.container.appendChild(this.element);
  
  	if (this.escapable) {
  		document.addEventListener('keyup', e => {
  			if (!this.isVisible) return;
  			if (e.which === 27) {
  				this.hide();
  			}
  		});
  	}
  
  	//init proper target
  	if (typeof this.target === 'string') {
  		this.target = document.querySelector(this.target);
  	}
  
  	//update on resize
  	window.addEventListener('resize', () => {
  		this.update();
  	});
  
  	this.emit('init');
  }