New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Mini-Modal Functionality #52

Closed
influxweb opened this Issue Dec 4, 2018 · 0 comments

Comments

Projects
None yet
1 participant
@influxweb
Copy link
Contributor

influxweb commented Dec 4, 2018

A popular request when using the Mini-Modal component has been to have the ability to trigger the opening and/or closing of a modal programmatically without resorting to triggering a click event on a trigger item. To facilitate this, two new function calls have been added to the Mini-Modal component as well as the automatic adding of an element id attribute, should one not exist, in the scripts.js file.

To open a modal, you would call the following:

minimodal().openModal(document.querySelector('#ID_OF_THE_ELEMENT'));

To close a modal, you would call the following:

minimodal().closeModal(document.querySelector('#active-ID_OF_THE_ELEMENT'));

Additionally, you now have the ability to call a function when the modal has loaded and/or closed. The onLoaded and onClosed callbacks can be passed as options in your initialization string or via the open/close calls as referenced above.

To open a modal and fire a callback, you would call the following:

minimodal(document.querySelector('#ID_OF_THE_ELEMENT'), {
	onLoaded: function () {
		console.log('Mini-Modal has Loaded!')
	}
}).openModal();

To close a modal and fire a callback, you would call the following:

minimodal(null, {
	onClosed: function () {
		console.log('Mini-Modal has Closed!')
	}
}).closeModal(document.querySelector('#active-ID_OF_THE_ELEMENT'));

Finally, Mini-Modal has been set to load only when modal targets exist on the page, by default.

I have added this enhancement and it will be included in the next maintenance release. To make the update prior to the next release, you will need to update the contents of:

User Interface -> JavaScript Resources -> scripts at Line 300

var targets = document.querySelectorAll('[data-mini-modal]');

if (targets.length) {
	$.loadScript(theme_path + 'core/js/mini-modal.js', function () {
		for (var i = 0; i < targets.length; i += 1) {
			var modal = minimodal(targets[i], {
				// If you would like a global onLoaded callback, add your function here
				//onLoaded: function () {},
				// If you would like a global onClosed callback, add your function here
				//onClosed: function () {},
				// If you are using, and have, a Google Maps API Key, enter it here.
				googleMapsAPIKey: ''
			});

			if (!targets[i].id) {
				targets[i].id = 'miniModal_' + i;
			}
			modal.init();
		}
	});
}

User Interface -> JavaScript Resources -> mini-modal

/**
 * +-+-+-+-+-+-+-+-+-+-+
 * |m|i|n|i|-|m|o|d|a|l|
 * +-+-+-+-+-+-+-+-+-+-+
 *
 * Mini-modal is a lightweight and responsive light box plugin which supports images, YouTube, Vimeo, Google Maps,
 * Inline, Iframe, and AJAX content.
 *
 * Original minimodal.js script created by mdmoreau.
 * Version: 0.2.2
 * License: MIT
 * Homepage: https://github.com/mdmoreau/minimodal
 *
 * Updated version by Matt Zimmermann [https://github.com/influxweb]
 * Version: 1.0.0
 * License: MIT
 * Homepage: https://github.com/influxweb/minimodal
 *
 * The original script has been updated to allow for `inline` and `iframe` options. The CSS classes have been updated to
 * conform more with a name-spaced, BEM format.
 */

(function (root, factory) {
	'use strict';

	if (typeof define === 'function' && define.amd) {
		define([], factory);
	}
	else if (typeof module === 'object' && module.exports) {
		module.exports = factory();
	}
	else {
		root.minimodal = factory();
	}
}(this, function () {
	'use strict';

	var minimodal = function (target, options) {

		options = typeof options !== 'undefined' ? options : {};

		var _ = {};

		var option = function (property, value) {
			_.options[property] = typeof options[property] !== 'undefined' ? options[property] : value;
		};

		_.options = {};
		option('loadingHTML', 'Loading');
		option('previousButtonHTML', '&#139;');
		option('nextButtonHTML', '&#155;');
		option('closeButtonHTML', '&#215;');
		option('statusTimeout', 300);
		option('removeTimeout', 300);
		option('closeTimeout', 300);
		option('onLoaded', function () {});
		option('onClosed', function () {});
		option('googleMapsAPIKey', '');

		_.node = function (html) {
			var div = document.createElement('div');

			div.innerHTML = html;
			return div.firstChild;
		};

		_.setup = function (setTarget) {
			_.current = setTarget ? setTarget : target;
			_.minimodal = _.node('<div id="active-' + _.current.id + '" class="c-mini-modal" tabindex="0" role="dialog">');
			_.overlay = _.node('<div class="c-mini-modal__overlay">');
			_.viewport = _.node('<div class="c-mini-modal__viewport">');
			_.closeButton = _.node('<button class="c-mini-modal__close">' + _.options.closeButtonHTML + '</button>');
		};

		_.build = function () {
			_.minimodal.appendChild(_.overlay);
			_.minimodal.appendChild(_.viewport);
			_.minimodal.appendChild(_.closeButton);
			document.body.appendChild(_.minimodal);
			_.minimodal.focus();
			_.minimodal.classList.add('c-mini-modal--active');
		};

		_.close = function () {
			var minimodal = _.minimodal;

			minimodal.classList.remove('c-mini-modal--active');
			setTimeout(function () {
				if (minimodal.parentNode) {
					minimodal.parentNode.removeChild(minimodal);
				}
			}, _.options.closeTimeout);
			document.documentElement.classList.remove('has-active-mini-modal');
			document.removeEventListener('keyup', _.keypress);
			if (target) {
				target.focus();
			}
			_.options.onClosed(_.type());
		};

		_.focusTrap = function (e) {
			if (e.shiftKey) {
				if (_.minimodal === document.activeElement) {
					e.preventDefault();
					_.closeButton.focus();
				}
			}
			else {
				if (_.closeButton === document.activeElement) {
					e.preventDefault();
					_.minimodal.focus();
				}
			}
		};

		_.keypress = function (event) {
			var key = event.key || event.keyCode;

			/* Tab Key */
			if (key === 'Tab' || key === 9) {
				_.focusTrap(event);
			}
			/* Esc Key */
			else if (key === 'Escape' || key === 27) {
				_.close();
			}
			/* Right Arrow */
			else if (key === 'ArrowRight' || key === 37) {
				if (_.index > -1) {
					_.previous();
				}
			}
			/* Left Arrow */
			else if (key === 'ArrowLeft' || key === 39) {
				if (_.index > -1) {
					_.next();
				}
			}
		};

		_.listen = function () {
			_.overlay.addEventListener('click', _.close);
			_.closeButton.addEventListener('click', _.close);
			document.addEventListener('keyup', _.keypress);
		};

		_.reflow = function () {
			var x = _.minimodal.clientWidth;
		};

		_.loading = function () {
			_.status = _.node('<div class="c-mini-modal__status">' + _.options.loadingHTML + '</div>');
			_.item.appendChild(_.status);
			_.reflow();
			_.item.classList.add('c-mini-modal__item--loading');
		};

		_.loaded = function () {
			var status = _.status;

			document.documentElement.classList.add('has-active-mini-modal');
			setTimeout(function () {
				if (status.parentNode) {
					status.parentNode.removeChild(status);
				}
			}, _.options.statusTimeout);
			_.item.appendChild(_.content);
			if (_.current.getAttribute('title')) {
				_.caption = _.node('<div class="c-mini-modal__caption">' + _.current.getAttribute('title'));
				_.item.appendChild(_.caption);
			}
			_.item.classList.remove('c-mini-modal__item--loading');
			_.reflow();
			_.item.classList.add('c-mini-modal__item--loaded');
			_.openCallback();
			_.options.onLoaded(_.type());
		};

		_.openCallback = function () {
			var openCallback = _.current.hasAttribute('data-mini-modal-openCallback') ? _.current.getAttribute('data-mini-modal-openCallback') : null;

			if (typeof mivaJS[openCallback] === 'function') {
				//console.log('this is a function');
				_.item.setAttribute('data-hook', openCallback);
				mivaJS[openCallback](_.item.getAttribute('data-hook'));
			}
		};

		_.error = function () {
			_.status.innerHTML = 'Error loading resource';
		};

		_.selectorExists = function (selector) {
			try {
				return document.querySelector(selector).length !== 0;
			} catch (e) {
				return false;
			}
		};

		_.ajax = function () {
			var url = _.url;
			var request = new XMLHttpRequest();

			request.open('GET', url, true);
			request.onload = function () {
				if (url === _.url) {
					if (request.status >= 200 && request.status < 400) {
						var response = request.responseText;

						_.content = _.node('<div class="c-mini-modal__content"><div class="c-mini-modal__element">' + response);
						_.loaded();
					}
					else {
						_.error();
					}
				}
			};
			request.onerror = function () {
				if (url === _.url) {
					_.error();
				}
			};
			request.send();
		};

		_.googleMaps = function () {
			var src = 'https://www.google.com/maps/embed/v1/';
			var apiKey = _.options.googleMapsAPIKey;

			if (_.url.indexOf('/maps/place/') > -1) {
				var place = _.url.match('(?:/maps/place/)([^/]+)')[1];

				src += 'place?key=' + apiKey + '&q=' + place;
			}
			else {
				var coords = _.url.match('(?:/maps/@)([^z]+)')[1];

				coords = coords.split(',');

				var lat = coords[0];
				var long = coords[1];
				var zoom = coords[2];

				src += 'view?key=' + apiKey + '&center=' + lat + ',' + long + '&zoom=' + zoom + 'z';
			}
			_.content = _.node('<div class="c-mini-modal__content"><iframe class="c-mini-modal__element c-mini-modal__element--map" src="' + src + '" frameborder="0">');
			_.loaded();
		};

		_.youtube = function () {
			var id = _.url.split('v=')[1];

			_.content = _.node('<div class="c-mini-modal__content"><div class="c-mini-modal__element c-mini-modal__element--video"><iframe class="c-mini-modal__video" src="https://www.youtube.com/embed/' + id + '" frameborder="0" allowfullscreen>');
			_.loaded();
		};

		_.vimeo = function () {
			var id = _.url.split('vimeo.com/')[1];

			_.content = _.node('<div class="c-mini-modal__content"><div class="c-mini-modal__element c-mini-modal__element--video"><iframe class="c-mini-modal__video" src="https://player.vimeo.com/video/' + id + '" frameborder="0" allowfullscreen>');
			_.loaded();
		};

		_.iframe = function () {
			var url = _.url;

			if (url === _.url) {
				_.content = _.node('<div class="c-mini-modal__content"><div class="c-mini-modal__element c-mini-modal__element--iframe"><iframe class="c-mini-modal__iframe" src="' + url + '" frameborder="0" allowfullscreen>');
				_.loaded();
			}
		};

		_.inline = function () {
			var url = _.url;

			if (url === _.url) {
				if (_.selectorExists(url) || _.selectorExists('[' + _.current.getAttribute('data-mini-modal-content') + ']')) {
					var inlineContent;

					if (_.selectorExists(url)) {
						inlineContent = document.querySelector(url);
					}
					else {
						inlineContent = document.querySelector('[' + _.current.getAttribute('data-mini-modal-content') + ']');
					}

					if (inlineContent.nodeName.toLowerCase() === 'img') {
						var img = document.createElement('img');
						var imgSrc = inlineContent.getAttribute('src');
						var imgAlt = inlineContent.getAttribute('alt');

						img.onload = function () {
							if (url === _.url) {
								_.content = _.node('<div class="c-mini-modal__content"><img class="c-mini-modal__element" src="' + imgSrc + '" alt="' + imgAlt + '">');
								_.loaded();
							}
						};
						img.onerror = function () {
							if (url === _.url) {
								_.error();
							}
						};
						img.src = imgSrc;
					}
					else {
						_.content = _.node('<div class="c-mini-modal__content"><div class="c-mini-modal__element">' + inlineContent.innerHTML);
						_.loaded();
					}
				}
				else {
					_.iframe();
				}
			}
		};

		_.image = function () {
			var url = _.url;
			var img = document.createElement('img');

			img.onload = function () {
				if (url === _.url) {
					_.content = _.node('<div class="c-mini-modal__content"><img class="c-mini-modal__element" src="' + _.url + '">');
					_.loaded();
				}
			};
			img.onerror = function () {
				if (url === _.url) {
					_.error();
				}
			};
			img.src = url;
		};

		_.type = function () {
			if (_.current.getAttribute('data-mini-modal-type')) {
				return _.current.getAttribute('data-mini-modal-type');
			}
			else if (_.url.indexOf('google.com/maps') > -1) {
				return 'googleMaps';
			}
			else if (_.url.indexOf('youtube.com') > -1) {
				return 'youtube';
			}
			else if (_.url.indexOf('vimeo.com') > -1) {
				return 'vimeo';
			}
			else {
				return 'image';
			}
		};

		_.load = function (change) {
			_.url = _.current.getAttribute('href');
			_.item = _.node('<div class="c-mini-modal__item">');
			_.viewport.appendChild(_.item);
			if (change) {
				_.item.classList.add('c-mini-modal__item--added');
				_.item.classList.add('c-mini-modal__item--added--' + change);
				_.reflow();
				_.item.classList.remove('c-mini-modal__item--added');
				_.item.classList.remove('c-mini-modal__item--added--' + change);
			}
			_.loading();
			_[_.type()]();
		};

		_.remove = function (change) {
			var item = _.item;

			item.classList.add('c-mini-modal__item--removed');
			item.classList.add('c-mini-modal__item--removed--' + change);
			setTimeout(function () {
				if (item.parentNode) {
					item.parentNode.removeChild(item);
				}
			}, _.options.removeTimeout);
		};

		_.update = function (change) {
			_.remove(change);
			_.current = _.groupItems[_.index];
			_.load(change);
		};

		_.previous = function () {
			if (_.index - 1 < 0) {
				_.index = _.indexMax;
			}
			else {
				_.index -= 1;
			}
			_.update('previous');
		};

		_.next = function () {
			if (_.index + 1 > _.indexMax) {
				_.index = 0;
			}
			else {
				_.index += 1;
			}
			_.update('next');
		};

		_.nav = function () {
			_.previousButton = _.node('<button class="c-mini-modal__nav c-mini-modal__nav--previous">' + _.options.previousButtonHTML + '</button>');
			_.nextButton = _.node('<button class="c-mini-modal__nav c-mini-modal__nav--next">' + _.options.nextButtonHTML + '</button>');
			_.minimodal.insertBefore(_.previousButton, _.closeButton);
			_.minimodal.insertBefore(_.nextButton, _.closeButton);
			_.previousButton.addEventListener('click', _.previous);
			_.nextButton.addEventListener('click', _.next);
		};

		_.group = function () {
			_.groupID = _.current.getAttribute('data-mini-modal');
			if (_.groupID) {
				_.groupItems = document.querySelectorAll('[data-mini-modal="' + _.groupID + '"]');
				if (_.groupItems.length > 1) {
					_.index = Array.prototype.indexOf.call(_.groupItems, _.current);
					_.indexMax = _.groupItems.length - 1;
					_.nav();
				}
			}
		};

		_.open = function (e) {
			e.preventDefault();
			_.setup();
			_.build();
			_.listen();
			_.load();
			_.group();
		};

		_.openModal = function (setTarget) {
			_.setup(setTarget);
			_.build();
			_.listen();
			_.load();
			_.group();
		};

		_.closeModal = function (activeModal) {
			activeModal.classList.remove('c-mini-modal--active');
			setTimeout(function () {
				if (activeModal.parentNode) {
					activeModal.parentNode.removeChild(activeModal);
				}
			}, _.options.closeTimeout);
			document.documentElement.classList.remove('has-active-mini-modal');
			document.removeEventListener('keyup', _.keypress);
			_.options.onClosed();
		};

		_.init = function () {
			target.addEventListener('click', _.open);
		};

		return _;

	};

	return minimodal;

}));

@influxweb influxweb added this to the v1.0.4 milestone Dec 4, 2018

@influxweb influxweb self-assigned this Dec 4, 2018

@influxweb influxweb closed this Dec 4, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment