src/ztfy/myams/resources/js/myams.js
changeset 0 8a19e25e39e4
child 3 8f1aadf54f1b
equal deleted inserted replaced
-1:000000000000 0:8a19e25e39e4
       
     1 /*
       
     2  * MyAMS
       
     3  * « My Application Management Skin »
       
     4  *
       
     5  * $Tag: $
       
     6  * A bootstrap based application/administration skin
       
     7  *
       
     8  * Custom administration and application skin tools
       
     9  * Released under Zope Public License ZPL 1.1
       
    10  * ©2014 Thierry Florac <tflorac@ulthar.net>
       
    11  */
       
    12 
       
    13 (function($) {
       
    14 
       
    15 	/**
       
    16 	 * String prototype extensions
       
    17 	 */
       
    18 	String.prototype.startsWith = function(str) {
       
    19 		var slen = this.length;
       
    20 		var dlen = str.length;
       
    21 		if (slen < dlen) {
       
    22 			return false;
       
    23 		}
       
    24 		return (this.substr(0,dlen) == str);
       
    25 	};
       
    26 
       
    27 	String.prototype.endsWith = function(str) {
       
    28 		var slen = this.length;
       
    29 		var dlen = str.length;
       
    30 		if (slen < dlen) {
       
    31 			return false;
       
    32 		}
       
    33 		return (this.substr(slen-dlen) == str);
       
    34 	};
       
    35 
       
    36 
       
    37 	/**
       
    38 	 * Array prototype extensions
       
    39 	 */
       
    40 	if (!Array.prototype.indexOf) {
       
    41 		Array.prototype.indexOf = function(elt /*, from*/) {
       
    42 			var len = this.length;
       
    43 
       
    44 			var from = Number(arguments[1]) || 0;
       
    45 			from = (from < 0) ? Math.ceil(from) : Math.floor(from);
       
    46 			if (from < 0)
       
    47 				from += len;
       
    48 
       
    49 			for (; from < len; from++) {
       
    50 				if (from in this &&
       
    51 					this[from] === elt)
       
    52 					return from;
       
    53 			}
       
    54 			return -1;
       
    55 		};
       
    56 	}
       
    57 
       
    58 
       
    59 	/**
       
    60 	 * JQuery 'econtains' expression
       
    61 	 * Case insensitive contains expression
       
    62 	 */
       
    63 	$.expr[":"].econtains = function(obj, index, meta, stack) {
       
    64 		return (obj.textContent || obj.innerText || $(obj).text() || "").toLowerCase() == meta[3].toLowerCase();
       
    65 	};
       
    66 
       
    67 
       
    68 	/**
       
    69 	 * JQuery 'withtext' expression
       
    70 	 * Case sensitive exact search expression
       
    71 	 */
       
    72 	$.expr[":"].withtext = function(obj, index, meta, stack) {
       
    73 		return (obj.textContent || obj.innerText || $(obj).text() || "") == meta[3];
       
    74 	};
       
    75 
       
    76 
       
    77 	/**
       
    78 	 * JQuery filter on parents class
       
    79 	 */
       
    80 	$.expr[':'].parents = function(obj, index, meta, stack) {
       
    81 		return $(obj).parents(meta[3]).length > 0;
       
    82 	};
       
    83 
       
    84 
       
    85 	/**
       
    86 	 * JQuery 'scrollbarWidth' function
       
    87 	 * Get width of vertical scrollbar
       
    88 	 */
       
    89 	if ($.scrollbarWidth === undefined) {
       
    90 		$.scrollbarWidth = function() {
       
    91 			var parent,
       
    92 				child,
       
    93 				width;
       
    94 			if (width === undefined) {
       
    95 				parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body');
       
    96 				child = parent.children();
       
    97 				width = child.innerWidth() - child.height(99).innerWidth();
       
    98 				parent.remove();
       
    99 			}
       
   100 			return width;
       
   101 		};
       
   102 	}
       
   103 
       
   104 
       
   105 	/**
       
   106 	 * MyAMS JQuery extensions
       
   107 	 */
       
   108 	$.fn.extend({
       
   109 
       
   110 		exists: function() {
       
   111 			return $(this).length > 0;
       
   112 		},
       
   113 
       
   114 		/*
       
   115 		 * CSS style function
       
   116 		 * Code from Aram Kocharyan on stackoverflow.com
       
   117 		 */
       
   118 		style: function(styleName, value, priority) {
       
   119 			// DOM node
       
   120 			var node = this.get(0);
       
   121 			// Ensure we have a DOM node
       
   122 			if (typeof node == 'undefined') {
       
   123 				return;
       
   124 			}
       
   125 			// CSSStyleDeclaration
       
   126 			var style = this.get(0).style;
       
   127 			// Getter/Setter
       
   128 			if (typeof styleName != 'undefined') {
       
   129 				if (typeof value != 'undefined') {
       
   130 					// Set style property
       
   131 					priority = typeof priority != 'undefined' ? priority : '';
       
   132 					style.setProperty(styleName, value, priority);
       
   133 					return this;
       
   134 				} else {
       
   135 					// Get style property
       
   136 					return style.getPropertyValue(styleName);
       
   137 				}
       
   138 			} else {
       
   139 				// Get CSSStyleDeclaration
       
   140 				return style;
       
   141 			}
       
   142 		},
       
   143 
       
   144 		removeClassPrefix: function (prefix) {
       
   145 			this.each(function (i, it) {
       
   146 				var classes = it.className.split(" ").map(function(item) {
       
   147 					return item.startsWith(prefix) ? "" : item
       
   148 				});
       
   149 				it.className = $.trim(classes.join(" "))
       
   150 			});
       
   151 			return this;
       
   152 		},
       
   153 
       
   154 		myams_menu: function(options) {
       
   155 			// Extend our default options with those provided
       
   156 			var defaults = {
       
   157 				accordion : 'true',
       
   158 				speed : 200,
       
   159 				closedSign : '<em class="fa fa-angle-down"></em>',
       
   160 				openedSign : '<em class="fa fa-angle-up"></em>'
       
   161 			};
       
   162 			var settings = $.extend({}, defaults, options);
       
   163 
       
   164 			// Assign current element to variable, in this case is UL element
       
   165 			var menu = $(this);
       
   166 
       
   167 			// Add a mark [+] to a multilevel menu
       
   168 			menu.find("LI").each(function() {
       
   169 				var menu_item = $(this);
       
   170 				if (menu_item.find("UL").size() > 0) {
       
   171 
       
   172 					// add the multilevel sign next to the link
       
   173 					menu_item.find("A:first")
       
   174 							 .append("<b class='collapse-sign'>" + settings.closedSign + "</b>");
       
   175 
       
   176 					// avoid jumping to the top of the page when the href is an #
       
   177 					var first_link = menu_item.find("A:first");
       
   178 					if (first_link.attr('href') == "#") {
       
   179 						first_link.click(function() {
       
   180 							return false;
       
   181 						});
       
   182 					}
       
   183 				}
       
   184 			});
       
   185 
       
   186 			// Open active level
       
   187 			menu.find("LI.active").each(function() {
       
   188 				var active_parent = $(this).parents('UL');
       
   189 				var active_item = active_parent.parent('LI');
       
   190 				active_parent.slideDown(settings.speed);
       
   191 				active_item.find("b:first").html(settings.openedSign);
       
   192 				active_item.addClass("open")
       
   193 			});
       
   194 
       
   195 			menu.find("LI A").on('click', function() {
       
   196 				var link = $(this);
       
   197 				var parent_ul = link.parent().find("UL");
       
   198 				if (parent_ul.size() != 0) {
       
   199 					if (settings.accordion) {
       
   200 						// Do nothing when the list is open
       
   201 						if (!parent_ul.is(':visible')) {
       
   202 							var parents = link.parent().parents("UL");
       
   203 							var visible = menu.find("UL:visible");
       
   204 							visible.each(function(visibleIndex) {
       
   205 								var close = true;
       
   206 								parents.each(function(parentIndex) {
       
   207 									if (parents[parentIndex] == visible[visibleIndex]) {
       
   208 										close = false;
       
   209 										return false;
       
   210 									}
       
   211 								});
       
   212 								if (close) {
       
   213 									if (parent_ul != visible[visibleIndex]) {
       
   214 										$(visible[visibleIndex]).slideUp(settings.speed, function() {
       
   215 											link.parent("LI")
       
   216 												.find("b:first")
       
   217 												.html(settings.closedSign);
       
   218 											link.parent("LI")
       
   219 												.removeClass("open");
       
   220 										});
       
   221 									}
       
   222 								}
       
   223 							});
       
   224 						}
       
   225 					}
       
   226 					var first_ul = link.parent().find("UL:first");
       
   227 					if (!link.attr('href').replace(/^#/,'') &&
       
   228 						first_ul.is(":visible") &&
       
   229 						!first_ul.hasClass("active")) {
       
   230 						first_ul.slideUp(settings.speed, function() {
       
   231 							link.parent("LI")
       
   232 								.removeClass("open")
       
   233 								.find("B:first")
       
   234 								.delay(settings.speed)
       
   235 								.html(settings.closedSign);
       
   236 						});
       
   237 					} else /*if (link.attr('href') != location.hash)*/ {
       
   238 						first_ul.slideDown(settings.speed, function() {
       
   239 							link.parent("LI")
       
   240 								.addClass("open")
       
   241 								.find("B:first")
       
   242 								.delay(settings.speed)
       
   243 								.html(settings.openedSign);
       
   244 						});
       
   245 					}
       
   246 				}
       
   247 			});
       
   248 		}
       
   249 	});
       
   250 
       
   251 
       
   252 	/**
       
   253 	 * UTF-8 encoding class
       
   254 	 * Mainly used by IE...
       
   255 	 */
       
   256 	$.UTF8 = {
       
   257 
       
   258 		// public method for url encoding
       
   259 		encode : function (string) {
       
   260 			string = string.replace(/\r\n/g,"\n");
       
   261 			var utftext = "";
       
   262 
       
   263 			for (var n = 0; n < string.length; n++) {
       
   264 
       
   265 				var c = string.charCodeAt(n);
       
   266 
       
   267 				if (c < 128) {
       
   268 					utftext += String.fromCharCode(c);
       
   269 				}
       
   270 				else if((c > 127) && (c < 2048)) {
       
   271 					utftext += String.fromCharCode((c >> 6) | 192);
       
   272 					utftext += String.fromCharCode((c & 63) | 128);
       
   273 				}
       
   274 				else {
       
   275 					utftext += String.fromCharCode((c >> 12) | 224);
       
   276 					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
       
   277 					utftext += String.fromCharCode((c & 63) | 128);
       
   278 				}
       
   279 			}
       
   280 			return utftext;
       
   281 		},
       
   282 
       
   283 		// public method for url decoding
       
   284 		decode : function (utftext) {
       
   285 			var string = "";
       
   286 			var i = 0;
       
   287 			var c = c1 = c2 = 0;
       
   288 
       
   289 			while ( i < utftext.length ) {
       
   290 
       
   291 				c = utftext.charCodeAt(i);
       
   292 
       
   293 				if (c < 128) {
       
   294 					string += String.fromCharCode(c);
       
   295 					i++;
       
   296 				}
       
   297 				else if((c > 191) && (c < 224)) {
       
   298 					c2 = utftext.charCodeAt(i+1);
       
   299 					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
       
   300 					i += 2;
       
   301 				}
       
   302 				else {
       
   303 					c2 = utftext.charCodeAt(i+1);
       
   304 					c3 = utftext.charCodeAt(i+2);
       
   305 					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
       
   306 					i += 3;
       
   307 				}
       
   308 			}
       
   309 			return string;
       
   310 		}
       
   311 	}; /** $.UTF8 */
       
   312 
       
   313 
       
   314 	/**
       
   315 	 * MyAMS extensions to JQuery
       
   316 	 */
       
   317 	if (window.MyAMS === undefined) {
       
   318 		MyAMS = {
       
   319 			devmode: true,
       
   320 			throttle_delay: 350,
       
   321 			menu_speed: 235,
       
   322 			navbar_height: 49,
       
   323 			ajax_nav: true,
       
   324 			enable_widgets: true,
       
   325 			enable_mobile: false,
       
   326 			enable_fastclick: false,
       
   327 			ismobile: (/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase()))
       
   328 		};
       
   329 	}
       
   330 	var ams = MyAMS;
       
   331 
       
   332 	/**
       
   333 	 * Get MyAMS base URL
       
   334 	 * Copyright Andrew Davy: https://forrst.com/posts/Get_the_URL_of_the_current_javascript_file-Dst
       
   335 	 */
       
   336 	MyAMS.baseURL = (function () {
       
   337 		var script = $("script:last");
       
   338 		var src = script.attr("src");
       
   339 		return src.substring(0, src.lastIndexOf('/') + 1);
       
   340 	})();
       
   341 
       
   342 
       
   343 	/**
       
   344 	 * Extract parameter value from given query string
       
   345 	 */
       
   346 	MyAMS.getQueryVar = function(src, varName) {
       
   347 		// Check src
       
   348 		if (src.indexOf('?') < 0)
       
   349 			return false;
       
   350 		if (!src.endsWith('&'))
       
   351 			src += '&';
       
   352 		// Dynamic replacement RegExp
       
   353 		var regex = new RegExp('.*?[&\\?]' + varName + '=(.*?)&.*');
       
   354 		// Apply RegExp to the query string
       
   355 		var val = src.replace(regex, "$1");
       
   356 		// If the string is the same, we didn't find a match - return false
       
   357 		return val == src ? false : val;
       
   358 	};
       
   359 
       
   360 
       
   361 	/**
       
   362 	 * Color conversion function
       
   363 	 */
       
   364 	MyAMS.rgb2hex = function(color) {
       
   365 		return "#" + $.map(color.match(/\b(\d+)\b/g), function(digit) {
       
   366 			return ('0' + parseInt(digit).toString(16)).slice(-2)
       
   367 		}).join('');
       
   368 	};
       
   369 
       
   370 
       
   371 	/**
       
   372 	 * Get and execute a function given by name
       
   373 	 * Small piece of code by Jason Bunting
       
   374 	 */
       
   375 	MyAMS.getFunctionByName = function(functionName, context) {
       
   376 		if (functionName === undefined)
       
   377 			return undefined;
       
   378 		else if (typeof(functionName) == 'function')
       
   379 			return functionName;
       
   380 		var namespaces = functionName.split(".");
       
   381 		var func = namespaces.pop();
       
   382 		var context = (context === undefined || context === null) ? window : context;
       
   383 		for (var i=0; i < namespaces.length; i++) {
       
   384 			try {
       
   385 				context = context[namespaces[i]];
       
   386 			} catch (e) {
       
   387 				return undefined;
       
   388 			}
       
   389 		}
       
   390 		try {
       
   391 			return context[func];
       
   392 		} catch (e) {
       
   393 			return undefined;
       
   394 		}
       
   395 	};
       
   396 
       
   397 	MyAMS.executeFunctionByName = function(functionName, context /*, args */) {
       
   398 		var func = ams.getFunctionByName(functionName, window);
       
   399 		if (typeof(func) == 'function') {
       
   400 			var args = Array.prototype.slice.call(arguments, 2);
       
   401 			return func.apply(context, args);
       
   402 		}
       
   403 	};
       
   404 
       
   405 
       
   406 	/**
       
   407 	 * Get script or CSS file using browser cache
       
   408 	 * Script or CSS URLs can include variable names, given between braces, as in
       
   409 	 * {MyAMS.baseURL}
       
   410 	 */
       
   411 	MyAMS.getSource = function(url) {
       
   412 		var src = url.replace(/{[^{}]*}/g, function(match) {
       
   413 			return ams.getFunctionByName(match.substr(1, match.length-2));
       
   414 		});
       
   415 		return src;
       
   416 	};
       
   417 
       
   418 	MyAMS.getScript = function(url, callback, options) {
       
   419 		var defaults = {
       
   420 			dataType: 'script',
       
   421 			url: ams.getSource(url),
       
   422 			success: callback,
       
   423 			error: ams.error.show,
       
   424 			cache: true,
       
   425 			async: true
       
   426 		};
       
   427 		var settings = $.extend({}, defaults, options);
       
   428 		return $.ajax(settings);
       
   429 	};
       
   430 
       
   431 	MyAMS.getCSS = function(url, id) {
       
   432 		var head = $('HEAD');
       
   433 		var css = $('link[data-ams-id="' + id + '"]', head);
       
   434 		if (css.length == 0) {
       
   435 			$('<link />').attr({rel: 'stylesheet',
       
   436 								type: 'text/css',
       
   437 								href: ams.getSource(url),
       
   438 								'data-ams-id': id})
       
   439 						 .appendTo(head);
       
   440 		}
       
   441 	};
       
   442 
       
   443 
       
   444 	/**
       
   445 	 * Events management
       
   446 	 */
       
   447 	MyAMS.event = {
       
   448 
       
   449 		stop: function(event) {
       
   450 			if (!event) {
       
   451 				var event = window.event;
       
   452 			}
       
   453 			if (event) {
       
   454 				if (event.stopPropagation) {
       
   455 					event.stopPropagation();
       
   456 					event.preventDefault();
       
   457 				} else {
       
   458 					event.cancelBubble = true;
       
   459 					event.returnValue = false;
       
   460 				}
       
   461 			}
       
   462 		}
       
   463 	};
       
   464 
       
   465 
       
   466 	/**
       
   467 	 * Browser testing functions; mostly for IE...
       
   468 	 */
       
   469 	MyAMS.browser = {
       
   470 
       
   471 		getInternetExplorerVersion: function() {
       
   472 			var rv = -1;
       
   473 			if (navigator.appName == "Microsoft Internet Explorer") {
       
   474 				var ua = navigator.userAgent;
       
   475 				var re = new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");
       
   476 				if (re.exec(ua) != null)
       
   477 					rv = parseFloat(RegExp.$1);
       
   478 			}
       
   479 			return rv;
       
   480 		},
       
   481 
       
   482 		checkVersion: function() {
       
   483 			var msg = "You're not using Windows Internet Explorer.";
       
   484 			var ver = this.getInternetExplorerVersion();
       
   485 			if (ver > -1) {
       
   486 				if (ver >= 8)
       
   487 					msg = "You're using a recent copy of Windows Internet Explorer.";
       
   488 				else
       
   489 					msg = "You should upgrade your copy of Windows Internet Explorer.";
       
   490 			}
       
   491 			alert(msg);
       
   492 		},
       
   493 
       
   494 		isIE8orlower: function() {
       
   495 			var msg = "0";
       
   496 			var ver = this.getInternetExplorerVersion();
       
   497 			if (ver > -1) {
       
   498 				if (ver >= 9)
       
   499 					msg = 0;
       
   500 				else
       
   501 					msg = 1;
       
   502 			}
       
   503 			return msg;
       
   504 		}
       
   505 	};
       
   506 
       
   507 
       
   508 	/**
       
   509 	 * Errors management features
       
   510 	 */
       
   511 	MyAMS.error = {
       
   512 
       
   513 		/**
       
   514 		 * Default JQuery AJAX error handler
       
   515 		 */
       
   516 		ajax: function(event, request, settings) {
       
   517 			if (request.statusText == 'OK')
       
   518 				return;
       
   519 			ams.skin.messageBox('error', {
       
   520 				title: ams.i18n.ERROR_OCCURED,
       
   521 				content: '<h4>' + event.type + '</h4><p>' + request.statusText + '</p>',
       
   522 				icon: 'fa fa-warning animated shake',
       
   523 				timeout: 10000
       
   524 			});
       
   525 			if (window.console) {
       
   526 				console.error(event);
       
   527 				console.debug(request);
       
   528 			}
       
   529 		},
       
   530 
       
   531 		/**
       
   532 		 * Show AJAX error
       
   533 		 */
       
   534 		show: function(request, status, error) {
       
   535 			if (!error)
       
   536 				return;
       
   537 			ams.skin.messageBox('error', {
       
   538 				title: ams.i18n.ERRORS_OCCURED,
       
   539 				content: '<h4>' + status + '</h4><p>' + error + '</p>',
       
   540 				icon: "fa fa-warning animated shake",
       
   541 				timeout: 10000
       
   542 			});
       
   543 			if (window.console) {
       
   544 				console.error(error);
       
   545 				console.debug(request);
       
   546 			}
       
   547 		}
       
   548 	};
       
   549 
       
   550 
       
   551 	/**
       
   552 	 * AJAX helper functions
       
   553 	 */
       
   554 	MyAMS.ajax = {
       
   555 
       
   556 		/**
       
   557 		 * Check for given feature and download script if necessary
       
   558 		 *
       
   559 		 * @checker: pointer to a javascript object which will be downloaded in undefined
       
   560 		 * @source: URL of a javascript file containing requested feature
       
   561 		 * @callback: pointer to a function which will be called after the script is downloaded. The first
       
   562 		 *   argument of this callback is a boolean value indicating if the script was just downloaded (true)
       
   563 		 *   or if the requested object was already loaded (false)
       
   564 		 * @options: callback options
       
   565 		 */
       
   566 		check: function(checker, source, callback, options) {
       
   567 			if (typeof(callback) == 'object') {
       
   568 				var options = callback;
       
   569 				var callback = undefined;
       
   570 			}
       
   571 			var defaults = {
       
   572 				async: typeof(callback) == 'function'
       
   573 			};
       
   574 			var settings = $.extend({}, defaults, options);
       
   575 			if (checker === undefined) {
       
   576 				ams.getScript(source, function() {
       
   577 					if (typeof(callback) == 'function')
       
   578 						callback(true, options);
       
   579 				}, settings);
       
   580 			} else {
       
   581 				if (typeof(callback) == 'function')
       
   582 					callback(false, options);
       
   583 			}
       
   584 		},
       
   585 
       
   586 		/**
       
   587 		 * Get address relative to current page
       
   588 		 */
       
   589 		getAddr: function(addr) {
       
   590 			var href = addr || $('HTML HEAD BASE').attr('href') || window.location.href;
       
   591 			return href.substr(0, href.lastIndexOf("/") + 1);
       
   592 		},
       
   593 
       
   594 		/**
       
   595 		 * Post data to given URL
       
   596 		 */
       
   597 		post: function(url, data, options, callback) {
       
   598 			if (url.startsWith(window.location.protocol))
       
   599 				var addr = url;
       
   600 			else
       
   601 				addr = this.getAddr() + url;
       
   602 			if (typeof(options) == 'function') {
       
   603 				callback = options;
       
   604 				options = {}
       
   605 			} else if (!options) {
       
   606 				options = {};
       
   607 			}
       
   608 			if (typeof(callback) == 'undefined')
       
   609 				callback = options.callback;
       
   610 			if (typeof(callback) == 'string')
       
   611 				callback = ams.getFunctionByName(callback);
       
   612 			delete options.callback;
       
   613 
       
   614 			var result;
       
   615 			var defaults = {
       
   616 				url: addr,
       
   617 				type: 'post',
       
   618 				cache: false,
       
   619 				async: typeof(callback) == 'function',
       
   620 				data: $.param(data, true),
       
   621 				dataType: 'json',
       
   622 				success: callback || function(data, status) {
       
   623 					result = data.result;
       
   624 				},
       
   625 				error: ams.error.show
       
   626 			};
       
   627 			var settings = $.extend({}, defaults, options);
       
   628 			$.ajax(settings);
       
   629 			return result;
       
   630 		},
       
   631 
       
   632 		/**
       
   633 		 * Extract data type and result from response
       
   634 		 */
       
   635 		getResponse: function(request) {
       
   636 			var content_type = request.getResponseHeader('content-type');
       
   637 			if (content_type.startsWith('application/javascript')) {
       
   638 				data_type = 'script';
       
   639 				result = request.responseText;
       
   640 			} else if (content_type.startsWith('text/html')) {
       
   641 				data_type = 'html';
       
   642 				result = request.responseText;
       
   643 			} else if (content_type.startsWith('text/xml')) {
       
   644 				data_type = 'xml';
       
   645 				result = request.responseText;
       
   646 			} else {
       
   647 				result = request.responseJSON;
       
   648 				if (result)
       
   649 					data_type = 'json';
       
   650 				else {
       
   651 					try {
       
   652 						result = JSON.parse(request.responseText);
       
   653 						data_type = 'json';
       
   654 					} catch (e) {
       
   655 						result = request.responseText;
       
   656 						data_type = 'text';
       
   657 					}
       
   658 				}
       
   659 			}
       
   660 			return {content_type: data_type,
       
   661 					data: result};
       
   662 		},
       
   663 
       
   664 		/**
       
   665 		 * Handle server response in JSON format
       
   666 		 *
       
   667 		 * Result is made of several JSON attributes:
       
   668 		 *  - status: error, success, callback, callbacks, reload or redirect
       
   669 		 *  - close_form: boolean indicating if current modal should be closed
       
   670 		 *  - location: target URL for reload or redirect status
       
   671 		 *  - target: target container's selector for loaded content ('#content' by default)
       
   672 		 *  - content: available for any status producing output content:
       
   673 		 *        {target: target container's selector (source form by default)
       
   674 		 *         html: HTML result}
       
   675 		 *  - message: available for any status producing output message:
       
   676 		 *        {target: target message container's selector
       
   677 		 *         status: message status
       
   678 		 *         header: message header
       
   679 		 *         subtitle: message subtitle,
       
   680 		 *         body: message body}
       
   681 		 *
       
   682 		 * For errors data structure, please see MyAMS.form.showErrors function
       
   683 		 */
       
   684 		handleJSON: function(result, form) {
       
   685 			var status = result.status;
       
   686 			switch (status) {
       
   687 				case 'error':
       
   688 					ams.form.showErrors(form, result);
       
   689 					break;
       
   690 				case 'success':
       
   691 					if (result.close_form != false)
       
   692 						ams.dialog.close(form);
       
   693 					break;
       
   694 				case 'message':
       
   695 				case 'messagebox':
       
   696 					break;
       
   697 				case 'callback':
       
   698 				case 'callbacks':
       
   699 					if (result.close_form != false)
       
   700 						ams.dialog.close(form);
       
   701 					break;
       
   702 				case 'modal':
       
   703 					ams.dialog.open(result.location);
       
   704 					break;
       
   705 				case 'reload':
       
   706 					if (result.close_form != false)
       
   707 						ams.dialog.close(form);
       
   708 					var url = result.location;
       
   709 					if (url.startsWith('#'))
       
   710 						ams.skin.loadURL(url.substr(1), result.target || '#content');
       
   711 					else
       
   712 						ams.skin.loadURL(url, result.target || '#content');
       
   713 					break;
       
   714 				case 'redirect':
       
   715 					var url = result.location;
       
   716 					if (result.window) {
       
   717 						window.open(url, result.window, result.options);
       
   718 					} else {
       
   719 						window.location.href = url;
       
   720 					}
       
   721 					break;
       
   722 				default:
       
   723 					console.log("Unhandled status: " + status);
       
   724 					break;
       
   725 			}
       
   726 			if (result.content) {
       
   727 				var content = result.content;
       
   728 				var container = $(content.target || form || '#content');
       
   729 				container.html(content.html);
       
   730 				ams.initContent(container);
       
   731 			}
       
   732 			if (result.message) {
       
   733 				var message = result.message;
       
   734 				if (typeof(message) == 'string')
       
   735 					ams.skin.alert($(form || '#content'),
       
   736 								   status, '', message);
       
   737 				else
       
   738 					ams.skin.alert($(message.target || form || '#content'),
       
   739 								   message.status || 'success',
       
   740 								   message.header,
       
   741 								   message.body,
       
   742 								   message.subtitle);
       
   743 			}
       
   744 			if (result.messagebox) {
       
   745 				message = result.messagebox;
       
   746 				if (typeof(message) == 'string')
       
   747 					ams.skin.messageBox('info',
       
   748 										{title: ams.i18n.ERROR_OCCURED,
       
   749 										 content: message,
       
   750 										 timeout: 10000});
       
   751 				else
       
   752 					ams.skin.messageBox(message.status || 'info',
       
   753 										{title: message.title || ams.i18n.ERROR_OCCURED,
       
   754 										 content: message.content,
       
   755 										 icon: message.icon,
       
   756 										 number: message.number,
       
   757 										 timeout: message.timeout || 10000});
       
   758 			}
       
   759 			if (result.callback)
       
   760 				ams.executeFunctionByName(result.callback, form, result.options);
       
   761 			if (result.callbacks) {
       
   762 				for (var index in result.callbacks) {
       
   763 					if (!$.isNumeric(index))
       
   764 						continue;
       
   765 					var callback = result.callbacks[index];
       
   766 					ams.executeFunctionByName(callback, form, callback.options);
       
   767 				}
       
   768 			}
       
   769 		}
       
   770 	};
       
   771 
       
   772 
       
   773 	/**
       
   774 	 * JSON-RPC helper functions
       
   775 	 */
       
   776 	MyAMS.jsonrpc = {
       
   777 
       
   778 		/**
       
   779 		 * Get address relative to current page
       
   780 		 */
       
   781 		getAddr: function(addr) {
       
   782 			var href = addr || $('HTML HEAD BASE').attr('href') || window.location.href;
       
   783 			var target = href.replace(/\+\+skin\+\+\w+\//, '');
       
   784 			return target.substr(0, target.lastIndexOf("/") + 1);
       
   785 		},
       
   786 
       
   787 		/**
       
   788 		 * Execute JSON-RPC request on given method
       
   789 		 *
       
   790 		 * Query can be given as a simple "query" string or as an object containing all query parameters.
       
   791 		 * Parameters:
       
   792 		 *  - @query: query string (posted as "query" parameter) or object containing all parameters
       
   793 		 *  - @method: name of JSON-RPC procedure to call
       
   794 		 *  - @options: additional JSON-RPC procedure parameters
       
   795 		 *  - @callback: name of a callback which will be called on server response
       
   796 		 */
       
   797 		query: function(query, method, options, callback) {
       
   798 			ams.ajax.check($.jsonRpc,
       
   799 						   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
       
   800 						   function() {
       
   801 								var result;
       
   802 								if (typeof(options) == 'function') {
       
   803 									callback = options;
       
   804 									options = {};
       
   805 								}
       
   806 								else if (!options)
       
   807 									options = {};
       
   808 								if (typeof(callback) == 'undefined')
       
   809 									callback = options.callback;
       
   810 								if (typeof(callback) == 'string')
       
   811 									callback = ams.getFunctionByName(callback);
       
   812 								delete options.callback;
       
   813 
       
   814 								var params = {};
       
   815 								if (typeof(query) == 'string')
       
   816 									params['query'] = query;
       
   817 								else if (typeof(query) == 'object')
       
   818 									$.extend(params, query);
       
   819 								$.extend(params, options);
       
   820 
       
   821 								var settings = {
       
   822 									url: ams.jsonrpc.getAddr(options.url),
       
   823 									type: 'post',
       
   824 									cache: false,
       
   825 									method: method,
       
   826 									params: params,
       
   827 									async: typeof(callback) == 'function',
       
   828 									success: callback || function(data, status) {
       
   829 										result = data.result;
       
   830 									},
       
   831 									error: ams.error.show
       
   832 								};
       
   833 								$.jsonRpc(settings);
       
   834 								return result;
       
   835 						   });
       
   836 		},
       
   837 
       
   838 		/**
       
   839 		 * Execute given JSON-RPC post on given method
       
   840 		 *
       
   841 		 * Parameters:
       
   842 		 *  - @method: name of JSON-RPC procedure to call
       
   843 		 *  - @options: additional JSON-RPC procedure parameters
       
   844 		 *  - @callback: name of a callback which will be called on server response
       
   845 		 */
       
   846 		post: function(method, options, callback) {
       
   847 			ams.ajax.check($.jsonRpc,
       
   848 						   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
       
   849 						   function() {
       
   850 								var result;
       
   851 								if (typeof(options) == 'function') {
       
   852 									callback = options;
       
   853 									options = {};
       
   854 								}
       
   855 								else if (!options)
       
   856 									options = {};
       
   857 								if (typeof(callback) == 'undefined')
       
   858 									callback = options.callback;
       
   859 								if (typeof(callback) == 'string')
       
   860 									callback = ams.getFunctionByName(callback);
       
   861 								delete options.callback;
       
   862 
       
   863 								var settings = {
       
   864 									url: ams.jsonrpc.getAddr(options.url),
       
   865 									type: 'post',
       
   866 									cache: false,
       
   867 									method: method,
       
   868 									params: options,
       
   869 									async: typeof(callback) == 'function',
       
   870 									success: callback || function(data, status) {
       
   871 										result = data.result;
       
   872 									},
       
   873 									error: ams.error.show
       
   874 								};
       
   875 								$.jsonRpc(settings);
       
   876 								return result;
       
   877 						   });
       
   878 		}
       
   879 	};
       
   880 
       
   881 
       
   882 	/**
       
   883 	 * Forms helper functions
       
   884 	 */
       
   885 	MyAMS.form = {
       
   886 
       
   887 		/**
       
   888 		 * Submit given form
       
   889 		 */
       
   890 		submit: function(form, handler, submit_options) {
       
   891 			// Check params
       
   892 			var form = $(form);
       
   893 			if (!form.exists())
       
   894 				return false;
       
   895 			if (typeof(handler) == 'object') {
       
   896 				submit_options = handler;
       
   897 				handler = undefined;
       
   898 			}
       
   899 			// Prevent multiple submits of the same form
       
   900 			if (form.data('submitted')) {
       
   901 				ams.skin.messageBox('warning', {
       
   902 					title: ams.i18n.WAIT,
       
   903 					content: ams.i18n.FORM_SUBMITTED,
       
   904 					icon: 'fa fa-save shake animated',
       
   905 					timeout: 5000
       
   906 				});
       
   907 				return false;
       
   908 			}
       
   909 			// Check submit validators
       
   910 			if (!ams.form._checkSubmitValidators(form))
       
   911 				return false;
       
   912 			// Remove remaining status messages
       
   913 			$('.alert, SPAN.state-error', form).remove();
       
   914 			$('.state-error', form).removeClassPrefix('state-');
       
   915 			$(form.data('ams-submit-button')).button('loading');
       
   916 			ams.ajax.check($.fn.ajaxSubmit,
       
   917 						   ams.baseURL + 'ext/jquery-form-3.49' + (ams.devmode ? '.js' : '.min.js'),
       
   918 						   function() {
       
   919 
       
   920 								function _submitAjaxForm(form, options) {
       
   921 
       
   922 									var data = form.data();
       
   923 									var form_options = data.amsFormOptions;
       
   924 
       
   925 									if (submit_options)
       
   926 										var form_data_callback = submit_options.formDataInitCallback;
       
   927 									if (form_data_callback)
       
   928 										delete submit_options.formDataInitCallback;
       
   929 									else
       
   930 										form_data_callback = data.amsFormDataInitCallback;
       
   931 									if (form_data_callback) {
       
   932 										if (typeof(form_data_callback) == 'function')
       
   933 											var form_data = form_data_callback.call(form);
       
   934 										else
       
   935 											form_data = ams.executeFunctionByName(form_data_callback, form);
       
   936 									} else {
       
   937 										form_data = data.amsFormData || {};
       
   938 									}
       
   939 
       
   940 									var action = form.attr('action').replace(/#/, '');
       
   941 									if (action.startsWith(window.location.protocol))
       
   942 										var url = action;
       
   943 									else
       
   944 										url = ams.ajax.getAddr() + action;
       
   945 									var button = $(form.data('ams-submit-button'));
       
   946 									url += handler || button.data('ams-button-handler') || data.amsFormHandler || '';
       
   947 
       
   948 									if (data.amsFormInitSubmitTarget) {
       
   949 										var target = $(button.data('ams-form-submit-target') || data.amsFormSubmitTarget || '#content');
       
   950 										ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmit', form, target);
       
   951 									} else if (!data.amsFormHideSubmitFooter)
       
   952 										ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmitFooter', form);
       
   953 
       
   954 									var hasUpload = typeof(options.uuid) != 'undefined';
       
   955 									if (hasUpload) {
       
   956 										if (url.indexOf('X-Progress-ID') < 0)
       
   957 											url += "?X-Progress-ID=" + options.uuid;
       
   958 										delete options.uuid;
       
   959 									}
       
   960 
       
   961 									var defaults = {
       
   962 										url: url,
       
   963 										type: 'post',
       
   964 										cache: false,
       
   965 										data: form_data,
       
   966 										dataType: data.amsFormDatatype,
       
   967 										beforeSerialize: function(form, options) {
       
   968 											if (typeof(tinyMCE) != 'undefined')
       
   969 												tinyMCE.triggerSave();
       
   970 										},
       
   971 										beforeSubmit: function(data, form, options) {
       
   972 											form.data('submitted', true);
       
   973 										},
       
   974 										error: function(request, status, error) {
       
   975 											ams.error.show(request, status, error);
       
   976 											if (form.is(':visible')) {
       
   977 												var button = form.data('ams-submit-button');
       
   978 												if (button)
       
   979 													button.button('reset');
       
   980 												ams.form.finalizeSubmitFooter.call(form);
       
   981 											}
       
   982 											form.data('submitted', false);
       
   983 										},
       
   984 										success: function(result, status, request, form) {
       
   985 											var callback;
       
   986 											var button = form.data('ams-submit-button');
       
   987 											if (button)
       
   988 												callback = button.data('ams-form-submit-callback');
       
   989 											if (!callback)
       
   990 												callback = ams.getFunctionByName(data.amsFormSubmitCallback) || ams.form._submitCallback;
       
   991 											callback.call(form, result, status, request, form);
       
   992 											if (form.is(':visible') && button)
       
   993 												button.button('reset');
       
   994 											form.data('submitted', false);
       
   995 										},
       
   996 										iframe: hasUpload
       
   997 									}
       
   998 									var settings = $.extend({}, defaults, options, form_options, submit_options);
       
   999 									$(form).ajaxSubmit(settings);
       
  1000 								}
       
  1001 
       
  1002 								var hasUpload = $('INPUT[type="file"]', form).length > 0;
       
  1003 								if (hasUpload) {
       
  1004 									// JQuery-progressbar plug-in must be loaded synchronously!!
       
  1005 									// Otherwise, hidden input fields created by jquery-validate plug-in
       
  1006 									// and matching named buttons will be deleted (on first form submit)
       
  1007 									// before JQuery-form plug-in can get them when submitting the form...
       
  1008 									ams.ajax.check($.progressBar,
       
  1009 												   ams.baseURL + 'ext/jquery-progressbar' + (ams.devmode ? '.js' : '.min.js'));
       
  1010 									var settings = $.extend({}, {
       
  1011 										uuid: $.progressBar.submit(form)
       
  1012 									});
       
  1013 									_submitAjaxForm(form, settings);
       
  1014 								} else
       
  1015 									_submitAjaxForm(form, {});
       
  1016 						   });
       
  1017 			return false;
       
  1018 		},
       
  1019 
       
  1020 		/**
       
  1021 		 * Initialize AJAX submit call
       
  1022 		 *
       
  1023 		 * @param this: the submitted form
       
  1024 		 * @param target: the form submit container target
       
  1025 		 * @param message: the optional message
       
  1026 		 */
       
  1027 		initSubmit: function(target, message) {
       
  1028 			var form = $(this);
       
  1029 			var spin = '<i class="fa fa-3x fa-gear fa-spin"></i>';
       
  1030 			if (!message)
       
  1031 				message = form.data('ams-form-submit-message');
       
  1032 			if (message)
       
  1033 				spin += '<strong>' + message + '</strong>';
       
  1034 			$(target).html('<div class="row margin-20"><div class="text-center">' + spin + '</div></div>');
       
  1035 			$(target).parents('.hidden').removeClass('hidden');
       
  1036 		},
       
  1037 
       
  1038 		/**
       
  1039 		 * Initialize AJAX submit call in form footer
       
  1040 		 *
       
  1041 		 * @param this: the submitted form
       
  1042 		 * @param message: the optional submit message
       
  1043 		 */
       
  1044 		initSubmitFooter: function(message) {
       
  1045 			var form = $(this);
       
  1046 			var spin = '<i class="fa fa-3x fa-gear fa-spin"></i>';
       
  1047 			if (!message)
       
  1048 				message = $(this).data('ams-form-submit-message');
       
  1049 			if (message)
       
  1050 				spin += '<strong class="submit-message align-top padding-left-10 margin-top-10">' + message + '</strong>';
       
  1051 			var footer = $('footer', form);
       
  1052 			form.data('ams-form-footer', footer.html());
       
  1053 			footer.html('<div class="row"><div class="text-center">' + spin + '</div></div>');
       
  1054 		},
       
  1055 
       
  1056 		/**
       
  1057 		 * Finalize AJAX submit call
       
  1058 		 *
       
  1059 		 * @param this: the submitted form
       
  1060 		 * @param target: the form submit container target
       
  1061 		 */
       
  1062 		finalizeSubmitFooter: function(target) {
       
  1063 			var form = $(this);
       
  1064 			var footer = form.data('ams-form-footer');
       
  1065 			if (footer) {
       
  1066 				$('footer', form).html(footer);
       
  1067 				form.removeData('ams-form-footer');
       
  1068 			}
       
  1069 		},
       
  1070 
       
  1071 		/**
       
  1072 		 * Handle AJAX submit results
       
  1073 		 *
       
  1074 		 * Submit results are auto-detected via response content type, except when this content type
       
  1075 		 * is specified into form's data attributes.
       
  1076 		 * Submit response can be of several content types:
       
  1077 		 * - html or text: the response is directly included into a "target" container (#content by default)
       
  1078 		 * - json: a "status" attribute indicates how the request was handled and how the response should be
       
  1079 		 *   treated:
       
  1080 		 *     - error: indicates that an error occured; other response attributes indicate error messages
       
  1081 		 *     - success: basic success, no other action is requested
       
  1082 		 *     - callback: only call given function to handle the result
       
  1083 		 *     - callbacks: only call given set of functions to handle the result
       
  1084 		 *     - reload: page's body should be reloaded from a given URL
       
  1085 		 *     - redirect: redirect browser to given URL
       
  1086 		 *   Each JSON response can also specify an HTML content, a message and a callback (
       
  1087 		 */
       
  1088 		_submitCallback: function(result, status, request, form) {
       
  1089 
       
  1090 			if (form.is(':visible')) {
       
  1091 				ams.form.finalizeSubmitFooter.call(form);
       
  1092 				var button = form.data('ams-submit-button');
       
  1093 				if (button)
       
  1094 					button.button('reset');
       
  1095 			}
       
  1096 			var data = form.data();
       
  1097 			if (data.amsFormDatatype)
       
  1098 				var data_type = data.amsFormDatatype;
       
  1099 			else {
       
  1100 				var request_data = ams.ajax.getResponse(request);
       
  1101 				data_type = request_data.content_type;
       
  1102 				result = request_data.data;
       
  1103 			}
       
  1104 			switch (data_type) {
       
  1105 				case 'json':
       
  1106 					ams.ajax.handleJSON(result, form);
       
  1107 					break;
       
  1108 				case 'script':
       
  1109 					break;
       
  1110 				case 'xml':
       
  1111 					break;
       
  1112 				case 'html':
       
  1113 				case 'text':
       
  1114 				default:
       
  1115 					if (button && (button.data('ams-keep-modal') !== true))
       
  1116 						ams.dialog.close(form);
       
  1117 					if (button)
       
  1118 						var target = $(button.amsFormSubmitTarget || data.amsFormSubmitTarget || '#content');
       
  1119 					else
       
  1120 						target = $(data.amsFormSubmitTarget || '#content');
       
  1121 					if (!target.exists())
       
  1122 						target = $('body');
       
  1123 					target.parents('.hidden').removeClass('hidden');
       
  1124 					$('.alert', target.parents('.alerts-container')).remove();
       
  1125 					target.css({opacity: '0.0'})
       
  1126 						  .html(result)
       
  1127 						  .delay(50)
       
  1128 						  .animate({opacity: '1.0'}, 300);
       
  1129 					ams.initContent(target);
       
  1130 			}
       
  1131 			var callback = request.getResponseHeader('X-AMS-Callback');
       
  1132 			if (callback) {
       
  1133 				var options = request.getResponseHeader('X-AMS-Callback-Options');
       
  1134 				ams.executeFunctionByName(callback, form, options === undefined ? {} : JSON.parse(options));
       
  1135 			}
       
  1136 		},
       
  1137 
       
  1138 		/**
       
  1139 		 * Get list of custom validators called before submit
       
  1140 		 */
       
  1141 		_getSubmitValidators: function(form) {
       
  1142 			var validators = new Array();
       
  1143 			var form_validator = form.data('ams-form-validator');
       
  1144 			if (form_validator)
       
  1145 				validators.push([form, form_validator]);
       
  1146 			$('[data-ams-form-validator]', form).each(function() {
       
  1147 				var source = $(this);
       
  1148 				validators.push([source, source.data('ams-form-validator')]);
       
  1149 			});
       
  1150 			return validators;
       
  1151 		},
       
  1152 
       
  1153 		/**
       
  1154 		 * Call list of custom validators before submit
       
  1155 		 *
       
  1156 		 * Each validator can return:
       
  1157 		 *  - a boolean 'false' value to just specify that an error occured
       
  1158 		 *  - a string value containing an error message
       
  1159 		 *  - an array containing a list of string error messages
       
  1160 		 * Any other value (undefined, null, True...) will lead to a successful submit.
       
  1161 		 */
       
  1162 		_checkSubmitValidators: function(form) {
       
  1163 			var validators = ams.form._getSubmitValidators(form);
       
  1164 			if (!validators.length)
       
  1165 				return true;
       
  1166 			var output = new Array();
       
  1167 			var result = true;
       
  1168 			for (var index in validators) {
       
  1169 				if (!$.isNumeric(index))  // IE check !
       
  1170 					continue;
       
  1171 				var validator = validators[index];
       
  1172 				var source = validator[0];
       
  1173 				var handler = validator[1];
       
  1174 				var validator_result = ams.executeFunctionByName(handler, form, source);
       
  1175 				if (validator_result === false)
       
  1176 					result = false;
       
  1177 				else if (typeof(validator_result) == 'string')
       
  1178 					output.push(validator_result);
       
  1179 				else if (result.length && (result.length > 0))
       
  1180 					output = output.concat(result);
       
  1181 			}
       
  1182 			if (output.length > 0) {
       
  1183 				var header = output.length == 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
       
  1184 				ams.skin.alert(form, 'danger', header, output);
       
  1185 				return false;
       
  1186 			} else
       
  1187 				return result;
       
  1188 		},
       
  1189 
       
  1190 		/**
       
  1191 		 * Display JSON errors
       
  1192 		 * JSON errors should be defined in an object as is:
       
  1193 		 * {status: 'error',
       
  1194 		 *  error_message: "Main error message",
       
  1195 		 *  messages: ["Message 1", "Message 2",...]
       
  1196 		 *  widgets: [{label: "First widget name",
       
  1197 		 *             name: "field-name-1",
       
  1198 		 *             message: "Error message"},
       
  1199 		 *            {label: "Second widget name",
       
  1200 		 *             name: "field-name-2",
       
  1201 		 *             message: "Second error message"},...]}
       
  1202 		 */
       
  1203 		showErrors: function(form, errors) {
       
  1204 			if (typeof(errors) == 'string') {
       
  1205 				ams.skin.alert(form, 'error', ams.i18n.ERROR_OCCURED, errors)
       
  1206 			} else if (errors instanceof Array) {
       
  1207 				var header = errors.length == 1 ? ams.i18n.ERROR_OCCURED : ams.i18n.ERRORS_OCCURED;
       
  1208 				ams.skin.alert(form, 'error', header, errors);
       
  1209 			} else {
       
  1210 				header = errors.widgets && (errors.widgets.length > 1) ? ams.i18n.ERRORS_OCCURED : ams.i18n.ERROR_OCCURED;
       
  1211 				var message = new Array();
       
  1212 				for (var index in errors.messages) {
       
  1213 					if (!$.isNumeric(index))
       
  1214 						continue;
       
  1215 					message.push(errors.messages[index])
       
  1216 				}
       
  1217 				for (index in errors.widgets) {
       
  1218 					if (!$.isNumeric(index))
       
  1219 						continue;
       
  1220 					var widget = errors.widgets[index];
       
  1221 					$('[name="' + widget.name + '"]', form).parent('label')
       
  1222 														   .removeClassPrefix('state-')
       
  1223 														   .addClass('state-error')
       
  1224 														   .after('<span for="name" class="state-error">' + widget.message + '</span>');
       
  1225 					if (widget.label) {
       
  1226 						message.push(widget.label + ' : ' + widget.message);
       
  1227 					}
       
  1228 				}
       
  1229 				ams.skin.alert(form, 'error', header, message, errors.error_message);
       
  1230 			}
       
  1231 		}
       
  1232 	};
       
  1233 
       
  1234 
       
  1235 	/**
       
  1236 	 * Modal dialogs helper functions
       
  1237 	 */
       
  1238 	MyAMS.dialog = {
       
  1239 
       
  1240 		/**
       
  1241 		 * Modal dialog opener
       
  1242 		 */
       
  1243 		open: function(source, options) {
       
  1244 			ams.ajax.check($.fn.modalmanager,
       
  1245 						   ams.baseURL + 'ext/bootstrap-modalmanager' + (ams.devmode ? '.js' : '.min.js'),
       
  1246 						   function() {
       
  1247 								ams.ajax.check($.fn.modal.defaults,
       
  1248 								ams.baseURL + 'ext/bootstrap-modal' + (ams.devmode ? '.js' : '.min.js'),
       
  1249 								function(first_load) {
       
  1250 									if (first_load) {
       
  1251 										$(document).off('click.modal');
       
  1252 										$.fn.modal.defaults.spinner = $.fn.modalmanager.defaults.spinner =
       
  1253 											'<div class="loading-spinner" style="width: 200px; margin-left: -100px;">' +
       
  1254 												'<div class="progress progress-striped active">' +
       
  1255 													'<div class="progress-bar" style="width: 100%;"></div>' +
       
  1256 												'</div>' +
       
  1257 											'</div>';
       
  1258 									}
       
  1259 									if (typeof(source) == 'string')
       
  1260 										var url = source;
       
  1261 									else
       
  1262 										url = source.attr('href') || source.data('ams-url');
       
  1263 									if (!url)
       
  1264 										return;
       
  1265 									$('body').modalmanager('loading');
       
  1266 									if (url.indexOf('#') == 0) {
       
  1267 										// Inner hidden modal dialog
       
  1268 										$(url).modal('show');
       
  1269 									} else {
       
  1270 										// Remote URL modal dialog
       
  1271 										$.get(url, options, function(data, status, request) {
       
  1272 											$('body').modalmanager('removeLoading');
       
  1273 											var request_data = ams.ajax.getResponse(request);
       
  1274 											var data_type = request_data.content_type;
       
  1275 											var result = request_data.data;
       
  1276 											switch (data_type) {
       
  1277 												case 'json':
       
  1278 													ams.ajax.handleJSON(result, $($(source).data('ams-json-target') || '#content'));
       
  1279 													break;
       
  1280 												case 'script':
       
  1281 													break;
       
  1282 												case 'xml':
       
  1283 													break;
       
  1284 												case 'html':
       
  1285 												case 'text':
       
  1286 												default:
       
  1287 													var content = $(result);
       
  1288 													var dialog = $('.modal-dialog', content.wrap('<div></div>').parent());
       
  1289 													var dialog_data = dialog.data();
       
  1290 													var data_options = {
       
  1291 														overflow: dialog_data.amsModalOverflow || '.modal-viewport',
       
  1292 														maxHeight: dialog_data.amsModalMaxHeight === undefined
       
  1293 																? function() {
       
  1294 																	return $(window).height() -
       
  1295 																				$('.modal-header', content).outerHeight(true) -
       
  1296 																				$('footer', content).outerHeight(true) - 85;
       
  1297 																}
       
  1298 																: ams.getFunctionByName(dialog_data.amsModalMaxHeight)
       
  1299 													};
       
  1300 													var settings = $.extend({}, data_options, dialog_data.amsModalOptions);
       
  1301 													settings = ams.executeFunctionByName(dialog_data.amsModalInitCallback, dialog, settings) || settings;
       
  1302 													$('<div>').addClass('modal fade')
       
  1303 															  .append(content)
       
  1304 															  .modal(settings);
       
  1305 													ams.initContent(content);
       
  1306 											}
       
  1307 										});
       
  1308 									}
       
  1309 								});
       
  1310 						   });
       
  1311 		},
       
  1312 
       
  1313 		/**
       
  1314 		 * Modals shown callback
       
  1315 		 * This callback is used to initialize modal's viewport size
       
  1316 		 */
       
  1317 		shown: function(e) {
       
  1318 
       
  1319 			function resetViewport(ev) {
       
  1320 				var top = $('.scrollmarker.top', viewport);
       
  1321 				var top_position = viewport.scrollTop();
       
  1322 				if (top_position > 0)
       
  1323 					top.show();
       
  1324 				else
       
  1325 					top.hide();
       
  1326 				var bottom = $('.scrollmarker.bottom', viewport);
       
  1327 				if (maxHeight + top_position >= viewport.get(0).scrollHeight)
       
  1328 					bottom.hide();
       
  1329 				else
       
  1330 					bottom.show();
       
  1331 			}
       
  1332 
       
  1333 			var modal = e.target;
       
  1334 			var viewport = $('.modal-viewport', modal);
       
  1335 			if (viewport.length == 0)
       
  1336 				return;
       
  1337 			var maxHeight = parseInt(viewport.css('max-height'));
       
  1338 			var barWidth = $.scrollbarWidth();
       
  1339 			if (viewport.height() == maxHeight) {
       
  1340 				$('<div></div>').addClass('scrollmarker')
       
  1341 								.addClass('top')
       
  1342 								.css('top', 0)
       
  1343 								.css('width', viewport.width() - barWidth)
       
  1344 								.hide()
       
  1345 								.appendTo(viewport);
       
  1346 				$('<div></div>').addClass('scrollmarker')
       
  1347 								.addClass('bottom')
       
  1348 								.css('top', maxHeight - 20)
       
  1349 								.css('width', viewport.width() - barWidth)
       
  1350 								.appendTo(viewport);
       
  1351 				viewport.scroll(resetViewport);
       
  1352 				viewport.off('resize')
       
  1353 						.on('resize', resetViewport);
       
  1354 			} else {
       
  1355 				$('.scrollmarker', viewport).remove();
       
  1356 			}
       
  1357 		},
       
  1358 
       
  1359 		/**
       
  1360 		 * Close modal dialog associated with given context
       
  1361 		 */
       
  1362 		close: function(context) {
       
  1363 			var modal = context.parents('.modal').data('modal');
       
  1364 			if (modal) {
       
  1365 				var manager = $('body').data('modalmanager');
       
  1366 				if (manager && (manager.getOpenModals().indexOf(modal) >= 0))
       
  1367 					modal.hide();
       
  1368 			}
       
  1369 		}
       
  1370 	};
       
  1371 
       
  1372 
       
  1373 	/**
       
  1374 	 * Plug-ins helpers functions
       
  1375 	 *
       
  1376 	 * These helpers functions are used by several JQuery plug-in extensions.
       
  1377 	 * They have been extracted from these extensions management code to reuse them more easily into
       
  1378 	 * application specific callbacks.
       
  1379 	 */
       
  1380 	MyAMS.helpers = {
       
  1381 
       
  1382 		/** Select2 selection formatter */
       
  1383 		select2FormatSelection: function(object, container) {
       
  1384 			if (object instanceof Array) {
       
  1385 				$(object).each(function() {
       
  1386 					if (typeof(this) == 'object')
       
  1387 						container.append(this.text);
       
  1388 					else
       
  1389 						container.append(this);
       
  1390 				});
       
  1391 			} else {
       
  1392 				if (typeof(object) == 'object')
       
  1393 					container.append(object.text);
       
  1394 				else
       
  1395 					container.append(object);
       
  1396 			}
       
  1397 		},
       
  1398 
       
  1399 		/** Select2 query results callback */
       
  1400 		select2QueryUrlResultsCallback: function(data, page, context) {
       
  1401 			switch (data.status) {
       
  1402 				case 'error':
       
  1403 					ams.skin.messageBox('error', {
       
  1404 						title: ams.i18n.ERROR_OCCURED,
       
  1405 						content: '<h4>' + data.error_message + '</h4>',
       
  1406 						icon: "fa fa-warning animated shake",
       
  1407 						timeout: 10000
       
  1408 					});
       
  1409 					break;
       
  1410 				default:
       
  1411 					return {
       
  1412 						results: data.results || data,
       
  1413 						more: data.has_more || false,
       
  1414 						context: data.context
       
  1415 					};
       
  1416 			}
       
  1417 		},
       
  1418 
       
  1419 		/** Select2 JSON-RPC success callback */
       
  1420 		select2QueryMethodSuccessCallback: function(data, status, options) {
       
  1421 			var result = data.result;
       
  1422 			switch (result.status) {
       
  1423 				case 'error':
       
  1424 					ams.skin.messageBox('error', {
       
  1425 						title: ams.i18n.ERROR_OCCURED,
       
  1426 						content: '<h4>' + result.error_message + '</h4>',
       
  1427 						icon: "fa fa-warning animated shake",
       
  1428 						timeout: 10000
       
  1429 					});
       
  1430 					break;
       
  1431 				default:
       
  1432 					options.callback({
       
  1433 						results: result.results || result,
       
  1434 						more: result.has_more || false,
       
  1435 						context: result.context
       
  1436 					});
       
  1437 			}
       
  1438 		}
       
  1439 	};
       
  1440 
       
  1441 
       
  1442 	/**
       
  1443 	 * Plug-ins management features
       
  1444 	 *
       
  1445 	 * Only basic JQuery, Bootstrap and MyAMS javascript extensions are typically loaded from main page.
       
  1446 	 * Other JQuery plug-ins may be loaded dynamically.
       
  1447 	 * Several JQuery extension plug-ins are already included and pre-configured by MyAMS. Other external
       
  1448 	 * plug-ins can be defined and loaded dynamically using simple "data" attributes.
       
  1449 	 *
       
  1450 	 * WARNING: any plug-in implicated into a form submit process (like JQuery-form or JQuery-progressbar)
       
  1451 	 * must be loaded in a synchronous way. Otherwise, if you use named buttons to submit your forms,
       
  1452 	 * dynamic hidden input fields created by JQuery-validate plug-in will be removed from the form
       
  1453 	 * before the form is submitted!
       
  1454 	 */
       
  1455 	MyAMS.plugins = {
       
  1456 
       
  1457 		/**
       
  1458 		 * Initialize list of content plug-ins
       
  1459 		 */
       
  1460 		init: function(element) {
       
  1461 
       
  1462 			// Initialize custom data attributes
       
  1463 			ams.plugins.initData(element);
       
  1464 
       
  1465 			// Check for disabled plug-ins
       
  1466 			var disabled = new Array();
       
  1467 			$('[data-ams-plugins-disabled]', element).each(function() {
       
  1468 				var plugins = $(this).data('ams-plugins-disabled').split(/\s+/);
       
  1469 				for (var name in plugins) {
       
  1470 					disabled.push(plugins[name]);
       
  1471 				}
       
  1472 			});
       
  1473 
       
  1474 			// Run already enabled plug-ins
       
  1475 			for (var index in ams.plugins.enabled) {
       
  1476 				if (disabled.indexOf(index) >= 0)
       
  1477 					continue;
       
  1478 				var plugin = ams.plugins.enabled[index];
       
  1479 				if (typeof(plugin) == 'function')
       
  1480 					plugin(element);
       
  1481 			}
       
  1482 
       
  1483 			// Load, run and register new plug-ins
       
  1484 			$('[data-ams-plugins]', element).each(function() {
       
  1485 				var source = $(this);
       
  1486 				var plugins = {}
       
  1487 				if (typeof(source.data('ams-plugins')) === 'string') {
       
  1488 					var names = source.data('ams-plugins').split(/\s+/);
       
  1489 					for (var index in names) {
       
  1490 						var name = names[index];
       
  1491 						var plugin_options = {
       
  1492 							src: source.data('ams-plugin-' + name + '-src'),
       
  1493 							css: source.data('ams-plugin-' + name + '-css'),
       
  1494 							callback: source.data('ams-plugin-' + name + '-callback'),
       
  1495 							register: source.data('ams-plugin-' + name + '-register'),
       
  1496 							async: source.data('ams-plugin-' + name + '-async')
       
  1497 						}
       
  1498 						plugins[name] = plugin_options;
       
  1499 					}
       
  1500 				} else {
       
  1501 					plugins = source.data('ams-plugins');
       
  1502 				}
       
  1503 				for (var name in plugins) {
       
  1504 					if (ams.plugins.enabled[name] === undefined) {
       
  1505 						var plugin = plugins[name];
       
  1506 						ams.getScript(plugin.src, function() {
       
  1507 							var callback = plugin.callback;
       
  1508 							if (callback) {
       
  1509 								var called = ams.getFunctionByName(callback);
       
  1510 								if (typeof(called) == 'function')
       
  1511 									called(element);
       
  1512 								if (plugin.register !== false)
       
  1513 									ams.plugins.enabled[name] = called;
       
  1514 							} else {
       
  1515 								if (plugin.register !== false)
       
  1516 									ams.plugins.enabled[name] = null;
       
  1517 							}
       
  1518 							var css = plugin['css'];
       
  1519 							if (css) {
       
  1520 								ams.getCSS(css, name + '_css');
       
  1521 							}
       
  1522 						}, {
       
  1523 							async: plugin.async === undefined ? true : plugin.async
       
  1524 						});
       
  1525 					}
       
  1526 				}
       
  1527 			});
       
  1528 		},
       
  1529 
       
  1530 		/**
       
  1531 		 * Data initializer
       
  1532 		 * This plug-in converts a single JSON "data-ams-data" attribute into a set of several equivalent "data-" attributes.
       
  1533 		 * This way of defining data attributes can be used with HTML templates engines which don't allow you
       
  1534 		 * to create dynamic attributes easily.
       
  1535 		 */
       
  1536 		initData: function(element) {
       
  1537 			$('[data-ams-data]', element).each(function() {
       
  1538 				var handler = $(this);
       
  1539 				var data = handler.data('ams-data');
       
  1540 				for (var index in data) {
       
  1541 					handler.attr('data-' + index, data[index]);
       
  1542 				}
       
  1543 			});
       
  1544 		},
       
  1545 
       
  1546 		/**
       
  1547 		 * Map of enabled plug-ins
       
  1548 		 * This map can be extended by external plug-ins.
       
  1549 		 *
       
  1550 		 * Standard MyAMS plug-ins management method generally includes:
       
  1551 		 * - applying a class matching plug-in name on a set of HTML entities to apply the plug-in
       
  1552 		 * - defining a set of data-attributes on each of these entities to customize the plug-in
       
  1553 		 * For each standard plug-in, you can also provide an options object (to define plug-in options not handled
       
  1554 		 * by default MyAMS initialization engine) and an initialization callback (to define these options dynamically).
       
  1555 		 * Another callback can also be provided to be called after plug-in initialization.
       
  1556 		 */
       
  1557 		enabled: {
       
  1558 
       
  1559 			/**
       
  1560 			 * Label hints
       
  1561 			 */
       
  1562 			hint: function(element) {
       
  1563 				var hints = $('.hint:not(:parents(.nohints))', element);
       
  1564 				if (hints.length > 0)
       
  1565 					ams.ajax.check($.fn.tipsy,
       
  1566 								   ams.baseURL + 'ext/jquery-tipsy' + (ams.devmode ? '.js' : '.min.js'),
       
  1567 								   function() {
       
  1568 										ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + (ams.devmode ? '.css' : '.min.css'),
       
  1569 												  'jquery-tipsy');
       
  1570 										hints.each(function() {
       
  1571 											var hint = $(this);
       
  1572 											var data = hint.data();
       
  1573 											var data_options = {
       
  1574 												html: data.amsHintHtml,
       
  1575 												title: ams.getFunctionByName(data.amsHintTitleGetter) || function() {
       
  1576 													var hint = $(this);
       
  1577 													return hint.attr('original-title') ||
       
  1578 														   hint.attr(data.amsHintTitleAttr || 'title') ||
       
  1579 														   (data.amsHintHtml ? hint.html() : hint.text());
       
  1580 												},
       
  1581 												opacity: data.amsHintOpacity,
       
  1582 												gravity: data.amsHintGravity || 'sw',
       
  1583 												offset: data.amsHintOffset || 0
       
  1584 											};
       
  1585 											var settings = $.extend({}, data_options, data.amsHintOptions);
       
  1586 											settings = ams.executeFunctionByName(data.amsHintInitCallback, hint, settings) || settings;
       
  1587 											var plugin = hint.tipsy(settings);
       
  1588 											ams.executeFunctionByName(data.amsHintAfterInitCallback, hint, plugin, settings);
       
  1589 										});
       
  1590 								   });
       
  1591 			},
       
  1592 
       
  1593 			/**
       
  1594 			 * Fieldset legend switcher
       
  1595 			 */
       
  1596 			switcher: function(element) {
       
  1597 				$('LEGEND.switcher', element).each(function() {
       
  1598 					var legend = $(this);
       
  1599 					var fieldset = legend.parent('fieldset');
       
  1600 					var data = legend.data();
       
  1601 					if (!data.amsSwitcher) {
       
  1602 						$('<i class="fa fa-fw"></i>')
       
  1603 							.prependTo($(this))
       
  1604 							.addClass(data.amsSwitcherState == 'open' ?
       
  1605 									  (data.amsSwitcherMinusClass || 'fa-minus') :
       
  1606 									  (data.amsSwitcherPlusClass || 'fa-plus'));
       
  1607 						legend.on('click', function(e) {
       
  1608 							e.preventDefault();
       
  1609 							var veto = {};
       
  1610 							legend.trigger('ams.switcher.before-switch', [legend, veto]);
       
  1611 							if (veto.veto)
       
  1612 								return;
       
  1613 							if (fieldset.hasClass('switched')) {
       
  1614 								fieldset.removeClass('switched');
       
  1615 								$('.fa', legend).removeClass(data.amsSwitcherPlusClass || 'fa-plus')
       
  1616 												.addClass(data.amsSwitcherMinusClass || 'fa-minus');
       
  1617 								legend.trigger('ams.switcher.opened', [legend]);
       
  1618 							} else {
       
  1619 								fieldset.addClass('switched');
       
  1620 								$('.fa', legend).removeClass(data.amsSwitcherMinusClass || 'fa-minus')
       
  1621 												.addClass(data.amsSwitcherPlusClass || 'fa-plus');
       
  1622 								legend.trigger('ams.switcher.closed', [legend]);
       
  1623 							}
       
  1624 						});
       
  1625 						if (data.amsSwitcherState != 'open')
       
  1626 							fieldset.addClass('switched');
       
  1627 						legend.data('ams-switcher', 'on');
       
  1628 					}
       
  1629 				});
       
  1630 			},
       
  1631 
       
  1632 			/**
       
  1633 			 * Fieldset legend checker
       
  1634 			 */
       
  1635 			checker: function(element) {
       
  1636 				$('LEGEND.checker', element).each(function() {
       
  1637 					var legend = $(this);
       
  1638 					var fieldset = legend.parent('fieldset');
       
  1639 					var data = legend.data();
       
  1640 					if (!data.amsChecker) {
       
  1641 						var checker = $('<label class="checkbox"></label>');
       
  1642 						var input = $('<input type="checkbox">').attr('name', data.amsCheckerFieldname)
       
  1643 																.attr('id', (data.amsCheckerFieldname ||
       
  1644 																			 ('checker_'+(+new Date()).toString())).replace(/\./, '_'))
       
  1645 																.val(data.amsCheckerState == 'on')
       
  1646 																.on('change', function(e) {
       
  1647 																	if ($(this).is(':checked')) {
       
  1648 																		if (data.amsCheckerMode == 'disable')
       
  1649 																			fieldset.removeAttr('disabled')
       
  1650 																		else
       
  1651 																			fieldset.removeClass('switched')
       
  1652 																	} else {
       
  1653 																		if (data.amsCheckerMode == 'disable')
       
  1654 																			fieldset.attr('disabled', 'disabled');
       
  1655 																		else
       
  1656 																			fieldset.addClass('switched');
       
  1657 																	}
       
  1658 																})
       
  1659 																.appendTo(checker);
       
  1660 						$('.legend', legend).attr('for', input.attr('id'));
       
  1661 						checker.append('<i></i>')
       
  1662 							   .prependTo(legend);
       
  1663 						if (data.amsCheckerState == 'on')
       
  1664 							input.attr('checked', true);
       
  1665 						else
       
  1666 							fieldset.addClass('switched');
       
  1667 						legend.data('ams-checker', 'on');
       
  1668 					}
       
  1669 				});
       
  1670 			},
       
  1671 
       
  1672 			/**
       
  1673 			 * Sliders
       
  1674 			 */
       
  1675 			slider: function(element) {
       
  1676 				var sliders = $('.slider', element);
       
  1677 				if (sliders.length > 0) {
       
  1678 					ams.ajax.check($.fn.slider,
       
  1679 								   ams.baseURL + 'ext/bootstrap-slider.min.js',
       
  1680 								   function() {
       
  1681 										sliders.each(function() {
       
  1682 											var slider = $(this);
       
  1683 											var data = slider.data();
       
  1684 											var data_options = {};
       
  1685 											var settings = $.extend({}, data_options, slider.data.amsSliderOptions);
       
  1686 											settings = ams.executeFunctionByName(data.amsSliderInitCallback, slider, settings) || settings;
       
  1687 											var plugin = slider.slider(settings);
       
  1688 											ams.executeFunctionByName(data.amsSliderAfterInitCallback, slider, plugin, settings);
       
  1689 										});
       
  1690 								   });
       
  1691 				}
       
  1692 			},
       
  1693 
       
  1694 			/**
       
  1695 			 * Select2 plug-in
       
  1696 			 */
       
  1697 			select2: function(element) {
       
  1698 				var selects = $('.select2', element);
       
  1699 				if (selects.length > 0) {
       
  1700 					ams.ajax.check($.fn.select2,
       
  1701 								   ams.baseURL + 'ext/jquery-select2-3.4.5' + (ams.devmode ? '.js' : '.min.js'),
       
  1702 								   function() {
       
  1703 										selects.each(function() {
       
  1704 											var select = $(this);
       
  1705 											var data = select.data();
       
  1706 											var data_options = {
       
  1707 												placeholder: data.amsSelect2Placeholder,
       
  1708 												multiple: data.amsSelect2Multiple,
       
  1709 												minimumInputLength: data.amsSelect2MinimumInputLength || 0,
       
  1710 												maximumSelectionSize: data.amsSelect2MaximumSelectionSize,
       
  1711 												openOnEnter: data.amsSelect2EnterOpen === undefined ? true : data.amsSelect2EnterOpen,
       
  1712 												allowClear: data.amsSelect2AllowClear === undefined ? true : data.amsSelect2AllowClear,
       
  1713 												width: data.amsSelect2Width || '100%',
       
  1714 												initSelection: ams.getFunctionByName(data.amsSelect2InitSelection),
       
  1715 												formatSelection: data.amsSelect2FormatSelection === undefined
       
  1716 																	? ams.helpers.select2FormatSelection
       
  1717 																	: ams.getFunctionByName(data.amsSelect2FormatSelection),
       
  1718 												formatResult: ams.getFunctionByName(data.amsSelect2FormatResult),
       
  1719 												formatNoMatches: data.amsSelect2FormatResult === undefined
       
  1720 																	? function(term) {
       
  1721 																		return ams.i18n.SELECT2_NOMATCHES;
       
  1722 																	}
       
  1723 																	: ams.getFunctionByName(data.amsSelect2FormatResult),
       
  1724 												formatInputTooShort: data.amsSelect2FormatInputTooShort === undefined
       
  1725 																	? function(input, min) {
       
  1726 																		var n = min - input.length;
       
  1727 																		return ams.i18n.SELECT2_INPUT_TOOSHORT
       
  1728 																						.replace(/\{0\}/, n)
       
  1729 																						.replace(/\{1\}/, n == 1 ? "" : ams.i18n.SELECT2_PLURAL);
       
  1730 																	}
       
  1731 																	: ams.getFunctionByName(data.amsSelect2FormatInputTooShort),
       
  1732 												formatInputTooLong: data.amsSelect2FormatInputTooLong === undefined
       
  1733 																	? function(input, max) {
       
  1734 																		var n = input.length - max;
       
  1735 																		return ams.i18n.SELECT2_INPUT_TOOLONG
       
  1736 																						.replace(/\{0\}/, n)
       
  1737 																						.replace(/\{1\}/, n == 1 ? "" : ams.i18n.SELECT2_PLURAL);
       
  1738 																	}
       
  1739 																	: ams.getFunctionByName(data.amsSelect2FormatInputTooLong),
       
  1740 												formatSelectionTooBig: data.amsSelect2FormatSelectionTooBig === undefined
       
  1741 																	? function(limit) {
       
  1742 																		return ams.i18n.SELECT2_SELECTION_TOOBIG
       
  1743 																						.replace(/\{0\}/, limit)
       
  1744 																						.replace(/\{1\}/, limit == 1 ? "" : ams.i18n.SELECT2_PLURAL);
       
  1745 																	}
       
  1746 																	: ams.getFunctionByName(data.amsSelect2FormatSelectionTooBig),
       
  1747 												formatLoadMore: data.amsSelect2FormatLoadMore === undefined
       
  1748 																	? function (pageNumber) {
       
  1749 																		return ams.i18n.SELECT2_LOADMORE;
       
  1750 																	}
       
  1751 																	: ams.getFunctionByName(data.amsSelect2FormatLoadMore),
       
  1752 												formatSearching: data.amsSelect2FormatSearching === undefined
       
  1753 																	? function() {
       
  1754 																		return ams.i18n.SELECT2_SEARCHING;
       
  1755 																	}
       
  1756 																	: ams.getFunctionByName(data.amsSelect2FormatSearching),
       
  1757 												separator: data.amsSelect2Separator || ',',
       
  1758 												tokenSeparators: data.amsSelect2TokensSeparators || [','],
       
  1759 												tokenizer: ams.getFunctionByName(data.amsSelect2Tokenizer)
       
  1760 											};
       
  1761 
       
  1762 											switch (select.context.type) {
       
  1763 												case 'text':
       
  1764 												case 'hidden':
       
  1765 													if (!data_options.initSelection) {
       
  1766 														var values_data = select.data('ams-select2-values');
       
  1767 														if (values_data) {
       
  1768 															data_options.initSelection = function(element, callback) {
       
  1769 																var data = [];
       
  1770 																$(element.val().split(data_options.separator)).each(function() {
       
  1771 																	data.push({id: this,
       
  1772 																			   text: values_data[this] || this});
       
  1773 																});
       
  1774 																callback(data);
       
  1775 															}
       
  1776 														}
       
  1777 													}
       
  1778 													break;
       
  1779 												default:
       
  1780 													break;
       
  1781 											}
       
  1782 
       
  1783 											if (data.amsSelect2Query) {
       
  1784 												// Custom query method
       
  1785 												data_options.query = ams.getFunctionByName(data.amsSelect2Query);
       
  1786 												data_options.minimumInputLength = data.amsSelect2MinimumInputLength || 1;
       
  1787 											} else if (data.amsSelect2QueryUrl) {
       
  1788 												// AJAX query
       
  1789 												data_options.ajax = {
       
  1790 													url: data.amsSelect2QueryUrl,
       
  1791 													quietMillis: data.amsSelect2QuietMillis || 200,
       
  1792 													type: data.amsSelect2QueryType || 'POST',
       
  1793 													dataType: data.amsSelect2QueryDatatype || 'json',
       
  1794 													data: function(term, page, context) {
       
  1795 														var options = {};
       
  1796 														options[data.amsSelect2QueryParamName || 'query'] = term;
       
  1797 														options[data.amsSelect2PageParamName || 'page'] = page;
       
  1798 														options[data.amsSelect2ContextParamName || 'context'] = context;
       
  1799 														return $.extend({}, options, data.amsSelect2QueryOptions);
       
  1800 													},
       
  1801 													results: ams.helpers.select2QueryUrlResultsCallback
       
  1802 												};
       
  1803 												data_options.minimumInputLength = data.amsSelect2MinimumInputLength || 1;
       
  1804 											} else if (data.amsSelect2QueryMethod) {
       
  1805 												// JSON-RPC query
       
  1806 												data_options.query = function(options) {
       
  1807 													var settings = {
       
  1808 														url: data.amsSelect2MethodTarget || ams.jsonrpc.getAddr(),
       
  1809 														type: data.amsSelect2MethodType || 'POST',
       
  1810 														cache: false,
       
  1811 														method: data.amsSelect2QueryMethod,
       
  1812 														params: data.amsSelect2QueryParams || {},
       
  1813 														success: function(data, status) {
       
  1814 															return ams.helpers.select2QueryMethodSuccessCallback(data, status, options);
       
  1815 														},
       
  1816 														error: ams.error.show
       
  1817 													};
       
  1818 													settings.params[data.amsSelect2QueryParamName || 'query'] = options.term;
       
  1819 													settings.params[data.amsSelect2PageParamName || 'page'] = options.page;
       
  1820 													settings.params[data.amsSelect2ContextParamName || 'context'] = options.context;
       
  1821 													settings = $.extend({}, settings, data.amsSelect2QueryOptions);
       
  1822 													settings = ams.executeFunctionByName(data.amsSelect2QueryInitCallback, select, settings) || settings;
       
  1823 													ams.ajax.check($.jsonRpc,
       
  1824 																   ams.baseURL + 'ext/jquery-jsonrpc' + (ams.devmode ? '.js' : '.min.js'),
       
  1825 																   function() {
       
  1826 																		$.jsonRpc(settings);
       
  1827 																   });
       
  1828 												};
       
  1829 												data_options.minimumInputLength = data.amsSelect2MinimumInputLength || 1;
       
  1830 											} else if (data.amsSelect2Tags) {
       
  1831 												// Tags mode
       
  1832 												data_options.tags = data.amsSelect2Tags;
       
  1833 											} else if (data.amsSelect2Data) {
       
  1834 												// Provided data mode
       
  1835 												data_options.data = data.amsSelect2Data;
       
  1836 											}
       
  1837 
       
  1838 											if (data.amsSelect2EnableFreeTags) {
       
  1839 												data_options.createSearchChoice = function(term) {
       
  1840 													return {id: term, text: term};
       
  1841 												};
       
  1842 											}
       
  1843 
       
  1844 											var settings = $.extend({}, data_options, data.amsSelect2Options);
       
  1845 											settings = ams.executeFunctionByName(data.amsSelect2InitCallback, select, settings) || settings;
       
  1846 											var plugin = select.select2(settings);
       
  1847 											ams.executeFunctionByName(data.amsSelect2AfterInitCallback, select, plugin, settings);
       
  1848 
       
  1849 											select.on('change', function() {
       
  1850 												var validator = $(select.get(0).form).data('validator');
       
  1851 												if (validator !== undefined)
       
  1852 													$(select).valid();
       
  1853 											});
       
  1854 										});
       
  1855 								   });
       
  1856 				}
       
  1857 			},
       
  1858 
       
  1859 			/**
       
  1860 			 * Edit mask plug-in
       
  1861 			 */
       
  1862 			maskedit: function(element) {
       
  1863 				var masks = $('[data-mask]', element);
       
  1864 				if (masks.length > 0) {
       
  1865 					ams.ajax.check($.fn.mask,
       
  1866 								   ams.baseURL + 'ext/jquery-maskedinput-1.3.1.min.js',
       
  1867 								   function() {
       
  1868 										masks.each(function() {
       
  1869 											var mask = $(this);
       
  1870 											var data = mask.data();
       
  1871 											var data_options = {
       
  1872 												placeholder: data.amsMaskeditPlaceholder || 'X'
       
  1873 											};
       
  1874 											var settings = $.extend({}, data_options, data.amsMaskeditOptions);
       
  1875 											settings = ams.executeFunctionByName(data.amsMaskeditInitCallback, mask, settings) || settings;
       
  1876 											var plugin = mask.mask(mask.attr('data-mask'), settings);
       
  1877 											ams.executeFunctionByName(data.amsMaskeditAfterInitCallback, mask, plugin, settings);
       
  1878 										});
       
  1879 								   });
       
  1880 				}
       
  1881 			},
       
  1882 
       
  1883 			/**
       
  1884 			 * JQuery-UI date picker
       
  1885 			 */
       
  1886 			datepicker: function(element) {
       
  1887 				var datepickers = $('.datepicker', element);
       
  1888 				if (datepickers.length > 0) {
       
  1889 					datepickers.each(function() {
       
  1890 						var picker = $(this);
       
  1891 						var data = picker.data();
       
  1892 						var data_options = {
       
  1893 							dateFormat: data.amsDatepickerFormat || 'dd/mm/yy',
       
  1894 							prevText: '<i class="fa fa-chevron-left"></i>',
       
  1895 							nextText: '<i class="fa fa-chevron-right"></i>',
       
  1896 							changeMonth: data.amsDatepickerChangeMonth,
       
  1897 							changeYear: data.amsDatepickerChangeYear,
       
  1898 							showButtonPanel: !data.amsDatepickerHidePanel
       
  1899 						};
       
  1900 						var settings = $.extend({}, data_options, data.amsDatepickerOptions);
       
  1901 						settings = ams.executeFunctionByName(data.amsDatepickerInitCallback, picker, settings) || settings;
       
  1902 						var plugin = picker.datepicker(settings);
       
  1903 						ams.executeFunctionByName(data.amsDatepickerAfterInitCallback, picker, plugin, settings);
       
  1904 					});
       
  1905 				}
       
  1906 			},
       
  1907 
       
  1908 			/**
       
  1909 			 * JQuery typeahead plug-in
       
  1910 			 */
       
  1911 			typeahead: function(element) {
       
  1912 				var typeaheads = $('.typeahead', element);
       
  1913 				if (typeaheads.length > 0) {
       
  1914 					ams.ajax.check($.fn.typeahead,
       
  1915 								   ams.baseURL + 'ext/jquery-typeahead' + (ams.devmode ? '.js' : '.min.js'),
       
  1916 								   function() {
       
  1917 										typeaheads.each(function() {
       
  1918 											var input = $(this);
       
  1919 											var data = input.data();
       
  1920 											var data_options = {};
       
  1921 											var settings = $.extend({}, data_options, data.amsTypeaheadOptions);
       
  1922 											settings = ams.executeFunctionByName(data.amsTypeaheadInitCallback, input, settings) || settings;
       
  1923 											var plugin = input.typeahead(settings);
       
  1924 											ams.executeFunctionByName(data.amsTypeaheadAfterInitCallback, input, plugin, settings);
       
  1925 										});
       
  1926 								   });
       
  1927 				}
       
  1928 			},
       
  1929 
       
  1930 			/**
       
  1931 			 * JQuery validation plug-in
       
  1932 			 */
       
  1933 			validate: function(element) {
       
  1934 				var forms = $('FORM:not([novalidate])', element);
       
  1935 				if (forms.length > 0) {
       
  1936 					ams.ajax.check($.fn.validate,
       
  1937 								   ams.baseURL + 'ext/jquery-validate-1.11.1' + (ams.devmode ? '.js' : '.min.js'),
       
  1938 								   function(first_load) {
       
  1939 										if (first_load) {
       
  1940 											$.validator.setDefaults({
       
  1941 												highlight: function(element) {
       
  1942 													$(element).closest('.form-group, label:not(:parents(.form-group))').addClass('state-error');
       
  1943 												},
       
  1944 												unhighlight: function(element) {
       
  1945 													$(element).closest('.form-group, label:not(:parents(.form-group))').removeClass('state-error');
       
  1946 												},
       
  1947 												errorElement: 'span',
       
  1948 												errorClass: 'state-error',
       
  1949 												errorPlacement: function(error, element) {
       
  1950 													if (element.parent('label').length)
       
  1951 														error.insertAfter(element.parent());
       
  1952 													else
       
  1953 														error.insertAfter(element);
       
  1954 												}
       
  1955 											});
       
  1956 											if (ams.plugins.i18n) {
       
  1957 												for (var key in ams.plugins.i18n.validate) {
       
  1958 													var message = ams.plugins.i18n.validate[key];
       
  1959 													if ((typeof(message) == 'string') &&
       
  1960 														(message.indexOf('{0}') > -1))
       
  1961 														ams.plugins.i18n.validate[key] = $.validator.format(message);
       
  1962 												}
       
  1963 												$.extend($.validator.messages, ams.plugins.i18n.validate);
       
  1964 											}
       
  1965 										}
       
  1966 										forms.each(function() {
       
  1967 											var form = $(this);
       
  1968 											var data = form.data();
       
  1969 											var data_options = {
       
  1970 												ignore: null,
       
  1971 												submitHandler: form.attr('data-async') !== undefined
       
  1972 															   ? data.amsFormSubmitHandler === undefined
       
  1973 																	? function() {
       
  1974 																		// JQuery-form plug-in must be loaded synchronously!!
       
  1975 																		// Otherwise, hidden input fields created by jquery-validate plug-in
       
  1976 																		// and matching named buttons will be deleted (on first form submit)
       
  1977 																		// before JQuery-form plug-in can get them when submitting the form...
       
  1978 																		ams.ajax.check($.fn.ajaxSubmit,
       
  1979 																					   ams.baseURL + 'ext/jquery-form-3.49' + (ams.devmode ? '.js' : '.min.js'));
       
  1980 																		return ams.form.submit(form);
       
  1981 																	}
       
  1982 																	: ams.getFunctionByName(data.amsFormSubmitHandler)
       
  1983 															   : undefined
       
  1984 											};
       
  1985 											var settings = $.extend({}, data_options, data.amsValidateOptions);
       
  1986 											settings = ams.executeFunctionByName(data.amsValidateInitCallback, form, settings) || settings;
       
  1987 											var plugin = form.validate(settings);
       
  1988 											ams.executeFunctionByName(data.amsValidateAfterInitCallback, form, plugin, settings);
       
  1989 										});
       
  1990 								   });
       
  1991 				}
       
  1992 			},
       
  1993 
       
  1994 			/**
       
  1995 			 * JQuery dataTables
       
  1996 			 */
       
  1997 			datatable: function(element) {
       
  1998 				var tables = $('.datatable', element);
       
  1999 				if (tables.length > 0) {
       
  2000 					ams.ajax.check($.fn.dataTable,
       
  2001 								   ams.baseURL + 'ext/jquery-dataTables-1.9.4' + (ams.devmode ? '.js' : '.min.js'),
       
  2002 								   function(first_load) {
       
  2003 										if (first_load) {
       
  2004 											$.fn.dataTableExt.oSort['numeric-comma-asc']  = function(a, b) {
       
  2005 												var x = a.replace(/,/, ".").replace(/ /g, '');
       
  2006 												var y = b.replace(/,/, ".").replace(/ /g, '');
       
  2007 												x = parseFloat(x);
       
  2008 												y = parseFloat(y);
       
  2009 												return ((x < y) ? -1 : ((x > y) ?  1 : 0));
       
  2010 											};
       
  2011 											$.fn.dataTableExt.oSort['numeric-comma-desc'] = function(a, b) {
       
  2012 												var x = a.replace(/,/, ".").replace(/ /g, '');
       
  2013 												var y = b.replace(/,/, ".").replace(/ /g, '');
       
  2014 												x = parseFloat(x);
       
  2015 												y = parseFloat(y);
       
  2016 												return ((x < y) ?  1 : ((x > y) ? -1 : 0));
       
  2017 											};
       
  2018 										}
       
  2019 										$(tables).each(function() {
       
  2020 											ams.ajax.check($.fn.dataTableExt.oPagination['bootstrap_full'],
       
  2021 														   ams.baseURL + 'myams-dataTables' + (ams.devmode ? '.js' : '.min.js'));
       
  2022 											var table = $(this);
       
  2023 											var data = table.data();
       
  2024 											var extensions = (data.amsDatatableExtensions || '').split(/\s+/);
       
  2025 											var sDom = data.amsDatatableSdom ||
       
  2026 												"W" +
       
  2027 												((extensions.indexOf('colreorder') >= 0 ||
       
  2028 												  extensions.indexOf('colreorderwithresize') >= 0) ? 'R' : '') +
       
  2029 												"<'dt-top-row'" +
       
  2030 												(extensions.indexOf('colvis') >= 0 ? 'C' : '') +
       
  2031 												((data.amsDatatablePagination === false ||
       
  2032 												  data.amsDatatablePaginationSize === false) ? '' : 'L') +
       
  2033 												(data.amsDatatableGlobalFilter === false ? '' : 'F') +
       
  2034 												">r<'dt-wrapper't" +
       
  2035 												(extensions.indexOf('scroller') >= 0 ? 'S' : '') +
       
  2036 												"><'dt-row dt-bottom-row'<'row'<'col-sm-6'i><'col-sm-6 text-right'p>>";
       
  2037 											var data_options = {
       
  2038 												bJQueryUI: false,
       
  2039 												bFilter: data.amsDatatableGlobalFilter !== false,
       
  2040 												bPaginate: data.amsDatatablePagination !== false,
       
  2041 												bInfo: data.amsDatatableInfo !== false,
       
  2042 												bSort: data.amsDatatableSort !== false,
       
  2043 												bDeferRender: true,
       
  2044 												bAutoWidth: false,
       
  2045 												iDisplayLength: data.amsDatatableDisplayLength || 25,
       
  2046 												sPaginationType: data.amsDatatablePaginationType || 'bootstrap_full',
       
  2047 												sDom: sDom,
       
  2048 												oLanguage: ams.plugins.i18n.datatables,
       
  2049 												fnInitComplete: function(oSettings, json) {
       
  2050 													$('.ColVis_Button').addClass('btn btn-default btn-sm')
       
  2051 																	   .html((ams.plugins.i18n.datatables.sColumns || "Columns") + ' <i class="fa fa-fw fa-caret-down"></i>');
       
  2052 												}
       
  2053 											};
       
  2054 											var settings = $.extend({}, data_options, data.amsDatatableOptions);
       
  2055 											if (extensions.length > 0) {
       
  2056 												for (var index in extensions) {
       
  2057 													switch (extensions[index]) {
       
  2058 														case 'autofill':
       
  2059 															ams.ajax.check($.fn.dataTable.AutoFill,
       
  2060 																		   ams.baseURL + 'ext/jquery-dataTables-autoFill' + (ams.devmode ? '.js' : '.min.js'));
       
  2061 															break;
       
  2062 														case 'columnfilter':
       
  2063 															ams.ajax.check($.fn.columnFilter,
       
  2064 																		   ams.baseURL + 'ext/jquery-dataTables-columnFilter' + (ams.devmode ? '.js' : '.min.js'));
       
  2065 															break;
       
  2066 														case 'colreorder':
       
  2067 															ams.ajax.check($.fn.dataTable.ColReorder,
       
  2068 																		   ams.baseURL + 'ext/jquery-dataTables-colReorder' + (ams.devmode ? '.js' : '.min.js'));
       
  2069 															break;
       
  2070 														case 'colreorderwithresize':
       
  2071 															ams.ajax.check($.fn.dataTable.ColReorder,
       
  2072 																		   ams.baseURL + 'ext/jquery-dataTables-colReorderWithResize' + (ams.devmode ? '.js' : '.min.js'));
       
  2073 															break;
       
  2074 														case 'colvis':
       
  2075 															ams.ajax.check($.fn.dataTable.ColVis,
       
  2076 																		   ams.baseURL + 'ext/jquery-dataTables-colVis' + (ams.devmode ? '.js' : '.min.js'));
       
  2077 															var cv_default = {
       
  2078 																activate: 'click',
       
  2079 																sAlign: 'right'
       
  2080 															};
       
  2081 															settings.oColVis = $.extend({}, cv_default, data.amsDatatableColvisOptions);
       
  2082 															break;
       
  2083 														case 'fixedcolumns':
       
  2084 															ams.ajax.check($.fn.dataTable.FixedColumns,
       
  2085 																		   ams.baseURL + 'ext/jquery-dataTables-fixedColumns' + (ams.devmode ? '.js' : '.min.js'));
       
  2086 															break;
       
  2087 														case 'fixedheader':
       
  2088 															ams.ajax.check($.fn.dataTable.FixedHeader,
       
  2089 																		   ams.baseURL + 'ext/jquery-dataTables-fixedHeader' + (ams.devmode ? '.js' : '.min.js'));
       
  2090 															break;
       
  2091 														case 'keytable':
       
  2092 															ams.ajax.check(window.KeyTable,
       
  2093 																		   ams.baseURL + 'ext/jquery-dataTables-keyTable' + (ams.devmode ? '.js' : '.min.js'));
       
  2094 															break;
       
  2095 														case 'rowgrouping':
       
  2096 															ams.ajax.check($.fn.rowGrouping,
       
  2097 																		   ams.baseURL + 'ext/jquery-dataTables-rowGrouping' + (ams.devmode ? '.js' : '.min.js'));
       
  2098 															break;
       
  2099 														case 'rowreordering':
       
  2100 															ams.ajax.check($.fn.rowReordering,
       
  2101 																		   ams.baseURL + 'ext/jquery-dataTables-rowReordering' + (ams.devmode ? '.js' : '.min.js'));
       
  2102 															break;
       
  2103 														case 'scroller':
       
  2104 															ams.ajax.check($.fn.dataTable.Scroller,
       
  2105 																		   ams.baseURL + 'ext/jquery-dataTables-scroller' + (ams.devmode ? '.js' : '.min.js'));
       
  2106 															break;
       
  2107 														default:
       
  2108 															break;
       
  2109 													}
       
  2110 												}
       
  2111 											}
       
  2112 											settings = ams.executeFunctionByName(data.amsDatatableInitCallback, table, settings) || settings;
       
  2113 											var plugin = table.dataTable(settings);
       
  2114 											ams.executeFunctionByName(data.amsDatatableAfterInitCallback, table, plugin, settings);
       
  2115 											if (extensions.length > 0) {
       
  2116 												for (var index in extensions) {
       
  2117 													switch(extensions[index]) {
       
  2118 														case 'autofill':
       
  2119 															var af_settings = $.extend({}, data.amsDatatableAutofillOptions, settings.autofill);
       
  2120 															af_settings = ams.executeFunctionByName(data.amsDatatableAutofillInitCallback, table, af_settings) || af_settings;
       
  2121 															table.data('ams-autofill', data.amsDatatableAutofillConstructor === undefined
       
  2122 																						? new $.fn.dataTable.AutoFill(table, af_settings)
       
  2123 																						: ams.executeFunctionByName(data.amsDatatableAutofillConstructor, table, plugin, af_settings));
       
  2124 															break;
       
  2125 														case 'columnfilter':
       
  2126 															var cf_default = {
       
  2127 																sPlaceHolder: 'head:after'
       
  2128 															};
       
  2129 															var cf_settings = $.extend({}, cf_default, data.amsDatatableColumnfilterOptions, settings.columnfilter);
       
  2130 															cf_settings = ams.executeFunctionByName(data.amsDatatableColumnfilterInitCallback, table, cf_settings) || cf_settings;
       
  2131 															table.data('ams-columnfilter', data.amsDatatableColumnfilterConstructor === undefined
       
  2132 																						? plugin.columnFilter(cf_settings)
       
  2133 																						: ams.executeFunctionByName(data.amsDatatableColumnfilterConstructor, table, plugin, cf_settings));
       
  2134 															break;
       
  2135 														case 'fixedcolumns':
       
  2136 															var fc_settings = $.extend({}, data.amsDatatableFixedcolumnsOptions, settings.fixedcolumns);
       
  2137 															fc_settings = ams.executeFunctionByName(data.amsDatatableFixedcolumnsInitCallback, table, fc_settings) || fc_settings;
       
  2138 															table.data('ams-fixedcolumns', data.amsDatatableFixedcolumnsConstructor === undefined
       
  2139 																						? new $.fn.dataTable.FixedColumns(table, fc_settings)
       
  2140 																						: ams.executeFunctionByName(data.amsDatatableFixedcolumnsConstructor, table, plugin, fc_settings));
       
  2141 															break;
       
  2142 														case 'fixedheader':
       
  2143 															var fh_settings = $.extend({}, data.amsDatatableFixedheaderOptions, settings.fixedheader);
       
  2144 															fh_settings = ams.executeFunctionByName(data.amsDatatableFixedheadeInitCallback, table, fh_settings) || fh_settings;
       
  2145 															table.data('ams-fixedheader', data.amsDatatableFixedheaderConstructor === undefined
       
  2146 																						? new $.fn.dataTable.FixedHeader(table, fh_settings)
       
  2147 																						: ams.executeFunctionByName(data.amsDatatableFixedheaderConstructor, table, plugin, fh_settings));
       
  2148 															break;
       
  2149 														case 'keytable':
       
  2150 															var kt_default = {
       
  2151 																table: table.get(0),
       
  2152 																datatable: plugin
       
  2153 															};
       
  2154 															var kt_settings = $.extend({}, kt_default, data.amsDatatableKeytableOptions, settings.keytable);
       
  2155 															kt_settings = ams.executeFunctionByName(data.amsDatatableKeytableInitCallback, table, kt_settings) || kt_settings;
       
  2156 															table.data('ams-keytable', data.amsDatatableKeytableConstructor === undefined
       
  2157 																						? new KeyTable(kt_settings)
       
  2158 																						: ams.executeFunctionByName(data.amsDatatableKeytableConstructor, table, plugin, kt_settings));
       
  2159 															break;
       
  2160 														case 'rowgrouping':
       
  2161 															var rg_settings = $.extend({}, data.amsDatatableRowgroupingOptions, settings.rowgrouping);
       
  2162 															rg_settings = ams.executeFunctionByName(data.amsDatatableRowgroupingInitCallback, table, rg_settings) || rg_settings;
       
  2163 															table.data('ams-rowgrouping', data.amsDatatableRowgroupingConstructor === undefined
       
  2164 																						? table.rowGrouping(rg_settings)
       
  2165 																						: ams.executeFunctionByName(data.amsDatatableRowgroupingConstructor, table, plugin, rg_settings));
       
  2166 															break;
       
  2167 														case 'rowreordering':
       
  2168 															var rr_settings = $.extend({}, data.amsDatatableRowreorderingOptions, settings.rowreordering);
       
  2169 															rr_settings = ams.executeFunctionByName(data.amsDatatableRowreorderingInitCallback, table, rr_settings) || rr_settings;
       
  2170 															table.data('ams-rowreordering', data.amsDatatableRowreorderingConstructor === undefined
       
  2171 																						? table.rowReordering(rr_settings)
       
  2172 																						: ams.executeFunctionByName(data.amsDatatableRowreorderingConstructor, table, plugin, rr_settings));
       
  2173 															break;
       
  2174 														default:
       
  2175 															break;
       
  2176 													}
       
  2177 												}
       
  2178 											}
       
  2179 										});
       
  2180 								   });
       
  2181 				}
       
  2182 			},
       
  2183 
       
  2184 			/**
       
  2185 			 * Sparkline graphs
       
  2186 			 */
       
  2187 			graphs: function(element) {
       
  2188 				var graphs = $('.sparkline', element);
       
  2189 				if (graphs.length > 0) {
       
  2190 					ams.ajax.check(ams.graphs,
       
  2191 								   ams.baseURL + 'myams-graphs' + (ams.devmode ? '.js' : '.min.js'),
       
  2192 								   function() {
       
  2193 										ams.graphs.init(graphs);
       
  2194 								   });
       
  2195 				}
       
  2196 			},
       
  2197 
       
  2198 			/**
       
  2199 			 * Custom scrollbars
       
  2200 			 */
       
  2201 			scrollbars: function(element) {
       
  2202 				var scrollbars = $('.scrollbar', element);
       
  2203 				if (scrollbars.length > 0) {
       
  2204 					ams.ajax.check($.event.special.mousewheel,
       
  2205 								   ams.baseURL + 'ext/jquery-mousewheel.min.js',
       
  2206 								   function() {
       
  2207 										ams.ajax.check($.fn.mCustomScrollbar,
       
  2208 													   ams.baseURL + 'ext/jquery-mCustomScrollbar' + (ams.devmode ? '.js' : '.min.js'),
       
  2209 													   function(first_load) {
       
  2210 															if (first_load)
       
  2211 																ams.getCSS(ams.baseURL + '../css/ext/jquery-mCustomScrollbar.css',
       
  2212 																		  'jquery-mCustomScrollbar');
       
  2213 															scrollbars.each(function() {
       
  2214 																var scrollbar = $(this);
       
  2215 																var data = scrollbar.data();
       
  2216 																var data_options = {
       
  2217 																	theme: data.amsScrollbarTheme || 'light'
       
  2218 																};
       
  2219 																var settings = $.extend({}, data_options, data.amsScrollbarOptions);
       
  2220 																settings = ams.executeFunctionByName(data.amsScrollbarInitCallback, scrollbar, settings) || settings;
       
  2221 																var plugin = scrollbar.mCustomScrollbar(settings);
       
  2222 																ams.executeFunctionByName(data.amsScrollbarAfterInitCallback, scrollbar, plugin, settings);
       
  2223 															});
       
  2224 													   });
       
  2225 									});
       
  2226 				}
       
  2227 			}
       
  2228 		}
       
  2229 	};
       
  2230 
       
  2231 
       
  2232 	/**
       
  2233 	 * Callbacks management features
       
  2234 	 */
       
  2235 	MyAMS.callbacks = {
       
  2236 
       
  2237 		/**
       
  2238 		 * Initialize list of callbacks
       
  2239 		 *
       
  2240 		 * Callbacks are initialized each time a page content is loaded and integrated into page's DOM.
       
  2241 		 * Unlike plug-ins, callbacks are called once in current's content context but are not kept into
       
  2242 		 * browser's memory for future use.
       
  2243 		 * Callbacks are defined via several data attributes:
       
  2244 		 * - data-ams-callback: name of function callback
       
  2245 		 * - data-ams-callback-source: source URL of file containing callback's function; can contain variables names
       
  2246 		 *   if enclosed between braces
       
  2247 		 * - data-ams-callback-options: JSON object containing callback options
       
  2248 		 */
       
  2249 		init: function(element) {
       
  2250 			$('[data-ams-callback]', element).each(function() {
       
  2251 				var self = this;
       
  2252 				var data = $(self).data();
       
  2253 				var callback = ams.getFunctionByName(data.amsCallback);
       
  2254 				if (callback === undefined) {
       
  2255 					if (data.amsCallbackSource) {
       
  2256 						ams.getScript(data.amsCallbackSource,
       
  2257 									  function() {
       
  2258 										ams.executeFunctionByName(data.amsCallback, self, data.amsCallbackOptions);
       
  2259 									  });
       
  2260 					} else if (window.console) {
       
  2261 						console.warn("Undefined callback: " + data.amsCallback);
       
  2262 					}
       
  2263 				} else {
       
  2264 					callback.call(self, data.amsCallbackOptions);
       
  2265 				}
       
  2266 			})
       
  2267 		},
       
  2268 
       
  2269 		/**
       
  2270 		 * Standard alert message callback
       
  2271 		 *
       
  2272 		 * An alert is an HTML div included on top of a "parent's" body
       
  2273 		 * Alert options include:
       
  2274 		 * - a status: 'info', 'warning', 'error' or 'success'
       
  2275 		 * - a parent: jQuery selector of parent's element
       
  2276 		 * - a header: alert's title
       
  2277 		 * - a subtitle
       
  2278 		 * - a message body
       
  2279 		 * - a boolean margin marker; if true, a 10 pixels margin will be added to alert's body
       
  2280 		 */
       
  2281 		alert: function(options) {
       
  2282 			var data = $(this).data();
       
  2283 			var settings = $.extend({}, options, data.amsAlertOptions);
       
  2284 			var parent = $(data.amsAlertParent || settings.parent || this);
       
  2285 			var status = data.amsAlertStatus || settings.status || 'info';
       
  2286 			var header = data.amsAlertHeader || settings.header;
       
  2287 			var message = data.amsAlertMessage || settings.message;
       
  2288 			var subtitle = data.amsAlertSubtitle || settings.subtitle;
       
  2289 			var margin = data.amsAlertMargin === undefined ? (settings.margin === undefined ? false : settings.margin) : data.amsAlertMargin;
       
  2290 			ams.skin.alert(parent, status, header, message, subtitle, margin);
       
  2291 		},
       
  2292 
       
  2293 		/**
       
  2294 		 * Standard message box callback
       
  2295 		 *
       
  2296 		 * Message boxes are small informations messages displayed on bottom right page's corner
       
  2297 		 * Message box options include:
       
  2298 		 * - data-ams-messagebox-status: determines message box color; given as 'info', 'warning', 'error' or 'success'
       
  2299 		 * - data-ams-messagebox-title: message's title
       
  2300 		 * - data-ams-messagebox-content: message's HTML content
       
  2301 		 * - data-ams-messagebox-icon: if given, CSS class of message's icon
       
  2302 		 * - data-ams-messagebox-number: if given, a small error/message number displayed below message
       
  2303 		 * - data-ams-messagebox-timeout: if given, the message box will be automatically hidden passed this number
       
  2304 		 *   of milliseconds
       
  2305 		 * - data-ams-messagebox-callback: a callback's name, which will be called when message box is closed
       
  2306 		 */
       
  2307 		messageBox: function(options) {
       
  2308 			var data = $(this).data();
       
  2309 			var data_options = $.extend({}, options, data.amsMessageboxOptions);
       
  2310 			var settings = $.extend({}, data_options, {
       
  2311 				title: data.amsMessageboxTitle || data_options.title || '',
       
  2312 				content: data.amsMessageboxContent || data_options.content || '',
       
  2313 				icon: data.amsMessageboxIcon || data_options.icon,
       
  2314 				number: data.amsMessageboxNumber || data_options.number,
       
  2315 				timeout: data.amsMessageboxTimeout || data_options.icon
       
  2316 			});
       
  2317 			var status = data.amsMessageboxStatus || data_options.status || 'info';
       
  2318 			var callback = ams.getFunctionByName(data.amsMessageboxCallback || data_options.callback);
       
  2319 			ams.skin.messageBox(status, settings, callback);
       
  2320 		},
       
  2321 
       
  2322 		/**
       
  2323 		 * Standard small box callback
       
  2324 		 *
       
  2325 		 * Small boxes are notification messages displayed on top right page's corner.
       
  2326 		 * Small box options include:
       
  2327 		 * - data-ams-smallbox-status: determines message box color; given as 'info', 'warning', 'error' or 'success'
       
  2328 		 * - data-ams-smallbox-title: message's title
       
  2329 		 * - data-ams-smallbox-content: message's HTML content
       
  2330 		 * - data-ams-smallbox-icon: if given, CSS class of message's icon
       
  2331 		 * - data-ams-smallbox-icon-small: if given, CSS class of small message's icon
       
  2332 		 * - data-ams-smallbox-timeout: if given, the message box will be automatically hidden passed this number
       
  2333 		 *   of milliseconds
       
  2334 		 * - data-ams-smallbox-callback: a callback's name, which will be called when message box is closed
       
  2335 		 */
       
  2336 		smallBox: function(options) {
       
  2337 			var data = $(this).data();
       
  2338 			var data_options = $.extend({}, options, data.amsSmallboxOptions);
       
  2339 			var settings = $.extend({}, data_options, {
       
  2340 				title: data.amsSmallboxTitle || data_options.title || '',
       
  2341 				content: data.amsSmallboxContent || data_options.content || '',
       
  2342 				icon: data.amsSmallboxIcon || data_options.icon,
       
  2343 				iconSmall: data.amsSmallboxIconSmall || data_options.iconSmall,
       
  2344 				timeout: data.amsSmallboxTimeout || data_options.icon
       
  2345 			});
       
  2346 			var status = data.amsSmallboxStatus || data_options.status || 'info';
       
  2347 			var callback = ams.getFunctionByName(data.amsSmallboxCallback || data_options.callback);
       
  2348 			ams.skin.smallBox(status, settings, callback);
       
  2349 		}
       
  2350 	};
       
  2351 
       
  2352 
       
  2353 	/**
       
  2354 	 * Events management
       
  2355 	 */
       
  2356 	MyAMS.events = {
       
  2357 
       
  2358 		/**
       
  2359 		 * Initialize events listeners
       
  2360 		 *
       
  2361 		 * "data-ams-events-handlers" is a data attribute containing a JSON object where:
       
  2362 		 *  - each key is an event name
       
  2363 		 *  - value is a callback name.
       
  2364 		 * For example: data-ams-events-handlers='{"change": "MyAPP.events.changeListener"}'
       
  2365 		 */
       
  2366 		init: function(element) {
       
  2367 			$('[data-ams-events-handlers]', element).each(function() {
       
  2368 				var element = $(this);
       
  2369 				var handlers = element.data('ams-events-handlers');
       
  2370 				for (var event in handlers) {
       
  2371 					element.on(event, ams.getFunctionByName(handlers[event]));
       
  2372 				}
       
  2373 			});
       
  2374 		}
       
  2375 	};
       
  2376 
       
  2377 
       
  2378 	/**
       
  2379 	 * Generic skin features
       
  2380 	 */
       
  2381 	MyAMS.skin = {
       
  2382 
       
  2383 		/**
       
  2384 		 * Compute navigation page height
       
  2385 		 */
       
  2386 		_setPageHeight: function() {
       
  2387 			var main_height = $('#main').height();
       
  2388 			var menu_height = ams.left_panel.height();
       
  2389 			var window_height = $(window).height() - ams.navbar_height;
       
  2390 			if (main_height > window_height) {
       
  2391 				ams.left_panel.css('min-height', main_height);
       
  2392 				ams.root.css('min-height', main_height + ams.navbar_height);
       
  2393 			} else {
       
  2394 				ams.left_panel.css('min-height', window_height);
       
  2395 				ams.root.css('min-height', window_height);
       
  2396 			}
       
  2397 		},
       
  2398 
       
  2399 		/**
       
  2400 		 * Check width for mobile devices
       
  2401 		 */
       
  2402 		_checkMobileWidth: function() {
       
  2403 			if ($(window).width() < 979)
       
  2404 				ams.root.addClass('mobile-view-activated')
       
  2405 			else if (ams.root.hasClass('mobile-view-activated'))
       
  2406 				ams.root.removeClass('mobile-view-activated');
       
  2407 		},
       
  2408 
       
  2409 		/**
       
  2410 		 * Show/hide shortcut buttons
       
  2411 		 */
       
  2412 		_showShortcutButtons: function() {
       
  2413 			ams.shortcuts.animate({
       
  2414 				height: 'show'
       
  2415 			}, 200, 'easeOutCirc');
       
  2416 			ams.root.addClass('shortcut-on');
       
  2417 		},
       
  2418 		
       
  2419 		_hideShortcutButtons: function() {
       
  2420 			ams.shortcuts.animate({
       
  2421 				height: 'hide'
       
  2422 			}, 300, 'easeOutCirc');
       
  2423 			ams.root.removeClass('shortcut-on');
       
  2424 		},
       
  2425 
       
  2426 		/**
       
  2427 		 * Check notification badge
       
  2428 		 */
       
  2429 		checkNotification: function() {
       
  2430 			$this = $('#activity > .badge');
       
  2431 			if (parseInt($this.text()) > 0)
       
  2432 				$this.removeClass("hidden")
       
  2433 					 .addClass("bg-color-red bounceIn animated");
       
  2434 			else
       
  2435 				$this.addClass("hidden")
       
  2436 					 .removeClass("bg-color-red bounceIn animated");
       
  2437 		},
       
  2438 
       
  2439 		/**
       
  2440 		 * Initialize desktop and mobile widgets
       
  2441 		 */
       
  2442 		_initDesktopWidgets: function(element) {
       
  2443 			if (ams.enable_widgets) {
       
  2444 				var widgets = $('.ams-widget', element);
       
  2445 				if (widgets.length > 0)
       
  2446 					ams.ajax.check($.fn.MyAMSWidget,
       
  2447 								   ams.baseURL + 'myams-widgets' + (ams.devmode ? '.js' : '.min.js'),
       
  2448 								   function() {
       
  2449 										widgets.each(function() {
       
  2450 											var widget = $(this);
       
  2451 											var data = widget.data();
       
  2452 											var data_options = {
       
  2453 												deleteSettingsKey: '#deletesettingskey-options',
       
  2454 												deletePositionKey: '#deletepositionkey-options'
       
  2455 											};
       
  2456 											var settings = $.extend({}, data_options, data.amsWidgetOptions);
       
  2457 											settings = ams.executeFunctionByName(data.amsWidgetInitcallback, widget, settings) || settings;
       
  2458 											widget.MyAMSWidget(settings);
       
  2459 										});
       
  2460 										MyAMSWidget.initWidgetsGrid($('.ams-widget-grid', element));
       
  2461 									});
       
  2462 			}
       
  2463 		},
       
  2464 
       
  2465 		_initMobileWidgets: function(element) {
       
  2466 			if (ams.enable_mobile && ams.enable_widgets)
       
  2467 				ams.skin._initDesktopWidgets(element);
       
  2468 		},
       
  2469 
       
  2470 		/**
       
  2471 		 * Add an alert on top of a container
       
  2472 		 *
       
  2473 		 * @parent: parent container where the alert will be displayed
       
  2474 		 * @status: info, success, warning or danger
       
  2475 		 * @header: alert header
       
  2476 		 * @message: main alert message
       
  2477 		 * @subtitle: optional subtitle
       
  2478 		 * @margin: if true, a margin will be displayed around alert
       
  2479 		 */
       
  2480 		alert: function(parent, status, header, message, subtitle, margin) {
       
  2481 			$('.alert', parent).remove();
       
  2482 			if (status == 'error')
       
  2483 				var status = 'danger';
       
  2484 			var content = '<div class="' + (margin ? 'margin-10' : '') + ' alert alert-block alert-' + status + ' fade in">' +
       
  2485 							'<a class="close" data-dismiss="alert"><i class="fa fa-check"></i></a>' +
       
  2486 							'<h4 class="alert-heading">' +
       
  2487 								'<i class="fa fa-fw fa-warning"></i> ' + header +
       
  2488 							'</h4>' +
       
  2489 							(subtitle ? ('<p>' + subtitle + '</p>') : '') +
       
  2490 							'<ul>';
       
  2491 			if (typeof(message) == 'string')
       
  2492 				content += '<li>' + message + '</li>';
       
  2493 			else {
       
  2494 				for (var index in message) {
       
  2495 					if (!$.isNumeric(index))  // IE check
       
  2496 						continue;
       
  2497 					content += '<li>' + message[index] + '</li>';
       
  2498 				};
       
  2499 			}
       
  2500 			content += '</ul></div>';
       
  2501 			var alert = $(content).prependTo(parent);
       
  2502 			if (parent.exists) {
       
  2503 				ams.ajax.check($.scrollTo,
       
  2504 							   ams.baseURL + 'ext/jquery-scrollTo.min.js',
       
  2505 							   function() {
       
  2506 									$.scrollTo(parent, {offset: {top: -50}});
       
  2507 							   });
       
  2508 			}
       
  2509 		},
       
  2510 
       
  2511 		/**
       
  2512 		 * Big message box
       
  2513 		 */
       
  2514 		bigBox: function(options, callback) {
       
  2515 			ams.ajax.check(ams.notify,
       
  2516 						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
       
  2517 						   function() {
       
  2518 								ams.notify.messageBox(options, callback);
       
  2519 						   });
       
  2520 		},
       
  2521 
       
  2522 		/**
       
  2523 		 * Medium notification message box, displayed on page's bottom right
       
  2524 		 */
       
  2525 		messageBox: function(status, options, callback) {
       
  2526 			if (typeof(status) == 'object') {
       
  2527 				var callback = options;
       
  2528 				var options = status || {};
       
  2529 				var status = 'info';
       
  2530 			}
       
  2531 			ams.ajax.check(ams.notify,
       
  2532 						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
       
  2533 						   function() {
       
  2534 								switch (status) {
       
  2535 									case 'error':
       
  2536 									case 'danger':
       
  2537 										options.color = '#C46A69';
       
  2538 										break;
       
  2539 									case 'warning':
       
  2540 										options.color = '#C79121';
       
  2541 										break;
       
  2542 									case 'success':
       
  2543 										options.color = '#739E73';
       
  2544 										break;
       
  2545 									default:
       
  2546 										options.color = options.color || '#3276B1';
       
  2547 								}
       
  2548 								options.sound = false;
       
  2549 								ams.notify.bigBox(options, callback);
       
  2550 						   });
       
  2551 		},
       
  2552 
       
  2553 		/**
       
  2554 		 * Small notification message box, displayed on page's top right
       
  2555 		 */
       
  2556 		smallBox: function(status, options, callback) {
       
  2557 			if (typeof(status) == 'object') {
       
  2558 				var callback = options;
       
  2559 				var options = status || {};
       
  2560 				var status = 'info';
       
  2561 			}
       
  2562 			ams.ajax.check(ams.notify,
       
  2563 						   ams.baseURL + 'myams-notify' + (ams.devmode ? '.js' : '.min.js'),
       
  2564 						   function() {
       
  2565 								switch (status) {
       
  2566 									case 'error':
       
  2567 									case 'danger':
       
  2568 										options.color = '#C46A69';
       
  2569 										break;
       
  2570 									case 'warning':
       
  2571 										options.color = '#C79121';
       
  2572 										break;
       
  2573 									case 'success':
       
  2574 										options.color = '#739E73';
       
  2575 										break;
       
  2576 									default:
       
  2577 										options.color = options.color || '#3276B1';
       
  2578 								}
       
  2579 								options.sound = false;
       
  2580 								ams.notify.smallBox(options, callback);
       
  2581 						   });
       
  2582 		},
       
  2583 
       
  2584 		/**
       
  2585 		 * Initialize breadcrumbs based on active menu position
       
  2586 		 */
       
  2587 		_drawBreadCrumb: function() {
       
  2588 			var crumb = $('#ribbon OL.breadcrumb');
       
  2589 			crumb.empty()
       
  2590 				 .append($('<li></li>').append($('<a></a>').text(ams.i18n.HOME)
       
  2591 				 										   .attr('href', $('nav a[href!="#"]:first').attr('href'))));
       
  2592 			$('nav LI.active >A').each(function() {
       
  2593 				var menu = $(this);
       
  2594 				var body = $.trim(menu.clone()
       
  2595 									  .children(".badge")
       
  2596 									  .remove()
       
  2597 									  .end()
       
  2598 									  .text());
       
  2599 				var item = $("<li></li>").append(menu.attr('href').replace(/^#/, '')
       
  2600 													? $("<a></a>").html(body)
       
  2601 																  .attr('href', menu.attr('href'))
       
  2602 													: body);
       
  2603 				crumb.append(item);
       
  2604 			});
       
  2605 		},
       
  2606 
       
  2607 		/**
       
  2608 		 * Check URL matching current location hash
       
  2609 		 */
       
  2610 		checkURL: function() {
       
  2611 
       
  2612 			function updateActiveMenus(menu) {
       
  2613 				$('nav .active').removeClass('active');
       
  2614 				menu.addClass('open')
       
  2615 					.addClass('active');
       
  2616 				menu.parents('li').addClass('open active')
       
  2617 								  .children('ul').addClass('active')
       
  2618 												 .show();
       
  2619 				menu.parents('li:first').removeClass('open');
       
  2620 				menu.parents('ul').addClass(menu.attr('href').replace(/^#/, '') ? 'active' : '')
       
  2621 								  .show();
       
  2622 			}
       
  2623 
       
  2624 			var hash = location.hash;
       
  2625 			var url = hash.replace(/^#/, '');
       
  2626 			if (url) {
       
  2627 				var container = $('#content');
       
  2628 				if (!container.exists())
       
  2629 					container = $('body');
       
  2630 				var menu = $('nav A[href="' + hash + '"]');
       
  2631 				if (menu.exists())
       
  2632 					updateActiveMenus(menu);
       
  2633 				ams.skin.loadURL(url, container);
       
  2634 				document.title = $('[data-ams-page-title]:first', container).data('ams-page-title') ||
       
  2635 								 $('nav A[href="' + hash + '"]').attr('title') ||
       
  2636 								 document.title;
       
  2637 			} else {
       
  2638 				var active_url = $('[data-ams-active-menu]').data('ams-active-menu');
       
  2639 				if (active_url) {
       
  2640 					menu = $('nav A[href="' + active_url + '"]');
       
  2641 				} else {
       
  2642 					menu = $('nav >UL >LI >A[href!="#"]').first();
       
  2643 				}
       
  2644 				if (menu.exists()) {
       
  2645 					updateActiveMenus(menu);
       
  2646 					if (active_url)
       
  2647 						ams.skin._drawBreadCrumb();
       
  2648 					else
       
  2649 						window.location.hash = menu.attr('href');
       
  2650 				}
       
  2651 			}
       
  2652 		},
       
  2653 
       
  2654 		/**
       
  2655 		 * Load given URL into container
       
  2656 		 */
       
  2657 		loadURL: function(url, container, options, callback) {
       
  2658 			if (url.startsWith('#')) {
       
  2659 				url = url.substr(1);
       
  2660 			}
       
  2661 			if (typeof(options) == 'function') {
       
  2662 				callback = options;
       
  2663 				options = {};
       
  2664 			}
       
  2665 			var container = $(container);
       
  2666 			var defaults = {
       
  2667 				type: 'GET',
       
  2668 				url: url,
       
  2669 				dataType: 'html',
       
  2670 				cache: false,
       
  2671 				beforeSend: function() {
       
  2672 					container.html('<h1><i class="fa fa-cog fa-spin"></i> Loading... </h1>');
       
  2673 					if (container[0] == $('#content')[0]) {
       
  2674 						ams.skin._drawBreadCrumb();
       
  2675 						document.title = $('.breadcrumb LI:last-child').text();
       
  2676 						$('html, body').animate({scrollTop: 0}, 'fast');
       
  2677 					} else {
       
  2678 						container.animate({scrollTop: 0}, 'fast');
       
  2679 					}
       
  2680 				},
       
  2681 				success: function(data, status, request) {
       
  2682 					if (callback)
       
  2683 						ams.executeFunctionByName(callback, this, data, status, request, options);
       
  2684 					else {
       
  2685 						var request_data = ams.ajax.getResponse(request);
       
  2686 						var data_type = request_data.content_type;
       
  2687 						var result = request_data.data;
       
  2688 						switch (data_type) {
       
  2689 							case 'json':
       
  2690 								ams.ajax.handleJSON(result, container);
       
  2691 								break;
       
  2692 							case 'script':
       
  2693 								break;
       
  2694 							case 'xml':
       
  2695 								break;
       
  2696 							case 'html':
       
  2697 							case 'text':
       
  2698 							default:
       
  2699 								container.parents('.hidden').removeClass('hidden');
       
  2700 								$('.alert', container.parents('.alerts-container')).remove();
       
  2701 								container.css({opacity: '0.0'})
       
  2702 										 .html(data)
       
  2703 										 .delay(50)
       
  2704 										 .animate({opacity: '1.0'}, 300);
       
  2705 								ams.initContent(container);
       
  2706 						}
       
  2707 					}
       
  2708 				},
       
  2709 				error: function(request, options, error) {
       
  2710 					container.html('<h3 class="error"><i class="fa fa-warning txt-color-orangeDark"></i> ' +
       
  2711 								   ams.i18n.ERROR + error + '</h3>' +
       
  2712 								   request.responseText);
       
  2713 				},
       
  2714 				async: false
       
  2715 			};
       
  2716 			var settings = $.extend({}, defaults, options);
       
  2717 			$.ajax(settings);
       
  2718 		},
       
  2719 
       
  2720 		logout: function() {
       
  2721 			window.location = ams.loginURL;
       
  2722 		}
       
  2723 	};
       
  2724 
       
  2725 
       
  2726 	/**
       
  2727 	 * Main page initialization
       
  2728 	 * This code is called only once to register global events and callbacks
       
  2729 	 */
       
  2730 	MyAMS.initPage = function() {
       
  2731 
       
  2732 		/* Init main components */
       
  2733 		ams.root = $('BODY');
       
  2734 		ams.left_panel = $('#left-panel');
       
  2735 		ams.shortcuts = $('#shortcut');
       
  2736 
       
  2737 		// Init main AJAX events
       
  2738 		$(document).ajaxError(ams.error.ajax);
       
  2739 
       
  2740 		// Check mobile/desktop
       
  2741 		if (!ams.isMobile) {
       
  2742 			ams.root.addClass('desktop-detected');
       
  2743 			ams.device = 'desktop';
       
  2744 		} else {
       
  2745 			ams.root.addClass('mobile-detected');
       
  2746 			ams.device = 'mobile';
       
  2747 			if (ams.enable_fastclick) {
       
  2748 				ams.ajax.check($.fn.noClickDelay,
       
  2749 							   ams.baseURL + '/ext/jquery-smartclick' + (ams.devmode ? '.js' : '.min.js'),
       
  2750 							   function() {
       
  2751 								   $('NAV UL A').noClickDelay();
       
  2752 								   $('#hide-menu A').noClickDelay();
       
  2753 							   });
       
  2754 			}
       
  2755 		}
       
  2756 
       
  2757 		// Hide menu button
       
  2758 		$('#hide-menu >:first-child > A').click(function(e) {
       
  2759 			$('BODY').toggleClass("hidden-menu");
       
  2760 			e.preventDefault();
       
  2761 		});
       
  2762 
       
  2763 		// Switch shortcuts
       
  2764 		$('#show-shortcut').click(function(e) {
       
  2765 			if (ams.shortcuts.is(":visible")) {
       
  2766 				ams.skin._hideShortcutButtons();
       
  2767 			} else {
       
  2768 				ams.skin._showShortcutButtons();
       
  2769 			}
       
  2770 			e.preventDefault();
       
  2771 		});
       
  2772 
       
  2773 		$(document).mouseup(function(e) {
       
  2774 			if (!ams.shortcuts.is(e.target)
       
  2775 				&& ams.shortcuts.has(e.target).length === 0) {
       
  2776 				ams.skin._hideShortcutButtons();
       
  2777 			}
       
  2778 		});
       
  2779 
       
  2780 		// Show & hide mobile search field
       
  2781 		$('#search-mobile').click(function() {
       
  2782 			ams.root.addClass('search-mobile');
       
  2783 		});
       
  2784 
       
  2785 		$('#cancel-search-js').click(function() {
       
  2786 			ams.root.removeClass('search-mobile');
       
  2787 		});
       
  2788 
       
  2789 		// Activity badge
       
  2790 		$('#activity').click(function(e) {
       
  2791 			var activity = $(this);
       
  2792 			var dropdown = activity.next('.ajax-dropdown');
       
  2793 			if (!dropdown.is(':visible')) {
       
  2794 				dropdown.css('left', activity.position().left - dropdown.innerWidth() / 2 + activity.innerWidth() / 2)
       
  2795 						.fadeIn(150);
       
  2796 				activity.addClass('active');
       
  2797 			} else {
       
  2798 				dropdown.fadeOut(150);
       
  2799 				activity.removeClass('active')
       
  2800 			}
       
  2801 			e.preventDefault();
       
  2802 		});
       
  2803 		ams.skin.checkNotification();
       
  2804 
       
  2805 		$(document).mouseup(function(e) {
       
  2806 			var dropdown = $('.ajax-dropdown');
       
  2807 			if (!dropdown.is(e.target) &&
       
  2808 				dropdown.has(e.target).length === 0) {
       
  2809 				dropdown.fadeOut(150)
       
  2810 						.prev().removeClass("active");
       
  2811 			}
       
  2812 		});
       
  2813 
       
  2814 		$('input[name="activity"]').change(function() {
       
  2815 			var url = $(this).data('ams-url');
       
  2816 			container = $('.ajax-notifications');
       
  2817 			ams.skin.loadURL(url, container);
       
  2818 		});
       
  2819 
       
  2820 		// Logout button
       
  2821 		$('#logout a').click(function(e) {
       
  2822 			e.preventDefault();
       
  2823 			//get the link
       
  2824 			ams.loginURL = $(this).attr('href');
       
  2825 			// ask verification
       
  2826 			ams.skin.bigBox({
       
  2827 				title : "<i class='fa fa-sign-out txt-color-orangeDark'></i> " + ams.i18n.LOGOUT +
       
  2828 						" <span class='txt-color-orangeDark'><strong>" + $('#show-shortcut').text() + "</strong></span> ?",
       
  2829 				content : ams.i18n.LOGOUT_COMMENT,
       
  2830 				buttons : '['+ams.i18n.BTN_NO+']['+ams.i18n.BTN_YES+']'
       
  2831 			}, function(ButtonPressed) {
       
  2832 				if (ButtonPressed == ams.i18n.BTN_YES) {
       
  2833 					ams.root.addClass('animated fadeOutUp');
       
  2834 					setTimeout(ams.skin.logout, 1000)
       
  2835 				}
       
  2836 			});
       
  2837 		});
       
  2838 
       
  2839 		// Initialize left nav
       
  2840 		$('NAV UL').myams_menu({
       
  2841 			accordion : true,
       
  2842 			speed : ams.menu_speed
       
  2843 		});
       
  2844 
       
  2845 		// Left navigation collapser
       
  2846 		$('.minifyme').click(function(e) {
       
  2847 			$('BODY').toggleClass("minified");
       
  2848 			$(this).effect("highlight", {}, 500);
       
  2849 			e.preventDefault();
       
  2850 		});
       
  2851 
       
  2852 		// Reset widgets
       
  2853 		$('#refresh').click(function(e) {
       
  2854 			ams.skin.bigBox({
       
  2855 				title: "<i class='fa fa-refresh' style='color: green'></i> " + ams.i18n.CLEAR_STORAGE_TITLE,
       
  2856 				content: ams.i18n.CLEAR_STORAGE_CONTENT,
       
  2857 				buttons: '['+ams.i18n.BTN_CANCEL+']['+ams.i18n.BTN_OK+']'
       
  2858 			}, function(buttonPressed) {
       
  2859 				if (buttonPressed == ams.i18n.BTN_OK && localStorage) {
       
  2860 					localStorage.clear();
       
  2861 					location.reload();
       
  2862 				}
       
  2863 			});
       
  2864 			e.preventDefault();
       
  2865 		});
       
  2866 
       
  2867 		// Check active pop-overs
       
  2868 		$('BODY').on('click', function(e) {
       
  2869 			var element = $(this);
       
  2870 			if (!element.is(e.target) &&
       
  2871 				element.has(e.target).length === 0 &&
       
  2872 				$('.popover').has(e.target).length === 0)
       
  2873 				element.popover('hide');
       
  2874 		});
       
  2875 
       
  2876 		// Resize events
       
  2877 		ams.ajax.check($.resize,
       
  2878 					   ams.baseURL + 'ext/jquery-resize' + (ams.devmode ? '.js' : '.min.js'),
       
  2879 					   function() {
       
  2880 						   $('#main').resize(function() {
       
  2881 							   ams.skin._setPageHeight();
       
  2882 							   ams.skin._checkMobileWidth();
       
  2883 						   });
       
  2884 						   $('nav').resize(function() {
       
  2885 							   ams.skin._setPageHeight();
       
  2886 						   });
       
  2887 					   });
       
  2888 
       
  2889 		// Init AJAX navigation
       
  2890 		if (ams.ajax_nav) {
       
  2891 			if ($('nav').length > 0)
       
  2892 				ams.skin.checkURL();
       
  2893 			$(document).on('click', 'a[href="#"]', function(e) {
       
  2894 				e.preventDefault();
       
  2895 			});
       
  2896 			$(document).on('click', 'a[href!="#"]:not([data-toggle]), [data-ams-url]:not([data-toggle])', function(e) {
       
  2897 				var link = $(e.currentTarget);
       
  2898 				var href = link.attr('href') || link.data('ams-url');
       
  2899 				if (!href || href.startsWith('javascript:') || link.attr('target'))
       
  2900 					return;
       
  2901 				e.preventDefault();
       
  2902 				var target = link.data('ams-target');
       
  2903 				if (target) {
       
  2904 					ams.skin.loadURL(href, target, link.data('ams-link-options'), link.data('ams-link-callback'));
       
  2905 				} else {
       
  2906 					if (href.startsWith('#')) {
       
  2907 						if (href != location.hash) {
       
  2908 							if (ams.root.hasClass('mobile-view-activated')) {
       
  2909 								ams.root.removeClass('hidden-menu');
       
  2910 								window.setTimeout(function() {
       
  2911 									window.location.hash = href;
       
  2912 								}, 150);
       
  2913 							} else
       
  2914 								window.location.hash = href;
       
  2915 						}
       
  2916 					} else
       
  2917 						window.location = href;
       
  2918 				}
       
  2919 			});
       
  2920 			$(document).on('click', 'a[target="_blank"]', function(e) {
       
  2921 				e.preventDefault();
       
  2922 				window.open($(e.currentTarget).attr('href'));
       
  2923 			});
       
  2924 			$(document).on('click', 'a[target="_top"]', function(e) {
       
  2925 				e.preventDefault();
       
  2926 				window.location = $(e.currentTarget).attr('href');
       
  2927 			});
       
  2928 
       
  2929 			// Check URL when hash changed
       
  2930 			$(window).on('hashchange', ams.skin.checkURL);
       
  2931 		}
       
  2932 
       
  2933 		// Initialize modal dialogs links
       
  2934 		$(document).off('click.modal')
       
  2935 				   .on('click', '[data-toggle="modal"]', function(e) {
       
  2936 			e.preventDefault();
       
  2937 			var source = $(this);
       
  2938 			ams.dialog.open(source);
       
  2939 			if (source.parents('#shortcut').exists())
       
  2940 				setTimeout(ams.skin._hideShortcutButtons, 300);
       
  2941 		});
       
  2942 		$(document).on('shown.bs.modal', ams.dialog.shown);
       
  2943 
       
  2944 		// Initialize form buttons
       
  2945 		$(document).on('click', 'button[type="submit"], button.submit', function() {
       
  2946 			var button = $(this);
       
  2947 			$(button.get(0).form).data('ams-submit-button', button);
       
  2948 		});
       
  2949 
       
  2950 		// Initialize custom click handlers
       
  2951 		$(document).on('click', '[data-ams-click-handler]', function(e) {
       
  2952 			var source = $(this);
       
  2953 			var data = source.data();
       
  2954 			if (data.amsClickHandler) {
       
  2955 				if (data.amsClickKeepDefault !== true)
       
  2956 					e.preventDefault();
       
  2957 				var callback = ams.getFunctionByName(data.amsClickHandler);
       
  2958 				if (callback !== undefined)
       
  2959 					callback.call(source, data.amsClickHandlerOptions);
       
  2960 			}
       
  2961 		});
       
  2962 
       
  2963 		// Initialize custom change handlers
       
  2964 		$(document).on('change', '[data-ams-change-handler]', function(e) {
       
  2965 			var source = $(this);
       
  2966 			var data = source.data();
       
  2967 			if (data.amsChangeHandler) {
       
  2968 				if (data.amsChangeKeepDefault !== true)
       
  2969 					e.preventDefault();
       
  2970 				var callback = ams.getFunctionByName(data.amsChangeHandler);
       
  2971 				if (callback !== undefined)
       
  2972 					callback.call(source, data.amsChangeHandlerOptions);
       
  2973 			}
       
  2974 		});
       
  2975 
       
  2976 		// Handle update on file upload placeholder
       
  2977 		$(document).on('change', 'input[type="file"]', function(e) {
       
  2978 			e.preventDefault();
       
  2979 			var input = $(this);
       
  2980 			var button = input.parent('.button');
       
  2981 			if (button.exists() && button.parent().hasClass('input-file')) {
       
  2982 				button.next('input[type="text"]').val(input.val());
       
  2983 			}
       
  2984 		});
       
  2985 
       
  2986 		// Disable clicks on disabled tabs
       
  2987 		$("a[data-toggle=tab]", ".nav-tabs").on("click", function(e) {
       
  2988 			if ($(this).parent('li').hasClass("disabled")) {
       
  2989 				e.preventDefault();
       
  2990 				return false;
       
  2991 			}
       
  2992 		});
       
  2993 
       
  2994 		// Enable tabs dynamic loading
       
  2995 		$(document).on('show.bs.tab', function(e) {
       
  2996 			var link = $(e.target);
       
  2997 			var data = link.data();
       
  2998 			if (data.amsUrl) {
       
  2999 				if (data.amsTabLoaded)
       
  3000 					return;
       
  3001 				ams.skin.loadURL(data.amsUrl, link.attr('href'));
       
  3002 				if (data.amsTabLoadOnce)
       
  3003 					link.data('ams-tab-loaded', true);
       
  3004 			}
       
  3005 		});
       
  3006 
       
  3007 		// Init plug-ins required by main layout
       
  3008 		ams.plugins.enabled.hint(document);
       
  3009 
       
  3010 		// Init content when not loaded by AJAX request
       
  3011 		if (window.location.hash == '')
       
  3012 			ams.initContent(document);
       
  3013 
       
  3014 	};
       
  3015 
       
  3016 
       
  3017 	/**
       
  3018 	 * Main content plug-ins initializer
       
  3019 	 * This code is called to initialize plugins, callbacks and events listeners each time an HTML content
       
  3020 	 * is loaded dynamically from remote server.
       
  3021 	 */
       
  3022 	MyAMS.initContent = function(element) {
       
  3023 
       
  3024 		// Remove left tips
       
  3025 		$('.tipsy').remove();
       
  3026 
       
  3027 		// Activate tooltips and popovers
       
  3028 		$("[rel=tooltip]", element).tooltip();
       
  3029 		$("[rel=popover]", element).popover();
       
  3030 
       
  3031 		// Activate popovers with hover states
       
  3032 		$("[rel=popover-hover]", element).popover({
       
  3033 			trigger : "hover"
       
  3034 		});
       
  3035 
       
  3036 		// Init registered plug-ins and callbacks
       
  3037 		ams.plugins.init(element);
       
  3038 		ams.callbacks.init(element);
       
  3039 		ams.events.init(element);
       
  3040 
       
  3041 		// Initialize widgets
       
  3042 		if (ams.device === 'desktop')
       
  3043 			ams.skin._initDesktopWidgets(element);
       
  3044 		else
       
  3045 			ams.skin._initMobileWidgets(element);
       
  3046 		ams.skin._setPageHeight();
       
  3047 
       
  3048 	};
       
  3049 
       
  3050 
       
  3051 	/**
       
  3052 	 * MyAMS locale strings
       
  3053 	 */
       
  3054 	MyAMS.i18n = {
       
  3055 
       
  3056 		INFO: "Information",
       
  3057 		WARNING: "!! WARNING !!",
       
  3058 		ERROR: "ERROR: ",
       
  3059 
       
  3060 		WAIT: "Please wait!",
       
  3061 		FORM_SUBMITTED: "This form was already submitted...",
       
  3062 		ERROR_OCCURED: "An error occured!",
       
  3063 		ERRORS_OCCURED: "Some errors occured!",
       
  3064 
       
  3065 		BAD_LOGIN_TITLE: "Bad login!",
       
  3066 		BAD_LOGIN_MESSAGE: "Your anthentication credentials didn't allow you to open a session; " +
       
  3067 						   "please check your credentials or contact administrator.",
       
  3068 
       
  3069 		CONFIRM: "Confirm",
       
  3070 		CONFIRM_REMOVE: "Removing this content can't be undone. Do you confirm?",
       
  3071 
       
  3072 		CLEAR_STORAGE_TITLE: "Clear Local Storage",
       
  3073 		CLEAR_STORAGE_CONTENT: "Would you like to RESET all your saved widgets and clear LocalStorage?",
       
  3074 
       
  3075 		BTN_OK: "OK",
       
  3076 		BTN_CANCEL: "Cancel",
       
  3077 		BTN_YES: "Yes",
       
  3078 		BTN_NO: "No",
       
  3079 
       
  3080 		NO_UPDATE: "No changes were applied.",
       
  3081 		DATA_UPDATED: "Data successfully updated.",
       
  3082 
       
  3083 		HOME: "Home",
       
  3084 		LOGOUT: "Logout ?",
       
  3085 		LOGOUT_COMMENT: "You can improve your security further after logging out by closing this opened browser",
       
  3086 
       
  3087 		SELECT2_PLURAL: 's',
       
  3088 		SELECT2_NOMATCHES: "No matches found",
       
  3089 		SELECT2_SEARCHING: "Searching...",
       
  3090 		SELECT2_LOADMORE: "Loading more results...",
       
  3091 		SELECT2_INPUT_TOOSHORT: "Please enter {0} more character{1}",
       
  3092 		SELECT2_INPUT_TOOLONG: "Please delete {0} character{1}",
       
  3093 		SELECT2_SELECTION_TOOBIG: "You can only select {0} item{1}",
       
  3094 
       
  3095 		DT_COLUMNS: "Columns"
       
  3096 
       
  3097 	};
       
  3098 
       
  3099 
       
  3100 	$(document).ready(function() {
       
  3101 		$ = jQuery.noConflict();
       
  3102 		var lang = $('HTML').attr('lang') || $('HTML').attr('xml:lang');
       
  3103 		if (lang && !lang.startsWith('en'))
       
  3104 			MyAMS.getScript(MyAMS.baseURL + 'i18n/myams_' + lang.substr(0,2) + '.js', function() {
       
  3105 				MyAMS.initPage();
       
  3106 			});
       
  3107 		else {
       
  3108 			MyAMS.initPage();
       
  3109 		}
       
  3110 	});
       
  3111 
       
  3112 })(jQuery);