src/pyams_skin/resources/js/myams.js
changeset 69 a361355b55c7
parent 64 3fe1d315747c
child 72 13f5fdd06484
--- a/src/pyams_skin/resources/js/myams.js	Wed May 20 15:01:45 2015 +0200
+++ b/src/pyams_skin/resources/js/myams.js	Wed Jun 17 10:00:10 2015 +0200
@@ -400,9 +400,9 @@
 	 * Copyright Andrew Davy: https://forrst.com/posts/Get_the_URL_of_the_current_javascript_file-Dst
 	 */
 	MyAMS.baseURL = (function () {
-		var script = $('script[src$="/myams.js"], script[src$="/myams.min.js"]');
+		var script = $('script[src*="/myams.js"], script[src*="/myams.min.js"]');
 		var src = script.attr("src");
-		ams.devmode = !src.endsWith('.min.js');
+		ams.devmode = src.indexOf('.min.js') < 0;
 		ams.devext = ams.devmode ? '' : '.min';
 		return src.substring(0, src.lastIndexOf('/') + 1);
 	})();
@@ -980,7 +980,7 @@
 		 */
 		query: function(query, method, options, callback) {
 			ams.ajax.check($.jsonRpc,
-						   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'ext/jquery-jsonrpc' + ams.devext + '.js',
 						   function() {
 								var result;
 								if (typeof(options) == 'function') {
@@ -1029,7 +1029,7 @@
 		 */
 		post: function(method, data, options, callback) {
 			ams.ajax.check($.jsonRPC,
-						   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'ext/jquery-jsonrpc' + ams.devext + '.js',
 						   function() {
 								var result;
 								if (typeof(options) == 'function') {
@@ -1092,7 +1092,7 @@
 		 */
 		post: function(url, method, data, options, callback) {
 			ams.ajax.check($.xmlrpc,
-						   ams.baseURL + 'ext/jquery-xmlrpc' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'ext/jquery-xmlrpc' + ams.devext + '.js',
 						   function() {
 								var result;
 								if (typeof(options) == 'function') {
@@ -1227,10 +1227,10 @@
 			$('.state-error', form).removeClassPrefix('state-');
 			// Check submit button
 			var button = $(form.data('ams-submit-button'));
-			if (button)
+			if (button && !button.data('ams-form-hide-loading'))
 				button.button('loading');
 			ams.ajax.check($.fn.ajaxSubmit,
-						   ams.baseURL + 'ext/jquery-form-3.49' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'ext/jquery-form-3.49' + ams.devext + '.js',
 						   function() {
 
 								function _submitAjaxForm(form, options) {
@@ -1409,7 +1409,7 @@
 									// and matching named buttons will be deleted (on first form submit)
 									// before JQuery-form plug-in can get them when submitting the form...
 									ams.ajax.check($.progressBar,
-												   ams.baseURL + 'ext/jquery-progressbar' + (ams.devmode ? '.js' : '.min.js'));
+												   ams.baseURL + 'ext/jquery-progressbar' + ams.devext + '.js');
 									var settings = $.extend({}, {
 										uuid: $.progressBar.submit(form)
 									});
@@ -1624,13 +1624,18 @@
 				ams.skin.alert(form, 'error', header, errors);
 			} else {
 				$('.state-error', form).removeClass('state-error');
-				header = errors.widgets && (errors.widgets.length > 1) ? ams.i18n.ERRORS_OCCURED : ams.i18n.ERROR_OCCURED;
+				header = errors.error_header ||
+						 (errors.widgets && (errors.widgets.length > 1) ? ams.i18n.ERRORS_OCCURED : ams.i18n.ERROR_OCCURED);
 				var message = new Array();
 				var index;
 				for (index in errors.messages) {
 					if (!$.isNumeric(index))
 						continue;
-					message.push(errors.messages[index].message || errors.messages[index]);
+					if (errors.messages[index].header) {
+						message.push('<strong>' + errors.messages[index].header + '</strong><br />' + errors.messages[index].message);
+					} else {
+						message.push(errors.messages[index].message || errors.messages[index]);
+					}
 				}
 				for (index in errors.widgets) {
 					if (!$.isNumeric(index))
@@ -1655,7 +1660,7 @@
 						$('li.state-error:first a', form).click();
 					}
 				}
-				ams.skin.alert(form, 'error', header, message, errors.error_message);
+				ams.skin.alert(form, errors.error_level || 'error', header, message, errors.error_message);
 			}
 		}
 	};
@@ -1685,10 +1690,10 @@
 		 */
 		open: function(source, options) {
 			ams.ajax.check($.fn.modalmanager,
-						   ams.baseURL + 'ext/bootstrap-modalmanager' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'ext/bootstrap-modalmanager' + ams.devext + '.js',
 						   function() {
 								ams.ajax.check($.fn.modal.defaults,
-											   ams.baseURL + 'ext/bootstrap-modal' + (ams.devmode ? '.js' : '.min.js'),
+											   ams.baseURL + 'ext/bootstrap-modal' + ams.devext + '.js',
 								function(first_load) {
 									if (first_load) {
 										$(document).off('click.modal');
@@ -1832,6 +1837,9 @@
 		 */
 		hidden: function(e) {
 			var modal = e.target;
+			// Call registered cleaning callbacks
+			ams.skin.cleanContainer(modal);
+			// Call registered hide callbacks
 			var callbacks = ams.dialog._hide_callbacks;
 			for (var index in callbacks) {
 				callbacks[index].call(modal);
@@ -2013,16 +2021,7 @@
 				}
 			});
 
-			// Run already enabled plug-ins
-			for (var index in ams.plugins.enabled) {
-				if (disabled.indexOf(index) >= 0)
-					continue;
-				var plugin = ams.plugins.enabled[index];
-				if (typeof(plugin) == 'function')
-					plugin(element);
-			}
-
-			// Load, run and register new plug-ins
+			// Load and register new plug-ins
 			var name;
 			$('[data-ams-plugins]', element).each(function() {
 				var source = $(this);
@@ -2050,8 +2049,6 @@
 							var callback = plugin.callback;
 							if (callback) {
 								var called = ams.getFunctionByName(callback);
-								if (typeof(called) == 'function')
-									called.apply(source);
 								if (plugin.register !== false)
 									ams.plugins.enabled[name] = called;
 							} else {
@@ -2068,6 +2065,15 @@
 					}
 				}
 			});
+
+			// Run all enabled plug-ins
+			for (var index in ams.plugins.enabled) {
+				if (disabled.indexOf(index) >= 0)
+					continue;
+				var plugin = ams.plugins.enabled[index];
+				if (typeof(plugin) == 'function')
+					plugin(element);
+			}
 		},
 
 		/**
@@ -2157,9 +2163,9 @@
 				var hints = $('.hint:not(:parents(.nohints))', element);
 				if (hints.length > 0)
 					ams.ajax.check($.fn.tipsy,
-								   ams.baseURL + 'ext/jquery-tipsy' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-tipsy' + ams.devext + '.js',
 								   function() {
-										ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + (ams.devmode ? '.css' : '.min.css'),
+										ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + ams.devext + '.css',
 												  'jquery-tipsy');
 										hints.each(function() {
 											var hint = $(this);
@@ -2172,7 +2178,7 @@
 														   hint.attr(data.amsHintTitleAttr || 'title') ||
 														   (data.amsHintHtml ? hint.html() : hint.text());
 												},
-												opacity: data.amsHintOpacity,
+												opacity: data.amsHintOpacity || 0.95,
 												gravity: data.amsHintGravity || 'sw',
 												offset: data.amsHintOffset || 0
 											};
@@ -2185,6 +2191,27 @@
 			},
 
 			/**
+			 * Context menu plug-in
+			 */
+			contextMenu: function(element) {
+				var menus = $('.context-menu', element);
+				if (menus.length > 0) {
+					menus.each(function() {
+						var menu = $(this);
+						var data = menu.data();
+						var data_options = {
+							menuSelector: data.amsContextmenuSelector,
+							menuSelected: ams.helpers.contextMenuHandler
+						};
+						var settings = $.extend({}, data_options, data.amsContextmenuOptions);
+						settings = ams.executeFunctionByName(data.amsContextmenuInitCallback, menu, settings) || settings;
+						var plugin = menu.contextMenu(settings);
+						ams.executeFunctionByName(data.amsContextmenuAfterInitCallback, menu, plugin, settings);
+					});
+				}
+			},
+
+			/**
 			 * Fieldset legend switcher
 			 */
 			switcher: function(element) {
@@ -2269,10 +2296,13 @@
 							input.on('change', function(e) {
 								e.preventDefault();
 								var veto = {};
+								var isChecked = $(this).is(':checked');
 								legend.trigger('ams.checker.before-switch', [legend, veto]);
-								if (veto.veto)
+								if (veto.veto) {
+									// reset checked status because event is fired after change...
+									$(this).prop('checked', !isChecked);
 									return;
-								var isChecked = $(this).is(':checked');
+								}
 								ams.executeFunctionByName(data.amsCheckerChangeHandler, legend, isChecked);
 								if (!data.amsCheckerCancelDefault) {
 									var hidden = input.data('ams-checker-hidden-input');
@@ -2287,7 +2317,7 @@
 										legend.trigger('ams.checker.opened', [legend]);
 									} else {
 										if (data.amsCheckerMode == 'disable')
-											fieldset.attr('disabled', 'disabled');
+											fieldset.prop('disabled', 'disabled');
 										else
 											fieldset.addClass('switched');
 										if (hidden)
@@ -2419,23 +2449,24 @@
 			},
 
 			/**
-			 * Context menu plug-in
+			 * JQuery typeahead plug-in
 			 */
-			contextMenu: function(element) {
-				var menus = $('.context-menu', element);
-				if (menus.length > 0) {
-					menus.each(function() {
-						var menu = $(this);
-						var data = menu.data();
-						var data_options = {
-							menuSelector: data.amsContextmenuSelector,
-							menuSelected: ams.helpers.contextMenuHandler
-						};
-						var settings = $.extend({}, data_options, data.amsContextmenuOptions);
-						settings = ams.executeFunctionByName(data.amsContextmenuInitCallback, menu, settings) || settings;
-						var plugin = menu.contextMenu(settings);
-						ams.executeFunctionByName(data.amsContextmenuAfterInitCallback, menu, plugin, settings);
-					});
+			typeahead: function(element) {
+				var typeaheads = $('.typeahead', element);
+				if (typeaheads.length > 0) {
+					ams.ajax.check($.fn.typeahead,
+								   ams.baseURL + 'ext/jquery-typeahead' + ams.devext + '.js',
+								   function() {
+										typeaheads.each(function() {
+											var input = $(this);
+											var data = input.data();
+											var data_options = {};
+											var settings = $.extend({}, data_options, data.amsTypeaheadOptions);
+											settings = ams.executeFunctionByName(data.amsTypeaheadInitCallback, input, settings) || settings;
+											var plugin = input.typeahead(settings);
+											ams.executeFunctionByName(data.amsTypeaheadAfterInitCallback, input, plugin, settings);
+										});
+								   });
 				}
 			},
 
@@ -2446,7 +2477,7 @@
 				var selects = $('.select2', element);
 				if (selects.length > 0) {
 					ams.ajax.check($.fn.select2,
-								   ams.baseURL + 'ext/jquery-select2-3.5.2' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-select2-3.5.2' + ams.devext + '.js',
 								   function() {
 										selects.each(function() {
 											var select = $(this);
@@ -2537,9 +2568,11 @@
 											}
 
 											if (select.attr('readonly')) {
-												data_options.query = function() {
-													return [];
-												};
+												if (select.attr('type') == 'hidden') {
+													data_options.query = function () {
+														return [];
+													};
+												}
 											} else if (data.amsSelect2Query) {
 												// Custom query method
 												data_options.query = ams.getFunctionByName(data.amsSelect2Query);
@@ -2578,7 +2611,7 @@
 													settings = $.extend({}, settings, data.amsSelect2QueryOptions);
 													settings = ams.executeFunctionByName(data.amsSelect2QueryInitCallback, select, settings) || settings;
 													ams.ajax.check($.jsonRPC,
-																   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
+																   ams.baseURL + 'ext/jquery-jsonrpc' + ams.devext + '.js',
 																   function() {
 																		$.jsonRPC.withOptions({
 																			endPoint: data.amsSelect2MethodTarget || ams.jsonrpc.getAddr(),
@@ -2610,9 +2643,8 @@
 											var plugin = select.select2(settings);
 											ams.executeFunctionByName(data.amsSelect2AfterInitCallback, select, plugin, settings);
 											if (select.hasClass('ordered')) {
-												select.addClass('nofloat');
 												ams.ajax.check($.fn.select2Sortable,
-															   ams.baseURL + 'ext/jquery-select2-sortable' + (ams.devmode ? '.js' : '.min.js'),
+															   ams.baseURL + 'ext/jquery-select2-sortable' + ams.devext + '.js',
 															   function() {
 																	select.select2Sortable({
 																		bindOrder: 'sortableStop'
@@ -2661,10 +2693,10 @@
 				var datepickers = $('.datepicker', element);
 				if (datepickers.length > 0) {
 					ams.ajax.check($.fn.datetimepicker,
-								   ams.baseURL + 'ext/jquery-datetimepicker' + (ams.devmode ? '.js': '.min.js'),
+								   ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + (ams.devmode ? '.css' : '.min.css'), 'jquery-datetimepicker');
+											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
 											ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
 										}
 										datepickers.each(function() {
@@ -2693,10 +2725,10 @@
 				var datetimepickers = $('.datetimepicker', element);
 				if (datetimepickers.length > 0) {
 					ams.ajax.check($.fn.datetimepicker,
-								   ams.baseURL + 'ext/jquery-datetimepicker' + (ams.devmode ? '.js': '.min.js'),
+								   ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + (ams.devmode ? '.css' : '.min.css'), 'jquery-datetimepicker');
+											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
 											ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
 										}
 										datetimepickers.each(function() {
@@ -2725,10 +2757,10 @@
 				var timepickers = $('.timepicker', element);
 				if (timepickers.length > 0) {
 					ams.ajax.check($.fn.datetimepicker,
-								   ams.baseURL + 'ext/jquery-datetimepicker' + (ams.devmode ? '.js': '.min.js'),
+								   ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + (ams.devmode ? '.css' : '.min.css'), 'jquery-datetimepicker');
+											ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
 											ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
 										}
 										timepickers.each(function() {
@@ -2756,10 +2788,10 @@
 				var colorpickers = $('.colorpicker', element);
 				if (colorpickers.length > 0) {
 					ams.ajax.check($.fn.minicolors,
-								   ams.baseURL + 'ext/jquery-minicolors' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-minicolors' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-minicolors' + (ams.devmode ? '.css' : '.min.css'), 'jquery-minicolors');
+											ams.getCSS(ams.baseURL + '../css/ext/jquery-minicolors' + ams.devext + '.css', 'jquery-minicolors');
 										}
 										colorpickers.each(function() {
 											var input = $(this);
@@ -2777,35 +2809,13 @@
 			},
 
 			/**
-			 * JQuery typeahead plug-in
-			 */
-			typeahead: function(element) {
-				var typeaheads = $('.typeahead', element);
-				if (typeaheads.length > 0) {
-					ams.ajax.check($.fn.typeahead,
-								   ams.baseURL + 'ext/jquery-typeahead' + (ams.devmode ? '.js' : '.min.js'),
-								   function() {
-										typeaheads.each(function() {
-											var input = $(this);
-											var data = input.data();
-											var data_options = {};
-											var settings = $.extend({}, data_options, data.amsTypeaheadOptions);
-											settings = ams.executeFunctionByName(data.amsTypeaheadInitCallback, input, settings) || settings;
-											var plugin = input.typeahead(settings);
-											ams.executeFunctionByName(data.amsTypeaheadAfterInitCallback, input, plugin, settings);
-										});
-								   });
-				}
-			},
-
-			/**
 			 * JQuery validation plug-in
 			 */
 			validate: function(element) {
 				var forms = $('FORM:not([novalidate])', element);
 				if (forms.length > 0) {
 					ams.ajax.check($.fn.validate,
-								   ams.baseURL + 'ext/jquery-validate-1.11.1' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-validate-1.11.1' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
 											$.validator.setDefaults({
@@ -2849,7 +2859,7 @@
 																		// before JQuery-form plug-in can get them when submitting the form...
 																		$('.state-error', form).removeClass('state-error');
 																		ams.ajax.check($.fn.ajaxSubmit,
-																					   ams.baseURL + 'ext/jquery-form-3.49' + (ams.devmode ? '.js' : '.min.js'));
+																					   ams.baseURL + 'ext/jquery-form-3.49' + ams.devext + '.js');
 																		return ams.form.submit(form);
 																	}
 																	: ams.getFunctionByName(data.amsFormSubmitHandler)
@@ -2889,7 +2899,7 @@
 				var tables = $('.datatable', element);
 				if (tables.length > 0) {
 					ams.ajax.check($.fn.dataTable,
-								   ams.baseURL + 'ext/jquery-dataTables-1.9.4' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-dataTables-1.9.4' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load) {
 											$.fn.dataTableExt.oSort['numeric-comma-asc']  = function(a, b) {
@@ -2909,7 +2919,7 @@
 										}
 										$(tables).each(function() {
 											ams.ajax.check($.fn.dataTableExt.oPagination['bootstrap_full'],
-														   ams.baseURL + 'myams-dataTables' + (ams.devmode ? '.js' : '.min.js'));
+														   ams.baseURL + 'myams-dataTables' + ams.devext + '.js');
 											var table = $(this);
 											var data = table.data();
 											var extensions = (data.amsDatatableExtensions || '').split(/\s+/);
@@ -2933,6 +2943,7 @@
 												bPaginate: data.amsDatatablePagination !== false,
 												bInfo: data.amsDatatableInfo !== false,
 												bSort: data.amsDatatableSort !== false,
+												aaSorting: data.amsDatatableSorting,
 												bDeferRender: true,
 												bAutoWidth: false,
 												iDisplayLength: data.amsDatatableDisplayLength || 25,
@@ -2951,23 +2962,23 @@
 													switch (extensions[index]) {
 														case 'autofill':
 															ams.ajax.check($.fn.dataTable.AutoFill,
-																		   ams.baseURL + 'ext/jquery-dataTables-autoFill' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-autoFill' + ams.devext + '.js');
 															break;
 														case 'columnfilter':
 															ams.ajax.check($.fn.columnFilter,
-																		   ams.baseURL + 'ext/jquery-dataTables-columnFilter' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-columnFilter' + ams.devext + '.js');
 															break;
 														case 'colreorder':
 															ams.ajax.check($.fn.dataTable.ColReorder,
-																		   ams.baseURL + 'ext/jquery-dataTables-colReorder' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-colReorder' + ams.devext + '.js');
 															break;
 														case 'colreorderwithresize':
 															ams.ajax.check($.fn.dataTable.ColReorder,
-																		   ams.baseURL + 'ext/jquery-dataTables-colReorderWithResize' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-colReorderWithResize' + ams.devext + '.js');
 															break;
 														case 'colvis':
 															ams.ajax.check($.fn.dataTable.ColVis,
-																		   ams.baseURL + 'ext/jquery-dataTables-colVis' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-colVis' + ams.devext + '.js');
 															var cv_default = {
 																activate: 'click',
 																sAlign: 'right'
@@ -2976,33 +2987,33 @@
 															break;
 														case 'editable':
 															ams.ajax.check($.fn.editable,
-																		   ams.baseURL + 'ext/jquery-jeditable' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-jeditable' + ams.devext + '.js');
 															ams.ajax.check($.fn.makeEditable,
-																		   ams.baseURL + 'ext/jquery-dataTables-editable' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-editable' + ams.devext + '.js');
 															break;
 														case 'fixedcolumns':
 															ams.ajax.check($.fn.dataTable.FixedColumns,
-																		   ams.baseURL + 'ext/jquery-dataTables-fixedColumns' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-fixedColumns' + ams.devext + '.js');
 															break;
 														case 'fixedheader':
 															ams.ajax.check($.fn.dataTable.FixedHeader,
-																		   ams.baseURL + 'ext/jquery-dataTables-fixedHeader' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-fixedHeader' + ams.devext + '.js');
 															break;
 														case 'keytable':
 															ams.ajax.check(window.KeyTable,
-																		   ams.baseURL + 'ext/jquery-dataTables-keyTable' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-keyTable' + ams.devext + '.js');
 															break;
 														case 'rowgrouping':
 															ams.ajax.check($.fn.rowGrouping,
-																		   ams.baseURL + 'ext/jquery-dataTables-rowGrouping' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-rowGrouping' + ams.devext + '.js');
 															break;
 														case 'rowreordering':
 															ams.ajax.check($.fn.rowReordering,
-																		   ams.baseURL + 'ext/jquery-dataTables-rowReordering' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-rowReordering' + ams.devext + '.js');
 															break;
 														case 'scroller':
 															ams.ajax.check($.fn.dataTable.Scroller,
-																		   ams.baseURL + 'ext/jquery-dataTables-scroller' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/jquery-dataTables-scroller' + ams.devext + '.js');
 															break;
 														default:
 															break;
@@ -3101,7 +3112,7 @@
 				var tables = $('.table-dnd', element);
 				if (tables.length > 0) {
 					ams.ajax.check($.fn.tableDnD,
-								   ams.baseURL + 'ext/jquery-tablednd' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-tablednd' + ams.devext + '.js',
 								   function(first_load) {
 										tables.each(function() {
 											var table = $(this);
@@ -3130,9 +3141,10 @@
 														if (typeof(local_target) == 'function') {
 															local_target.call(table, dnd_table, rows);
 														} else {
-															ams.ajax.post(target, {names: rows});
+															ams.ajax.post(target, {names: JSON.stringify(rows)});
 														}
 													}
+													return false;
 												}
 											};
 											var settings = $.extend({}, data_options, data.amsTabledndOptions);
@@ -3145,16 +3157,133 @@
 			},
 
 			/**
+			 * TinyMCE plug-in
+			 */
+			tinymce: function(element) {
+
+				function cleanEditors() {
+					$('.tinymce', $(this)).each(function() {
+						var editor = tinymce.get($(this).attr('id'));
+						if (editor) {
+							editor.remove();
+						}
+					});
+				}
+
+				var editors = $('.tinymce', element);
+				if (editors.length > 0) {
+					var baseURL = ams.baseURL + 'ext/tinymce' + (ams.devmode ? '/dev' : '');
+					ams.ajax.check(window.tinymce,
+								   baseURL + '/tinymce' + ams.devext + '.js',
+								   function(first_load) {
+										if (first_load) {
+											ams.getScript(baseURL + '/jquery.tinymce' + ams.devext + '.js');
+											tinymce.baseURL = baseURL;
+											tinymce.suffix = ams.devext;
+											ams.skin.registerCleanCallback(cleanEditors);
+										}
+										editors.each(function() {
+											var editor = $(this);
+											var data = editor.data();
+											var data_options = {
+												theme: "modern",
+												language: ams.lang,
+												plugins: [
+													"advlist autosave autolink lists link image charmap print preview hr anchor pagebreak",
+													"searchreplace wordcount visualblocks visualchars code fullscreen",
+													"insertdatetime media nonbreaking save table contextmenu directionality",
+													"emoticons paste textcolor colorpicker textpattern"
+												],
+												toolbar1: "newdocument undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
+												toolbar2: "print preview media | forecolor backcolor emoticons | code fullscreen",
+												content_css: data.amsTinymceContentCss,
+												formats: data.amsTinymceFormats,
+												style_formats: data.amsTinymceStyleFormats,
+												block_formats: data.amsTinymceBlockFormats,
+												valid_classes: data.amsTinymceValidClasses,
+												image_advtab: true,
+												image_list: ams.getFunctionByName(data.amsTinymceImageList) || data.amsTinymceImageList,
+												image_class_list: data.amsTinymceImageClassList,
+												link_list: ams.getFunctionByName(data.amsTinymceLinkList) || data.amsTinymceLinkList,
+												link_class_list: data.amsTinymceLinkClassList,
+												resize: true
+											};
+											if (data.amsTinymceExternalPlugins) {
+												var names = data.amsTinymceExternalPlugins.split(/\s+/);
+												for (var index in names) {
+													var plugin_src = editor.data('ams-tinymce-plugin-' + names[index]);
+													tinymce.PluginManager.load(names[index], ams.getSource(plugin_src));
+												}
+											}
+											var settings = $.extend({}, data_options, data.amsTinymceOptions);
+											settings = ams.executeFunctionByName(data.amsTinymceInitCallback, editor, settings) || settings;
+											var plugin = editor.tinymce(settings);
+											ams.executeFunctionByName(data.amsTinymceAfterInitCallback, editor, plugin, settings);
+										});
+								   });
+				}
+			},
+
+			/**
+			 * Image area select plug-in
+			 */
+			imgareaselect: function(element) {
+				var images = $('.imgareaselect', element);
+				if (images.length > 0) {
+					ams.ajax.check($.fn.imgAreaSelect,
+								   ams.baseURL + 'ext/jquery-imgareaselect-0.9.10' + ams.devext + '.js',
+								   function(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();
+										   var parent = data.amsImgareaselectParent ? image.parents(data.amsImgareaselectParent) : 'body';
+										   var data_options = {
+											   instance: true,
+											   handles: true,
+											   parent: parent,
+											   x1: data.amsImgareaselectX1 || 0,
+											   y1: data.amsImgareaselectY1 || 0,
+											   x2: data.amsImgareaselectX2 || data.amsImgareaselectImageWidth,
+											   y2: data.amsImgareaselectY2 || data.amsImgareaselectImageHeight,
+											   imageWidth: data.amsImgareaselectImageWidth,
+											   imageHeight: data.amsImgareaselectImageHeight,
+											   minWidth: 128,
+											   minHeight: 128,
+											   aspectRatio: data.amsImgareaselectRatio,
+											   onSelectEnd: ams.getFunctionByName(data.amsImgareaselectSelectEnd) || function(img, selection) {
+												   var target = data.amsImgareaselectTargetField || 'image_';
+												   $('input[name="' + target + 'x1"]', parent).val(selection.x1);
+												   $('input[name="' + target + 'y1"]', parent).val(selection.y1);
+												   $('input[name="' + target + 'x2"]', parent).val(selection.x2);
+												   $('input[name="' + target + 'y2"]', parent).val(selection.y2);
+											   }
+										   };
+										   var settings = $.extend({}, data_options, data.amsImgareaselectOptions);
+										   settings = ams.executeFunctionByName(data.amsImgareaselectInitCallback, image, settings) || settings;
+										   var plugin = image.imgAreaSelect(settings);
+										   ams.executeFunctionByName(data.amsImgareaselectAfterInitCallback, image, plugin, settings);
+										   // Add update timeout when plug-in is displayed into a modal dialog
+										   setTimeout(function() {
+											   plugin.update();
+										   }, 250);
+									   });
+								   })
+				}
+			},
+
+			/**
 			 * FancyBox plug-in
 			 */
 			fancybox: function(element) {
 				var fancyboxes = $('.fancybox', element);
 				if (fancyboxes.length > 0) {
 					ams.ajax.check($.fn.fancybox,
-								   ams.baseURL + 'ext/jquery-fancybox-2.1.5' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'ext/jquery-fancybox-2.1.5' + ams.devext + '.js',
 								   function(first_load) {
 										if (first_load)
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-fancybox-2.1.5' + (ams.devmode ? '.css' : '.min.css'));
+											ams.getCSS(ams.baseURL + '../css/ext/jquery-fancybox-2.1.5' + ams.devext + '.css');
 										fancyboxes.each(function() {
 											var fancybox = $(this);
 											var data = fancybox.data();
@@ -3165,15 +3294,15 @@
 													switch (helper) {
 														case 'buttons':
 															ams.ajax.check($.fancybox.helpers.buttons,
-																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-buttons.js' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-buttons' + ams.devext + '.js');
 															break;
 														case 'thumbs':
 															ams.ajax.check($.fancybox.helpers.thumbs,
-																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-thumbs.js' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-thumbs' + ams.devext + '.js');
 															break;
 														case 'media':
 															ams.ajax.check($.fancybox.helpers.media,
-																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-media.js' + (ams.devmode ? '.js' : '.min.js'));
+																		   ams.baseURL + 'ext/fancybox-helpers/fancybox-media' + ams.devext + '.js');
 															break;
 														default:
 															break;
@@ -3203,62 +3332,13 @@
 			},
 
 			/**
-			 * Image area select plug-in
-			 */
-			imgareaselect: function(element) {
-				var images = $('.imgareaselect', element);
-				if (images.length > 0) {
-					ams.ajax.check($.fn.imgAreaSelect,
-								   ams.baseURL + 'ext/jquery-imgareaselect-0.9.10' + (ams.devmode ? '.js' : '.min.js'),
-								   function(first_load) {
-										if (first_load)
-											ams.getCSS(ams.baseURL + '../css/ext/jquery-imgareaselect' + (ams.devmode ? '.css' : '.min.css'));
-										images.each(function() {
-											var image = $(this);
-											var data = image.data();
-											var parent = data.amsImgareaselectParent ? image.parents(data.amsImgareaselectParent) : 'body';
-											var data_options = {
-												instance: true,
-												handles: true,
-												parent: parent,
-												x1: data.amsImgareaselectX1 || 0,
-												y1: data.amsImgareaselectY1 || 0,
-												x2: data.amsImgareaselectX2 || data.amsImgareaselectImageWidth,
-												y2: data.amsImgareaselectY2 || data.amsImgareaselectImageHeight,
-												imageWidth: data.amsImgareaselectImageWidth,
-												imageHeight: data.amsImgareaselectImageHeight,
-												minWidth: 128,
-												minHeight: 128,
-												aspectRatio: data.amsImgareaselectRatio,
-												onSelectEnd: ams.getFunctionByName(data.amsImgareaselectSelectEnd) || function(img, selection) {
-													var target = data.amsImgareaselectTargetField || 'image_';
-													$('input[name="' + target + 'x1"]', parent).val(selection.x1);
-													$('input[name="' + target + 'y1"]', parent).val(selection.y1);
-													$('input[name="' + target + 'x2"]', parent).val(selection.x2);
-													$('input[name="' + target + 'y2"]', parent).val(selection.y2);
-												}
-											};
-											var settings = $.extend({}, data_options, data.amsImgareaselectOptions);
-											settings = ams.executeFunctionByName(data.amsImgareaselectInitCallback, image, settings) || settings;
-											var plugin = image.imgAreaSelect(settings);
-											ams.executeFunctionByName(data.amsImgareaselectAfterInitCallback, image, plugin, settings);
-											// Add update timeout when plug-in is displayed into a modal dialog
-											setTimeout(function() {
-												plugin.update();
-											}, 250);
-										});
-								   })
-				}
-			},
-
-			/**
 			 * Sparkline graphs
 			 */
 			graphs: function(element) {
 				var graphs = $('.sparkline', element);
 				if (graphs.length > 0) {
 					ams.ajax.check(ams.graphs,
-								   ams.baseURL + 'myams-graphs' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'myams-graphs' + ams.devext + '.js',
 								   function() {
 										ams.graphs.init(graphs);
 								   });
@@ -3275,7 +3355,7 @@
 								   ams.baseURL + 'ext/jquery-mousewheel.min.js',
 								   function() {
 										ams.ajax.check($.fn.mCustomScrollbar,
-													   ams.baseURL + 'ext/jquery-mCustomScrollbar' + (ams.devmode ? '.js' : '.min.js'),
+													   ams.baseURL + 'ext/jquery-mCustomScrollbar' + ams.devext + '.js',
 													   function(first_load) {
 															if (first_load)
 																ams.getCSS(ams.baseURL + '../css/ext/jquery-mCustomScrollbar.css',
@@ -3568,7 +3648,7 @@
 				var widgets = $('.ams-widget', element);
 				if (widgets.length > 0)
 					ams.ajax.check($.fn.MyAMSWidget,
-								   ams.baseURL + 'myams-widgets' + (ams.devmode ? '.js' : '.min.js'),
+								   ams.baseURL + 'myams-widgets' + ams.devext + '.js',
 								   function() {
 										widgets.each(function() {
 											var widget = $(this);
@@ -3638,7 +3718,7 @@
 		 */
 		bigBox: function(options, callback) {
 			ams.ajax.check(ams.notify,
-						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'myams-notify' + ams.devext + '.js',
 						   function() {
 								ams.notify.messageBox(options, callback);
 						   });
@@ -3654,7 +3734,7 @@
 				status = 'info';
 			}
 			ams.ajax.check(ams.notify,
-						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'myams-notify' + ams.devext + '.js',
 						   function() {
 								switch (status) {
 									case 'error':
@@ -3685,7 +3765,7 @@
 				status = 'info';
 			}
 			ams.ajax.check(ams.notify,
-						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
+						   ams.baseURL + 'myams-notify' + ams.devext + '.js',
 						   function() {
 								switch (status) {
 									case 'error':
@@ -3777,6 +3857,32 @@
 		},
 
 		/**
+		 * List of registered 'cleaning' callbacks
+		 * These callbacks are called before loading a new URL into a given container
+		 * to clean required elements from memory before the DOM elements are removed
+		 */
+		_clean_callbacks: [],
+
+		/**
+		 * Register a callback which should be called before a container is replaced
+		 */
+		registerCleanCallback: function(callback) {
+			var callbacks = ams.skin._clean_callbacks;
+			if (callbacks.indexOf(callback) < 0)
+				callbacks.push(callback);
+		},
+
+		/**
+		 * Call registered cleaning callbacks on given container
+		 */
+		cleanContainer: function(container) {
+			var callbacks = ams.skin._clean_callbacks;
+			for (var index in callbacks) {
+				callbacks[index].call(container);
+			}
+		},
+
+		/**
 		 * Load given URL into container
 		 */
 		loadURL: function(url, container, options, callback) {
@@ -3794,7 +3900,8 @@
 				dataType: 'html',
 				cache: false,
 				beforeSend: function() {
-					container.html('<h1><i class="fa fa-cog fa-spin"></i> Loading... </h1>');
+					ams.skin.cleanContainer(container);
+					container.html('<h1 class="loading"><i class="fa fa-cog fa-spin"></i> Loading... </h1>');
 					if (container[0] == $('#content')[0]) {
 						ams.skin._drawBreadCrumb();
 						document.title = $('.breadcrumb LI:last-child').text();
@@ -3810,6 +3917,7 @@
 						var request_data = ams.ajax.getResponse(request);
 						var data_type = request_data.content_type;
 						var result = request_data.data;
+						$('.loading', container).remove();
 						switch (data_type) {
 							case 'json':
 								ams.ajax.handleJSON(result, container);
@@ -3920,7 +4028,7 @@
 			ams.device = 'mobile';
 			if (ams.enable_fastclick) {
 				ams.ajax.check($.fn.noClickDelay,
-							   ams.baseURL + '/ext/jquery-smartclick' + (ams.devmode ? '.js' : '.min.js'),
+							   ams.baseURL + '/ext/jquery-smartclick' + ams.devext + '.js',
 							   function() {
 								   $('NAV UL A').noClickDelay();
 								   $('#hide-menu A').noClickDelay();
@@ -4050,7 +4158,7 @@
 
 		// Resize events
 		ams.ajax.check($.resize,
-					   ams.baseURL + 'ext/jquery-resize' + (ams.devmode ? '.js' : '.min.js'),
+					   ams.baseURL + 'ext/jquery-resize' + ams.devext + '.js',
 					   function() {
 						   $('#main').resize(function() {
 							   ams.skin._setPageHeight();
@@ -4063,8 +4171,6 @@
 
 		// Init AJAX navigation
 		if (ams.ajax_nav) {
-			if ($('nav').length > 0)
-				ams.skin.checkURL();
 			$(document).on('click', 'a[href="#"]', function(e) {
 				e.preventDefault();
 			});
@@ -4196,6 +4302,13 @@
 			}
 		});
 
+		// Prevent bootstrap dialog from blocking TinyMCE focus
+		$(document).on('focusin', function(e) {
+			if ($(e.target).closest('.mce-window').length) {
+				e.stopImmediatePropagation();
+			}
+		});
+
 		// Disable clicks on disabled tabs
 		$("a[data-toggle=tab]", ".nav-tabs").on("click", function(e) {
 			if ($(this).parent('li').hasClass("disabled")) {
@@ -4217,13 +4330,10 @@
 			}
 		});
 
-		// Init plug-ins required by main layout
-		ams.plugins.enabled.hint(document);
-
-		// Init content when not loaded by AJAX request
-		// or when redirecting to authentication page...
-		if ((window.location.hash == '') || (ams.getQueryVar(window.location.href, 'came_from') != false))
-			ams.initContent(document);
+		// Init page content
+		ams.initContent(document);
+		if (ams.ajax_nav && ($('nav').length > 0))
+			ams.skin.checkURL();
 
 		// Add unload event listener to check for modified forms
 		$(window).on('beforeunload', ams.form.checkBeforeUnload);