Observium_CE/html/js/bootstrap-confirmation.js

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));