src/ztfy/myams/resources/js/myams.js
changeset 142 0201f1422bd7
parent 136 3ea42c4b9938
child 145 1a37bfc4aa5b
--- a/src/ztfy/myams/resources/js/myams.js	Tue Nov 17 12:04:26 2015 +0100
+++ b/src/ztfy/myams/resources/js/myams.js	Tue Nov 17 12:05:38 2015 +0100
@@ -10,27 +10,29 @@
  * ©2014-2015 Thierry Florac <tflorac@ulthar.net>
  */
 
-(function($) {
+(function($, globals) {
+
+	"use strict";
 
 	/**
 	 * String prototype extensions
 	 */
 	String.prototype.startsWith = function(str) {
-		var slen = this.length;
-		var dlen = str.length;
+		var slen = this.length,
+			dlen = str.length;
 		if (slen < dlen) {
 			return false;
 		}
-		return (this.substr(0,dlen) == str);
+		return (this.substr(0,dlen) === str);
 	};
 
 	String.prototype.endsWith = function(str) {
-		var slen = this.length;
-		var dlen = str.length;
+		var slen = this.length,
+			dlen = str.length;
 		if (slen < dlen) {
 			return false;
 		}
-		return (this.substr(slen-dlen) == str);
+		return (this.substr(slen-dlen) === str);
 	};
 
 
@@ -38,18 +40,19 @@
 	 * Array prototype extensions
 	 */
 	if (!Array.prototype.indexOf) {
-		Array.prototype.indexOf = function(elt /*, from*/) {
+		Array.prototype.indexOf = function(elt, from) {
 			var len = this.length;
 
-			var from = Number(arguments[1]) || 0;
+			from = Number(from) || 0;
 			from = (from < 0) ? Math.ceil(from) : Math.floor(from);
-			if (from < 0)
+			if (from < 0) {
 				from += len;
+			}
 
 			for (; from < len; from++) {
-				if (from in this &&
-					this[from] === elt)
+				if (from in this && this[from] === elt) {
 					return from;
+				}
 			}
 			return -1;
 		};
@@ -61,7 +64,7 @@
 	 * Case insensitive contains expression
 	 */
 	$.expr[":"].econtains = function(obj, index, meta /*, stack*/) {
-		return (obj.textContent || obj.innerText || $(obj).text() || "").toLowerCase() == meta[3].toLowerCase();
+		return (obj.textContent || obj.innerText || $(obj).text() || "").toLowerCase() === meta[3].toLowerCase();
 	};
 
 
@@ -70,7 +73,7 @@
 	 * Case sensitive exact search expression
 	 */
 	$.expr[":"].withtext = function(obj, index, meta /*, stack*/) {
-		return (obj.textContent || obj.innerText || $(obj).text() || "") == meta[3];
+		return (obj.textContent || obj.innerText || $(obj).text() || "") === meta[3];
 	};
 
 
@@ -114,10 +117,11 @@
 		 * otherwise looks for parents
 		 */
 		objectOrParentWithClass: function(klass) {
-			if (this.hasClass(klass))
+			if (this.hasClass(klass)) {
 				return this;
-			else
+			} else {
 				return this.parents('.' + klass);
+			}
 		},
 
 		/*
@@ -139,16 +143,16 @@
 			// DOM node
 			var node = this.get(0);
 			// Ensure we have a DOM node
-			if (typeof node == 'undefined') {
+			if (typeof(node) === 'undefined') {
 				return;
 			}
 			// CSSStyleDeclaration
 			var style = this.get(0).style;
 			// Getter/Setter
-			if (typeof styleName != 'undefined') {
-				if (typeof value != 'undefined') {
+			if (typeof(styleName) !== 'undefined') {
+				if (typeof(value) !== 'undefined') {
 					// Set style property
-					priority = typeof priority != 'undefined' ? priority : '';
+					priority = typeof(priority) !== 'undefined' ? priority : '';
 					style.setProperty(styleName, value, priority);
 					return this;
 				} else {
@@ -167,9 +171,9 @@
 		removeClassPrefix: function (prefix) {
 			this.each(function (i, it) {
 				var classes = it.className.split(" ").map(function(item) {
-					return item.startsWith(prefix) ? "" : item
+					return item.startsWith(prefix) ? "" : item;
 				});
-				it.className = $.trim(classes.join(" "))
+				it.className = $.trim(classes.join(" "));
 			});
 			return this;
 		},
@@ -184,8 +188,9 @@
 					menu = $(settings.menuSelector)[direction](),
 					position = mouse;
 				// opening menu would pass the side of the page
-				if (mouse + menu > win && menu < mouse)
+				if (mouse + menu > win && menu < mouse) {
 					position -= menu;
+				}
 				return position;
 			}
 
@@ -197,8 +202,9 @@
 				});
 				$(this).on("contextmenu", function (e) {
 					// return native menu if pressing control
-					if (e.ctrlKey)
+					if (e.ctrlKey) {
 						return;
+					}
 					//open menu
 					$(settings.menuSelector).data("invokedOn", $(e.target))
 											.show()
@@ -252,7 +258,7 @@
 
 					// avoid jumping to the top of the page when the href is an #
 					var first_link = menu_item.find("A:first");
-					if (first_link.attr('href') == "#") {
+					if (first_link.attr('href') === "#") {
 						first_link.click(function() {
 							return false;
 						});
@@ -266,7 +272,7 @@
 				var active_item = active_parent.parent('LI');
 				active_parent.slideDown(settings.speed);
 				active_item.find("b:first").html(settings.openedSign);
-				active_item.addClass("open")
+				active_item.addClass("open");
 			});
 
 			menu.find("LI A").on('click', function() {
@@ -278,13 +284,13 @@
 					visible.each(function(visibleIndex) {
 						var close = true;
 						parents.each(function(parentIndex) {
-							if (parents[parentIndex] == visible[visibleIndex]) {
+							if (parents[parentIndex] === visible[visibleIndex]) {
 								close = false;
 								return false;
 							}
 						});
 						if (close) {
-							if (parent_ul != visible[visibleIndex]) {
+							if (parent_ul !== visible[visibleIndex]) {
 								$(visible[visibleIndex]).slideUp(settings.speed, function() {
 									link.parent("LI")
 										.find("b:first")
@@ -307,7 +313,7 @@
 							.delay(settings.speed)
 							.html(settings.closedSign);
 					});
-				} else /*if (link.attr('href') != location.hash)*/ {
+				} else /*if (link.attr('href') !== location.hash)*/ {
 					first_ul.slideDown(settings.speed, function() {
 						link.parent("LI")
 							.addClass("open")
@@ -388,8 +394,8 @@
 	/**
 	 * MyAMS extensions to JQuery
 	 */
-	if (window.MyAMS === undefined) {
-		window.MyAMS = {
+	if (globals.MyAMS === undefined) {
+		globals.MyAMS = {
 			devmode: true,
 			devext: '',
 			lang: 'en',
@@ -404,6 +410,7 @@
 			ismobile: (/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase()))
 		};
 	}
+	var MyAMS = globals.MyAMS;
 	var ams = MyAMS;
 
 	/**
@@ -424,16 +431,18 @@
 	 */
 	MyAMS.getQueryVar = function(src, varName) {
 		// Check src
-		if (src.indexOf('?') < 0)
+		if (src.indexOf('?') < 0) {
 			return false;
-		if (!src.endsWith('&'))
+		}
+		if (!src.endsWith('&')) {
 			src += '&';
+		}
 		// Dynamic replacement RegExp
 		var regex = new RegExp('.*?[&\\?]' + varName + '=(.*?)&.*');
 		// Apply RegExp to the query string
 		var val = src.replace(regex, "$1");
 		// If the string is the same, we didn't find a match - return false
-		return val == src ? false : val;
+		return val === src ? false : val;
 	};
 
 
@@ -442,7 +451,7 @@
 	 */
 	MyAMS.rgb2hex = function(color) {
 		return "#" + $.map(color.match(/\b(\d+)\b/g), function(digit) {
-			return ('0' + parseInt(digit).toString(16)).slice(-2)
+			return ('0' + parseInt(digit).toString(16)).slice(-2);
 		}).join('');
 	};
 
@@ -465,10 +474,11 @@
 	 * Small piece of code by Jason Bunting
 	 */
 	MyAMS.getFunctionByName = function(functionName, context) {
-		if (functionName === undefined)
+		if (functionName === undefined) {
 			return undefined;
-		else if (typeof(functionName) == 'function')
+		} else if (typeof(functionName) === 'function') {
 			return functionName;
+		}
 		var namespaces = functionName.split(".");
 		var func = namespaces.pop();
 		context = (context === undefined || context === null) ? window : context;
@@ -488,7 +498,7 @@
 
 	MyAMS.executeFunctionByName = function(functionName, context /*, args */) {
 		var func = ams.getFunctionByName(functionName, window);
-		if (typeof(func) == 'function') {
+		if (typeof(func) === 'function') {
 			var args = Array.prototype.slice.call(arguments, 2);
 			return func.apply(context, args);
 		}
@@ -513,7 +523,7 @@
 			success: callback,
 			error: ams.error.show,
 			cache: !ams.devmode,
-			async: typeof(callback) == 'function'
+			async: typeof(callback) === 'function'
 		};
 		var settings = $.extend({}, defaults, options);
 		return $.ajax(settings);
@@ -522,10 +532,11 @@
 	MyAMS.getCSS = function(url, id) {
 		var head = $('HEAD');
 		var css = $('link[data-ams-id="' + id + '"]', head);
-		if (css.length == 0) {
+		if (css.length === 0) {
 			var source = ams.getSource(url);
-			if (ams.devmode)
+			if (ams.devmode) {
 				source += '?_=' + new Date().getTime();
+			}
 			$('<link />').attr({rel: 'stylesheet',
 								type: 'text/css',
 								href: source,
@@ -564,11 +575,12 @@
 
 		getInternetExplorerVersion: function() {
 			var rv = -1;
-			if (navigator.appName == "Microsoft Internet Explorer") {
+			if (navigator.appName === "Microsoft Internet Explorer") {
 				var ua = navigator.userAgent;
 				var re = new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");
-				if (re.exec(ua) != null)
+				if (re.exec(ua) !== null) {
 					rv = parseFloat(RegExp.$1);
+				}
 			}
 			return rv;
 		},
@@ -577,22 +589,26 @@
 			var msg = "You're not using Windows Internet Explorer.";
 			var ver = this.getInternetExplorerVersion();
 			if (ver > -1) {
-				if (ver >= 8)
+				if (ver >= 8) {
 					msg = "You're using a recent copy of Windows Internet Explorer.";
-				else
+				} else {
 					msg = "You should upgrade your copy of Windows Internet Explorer.";
+				}
 			}
-			alert(msg);
+			if (globals.alert) {
+				globals.alert(msg);
+			}
 		},
 
 		isIE8orlower: function() {
 			var msg = "0";
 			var ver = this.getInternetExplorerVersion();
 			if (ver > -1) {
-				if (ver >= 9)
+				if (ver >= 9) {
 					msg = 0;
-				else
+				} else {
 					msg = 1;
+				}
 			}
 			return msg;
 		},
@@ -601,8 +617,10 @@
 			return function() {
 				var source = $(this);
 				source.parents('.btn-group').removeClass('open');
-				window.prompt(MyAMS.i18n.CLIPBOARD_COPY, source.text());
-			}
+				if (globals.prompt) {
+					globals.prompt(MyAMS.i18n.CLIPBOARD_COPY, source.text());
+				}
+			};
 		}
 	};
 
@@ -616,10 +634,11 @@
 		 * Default JQuery AJAX error handler
 		 */
 		ajax: function(event, request /*, settings*/) {
-			if (request.statusText == 'OK')
+			if (request.statusText === 'OK') {
 				return;
+			}
 			var response = ams.ajax.getResponse(request);
-			if (response.content_type == 'json') {
+			if (response.content_type === 'json') {
 				ams.ajax.handleJSON(response.data);
 			} else {
 				var title = event.statusText || event.type;
@@ -631,9 +650,9 @@
 					timeout: 10000
 				});
 			}
-			if (window.console) {
-				console.error(event);
-				console.debug(request);
+			if (globals.console) {
+				globals.console.error(event);
+				globals.console.debug(request);
 			}
 		},
 
@@ -641,10 +660,11 @@
 		 * Show AJAX error
 		 */
 		show: function(request, status, error) {
-			if (!error)
+			if (!error) {
 				return;
+			}
 			var response = ams.ajax.getResponse(request);
-			if (response.content_type == 'json') {
+			if (response.content_type === 'json') {
 				ams.ajax.handleJSON(response.data);
 			} else {
 				ams.skin.messageBox('error', {
@@ -654,9 +674,9 @@
 					timeout: 10000
 				});
 			}
-			if (window.console) {
-				console.error(error);
-				console.debug(request);
+			if (globals.console) {
+				globals.console.error(error);
+				globals.console.debug(request);
 			}
 		}
 	};
@@ -678,22 +698,24 @@
 		 * @options: callback options
 		 */
 		check: function(checker, source, callback, options) {
-			if (typeof(callback) == 'object') {
+			if (typeof(callback) === 'object') {
 				options = callback;
 				callback = undefined;
 			}
 			var defaults = {
-				async: typeof(callback) == 'function'
+				async: typeof(callback) === 'function'
 			};
 			var settings = $.extend({}, defaults, options);
 			if (checker === undefined) {
 				ams.getScript(source, function() {
-					if (typeof(callback) == 'function')
+					if (typeof(callback) === 'function') {
 						callback(true, options);
+					}
 				}, settings);
 			} else {
-				if (typeof(callback) == 'function')
+				if (typeof(callback) === 'function') {
 					callback(false, options);
+				}
 			}
 		},
 
@@ -727,39 +749,47 @@
 		 * @param options: AJAX options
 		 */
 		progress: function(event) {
-			if (!event.lengthComputable)
+			if (!event.lengthComputable) {
+				return;
+			}
+			if (event.loaded >= event.total) {
 				return;
-			if (event.loaded >= event.total)
-				return;
-			console.log(parseInt((event.loaded / event.total * 100), 10) + "%");
+			}
+			if (globals.console) {
+				globals.console.log(parseInt((event.loaded / event.total * 100), 10) + "%");
+			}
 		},
 
 		/**
 		 * Post data to given URL
 		 */
 		post: function(url, data, options, callback) {
-			if (url.startsWith(window.location.protocol))
-				var addr = url;
-			else
+			var addr;
+			if (url.startsWith(window.location.protocol)) {
+				addr = url;
+			} else {
 				addr = this.getAddr() + url;
-			if (typeof(options) == 'function') {
+			}
+			if (typeof(options) === 'function') {
 				callback = options;
-				options = {}
+				options = {};
 			} else if (!options) {
 				options = {};
 			}
-			if (typeof(callback) == 'undefined')
+			if (typeof(callback) === 'undefined') {
 				callback = options.callback;
-			if (typeof(callback) == 'string')
+			}
+			if (typeof(callback) === 'string') {
 				callback = ams.getFunctionByName(callback);
+			}
 			delete options.callback;
 
-			var result = undefined;
+			var result;
 			var defaults = {
 				url: addr,
 				type: 'post',
 				cache: false,
-				async: typeof(callback) == 'function',
+				async: typeof(callback) === 'function',
 				data: $.param(data),
 				dataType: 'json',
 				success: callback || function(data /*, status*/) {
@@ -792,9 +822,9 @@
 					result = request.responseText;
 				} else {
 					result = request.responseJSON;
-					if (result)
+					if (result) {
 						data_type = 'json';
-					else {
+					} else {
 						try {
 							result = JSON.parse(request.responseText);
 							data_type = 'json';
@@ -844,15 +874,18 @@
 			var url;
 			switch (status) {
 				case 'alert':
-					alert(result.alert.title + '\n\n' + result.alert.content);
+					if (globals.alert) {
+						globals.alert(result.alert.title + '\n\n' + result.alert.content);
+					}
 					break;
 				case 'error':
 					ams.form.showErrors(form, result);
 					break;
 				case 'info':
 				case 'success':
-					if (result.close_form != false)
+					if (result.close_form !== false) {
 						ams.dialog.close(form);
+					}
 					break;
 				case 'message':
 				case 'messagebox':
@@ -860,28 +893,32 @@
 				case 'notify':
 				case 'callback':
 				case 'callbacks':
-					if (result.close_form != false)
+					if (result.close_form !== false) {
 						ams.dialog.close(form);
+					}
 					break;
 				case 'modal':
 					ams.dialog.open(result.location);
 					break;
 				case 'reload':
-					if (result.close_form != false)
+					if (result.close_form !== false) {
 						ams.dialog.close(form);
+					}
 					url = result.location || window.location.hash;
-					if (url.startsWith('#'))
+					if (url.startsWith('#')) {
 						url = url.substr(1);
+					}
 					ams.skin.loadURL(url, result.target || target || '#content');
 					break;
 				case 'redirect':
-					if (result.close_form == true)
+					if (result.close_form === true) {
 						ams.dialog.close(form);
+					}
 					url = result.location || window.location.href;
 					if (result.window) {
 						window.open(url, result.window, result.options);
 					} else {
-						if (window.location.href == url) {
+						if (window.location.href === url) {
 							window.location.reload(true);
 						} else {
 							window.location.href = url;
@@ -889,52 +926,65 @@
 					}
 					break;
 				default:
-					console.log("Unhandled status: " + status);
-					break;
+					if (globals.console) {
+						globals.console.log("Unhandled status: " + status);
+					}
 			}
+
+			var index;
+			var content;
+			var container;
 			if (result.content) {
-				var content = result.content;
-				var container = $(content.target || target || form || '#content');
-				if (content.raw == true) {
+				content = result.content;
+				container = $(content.target || target || form || '#content');
+				if (content.raw === true) {
 					container.text(content.text);
 				} else {
 					container.html(content.html);
 					ams.initContent(container);
 				}
-				if (!content.keep_hidden)
+				if (!content.keep_hidden) {
 					container.removeClass('hidden');
+				}
 			}
 			if (result.contents) {
 				var contents = result.contents;
-				for (var index in contents) {
+				for (index=0; index < contents.length; index++) {
 					content = contents[index];
 					container = $(content.target);
-					if (content.raw == true) {
+					if (content.raw === true) {
 						container.text(content.text);
 					} else {
 						container.html(content.html);
 						ams.initContent(container);
 					}
-					if (!content.keep_hidden)
+					if (!content.keep_hidden) {
 						container.removeClass('hidden');
+					}
 				}
 			}
+
+			var message;
 			if (result.message) {
-				var message = result.message;
-				if (typeof(message) == 'string') {
-					if ((status == 'info') || (status == 'success'))
+				message = result.message;
+				if (typeof(message) === 'string') {
+					if ((status === 'info') || (status === 'success')) {
 						ams.skin.smallBox(status,
-										  {title: message,
-										   icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
-										   timeout: 3000});
-					else
+										  {
+											  title: message,
+											  icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
+											  timeout: 3000
+										  });
+					} else {
 						ams.skin.alert($(form || '#content'), status, message);
-				} else
+					}
+				} else {
 					ams.skin.alert($(message.target || target || form || '#content'),
 								   message.status || 'success',
 								   message.header,
 								   message.body,
 								   message.subtitle);
+				}
 			}
 			if (result.smallbox) {
 				ams.skin.smallBox(result.smallbox_status || status,
@@ -944,32 +994,36 @@
 			}
 			if (result.messagebox) {
 				message = result.messagebox;
-				if (typeof(message) == 'string')
+				if (typeof(message) === 'string') {
 					ams.skin.messageBox('info',
-										{title: ams.i18n.ERROR_OCCURED,
-										 content: message,
-										 timeout: 10000});
-				else {
+										{
+											title: ams.i18n.ERROR_OCCURED,
+											content: message,
+											timeout: 10000
+										});
+				} else {
 					var message_status = message.status || 'info';
-					if (message_status == 'error' && form && target)
+					if (message_status === 'error' && form && target) {
 						ams.executeFunctionByName(form.data('ams-form-submit-error') || 'MyAMS.form.finalizeSubmitOnError', form, target);
+					}
 					ams.skin.messageBox(message_status,
 										{title: message.title || ams.i18n.ERROR_OCCURED,
 										 content: message.content,
 										 icon: message.icon,
 										 number: message.number,
-										 timeout: message.timeout == null ? undefined : (message.timeout || 10000)});
+										 timeout: message.timeout === null ? undefined : (message.timeout || 10000)});
 				}
 			}
-			if (result.event)
+			if (result.event) {
 				form.trigger(result.event, result.event_options);
-			if (result.callback)
+			}
+			if (result.callback) {
 				ams.executeFunctionByName(result.callback, form, result.options);
+			}
 			if (result.callbacks) {
-				for (index in result.callbacks) {
-					if (!$.isNumeric(index))
-						continue;
-					var callback = result.callbacks[index];
+				var callback;
+				for (index=0; index < result.callbacks.length; index++) {
+					callback = result.callbacks[index];
 					ams.executeFunctionByName(callback, form, callback.options);
 				}
 			}
@@ -1005,33 +1059,37 @@
 			ams.ajax.check($.jsonRpc,
 						   ams.baseURL + 'ext/jquery-jsonrpc' + ams.devext + '.js',
 						   function() {
-								var result;
-								if (typeof(options) == 'function') {
+								if (typeof(options) === 'function') {
 									callback = options;
 									options = {};
 								}
-								else if (!options)
+								else if (!options) {
 									options = {};
-								if (typeof(callback) == 'undefined')
+								}
+								if (callback === 'undefined') {
 									callback = options.callback;
-								if (typeof(callback) == 'string')
+								}
+								if (typeof(callback) === 'string') {
 									callback = ams.getFunctionByName(callback);
+								}
 								delete options.callback;
 
 								var params = {};
-								if (typeof(query) == 'string')
-									params['query'] = query;
-								else if (typeof(query) == 'object')
+								if (typeof(query) === 'string') {
+									params.query = query;
+								} else if (typeof(query) === 'object') {
 									$.extend(params, query);
+								}
 								$.extend(params, options);
 
+								var result;
 								var settings = {
 									url: ams.jsonrpc.getAddr(options.url),
 									type: 'post',
 									cache: false,
 									method: method,
 									params: params,
-									async: typeof(callback) == 'function',
+									async: typeof(callback) === 'function',
 									success: callback || function(data /*, status*/) {
 										result = data.result;
 									},
@@ -1054,17 +1112,19 @@
 			ams.ajax.check($.jsonRpc,
 						   ams.baseURL + 'ext/jquery-jsonrpc' + ams.devext + '.js',
 						   function() {
-								var result;
-								if (typeof(options) == 'function') {
+								if (typeof(options) === 'function') {
 									callback = options;
 									options = {};
 								}
-								else if (!options)
+								else if (!options) {
 									options = {};
-								if (typeof(callback) == 'undefined')
+								}
+								if (typeof(callback) === 'undefined') {
 									callback = options.callback;
-								if (typeof(callback) == 'string')
+								}
+								if (typeof(callback) === 'string') {
 									callback = ams.getFunctionByName(callback);
+								}
 								delete options.callback;
 
 								var result;
@@ -1074,7 +1134,7 @@
 									cache: false,
 									method: method,
 									params: data,
-									async: typeof(callback) == 'function',
+									async: typeof(callback) === 'function',
 									success: callback || function(data /*, status*/) {
 										result = data.result;
 									},
@@ -1115,19 +1175,22 @@
 			ams.ajax.check($.xmlrpc,
 						   ams.baseURL + 'ext/jquery-xmlrpc' + ams.devext + '.js',
 						   function() {
-								var result;
-								if (typeof(options) == 'function') {
+								if (typeof(options) === 'function') {
 									callback = options;
 									options = {};
 								}
-								else if (!options)
+								else if (!options) {
 									options = {};
-								if (typeof(callback) == 'undefined')
+								}
+								if (typeof(callback) === 'undefined') {
 									callback = options.callback;
-								if (typeof(callback) == 'string')
+								}
+								if (typeof(callback) === 'string') {
 									callback = ams.getFunctionByName(callback);
+								}
 								delete options.callback;
 
+								var result;
 								var defaults = {
 									url: ams.xmlrpc.getAddr(url),
 									methodName: method,
@@ -1157,10 +1220,12 @@
 		 */
 		init: function(element) {
 			// Activate form changes if required
-			if (ams.warn_on_form_change)
-				var forms = $('FORM[data-ams-warn-on-change!="false"]', element);
-			else
+			var forms;
+			if (ams.warn_on_form_change) {
+				forms = $('FORM[data-ams-warn-on-change!="false"]', element);
+			} else {
 				forms = $('FORM[data-ams-warn-on-change="true"]', element);
+			}
 			forms.each(function() {
 				var form = $(this);
 				$('INPUT[type="text"], ' +
@@ -1197,7 +1262,7 @@
 		 * Check for modified forms before loading new inner content
 		 */
 		confirmChangedForm: function(element, callback) {
-			if (typeof(element) == 'function') {
+			if (typeof(element) === 'function') {
 				callback = element;
 				element = undefined;
 			}
@@ -1208,8 +1273,9 @@
 					content: '<i class="text-danger fa fa-2x fa-bell shake animated"></i>&nbsp; ' + ams.i18n.FORM_CHANGED_WARNING,
 					buttons: ams.i18n.BTN_OK_CANCEL
 				}, function(button) {
-					if (button == ams.i18n.BTN_OK)
+					if (button === ams.i18n.BTN_OK) {
 						callback.call(element);
+					}
 				});
 			} else {
 				callback.call(element);
@@ -1222,9 +1288,10 @@
 		submit: function(form, handler, submit_options) {
 			// Check params
 			form = $(form);
-			if (!form.exists())
+			if (!form.exists()) {
 				return false;
-			if (typeof(handler) == 'object') {
+			}
+			if (typeof(handler) === 'object') {
 				submit_options = handler;
 				handler = undefined;
 			}
@@ -1241,15 +1308,17 @@
 				return false;
 			}
 			// Check submit validators
-			if (!ams.form._checkSubmitValidators(form))
+			if (!ams.form._checkSubmitValidators(form)) {
 				return false;
+			}
 			// Remove remaining status messages
-			$('.alert, SPAN.state-error', form).remove();
+			$('.alert-danger, SPAN.state-error', form).remove();
 			$('.state-error', form).removeClassPrefix('state-');
 			// Check submit button
 			var button = $(form.data('ams-submit-button'));
-			if (button && !button.data('ams-form-hide-loading'))
+			if (button && !button.data('ams-form-hide-loading')) {
 				button.button('loading');
+			}
 			ams.ajax.check($.fn.ajaxSubmit,
 						   ams.baseURL + 'ext/jquery-form-3.49' + ams.devext + '.js',
 						   function() {
@@ -1259,23 +1328,29 @@
 									var button;
 									var data = form.data();
 									var form_options = data.amsFormOptions;
-
-									if (submit_options)
-										var form_data_callback = submit_options.formDataInitCallback;
-									if (form_data_callback)
+									var form_data;
+									var form_data_callback;
+
+									if (submit_options) {
+										form_data_callback = submit_options.formDataInitCallback;
+									}
+									if (form_data_callback) {
 										delete submit_options.formDataInitCallback;
-									else
+									} else {
 										form_data_callback = data.amsFormDataInitCallback;
+									}
 									if (form_data_callback) {
 										var veto = {};
-										if (typeof(form_data_callback) == 'function')
-											var form_data = form_data_callback.call(form, veto);
-										else
+										if (typeof(form_data_callback) === 'function') {
+											form_data = form_data_callback.call(form, veto);
+										} else {
 											form_data = ams.executeFunctionByName(form_data_callback, form, veto);
+										}
 										if (veto.veto) {
 											button = form.data('ams-submit-button');
-											if (button)
+											if (button) {
 												button.button('reset');
+											}
 											ams.form.finalizeSubmitFooter.call(form);
 											return false;
 										}
@@ -1291,32 +1366,43 @@
 										buttonTarget = button.data('ams-form-submit-target');
 									}
 
+									var url;
 									var form_handler = handler || buttonHandler || data.amsFormHandler || '';
 									if (form_handler.startsWith(window.location.protocol)) {
-										var url = form_handler;
+										url = form_handler;
 									} else {
 										var action = form.attr('action').replace(/#/, '');
-										if (action.startsWith(window.location.protocol))
+										if (action.startsWith(window.location.protocol)) {
 											url = action;
-										else
+										} else {
 											url = ams.ajax.getAddr() + action;
+										}
 										url += form_handler;
 									}
 
 									var target = null;
-									if (data.amsFormInitSubmitTarget) {
-										target = $(buttonTarget || data.amsFormSubmitTarget || '#content');
-										ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmit', form, target);
-									} else if (!data.amsFormHideSubmitFooter)
-										ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmitFooter', form);
-
-									var hasUpload = typeof(options.uuid) != 'undefined';
+									if (submit_options && submit_options.initSubmitTarget) {
+										ams.executeFunctionByName(submit_options.initSubmitTarget);
+									} else {
+										if (data.amsFormInitSubmitTarget) {
+											target = $(buttonTarget || data.amsFormSubmitTarget || '#content');
+											ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmit', form, target);
+										} else if (!data.amsFormHideSubmitFooter) {
+											ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmitFooter', form);
+										}
+									}
+
+									var hasUpload = typeof(options.uuid) !== 'undefined';
 									if (hasUpload) {
-										if (url.indexOf('X-Progress-ID') < 0)
+										if (url.indexOf('X-Progress-ID') < 0) {
 											url += "?X-Progress-ID=" + options.uuid;
+										}
 										delete options.uuid;
 									}
 
+									if (submit_options) {
+										form_data = $.extend({}, form_data, submit_options.form_data);
+									}
 									var defaults = {
 										url: url,
 										type: 'post',
@@ -1324,33 +1410,37 @@
 										data: form_data,
 										dataType: data.amsFormDatatype,
 										beforeSerialize: function(/*form, options*/) {
-											if (typeof(tinyMCE) != 'undefined')
-												tinyMCE.triggerSave();
+											if (typeof(globals.tinyMCE) !== 'undefined') {
+												globals.tinyMCE.triggerSave();
+											}
 										},
 										beforeSubmit: function(data, form /*, options*/) {
 											form.data('submitted', true);
 										},
 										error: function(request, status, error, form) {
-											if (target)
+											if (target) {
 												ams.executeFunctionByName(data.amsFormSubmitError || 'MyAMS.form.finalizeSubmitOnError', form, target);
+											}
 											if (form.is(':visible')) {
 												var button = form.data('ams-submit-button');
-												if (button)
+												if (button) {
 													button.button('reset');
+												}
 												ams.form.finalizeSubmitFooter.call(form);
 											}
 											form.data('submitted', false);
 											form.removeData('ams-submit-button');
 										},
 										iframe: hasUpload
-									}
-									var download_target = data.amsFormDownloadTarget;
+									};
+									var download_target = (submit_options && submit_options.downloadTarget) || data.amsFormDownloadTarget;
 									if (download_target) {
 										var iframe = $('iframe[name="' + download_target + '"]');
-										if (!iframe.exists())
+										if (!iframe.exists()) {
 											iframe = $('<iframe></iframe>').hide()
 																		   .attr('name', download_target)
 																		   .appendTo(form);
+										}
 										defaults = $.extend({}, defaults, {
 											iframe: true,
 											iframeTarget: iframe,
@@ -1361,13 +1451,16 @@
 												} else {
 													var callback;
 													var button = form.data('ams-submit-button');
-													if (button)
+													if (button) {
 														callback = button.data('ams-form-submit-callback');
-													if (!callback)
+													}
+													if (!callback) {
 														callback = ams.getFunctionByName(data.amsFormSubmitCallback) || ams.form._submitCallback;
+													}
 													callback.call(form, result, status, request, form);
-													if (form.is(':visible') && button)
+													if (form.is(':visible') && button) {
 														button.button('reset');
+													}
 													form.data('submitted', false);
 													form.removeData('ams-submit-button');
 													form.removeAttr('data-ams-form-changed');
@@ -1377,12 +1470,14 @@
 									} else {
 										defaults = $.extend({}, defaults, {
 											error: function(request, status, error, form) {
-												if (target)
+												if (target) {
 													ams.executeFunctionByName(data.amsFormSubmitError || 'MyAMS.form.finalizeSubmitOnError', form, target);
+												}
 												if (form.is(':visible')) {
 													var button = form.data('ams-submit-button');
-													if (button)
+													if (button) {
 														button.button('reset');
+													}
 													ams.form.finalizeSubmitFooter.call(form);
 												}
 												form.data('submitted', false);
@@ -1391,13 +1486,16 @@
 											success: function(result, status, request, form) {
 												var callback;
 												var button = form.data('ams-submit-button');
-												if (button)
+												if (button) {
 													callback = button.data('ams-form-submit-callback');
-												if (!callback)
+												}
+												if (!callback) {
 													callback = ams.getFunctionByName(data.amsFormSubmitCallback) || ams.form._submitCallback;
+												}
 												callback.call(form, result, status, request, form);
-												if (form.is(':visible') && button)
+												if (form.is(':visible') && button) {
 													button.button('reset');
+												}
 												form.data('submitted', false);
 												form.removeData('ams-submit-button');
 												form.removeAttr('data-ams-form-changed');
@@ -1414,8 +1512,9 @@
 											ams.dialog.close(form);
 										} else {
 											ams.form.finalizeSubmitFooter.call(form);
-											if (button)
+											if (button) {
 												button.button('reset');
+											}
 											form.data('submitted', false);
 											form.removeData('ams-submit-button');
 											form.removeAttr('data-ams-form-changed');
@@ -1435,8 +1534,9 @@
 										uuid: $.progressBar.submit(form)
 									});
 									_submitAjaxForm(form, settings);
-								} else
+								} else {
 									_submitAjaxForm(form, {});
+								}
 						   });
 			return false;
 		},
@@ -1451,10 +1551,12 @@
 		initSubmit: function(target, message) {
 			var form = $(this);
 			var spin = '<i class="fa fa-3x fa-gear fa-spin"></i>';
-			if (!message)
+			if (!message) {
 				message = form.data('ams-form-submit-message');
-			if (message)
+			}
+			if (message) {
 				spin += '<strong>' + message + '</strong>';
+			}
 			$(target).html('<div class="row margin-20"><div class="text-center">' + spin + '</div></div>');
 			$(target).parents('.hidden').removeClass('hidden');
 		},
@@ -1479,10 +1581,12 @@
 		initSubmitFooter: function(message) {
 			var form = $(this);
 			var spin = '<i class="fa fa-3x fa-gear fa-spin"></i>';
-			if (!message)
+			if (!message) {
 				message = $(this).data('ams-form-submit-message');
-			if (message)
+			}
+			if (message) {
 				spin += '<strong class="submit-message align-top padding-left-10 margin-top-10">' + message + '</strong>';
+			}
 			var footer = $('footer', form);
 			$('button', footer).hide();
 			footer.append('<div class="row"><div class="text-center">' + spin + '</div></div>');
@@ -1522,25 +1626,31 @@
 		 */
 		_submitCallback: function(result, status, request, form) {
 
+			var button;
 			if (form.is(':visible')) {
 				ams.form.finalizeSubmitFooter.call(form);
-				var button = form.data('ams-submit-button');
-				if (button)
+				button = form.data('ams-submit-button');
+				if (button) {
 					button.button('reset');
+				}
 			}
+
 			var data = form.data();
-			if (data.amsFormDatatype)
-				var data_type = data.amsFormDatatype;
-			else {
+			var data_type;
+			if (data.amsFormDatatype) {
+				data_type = data.amsFormDatatype;
+			} else {
 				var request_data = ams.ajax.getResponse(request);
 				data_type = request_data.content_type;
 				result = request_data.data;
 			}
 
-			if (button)
-				var target = $(button.amsFormSubmitTarget || data.amsFormSubmitTarget || '#content');
-			else
+			var target;
+			if (button) {
+				target = $(button.amsFormSubmitTarget || data.amsFormSubmitTarget || '#content');
+			} else {
 				target = $(data.amsFormSubmitTarget || '#content');
+			}
 
 			switch (data_type) {
 				case 'json':
@@ -1551,12 +1661,16 @@
 				case 'xml':
 					break;
 				case 'html':
+					/* falls through */
 				case 'text':
+					/* falls through */
 				default:
-					if (button && (button.data('ams-keep-modal') !== true))
+					if (button && (button.data('ams-keep-modal') !== true)) {
 						ams.dialog.close(form);
-					if (!target.exists())
+					}
+					if (!target.exists()) {
 						target = $('body');
+					}
 					target.parents('.hidden').removeClass('hidden');
 					$('.alert', target.parents('.alerts-container')).remove();
 					target.css({opacity: '0.0'})
@@ -1576,10 +1690,11 @@
 		 * Get list of custom validators called before submit
 		 */
 		_getSubmitValidators: function(form) {
-			var validators = new Array();
+			var validators = [];
 			var form_validator = form.data('ams-form-validator');
-			if (form_validator)
+			if (form_validator) {
 				validators.push([form, form_validator]);
+			}
 			$('[data-ams-form-validator]', form).each(function() {
 				var source = $(this);
 				validators.push([source, source.data('ams-form-validator')]);
@@ -1598,30 +1713,31 @@
 		 */
 		_checkSubmitValidators: function(form) {
 			var validators = ams.form._getSubmitValidators(form);
-			if (!validators.length)
+			if (!validators.length) {
 				return true;
-			var output = new Array();
+			}
+			var output = [];
 			var result = true;
-			for (var index in validators) {
-				if (!$.isNumeric(index))  // IE check !
-					continue;
+			for (var index=0; index < validators.length; index++) {
 				var validator = validators[index];
 				var source = validator[0];
 				var handler = validator[1];
 				var validator_result = ams.executeFunctionByName(handler, form, source);
-				if (validator_result === false)
+				if (validator_result === false) {
 					result = false;
-				else if (typeof(validator_result) == 'string')
+				} else if (typeof(validator_result) === 'string') {
 					output.push(validator_result);
-				else if (result.length && (result.length > 0))
+				} else if (result.length && (result.length > 0)) {
 					output = output.concat(result);
+				}
 			}
 			if (output.length > 0) {
-				var header = output.length == 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
+				var header = output.length === 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
 				ams.skin.alert(form, 'danger', header, output);
 				return false;
-			} else
+			} else {
 				return result;
+			}
 		},
 
 		/**
@@ -1638,47 +1754,47 @@
 		 *             message: "Second error message"},...]}
 		 */
 		showErrors: function(form, errors) {
-			if (typeof(errors) == 'string') {
-				ams.skin.alert(form, 'error', ams.i18n.ERROR_OCCURED, errors)
+			var header;
+			if (typeof(errors) === 'string') {
+				ams.skin.alert(form, 'error', ams.i18n.ERROR_OCCURED, errors);
 			} else if (errors instanceof Array) {
-				var header = errors.length == 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
+				header = errors.length === 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
 				ams.skin.alert(form, 'error', header, errors);
 			} else {
 				$('.state-error', form).removeClass('state-error');
 				header = errors.error_header ||
 						 (errors.widgets && (errors.widgets.length > 1) ? ams.i18n.ERRORS_OCCURED : ams.i18n.ERROR_OCCURED);
-				var message = new Array();
+				var message = [];
 				var index;
-				for (index in errors.messages) {
-					if (!$.isNumeric(index))
-						continue;
-					if (errors.messages[index].header) {
-						message.push('<strong>' + errors.messages[index].header + '</strong><br />' + errors.messages[index].message);
+				for (index=0; index < errors.messages.length; index++) {
+					var msg = errors.messages[index];
+					if (msg.header) {
+						message.push('<strong>' + msg.header + '</strong><br />' + msg.message);
 					} else {
-						message.push(errors.messages[index].message || errors.messages[index]);
+						message.push(msg.message || msg);
 					}
 				}
-				for (index in errors.widgets) {
-					if (!$.isNumeric(index))
-						continue;
-					// set widget status message
-					var widget_data = errors.widgets[index];
-					var widget = $('[name="' + widget_data.name + '"]', form);
-					widget.parents('label:first')
-						  .removeClassPrefix('state-')
-						  .addClass('state-error')
-						  .after('<span for="name" class="state-error">' + widget_data.message + '</span>');
-					// complete form alert message
-					if (widget_data.label) {
-						message.push(widget_data.label + ' : ' + widget_data.message);
-					}
-					// mark parent tab (if any) with error status
-					var tab_index = widget.parents('.tab-pane').index() + 1;
-					if (tab_index > 0) {
-						var nav_tabs = $('.nav-tabs', $(widget).parents('.tabforms'));
-						$('li:nth-child(' + tab_index + ')', nav_tabs).removeClassPrefix('state-')
-																	  .addClass('state-error');
-						$('li.state-error:first a', form).click();
+				if (errors.widgets) {
+					for (index = 0; index < errors.widgets.length; index++) {
+						// set widget status message
+						var widget_data = errors.widgets[index];
+						var widget = $('[name="' + widget_data.name + '"]', form);
+						widget.parents('label:first')
+								.removeClassPrefix('state-')
+								.addClass('state-error')
+								.after('<span for="name" class="state-error">' + widget_data.message + '</span>');
+						// complete form alert message
+						if (widget_data.label) {
+							message.push(widget_data.label + ' : ' + widget_data.message);
+						}
+						// mark parent tab (if any) with error status
+						var tab_index = widget.parents('.tab-pane').index() + 1;
+						if (tab_index > 0) {
+							var nav_tabs = $('.nav-tabs', $(widget).parents('.tabforms'));
+							$('li:nth-child(' + tab_index + ')', nav_tabs).removeClassPrefix('state-')
+									.addClass('state-error');
+							$('li.state-error:first a', form).click();
+						}
 					}
 				}
 				ams.skin.alert(form, errors.error_level || 'error', header, message, errors.error_message);
@@ -1705,8 +1821,10 @@
 			if (element) {
 				dialog = element.objectOrParentWithClass('modal-dialog');
 			}
+
+			var callbacks;
 			if (dialog && dialog.exists()) {
-				var callbacks = dialog.data('shown-callbacks');
+				callbacks = dialog.data('shown-callbacks');
 				if (callbacks === undefined) {
 					callbacks = [];
 					dialog.data('shown-callbacks', callbacks);
@@ -1714,8 +1832,9 @@
 			} else {
 				callbacks = ams.dialog._shown_callbacks;
 			}
-			if (callbacks.indexOf(callback) < 0)
+			if (callbacks.indexOf(callback) < 0) {
 				callbacks.push(callback);
+			}
 		},
 
 		/**
@@ -1731,8 +1850,10 @@
 			if (element) {
 				dialog = element.objectOrParentWithClass('modal-dialog');
 			}
+
+			var callbacks;
 			if (dialog && dialog.exists()) {
-				var callbacks = dialog.data('hide-callbacks');
+				callbacks = dialog.data('hide-callbacks');
 				if (callbacks === undefined) {
 					callbacks = [];
 					dialog.data('hide-callbacks', callbacks);
@@ -1740,8 +1861,9 @@
 			} else {
 				callbacks = ams.dialog._hide_callbacks;
 			}
-			if (callbacks.indexOf(callback) < 0)
+			if (callbacks.indexOf(callback) < 0) {
 				callbacks.push(callback);
+			}
 		},
 
 		/**
@@ -1763,21 +1885,25 @@
 												'</div>' +
 											'</div>';
 									}
-									if (typeof(source) == 'string') {
-										var source_data = {};
-										var url = source;
+
+									var source_data;
+									var url;
+									if (typeof(source) === 'string') {
+										source_data = {};
+										url = source;
 									} else {
 										source_data = source.data();
 										url = source.attr('href') || source_data.amsUrl;
 										var url_getter = ams.getFunctionByName(url);
-										if (typeof(url_getter) == 'function') {
+										if (typeof(url_getter) === 'function') {
 											url = url_getter.call(source);
 										}
 									}
-									if (!url)
+									if (!url) {
 										return;
+									}
 									$('body').modalmanager('loading');
-									if (url.indexOf('#') == 0) {
+									if (url.indexOf('#') === 0) {
 										// Inner hidden modal dialog
 										$(url).modal('show');
 									} else {
@@ -1801,15 +1927,17 @@
 													case 'xml':
 														break;
 													case 'html':
+														/* falls through */
 													case 'text':
+														/* falls through */
 													default:
 														var content = $(result);
 														var dialog = $('.modal-dialog', content.wrap('<div></div>').parent());
 														var dialog_data = dialog.data();
 														var data_options = {
 															overflow: dialog_data.amsModalOverflow || '.modal-viewport',
-															maxHeight: dialog_data.amsModalMaxHeight === undefined
-																	? function() {
+															maxHeight: dialog_data.amsModalMaxHeight === undefined ?
+																	function() {
 																		return $(window).height() -
 																					$('.modal-header', content).outerHeight(true) -
 																					$('footer', content).outerHeight(true) - 85;
@@ -1841,15 +1969,17 @@
 			function resetViewport(ev) {
 				var top = $('.scrollmarker.top', viewport);
 				var top_position = viewport.scrollTop();
-				if (top_position > 0)
+				if (top_position > 0) {
 					top.show();
-				else
+				} else {
 					top.hide();
+				}
 				var bottom = $('.scrollmarker.bottom', viewport);
-				if (maxHeight + top_position >= viewport.get(0).scrollHeight)
+				if (maxHeight + top_position >= viewport.get(0).scrollHeight) {
 					bottom.hide();
-				else
+				} else {
 					bottom.show();
+				}
 			}
 
 			var modal = e.target;
@@ -1857,7 +1987,7 @@
 			if (viewport.exists()) {
 				var maxHeight = parseInt(viewport.css('max-height'));
 				var barWidth = $.scrollbarWidth();
-				if (viewport.height() == maxHeight) {
+				if (viewport.height() === maxHeight) {
 					$('<div></div>').addClass('scrollmarker')
 						.addClass('top')
 						.css('top', 0)
@@ -1878,16 +2008,28 @@
 			}
 
 			// Call shown callbacks registered for this dialog
+			var index;
 			var callbacks = $('.modal-dialog', modal).data('shown-callbacks');
 			if (callbacks) {
-				for (var index in callbacks) {
+				for (index=0; index < callbacks.length; index++) {
 					callbacks[index].call(modal);
 				}
 			}
 			// Call globally registered shown callbacks
 			callbacks = ams.dialog._shown_callbacks;
-			for (index in callbacks) {
-				callbacks[index].call(modal);
+			if (callbacks) {
+				for (index=0; index < callbacks.length; index++) {
+					callbacks[index].call(modal);
+				}
+			}
+
+			// Set focus to first input
+			var focused = $('[data-ams-focus-target]', modal).first();
+			if (!focused.exists()) {
+				focused = $('input, select', modal).first();
+			}
+			if (focused.exists()) {
+				focused.focus();
 			}
 		},
 
@@ -1898,8 +2040,9 @@
 			var modal = context.parents('.modal').data('modal');
 			if (modal) {
 				var manager = $('body').data('modalmanager');
-				if (manager && (manager.getOpenModals().indexOf(modal) >= 0))
+				if (manager && (manager.getOpenModals().indexOf(modal) >= 0)) {
 					modal.hide();
+				}
 			}
 		},
 
@@ -1912,16 +2055,19 @@
 			// Call registered cleaning callbacks
 			ams.skin.cleanContainer(modal);
 			// Call hide callbacks registered for this dialog
+			var index;
 			var callbacks = $('.modal-dialog', modal).data('hide-callbacks');
 			if (callbacks) {
-				for (var index in callbacks) {
+				for (index=0; index < callbacks.length; index++) {
 					callbacks[index].call(modal);
 				}
 			}
 			// Call globally registered hide callbacks
 			callbacks = ams.dialog._hide_callbacks;
-			for (index in callbacks) {
-				callbacks[index].call(modal);
+			if (callbacks) {
+				for (index=0; index < callbacks.length; index++) {
+					callbacks[index].call(modal);
+				}
 			}
 		}
 	};
@@ -1948,16 +2094,18 @@
 		select2FormatSelection: function(object, container) {
 			if (object instanceof Array) {
 				$(object).each(function() {
-					if (typeof(this) == 'object')
+					if (typeof(this) === 'object') {
 						container.append(this.text);
-					else
+					} else {
 						container.append(this);
+					}
 				});
 			} else {
-				if (typeof(object) == 'object')
+				if (typeof(object) === 'object') {
 					container.append(object.text);
-				else
+				} else {
 					container.append(object);
+				}
 			}
 		},
 
@@ -1974,7 +2122,7 @@
 					break;
 				case 'modal':
 					$(this).data('select2').dropdown.hide();
-					ams.dialog.open(result.location);
+					ams.dialog.open(data.location);
 					break;
 				default:
 					return {
@@ -1988,7 +2136,7 @@
 		/** Select2 JSON-RPC success callback */
 		select2QueryMethodSuccessCallback: function(data, status, options) {
 			var result = data.result;
-			if (typeof(result) == 'string') {
+			if (typeof(result) === 'string') {
 				try {
 					result = JSON.parse(result);
 				} catch (e) {}
@@ -2018,25 +2166,26 @@
 		/** Context menu handler */
 		contextMenuHandler: function(target, menu) {
 			var menu_data = menu.data();
-			if (menu_data.toggle == 'modal') {
+			if (menu_data.toggle === 'modal') {
 				ams.dialog.open(menu);
 			} else {
 				var href = menu.attr('href') || menu_data.amsUrl;
-				if (!href || href.startsWith('javascript:') || menu.attr('target'))
+				if (!href || href.startsWith('javascript') || menu.attr('target')) {
 					return;
+				}
 				ams.event.stop();
 				var href_getter = ams.getFunctionByName(href);
-				if (typeof(href_getter) == 'function') {
+				if (typeof(href_getter) === 'function') {
 					href = href_getter.call(menu, target);
 				}
-				if (typeof(href) == 'function') {
+				if (typeof(href) === 'function') {
 					// Javascript function call
 					href.call(menu, target);
 				} else {
 					// Standard AJAX or browser URL call
 					// Convert %23 chars to #
 					href = href.replace(/\%23/, '#');
-					var target = menu.data('ams-target');
+					target = menu.data('ams-target');
 					if (target) {
 						ams.form.confirmChangedForm(target, function () {
 							ams.skin.loadURL(href, target, menu.data('ams-link-options'), menu.data('ams-link-callback'));
@@ -2044,17 +2193,19 @@
 					} else {
 						ams.form.confirmChangedForm(function () {
 							if (href.startsWith('#')) {
-								if (href != location.hash) {
+								if (href !== location.hash) {
 									if (ams.root.hasClass('mobile-view-activated')) {
 										ams.root.removeClass('hidden-menu');
 										window.setTimeout(function () {
 											window.location.hash = href;
 										}, 150);
-									} else
+									} else {
 										window.location.hash = href;
+									}
 								}
-							} else
+							} else {
 								window.location = href;
+							}
 						});
 					}
 				}
@@ -2092,53 +2243,58 @@
 			ams.plugins.initData(element);
 
 			// Check for disabled plug-ins
-			var disabled = new Array();
+			var disabled = [];
 			$('[data-ams-plugins-disabled]', element).each(function() {
 				var plugins = $(this).data('ams-plugins-disabled').split(/\s+/);
-				for (var name in plugins) {
-					disabled.push(plugins[name]);
+				for (var index=0; index < plugins.length; index++) {
+					disabled.push(plugins[index]);
 				}
 			});
 
 			// Load and register new plug-ins
-			var name;
+			var plugin;
 			$('[data-ams-plugins]', element).each(function() {
 				var source = $(this);
-				var plugins = {}
+				var plugins = {};
+				var name;
 				if (typeof(source.data('ams-plugins')) === 'string') {
 					var names = source.data('ams-plugins').split(/\s+/);
-					for (var index in names) {
+					for (var index=0; index < names.length; index++) {
 						name = names[index];
-						var plugin_options = {
+						plugins[name] = {
 							src: source.data('ams-plugin-' + name + '-src'),
 							css: source.data('ams-plugin-' + name + '-css'),
 							callback: source.data('ams-plugin-' + name + '-callback'),
 							register: source.data('ams-plugin-' + name + '-register'),
 							async: source.data('ams-plugin-' + name + '-async')
-						}
-						plugins[name] = plugin_options;
+						};
 					}
 				} else {
 					plugins = source.data('ams-plugins');
 				}
+
+				function executePlugin() {
+					var callback = plugin.callback;
+					if (callback) {
+						var called = ams.getFunctionByName(callback);
+						if (plugin.register !== false) {
+							ams.plugins.enabled[name] = called;
+						}
+					} else {
+						if (plugin.register !== false) {
+							ams.plugins.enabled[name] = null;
+						}
+					}
+					var css = plugin.css;
+					if (css) {
+						ams.getCSS(css, name + '_css');
+					}
+				}
+
 				for (name in plugins) {
 					if (ams.plugins.enabled[name] === undefined) {
-						var plugin = plugins[name];
-						ams.getScript(plugin.src, function() {
-							var callback = plugin.callback;
-							if (callback) {
-								var called = ams.getFunctionByName(callback);
-								if (plugin.register !== false)
-									ams.plugins.enabled[name] = called;
-							} else {
-								if (plugin.register !== false)
-									ams.plugins.enabled[name] = null;
-							}
-							var css = plugin['css'];
-							if (css) {
-								ams.getCSS(css, name + '_css');
-							}
-						}, {
+						plugin = plugins[name];
+						ams.getScript(plugin.src, executePlugin, {
 							async: plugin.async === undefined ? true : plugin.async
 						});
 					}
@@ -2147,11 +2303,16 @@
 
 			// Run all enabled plug-ins
 			for (var index in ams.plugins.enabled) {
-				if (disabled.indexOf(index) >= 0)
+				if (!ams.plugins.enabled.hasOwnProperty(index)) {
+					continue;
+				}
+				if (disabled.indexOf(index) >= 0) {
 					continue;
-				var plugin = ams.plugins.enabled[index];
-				if (typeof(plugin) == 'function')
+				}
+				plugin = ams.plugins.enabled[index];
+				if (typeof(plugin) === 'function') {
 					plugin(element);
+				}
 			}
 		},
 
@@ -2165,8 +2326,10 @@
 			$('[data-ams-data]', element).each(function() {
 				var handler = $(this);
 				var data = handler.data('ams-data');
-				for (var index in data) {
-					handler.attr('data-' + index, data[index]);
+				if (data) {
+					for (var index=0; index < data.length; index++) {
+						handler.attr('data-' + index, data[index]);
+					}
 				}
 			});
 		},
@@ -2179,17 +2342,18 @@
 		 * @callback: a callback function which can be called after plug-in registry
 		 */
 		register: function(plugin, name, callback) {
-			if (typeof(name) == 'function') {
+			if (typeof(name) === 'function') {
 				callback = name;
 				name = null;
 			}
 			name = name || plugin.name;
 			if (ams.plugins.enabled.indexOf(name) >= 0) {
-				if (window.console)
-					console.warn("Plugin "+name+" is already registered!");
+				if (globals.console) {
+					globals.console.warn("Plugin " + name + " is already registered!");
+				}
 				return;
 			}
-			if (typeof(plugin) == 'object') {
+			if (typeof(plugin) === 'object') {
 				var src = plugin.src;
 				if (src) {
 					ams.ajax.check(plugin.callback, src, function(first_load) {
@@ -2202,7 +2366,7 @@
 								ams.executeFunctionByName(callback);
 							}
 						}
-					})
+					});
 				} else {
 					ams.plugins.enabled[name] = ams.getFunctionByName(plugin.callback);
 					if (plugin.css) {
@@ -2212,7 +2376,7 @@
 						ams.executeFunctionByName(callback);
 					}
 				}
-			} else if (typeof(plugin) == 'function') {
+			} else if (typeof(plugin) === 'function') {
 				ams.plugins.enabled[name] = plugin;
 				if (callback) {
 					ams.executeFunctionByName(callback);
@@ -2240,33 +2404,34 @@
 			 */
 			hint: function(element) {
 				var hints = $('.hint:not(:parents(.nohints))', element);
-				if (hints.length > 0)
+				if (hints.length > 0) {
 					ams.ajax.check($.fn.tipsy,
 								   ams.baseURL + 'ext/jquery-tipsy' + ams.devext + '.js',
-								   function() {
-										ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + ams.devext + '.css',
+								   function () {
+									   ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + ams.devext + '.css',
 												  'jquery-tipsy');
-										hints.each(function() {
-											var hint = $(this);
-											var data = hint.data();
-											var data_options = {
-												html: data.amsHintHtml,
-												title: ams.getFunctionByName(data.amsHintTitleGetter) || function() {
-													var hint = $(this);
-													return hint.attr('original-title') ||
+									   hints.each(function () {
+										   var hint = $(this);
+										   var data = hint.data();
+										   var data_options = {
+											   html: data.amsHintHtml,
+											   title: ams.getFunctionByName(data.amsHintTitleGetter) || function () {
+												   var hint = $(this);
+												   return hint.attr('original-title') ||
 														   hint.attr(data.amsHintTitleAttr || 'title') ||
 														   (data.amsHintHtml ? hint.html() : hint.text());
-												},
-												opacity: data.amsHintOpacity || 0.95,
-												gravity: data.amsHintGravity || 'sw',
-												offset: data.amsHintOffset || 0
-											};
-											var settings = $.extend({}, data_options, data.amsHintOptions);
-											settings = ams.executeFunctionByName(data.amsHintInitCallback, hint, settings) || settings;
-											var plugin = hint.tipsy(settings);
-											ams.executeFunctionByName(data.amsHintAfterInitCallback, hint, plugin, settings);
-										});
+											   },
+											   opacity: data.amsHintOpacity || 0.95,
+											   gravity: data.amsHintGravity || 'sw',
+											   offset: data.amsHintOffset || 0
+										   };
+										   var settings = $.extend({}, data_options, data.amsHintOptions);
+										   settings = ams.executeFunctionByName(data.amsHintInitCallback, hint, settings) || settings;
+										   var plugin = hint.tipsy(settings);
+										   ams.executeFunctionByName(data.amsHintAfterInitCallback, hint, plugin, settings);
+									   });
 								   });
+				}
 			},
 
 			/**
@@ -2301,15 +2466,16 @@
 					if (!data.amsSwitcher) {
 						$('<i class="fa fa-fw"></i>')
 							.prependTo($(this))
-							.addClass(data.amsSwitcherState == 'open' ?
+							.addClass(data.amsSwitcherState === 'open' ?
 									  (data.amsSwitcherMinusClass || 'fa-minus') :
 									  (data.amsSwitcherPlusClass || 'fa-plus'));
 						legend.on('click', function(e) {
 							e.preventDefault();
 							var veto = {};
 							legend.trigger('ams.switcher.before-switch', [legend, veto]);
-							if (veto.veto)
+							if (veto.veto) {
 								return;
+							}
 							if (fieldset.hasClass('switched')) {
 								fieldset.removeClass('switched');
 								$('.fa', legend).removeClass(data.amsSwitcherPlusClass || 'fa-plus')
@@ -2319,8 +2485,9 @@
 								if (id) {
 									$('legend.switcher[data-ams-switcher-sync="'+id+'"]', fieldset).each(function() {
 										var switcher = $(this);
-										if (switcher.parents('fieldset').hasClass('switched'))
+										if (switcher.parents('fieldset').hasClass('switched')) {
 											switcher.click();
+										}
 									});
 								}
 							} else {
@@ -2330,8 +2497,9 @@
 								legend.trigger('ams.switcher.closed', [legend]);
 							}
 						});
-						if (data.amsSwitcherState != 'open')
+						if (data.amsSwitcherState !== 'open') {
 							fieldset.addClass('switched');
+						}
 						legend.data('ams-switcher', 'on');
 					}
 				});
@@ -2356,7 +2524,7 @@
 						var marker = data.amsCheckerMarker || false;
 						if (prefix) {
 							hidden = $('<input type="hidden">').attr('name', prefix + fieldname)
-															   .val(data.amsCheckerState == 'on' ? checkedValue : uncheckedValue)
+															   .val(data.amsCheckerState === 'on' ? checkedValue : uncheckedValue)
 															   .prependTo(legend);
 						} else if (marker) {
 							$('<input type="hidden">').attr('name', marker)
@@ -2368,7 +2536,7 @@
 																.data('ams-checker-hidden-input', hidden)
 																.data('ams-checker-init', true)
 																.val(data.amsCheckerValue || true)
-																.attr('checked', data.amsCheckerState == 'on' ? 'checked' : null);
+																.attr('checked', data.amsCheckerState === 'on' ? 'checked' : null);
 						if (data.amsCheckerReadonly) {
 							input.attr('disabled', 'disabled');
 						} else {
@@ -2386,21 +2554,25 @@
 								if (!data.amsCheckerCancelDefault) {
 									var hidden = input.data('ams-checker-hidden-input');
 									if (isChecked) {
-										if (data.amsCheckerMode == 'disable')
+										if (data.amsCheckerMode === 'disable') {
 											fieldset.removeAttr('disabled');
-										else
+										} else {
 											fieldset.removeClass('switched');
-										if (hidden)
+										}
+										if (hidden) {
 											hidden.val(checkedValue);
+										}
 										$('[data-required]', fieldset).attr('required', 'required');
 										legend.trigger('ams.checker.opened', [legend]);
 									} else {
-										if (data.amsCheckerMode == 'disable')
+										if (data.amsCheckerMode === 'disable') {
 											fieldset.prop('disabled', 'disabled');
-										else
+										} else {
 											fieldset.addClass('switched');
-										if (hidden)
+										}
+										if (hidden) {
 											hidden.val(uncheckedValue);
+										}
 										$('[data-required]', fieldset).removeAttr('required');
 										legend.trigger('ams.checker.closed', [legend]);
 									}
@@ -2413,13 +2585,14 @@
 							   .prependTo(legend);
 						var required = $('[required]', fieldset);
 						required.attr('data-required', true);
-						if (data.amsCheckerState == 'on') {
+						if (data.amsCheckerState === 'on') {
 							input.attr('checked', true);
 						} else {
-							if (data.amsCheckerMode == 'disable')
+							if (data.amsCheckerMode === 'disable') {
 								fieldset.attr('disabled', 'disabled');
-							else
+							} else {
 								fieldset.addClass('switched');
+							}
 							required.removeAttr('required');
 						}
 						legend.data('ams-checker', 'on');
@@ -2570,53 +2743,54 @@
 												allowClear: data.amsSelect2AllowClear === undefined ? true : data.amsSelect2AllowClear,
 												width: data.amsSelect2Width || '100%',
 												initSelection: ams.getFunctionByName(data.amsSelect2InitSelection),
-												formatSelection: data.amsSelect2FormatSelection === undefined
-																	? ams.helpers.select2FormatSelection
+												formatSelection: data.amsSelect2FormatSelection === undefined ?
+																	ams.helpers.select2FormatSelection
 																	: ams.getFunctionByName(data.amsSelect2FormatSelection),
 												formatResult: ams.getFunctionByName(data.amsSelect2FormatResult),
-												formatMatches: data.amsSelect2FormatMatches === undefined
-																	? function(matches) {
-																		if (matches == 1)
+												formatMatches: data.amsSelect2FormatMatches === undefined ?
+																	function(matches) {
+																		if (matches === 1) {
 																			return ams.i18n.SELECT2_MATCH;
-																		else
+																		} else {
 																			return matches + ams.i18n.SELECT2_MATCHES;
+																		}
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatMatches),
-												formatNoMatches: data.amsSelect2FormatResult === undefined
-																	? function(term) {
+												formatNoMatches: data.amsSelect2FormatResult === undefined ?
+																	function(term) {
 																		return ams.i18n.SELECT2_NOMATCHES;
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatResult),
-												formatInputTooShort: data.amsSelect2FormatInputTooShort === undefined
-																	? function(input, min) {
+												formatInputTooShort: data.amsSelect2FormatInputTooShort === undefined ?
+																	function(input, min) {
 																		var n = min - input.length;
 																		return ams.i18n.SELECT2_INPUT_TOOSHORT
 																						.replace(/\{0\}/, n)
-																						.replace(/\{1\}/, n == 1 ? "" : ams.i18n.SELECT2_PLURAL);
+																						.replace(/\{1\}/, n === 1 ? "" : ams.i18n.SELECT2_PLURAL);
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatInputTooShort),
-												formatInputTooLong: data.amsSelect2FormatInputTooLong === undefined
-																	? function(input, max) {
+												formatInputTooLong: data.amsSelect2FormatInputTooLong === undefined ?
+																	function(input, max) {
 																		var n = input.length - max;
 																		return ams.i18n.SELECT2_INPUT_TOOLONG
 																						.replace(/\{0\}/, n)
-																						.replace(/\{1\}/, n == 1 ? "" : ams.i18n.SELECT2_PLURAL);
+																						.replace(/\{1\}/, n === 1 ? "" : ams.i18n.SELECT2_PLURAL);
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatInputTooLong),
-												formatSelectionTooBig: data.amsSelect2FormatSelectionTooBig === undefined
-																	? function(limit) {
+												formatSelectionTooBig: data.amsSelect2FormatSelectionTooBig === undefined ?
+																	function(limit) {
 																		return ams.i18n.SELECT2_SELECTION_TOOBIG
 																						.replace(/\{0\}/, limit)
-																						.replace(/\{1\}/, limit == 1 ? "" : ams.i18n.SELECT2_PLURAL);
+																						.replace(/\{1\}/, limit === 1 ? "" : ams.i18n.SELECT2_PLURAL);
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatSelectionTooBig),
-												formatLoadMore: data.amsSelect2FormatLoadMore === undefined
-																	? function (pageNumber) {
+												formatLoadMore: data.amsSelect2FormatLoadMore === undefined ?
+																	function (pageNumber) {
 																		return ams.i18n.SELECT2_LOADMORE;
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatLoadMore),
-												formatSearching: data.amsSelect2FormatSearching === undefined
-																	? function() {
+												formatSearching: data.amsSelect2FormatSearching === undefined ?
+																	function() {
 																		return ams.i18n.SELECT2_SEARCHING;
 																	}
 																	: ams.getFunctionByName(data.amsSelect2FormatSearching),
@@ -2638,7 +2812,7 @@
 																			   text: values_data[this] || this});
 																});
 																callback(data);
-															}
+															};
 														}
 													}
 													break;
@@ -2647,7 +2821,7 @@
 											}
 
 											if (select.attr('readonly')) {
-												if (select.attr('type') == 'hidden') {
+												if (select.attr('type') === 'hidden') {
 													data_options.query = function () {
 														return [];
 													};
@@ -2730,8 +2904,9 @@
 
 											select.on('change', function() {
 												var validator = $(select.get(0).form).data('validator');
-												if (validator !== undefined)
+												if (validator !== undefined) {
 													$(select).valid();
+												}
 											});
 										});
 								   });
@@ -2905,18 +3080,23 @@
 												errorClass: 'state-error',
 												errorPlacement: function(error, element) {
 													var label = element.parents('label:first');
-													if (label.length)
+													if (label.length) {
 														error.insertAfter(label);
-													else
+													} else {
 														error.insertAfter(element);
+													}
 												}
 											});
 											if (ams.plugins.i18n) {
 												for (var key in ams.plugins.i18n.validate) {
+													if (!ams.plugins.i18n.validate.hasOwnProperty(key)) {
+														continue;
+													}
 													var message = ams.plugins.i18n.validate[key];
-													if ((typeof(message) == 'string') &&
-														(message.indexOf('{0}') > -1))
+													if ((typeof(message) === 'string') &&
+														(message.indexOf('{0}') > -1)) {
 														ams.plugins.i18n.validate[key] = $.validator.format(message);
+													}
 												}
 												$.extend($.validator.messages, ams.plugins.i18n.validate);
 											}
@@ -2926,9 +3106,9 @@
 											var data = form.data();
 											var data_options = {
 												ignore: null,
-												submitHandler: form.attr('data-async') !== undefined
-															   ? data.amsFormSubmitHandler === undefined
-																	? function() {
+												submitHandler: form.attr('data-async') !== undefined ?
+															   data.amsFormSubmitHandler === undefined ?
+																	function() {
 																		// JQuery-form plug-in must be loaded synchronously!!
 																		// Otherwise, hidden input fields created by jquery-validate plug-in
 																		// and matching named buttons will be deleted (on first form submit)
@@ -2940,11 +3120,11 @@
 																	}
 																	: ams.getFunctionByName(data.amsFormSubmitHandler)
 															   : undefined,
-												invalidHandler: form.attr('data-async') !== undefined
-																? data.amsFormInvalidHandler === undefined
-																	? function(event, validator) {
+												invalidHandler: form.attr('data-async') !== undefined ?
+																data.amsFormInvalidHandler === undefined ?
+																	function(event, validator) {
 																		$('.state-error', form).removeClass('state-error');
-																		for (var index in validator.errorList) {
+																		for (var index=0; index < validator.errorList.length; index++) {
 																			var error = validator.errorList[index];
 																			var tab_index = $(error.element).parents('.tab-pane').index() + 1;
 																			if (tab_index > 0) {
@@ -2978,7 +3158,7 @@
 								   ams.baseURL + 'ext/jquery-dataTables-1.9.4' + ams.devext + '.js',
 								   function(first_load) {
 										$(tables).each(function() {
-											ams.ajax.check($.fn.dataTableExt.oPagination['bootstrap_full'],
+											ams.ajax.check($.fn.dataTableExt.oPagination.bootstrap_full,
 														   ams.baseURL + 'myams-dataTables' + ams.devext + '.js');
 											var table = $(this);
 											var data = table.data();
@@ -2998,13 +3178,14 @@
 												"><'dt-row dt-bottom-row'<'row'<'col-sm-6'" +
 												(data.amsDatatableInformation === false ? '': 'i') +
 												"><'col-sm-6 text-right'p>>";
+
 											var index;
 											// Check initial sorting
 											var sorting = data.amsDatatableSorting;
 											if (typeof(sorting) === 'string') {
 												var sortings = sorting.split(';');
 												sorting = [];
-												for (index in sortings) {
+												for (index=0; index < sortings.length; index++) {
 													var col_sorting = sortings[index].split(',');
 													col_sorting[0] = parseInt(col_sorting[0]);
 													sorting.push(col_sorting);
@@ -3013,7 +3194,7 @@
 											// Check columns types
 											var columns = [];
 											var sort_types = $('th', table).listattr('data-ams-datatable-stype');
-											for (index in sort_types) {
+											for (index=0; index < sort_types.length; index++) {
 												var sort_type = sort_types[index];
 												if (sort_type) {
 													var column = columns[index] || {};
@@ -3043,7 +3224,7 @@
 											};
 											var settings = $.extend({}, data_options, data.amsDatatableOptions);
 											if (extensions.length > 0) {
-												for (index in extensions) {
+												for (index=0; index < extensions.length; index++) {
 													switch (extensions[index]) {
 														case 'autofill':
 															ams.ajax.check($.fn.dataTable.AutoFill,
@@ -3110,13 +3291,13 @@
 												var plugin = table.dataTable(settings);
 												ams.executeFunctionByName(data.amsDatatableAfterInitCallback, table, plugin, settings);
 												if (extensions.length > 0) {
-													for (index in extensions) {
+													for (index=0; index < extensions.length; index++) {
 														switch (extensions[index]) {
 															case 'autofill':
 																var af_settings = $.extend({}, data.amsDatatableAutofillOptions, settings.autofill);
 																af_settings = ams.executeFunctionByName(data.amsDatatableAutofillInitCallback, table, af_settings) || af_settings;
-																table.data('ams-autofill', data.amsDatatableAutofillConstructor === undefined
-																							? new $.fn.dataTable.AutoFill(table, af_settings)
+																table.data('ams-autofill', data.amsDatatableAutofillConstructor === undefined ?
+																							new $.fn.dataTable.AutoFill(table, af_settings)
 																							: ams.executeFunctionByName(data.amsDatatableAutofillConstructor, table, plugin, af_settings));
 																break;
 															case 'columnfilter':
@@ -3125,29 +3306,29 @@
 																};
 																var cf_settings = $.extend({}, cf_default, data.amsDatatableColumnfilterOptions, settings.columnfilter);
 																cf_settings = ams.executeFunctionByName(data.amsDatatableColumnfilterInitCallback, table, cf_settings) || cf_settings;
-																table.data('ams-columnfilter', data.amsDatatableColumnfilterConstructor === undefined
-																							? plugin.columnFilter(cf_settings)
+																table.data('ams-columnfilter', data.amsDatatableColumnfilterConstructor === undefined ?
+																							plugin.columnFilter(cf_settings)
 																							: ams.executeFunctionByName(data.amsDatatableColumnfilterConstructor, table, plugin, cf_settings));
 																break;
 															case 'editable':
 																var ed_settings = $.extend({}, data.amsDatatableEditableOptions, settings.editable);
 																ed_settings = ams.executeFunctionByName(data.amsDatatableEditableInitCallback, table, ed_settings) || ed_settings;
-																table.data('ams-editable', data.amsDatatableEditableConstructor === undefined
-																							? table.makeEditable(ed_settings)
+																table.data('ams-editable', data.amsDatatableEditableConstructor === undefined ?
+																							table.makeEditable(ed_settings)
 																							: ams.executeFunctionByName(data.amsDatatableEditableConstructor, table, plugin, ed_settings));
 																break;
 															case 'fixedcolumns':
 																var fc_settings = $.extend({}, data.amsDatatableFixedcolumnsOptions, settings.fixedcolumns);
 																fc_settings = ams.executeFunctionByName(data.amsDatatableFixedcolumnsInitCallback, table, fc_settings) || fc_settings;
-																table.data('ams-fixedcolumns', data.amsDatatableFixedcolumnsConstructor === undefined
-																							? new $.fn.dataTable.FixedColumns(table, fc_settings)
+																table.data('ams-fixedcolumns', data.amsDatatableFixedcolumnsConstructor === undefined ?
+																							new $.fn.dataTable.FixedColumns(table, fc_settings)
 																							: ams.executeFunctionByName(data.amsDatatableFixedcolumnsConstructor, table, plugin, fc_settings));
 																break;
 															case 'fixedheader':
 																var fh_settings = $.extend({}, data.amsDatatableFixedheaderOptions, settings.fixedheader);
 																fh_settings = ams.executeFunctionByName(data.amsDatatableFixedheadeInitCallback, table, fh_settings) || fh_settings;
-																table.data('ams-fixedheader', data.amsDatatableFixedheaderConstructor === undefined
-																							? new $.fn.dataTable.FixedHeader(table, fh_settings)
+																table.data('ams-fixedheader', data.amsDatatableFixedheaderConstructor === undefined ?
+																							new $.fn.dataTable.FixedHeader(table, fh_settings)
 																							: ams.executeFunctionByName(data.amsDatatableFixedheaderConstructor, table, plugin, fh_settings));
 																break;
 															case 'keytable':
@@ -3157,22 +3338,22 @@
 																};
 																var kt_settings = $.extend({}, kt_default, data.amsDatatableKeytableOptions, settings.keytable);
 																kt_settings = ams.executeFunctionByName(data.amsDatatableKeytableInitCallback, table, kt_settings) || kt_settings;
-																table.data('ams-keytable', data.amsDatatableKeytableConstructor === undefined
-																							? new KeyTable(kt_settings)
+																table.data('ams-keytable', data.amsDatatableKeytableConstructor === undefined ?
+																							new KeyTable(kt_settings)
 																							: ams.executeFunctionByName(data.amsDatatableKeytableConstructor, table, plugin, kt_settings));
 																break;
 															case 'rowgrouping':
 																var rg_settings = $.extend({}, data.amsDatatableRowgroupingOptions, settings.rowgrouping);
 																rg_settings = ams.executeFunctionByName(data.amsDatatableRowgroupingInitCallback, table, rg_settings) || rg_settings;
-																table.data('ams-rowgrouping', data.amsDatatableRowgroupingConstructor === undefined
-																							? table.rowGrouping(rg_settings)
+																table.data('ams-rowgrouping', data.amsDatatableRowgroupingConstructor === undefined ?
+																							table.rowGrouping(rg_settings)
 																							: ams.executeFunctionByName(data.amsDatatableRowgroupingConstructor, table, plugin, rg_settings));
 																break;
 															case 'rowreordering':
 																var rr_settings = $.extend({}, data.amsDatatableRowreorderingOptions, settings.rowreordering);
 																rr_settings = ams.executeFunctionByName(data.amsDatatableRowreorderingInitCallback, table, rr_settings) || rr_settings;
-																table.data('ams-rowreordering', data.amsDatatableRowreorderingConstructor === undefined
-																							? table.rowReordering(rr_settings)
+																table.data('ams-rowreordering', data.amsDatatableRowreorderingConstructor === undefined ?
+																							table.rowReordering(rr_settings)
 																							: ams.executeFunctionByName(data.amsDatatableRowreorderingConstructor, table, plugin, rr_settings));
 																break;
 															default:
@@ -3182,7 +3363,7 @@
 												}
 												var finalizers = (data.amsDatatableFinalizeCallback || '').split(/\s+/);
 												if (finalizers.length > 0) {
-													for (index in finalizers) {
+													for (index=0; index < finalizers.length; index++) {
 														ams.executeFunctionByName(finalizers[index], table, plugin, settings);
 													}
 												}
@@ -3222,11 +3403,12 @@
 														var rows = [];
 														$(dnd_table.rows).each(function() {
 															var row_id = $(this).data('ams-element-name');
-															if (row_id)
+															if (row_id) {
 																rows.push(row_id);
+															}
 														});
 														var local_target = ams.getFunctionByName(target);
-														if (typeof(local_target) == 'function') {
+														if (typeof(local_target) === 'function') {
 															local_target.call(table, dnd_table, rows);
 														} else {
 															ams.ajax.post(target, {names: JSON.stringify(rows)});
@@ -3253,8 +3435,9 @@
 					ams.ajax.check($.fn.imgAreaSelect,
 								   ams.baseURL + 'ext/jquery-imgareaselect-0.9.10' + ams.devext + '.js',
 								   function(first_load) {
-									   if (first_load)
+									   if (first_load) {
 										   ams.getCSS(ams.baseURL + '../css/ext/jquery-imgareaselect' + ams.devext + '.css');
+									   }
 									   images.each(function() {
 										   var image = $(this);
 										   var data = image.data();
@@ -3289,7 +3472,7 @@
 											   plugin.update();
 										   }, 250);
 									   });
-								   })
+								   });
 				}
 			},
 
@@ -3302,14 +3485,15 @@
 					ams.ajax.check($.fn.fancybox,
 								   ams.baseURL + 'ext/jquery-fancybox-2.1.5' + ams.devext + '.js',
 								   function(first_load) {
-										if (first_load)
+										if (first_load) {
 											ams.getCSS(ams.baseURL + '../css/ext/jquery-fancybox-2.1.5' + ams.devext + '.css');
+										}
 										fancyboxes.each(function() {
 											var fancybox = $(this);
 											var data = fancybox.data();
 											var helpers = (data.amsFancyboxHelpers || '').split(/\s+/);
 											if (helpers.length > 0) {
-												for (var index in helpers) {
+												for (var index=0; index < helpers.length; index++) {
 													var helper = helpers[index];
 													switch (helper) {
 														case 'buttons':
@@ -3377,9 +3561,10 @@
 										ams.ajax.check($.fn.mCustomScrollbar,
 													   ams.baseURL + 'ext/jquery-mCustomScrollbar' + ams.devext + '.js',
 													   function(first_load) {
-															if (first_load)
+															if (first_load) {
 																ams.getCSS(ams.baseURL + '../css/ext/jquery-mCustomScrollbar.css',
-																		  'jquery-mCustomScrollbar');
+																		   'jquery-mCustomScrollbar');
+															}
 															scrollbars.each(function() {
 																var scrollbar = $(this);
 																var data = scrollbar.data();
@@ -3427,13 +3612,13 @@
 									  function() {
 										ams.executeFunctionByName(data.amsCallback, self, data.amsCallbackOptions);
 									  });
-					} else if (window.console) {
-						console.warn("Undefined callback: " + data.amsCallback);
+					} else if (globals.console) {
+						globals.console.warn("Undefined callback: " + data.amsCallback);
 					}
 				} else {
 					callback.call(self, data.amsCallbackOptions);
 				}
-			})
+			});
 		},
 
 		/**
@@ -3537,8 +3722,12 @@
 			$('[data-ams-events-handlers]', element).each(function() {
 				var element = $(this);
 				var handlers = element.data('ams-events-handlers');
-				for (var event in handlers) {
-					element.on(event, ams.getFunctionByName(handlers[event]));
+				if (handlers) {
+					for (var event in handlers) {
+						if (handlers.hasOwnProperty(event)) {
+							element.on(event, ams.getFunctionByName(handlers[event]));
+						}
+					}
 				}
 			});
 		}
@@ -3577,14 +3766,14 @@
 					content: '<i class="text-danger fa fa-2x fa-bell shake animated"></i>&nbsp; ' + ams.i18n.DELETE_WARNING,
 					buttons: ams.i18n.BTN_OK_CANCEL
 				}, function(button) {
-					if (button == ams.i18n.BTN_OK) {
+					if (button === ams.i18n.BTN_OK) {
 						var table = link.parents('table');
 						var location = table.data('ams-location') || '';
 						var tr = link.parents('tr');
 						var delete_target = tr.data('ams-delete-target') || table.data('ams-delete-target') || 'delete-element.json';
 						var object_name = tr.data('ams-element-name');
 						MyAMS.ajax.post(location + '/' + delete_target, {'object_name': object_name}, function(result, status) {
-							if (result.status == 'success') {
+							if (result.status === 'success') {
 								if (table.hasClass('datatable')) {
 									table.dataTable().fnDeleteRow(tr[0]);
 								} else {
@@ -3594,7 +3783,7 @@
 						});
 					}
 				});
-			}
+			};
 		}
 	};
 
@@ -3624,10 +3813,11 @@
 		 * Check width for mobile devices
 		 */
 		_checkMobileWidth: function() {
-			if ($(window).width() < 979)
-				ams.root.addClass('mobile-view-activated')
-			else if (ams.root.hasClass('mobile-view-activated'))
+			if ($(window).width() < 979) {
+				ams.root.addClass('mobile-view-activated');
+			} else if (ams.root.hasClass('mobile-view-activated')) {
 				ams.root.removeClass('mobile-view-activated');
+			}
 		},
 
 		/**
@@ -3639,7 +3829,7 @@
 			}, 200, 'easeOutCirc');
 			ams.root.addClass('shortcut-on');
 		},
-		
+
 		_hideShortcutButtons: function() {
 			ams.shortcuts.animate({
 				height: 'hide'
@@ -3651,13 +3841,14 @@
 		 * Check notification badge
 		 */
 		checkNotification: function() {
-			$this = $('#activity > .badge');
-			if (parseInt($this.text()) > 0)
-				$this.removeClass("hidden")
+			var badge = $('#activity > .badge');
+			if (parseInt(badge.text()) > 0) {
+				badge.removeClass("hidden")
 					 .addClass("bg-color-red bounceIn animated");
-			else
-				$this.addClass("hidden")
+			} else {
+				badge.addClass("hidden")
 					 .removeClass("bg-color-red bounceIn animated");
+			}
 		},
 
 		/**
@@ -3666,29 +3857,31 @@
 		_initDesktopWidgets: function(element) {
 			if (ams.enable_widgets) {
 				var widgets = $('.ams-widget', element);
-				if (widgets.length > 0)
+				if (widgets.length > 0) {
 					ams.ajax.check($.fn.MyAMSWidget,
 								   ams.baseURL + 'myams-widgets' + ams.devext + '.js',
-								   function() {
-										widgets.each(function() {
-											var widget = $(this);
-											var data = widget.data();
-											var data_options = {
-												deleteSettingsKey: '#deletesettingskey-options',
-												deletePositionKey: '#deletepositionkey-options'
-											};
-											var settings = $.extend({}, data_options, data.amsWidgetOptions);
-											settings = ams.executeFunctionByName(data.amsWidgetInitcallback, widget, settings) || settings;
-											widget.MyAMSWidget(settings);
-										});
-										MyAMSWidget.initWidgetsGrid($('.ams-widget-grid', element));
-									});
+								   function () {
+									   widgets.each(function () {
+										   var widget = $(this);
+										   var data = widget.data();
+										   var data_options = {
+											   deleteSettingsKey: '#deletesettingskey-options',
+											   deletePositionKey: '#deletepositionkey-options'
+										   };
+										   var settings = $.extend({}, data_options, data.amsWidgetOptions);
+										   settings = ams.executeFunctionByName(data.amsWidgetInitcallback, widget, settings) || settings;
+										   widget.MyAMSWidget(settings);
+									   });
+									   globals.MyAMSWidget.initWidgetsGrid($('.ams-widget-grid', element));
+								   });
+				}
 			}
 		},
 
 		_initMobileWidgets: function(element) {
-			if (ams.enable_mobile && ams.enable_widgets)
+			if (ams.enable_mobile && ams.enable_widgets) {
 				ams.skin._initDesktopWidgets(element);
+			}
 		},
 
 		/**
@@ -3703,23 +3896,25 @@
 		 */
 		alert: function(parent, status, header, message, subtitle, margin) {
 			$('.alert', parent).remove();
-			if (status == 'error')
+			if (status === 'error') {
 				status = 'danger';
+			}
 			var content = '<div class="' + (margin ? 'margin-10' : '') + ' alert alert-block alert-' + status + ' fade in">' +
-							'<a class="close" data-dismiss="alert"><i class="fa fa-check"></i></a>' +
-							'<h4 class="alert-heading">' +
-								'<i class="fa fa-fw fa-warning"></i> ' + header +
-							'</h4>' +
-							(subtitle ? ('<p>' + subtitle + '</p>') : '');
-			if (typeof(message) == 'string')
+				'<a class="close" data-dismiss="alert"><i class="fa fa-check"></i></a>' +
+				'<h4 class="alert-heading">' +
+				'<i class="fa fa-fw fa-warning"></i> ' + header +
+				'</h4>' +
+				(subtitle ? ('<p>' + subtitle + '</p>') : '');
+			if (typeof(message) === 'string') {
 				content += '<ul><li>' + message + '</li></ul>';
-			else if (message) {
+			} else if (message) {
 				content += '<ul>';
 				for (var index in message) {
-					if (!$.isNumeric(index))  // IE check
+					if (!$.isNumeric(index)) {  // IE check
 						continue;
+					}
 					content += '<li>' + message[index] + '</li>';
-				};
+				}
 				content += '</ul>';
 			}
 			content += '</div>';
@@ -3728,7 +3923,7 @@
 				ams.ajax.check($.scrollTo,
 							   ams.baseURL + 'ext/jquery-scrollTo.min.js',
 							   function() {
-									$.scrollTo(parent, {offset: {top: -50}});
+								   $.scrollTo(parent, {offset: {top: -50}});
 							   });
 			}
 		},
@@ -3748,7 +3943,7 @@
 		 * Medium notification message box, displayed on page's bottom right
 		 */
 		messageBox: function(status, options, callback) {
-			if (typeof(status) == 'object') {
+			if (typeof(status) === 'object') {
 				callback = options;
 				options = status || {};
 				status = 'info';
@@ -3756,22 +3951,22 @@
 			ams.ajax.check(ams.notify,
 						   ams.baseURL + 'myams-notify' + ams.devext + '.js',
 						   function() {
-								switch (status) {
-									case 'error':
-									case 'danger':
-										options.color = '#C46A69';
-										break;
-									case 'warning':
-										options.color = '#C79121';
-										break;
-									case 'success':
-										options.color = '#739E73';
-										break;
-									default:
-										options.color = options.color || '#3276B1';
-								}
-								options.sound = false;
-								ams.notify.bigBox(options, callback);
+							   switch (status) {
+								   case 'error':
+								   case 'danger':
+									   options.color = '#C46A69';
+									   break;
+								   case 'warning':
+									   options.color = '#C79121';
+									   break;
+								   case 'success':
+									   options.color = '#739E73';
+									   break;
+								   default:
+									   options.color = options.color || '#3276B1';
+							   }
+							   options.sound = false;
+							   ams.notify.bigBox(options, callback);
 						   });
 		},
 
@@ -3779,30 +3974,30 @@
 		 * Small notification message box, displayed on page's top right
 		 */
 		smallBox: function(status, options, callback) {
-			if (typeof(status) == 'object') {
+			if (typeof(status) === 'object') {
 				callback = options;
 				options = status || {};
 				status = 'info';
 			}
 			ams.ajax.check(ams.notify,
 						   ams.baseURL + 'myams-notify' + ams.devext + '.js',
-						   function() {
-								switch (status) {
-									case 'error':
-									case 'danger':
-										options.color = '#C46A69';
-										break;
-									case 'warning':
-										options.color = '#C79121';
-										break;
-									case 'success':
-										options.color = '#739E73';
-										break;
-									default:
-										options.color = options.color || '#3276B1';
-								}
-								options.sound = false;
-								ams.notify.smallBox(options, callback);
+						   function () {
+							   switch (status) {
+								   case 'error':
+								   case 'danger':
+									   options.color = '#C46A69';
+									   break;
+								   case 'warning':
+									   options.color = '#C79121';
+									   break;
+								   case 'success':
+									   options.color = '#739E73';
+									   break;
+								   default:
+									   options.color = options.color || '#3276B1';
+							   }
+							   options.sound = false;
+							   ams.notify.smallBox(options, callback);
 						   });
 		},
 
@@ -3813,7 +4008,7 @@
 			var crumb = $('#ribbon OL.breadcrumb');
 			crumb.empty()
 				 .append($('<li></li>').append($('<a></a>').text(ams.i18n.HOME)
-				 										   .attr('href', $('nav a[href!="#"]:first').attr('href'))));
+														   .attr('href', $('nav a[href!="#"]:first').attr('href'))));
 			$('nav LI.active >A').each(function() {
 				var menu = $(this);
 				var body = $.trim(menu.clone()
@@ -3821,10 +4016,9 @@
 									  .remove()
 									  .end()
 									  .text());
-				var item = $("<li></li>").append(menu.attr('href').replace(/^#/, '')
-													? $("<a></a>").html(body)
-																  .attr('href', menu.attr('href'))
-													: body);
+				var item = $("<li></li>").append(menu.attr('href').replace(/^#/, '') ?
+												 $("<a></a>").html(body).attr('href', menu.attr('href'))
+												 : body);
 				crumb.append(item);
 			});
 		},
@@ -3839,22 +4033,25 @@
 				menu.addClass('open')
 					.addClass('active');
 				menu.parents('li').addClass('open active')
-								  .children('ul').addClass('active')
-												 .show();
+					.children('ul').addClass('active')
+					.show();
 				menu.parents('li:first').removeClass('open');
 				menu.parents('ul').addClass(menu.attr('href').replace(/^#/, '') ? 'active' : '')
-								  .show();
+					.show();
 			}
 
+			var menu;
 			var hash = location.hash;
 			var url = hash.replace(/^#/, '');
 			if (url) {
 				var container = $('#content');
-				if (!container.exists())
+				if (!container.exists()) {
 					container = $('body');
-				var menu = $('nav A[href="' + hash + '"]');
-				if (menu.exists())
+				}
+				menu = $('nav A[href="' + hash + '"]');
+				if (menu.exists()) {
 					updateActiveMenus(menu);
+				}
 				ams.skin.loadURL(url, container);
 				document.title = $('[data-ams-page-title]:first', container).data('ams-page-title') ||
 								 menu.attr('title') ||
@@ -3868,10 +4065,11 @@
 				}
 				if (menu.exists()) {
 					updateActiveMenus(menu);
-					if (active_url)
+					if (active_url) {
 						ams.skin._drawBreadCrumb();
-					else
+					} else {
 						window.location.hash = menu.attr('href');
+					}
 				}
 			}
 		},
@@ -3888,8 +4086,9 @@
 		 */
 		registerCleanCallback: function(callback) {
 			var callbacks = ams.skin._clean_callbacks;
-			if (callbacks.indexOf(callback) < 0)
+			if (callbacks.indexOf(callback) < 0) {
 				callbacks.push(callback);
+			}
 		},
 
 		/**
@@ -3897,7 +4096,7 @@
 		 */
 		cleanContainer: function(container) {
 			var callbacks = ams.skin._clean_callbacks;
-			for (var index in callbacks) {
+			for (var index=0; index < callbacks.length; index++) {
 				callbacks[index].call(container);
 			}
 		},
@@ -3909,7 +4108,7 @@
 			if (url.startsWith('#')) {
 				url = url.substr(1);
 			}
-			if (typeof(options) == 'function') {
+			if (typeof(options) === 'function') {
 				callback = options;
 				options = {};
 			}
@@ -3922,7 +4121,7 @@
 				beforeSend: function() {
 					ams.skin.cleanContainer(container);
 					container.html('<h1 class="loading"><i class="fa fa-cog fa-spin"></i> Loading... </h1>');
-					if (container[0] == $('#content')[0]) {
+					if (container[0] === $('#content')[0]) {
 						ams.skin._drawBreadCrumb();
 						document.title = $('.breadcrumb LI:last-child').text();
 						$('html, body').animate({scrollTop: 0}, 'fast');
@@ -3931,9 +4130,9 @@
 					}
 				},
 				success: function(data, status, request) {
-					if (callback)
+					if (callback) {
 						ams.executeFunctionByName(callback, this, data, status, request, options);
-					else {
+					} else {
 						var request_data = ams.ajax.getResponse(request);
 						var data_type = request_data.content_type;
 						var result = request_data.data;
@@ -3947,8 +4146,11 @@
 							case 'xml':
 								break;
 							case 'html':
+								/* falls through */
 							case 'text':
+								/* falls through */
 							default:
+								// Show and init container
 								container.parents('.hidden').removeClass('hidden');
 								$('.alert', container.parents('.alerts-container')).remove();
 								container.css({opacity: '0.0'})
@@ -3957,6 +4159,14 @@
 										 .delay(50)
 										 .animate({opacity: '1.0'}, 300);
 								ams.initContent(container);
+								// Set focus to first input
+								var focused = $('[data-ams-focus-target]', container).first();
+								if (!focused.exists()) {
+									focused = $('input, select', container).first();
+								}
+								if (focused.exists()) {
+									focused.focus();
+								}
 						}
 						if (options && options.afterLoadCallback) {
 							ams.executeFunctionByName(options.afterLoadCallback, this);
@@ -4006,6 +4216,32 @@
 
 
 	/**
+	 * Statistics management
+	 */
+	MyAMS.stats = {
+
+		/**
+		 * Send event to Google Analytics platform
+		 *
+		 * @param category
+		 * @param action
+		 * @param label
+		 */
+		logEvent: function(category, action, label) {
+			if (typeof(globals._gaq) === 'undefined') {
+				return;
+			}
+			if (typeof(category) === 'object') {
+				action = category.action;
+				label = category.label;
+				category = category.category;
+			}
+			globals._gaq.push(['_trackEvent', category, action, label]);
+		}
+	};
+
+
+	/**
 	 * Main page initialization
 	 * This code is called only once to register global events and callbacks
 	 */
@@ -4026,7 +4262,7 @@
 			progressUpload: ams.ajax.progress,
 			xhr: function() {
 				var request = jquery_xhr();
-				if (request && (typeof(request.addEventListener) == "function")) {
+				if (request && (typeof(request.addEventListener) === "function")) {
 					var that = this;
 					request.addEventListener("progress", function(evt) {
 						that.progress(evt);
@@ -4076,8 +4312,8 @@
 		});
 
 		$(document).mouseup(function(e) {
-			if (!ams.shortcuts.is(e.target)
-				&& ams.shortcuts.has(e.target).length === 0) {
+			if (!ams.shortcuts.is(e.target) &&
+				ams.shortcuts.has(e.target).length === 0) {
 				ams.skin._hideShortcutButtons();
 			}
 		});
@@ -4101,7 +4337,7 @@
 				activity.addClass('active');
 			} else {
 				dropdown.fadeOut(150);
-				activity.removeClass('active')
+				activity.removeClass('active');
 			}
 			e.preventDefault();
 		});
@@ -4118,7 +4354,7 @@
 
 		$('input[name="activity"]').change(function() {
 			var url = $(this).data('ams-url');
-			container = $('.ajax-notifications');
+			var container = $('.ajax-notifications');
 			ams.skin.loadURL(url, container);
 		});
 
@@ -4135,9 +4371,9 @@
 				content : ams.i18n.LOGOUT_COMMENT,
 				buttons : '['+ams.i18n.BTN_NO+']['+ams.i18n.BTN_YES+']'
 			}, function(ButtonPressed) {
-				if (ButtonPressed == ams.i18n.BTN_YES) {
+				if (ButtonPressed === ams.i18n.BTN_YES) {
 					ams.root.addClass('animated fadeOutUp');
-					setTimeout(ams.skin.logout, 1000)
+					setTimeout(ams.skin.logout, 1000);
 				}
 			});
 		});
@@ -4162,7 +4398,7 @@
 				content: ams.i18n.CLEAR_STORAGE_CONTENT,
 				buttons: '['+ams.i18n.BTN_CANCEL+']['+ams.i18n.BTN_OK+']'
 			}, function(buttonPressed) {
-				if (buttonPressed == ams.i18n.BTN_OK && localStorage) {
+				if (buttonPressed === ams.i18n.BTN_OK && localStorage) {
 					localStorage.clear();
 					location.reload();
 				}
@@ -4175,8 +4411,9 @@
 			var element = $(this);
 			if (!element.is(e.target) &&
 				element.has(e.target).length === 0 &&
-				$('.popover').has(e.target).length === 0)
+				$('.popover').has(e.target).length === 0) {
 				element.popover('hide');
+			}
 		});
 
 		// Resize events
@@ -4200,15 +4437,16 @@
 			$(document).on('click', 'a[href!="#"]:not([data-toggle]), [data-ams-url]:not([data-toggle])', function(e) {
 				var link = $(e.currentTarget);
 				var href = link.attr('href') || link.data('ams-url');
-				if (!href || href.startsWith('javascript:') || link.attr('target') || (link.data('ams-context-menu') === true))
+				if (!href || href.startsWith('javascript') || link.attr('target') || (link.data('ams-context-menu') === true)) {
 					return;
+				}
 				e.preventDefault();
 				e.stopPropagation();
 				var href_getter = ams.getFunctionByName(href);
-				if (typeof(href_getter) == 'function') {
+				if (typeof(href_getter) === 'function') {
 					href = href_getter.call(link);
 				}
-				if (typeof(href) == 'function') {
+				if (typeof(href) === 'function') {
 					// Javascript function call
 					href.call(link);
 				} else {
@@ -4223,17 +4461,19 @@
 					} else {
 						ams.form.confirmChangedForm(function () {
 							if (href.startsWith('#')) {
-								if (href != location.hash) {
+								if (href !== location.hash) {
 									if (ams.root.hasClass('mobile-view-activated')) {
 										ams.root.removeClass('hidden-menu');
 										window.setTimeout(function () {
 											window.location.hash = href;
 										}, 50);
-									} else
+									} else {
 										window.location.hash = href;
+									}
 								}
-							} else
+							} else {
 								window.location = href;
+							}
 						});
 					}
 				}
@@ -4257,14 +4497,17 @@
 		$(document).off('click.modal')
 				   .on('click', '[data-toggle="modal"]', function(e) {
 			var source = $(this);
-			if (source.data('ams-context-menu') === true)
+			if (source.data('ams-context-menu') === true) {
 				return;
-			if (source.data('ams-stop-propagation') === true)
+			}
+			if (source.data('ams-stop-propagation') === true) {
 				e.stopPropagation();
+			}
 			e.preventDefault();
 			ams.dialog.open(source);
-			if (source.parents('#shortcut').exists())
+			if (source.parents('#shortcut').exists()) {
 				setTimeout(ams.skin._hideShortcutButtons, 300);
+			}
 		});
 
 		// Initialize form buttons
@@ -4278,13 +4521,16 @@
 			var source = $(this);
 			var data = source.data();
 			if (data.amsClickHandler) {
-				if ((data.amsStopPropagation === true) || (data.amsClickStopPropagation === true))
+				if ((data.amsStopPropagation === true) || (data.amsClickStopPropagation === true)) {
 					e.stopPropagation();
-				if (data.amsClickKeepDefault !== true)
+				}
+				if (data.amsClickKeepDefault !== true) {
 					e.preventDefault();
+				}
 				var callback = ams.getFunctionByName(data.amsClickHandler);
-				if (callback !== undefined)
+				if (callback !== undefined) {
 					callback.call(source, data.amsClickHandlerOptions);
+				}
 			}
 		});
 
@@ -4293,11 +4539,13 @@
 			var source = $(this);
 			var data = source.data();
 			if (data.amsChangeHandler) {
-				if (data.amsChangeKeepDefault !== true)
+				if (data.amsChangeKeepDefault !== true) {
 					e.preventDefault();
+				}
 				var callback = ams.getFunctionByName(data.amsChangeHandler);
-				if (callback !== undefined)
+				if (callback !== undefined) {
 					callback.call(source, data.amsChangeHandlerOptions);
+				}
 			}
 		});
 
@@ -4307,6 +4555,14 @@
 			setTimeout(function() {
 				form.find('.select2').trigger('change');
 			}, 10);
+			// Set focus to first input
+			var focused = $('[data-ams-focus-target]', form).first();
+			if (!focused.exists()) {
+				focused = $('input, select', form).first();
+			}
+			if (focused.exists()) {
+				focused.focus();
+			}
 		});
 
 		// Initialize custom reset handlers
@@ -4314,11 +4570,13 @@
 			var form = $(this);
 			var data = form.data();
 			if (data.amsResetHandler) {
-				if (data.amsResetKeepDefault !== true)
+				if (data.amsResetKeepDefault !== true) {
 					e.preventDefault();
+				}
 				var callback = ams.getFunctionByName(data.amsResetHandler);
-				if (callback !== undefined)
+				if (callback !== undefined) {
 					callback.call(form, data.amsResetHandlerOptions);
+				}
 			}
 		});
 
@@ -4352,13 +4610,15 @@
 			var link = $(e.target);
 			var data = link.data();
 			if (data.amsUrl) {
-				if (data.amsTabLoaded)
+				if (data.amsTabLoaded) {
 					return;
+				}
 				try {
 					link.append('<i class="fa fa-spin fa-cog margin-left-5"></i>');
 					ams.skin.loadURL(data.amsUrl, link.attr('href'));
-					if (data.amsTabLoadOnce)
+					if (data.amsTabLoadOnce) {
 						link.data('ams-tab-loaded', true);
+					}
 				}
 				finally {
 					$('i', link).remove();
@@ -4368,8 +4628,9 @@
 
 		// Init page content
 		ams.initContent(document);
-		if (ams.ajax_nav && $('nav').exists())
+		if (ams.ajax_nav && $('nav').exists()) {
 			ams.skin.checkURL();
+		}
 
 		// Add unload event listener to check for modified forms
 		$(window).on('beforeunload', ams.form.checkBeforeUnload);
@@ -4403,10 +4664,11 @@
 		ams.form.init(element);
 
 		// Initialize widgets
-		if (ams.device === 'desktop')
+		if (ams.device === 'desktop') {
 			ams.skin._initDesktopWidgets(element);
-		else
+		} else {
 			ams.skin._initMobileWidgets(element);
+		}
 		ams.skin._setPageHeight();
 
 	};
@@ -4485,4 +4747,4 @@
 		}
 	});
 
-})(jQuery);
+})(jQuery, this);