466 lines
13 KiB
JavaScript
466 lines
13 KiB
JavaScript
/*!
|
|
* Bootstrap Confirmation
|
|
* Copyright 2013 Nimit Suwannagate <ethaizone@hotmail.com>
|
|
* Copyright 2014-2019 Damien "Mistic" Sorel <contact@git.strangeplanet.fr>
|
|
* Licensed under the Apache License, Version 2.0
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
var activeConfirmation;
|
|
|
|
// Confirmation extends popover.js
|
|
if (!$.fn.popover) {
|
|
throw new Error('Confirmation requires popover.js');
|
|
}
|
|
|
|
// CONFIRMATION PUBLIC CLASS DEFINITION
|
|
// ===============================
|
|
var Confirmation = function(element, options) {
|
|
this.init(element, options);
|
|
};
|
|
|
|
Confirmation.VERSION = '2.4.4';
|
|
|
|
/**
|
|
* Map between keyboard events "keyCode|which" and "key"
|
|
*/
|
|
Confirmation.KEYMAP = {
|
|
13: 'Enter',
|
|
27: 'Escape',
|
|
39: 'ArrowRight',
|
|
40: 'ArrowDown'
|
|
};
|
|
|
|
Confirmation.DEFAULTS = $.extend({}, $.fn.popover.Constructor.DEFAULTS, {
|
|
placement: 'top',
|
|
title: 'Are you sure?',
|
|
trigger: 'click',
|
|
confirmationEvent: undefined,
|
|
popout: false,
|
|
singleton: false,
|
|
copyAttributes: 'href target',
|
|
buttons: null,
|
|
onConfirm: $.noop,
|
|
onCancel: $.noop,
|
|
btnOkClass: 'btn-sm btn-primary',
|
|
btnOkIcon: 'icon-ok',
|
|
btnOkLabel: 'Yes',
|
|
btnCancelClass: 'btn-sm btn-danger',
|
|
btnCancelIcon: 'icon-remove',
|
|
btnCancelLabel: 'No',
|
|
// @formatter:off
|
|
// href="#" allows the buttons to be focused
|
|
template: '<div class="popover confirmation">' +
|
|
'<div class="arrow"></div>' +
|
|
'<h3 class="popover-title"></h3>' +
|
|
'<div class="popover-content">' +
|
|
'<p class="confirmation-content"></p>' +
|
|
'<div class="confirmation-buttons text-center">' +
|
|
'<div class="btn-group">' +
|
|
'<a href="#" class="btn" data-apply="confirmation"></a>' +
|
|
'<a href="#" class="btn" data-dismiss="confirmation"></a>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>'
|
|
// @formatter:on
|
|
});
|
|
|
|
if (Confirmation.DEFAULTS.whiteList) {
|
|
Confirmation.DEFAULTS.whiteList['*'].push('data-apply', 'data-dismiss');
|
|
}
|
|
|
|
Confirmation.prototype = $.extend({}, $.fn.popover.Constructor.prototype);
|
|
Confirmation.prototype.constructor = Confirmation;
|
|
|
|
/**
|
|
* Expose defaults
|
|
* @returns {object}
|
|
*/
|
|
Confirmation.prototype.getDefaults = function() {
|
|
return Confirmation.DEFAULTS;
|
|
};
|
|
|
|
/**
|
|
* Init the component
|
|
* @param element {jQuery}
|
|
* @param options {object}
|
|
*/
|
|
Confirmation.prototype.init = function(element, options) {
|
|
$.fn.popover.Constructor.prototype.init.call(this, 'confirmation', element, options);
|
|
|
|
if ((this.options.popout || this.options.singleton) && !options.rootSelector) {
|
|
throw new Error('The rootSelector option is required to use popout and singleton features since jQuery 3.');
|
|
}
|
|
|
|
// keep trace of selectors
|
|
this.options._isDelegate = false;
|
|
if (options.selector) { // container of buttons
|
|
this.options._selector = this._options._selector = options.rootSelector + ' ' + options.selector;
|
|
}
|
|
else if (options._selector) { // children of container
|
|
this.options._selector = options._selector;
|
|
this.options._isDelegate = true;
|
|
}
|
|
else { // standalone
|
|
this.options._selector = options.rootSelector;
|
|
}
|
|
|
|
if (this.options.confirmationEvent === undefined) {
|
|
this.options.confirmationEvent = this.options.trigger;
|
|
}
|
|
|
|
var self = this;
|
|
|
|
if (!this.options.selector) {
|
|
// store copied attributes
|
|
this.options._attributes = {};
|
|
if (this.options.copyAttributes) {
|
|
if (typeof this.options.copyAttributes === 'string') {
|
|
this.options.copyAttributes = this.options.copyAttributes.split(' ');
|
|
}
|
|
}
|
|
else {
|
|
this.options.copyAttributes = [];
|
|
}
|
|
|
|
this.options.copyAttributes.forEach(function(attr) {
|
|
this.options._attributes[attr] = this.$element.attr(attr);
|
|
}, this);
|
|
|
|
// cancel original event
|
|
this.$element.on(this.options.trigger, function(e, ack) {
|
|
if (!ack) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
}
|
|
});
|
|
|
|
// manage singleton
|
|
this.$element.on('show.bs.confirmation', function(e) {
|
|
if (self.options.singleton) {
|
|
// close all other popover already initialized
|
|
$(self.options._selector).not($(this)).filter(function() {
|
|
return $(this).data('bs.confirmation') !== undefined;
|
|
}).confirmation('hide');
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
// cancel original event
|
|
this.$element.on(this.options.trigger, this.options.selector, function(e, ack) {
|
|
if (!ack) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!this.options._isDelegate) {
|
|
// manage popout
|
|
this.eventBody = false;
|
|
this.uid = this.$element[0].id || this.getUID('group_');
|
|
|
|
this.$element.on('shown.bs.confirmation', function(e) {
|
|
if (self.options.popout && !self.eventBody) {
|
|
self.eventBody = $('body').on('click.bs.confirmation.' + self.uid, function(e) {
|
|
if ($(self.options._selector).is(e.target) || $(self.options._selector).has(e.target).length > 0) {
|
|
return;
|
|
}
|
|
|
|
// close all popover already initialized
|
|
$(self.options._selector).filter(function() {
|
|
return $(this).data('bs.confirmation') !== undefined;
|
|
}).confirmation('hide');
|
|
|
|
$('body').off('click.bs.' + self.uid);
|
|
self.eventBody = false;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Overrides, always show
|
|
* @returns {boolean}
|
|
*/
|
|
Confirmation.prototype.hasContent = function() {
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Sets the popover content
|
|
*/
|
|
Confirmation.prototype.setContent = function() {
|
|
var self = this;
|
|
var $tip = this.tip();
|
|
var title = this.getTitle();
|
|
var content = this.getContent();
|
|
|
|
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title);
|
|
|
|
$tip.find('.confirmation-content').toggle(!!content).children().detach().end()[
|
|
// we use append for html objects to maintain js events
|
|
this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
|
|
](content);
|
|
|
|
$tip.on('click', function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
|
|
if (this.options.buttons) {
|
|
// configure custom buttons
|
|
var $group = $tip.find('.confirmation-buttons .btn-group').empty();
|
|
|
|
this.options.buttons.forEach(function(button) {
|
|
$group.append(
|
|
$('<a href="#"></a>')
|
|
.addClass(button.class || 'btn btn-xs btn-default')
|
|
.html(button.label || '')
|
|
.attr(button.attr || {})
|
|
.prepend($('<i></i>').addClass(button.icon), ' ')
|
|
.one('click', function(e) {
|
|
if ($(this).attr('href') === '#') {
|
|
e.preventDefault();
|
|
}
|
|
|
|
if (button.onClick) {
|
|
button.onClick.call(self.$element);
|
|
}
|
|
|
|
if (button.cancel) {
|
|
self.getOnCancel().call(self.$element, button.value);
|
|
self.$element.trigger('canceled.bs.confirmation', [button.value]);
|
|
}
|
|
else {
|
|
self.getOnConfirm().call(self.$element, button.value);
|
|
self.$element.trigger('confirmed.bs.confirmation', [button.value]);
|
|
}
|
|
|
|
if (self.inState) { // Bootstrap 3.3.5
|
|
self.inState.click = false;
|
|
}
|
|
|
|
self.hide();
|
|
})
|
|
);
|
|
}, this);
|
|
}
|
|
else {
|
|
// configure 'ok' button
|
|
$tip.find('[data-apply="confirmation"]')
|
|
.addClass(this.options.btnOkClass)
|
|
.html(this.options.btnOkLabel)
|
|
.attr(this.options._attributes)
|
|
.prepend($('<i></i>').addClass(this.options.btnOkIcon), ' ')
|
|
.off('click')
|
|
.one('click', function(e) {
|
|
if ($(this).attr('href') === '#') {
|
|
e.preventDefault();
|
|
}
|
|
|
|
self.getOnConfirm().call(self.$element);
|
|
self.$element.trigger('confirmed.bs.confirmation');
|
|
|
|
self.$element.trigger(self.options.confirmationEvent, [true]);
|
|
|
|
self.hide();
|
|
});
|
|
|
|
// configure 'cancel' button
|
|
$tip.find('[data-dismiss="confirmation"]')
|
|
.addClass(this.options.btnCancelClass)
|
|
.html(this.options.btnCancelLabel)
|
|
.prepend($('<i></i>').addClass(this.options.btnCancelIcon), ' ')
|
|
.off('click')
|
|
.one('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
self.getOnCancel().call(self.$element);
|
|
self.$element.trigger('canceled.bs.confirmation');
|
|
|
|
if (self.inState) { // Bootstrap 3.3.5
|
|
self.inState.click = false;
|
|
}
|
|
|
|
self.hide();
|
|
});
|
|
}
|
|
|
|
$tip.removeClass('fade top bottom left right in');
|
|
|
|
// IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
|
|
// this manually by checking the contents.
|
|
if (!$tip.find('.popover-title').html()) {
|
|
$tip.find('.popover-title').hide();
|
|
}
|
|
|
|
// bind key navigation
|
|
activeConfirmation = this;
|
|
$(window)
|
|
.off('keyup.bs.confirmation')
|
|
.on('keyup.bs.confirmation', this._onKeyup.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Remove key binding on destroy
|
|
*/
|
|
Confirmation.prototype.destroy = function() {
|
|
if (activeConfirmation === this) {
|
|
activeConfirmation = undefined;
|
|
$(window).off('keyup.bs.confirmation');
|
|
}
|
|
$.fn.popover.Constructor.prototype.destroy.call(this);
|
|
};
|
|
|
|
/**
|
|
* Remove key binding on hide
|
|
*/
|
|
Confirmation.prototype.hide = function() {
|
|
if (activeConfirmation === this) {
|
|
activeConfirmation = undefined;
|
|
$(window).off('keyup.bs.confirmation');
|
|
}
|
|
$.fn.popover.Constructor.prototype.hide.call(this);
|
|
};
|
|
|
|
/**
|
|
* Navigate through buttons with keyboard
|
|
* @param event
|
|
* @private
|
|
*/
|
|
Confirmation.prototype._onKeyup = function(event) {
|
|
if (!this.$tip) {
|
|
activeConfirmation = undefined;
|
|
$(window).off('keyup.bs.confirmation');
|
|
return;
|
|
}
|
|
|
|
var key = event.key || Confirmation.KEYMAP[event.keyCode || event.which];
|
|
|
|
var $group = this.$tip.find('.confirmation-buttons .btn-group');
|
|
var $active = $group.find('.active');
|
|
var $next;
|
|
|
|
switch (key) {
|
|
case 'Escape':
|
|
this.hide();
|
|
break;
|
|
|
|
case 'ArrowRight':
|
|
if ($active.length && $active.next().length) {
|
|
$next = $active.next();
|
|
}
|
|
else {
|
|
$next = $group.children().first();
|
|
}
|
|
$active.removeClass('active');
|
|
$next.addClass('active').focus();
|
|
break;
|
|
|
|
case 'ArrowLeft':
|
|
if ($active.length && $active.prev().length) {
|
|
$next = $active.prev();
|
|
}
|
|
else {
|
|
$next = $group.children().last();
|
|
}
|
|
$active.removeClass('active');
|
|
$next.addClass('active').focus();
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the on-confirm callback
|
|
* @returns {function}
|
|
*/
|
|
Confirmation.prototype.getOnConfirm = function() {
|
|
if (this.$element.attr('data-on-confirm')) {
|
|
return getFunctionFromString(this.$element.attr('data-on-confirm'));
|
|
}
|
|
else {
|
|
return this.options.onConfirm;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the on-cancel callback
|
|
* @returns {function}
|
|
*/
|
|
Confirmation.prototype.getOnCancel = function() {
|
|
if (this.$element.attr('data-on-cancel')) {
|
|
return getFunctionFromString(this.$element.attr('data-on-cancel'));
|
|
}
|
|
else {
|
|
return this.options.onCancel;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generates an anonymous function from a function name
|
|
* function name may contain dots (.) to navigate through objects
|
|
* root context is window
|
|
*/
|
|
function getFunctionFromString(functionName) {
|
|
var context = window;
|
|
var namespaces = functionName.split('.');
|
|
var func = namespaces.pop();
|
|
|
|
for (var i = 0, l = namespaces.length; i < l; i++) {
|
|
context = context[namespaces[i]];
|
|
}
|
|
|
|
return function() {
|
|
context[func].call(this);
|
|
};
|
|
}
|
|
|
|
|
|
// CONFIRMATION PLUGIN DEFINITION
|
|
// =========================
|
|
|
|
var old = $.fn.confirmation;
|
|
|
|
$.fn.confirmation = function(option) {
|
|
var options = (typeof option == 'object' && option) || {};
|
|
options.rootSelector = this.selector || options.rootSelector; // this.selector removed in jQuery > 3
|
|
|
|
return this.each(function() {
|
|
var $this = $(this);
|
|
var data = $this.data('bs.confirmation');
|
|
|
|
if (!data && option == 'destroy') {
|
|
return;
|
|
}
|
|
if (!data) {
|
|
$this.data('bs.confirmation', (data = new Confirmation(this, options)));
|
|
}
|
|
if (typeof option == 'string') {
|
|
data[option]();
|
|
|
|
if (option == 'hide' && data.inState) { //data.inState doesn't exist in Bootstrap < 3.3.5
|
|
data.inState.click = false;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
$.fn.confirmation.Constructor = Confirmation;
|
|
|
|
|
|
// CONFIRMATION NO CONFLICT
|
|
// ===================
|
|
|
|
$.fn.confirmation.noConflict = function() {
|
|
$.fn.confirmation = old;
|
|
return this;
|
|
};
|
|
|
|
}(jQuery));
|