560 lines
18 KiB
JavaScript
560 lines
18 KiB
JavaScript
/**
|
|
* Provides a basic modal window capability
|
|
*
|
|
* Use this directly (as described below) or copy and paste into your Process module's
|
|
* javascript file and modify as needed.
|
|
*
|
|
* USE
|
|
* ===
|
|
* 1. In your module call $this->modules->get('JqueryUI')->use('modal');
|
|
* 2. For the <a> link or <button> you want to open a modal window, give it the class
|
|
* "pw-modal". For a larger modal window, also add class "pw-modal-large". Other options
|
|
* are "pw-modal-small" and "pw-modal-full". Default is "pw-modal-medium".
|
|
* If using a <button> the "href" attribute will be pulled from a containing <a> element.
|
|
* If button not wrapped with an <a>, it may also be specified in a "data-href" attribute
|
|
* on the button.
|
|
*
|
|
*
|
|
* ATTRIBUTES
|
|
* ==========
|
|
* Other attributes you may specify with your a.pw-modal or button.pw-modal:
|
|
*
|
|
* data-buttons: If you want to use modal window buttons, specify a jQuery selector
|
|
* that identifies these buttons in the modal window. The original buttons will be
|
|
* hidden in the modal content and moved outside to the modal window interface.
|
|
*
|
|
* data-autoclose: If also using data-buttons, this option will make clicking any
|
|
* of those buttons automatically close the modal window, after the next page loads.
|
|
* This enables save actions to take place. You may also populate this property with
|
|
* a jQuery selector, in which case autoclose will only take place if the button
|
|
* matches the given selector.
|
|
*
|
|
* data-close: Populate with a selector that matches the button (or buttons) that
|
|
* should immediately close the window (no follow-up save or anything).
|
|
*
|
|
* MODAL CONTENT
|
|
* =============
|
|
* The window opened in the modal may optionally create a button or link with the
|
|
* class 'pw-modal-cancel'. When clicked, the window will immediately close. This
|
|
* can also be one of your 'data-buttons' if you want it to.
|
|
*
|
|
* EVENTS
|
|
* ======
|
|
* When the window is closed, the a.pw-modal link receives a "pw-modal-closed" event
|
|
* with the arguments (event, ui).
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
var pwModalWindows = [];
|
|
|
|
/**
|
|
* Dialog function that returns settings ready for population to jQuery.dialog
|
|
*
|
|
* Settings include:
|
|
* - position
|
|
* - width
|
|
* - height
|
|
*
|
|
* @param name Name of modal settings to use: 'full', 'large', 'medium' or 'small'
|
|
* @returns {{position: *[], width: number, height: number}}
|
|
*
|
|
*/
|
|
function pwModalWindowSettings(name) {
|
|
|
|
var modal = ProcessWire.config.modals[name];
|
|
if(typeof modal == "undefined") modal = ProcessWire.config.modals['medium'];
|
|
modal = modal.split(',');
|
|
|
|
// options that can be customized via config.modals, with default values
|
|
var options = {
|
|
modal: true,
|
|
draggable: false,
|
|
resizable: true,
|
|
hide: 250,
|
|
show: 100,
|
|
hideOverflow: true,
|
|
closeOnEscape: false
|
|
}
|
|
|
|
if(modal.length >= 4) {
|
|
for(var n = 4; n < modal.length; n++) {
|
|
var val = modal[n];
|
|
if (val.indexOf('=') < 1) continue;
|
|
val = val.split('=');
|
|
var key = val[0].toString().trim();
|
|
val = val[1].toLowerCase().trim();
|
|
if (typeof options[key] == "undefined") continue;
|
|
if (val == "true" || val == "1") {
|
|
val = true;
|
|
} else if (val == "false" || val == "0") {
|
|
val = false;
|
|
} else {
|
|
val = parseInt(val);
|
|
}
|
|
options[key] = val;
|
|
}
|
|
}
|
|
|
|
if(jQuery.ui.version === '1.10.4') {
|
|
var position = [ parseInt(modal[0]), parseInt(modal[1]) ];
|
|
} else {
|
|
var position = { my: "left+" + modal[0] + " top+" + modal[1], at: "left top" };
|
|
}
|
|
|
|
var width = jQuery(window).width() - parseInt(modal[2]);
|
|
var height = jQuery(window).height() - parseInt(modal[3]);
|
|
|
|
var settings = {
|
|
modal: options.modal,
|
|
draggable: options.draggable,
|
|
resizable: options.resizable,
|
|
position: position,
|
|
width: width,
|
|
height: height,
|
|
hide: options.hide,
|
|
show: options.show,
|
|
closeOnEscape: options.closeOnEscape,
|
|
closeText: '',
|
|
create: function(event, ui) {
|
|
if(options.hideOverflow) {
|
|
if(typeof parent.jQuery != "undefined") {
|
|
parent.jQuery('body').css('overflow', 'hidden');
|
|
} else {
|
|
parent.document.querySelector('body').style.overflow = 'hidden';
|
|
}
|
|
}
|
|
// replace the jQuery ui close icon with a font-awesome equivalent (for hipdi support)
|
|
var $widget = jQuery(this).dialog("widget");
|
|
jQuery(".ui-dialog-titlebar-close", $widget)
|
|
.css('padding-top', 0)
|
|
.addClass('ui-state-default')
|
|
.prepend("<i class='fa fa-times'></i>")
|
|
.find('.ui-icon').remove();
|
|
if(frameElement) {
|
|
// dialog on top of dialog
|
|
if(typeof parent.jQuery !== 'undefined') {
|
|
// jQuery available in parent
|
|
if(parent.jQuery('.ui-dialog').length) {
|
|
parent.jQuery('.ui-dialog .ui-button').addClass('pw-modal-hidden').hide();
|
|
parent.jQuery('.ui-dialog-buttonpane').css('margin-top', '-10px');
|
|
jQuery('body').css('overflow', 'hidden');
|
|
}
|
|
} else {
|
|
// jQuery NOT available in parent
|
|
if(parent.document.querySelector('.ui-dialog')) {
|
|
var parentButtons = parent.document.querySelectorAll('.ui-dialog .ui-button');
|
|
var i;
|
|
for(i = 0; i < parentButtons.length; i++) {
|
|
parentButtons[i].classList.add('pw-modal-hidden');
|
|
parentButtons[i].style.display = 'none';
|
|
}
|
|
var parentPanes = parent.document.querySelectorAll('.ui-dialog-buttonpane');
|
|
for(i = 0; i < parentPanes.length; i++) {
|
|
parentPanes[i].style.marginTop = '-10px';
|
|
}
|
|
document.querySelector('body').style.overflow = 'hidden';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
beforeClose: function(event, ui) {
|
|
if(typeof parent.jQuery != 'undefined') {
|
|
if(parent.jQuery('.ui-dialog').length) {
|
|
if(frameElement) {
|
|
// dialog on top of another dialog
|
|
parent.jQuery('.pw-modal-hidden').show();
|
|
jQuery('body').css('overflow', '');
|
|
} else if(options.hideOverflow) {
|
|
parent.jQuery('body').css('overflow', '');
|
|
}
|
|
}
|
|
} else {
|
|
// no jQuery available
|
|
if(frameElement) {
|
|
// dialog on top of another dialog
|
|
var parentModalHidden = parent.document.querySelector('.pw-modal-hidden');
|
|
if(parentModalHidden) parentModalHidden.style.display = 'block';
|
|
document.querySelector('body').style.overflow = '';
|
|
} else if(options.hideOverflow) {
|
|
parent.document.querySelector('body').style.overflow = '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return settings;
|
|
};
|
|
|
|
/**
|
|
* Open a modal window and return the iframe/dialog object
|
|
*
|
|
* @param href URL to open
|
|
* @param options Settings to provide to jQuery UI dialog (if additional or overrides)
|
|
* @param size Specify one of: full, large, medium, small (default=medium)
|
|
* @returns jQuery $iframe
|
|
*
|
|
*/
|
|
function pwModalWindow(href, options, size) {
|
|
|
|
var $iframe, url;
|
|
|
|
// destory any existing pw-modals that aren't currently open
|
|
for(var n = 0; n <= pwModalWindows.length; n++) {
|
|
$iframe = pwModalWindows[n];
|
|
if($iframe == null) continue;
|
|
if($iframe.dialog('isOpen')) continue;
|
|
$iframe.dialog('destroy').remove();
|
|
pwModalWindows[n] = null;
|
|
}
|
|
|
|
if(href.indexOf('modal=') > 0) {
|
|
url = href;
|
|
} else {
|
|
url = href + (href.indexOf('?') > -1 ? '&' : '?') + 'modal=1';
|
|
}
|
|
$iframe = jQuery('<iframe class="pw-modal-window" frameborder="0" src="' + url + '"></iframe>');
|
|
$iframe.attr('id', 'pw-modal-window-' + (pwModalWindows.length+1));
|
|
pwModalWindows[pwModalWindows.length] = $iframe;
|
|
|
|
if(typeof size == "undefined" || size.length == 0) size = 'large';
|
|
var settings = pwModalWindowSettings(size);
|
|
|
|
if(settings == null) {
|
|
alert("Unknown modal setting: " + size);
|
|
return $iframe;
|
|
}
|
|
|
|
if(typeof options != "undefined") jQuery.extend(settings, options);
|
|
|
|
$iframe.on('dialogopen', function(event, ui) {
|
|
jQuery(document).trigger('pw-modal-opened', { event: event, ui: ui });
|
|
});
|
|
$iframe.on('dialogclose', function(event, ui) {
|
|
jQuery(document).trigger('pw-modal-closed', { event: event, ui: ui });
|
|
});
|
|
|
|
$iframe.dialog(settings);
|
|
$iframe.data('settings', settings);
|
|
$iframe.on('load', function() {
|
|
if(typeof settings.title == "undefined" || !settings.title) {
|
|
var title = jQuery('<textarea />').text($iframe.contents().find('title').text()).html();
|
|
$iframe.dialog('option', 'title', title);
|
|
}
|
|
$iframe.contents().find('form').css('-webkit-backface-visibility', 'hidden'); // to prevent jumping
|
|
});
|
|
|
|
var lastWidth = 0;
|
|
var lastHeight = 0;
|
|
|
|
function updateWindowSize() {
|
|
var width = jQuery(window).width();
|
|
var height = jQuery(window).height();
|
|
if((width == lastWidth && height == lastHeight) || !$iframe.hasClass('ui-dialog-content')) return;
|
|
var _size = size;
|
|
if(width <= 960 && size != 'full' && size != 'large') _size = 'large';
|
|
if(width <= 700 && size != 'full') _size = 'full';
|
|
var _settings = pwModalWindowSettings(_size);
|
|
var $dialog = $iframe.closest('.ui-dialog');
|
|
if($dialog.length > 0) {
|
|
var $buttonPane = $dialog.find('.ui-dialog-buttonpane');
|
|
var $titleBar = $dialog.find('.ui-dialog-titlebar');
|
|
var subtractHeight = 0;
|
|
if($buttonPane.length) subtractHeight += $buttonPane.outerHeight();
|
|
if($titleBar.length) subtractHeight += $titleBar.outerHeight();
|
|
if(subtractHeight) _settings.height -= subtractHeight;
|
|
}
|
|
$iframe.dialog('option', 'width', _settings.width);
|
|
$iframe.dialog('option', 'height', _settings.height);
|
|
$iframe.dialog('option', 'position', _settings.position);
|
|
$iframe.width(_settings.width).height(_settings.height);
|
|
lastWidth = width;
|
|
lastHeight = height;
|
|
}
|
|
updateWindowSize();
|
|
|
|
jQuery(window).on('resize', updateWindowSize);
|
|
|
|
$iframe.refresh = function() {
|
|
lastWidth = 0; // force update
|
|
lastHeight = 0;
|
|
updateWindowSize();
|
|
};
|
|
|
|
$iframe.setButtons = function(buttons) {
|
|
$iframe.dialog('option', 'buttons', buttons);
|
|
$iframe.refresh();
|
|
};
|
|
$iframe.setTitle = function(title) {
|
|
$iframe.dialog('option', 'title', jQuery('<textarea />').text(title).html());
|
|
};
|
|
|
|
return $iframe;
|
|
}
|
|
|
|
/**
|
|
* Event handler for when an action opens a pw modal window
|
|
*
|
|
* @param e
|
|
* @returns {boolean}
|
|
*
|
|
*/
|
|
function pwModalOpenEvent(e) {
|
|
|
|
var $a = jQuery(this);
|
|
var _autoclose = $a.attr('data-autoclose');
|
|
var autoclose = _autoclose != null; // whether autoclose is enabled
|
|
var autocloseSelector = autoclose && _autoclose.length > 1 ? _autoclose : ''; // autoclose only enabled if clicked button matches this selector
|
|
var closeSelector = $a.attr('data-close'); // immediately close window (no closeOnLoad) for buttons/links matching this selector
|
|
var closeOnLoad = false;
|
|
var modalSize = 'medium';
|
|
|
|
if($a.hasClass('pw-modal-large')) modalSize = 'large';
|
|
else if($a.hasClass('pw-modal-small')) modalSize = 'small';
|
|
else if($a.hasClass('pw-modal-full')) modalSize = 'full';
|
|
|
|
var settings = {
|
|
title: $a.attr('title'),
|
|
close: function(e, ui) {
|
|
// abort is true when the "x" button at top right of window is what closed the window
|
|
var abort = typeof e.originalEvent != "undefined" && jQuery(e.originalEvent.target).closest('.ui-dialog-titlebar-close').length > 0;
|
|
var eventData = {
|
|
event: e,
|
|
ui: ui,
|
|
abort: abort
|
|
};
|
|
$a.trigger('modal-close', eventData); // legacy, deprecated
|
|
$a.trigger('pw-modal-closed', eventData); // new
|
|
jQuery(document).trigger('pw-modal-closed', eventData);
|
|
$spinner.remove();
|
|
// console.log(eventData);
|
|
}
|
|
};
|
|
|
|
// attribute holding selector that determines what buttons to show, example: "#content form button.ui-button[type=submit]"
|
|
var buttonSelector = $a.attr('data-buttons');
|
|
|
|
// a class of pw-modal-cancel on one of the buttons always does an immediate close
|
|
if(closeSelector == null) closeSelector = '';
|
|
closeSelector += (closeSelector.length > 0 ? ', ' : '') + '.pw-modal-cancel';
|
|
|
|
var $spinner = jQuery("<i class='fa fa-spin fa-spinner fa-2x ui-priority-secondary'></i>")
|
|
.css({
|
|
'position': 'absolute',
|
|
'top': (parseInt(jQuery(window).height() / 2) - 80) + 'px',
|
|
'left': (parseInt(jQuery(window).width() / 2) - 20) + 'px',
|
|
'z-index': 9999
|
|
}).hide();
|
|
|
|
// first see if there is a specific/override href available
|
|
var href = $a.attr('data-pw-modal-href')
|
|
|
|
if(href && href.length) {
|
|
// use the data-pw-modal-href attribute
|
|
} else if($a.is('button')) {
|
|
var $aparent = $a.closest('a');
|
|
href = $aparent.length ? $aparent.attr('href') : $a.attr('data-href');
|
|
if(!href) href = $a.find('a').attr('href');
|
|
} else if($a.is('a')) {
|
|
href = $a.attr('href');
|
|
} else {
|
|
// some other element, we require a data-href attribute
|
|
href = $a.attr('data-href');
|
|
}
|
|
|
|
if(!href) {
|
|
alert("Unable to find href attribute for: " + $a.text());
|
|
return false;
|
|
}
|
|
|
|
var $iframe = pwModalWindow(href, settings, modalSize);
|
|
|
|
jQuery("body").append($spinner.fadeIn('fast'));
|
|
setTimeout(function() {
|
|
$a.removeClass('ui-state-active');
|
|
}, 500);
|
|
|
|
$iframe.on('load', function() {
|
|
|
|
var buttons = [];
|
|
var $icontents = $iframe.contents();
|
|
var n = 0;
|
|
|
|
$spinner.fadeOut('fast', function() {
|
|
$spinner.remove();
|
|
});
|
|
|
|
if(closeOnLoad) {
|
|
// this occurs when item saved and resulting page is loaded
|
|
var $errorItems = $icontents.find(".NoticeError, .ui-state-error");
|
|
if($errorItems.length == 0) {
|
|
// if there are no error messages present, close the window
|
|
if(typeof Notifications != "undefined") {
|
|
var messages = [];
|
|
$icontents.find(".NoticeMessage").each(function() {
|
|
messages[messages.length] = jQuery(this).text();
|
|
});
|
|
if(messages.length > 0) setTimeout(function() {
|
|
for(var i = 0; i < messages.length; i++) {
|
|
Notifications.message(messages[i]);
|
|
}
|
|
}, 500);
|
|
}
|
|
$iframe.dialog('close');
|
|
return;
|
|
} else {
|
|
// console.log('error items prevent modal autoclose:');
|
|
// console.log($errorItems);
|
|
}
|
|
}
|
|
|
|
var $body = $icontents.find('body');
|
|
$body.hide();
|
|
|
|
// copy buttons in iframe to dialog
|
|
if(buttonSelector) {
|
|
$icontents.find(buttonSelector).each(function() {
|
|
var $button = jQuery(this);
|
|
$button.find(".ui-button-text").removeClass("ui-button-text"); // prevent doubled
|
|
var text = $button.html();
|
|
var skip = false;
|
|
// avoid duplicate buttons
|
|
for(var i = 0; i < buttons.length; i++) {
|
|
if(buttons[i].text == text || text.length < 1) skip = true;
|
|
}
|
|
if(!skip) {
|
|
buttons[n] = {
|
|
'html': text,
|
|
'class': ($button.is('.ui-priority-secondary') ? 'ui-priority-secondary' : ''),
|
|
'click': function(e) {
|
|
// jQuery(e.currentTarget).fadeOut('fast');
|
|
$button.trigger('click');
|
|
if(closeSelector.length > 0 && $button.is(closeSelector)) {
|
|
// immediately close if matches closeSelector
|
|
$iframe.dialog('close');
|
|
}
|
|
if(autoclose) {
|
|
// automatically close on next page load
|
|
jQuery("body").append($spinner.fadeIn());
|
|
if(autocloseSelector.length > 1) {
|
|
closeOnLoad = $button.is(autocloseSelector); // if button matches selector
|
|
} else {
|
|
closeOnLoad = true; // tell it to close window on the next 'load' event
|
|
}
|
|
}
|
|
}
|
|
};
|
|
n++;
|
|
};
|
|
if(!$button.hasClass('pw-modal-button-visible')) $button.hide(); // hide button that is in interface
|
|
});
|
|
} // .pw-modal-buttons
|
|
|
|
// render buttons
|
|
if(buttons.length > 0) $iframe.setButtons(buttons);
|
|
|
|
$body.fadeIn('fast', function() {
|
|
$body.show(); // for Firefox, which ignores the fadeIn()
|
|
});
|
|
|
|
}); // $iframe.load
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* jQuery Double Tap
|
|
* Developer: Sergey Margaritov (sergey@margaritov.net)
|
|
* Date: 22.10.2013
|
|
* Based on jquery documentation http://learn.jquery.com/events/event-extensions/
|
|
*/
|
|
|
|
(function($){
|
|
|
|
$.event.special.pwdoubletap = {
|
|
bindType: 'touchend',
|
|
delegateType: 'touchend',
|
|
|
|
handle: function(event) {
|
|
var handleObj = event.handleObj,
|
|
targetData = jQuery.data(event.target),
|
|
now = new Date().getTime(),
|
|
delta = targetData.lastTouch ? now - targetData.lastTouch : 0,
|
|
delay = delay == null ? 300 : delay;
|
|
|
|
if (delta < delay && delta > 30) {
|
|
targetData.lastTouch = null;
|
|
event.type = handleObj.origType;
|
|
['clientX', 'clientY', 'pageX', 'pageY'].forEach(function(property) {
|
|
event[property] = event.originalEvent.changedTouches[0][property];
|
|
})
|
|
|
|
// let jQuery handle the triggering of "doubletap" event handlers
|
|
handleObj.handler.apply(this, arguments);
|
|
} else {
|
|
targetData.lastTouch = now;
|
|
}
|
|
}
|
|
};
|
|
|
|
})(jQuery);
|
|
|
|
function pwModalDoubleClick() {
|
|
// double click handler that still enables links within to work as single-click
|
|
var clicks = 0, timer = null, allowClick = false;
|
|
jQuery(document).on('click', '.pw-modal-dblclick a', function() {
|
|
var $a = jQuery(this);
|
|
if(allowClick) {
|
|
allowClick = false;
|
|
return true;
|
|
}
|
|
clicks++; //count clicks
|
|
if(clicks === 1) {
|
|
timer = setTimeout(function() {
|
|
clicks = 0;
|
|
allowClick = true;
|
|
$a[0].trigger('click');
|
|
return true;
|
|
}, 700);
|
|
} else {
|
|
clearTimeout(timer); // prevent single-click action
|
|
allowClick = false;
|
|
clicks = 0;
|
|
jQuery(this).closest('.pw-modal-dblclick').trigger('dblclick');
|
|
}
|
|
return false;
|
|
});
|
|
jQuery(document).on('dblclick', '.pw-modal-dblclick a', function(e) {
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
|
|
var isTouch = (('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
|
|
|
|
if(isTouch) {
|
|
jQuery(document).on('pwdoubletap', '.pw-modal-dblclick', pwModalOpenEvent);
|
|
}
|
|
}
|
|
|
|
jQuery(document).ready(function($) {
|
|
|
|
// enable titles with HTML in ui dialog
|
|
$.widget("ui.dialog", $.extend({}, $.ui.dialog.prototype, {
|
|
_title: function(title) {
|
|
if (!this.options.title ) {
|
|
title.html(" ");
|
|
} else {
|
|
title.html(this.options.title);
|
|
}
|
|
}
|
|
}));
|
|
|
|
$(document).on('pwdblclick', '.pw-modal-dblclick', pwModalOpenEvent);
|
|
$(document).on('click', '.pw-modal:not(.pw-modal-dblclick):not(.pw-modal-longclick)', pwModalOpenEvent);
|
|
$(document).on('dblclick', '.pw-modal-dblclick', pwModalOpenEvent);
|
|
$(document).on('longclick', '.pw-modal-longclick', pwModalOpenEvent);
|
|
|
|
pwModalDoubleClick();
|
|
|
|
});
|