/*
	Jquery-плагин модальных окон
	Автор: Александр zak Новиков, 02.2021
	
	callbacks:
	  after_show(id_modal)
	  after_close(id_modal)
*/

(function($, window, document, undefined) {
    
	// массив открытых окон
	var modals = [];
	
	//обработчик нажития эскейпа
	$(document).on('keyup', function(event){
		if(event.keyCode === 27 && $.zmodal.isActive()){
			$.zmodal.close(event);
		}
	});
	
	// на сколько прокручена сейчас страница?
	var get_window_scroll = function() {
		return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
	}
	
	var isset_page_scroll = function(){
		return document.body.scrollHeight > window.innerHeight;
	}
	
	// есть ли на странице вертикальный скролл?
	var isset_scroll;

	// возвращаем самое верхнее открытое модальное окно
	var getCurrent = function() {
		return modals.length ? modals[modals.length - 1] : null;
	};
	
	// генерация рандомной строки
	var rand_str = function(length) {
		var result           = '';
		var characters       = 'abcdefghijklmnopqrstuvwxyz';
		var charactersLength = characters.length;
		for ( var i = 0; i < length; i++ ) {
			result += characters.charAt(Math.floor(Math.random() * charactersLength));
		}
	   return result;
	};
  
	$.zmodal = function(btn, options) {

		var href = btn.attr('href');

		this.options = $.extend({}, $.zmodal.defaults, options);

		// перебиваем настройки data-атрибутами
		if (typeof btn.data('modal-noclose') !== typeof undefined && btn.data('modal-noclose') !== false) {
			this.options.noclose = true;
		}
		  
		if (typeof btn.data('modal-wrapper-class') !== typeof undefined && btn.data('modal-wrapper-class') !== false) {
			this.options.wrapper_class = btn.data('modal-wrapper-class');
		}
		
		if (typeof btn.data('modal-class') !== typeof undefined && btn.data('modal-class') !== false) {
			this.options.class = btn.data('modal-class');
		}
		  
		if (typeof btn.data('modal-style') !== typeof undefined && btn.data('modal-style') !== false) {
			this.options.style = btn.data('modal-style');
		}
		
		if (typeof btn.data('modal-max-width') !== typeof undefined && btn.data('modal-max-width') !== false) {
			this.options.max_width = btn.data('modal-max-width');
		}
		  
		// если ссылка, проверяем атрибут href
		if(href){
			
			if(href.substring(0,1)=='#'){

				this.elm_id = href.substring(1);
				
			}else if(href.substring(0,1)=='/' || href.substring(0,4)=='http'){				
	
				this.elm_id = 'zmodal_ajax' + modals.length;
				this.url = href;	
			}

		// атрибут data-modal='id'	
		 }else if(btn.data('modal')){
	 
			this.elm_id = btn.data('modal');
			
		// атрибут data-modal-ajax='url'	
		}else if(btn.data('modal-ajax')){

			this.elm_id = 'zmodal_ajax' + modals.length;
			this.url = btn.data('modal-ajax');
		}
		
		// задание ajax url через опции
		if ('url' in this.options){
			this.elm_id = 'zmodal_ajax' + modals.length;
			this.url = this.options.url;
		}

		// ID всего окна
		//this.window_id = this.elm_id + '_modal';
		this.window_id = this.elm_id + '_window';
		
		// 2. открываем окно
		$.zmodal.open({
			btn: 		btn,
			elm_id: 	this.elm_id,
			window_id: 	this.window_id,	
			url:		this.url,				
			options: 	this.options		
		});	

	};
 

	
	
	
	// ОБЕРТКА ОКНА 
	$.zmodal.wrap = function(params){

		// загрузка ajax-контента
		if(params.url){
			
			params.options.loading = 'loading';
			
			var zmodal_ajax_modal = params.window_id;
			
			// если в DOM есть прерыдущее ajax-окно - удаляем
			if($('#'+zmodal_ajax_modal).length){
				$('#'+zmodal_ajax_modal).remove();
			}
			
			// добавляем элемент с таким id
			$('body').append("<div class='zmodal_content' id='" + params.elm_id + "'></div>");	
			
			// ajax-запрос
			$('#' + params.elm_id).load(params.url, function(){				
				$('#'+zmodal_ajax_modal).removeClass('loading').addClass('loaded');			
			});
			
		}
		
		// открываем динамический контент
		// нужно сначала добавить его в DOM
		if(params.content){

			// если в DOM есть прерыдущее ajax-окно - удаляем
			if($('#' + params.window_id).length){$('#' + params.window_id).remove();}

			// добавляем в DOM
			$('body').append("<div class='zmodal_content' id='" + params.elm_id + "' aria-hidden='true'>" + params.content + "</div>");					
		}
		
		// получаем содержимое модального окна
		$zmodal_content = $('#' + params.elm_id);

		// максимальную ширину можно указывать как в data-параметре (<button class="zmodal_btn" data-modal-max-width="333" data-modal-ajax="/ajax/?id=123">)
		// так и через style самой модалки (<div class="zmodal_content" id="xxx" style="max-width:222px;">	)
		if($zmodal_content.css('max-width')!='none'){
			params.options.max_width = $zmodal_content.css('max-width');
		}
		
		// добавляем обертки вокруг контента модалки
		$zmodal_content.wrap('<div class="zmodal_window ' + params.options.wrapper_class + ' ' + params.options.loading + '" id="' + params.window_id + '" aria-hidden="true">');
		
		// параметр "nooverlayclose = true" отключает закрытие модалки по клику на оверлее
		// полезно для исключения случайных кликов и закрытия важных модалок
		var overlay_data_close_attr = params.options.nooverlayclose ? '' : 'data-close';
		
		$zmodal_content.wrap('<div class="zmodal_overlay" tabindex="-1" '+overlay_data_close_attr+'>');		

		$zmodal_content.wrap('<div class="zmodal ' + params.options.class + '" style="' + params.options.style + '" role="dialog">');
		$zmodal_content.wrap("<div class='zmodal_inner'></div>");
		
		// кнопка "закрыть"
		var zmodal_close = '\
			<button class="zmodal_close" type="button" data-close>\
				<svg class="zmodal_close_mobile" viewBox="0 0 18 18"><path d="m13.7 16.29a1 1 0 1 1 -1.42 1.41l-8-8a1 1 0 0 1 0-1.41l8-8a1 1 0 1 1 1.42 1.41l-7.29 7.29z" fill-rule="evenodd"></path></svg>\
				<svg class="zmodal_close_desktop" viewBox="0 0 32 32"><path d="m6 6 20 20"></path><path d="m26 6-20 20"></path></svg>\
				<span class="zmodal_close_desktop">✕</span>\
			</button>\
		';
		
		if(params.options.noclose){zmodal_close='';}
		
		// loader + header
		var zmodal_header_html = '\
			<div class="zmodal_loader"></div>\
			<div class="zmodal_header">' + zmodal_close + '</div>\
		';
		
		var $zmodal = $('#' + params.window_id).find('.zmodal');
		var $zmodal_overlay = $('#' + params.window_id).find('.zmodal_overlay');
		
		$zmodal.prepend(zmodal_header_html);
		
		// добавляем zindex
		$zmodal_overlay.css('z-index', (params.options.zindex + modals.length));
		
		if(params.options.max_width){
			$zmodal.css('max-width',  params.options.max_width);
		}

	}
	
	

	
	// ОТКРЫТЬ
	$.zmodal.open = function(params){
		
		params.options = $.extend({}, $.zmodal.defaults, params.options);
		
		// генерируем 'elm_id'
		if('elm_id' in params == false){
			params.elm_id = 'zmodal_' + rand_str(8);
		}
		
		// генерируем 'window_id'
		if('window_id' in params == false){
			params.window_id = params.elm_id + '_window';
		}

		// подготоваливаем разметку
		// если окно уже инициализировано - прекращаем		
		if($('#' + params.window_id).length==0){
			$.zmodal.wrap(params);	
		}

		var $modal_window = $('#' + params.window_id);

		// добавляем css-класс открытия
		$modal_window.addClass('is_open').attr('aria-hidden', false);

		// добавляем в массив открытых окон
		modals.push(params);
		
		isset_scroll = isset_page_scroll();
		
		// если на странице есть вертикальный скролл
		// и еще не установлен блокирующий класс 'noscroll'
		if(isset_scroll && !$('html.noscroll').length){
			var window_scroll = get_window_scroll();
			$('html').addClass('noscroll').data('scroll', window_scroll).css('top', '-' + window_scroll + 'px');
		}
		
		// добавляем событие 'open.zmodal'
		if(params.btn){params.btn.trigger('open.zmodal', false);}		
		
		// callback
		if(typeof (params.options.after_show) === 'function'){			
			params.options.after_show(params.elm_id);			
		}			
		
		// callback (после окончания анимации открытия)
		$('#' + params.window_id).find('.zmodal').on("animationend webkitAnimationEnd oAnimationEnd", function(e){
    		
			// отключаем
    		$(this).off(e);

			if(typeof (params.options.after_show_final) === 'function'){			
				params.options.after_show_final(params.elm_id);			
			}	

 		});
		
		// отпускание мыши за пределами окна
		var no_current_close = false;
		var mousedown_target = '';
		
		// обработчик нажатия мыши
		$modal_window.on('mousedown', function(event){						
			mousedown_target = $(event.target);		
		});
		
		// обработчик отпускания мыши
		$modal_window.on('mouseup', function(event){
			if(!$(mousedown_target).is('.zmodal_overlay') && $(event.target).is('.zmodal_overlay')){					
				no_current_close = true;
			}else{
				no_current_close = false;
			}
		});
			
		// обработчик закрытия			
		$modal_window.on('click', function(event){	

			if($(event.target).is(params.options.close_trigger) && !no_current_close){				
				$.zmodal.close(params);
			}
		});

		return $modal_window;
			
	};
	
	
	
	
	// ЗАКРЫТЬ
	$.zmodal.close = function(params) {		

		// обязательное закрытие 
		if(params===true){
			var important_close = true;
		}
		
		if(params===undefined || !params.window_id){
			// достаем самое верхнее открытое окно из массива
			params = getCurrent();
		}
		
		// окно запрещено закрывать
		if(!important_close && params.options.noclose){return;}

		var $modal_window = $('#' + params.window_id);
		
		// ждем окончания анимации
		if(params.options.awaitCloseAnimation){
			
			$modal_window.on('animationend', function(){
				$modal_window.removeClass('is_open');
				$modal_window.off('animationend');
				
				// если это аякс-контент, 
				// то после закрытия удаляем из DOM
				if(params.url){
					$modal_window.remove();
				}
				
			});

		} else {
			
			$modal_window.removeClass('is_open');
			
			// если это аякс-контент, 
			// то после закрытия удаляем из DOM
			if(params.url){
				$modal_window.remove();
			}
			
		}
		
		$modal_window.attr('aria-hidden', true);
			
		// удаляем последний из массива открытых окон
		modals.pop();
		
		// только если на странице есть вертикальный скролл
		if(isset_scroll){
			$('html').removeClass('noscroll');
			window.scrollTo(0, $('html').data('scroll'));
		}
			
		// удаляем обработчики мыши
		$modal_window.off('mouseup mousedown click');
		
		// добавляем событие 'close.zmodal'
		if(params.btn){params.btn.trigger('close.zmodal', false);}
		
		// добавляем событие 'close.zmodal' на сам элемент модалки
		if($('#'+params.elm_id).length!=0){
			$('#'+params.elm_id).trigger('close.zmodal', false);
		}

		// callback
		if(typeof (params.options.after_close) === 'function'){			
			params.options.after_close(params);			
		}	
		
	};
	
	
	
	// есть сейчас активное окно?
	$.zmodal.isActive = function () {
		return modals.length > 0;
	};
	
	
	// дефолтные настройки 
	$.zmodal.defaults = {
		close_trigger: '[data-close]',
		noclose: false,
		nooverlayclose: false,
		class: '',
		wrapper_class: '',
		style: '',
		loading: '',
		zindex: 9000,
		awaitCloseAnimation: true // перед закрытием ждем окончания css-анимации
	};
	
  
	// инициализация самого плагина
	$.fn.zmodal = function(options){	  

		$(document).on('click', this.selector, function(event){
			
			event.preventDefault();
			
			var btn = $(this);
			new $.zmodal(btn, options);
			
		});
		
	};

})(jQuery, window, document);