Team:SYSU-China/js/jquery.remodal
From 2014.igem.org
- (function ($) {
"use strict";
/** * Remodal settings */ var pluginName = "remodal", defaults = { hashTracking: true, closeOnConfirm: true, closeOnCancel: true, closeOnEscape: true, closeOnAnyClick: true };
/** * Special plugin object for instances. * @type {Object} */ $[pluginName] = { lookup: [] };
var current, // current modal scrollTop; // scroll position
/** * Get transition duration in ms * @return {Number} */ var getTransitionDuration = function ($elem) { var duration = $elem.css("transition-duration") || $elem.css("-webkit-transition-duration") || $elem.css("-moz-transition-duration") || $elem.css("-o-transition-duration") || $elem.css("-ms-transition-duration") || 0; var delay = $elem.css("transition-delay") || $elem.css("-webkit-transition-delay") || $elem.css("-moz-transition-delay") || $elem.css("-o-transition-delay") || $elem.css("-ms-transition-delay") || 0;
return (parseFloat(duration) + parseFloat(delay)) * 1000; };
/** * Get a scrollbar width * @return {Number} */ var getScrollbarWidth = function () { if ($(document.body).height() <= $(window).height()) { return 0; }
var outer = document.createElement("div"); outer.style.visibility = "hidden"; outer.style.width = "100px"; document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth; // force scrollbars outer.style.overflow = "scroll";
// add innerdiv var inner = document.createElement("div"); inner.style.width = "100%"; outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
// remove divs outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll; };
/** * Lock screen */ var lockScreen = function () { $(document.body).css("padding-right", "+=" + getScrollbarWidth()); $("html, body").addClass(pluginName + "_lock"); };
/** * Unlock screen */ var unlockScreen = function () { $(document.body).css("padding-right", "-=" + getScrollbarWidth()); $("html, body").removeClass(pluginName + "_lock"); };
/** * Parse string with options * @param str * @returns {Object} */ var parseOptions = function (str) { var obj = {}, clearedStr, arr;
// remove spaces before and after delimiters clearedStr = str.replace(/\s*:\s*/g, ":").replace(/\s*,\s*/g, ",");
// parse string arr = clearedStr.split(","); var i, len, val; for (i = 0, len = arr.length; i < len; i++) { arr[i] = arr[i].split(":"); val = arr[i][1];
// convert string value if it is like a boolean if (typeof val === "string" || val instanceof String) { val = val === "true" || (val === "false" ? false : val); }
// convert string value if it is like a number if (typeof val === "string" || val instanceof String) { val = !isNaN(val) ? +val : val; }
obj[arr[i][0]] = val; }
return obj; };
/** * Remodal constructor */ function Remodal(modal, options) { this.settings = $.extend({}, defaults, options); this.modal = modal; this.buildDOM(); this.addEventListeners(); this.index = $[pluginName].lookup.push(this) - 1; this.busy = false; }
/** * Build required DOM */ Remodal.prototype.buildDOM = function () { this.body = $(document.body); this.bg = $("." + pluginName + "-bg"); this.modalClose = $("<a href='#'>").addClass(pluginName + "-close");this.overlay = $("
").addClass(pluginName + "-overlay");
if (!this.modal.hasClass(pluginName)) { this.modal.addClass(pluginName); }
this.modal.css("visibility", "visible"); this.modal.append(this.modalClose); this.overlay.append(this.modal); this.body.append(this.overlay);
this.confirm = this.modal.find("." + pluginName + "-confirm"); this.cancel = this.modal.find("." + pluginName + "-cancel");
var tdOverlay = getTransitionDuration(this.overlay), tdModal = getTransitionDuration(this.modal), tdBg = getTransitionDuration(this.bg); this.td = tdModal > tdOverlay ? tdModal : tdOverlay; this.td = tdBg > this.td ? tdBg : this.td; };
/** * Add event listeners to the current modal window */ Remodal.prototype.addEventListeners = function () { var self = this;
this.modalClose.bind("click." + pluginName, function (e) { e.preventDefault(); self.close(); });
this.cancel.bind("click." + pluginName, function (e) { e.preventDefault(); self.modal.trigger("cancel"); if (self.settings.closeOnCancel) { self.close(); } });
this.confirm.bind("click." + pluginName, function (e) { e.preventDefault(); self.modal.trigger("confirm"); if (self.settings.closeOnConfirm) { self.close(); } });
$(document).bind("keyup." + pluginName, function (e) { if (e.keyCode === 27 && self.settings.closeOnEscape) { self.close(); } });
this.overlay.bind("click." + pluginName, function (e) { var $target = $(e.target); if (!$target.hasClass(pluginName + "-overlay")) { return; }
if (self.settings.closeOnAnyClick) { self.close(); } }); };
/** * Open modal window */ Remodal.prototype.open = function () { // check if animation is complete if (this.busy) { return; } this.busy = true;
this.modal.trigger("open");
var id = this.modal.attr("data-" + pluginName + "-id"); if (id && this.settings.hashTracking) { scrollTop = $(window).scrollTop(); location.hash = id; }
if (current && current !== this) { current.overlay.hide(); current.body.removeClass(pluginName + "_active"); } current = this;
lockScreen(); this.overlay.show();
var self = this; setTimeout(function () { self.body.addClass(pluginName + "_active");
setTimeout(function () { self.busy = false; self.modal.trigger("opened"); }, self.td + 50); }, 25); };
/** * Close modal window */ Remodal.prototype.close = function () { // check if animation is complete if (this.busy) { return; } this.busy = true;
this.modal.trigger("close");
if (this.settings.hashTracking && this.modal.attr("data-" + pluginName + "-id") === location.hash.substr(1)) { location.hash = ""; $(window).scrollTop(scrollTop); }
this.body.removeClass(pluginName + "_active");
var self = this; setTimeout(function () { self.overlay.hide(); unlockScreen();
self.busy = false; self.modal.trigger("closed"); }, self.td + 50); };
if ($) { $.fn[pluginName] = function (opts) { var instance; this.each(function (i, e) { var $e = $(e); if ($e.data(pluginName) == null) { instance = new Remodal($e, opts); $e.data(pluginName, instance.index);
if (instance.settings.hashTracking && $e.attr("data-" + pluginName + "-id") === location.hash.substr(1)) { instance.open(); } } });
return instance; }; }
$(document).ready(function () { /** * data-remodal-target opens a modal window with a special id without hash change. */ $(document).on("click", "[data-" + pluginName + "-target]", function (e) { e.preventDefault();
var elem = e.currentTarget, id = elem.getAttribute("data-" + pluginName + "-target"), $target = $("[data-" + pluginName + "-id=" + id + "]");
$[pluginName].lookup[$target.data(pluginName)].open(); });
/** * Auto initialization of modal windows. * They should have the 'remodal' class attribute. * Also you can pass params into the modal throw the data-remodal-options attribute. * data-remodal-options must be a valid JSON string. */ $(document).find("." + pluginName).each(function (i, container) { var $container = $(container), options = $container.data(pluginName + "-options");
if (!options) { options = {}; } else if (typeof options === "string" || options instanceof String) { options = parseOptions(options); }
$container[pluginName](options); }); });
/** * Hashchange handling to show a modal with a special id. */ var hashHandler = function (e, closeOnEmptyHash) { var id = location.hash.replace("#", "");
if (typeof closeOnEmptyHash === "undefined") { closeOnEmptyHash = true; }
if (!id) { if (closeOnEmptyHash) { // check if we have currently opened modal and animation is complete if (current && !current.busy && current.settings.hashTracking) { current.close(); } } } else { var $elem;
// Catch syntax error if your hash is bad try { $elem = $("[data-" + pluginName + "-id=" + id.replace(new RegExp("/", "g"), "\\/") + "]"); } catch (e) {}
if ($elem && $elem.length) { var instance = $[pluginName].lookup[$elem.data(pluginName)];
if (instance && instance.settings.hashTracking) { instance.open(); } }
} }; $(window).bind("hashchange." + pluginName, hashHandler);})(window.jQuery || window.Zepto);