$.fn.romoModal = function() {
return $.map(this, function(element) {
return new RomoModal(element);
});
}
var RomoModal = function(element) {
this.elem = $(element);
this.doInitPopup();
this.romoInvoke = this.elem.romoInvoke()[0];
this.romoInvoke.doUnBindInvoke(); // disable auto invoke on click
if (this.elem.data('romo-modal-disable-click-invoke') !== true) {
this.elem.unbind('click');
this.elem.on('click', $.proxy(this.onToggleClick, this));
}
this.elem.on('modal:triggerToggle', $.proxy(this.onToggleClick, this));
this.elem.on('modal:triggerPopupOpen', $.proxy(this.onPopupOpen, this));
this.elem.on('modal:triggerPopupClose', $.proxy(this.onPopupClose, this));
this.elem.on('invoke:loadStart', $.proxy(function(e, invoke) {
this.doLoadBodyStart();
return false;
}, this));
this.elem.on('invoke:loadSuccess', $.proxy(function(e, data, invoke) {
this.doLoadBodySuccess(data);
return false;
}, this));
this.elem.on('invoke:loadError', $.proxy(function(e, xhr, invoke) {
this.doLoadBodyError(xhr);
return false;
}, this));
this.doBindElemKeyUp();
this.doInit();
this.doInitBody();
this.elem.trigger('modal:ready', [this]);
}
RomoModal.prototype.doInit = function() {
// override as needed
}
RomoModal.prototype.doInitPopup = function() {
this.popupElem = $('
');
this.popupElem.appendTo(this.elem.closest(this.elem.data('romo-modal-append-to-closest') || 'body'));
this.bodyElem = this.popupElem.find('> .romo-modal-body');
if (this.elem.data('romo-modal-style-class') !== undefined) {
this.bodyElem.addClass(this.elem.data('romo-modal-style-class'));
}
this.contentElem = $();
this.closeElem = $();
this.dragElem = $();
// the popup should be treated like a child elem. add it to Romo's
// parent-child elems so it will be removed when the elem is removed.
// delay adding it b/c other components may `append` generated modals
// meaning the modal is removed and then re-added. if added immediately
// the "remove" part will incorrectly remove the popup.
setTimeout($.proxy(function() {
Romo.parentChildElems.add(this.elem, [this.popupElem]);
}, this), 1);
}
RomoModal.prototype.doInitBody = function() {
this.doResetBody();
this.contentElem = this.bodyElem.find('.romo-modal-content').last();
if (this.contentElem.size() === 0) {
this.contentElem = this.bodyElem;
}
this.closeElem = this.popupElem.find('[data-romo-modal-close="true"]');
this.closeElem.unbind('click');
this.closeElem.on('click', $.proxy(this.onPopupClose, this));
this.dragElem = this.popupElem.find('[data-romo-modal-drag="true"]');
this.dragElem.addClass('romo-modal-grab');
this.dragElem.on('mousedown', $.proxy(this.onMouseDown, this));
var css = {
'min-width': this.elem.data('romo-modal-min-width'),
'max-width': this.elem.data('romo-modal-max-width'),
'width': this.elem.data('romo-modal-width'),
'min-height': this.elem.data('romo-modal-min-height'),
'height': this.elem.data('romo-modal-height'),
'overflow-x': 'auto',
'overflow-y': 'auto'
}
if (this.elem.data('romo-modal-max-height') === undefined) {
this.elem.attr('data-romo-modal-max-height', 'detect');
}
if (this.elem.data('romo-modal-max-height') !== 'detect') {
css['max-height'] = this.elem.data('romo-modal-max-height');
}
this.contentElem.css(css);
}
RomoModal.prototype.doResetBody = function() {
this.contentElem.css({
'min-width': '',
'max-width': '',
'width': '',
'min-height': '',
'max-height': '',
'height': '',
'overflow': ''
});
this.closeElem.off('click', $.proxy(this.onPopupClose, this));
}
RomoModal.prototype.doLoadBodyStart = function() {
this.bodyElem.html('');
this.doInitBody();
this.doPlacePopupElem();
this.elem.trigger('modal:loadBodyStart', [this]);
}
RomoModal.prototype.doLoadBodySuccess = function(data) {
Romo.initHtml(this.bodyElem, data);
this.doInitBody();
this.doPlacePopupElem();
this.elem.trigger('modal:loadBodySuccess', [data, this]);
}
RomoModal.prototype.doLoadBodyError = function(xhr) {
this.elem.trigger('modal:loadBodyError', [xhr, this]);
}
RomoModal.prototype.onToggleClick = function(e) {
if (e !== undefined) {
e.preventDefault();
}
if (this.elem.hasClass('disabled') === false) {
this.doToggle();
}
}
RomoModal.prototype.doToggle = function() {
if (this.popupElem.hasClass('romo-modal-open')) {
setTimeout($.proxy(function() {
this.doPopupClose();
}, this), 100);
} else {
setTimeout($.proxy(function() {
this.doPopupOpen();
}, this), 100);
}
this.elem.trigger('modal:toggle', [this]);
}
RomoModal.prototype.onPopupOpen = function(e) {
if (e !== undefined) {
e.preventDefault();
}
if ((this.elem.hasClass('disabled') === false) &&
(this.popupElem.hasClass('romo-modal-open') === false)) {
setTimeout($.proxy(function() {
this.doPopupOpen();
}, this), 100);
}
}
RomoModal.prototype.doPopupOpen = function() {
if (this.elem.data('romo-modal-content-elem') !== undefined) {
this.doLoadBodySuccess($(this.elem.data('romo-modal-content-elem')).html())
} else {
this.romoInvoke.doInvoke();
}
this.popupElem.addClass('romo-modal-open');
this.doPlacePopupElem();
// bind an event to close the popup when clicking away from the
// popup. Bind on a timeout to allow time for any toggle
// click event to propagate. If no timeout, we'll bind this
// event, then the toggle click will propagate which will call
// this event and immediately close the popup.
setTimeout($.proxy(function() {
this.doBindWindowBodyClick();
}, this), 100);
// bind "esc" keystroke to toggle close
this.doBindWindowBodyKeyUp();
// bind window resizes reposition modal
$(window).on('resize', $.proxy(this.onResizeWindow, this));
this.elem.trigger('modal:popupOpen', [this]);
}
RomoModal.prototype.onPopupClose = function(e) {
if (e !== undefined) {
e.preventDefault();
}
if (this.elem.hasClass('disabled') === false) {
setTimeout($.proxy(function() {
this.doPopupClose();
}, this), 100);
}
}
RomoModal.prototype.doPopupClose = function() {
$('body').trigger('modal:popupclose');
this.popupElem.removeClass('romo-modal-open');
// unbind any event to close the popup when clicking away from it
this.doUnBindWindowBodyClick();
// unbind "esc" keystroke to toggle close
this.doUnBindWindowBodyKeyUp();
// unbind window resizes reposition modal
$(window).off('resize', $.proxy(this.onResizeWindow, this));
// clear the content elem markup if configured to
if (this.elem.data('romo-modal-clear-content') === true) {
this.contentElem.html('');
}
this.elem.trigger('modal:popupClose', [this]);
}
RomoModal.prototype.onMouseDown = function(e) {
e.preventDefault();
e.stopPropagation();
this.doDragStart(e);
return false;
}
RomoModal.prototype.doDragStart = function(e) {
this.dragElem.addClass('romo-modal-grabbing');
this.dragElem.removeClass('romo-modal-grab');
this.popupElem.css('width', this.popupElem.width()+'px');
this.popupElem.css('height', this.popupElem.height()+'px');
this._dragDiffX = e.clientX - this.popupElem[0].offsetLeft;
this._dragDiffY = e.clientY - this.popupElem[0].offsetTop;
$(window).on('mousemove', $.proxy(this.onMouseMove, this));
$(window).on('mouseup', $.proxy(this.onMouseUp, this));
this.elem.trigger("modal:dragStart", [this]);
}
RomoModal.prototype.onMouseMove = function(e) {
$('body').trigger('modal:mousemove');
e.preventDefault();
e.stopPropagation();
this.doDragMove(e.clientX, e.clientY);
return false;
}
RomoModal.prototype.doDragMove = function(clientX, clientY) {
var placeX = clientX - this._dragDiffX;
var placeY = clientY - this._dragDiffY;
this.popupElem.css({ left: placeX+'px' , top: placeY+'px' });
this.elem.trigger("modal:dragMove", [placeX, placeY, this]);
}
RomoModal.prototype.onMouseUp = function(e) {
e.preventDefault();
e.stopPropagation();
this.doDragStop(e);
return false;
}
RomoModal.prototype.doDragStop = function(e) {
this.dragElem.addClass('romo-modal-grab');
this.dragElem.removeClass('romo-modal-grabbing');
this.popupElem.css('width', '');
this.popupElem.css('height', '');
$(window).off('mousemove', $.proxy(this.onMouseMove, this));
$(window).off('mouseup', $.proxy(this.onMouseUp, this));
delete this._dragDiffX;
delete this._dragDiffY;
this.elem.trigger("modal:dragStop", [this]);
}
RomoModal.prototype.doBindElemKeyUp = function() {
this.elem.on('keyup', $.proxy(this.onElemKeyUp, this));
this.popupElem.on('keyup', $.proxy(this.onElemKeyUp, this));
}
RomoModal.prototype.doUnBindElemKeyUp = function() {
this.elem.off('keyup', $.proxy(this.onElemKeyUp, this));
this.popupElem.off('keyup', $.proxy(this.onElemKeyUp, this));
}
RomoModal.prototype.onElemKeyUp = function(e) {
if (this.elem.hasClass('disabled') === false) {
if (this.popupElem.hasClass('romo-modal-open')) {
if(e.keyCode === 27 /* Esc */ ) {
this.doPopupClose();
return false;
} else {
return true;
}
} else {
return true;
}
}
return true;
}
RomoModal.prototype.doBindWindowBodyClick = function() {
$('body').on('click', $.proxy(this.onWindowBodyClick, this));
}
RomoModal.prototype.doUnBindWindowBodyClick = function() {
$('body').off('click', $.proxy(this.onWindowBodyClick, this));
}
RomoModal.prototype.onWindowBodyClick = function(e) {
// if not clicked on the popup elem
if (e !== undefined && $(e.target).parents('.romo-modal-popup').size() === 0) {
this.doPopupClose();
}
return true;
}
RomoModal.prototype.doBindWindowBodyKeyUp = function() {
$('body').on('keyup', $.proxy(this.onWindowBodyKeyUp, this));
}
RomoModal.prototype.doUnBindWindowBodyKeyUp = function() {
$('body').off('keyup', $.proxy(this.onWindowBodyKeyUp, this));
}
RomoModal.prototype.onWindowBodyKeyUp = function(e) {
if (e.keyCode === 27 /* Esc */) {
this.doPopupClose();
}
return true;
}
RomoModal.prototype.onResizeWindow = function(e) {
this.doPlacePopupElem();
return true;
}
RomoModal.prototype.doPlacePopupElem = function() {
var w = this.popupElem[0].offsetWidth;
var h = this.popupElem[0].offsetHeight;
var min = 75;
var centerTop = $(window).height() / 2 - h / 2;
var centerLeft = $(window).width() / 2 - w / 2;
var css = {};
css.top = $(window).height() * 0.15;
if (centerTop < css.top) { css.top = centerTop; }
if (css.top < min) { css.top = min; }
css.left = centerLeft;
if (css.left < min) { css.left = min; }
this.popupElem.css(css);
if (this.elem.data('romo-modal-max-height') === 'detect') {
var pad = this.elem.data('romo-modal-max-height-detect-pad') || 10;
var contentTop = this.contentElem[0].getBoundingClientRect().top;
var contentBottom = this.contentElem[0].getBoundingClientRect().bottom;
var bodyBottom = this.bodyElem[0].getBoundingClientRect().bottom;
var padBottom = bodyBottom - contentBottom;
var maxHeight = $(window).height() - contentTop - padBottom - pad;
this.contentElem.css({'max-height': maxHeight.toString() + 'px'});
}
}
Romo.onInitUI(function(e) {
Romo.initUIElems(e, '[data-romo-modal-auto="true"]').romoModal();
});