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