-
Rafael Ostertag authored
Alert.js: buttons on alerts are now really `<button>`. Buttons can now have the default focus, as specified by the `focus` attribue in the button configuration.
Rafael Ostertag authoredAlert.js: buttons on alerts are now really `<button>`. Buttons can now have the default focus, as specified by the `focus` attribue in the button configuration.
Alert.js 6.73 KiB
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
var QfqNS = QfqNS || {};
(function (n) {
'use strict';
/**
* Display a message.
*
* Display one message on a page. Several instances can be used per page, which results in messages being
* stacked, with the latest message being at the bottom.
*
* The first instance displaying a message will append an `alert container` to the body. The last message being
* dismissed will remove the `alert container`. A typical call sequence might look like:
*
* var alert = new QfqNS.Alert("Text being displayed", "info");
* alert.show();
*
* Messages may have different background colors (severity levels), controlled by the second argument
* `messageType` of the constructor. The possible values are
*
* * `"info"`
* * `"warning"`
* * `"error"`
*
* The values are translated into Bootstrap `alert-*` classes internally.
*
* If no buttons are configured, a click anywhere on the alert will close it.
*
* Buttons are configured by passing an array of objects to the constructor. The attributes of the object are as
* follows
*
* {
* label: <button label>,
* focus: true | false,
* eventName: <eventname>
* }
*
* You can connect to the button events by using
*
* var alert = new QfqNS.Alert("Text being displayed", "info", [{ label: "OK", eventName: "ok" }]);
* alert.on('alert.ok', function(...) { ... });
*
* Events are named according to `alert.<eventname>`.
*
* @param message {string} message to be displayed
* @param messageType {string} type of message, can either be `"info"`, `"warning"`, or `"error"`.
* @param buttons {list} buttons to be displayed
* When `"none"` is provided, clicking anywhere on the message will dismiss it.
* @constructor
*/
n.Alert = function (message, messageType, buttons) {
this.message = message;
this.messageType = messageType || "info";
this.buttons = buttons || [];
this.$alertDiv = null;
this.shown = false;
// this.timeout < 1 means forever
this.timeout = 0;
this.fadeInDuration = 400;
this.fadeOutDuration = 400;
this.timerId = null;
this.eventEmitter = new EventEmitter();
};
n.Alert.prototype.on = n.EventEmitter.onMixin;
/**
*
* @private
*/
n.Alert.prototype.makeAlertContainerSingleton = function () {
var alertContainer = $("#qfqAlertContainer");
if (alertContainer.length === 0) {
// No container so far, create one
alertContainer = $("<div>").attr("id", "qfqAlertContainer");
$("body").append(alertContainer);
}
return alertContainer;
};
/**
*
* @returns {number|jQuery}
* @private
*/
n.Alert.prototype.countAlertsInAlertContainer = function () {
return $("#qfqAlertContainer > div").length;
};
/**
* @private
*/
n.Alert.prototype.removeAlertContainer = function () {
$("#qfqAlertContainer").remove();
};
/**
* @private
*/
n.Alert.prototype.getAlertClassBasedOnMessageType = function () {
switch (this.messageType) {
case "warning":
return "alert-warning";
case "error":
return "alert-danger";
/* jshint -W086 */
default:
n.Log.warning("Message type '" + this.messageType + "' unknown. Use default type.");
case "info":
return "alert-success";
/* jshint +W086 */
}
};
/**
* @private
*/
n.Alert.prototype.getButtons = function () {
var $buttons = null;
var numberOfButtons = this.buttons.length;
var index;
var buttonConfiguration;
for (index = 0; index < numberOfButtons; index++) {
buttonConfiguration = this.buttons[index];
if (!$buttons) {
$buttons = $("<div>").addClass("alert-buttons");
}
var focus = buttonConfiguration.focus ? buttonConfiguration.focus : false;
$buttons.append($("<button>").append(buttonConfiguration.label)
.attr('type', 'button')
.addClass("btn btn-default" + (focus ? " wants-focus" : ""))
.click(buttonConfiguration, this.buttonHandler.bind(this)));
}
return $buttons;
};
n.Alert.prototype.show = function () {
if (this.shown) {
// We only allow showing once
return;
}
var $alertContainer = this.makeAlertContainerSingleton();
this.$alertDiv = $("<div>")
.hide()
.addClass("alert")
.addClass(this.getAlertClassBasedOnMessageType())
.attr("role", "alert")
.append(this.message);
var buttons = this.getButtons();
if (buttons) {
// Buttons will take care of removing the message
this.$alertDiv.append(buttons);
} else {
// Click on the message anywhere will remove the message
this.$alertDiv.click(this.removeAlert.bind(this));
}
$alertContainer.append(this.$alertDiv);
this.$alertDiv.slideDown(this.fadeInDuration, this.afterFadeIn.bind(this));
this.$alertDiv.find(".wants-focus").focus();
this.shown = true;
};
/**
* @private
*/
n.Alert.prototype.afterFadeIn = function () {
if (this.timeout > 0) {
this.timerId = window.setTimeout(this.removeAlert.bind(this), this.timeout);
}
};
/**
*
*
* @private
*/
n.Alert.prototype.removeAlert = function () {
// In case we have an armed timer (or expired timer, for that matter), disarm it.
if (this.timerId) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
var that = this;
this.$alertDiv.slideUp(this.fadeOutDuration, function () {
that.$alertDiv.remove();
that.$alertDiv = null;
that.shown = false;
if (that.countAlertsInAlertContainer() === 0) {
that.removeAlertContainer();
}
});
};
/**
*
* @param handler
*
* @private
*/
n.Alert.prototype.buttonHandler = function (event) {
this.removeAlert();
this.eventEmitter.emitEvent('alert.' + event.data.eventName, n.EventEmitter.makePayload(this, null));
};
n.Alert.prototype.isShown = function () {
return this.shown;
};
})(QfqNS);