src/pyams_skin/resources/js/myams-skin.js
changeset 557 bca7a7e058a3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_skin/resources/js/myams-skin.js	Thu Feb 13 11:43:31 2020 +0100
@@ -0,0 +1,600 @@
+/**
+ * MyAMS skin management
+ */
+(function($, globals) {
+
+	var ams = globals.MyAMS;
+
+	ams.skin = {
+
+		/**
+		 * Compute navigation page height
+		 */
+		_setPageHeight: function() {
+			var mainHeight = $('#main').height();
+			var windowHeight = $(window).height() - ams.navbarHeight;
+			if (mainHeight > windowHeight) {
+				ams.root.css('min-height', mainHeight + ams.navbarHeight);
+			} else {
+				ams.root.css('min-height', windowHeight);
+			}
+			ams.leftPanel.css('min-height', windowHeight);
+			ams.leftPanel.css('max-height', windowHeight);
+		},
+
+		/**
+		 * 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')) {
+				ams.root.removeClass('mobile-view-activated');
+			}
+		},
+
+		/**
+		 * Show/hide shortcut buttons
+		 */
+		_showShortcutButtons: function() {
+			ams.shortcuts && ams.shortcuts.animate({
+													   height: 'show'
+												   }, 200, 'easeOutCirc');
+			ams.root.addClass('shortcut-on');
+		},
+
+		_hideShortcutButtons: function() {
+			ams.shortcuts && ams.shortcuts.animate({
+													   height: 'hide'
+												   }, 300, 'easeOutCirc');
+			ams.root.removeClass('shortcut-on');
+		},
+
+		/**
+		 * Check notification badge
+		 */
+		checkNotification: function() {
+			var badge = $('.badge', '#user-activity >span');
+			if (parseInt(badge.text()) > 0) {
+				badge.removeClass("hidden")
+					.addClass("bg-color-red bounceIn animated");
+			} else {
+				badge.addClass("hidden")
+					.removeClass("bg-color-red bounceIn animated");
+			}
+		},
+
+		refreshNotificationsPanel: function(e) {
+			var button = $(this);
+			button.addClass('disabled');
+			$('i', button).addClass('fa-spin');
+			$('input[name="activity"]:checked', '#user-activity').change();
+			$('i', button).removeClass('fa-spin');
+			button.removeClass('disabled');
+		},
+
+		/**
+		 * Replace given form with new content
+		 */
+		refreshContent: function(options) {
+			var target = $('[id="' + options.object_id + '"]');
+			target.replaceWith($(options.content));
+			target = $('[id="' + options.object_id + '"]');
+			ams.initContent && ams.initContent(target);
+			return target;
+		},
+
+		/**
+		 * Replace given image from updated source
+		 */
+		refreshImage: function(options) {
+			$('img[src^="' + options.src + '"]').attr('src', options.target);
+		},
+
+		/**
+		 * Replace given widget with given content
+		 */
+		refreshWidget: function(options) {
+			var target = $('[id="' + options.parent_id + '"]');
+			var widget = $('[name="' + options.widget_name + '"]', target);
+			if (!widget.exists()) {
+				widget = $('[name="' + options.widget_name + ':list"]', target);
+			}
+			var label = widget.parents('.input').last();
+			label.html(options.content);
+			ams.initContent && ams.initContent(label);
+			return label;
+		},
+
+		/**
+		 * Replace given table with new content
+		 */
+		refreshTable: function(options) {
+			var widget = $('[id="' + options.object_id + '"]').parents('.ams-widget:first');
+			widget.replaceWith($(options.table));
+			widget = $('[id="' + options.object_id + '"]').parents('.ams-widget:first');
+			ams.initContent && ams.initContent(widget);
+			return widget;
+		},
+
+		/**
+		 * Replace given table with new content
+		 * If table is located inside a switched fieldset, fieldset is opened
+		 */
+		refreshSwitchedTable: function(options) {
+			var widget = ams.skin.refreshTable(options);
+			if (widget) {
+				var legend = widget.siblings('legend');
+				if (legend.parents('fieldset:first').hasClass('switched')) {
+					legend.click();
+				}
+			}
+		},
+
+		/**
+		 * Replace given row with new content
+		 */
+		refreshRow: function(options) {
+			var tr = $('tr[id="' + options.object_id + '"]');
+			var table = tr.parents('table').first();
+			var new_tr = $(options.row);
+			tr.replaceWith(new_tr);
+			ams.initContent && ams.initContent(new_tr);
+			if (table.hasClass('table-dnd')) {
+				new_tr.addClass('no-drag-handle');
+				table.tableDnDUpdate();
+			}
+			return new_tr;
+		},
+
+		/**
+		 * Replace given row cell with new content
+		 */
+		refreshRowCell: function(options) {
+			var tr = $('tr[id="' + options.object_id + '"]');
+			var table = tr.parents('table').first();
+			var headRow = $('tr', $('thead', table));
+			var headCell = $('th[data-ams-column-name="' + options.col_name + '"]', headRow);
+			var index = $('th', headRow).index(headCell);
+			if (index > -1) {
+				var cell = $($('td', tr).get(index));
+				cell.html(options.cell);
+				ams.initContent && ams.initContent(cell);
+			}
+		},
+
+		switchCellContent: function(element) {
+			var source = $(this);
+			var switcher = $('i.switch', source);
+			var td = source.parents('td');
+			var innerdiv = $(source.data('ams-switch-target') || '.inner-table-form', td);
+			var datatype = source.parents('tr');
+			if (switcher.hasClass('fa-plus-square-o')) {
+				var container = datatype.parents('table');
+				innerdiv.html('<h1 class="loading"><i class="fa fa-gear fa-spin"></i></h1>');
+				ams.ajax && ams.ajax.post(container.data('ams-location') + '/' + source.data('ams-switch-handler'),
+										  {object_name: datatype.data('ams-element-name')},
+										  function(result) {
+											  innerdiv.html(result);
+											  if (result) {
+												  ams.initContent && ams.initContent(innerdiv);
+												  switcher.removeClass('fa-plus-square-o')
+													  .addClass('fa-minus-square-o');
+											  }
+										  });
+			} else {
+				ams.skin.cleanContainer(innerdiv);
+				innerdiv.empty();
+				switcher.removeClass('fa-minus-square-o')
+					.addClass('fa-plus-square-o');
+			}
+		},
+
+		/**
+		 * Initialize desktop and mobile widgets
+		 */
+		_initDesktopWidgets: function(element) {
+			if (ams.enableWidgets) {
+				var widgets = $('.ams-widget', element);
+				if (widgets.length > 0) {
+					ams.ajax && ams.ajax.check($.fn.MyAMSWidget,
+											   ams.baseURL + 'myams-widgets' + ams.devext + '.js',
+											   function() {
+												   widgets.each(function() {
+													   var widget = $(this);
+													   var data = widget.data();
+													   var dataOptions = {
+														   deleteSettingsKey: '#deletesettingskey-options',
+														   deletePositionKey: '#deletepositionkey-options'
+													   };
+													   var settings = $.extend({}, dataOptions, 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.enableMobile && ams.enableWidgets) {
+				ams.skin._initDesktopWidgets(element);
+			}
+		},
+
+		/**
+		 * Add an alert on top of a container
+		 *
+		 * @parent: parent container where the alert will be displayed
+		 * @status: info, success, warning or danger
+		 * @header: alert header
+		 * @message: main alert message
+		 * @subtitle: optional subtitle
+		 * @margin: if true, a margin will be displayed around alert
+		 */
+		alert: function(parent, status, header, message, subtitle, margin) {
+			if (status === 'error') {
+				status = 'danger';
+			}
+			$('.alert-' + status, parent).not('.persistent').remove();
+			var content = '<div class="' + (margin ? 'margin-10' : '') + ' alert alert-block alert-' + status + ' padding-5 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') {
+				content += '<ul><li>' + message + '</li></ul>';
+			} else if (message) {
+				content += '<ul>';
+				for (var index in message) {
+					if (!$.isNumeric(index)) {  // IE check
+						continue;
+					}
+					content += '<li>' + message[index] + '</li>';
+				}
+				content += '</ul>';
+			}
+			content += '</div>';
+			$(content).insertBefore(parent);
+			if (parent.exists) {
+				ams.skin.scrollTo(parent, {offset: {top: -50}});
+			}
+		},
+
+		/**
+		 * Big message box
+		 */
+		bigBox: function(options, callback) {
+			ams.ajax && ams.ajax.check(ams.notify,
+									   ams.baseURL + 'myams-notify' + ams.devext + '.js',
+									   function() {
+										   ams.notify.messageBox(options, callback);
+									   });
+		},
+
+		/**
+		 * Medium notification message box, displayed on page's bottom right
+		 */
+		messageBox: function(status, options, callback) {
+			if (typeof (status) === 'object') {
+				callback = options;
+				options = status || {};
+				status = 'info';
+			}
+			ams.ajax && 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);
+									   });
+		},
+
+		/**
+		 * Small notification message box, displayed on page's top right
+		 */
+		smallBox: function(status, options, callback) {
+			if (typeof (status) === 'object') {
+				callback = options;
+				options = status || {};
+				status = 'info';
+			}
+			ams.ajax && 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);
+									   });
+		},
+
+		/**
+		 * Scroll to given element
+		 *
+		 * @param element: the element to which to scroll
+		 * @param options: scroll options
+		 */
+		scrollTo: function(element, options) {
+			ams.ajax && ams.ajax.check($.scrollTo,
+									   ams.baseURL + 'ext/jquery-scrollto-2.1.2' + ams.devext + '.js',
+									   function() {
+										   var body = $('body');
+										   var offset = options.offset || 0;
+										   if (body.hasClass('fixed-header')) {
+											   offset -= $('#header').height();
+										   }
+										   if (body.hasClass('fixed-ribbon')) {
+											   offset -= $('#ribbon').height();
+										   }
+										   options = $.extend({}, options, {offset: offset});
+										   $.scrollTo(element, options);
+									   });
+		},
+
+		/**
+		 * Initialize breadcrumbs based on active menu position
+		 */
+		_drawBreadCrumb: function() {
+			var crumb = $('OL.breadcrumb', '#ribbon');
+			$('li', crumb).not('.parent').remove();
+			if (!$('li', crumb).exists()) {
+				crumb.append($('<li></li>').append($('<a></a>').text(ams.i18n.HOME)
+													   .addClass('padding-right-5')
+													   .attr('href', $('nav a[href!="#"]:first').attr('href'))));
+			}
+			$('LI.active >A', 'nav').each(function() {
+				var menu = $(this);
+				var body = $.trim(menu.clone()
+									  .children(".badge")
+									  .remove()
+									  .end()
+									  .text());
+				var item = $("<li></li>").append(menu.attr('href').replace(/^#/, '') ?
+													 $("<a></a>").html(body).attr('href', menu.attr('href'))
+													 : body);
+				crumb.append(item);
+			});
+		},
+
+		/**
+		 * Check URL matching current location hash
+		 */
+		checkURL: function() {
+
+			function updateActiveMenus(menu) {
+				$('.active', nav).removeClass('active');
+				menu.addClass('open')
+					.addClass('active');
+				menu.parents('li').addClass('open active')
+					.children('ul').addClass('active')
+					.show();
+				menu.parents('li:first').removeClass('open');
+				menu.parents('ul').addClass(menu.attr('href').replace(/^#/, '') ? 'active' : '')
+					.show();
+			}
+
+			var menu;
+			var nav = $('nav');
+			var hash = location.hash;
+			var url = hash.replace(/^#/, '');
+			if (url) {
+				var container = $('#content');
+				if (!container.exists()) {
+					container = $('body');
+				}
+				menu = $('A[href="' + hash + '"]', nav);
+				if (menu.exists()) {
+					updateActiveMenus(menu);
+				}
+				ams.skin.loadURL(url, container, {
+					afterLoadCallback: function() {
+						var prefix = $('html head title').data('ams-title-prefix');
+						document.title = (prefix ? prefix + ' > ' : '') +
+							($('[data-ams-page-title]:first', container).data('ams-page-title') ||
+								menu.attr('title') ||
+								document.title);
+					}
+				});
+			} else {
+				var activeUrl = $('[data-ams-active-menu]').data('ams-active-menu');
+				if (activeUrl) {
+					menu = $('A[href="' + activeUrl + '"]', nav);
+				} else {
+					menu = $('>UL >LI >A[href!="#"]', nav).first();
+				}
+				if (menu.exists()) {
+					updateActiveMenus(menu);
+					if (activeUrl) {
+						ams.skin._drawBreadCrumb();
+					} else {
+						window.location.hash = menu.attr('href');
+					}
+				}
+			}
+		},
+
+		/**
+		 * 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);
+			}
+		},
+
+		/**
+		 * Remove given callback from registry
+		 */
+		unregisterCleanCallback: function(callback) {
+			var callbacks = ams.skin._clean_callbacks;
+			var index = callbacks.indexOf(callback);
+			if (index >= 0) {
+				callbacks.splice(index, 1);
+			}
+		},
+
+		/**
+		 * Call registered cleaning callbacks on given container
+		 */
+		cleanContainer: function(container) {
+			var callbacks = ams.skin._clean_callbacks;
+			for (var index = 0; index < callbacks.length; index++) {
+				callbacks[index].call(container);
+			}
+		},
+
+		/**
+		 * Load given URL into container
+		 */
+		loadURL: function(url, container, options, callback) {
+			if (url.startsWith('#')) {
+				url = url.substr(1);
+			}
+			if (typeof (options) === 'function') {
+				callback = options;
+				options = {};
+			} else if (options === undefined) {
+				options = {};
+			}
+			container = $(container);
+			var defaults = {
+				type: 'GET',
+				url: url,
+				dataType: 'html',
+				cache: false,
+				beforeSend: function() {
+					if (options && options.preLoadCallback) {
+						ams.executeFunctionByName(options.preLoadCallback, this, options.preLoadCallbackOptions);
+					}
+					ams.skin.cleanContainer(container);
+					container.html('<h1 class="loading"><i class="fa fa-cog fa-spin"></i> ' + ams.i18n.LOADING + ' </h1>');
+					if (container[0] === $('#content')[0]) {
+						ams.skin._drawBreadCrumb();
+						var prefix = $('html head title').data('ams-title-prefix');
+						document.title = (prefix ? prefix + ' > ' : '') + $('.breadcrumb LI:last-child').text();
+						$('html, body').animate({scrollTop: 0}, 'fast');
+					} else {
+						container.animate({scrollTop: 0}, 'fast');
+					}
+				},
+				success: function(data, status, request) {
+					if (callback) {
+						ams.executeFunctionByName(callback, this, data, status, request, options);
+					} else {
+						var response = ams.ajax && ams.ajax.getResponse(request);
+						if (response) {
+							var dataType = response.contentType;
+							var result = response.data;
+							$('.loading', container).remove();
+							switch (dataType) {
+								case 'json':
+									ams.ajax.handleJSON(result, container);
+									break;
+								case 'script':
+									break;
+								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'})
+										.html(data)
+										.removeClass('hidden')
+										.delay(50)
+										.animate({opacity: '1.0'}, 300);
+									ams.initContent && ams.initContent(container);
+									ams.form && ams.form.setFocus(container);
+							}
+							if (options && options.afterLoadCallback) {
+								ams.executeFunctionByName(options.afterLoadCallback, this, options.afterLoadCallbackOptions);
+							}
+							ams.stats && ams.stats.logPageview();
+						}
+					}
+				},
+				error: function(request, errorOptions, error) {
+					container.html('<h3 class="error"><i class="fa fa-warning txt-color-orangeDark"></i> ' +
+									   ams.i18n.ERROR + error + '</h3>' +
+									   request.responseText);
+					if (options && options.afterErrorCallback) {
+						ams.executeFunctionByName(options.afterErrorCallback, this);
+					}
+				},
+				async: options.async === undefined ? true : options.async
+			};
+			var settings = $.extend({}, defaults, options);
+			$.ajax(settings);
+		},
+
+		/**
+		 * Change user language
+		 */
+		setLanguage: function(event, options) {
+			var lang = options.lang;
+			var handlerType = options.handler_type || 'json';
+			switch (handlerType) {
+				case 'json':
+					var method = options.method || 'setUserLanguage';
+					ams.jsonrpc && ams.jsonrpc.post(method, {lang: lang}, function() {
+						window.location.reload(true);
+					});
+					break;
+				case 'ajax':
+					var href = options.href || 'setUserLanguage';
+					ams.ajax && ams.ajax.post(href, {lang: lang}, function() {
+						window.location.reload(true);
+					});
+					break;
+			}
+		},
+
+		/**
+		 * Go to logout page
+		 */
+		logout: function() {
+			window.location = ams.loginURL;
+		}
+	};
+
+})(jQuery, this);