diff --git a/javascript/src/Alert.js b/javascript/src/Alert.js index 57c9077f4978e1877d799e6fc9d2218d76c1ff14..1168877f0f7804fa34f4afcc75dbc7d91a5ce3f2 100644 --- a/javascript/src/Alert.js +++ b/javascript/src/Alert.js @@ -20,7 +20,7 @@ var QfqNS = QfqNS || {}; * 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", "none"); + * var alert = new QfqNS.Alert("Text being displayed", "info"); * alert.show(); * * Messages may have different background colors (severity levels), controlled by the second argument @@ -32,29 +32,33 @@ var QfqNS = QfqNS || {}; * * The values are translated into Bootstrap `alert-*` classes internally. * - * Messages can feature clickable buttons, or no buttons at all, in which case a click anywhere on the message - * will dismiss it. Buttons are controlled by the third argument to the constructor: + * If no buttons are configured, a click anywhere on the alert will close it. * - * * `"okcancel"` - * * `"yesno"` - * * `"yesnosave"` - * * `"none"` + * Buttons are configured by passing an array of objects to the constructor. The attributes of the object are as + * follows * - * Callback functions of the `Ok` or `Yes` button are added by calling Alert#addOkButtonHandler(). Callback - * functions of the `Cancel` or `No` button are added by calling Alert#addCancelButtonHandler(). Lastly, - * Alert#addSaveButtonHandler() adds callback functions to the `Save` button. + * { + * label: <button label>, + * 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 {string} buttons to be displayed, can either be `"okcancel"`, `"yesno"`, `"yesnosave"`, or `"none"`. + * @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 || "none"; + this.buttons = buttons || []; this.$alertDiv = null; this.shown = false; @@ -123,72 +127,24 @@ var QfqNS = QfqNS || {}; * @private */ n.Alert.prototype.getButtons = function () { - var buttons = null; - switch (this.buttons) { - case 'okcancel': - buttons = $("<div>") - .addClass("alert-buttons") - .append( - $("<a>") - .append("Ok") - .addClass("btn btn-default") - .click(this.okButtonHandler.bind(this)) - ) - .append( - $("<a>") - .append("Cancel") - .addClass("btn btn-default") - .click(this.cancelButtonHandler.bind(this)) - ); - return buttons; - case "yesno": - buttons = $("<div>") - .addClass("alert-buttons") - .append( - $("<a>") - .append("Yes") - .addClass("btn btn-default") - .click(this.okButtonHandler.bind(this)) - ) - .append( - $("<a>") - .append("No") - .addClass("btn btn-default") - .click(this.cancelButtonHandler.bind(this)) - ); - return buttons; - case "yesnosave": - buttons = $("<div>") - .addClass("alert-buttons") - .append( - $("<a>") - .append("Yes") - .addClass("btn btn-default") - .click(this.okButtonHandler.bind(this)) - ) - .append( - $("<a>") - .append("No") - .addClass("btn btn-default") - .click(this.cancelButtonHandler.bind(this)) - ) - .append( - $("<a>") - .append("Save & Close") + 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"); + } + + $buttons.append($("<a>").append(buttonConfiguration.label) .addClass("btn btn-default") - .click(this.saveButtonHandler.bind(this)) - ) - ; - return buttons; - /* jshint -W086 */ - default: - n.Log.warning("Buttons '" + this.buttons + "' unknown. Use default buttons"); - case "none": - break; - /* jshint +W086 */ + .click(buttonConfiguration, this.buttonHandler.bind(this))); } - return buttons; + return $buttons; }; n.Alert.prototype.show = function () { @@ -207,7 +163,7 @@ var QfqNS = QfqNS || {}; var buttons = this.getButtons(); - if (buttons && this.timeout < 1) { + if (buttons) { // Buttons will take care of removing the message this.$alertDiv.append(buttons); } else { @@ -262,31 +218,9 @@ var QfqNS = QfqNS || {}; * * @private */ - n.Alert.prototype.okButtonHandler = function (handler) { - this.removeAlert(); - this.eventEmitter.emitEvent('alert.ok', n.EventEmitter.makePayload(this, null)); - }; - - /** - * - * @param handler - * - * @private - */ - n.Alert.prototype.saveButtonHandler = function (handler) { - this.removeAlert(); - this.eventEmitter.emitEvent('alert.save', n.EventEmitter.makePayload(this, null)); - }; - - /** - * - * @param handler - * - * @private - */ - n.Alert.prototype.cancelButtonHandler = function (handler) { + n.Alert.prototype.buttonHandler = function (event) { this.removeAlert(); - this.eventEmitter.emitEvent('alert.cancel', n.EventEmitter.makePayload(this, null)); + this.eventEmitter.emitEvent('alert.' + event.data.eventName, n.EventEmitter.makePayload(this, null)); }; n.Alert.prototype.isShown = function () { diff --git a/javascript/src/FileDelete.js b/javascript/src/FileDelete.js index 40324659593e1a7cd41618aaebe2fd3551470d3c..bf699d593149a1393d7f60481ca8ae81bc43b8ec 100644 --- a/javascript/src/FileDelete.js +++ b/javascript/src/FileDelete.js @@ -27,7 +27,9 @@ var QfqNS = QfqNS || {}; n.FileDelete.prototype.buttonClicked = function (event) { event.preventDefault(); - var alert = new n.Alert("Do you want to delete the file?", "warning", "okcancel"); + var alert = new n.Alert("Do you want to delete the file?", + "warning", + [{label: "OK", eventName: "ok"}, {label: "Cancel", eventName: "cancel"}]); alert.on('alert.ok', function () { this.performFileDelete(event); }.bind(this)); diff --git a/javascript/src/Form.js b/javascript/src/Form.js index 7a7fb399562ba991b15d723de207e50042bd8166..16bc10b17bf34fb70b665bb9dc1d6fc27ce08c41 100644 --- a/javascript/src/Form.js +++ b/javascript/src/Form.js @@ -26,6 +26,10 @@ var QfqNS = QfqNS || {}; // On <input> elements, we specifically bind this events, in order to update the formChanged property // immediately, not only after loosing focus. Same goes for <textarea> this.$form.find("input, textarea").on("input paste", this.changeHandler.bind(this)); + + this.$form.on('submit', function (event) { + event.preventDefault(); + }); }; n.Form.prototype.on = n.EventEmitter.onMixin; @@ -105,4 +109,25 @@ var QfqNS = QfqNS || {}; return document.forms[this.formId].checkValidity(); }; + /** + * @public + */ + n.Form.prototype.getFirstNonValidElement = function () { + var index; + var elementNumber = document.forms[this.formId].length; + + for (index = 0; index < elementNumber; index++) { + var element = document.forms[this.formId][index]; + if (!element.willValidate) { + continue; + } + + if (!element.checkValidity()) { + return element; + } + } + + return null; + }; + })(QfqNS); diff --git a/javascript/src/QfqForm.js b/javascript/src/QfqForm.js index 3a151bf0c8d25690cc4883bfca76ecd83112ec55..04a548259dbd8244953af72ed0343b5e7fb1145f 100644 --- a/javascript/src/QfqForm.js +++ b/javascript/src/QfqForm.js @@ -51,6 +51,7 @@ var QfqNS = QfqNS || {}; this.getDeleteButton().click(this.handleDeleteClick.bind(this)); this.setupFormUpdateHandler(); + this.setupEnterKeyHandler(); this.fileUploader = new n.FileUpload('#' + this.formId, this.fileUploadTo); this.fileUploader.on('fileupload.started', this.startUploadHandler); @@ -77,6 +78,21 @@ var QfqNS = QfqNS || {}; n.QfqForm.prototype.on = n.EventEmitter.onMixin; + /** + * @private + */ + n.QfqForm.prototype.setupEnterKeyHandler = function () { + $("input").keyup(function (event) { + if (event.which === 13) { + if (this.form.formChanged) { + this.getSaveButton().trigger("click"); + } + event.preventDefault(); + } + }.bind(this)); + }; + + /** * * @private @@ -274,7 +290,11 @@ var QfqNS = QfqNS || {}; n.QfqForm.prototype.handleCloseClick = function () { this.lastButtonPress = "close"; if (this.form.getFormChanged()) { - var alert = new n.Alert("You have unsaved changes. Do you want to close?", "warning", "yesnosave"); + var alert = new n.Alert("You have unsaved changes. Do you want to close?", "warning", + [{label: "Yes", eventName: "ok"}, {label: "No", eventName: "no"}, { + label: "Save & Close", + eventName: "save" + }]); var that = this; alert.on('alert.save', function () { that.submit(); @@ -292,6 +312,15 @@ var QfqNS = QfqNS || {}; if (this.form.validate() !== true) { this.form.$form.validator('validate'); + + var element = this.form.getFirstNonValidElement(); + if (element.hasAttribute('name')) { + var tabId = this.bsTabs.getContainingTabIdForFormControl(element.getAttribute('name')); + if (tabId) { + this.bsTabs.activateTab(tabId); + } + } + var alert = new n.Alert("Form is incomplete.", "warning"); alert.show(); return; @@ -307,7 +336,12 @@ var QfqNS = QfqNS || {}; this.lastButtonPress = "new"; if (this.form.getFormChanged()) { - var alert = new n.Alert("You have unsaved changes. Do you want to close?", "warning", "yesnosave"); + var alert = new n.Alert("You have unsaved changes. Do you want to close?", "warning", + [ + {label: "Yes", eventName: "ok"}, + {label: "No", eventName: "cancel"}, + {label: "Save & Close", eventName: "save"} + ]); var that = this; alert.on('alert.save', function () { that.submit(); @@ -330,7 +364,11 @@ var QfqNS = QfqNS || {}; n.QfqForm.prototype.handleDeleteClick = function () { this.lastButtonPress = "delete"; n.Log.debug("delete click"); - var alert = new n.Alert("Do you really want to delete the record?", "warning", "yesno"); + var alert = new n.Alert("Do you really want to delete the record?", "warning", + [ + {label: "Yes", eventName: "ok"}, + {label: "No", eventName: "cancel"} + ]); var that = this; alert.on('alert.ok', function () { $.post(that.deleteUrl) diff --git a/javascript/src/QfqRecordList.js b/javascript/src/QfqRecordList.js index 18ea78a6f8a20ee521b5c269c7488829cf6cbe03..0dba04fe40ac4e67d15db0dbcf7150fa40880fc9 100644 --- a/javascript/src/QfqRecordList.js +++ b/javascript/src/QfqRecordList.js @@ -40,7 +40,11 @@ var QfqNS = QfqNS || {}; } - var alert = new n.Alert("Do you really want to delete the record?", "warning", "yesno"); + var alert = new n.Alert("Do you really want to delete the record?", "warning", + [ + {label: "Yes", eventName: "ok"}, + {label: "No", eventName: "cancel"} + ]); var that = this; alert.on('alert.ok', function () { $.post(that.deleteUrl + "?s=" + sip) diff --git a/mockup/alert.html b/mockup/alert.html index 7e640c77a6adebcc612518d9b05bc7875e799718..ce0bcd42564c87ad210a84a733f7d426e228db5b 100644 --- a/mockup/alert.html +++ b/mockup/alert.html @@ -23,11 +23,12 @@ <script> $(function () { $('#showalert1').click(function () { - var alert = new QfqNS.Alert("This is alert 1", 'error', 'okcancel'); - alert.addOkButtonHandler(function () { + var alert = new QfqNS.Alert("This is alert 1", 'error', [{label: "OK", eventName: "ok"}, + {label: "Cancel", eventName: "cancel"}]); + alert.on('alert.ok', function () { console.log("OK button alert 1"); }); - alert.addCancelButtonHandler(function () { + alert.on('alert.cancel', function () { console.log("Cancel button alert 1"); }); alert.show(); @@ -35,12 +36,6 @@ $('#showalert2').click(function () { var alert = new QfqNS.Alert("This is alert 2", 'warning'); - alert.addOkButtonHandler(function () { - console.log("OK button alert 2"); - }); - alert.addCancelButtonHandler(function () { - console.log("Cancel button alert 2"); - }); alert.show(); });