src/pyams_skin/resources/js/myams.js
changeset 245 79635dd71cca
parent 241 4b68a8759677
child 248 86b71518e457
--- a/src/pyams_skin/resources/js/myams.js	Sun Nov 26 10:09:52 2017 +0100
+++ b/src/pyams_skin/resources/js/myams.js	Sun Nov 26 10:14:21 2017 +0100
@@ -2,7 +2,7 @@
  * MyAMS
  * « My Application Management Skin »
  *
- * $Tag$ (rev. 1)
+ * $Tag: 0.1.11 $ (rev. 1)
  * A bootstrap based application/administration skin
  *
  * Custom administration and application skin tools
@@ -3205,6 +3205,69 @@
 			},
 
 			/**
+			 * Treeview plug-in
+			 */
+			treeview: function(element) {
+				var treeviews = $('.treeview', element);
+				if (treeviews.length > 0) {
+					ams.ajax.check($.fn.treview,
+								   ams.baseURL + 'ext/bootstrap-treeview' + ams.devext + '.js',
+								   function(first_load) {
+										if (first_load) {
+											ams.getCSS(ams.baseURL + '../css/ext/bootstrap-treeview' + ams.devext + '.css', 'bootstrap-treeview');
+										}
+										treeviews.each(function() {
+											var treeview = $(this);
+											var data = treeview.data();
+											var dataOptions = {
+												data: data.amsTreeviewData,
+												levels: data.amsTreeviewLevels,
+												injectStyle: data.amsTreeviewInjectStyle,
+												expandIcon: data.amsTreeviewExpandIcon || 'fa fa-fw fa-plus-square-o',
+												collapseIcon: data.amsTreeviewCollaspeIcon || 'fa fa-fw fa-minus-square-o',
+												emptyIcon: data.amsTreeviewEmptyIcon || 'fa fa-fw',
+												nodeIcon: data.amsTreeviewNodeIcon,
+												selectedIcon: data.amsTreeviewSelectedIcon,
+												checkedIcon: data.amsTreeviewCheckedIcon || 'fa fa-fw fa-check-square-o',
+												uncheckedIcon: data.amsTreeviewUncheckedIcon || 'fa fa-fw fa-square-o',
+												color: data.amsTreeviewColor,
+												backColor: data.amsTreeviewBackColor,
+												borderColor: data.amsTreeviewBorderColor,
+												onHoverColor: data.amsTreeviewHoverColor,
+												selectedColor: data.amsTreeviewSelectedColor,
+												selectedBackColor: data.amsTreeviewSelectedBackColor,
+												unselectableColor: data.amsTreeviewUnselectableColor || 'rgba(1,1,1,0.25)',
+												unselectableBackColor: data.amsTreeviewUnselectableBackColor || 'rgba(1,1,1,0.25)',
+												enableLinks: data.amsTreeviewEnableLinks,
+												highlightSelected: data.amsTreeviewHighlightSelected,
+												highlightSearchResults: data.amsTreeviewhighlightSearchResults,
+												showBorder: data.amsTreeviewShowBorder,
+												showIcon: data.amsTreeviewShowIcon,
+												showCheckbox: data.amsTreeviewShowCheckbox,
+												showTags: data.amsTreeviewShowTags,
+												toggleUnselectable: data.amsTreeviewToggleUnselectable,
+												multiSelect: data.amsTreeviewMultiSelect,
+												onNodeChecked: ams.getFunctionByName(data.amsTreeviewNodeChecked),
+												onNodeCollapsed: ams.getFunctionByName(data.amsTreeviewNodeCollapsed),
+												onNodeDisabled: ams.getFunctionByName(data.amsTreeviewNodeDisabled),
+												onNodeEnabled: ams.getFunctionByName(data.amsTreeviewNodeEnabled),
+												onNodeExpanded: ams.getFunctionByName(data.amsTreeviewNodeExpanded),
+												onNodeSelected: ams.getFunctionByName(data.amsTreeviewNodeSelected),
+												onNodeUnchecked: ams.getFunctionByName(data.amsTreeviewNodeUnchecked),
+												onNodeUnselected: ams.getFunctionByName(data.amsTreeviewNodeUnselected),
+												onSearchComplete: ams.getFunctionByName(data.amsTreeviewSearchComplete),
+												onSearchCleared: ams.getFunctionByName(data.amsTreeviewSearchCleared)
+											};
+											var settings = $.extend({}, dataOptions, data.amsTreeviewOptions);
+											settings = ams.executeFunctionByName(data.amsTreeviewInitcallback, treeview, settings) || settings;
+											var plugin = treeview.treeview(settings);
+											ams.executeFunctionByName(data.amsTreeviewAfterInitCallback, treeview, plugin, settings);
+										});
+								   });
+				}
+			},
+
+			/**
 			 * Select2 plug-in
 			 */
 			select2: function(element) {
@@ -3970,29 +4033,32 @@
 													if (target) {
 														// Disable row click handler
 														$(row).data('ams-disabled-handlers', 'click');
-														var rows = [];
-														$(dnd_table.rows).each(function() {
-															var rowId = $(this).data('ams-element-name');
-															if (rowId) {
-																rows.push(rowId);
+														try {
+															var rows = [];
+															$(dnd_table.rows).each(function() {
+																var rowId = $(this).data('ams-element-name');
+																if (rowId) {
+																	rows.push(rowId);
+																}
+															});
+															var localTarget = ams.getFunctionByName(target);
+															if (typeof(localTarget) === 'function') {
+																localTarget.call(table, dnd_table, rows);
+															} else {
+																if (!target.startsWith(window.location.protocol)) {
+																	var location = data.amsLocation;
+																	if (location) {
+																		target = location + '/' + target;
+																	}
+																}
+																ams.ajax.post(target, {names: JSON.stringify(rows)});
 															}
-														});
-														var localTarget = ams.getFunctionByName(target);
-														if (typeof(localTarget) === 'function') {
-															localTarget.call(table, dnd_table, rows);
-														} else {
-															if (!target.startsWith(window.location.protocol)) {
-																var location = data.amsLocation;
-																if (location) {
-																	target = location + '/' + target;
-																}
-															}
-															ams.ajax.post(target, {names: JSON.stringify(rows)});
+														} finally {
+															// Restore row click handler
+															setTimeout(function() {
+																$(row).removeData('ams-disabled-handlers');
+															}, 50);
 														}
-														// Restore row click handler
-														setTimeout(function() {
-															$(row).removeData('ams-disabled-handlers');
-														}, 50);
 													}
 													return false;
 												}
@@ -4552,9 +4618,9 @@
 					buttons: ams.i18n.BTN_OK_CANCEL
 				}, function(button) {
 					if (button === ams.i18n.BTN_OK) {
-						var table = link.parents('table').first();
-						var location = table.data('ams-location') || '';
 						var tr = link.parents('tr').first();
+						var table = tr.parents('table').first();
+						var location = tr.data('ams-location') || table.data('ams-location') || '';
 						var deleteTarget = tr.data('ams-delete-target') || table.data('ams-delete-target') || 'delete-element.json';
 						var objectName = tr.data('ams-element-name');
 						MyAMS.ajax.post(location + '/' + deleteTarget, {'object_name': objectName}, function(result, status) {
@@ -4579,6 +4645,226 @@
 
 
 	/**
+	 * Tree management
+	 */
+	MyAMS.tree = {
+
+		/**
+		 * Open close tree node inside a table
+		 */
+		switchTableNode: function() {
+
+			function removeChildNodes(node_id) {
+				$('tr[data-ams-tree-node-parent-id="' + node_id + '"]').each(function() {
+					var row = $(this);
+					removeChildNodes(row.data('ams-tree-node-id'));
+					row.remove();
+				})
+			}
+
+			var node = $(this);
+			var switcher = $('i.switch', node);
+			var tr = node.parents('tr').first();
+			var table = tr.parents('table').first();
+			if (switcher.hasClass('fa-minus-square-o')) {
+				removeChildNodes(tr.data('ams-tree-node-id'));
+				switcher.removeClass('fa-minus-square-o')
+						.addClass('fa-plus-square-o');
+			} else {
+				var location = tr.data('ams-location') || table.data('ams-location') || '';
+				var treeNodesTarget = tr.data('ams-tree-nodes-target') || table.data('ams-tree-nodes-target') || 'get-tree-nodes.json';
+				var sourceName = tr.data('ams-element-name');
+				switcher.removeClass('fa-plus-square-o')
+						.addClass('fa-cog fa-spin');
+				MyAMS.ajax.post(location + '/' + sourceName + '/' + treeNodesTarget, {
+					can_sort: !$('td.sorter', tr).is(':empty')
+				}, function(result, status) {
+					if (result.length > 0) {
+						var old_row = tr;
+						for (var index = 0; index < result.length; index++) {
+							var new_row = $(result[index]);
+							new_row.insertAfter(old_row)
+								   .addClass('no-drag-handle');
+							ams.initContent(new_row);
+							old_row = new_row;
+						}
+						if (table.hasClass('table-dnd')) {
+							table.tableDnDUpdate();
+						}
+					}
+					switcher.removeClass('fa-cog fa-spin')
+							.addClass('fa-minus-square-o');
+				});
+			}
+		},
+
+		/**
+		 * Open close all tree nodes
+		 */
+		switchTree: function() {
+			var th = $(this);
+			var switcher = $('i.switch', th);
+			var table = $(this).parents('table').first();
+			var tableID = table.data('ams-tree-node-id');
+			if (switcher.hasClass('fa-minus-square-o')) {
+				$('tr[data-ams-tree-node-parent-id]').filter('tr[data-ams-tree-node-parent-id!="' + tableID + '"]').remove();
+				$('i.switch', table).removeClass('fa-minus-square-o')
+									.addClass('fa-plus-square-o');
+			} else {
+				var tr = $('tbody tr', table).first();
+				var location = table.data('ams-location') || '';
+				var target = table.data('ams-tree-nodes-target') || 'get-tree.json';
+				switcher.removeClass('fa-plus-square-o')
+						.addClass('fa-cog fa-spin');
+				MyAMS.ajax.post(location + '/' + target, {
+					can_sort: !$('td.sorter', tr).is(':empty')
+				}, function(result, status) {
+					$('tr[data-ams-tree-node-id]', table).remove();
+					var old_row = null;
+					for (var index = 0; index < result.length; index++) {
+						var new_row = $(result[index]);
+						if (old_row === null) {
+							new_row.appendTo($('tbody', table));
+						} else {
+							new_row.insertAfter(old_row);
+						}
+						new_row.addClass('no-drag-handle');
+						ams.initContent(new_row);
+						old_row = new_row;
+					}
+					if (table.hasClass('table-dnd')) {
+						table.tableDnDUpdate();
+					}
+					$('i.switch', table).removeClass('fa-plus-square-o')
+										.addClass('fa-minus-square-o');
+					switcher.removeClass('fa-cog fa-spin')
+							.addClass('fa-minus-square-o');
+				});
+			}
+		},
+
+		/**
+		 * Sort and re-parent tree elements
+		 */
+		sortTree: function(dnd_table, row) {
+			var data = $(dnd_table).data();
+			var target = data.amsTabledndDropTarget;
+			if (target) {
+				// Disable row click handler
+				row = $(row);
+				row.data('ams-disabled-handlers', 'click');
+				try {
+					// Get root ID
+					var tableID = row.parents('table').first().data('ams-tree-node-id');
+					// Get moved row ID
+					var rowID = row.data('ams-tree-node-id');
+					var rowParentID = row.data('ams-tree-node-parent-id');
+					// Get new parent ID
+					var parent = row.prev('tr');
+					if (parent.exists()) {
+						// Move below an existing row
+						var parentID = parent.data('ams-tree-node-id');
+						// Check switcher state
+						var switcher = $('.switch', parent);
+						if (switcher.hasClass('fa-minus-square-o')) {
+							// Opened folder: move as child
+							if (rowParentID === parentID) {
+								// Don't change parent
+								var action = 'reorder';
+							} else {
+								// Change parent
+								action = 'reparent';
+							}
+						} else {
+							// Closed folder or simple item: move as sibling
+							parentID = parent.data('ams-tree-node-parent-id');
+							if (rowParentID === parentID) {
+								// Don't change parent
+								action = 'reorder';
+							} else {
+								// Change parent
+								action = 'reparent';
+							}
+						}
+					} else {
+						// Move to site root
+						parentID = tableID;
+						switcher = null;
+						if (rowParentID === parentID) {
+							// Already child of site root
+							action = 'reorder';
+						} else {
+							// Move from inner folder to site root
+							action = 'reparent';
+						}
+					}
+					// Call ordering target
+					var localTarget = ams.getFunctionByName(target);
+					if (typeof(localTarget) === 'function') {
+						localTarget.call(table, dnd_table, post_data);
+					} else {
+						if (!target.startsWith(window.location.protocol)) {
+							var location = data.amsLocation;
+							if (location) {
+								target = location + '/' + target;
+							}
+						}
+						var post_data = {
+							action: action,
+							child: rowID,
+							parent: parentID,
+							order: JSON.stringify($('tr[data-ams-tree-node-id]').listattr('data-ams-tree-node-id')),
+							can_sort: !$('td.sorter', row).is(':empty')
+						};
+						ams.ajax.post(target, post_data, function(result) {
+
+							function removeChildRows(rowID) {
+								var childs = $('tr[data-ams-tree-node-parent-id="' + rowID + '"]');
+								childs.each(function() {
+									var childRow = $(this);
+									var childID = childRow.attr('data-ams-tree-node-id');
+									removeChildRows(childID);
+									childRow.remove();
+								});
+							}
+
+							// Remove moved row childrens
+							var body = $(row).parents('tbody').first();
+							removeChildRows(rowID);
+							if (post_data.action === 'reparent') {
+								// Remove new parent childrens
+								removeChildRows(parentID);
+								row.remove();
+								var old_row = $('tr[data-ams-tree-node-id="' + parentID + '"]');
+								for (var index = 0; index < result.length; index++) {
+									var new_row = $(result[index]);
+									if (old_row.exists()) {
+										new_row.insertAfter(old_row)
+											   .addClass('no-drag-handle');
+									} else {
+										new_row.prependTo(body)
+											   .addClass('no-drag-handle');
+									}
+									ams.initContent(new_row);
+									old_row = new_row;
+								}
+							}
+							$('tr').parents('table').tableDnDUpdate();
+						});
+					}
+				} finally {
+					// Restore row click handler
+					setTimeout(function() {
+						$(row).removeData('ams-disabled-handlers');
+					}, 50);
+				}
+			}
+			return false;
+		}
+	};
+
+
+	/**
 	 * Generic skin features
 	 */
 	MyAMS.skin = {
@@ -4651,6 +4937,57 @@
 		},
 
 		/**
+		 * Replace given form with new content
+		 */
+		refreshContent: function(changes) {
+			var target = $('[id="' + changes.object_id + '"]');
+			target.replaceWith($(changes.content));
+			target = $('[id="' + changes.object_id + '"]');
+			MyAMS.initContent(target);
+			return target;
+		},
+
+		/**
+		 * Replace given table with new content
+		 */
+		refreshTable: function(changes) {
+			var widget = $('[id="' + changes.object_id + '"]').parent('.ams-widget');
+			widget.replaceWith($(changes.table));
+			widget = $('[id="' + changes.object_id + '"]').parent('.ams-widget');
+			MyAMS.initContent(widget);
+			return widget;
+		},
+		/**
+		 * Replace given row with new content
+		 */
+		refreshRow: function(changes) {
+			var tr = $('tr[id="' + changes.object_id + '"]');
+			var table = tr.parents('table').first();
+			tr = tr.replaceWith($(changes.row));
+			MyAMS.initContent(tr);
+			if (table.hasClass('table-dnd')) {
+				table.tableDnDUpdate();
+			}
+			return tr;
+		},
+
+		/**
+		 * Replace given row cell with new content
+		 */
+		refreshRowCell: function(changes) {
+			var tr = $('tr[id="' + changes.object_id + '"]');
+			var table = tr.parents('table').first();
+			var headRow = $('tr', $('thead', table));
+			var headCell = $('th[data-ams-column-name="' + changes.col_name + '"]', headRow);
+			var index = $('th', headRow).index(headCell);
+			if (index > -1) {
+				var cell = $($('td', tr).get(index));
+				cell.html(changes.cell);
+				MyAMS.initContent(cell);
+			}
+		},
+
+		/**
 		 * Initialize desktop and mobile widgets
 		 */
 		_initDesktopWidgets: function(element) {
@@ -5527,6 +5864,11 @@
 			});
 		});
 
+		// Enable custom MyAMS refresh events
+		$(document).on('myams.refresh', function(event, settings) {
+			MyAMS.executeFunctionByName(settings.handler || MyAMS.skin.refreshContent, event.target, settings);
+		});
+		
 		// Init page content
 		ams.initContent(document);
 		if (ams.ajaxNav && nav.exists()) {