src/ztfy/myams/resources/js/myams.js
changeset 39 2c6d47b76a7b
parent 19 dc18d0700e39
child 49 f830ae04d473
--- a/src/ztfy/myams/resources/js/myams.js	Tue Jun 03 23:40:12 2014 +0200
+++ b/src/ztfy/myams/resources/js/myams.js	Tue Jun 03 23:45:55 2014 +0200
@@ -2,7 +2,7 @@
  * MyAMS
  * « My Application Management Skin »
  *
- * $Tag$
+ * $Tag: 0.1.2 $
  * A bootstrap based application/administration skin
  *
  * Custom administration and application skin tools
@@ -102,6 +102,9 @@
 	 */
 	$.fn.extend({
 
+		/*
+		 * Check if current object is empty or not
+		 */
 		exists: function() {
 			return $(this).length > 0;
 		},
@@ -136,6 +139,9 @@
 			}
 		},
 
+		/*
+		 * Remove CSS classes starting with a given prefix
+		 */
 		removeClassPrefix: function (prefix) {
 			this.each(function (i, it) {
 				var classes = it.className.split(" ").map(function(item) {
@@ -146,6 +152,9 @@
 			return this;
 		},
 
+		/*
+		 * Main menus manager
+		 */
 		myams_menu: function(options) {
 			// Extend our default options with those provided
 			var defaults = {
@@ -321,6 +330,7 @@
 			enable_widgets: true,
 			enable_mobile: false,
 			enable_fastclick: false,
+			warn_on_form_change: false,
 			ismobile: (/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase()))
 		};
 	}
@@ -613,6 +623,21 @@
 		},
 
 		/**
+		 * Handle AJAX upload and download progress
+		 *
+		 * @param event: the source event
+		 * @param request: the request
+		 * @param options: AJAX options
+		 */
+		progress: function(event) {
+			if (!event.lengthComputable)
+				return;
+			if (event.loaded >= event.total)
+				return;
+			console.log(parseInt((event.loaded / event.total * 100), 10) + "%");
+		},
+
+		/**
 		 * Post data to given URL
 		 */
 		post: function(url, data, options, callback) {
@@ -764,7 +789,7 @@
 			}
 			if (result.content) {
 				var content = result.content;
-				var container = $(content.target || form || '#content');
+				var container = $(content.target || target || form || '#content');
 				container.html(content.html);
 				ams.initContent(container);
 			}
@@ -774,7 +799,7 @@
 					ams.skin.alert($(form || '#content'),
 								   status, '', message);
 				else
-					ams.skin.alert($(message.target || form || '#content'),
+					ams.skin.alert($(message.target || target || form || '#content'),
 								   message.status || 'success',
 								   message.header,
 								   message.body,
@@ -886,7 +911,7 @@
 		 *  - @options: additional JSON-RPC procedure parameters
 		 *  - @callback: name of a callback which will be called on server response
 		 */
-		post: function(method, options, callback) {
+		post: function(method, data, options, callback) {
 			ams.ajax.check($.jsonRpc,
 						   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
 						   function() {
@@ -903,18 +928,19 @@
 									callback = ams.getFunctionByName(callback);
 								delete options.callback;
 
-								var settings = {
+								var defaults = {
 									url: ams.jsonrpc.getAddr(options.url),
 									type: 'post',
 									cache: false,
 									method: method,
-									params: options,
+									params: data,
 									async: typeof(callback) == 'function',
-									success: callback || function(data, status) {
+									success: callback || function(data /*, status*/) {
 										result = data.result;
 									},
 									error: ams.error.show
 								};
+								var settings = $.extend({}, defaults, options);
 								$.jsonRpc(settings);
 								return result;
 						   });
@@ -928,6 +954,72 @@
 	MyAMS.form = {
 
 		/**
+		 * Init forms to activate form change listeners
+		 *
+		 * @param element: the parent element
+		 */
+		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
+				forms = $('FORM[data-ams-warn-on-change="true"]', element);
+			forms.each(function() {
+				var form = $(this);
+				$('INPUT[type="text"], ' +
+				  'INPUT[type="checkbox"], ' +
+				  'INPUT[type="radio"], ' +
+				  'SELECT, ' +
+				  'TEXTAREA, ' +
+				  '[data-ams-changed-event]', form).each(function() {
+						var source = $(this);
+						if (source.data('ams-ignore-change') !== true) {
+							var event = source.data('ams-changed-event') || 'change';
+							source.on(event, function () {
+								$(this).parents('FORM').attr('data-ams-form-changed', true);
+							});
+						}
+				});
+				form.on('reset', function() {
+					$(this).removeAttr('data-ams-form-changed');
+				});
+			});
+		},
+
+		/**
+		 * Check for modified forms before exiting
+		 */
+		checkBeforeUnload: function() {
+			var forms = $('FORM[data-ams-form-changed="true"]');
+			if (forms.exists()) {
+				return ams.i18n.FORM_CHANGED_WARNING;
+			}
+		},
+
+		/**
+		 * Check for modified forms before loading new inner content
+		 */
+		confirmChangedForm: function(element, callback) {
+			if (typeof(element) == 'function') {
+				callback = element;
+				element = undefined;
+			}
+			var forms = $('FORM[data-ams-form-changed="true"]', element);
+			if (forms.exists()) {
+				ams.skin.bigBox({
+					title: ams.i18n.WARNING,
+					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)
+						callback.call(element);
+				});
+			} else {
+				callback.call(element);
+			}
+		},
+
+		/**
 		 * Submit given form
 		 */
 		submit: function(form, handler, submit_options) {
@@ -1049,6 +1141,7 @@
 												button.button('reset');
 											form.data('submitted', false);
 											form.removeData('ams-submit-button');
+											form.removeAttr('data-ams-form-changed');
 										},
 										iframe: hasUpload
 									}
@@ -1329,8 +1422,13 @@
 									}
 									if (typeof(source) == 'string')
 										var url = source;
-									else
+									else {
 										url = source.attr('href') || source.data('ams-url');
+										var url_getter = ams.getFunctionByName(url);
+										if (typeof(url_getter) == 'function') {
+											url = url_getter.call(source);
+										}
+									}
 									if (!url)
 										return;
 									$('body').modalmanager('loading');
@@ -1734,38 +1832,47 @@
 						var input = $('<input type="checkbox">').attr('name', fieldname)
 																.attr('id', fieldname.replace(/\./, '_'))
 																.data('ams-checker-init', true)
-																.val(data.amsCheckerState == 'on')
-																.on('change', function(e) {
-																	e.preventDefault();
-																	var veto = {};
-																	legend.trigger('ams.checker.before-switch', [legend, veto]);
-																	if (veto.veto)
-																		return;
-																	if ($(this).is(':checked')) {
-																		if (data.amsCheckerMode == 'disable')
-																			fieldset.removeAttr('disabled')
-																		else
-																			fieldset.removeClass('switched');
-																		legend.trigger('ams.checker.opened', [legend]);
-																	} else {
-																		if (data.amsCheckerMode == 'disable')
-																			fieldset.attr('disabled', 'disabled');
-																		else
-																			fieldset.addClass('switched');
-																		legend.trigger('ams.checker.closed', [legend]);
-																	}
-																})
-																.appendTo(checker);
+																.val(data.amsCheckerState == 'on');
+						if (data.amsCheckerReadonly) {
+							input.attr('disabled', 'disabled');
+						} else {
+							input.on('change', function(e) {
+								e.preventDefault();
+								var veto = {};
+								legend.trigger('ams.checker.before-switch', [legend, veto]);
+								if (veto.veto)
+									return;
+								if ($(this).is(':checked')) {
+									if (data.amsCheckerMode == 'disable')
+										fieldset.removeAttr('disabled')
+									else
+										fieldset.removeClass('switched');
+									$('[data-required]', fieldset).attr('required', 'required');
+									legend.trigger('ams.checker.opened', [legend]);
+								} else {
+									if (data.amsCheckerMode == 'disable')
+										fieldset.attr('disabled', 'disabled');
+									else
+										fieldset.addClass('switched');
+									$('[data-required]', fieldset).removeAttr('required');
+									legend.trigger('ams.checker.closed', [legend]);
+								}
+							});
+						}
+						input.appendTo(checker);
 						$('.legend', legend).attr('for', input.attr('id'));
 						checker.append('<i></i>')
 							   .prependTo(legend);
-						if (data.amsCheckerState == 'on')
+						var required = $('[required]', fieldset);
+						required.attr('data-required', true);
+						if (data.amsCheckerState == 'on') {
 							input.attr('checked', true);
-						else {
+						} else {
 							if (data.amsCheckerMode == 'disable')
 								fieldset.attr('disabled', 'disabled');
 							else
 								fieldset.addClass('switched');
+							required.removeAttr('required');
 						}
 						legend.data('ams-checker', 'on');
 					}
@@ -2737,7 +2844,7 @@
 					updateActiveMenus(menu);
 				ams.skin.loadURL(url, container);
 				document.title = $('[data-ams-page-title]:first', container).data('ams-page-title') ||
-								 $('nav A[href="' + hash + '"]').attr('title') ||
+								 menu.attr('title') ||
 								 document.title;
 			} else {
 				var active_url = $('[data-ams-active-menu]').data('ams-active-menu');
@@ -2822,6 +2929,31 @@
 			$.ajax(settings);
 		},
 
+		/**
+		 * Change user language
+		 */
+		setLanguage: function(options) {
+			var lang = options.lang;
+			var handler_type = options.handler_type || 'json';
+			switch (handler_type) {
+				case 'json':
+					var method = options.method || 'setUserLanguage';
+					ams.jsonrpc.post(method, {lang: lang}, function() {
+						window.location.reload(true);
+					});
+					break;
+				case 'ajax':
+					var href = options.href || 'setUserLanguage';
+					ams.ajax.post(href, {lang: lang}, function() {
+						window.location.reload(true);
+					});
+					break;
+			}
+		},
+
+		/**
+		 * Go to logout page
+		 */
 		logout: function() {
 			window.location = ams.loginURL;
 		}
@@ -2840,6 +2972,21 @@
 		ams.shortcuts = $('#shortcut');
 
 		// Init main AJAX events
+		var jquery_xhr = $.ajaxSettings.xhr;
+		$.ajaxSetup({
+			progress: ams.ajax.progress,
+			progressUpload: ams.ajax.progress,
+			xhr: function() {
+				var request = jquery_xhr();
+				if (request && (typeof(request.addEventListener) == "function")) {
+					var that = this;
+					request.addEventListener("progress", function(evt) {
+						that.progress(evt);
+					}, false);
+				}
+				return request;
+			}
+		});
 		$(document).ajaxError(ams.error.ajax);
 
 		// Check mobile/desktop
@@ -3004,22 +3151,30 @@
 				if (!href || href.startsWith('javascript:') || link.attr('target'))
 					return;
 				e.preventDefault();
+				var href_getter = ams.getFunctionByName(href);
+				if (typeof(href_getter) == 'function') {
+					href = href_getter.call(link);
+				}
 				var target = link.data('ams-target');
 				if (target) {
-					ams.skin.loadURL(href, target, link.data('ams-link-options'), link.data('ams-link-callback'));
+					ams.form.confirmChangedForm(target, function() {
+						ams.skin.loadURL(href, target, link.data('ams-link-options'), link.data('ams-link-callback'));
+					});
 				} else {
-					if (href.startsWith('#')) {
-						if (href != location.hash) {
-							if (ams.root.hasClass('mobile-view-activated')) {
-								ams.root.removeClass('hidden-menu');
-								window.setTimeout(function() {
+					ams.form.confirmChangedForm(function() {
+						if (href.startsWith('#')) {
+							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
 									window.location.hash = href;
-								}, 150);
-							} else
-								window.location.hash = href;
-						}
-					} else
-						window.location = href;
+							}
+						} else
+							window.location = href;
+					});
 				}
 			});
 			$(document).on('click', 'a[target="_blank"]', function(e) {
@@ -3028,7 +3183,9 @@
 			});
 			$(document).on('click', 'a[target="_top"]', function(e) {
 				e.preventDefault();
-				window.location = $(e.currentTarget).attr('href');
+				ams.form.confirmChangedForm(function() {
+					window.location = $(e.currentTarget).attr('href');
+				});
 			});
 
 			// Check URL when hash changed
@@ -3057,6 +3214,8 @@
 			var source = $(this);
 			var data = source.data();
 			if (data.amsClickHandler) {
+				if (data.amsClickStopPropagation === true)
+					e.stopPropagation();
 				if (data.amsClickKeepDefault !== true)
 					e.preventDefault();
 				var callback = ams.getFunctionByName(data.amsClickHandler);
@@ -3078,6 +3237,19 @@
 			}
 		});
 
+		// Initialize custom reset handlers
+		$(document).on('reset', '[data-ams-reset-handler]', function(e) {
+			var form = $(this);
+			var data = form.data();
+			if (data.amsResetHandler) {
+				if (data.amsResetKeepDefault !== true)
+					e.preventDefault();
+				var callback = ams.getFunctionByName(data.amsResetHandler);
+				if (callback !== undefined)
+					callback.call(form, data.amsResetHandlerOptions);
+			}
+		});
+
 		// Handle update on file upload placeholder
 		$(document).on('change', 'input[type="file"]', function(e) {
 			e.preventDefault();
@@ -3117,6 +3289,9 @@
 		if ((window.location.hash == '') || (ams.getQueryVar(window.location.href, 'came_from') != false))
 			ams.initContent(document);
 
+		// Add unload event listener to check for modified forms
+		$(window).on('beforeunload', ams.form.checkBeforeUnload);
+
 	};
 
 
@@ -3143,6 +3318,7 @@
 		ams.plugins.init(element);
 		ams.callbacks.init(element);
 		ams.events.init(element);
+		ams.form.init(element);
 
 		// Initialize widgets
 		if (ams.device === 'desktop')
@@ -3183,12 +3359,14 @@
 		BTN_CANCEL: "Cancel",
 		BTN_YES: "Yes",
 		BTN_NO: "No",
+		BTN_OK_CANCEL: "[OK][Cancel]",
 
+		FORM_CHANGED_WARNING: "Some changes were not saved. These updates will be lost if you leave this page.",
 		NO_UPDATE: "No changes were applied.",
 		DATA_UPDATED: "Data successfully updated.",
 
 		HOME: "Home",
-		LOGOUT: "Logout ?",
+		LOGOUT: "Logout?",
 		LOGOUT_COMMENT: "You can improve your security further after logging out by closing this opened browser",
 
 		SELECT2_PLURAL: 's',