Added treeview plug-in
authorThierry Florac <tflorac@ulthar.net>
Sun, 26 Nov 2017 10:09:52 +0100
changeset 244 4b5ae98a90b1
parent 243 b9ed4b78393f
child 245 79635dd71cca
Added treeview plug-in
src/pyams_skin/resources/js/ext/bootstrap-treeview.js
src/pyams_skin/resources/js/ext/bootstrap-treeview.min.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_skin/resources/js/ext/bootstrap-treeview.js	Sun Nov 26 10:09:52 2017 +0100
@@ -0,0 +1,1260 @@
+/* =========================================================
+ * bootstrap-treeview.js v1.3.0-b1-tf
+ * =========================================================
+ * Copyright 2013 Jonathan Miles
+ * Project URL : http://www.jondmiles.com/bootstrap-treeview
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+(function ($, window, document, undefined) {
+
+	/*global jQuery, console*/
+
+	'use strict';
+
+	var pluginName = 'treeview';
+
+	var _default = {};
+
+	_default.settings = {
+
+		injectStyle: true,
+
+		levels: 2,
+
+		expandIcon: 'glyphicon glyphicon-plus',
+		collapseIcon: 'glyphicon glyphicon-minus',
+		emptyIcon: 'glyphicon',
+		nodeIcon: '',
+		selectedIcon: '',
+		checkedIcon: 'glyphicon glyphicon-check',
+		uncheckedIcon: 'glyphicon glyphicon-unchecked',
+
+		color: undefined, // '#000000',
+		backColor: undefined, // '#FFFFFF',
+		borderColor: undefined, // '#dddddd',
+		onhoverColor: '#F5F5F5',
+		selectedColor: '#FFFFFF',
+		selectedBackColor: '#428bca',
+		unselectableBackColor: undefined, //'#FFFFFF',
+		searchResultColor: '#D9534F',
+		searchResultBackColor: undefined, //'#FFFFFF',
+
+		enableLinks: false,
+		highlightSelected: true,
+		highlightSearchResults: true,
+		showBorder: true,
+		showIcon: true,
+		showCheckbox: false,
+		showTags: false,
+		toggleUnselectable: true,
+		multiSelect: false,
+
+		// Event handlers
+		onNodeChecked: undefined,
+		onNodeCollapsed: undefined,
+		onNodeDisabled: undefined,
+		onNodeEnabled: undefined,
+		onNodeExpanded: undefined,
+		onNodeSelected: undefined,
+		onNodeUnchecked: undefined,
+		onNodeUnselected: undefined,
+		onSearchComplete: undefined,
+		onSearchCleared: undefined
+	};
+
+	_default.options = {
+		silent: false,
+		ignoreChildren: false
+	};
+
+	_default.searchOptions = {
+		ignoreCase: true,
+		exactMatch: false,
+		revealResults: true
+	};
+
+	var Tree = function (element, options) {
+
+		this.$element = $(element);
+		this.elementId = element.id;
+		this.styleId = this.elementId + '-style';
+
+		this.init(options);
+
+		return {
+
+			// Options (public access)
+			options: this.options,
+
+			// Initialize / destroy methods
+			init: $.proxy(this.init, this),
+			remove: $.proxy(this.remove, this),
+
+			// Get methods
+			getNode: $.proxy(this.getNode, this),
+			getParent: $.proxy(this.getParent, this),
+			getSiblings: $.proxy(this.getSiblings, this),
+			getSelected: $.proxy(this.getSelected, this),
+			getUnselected: $.proxy(this.getUnselected, this),
+			getExpanded: $.proxy(this.getExpanded, this),
+			getCollapsed: $.proxy(this.getCollapsed, this),
+			getChecked: $.proxy(this.getChecked, this),
+			getUnchecked: $.proxy(this.getUnchecked, this),
+			getDisabled: $.proxy(this.getDisabled, this),
+			getEnabled: $.proxy(this.getEnabled, this),
+
+			// Select methods
+			selectNode: $.proxy(this.selectNode, this),
+			unselectNode: $.proxy(this.unselectNode, this),
+			toggleNodeSelected: $.proxy(this.toggleNodeSelected, this),
+
+			// Expand / collapse methods
+			collapseAll: $.proxy(this.collapseAll, this),
+			collapseNode: $.proxy(this.collapseNode, this),
+			expandAll: $.proxy(this.expandAll, this),
+			expandNode: $.proxy(this.expandNode, this),
+			toggleNodeExpanded: $.proxy(this.toggleNodeExpanded, this),
+			revealNode: $.proxy(this.revealNode, this),
+
+			// Expand / collapse methods
+			checkAll: $.proxy(this.checkAll, this),
+			checkNode: $.proxy(this.checkNode, this),
+			uncheckAll: $.proxy(this.uncheckAll, this),
+			uncheckNode: $.proxy(this.uncheckNode, this),
+			toggleNodeChecked: $.proxy(this.toggleNodeChecked, this),
+
+			// Disable / enable methods
+			disableAll: $.proxy(this.disableAll, this),
+			disableNode: $.proxy(this.disableNode, this),
+			enableAll: $.proxy(this.enableAll, this),
+			enableNode: $.proxy(this.enableNode, this),
+			toggleNodeDisabled: $.proxy(this.toggleNodeDisabled, this),
+
+			// Search methods
+			search: $.proxy(this.search, this),
+			clearSearch: $.proxy(this.clearSearch, this)
+		};
+	};
+
+	Tree.prototype.init = function (options) {
+
+		this.tree = [];
+		this.nodes = [];
+
+		if (options.data) {
+			if (typeof options.data === 'string') {
+				options.data = $.parseJSON(options.data);
+			}
+			this.tree = $.extend(true, [], options.data);
+			delete options.data;
+		}
+		this.options = $.extend({}, _default.settings, options);
+
+		this.destroy();
+		this.subscribeEvents();
+		this.setInitialStates({ nodes: this.tree }, 0);
+		this.render();
+	};
+
+	Tree.prototype.remove = function () {
+		this.destroy();
+		$.removeData(this, pluginName);
+		$('#' + this.styleId).remove();
+	};
+
+	Tree.prototype.destroy = function () {
+
+		if (!this.initialized) return;
+
+		this.$wrapper.remove();
+		this.$wrapper = null;
+
+		// Switch off events
+		this.unsubscribeEvents();
+
+		// Reset this.initialized flag
+		this.initialized = false;
+	};
+
+	Tree.prototype.unsubscribeEvents = function () {
+
+		this.$element.off('click');
+		this.$element.off('nodeChecked');
+		this.$element.off('nodeCollapsed');
+		this.$element.off('nodeDisabled');
+		this.$element.off('nodeEnabled');
+		this.$element.off('nodeExpanded');
+		this.$element.off('nodeSelected');
+		this.$element.off('nodeUnchecked');
+		this.$element.off('nodeUnselected');
+		this.$element.off('searchComplete');
+		this.$element.off('searchCleared');
+	};
+
+	Tree.prototype.subscribeEvents = function () {
+
+		this.unsubscribeEvents();
+
+		this.$element.on('click', $.proxy(this.clickHandler, this));
+
+		if (typeof (this.options.onNodeChecked) === 'function') {
+			this.$element.on('nodeChecked', this.options.onNodeChecked);
+		}
+
+		if (typeof (this.options.onNodeCollapsed) === 'function') {
+			this.$element.on('nodeCollapsed', this.options.onNodeCollapsed);
+		}
+
+		if (typeof (this.options.onNodeDisabled) === 'function') {
+			this.$element.on('nodeDisabled', this.options.onNodeDisabled);
+		}
+
+		if (typeof (this.options.onNodeEnabled) === 'function') {
+			this.$element.on('nodeEnabled', this.options.onNodeEnabled);
+		}
+
+		if (typeof (this.options.onNodeExpanded) === 'function') {
+			this.$element.on('nodeExpanded', this.options.onNodeExpanded);
+		}
+
+		if (typeof (this.options.onNodeSelected) === 'function') {
+			this.$element.on('nodeSelected', this.options.onNodeSelected);
+		}
+
+		if (typeof (this.options.onNodeUnchecked) === 'function') {
+			this.$element.on('nodeUnchecked', this.options.onNodeUnchecked);
+		}
+
+		if (typeof (this.options.onNodeUnselected) === 'function') {
+			this.$element.on('nodeUnselected', this.options.onNodeUnselected);
+		}
+
+		if (typeof (this.options.onSearchComplete) === 'function') {
+			this.$element.on('searchComplete', this.options.onSearchComplete);
+		}
+
+		if (typeof (this.options.onSearchCleared) === 'function') {
+			this.$element.on('searchCleared', this.options.onSearchCleared);
+		}
+	};
+
+	/*
+		Recurse the tree structure and ensure all nodes have
+		valid initial states.  User defined states will be preserved.
+		For performance we also take this opportunity to
+		index nodes in a flattened structure
+	*/
+	Tree.prototype.setInitialStates = function (node, level) {
+
+		if (!node.nodes) return;
+		level += 1;
+
+		var parent = node;
+		var _this = this;
+		$.each(node.nodes, function checkStates(index, node) {
+
+			// nodeId : unique, incremental identifier
+			node.nodeId = _this.nodes.length;
+
+			// parentId : transversing up the tree
+			node.parentId = parent.nodeId;
+
+			// if not provided set selectable default value
+			if (!node.hasOwnProperty('selectable')) {
+				node.selectable = true;
+			}
+
+			// where provided we should preserve states
+			node.state = node.state || {};
+
+			// set checked state; unless set always false
+			if (!node.state.hasOwnProperty('checked')) {
+				node.state.checked = false;
+			}
+
+			// set enabled state; unless set always false
+			if (!node.state.hasOwnProperty('disabled')) {
+				node.state.disabled = false;
+			}
+
+			// set expanded state; if not provided based on levels
+			if (!node.state.hasOwnProperty('expanded')) {
+				if (!node.state.disabled &&
+						(level < _this.options.levels) &&
+						(node.nodes && node.nodes.length > 0)) {
+					node.state.expanded = true;
+				}
+				else {
+					node.state.expanded = false;
+				}
+			}
+
+			// set selected state; unless set always false
+			if (!node.state.hasOwnProperty('selected')) {
+				node.state.selected = false;
+			}
+
+			// index nodes in a flattened structure for use later
+			_this.nodes.push(node);
+
+			// recurse child nodes and transverse the tree
+			if (node.nodes) {
+				_this.setInitialStates(node, level);
+			}
+		});
+	};
+
+	Tree.prototype.clickHandler = function (event) {
+
+		if (!this.options.enableLinks) event.preventDefault();
+
+		var target = $(event.target);
+		var node = this.findNode(target);
+		if (!node || node.state.disabled) return;
+		
+		var classList = target.attr('class') ? target.attr('class').split(' ') : [];
+		if ((classList.indexOf('expand-icon') !== -1)) {
+
+			this.toggleExpandedState(node, _default.options);
+			this.render();
+		}
+		else if ((classList.indexOf('check-icon') !== -1)) {
+			
+			this.toggleCheckedState(node, _default.options);
+			this.render();
+		}
+		else {
+			
+			if (node.selectable) {
+				this.toggleSelectedState(node, _default.options);
+			} else if (this.options.toggleUnselectable) {
+				this.toggleExpandedState(node, _default.options);
+			}
+
+			this.render();
+		}
+	};
+
+	// Looks up the DOM for the closest parent list item to retrieve the
+	// data attribute nodeid, which is used to lookup the node in the flattened structure.
+	Tree.prototype.findNode = function (target) {
+
+		var nodeId = target.closest('li.list-group-item').attr('data-nodeid');
+		var node = this.nodes[nodeId];
+
+		if (!node) {
+			console.log('Error: node does not exist');
+		}
+		return node;
+	};
+
+	Tree.prototype.toggleExpandedState = function (node, options) {
+		if (!node) return;
+		this.setExpandedState(node, !node.state.expanded, options);
+	};
+
+	Tree.prototype.setExpandedState = function (node, state, options) {
+
+		if (state === node.state.expanded) return;
+
+		if (state && node.nodes) {
+
+			// Expand a node
+			node.state.expanded = true;
+			if (!options.silent) {
+				this.$element.trigger('nodeExpanded', $.extend(true, {}, node));
+			}
+		}
+		else if (!state) {
+
+			// Collapse a node
+			node.state.expanded = false;
+			if (!options.silent) {
+				this.$element.trigger('nodeCollapsed', $.extend(true, {}, node));
+			}
+
+			// Collapse child nodes
+			if (node.nodes && !options.ignoreChildren) {
+				$.each(node.nodes, $.proxy(function (index, node) {
+					this.setExpandedState(node, false, options);
+				}, this));
+			}
+		}
+	};
+
+	Tree.prototype.toggleSelectedState = function (node, options) {
+		if (!node) return;
+		this.setSelectedState(node, !node.state.selected, options);
+	};
+
+	Tree.prototype.setSelectedState = function (node, state, options) {
+
+		if (state === node.state.selected) return;
+
+		if (state) {
+
+			// If multiSelect false, unselect previously selected
+			if (!this.options.multiSelect) {
+				$.each(this.findNodes('true', 'g', 'state.selected'), $.proxy(function (index, node) {
+					this.setSelectedState(node, false, options);
+				}, this));
+			}
+
+			// Continue selecting node
+			node.state.selected = true;
+			if (!options.silent) {
+				this.$element.trigger('nodeSelected', $.extend(true, {}, node));
+			}
+		}
+		else {
+
+			// Unselect node
+			node.state.selected = false;
+			if (!options.silent) {
+				this.$element.trigger('nodeUnselected', $.extend(true, {}, node));
+			}
+		}
+	};
+
+	Tree.prototype.toggleCheckedState = function (node, options) {
+		if (!node) return;
+		this.setCheckedState(node, !node.state.checked, options);
+	};
+
+	Tree.prototype.setCheckedState = function (node, state, options) {
+
+		if (state === node.state.checked) return;
+
+		if (state) {
+
+			// Check node
+			node.state.checked = true;
+
+			if (!options.silent) {
+				this.$element.trigger('nodeChecked', $.extend(true, {}, node));
+			}
+		}
+		else {
+
+			// Uncheck node
+			node.state.checked = false;
+			if (!options.silent) {
+				this.$element.trigger('nodeUnchecked', $.extend(true, {}, node));
+			}
+		}
+	};
+
+	Tree.prototype.setDisabledState = function (node, state, options) {
+
+		if (state === node.state.disabled) return;
+
+		if (state) {
+
+			// Disable node
+			node.state.disabled = true;
+
+			// Disable all other states
+			this.setExpandedState(node, false, options);
+			this.setSelectedState(node, false, options);
+			this.setCheckedState(node, false, options);
+
+			if (!options.silent) {
+				this.$element.trigger('nodeDisabled', $.extend(true, {}, node));
+			}
+		}
+		else {
+
+			// Enabled node
+			node.state.disabled = false;
+			if (!options.silent) {
+				this.$element.trigger('nodeEnabled', $.extend(true, {}, node));
+			}
+		}
+	};
+
+	Tree.prototype.render = function () {
+
+		if (!this.initialized) {
+
+			// Setup first time only components
+			this.$element.addClass(pluginName);
+			this.$wrapper = $(this.template.list);
+
+			this.injectStyle();
+
+			this.initialized = true;
+		}
+
+		this.$element.empty().append(this.$wrapper.empty());
+
+		// Build tree
+		this.buildTree(this.tree, 0);
+	};
+
+	// Starting from the root node, and recursing down the
+	// structure we build the tree one node at a time
+	Tree.prototype.buildTree = function (nodes, level) {
+
+		if (!nodes) return;
+		level += 1;
+
+		var _this = this;
+		$.each(nodes, function addNodes(id, node) {
+
+			var treeItem = $(_this.template.item)
+				.addClass('node-' + _this.elementId)
+				.addClass(node.state.checked ? 'node-checked' : '')
+				.addClass(node.state.disabled ? 'node-disabled': '')
+				.addClass(node.state.selected ? 'node-selected' : '')
+				.addClass(node.searchResult ? 'search-result' : '') 
+				.attr('data-nodeid', node.nodeId)
+				.attr('style', _this.buildStyleOverride(node));
+
+			// Add indent/spacer to mimic tree structure
+			for (var i = 0; i < (level - 1); i++) {
+				treeItem.append(_this.template.indent);
+			}
+
+			// Add expand, collapse or empty spacer icons
+			var classList = [];
+			if (node.nodes) {
+				classList.push('expand-icon');
+				if (node.state.expanded) {
+					classList.push(_this.options.collapseIcon);
+				}
+				else {
+					classList.push(_this.options.expandIcon);
+				}
+			}
+			else {
+				classList.push(_this.options.emptyIcon);
+			}
+
+			treeItem
+				.append($(_this.template.icon)
+					.addClass(classList.join(' '))
+				);
+
+
+			// Add node icon
+			if (_this.options.showIcon) {
+				
+				var classList = ['node-icon'];
+
+				classList.push(node.icon || _this.options.nodeIcon);
+				if (node.state.selected) {
+					classList.pop();
+					classList.push(node.selectedIcon || _this.options.selectedIcon || 
+									node.icon || _this.options.nodeIcon);
+				}
+
+				treeItem
+					.append($(_this.template.icon)
+						.addClass(classList.join(' '))
+					);
+			}
+
+			// Add check / unchecked icon
+			if (_this.options.showCheckbox) {
+
+				var classList = ['check-icon'];
+				if (node.state.checked) {
+					classList.push(_this.options.checkedIcon); 
+				}
+				else {
+					classList.push(_this.options.uncheckedIcon);
+				}
+
+				treeItem
+					.append($(_this.template.icon)
+						.addClass(classList.join(' '))
+					);
+			}
+
+			// Add text
+			if (_this.options.enableLinks) {
+				// Add hyperlink
+				treeItem
+					.append($(_this.template.link)
+						.attr('href', node.href)
+						.append(node.text)
+					);
+			}
+			else {
+				// otherwise just text
+				treeItem
+					.append(node.text);
+			}
+
+			// Add tags as badges
+			if (_this.options.showTags && node.tags) {
+				$.each(node.tags, function addTag(id, tag) {
+					treeItem
+						.append($(_this.template.badge)
+							.append(tag)
+						);
+				});
+			}
+
+			// Add item to the tree
+			_this.$wrapper.append(treeItem);
+
+			// Recursively add child ndoes
+			if (node.nodes && node.state.expanded && !node.state.disabled) {
+				return _this.buildTree(node.nodes, level);
+			}
+		});
+	};
+
+	// Define any node level style override for
+	// 1. selectedNode
+	// 2. node|data assigned color overrides
+	Tree.prototype.buildStyleOverride = function (node) {
+
+		if (node.state.disabled) return '';
+
+		var color = node.color;
+		var backColor = node.backColor;
+
+		if (!node.selectable) {
+			if (this.options.unselectableColor) {
+				color = this.options.unselectableColor;
+			}
+			if (this.options.unselectableBackColor) {
+				backColor = this.options.unselectableBackColor;
+			}
+		}
+
+		if (this.options.highlightSelected && node.state.selected) {
+			if (this.options.selectedColor) {
+				color = this.options.selectedColor;
+			}
+			if (this.options.selectedBackColor) {
+				backColor = this.options.selectedBackColor;
+			}
+		}
+
+		if (this.options.highlightSearchResults && node.searchResult && !node.state.disabled) {
+			if (this.options.searchResultColor) {
+				color = this.options.searchResultColor;
+			}
+			if (this.options.searchResultBackColor) {
+				backColor = this.options.searchResultBackColor;
+			}
+		}
+
+		return 'color:' + color +
+			';background-color:' + backColor + ';';
+	};
+
+	// Add inline style into head
+	Tree.prototype.injectStyle = function () {
+
+		if (this.options.injectStyle && !document.getElementById(this.styleId)) {
+			$('<style type="text/css" id="' + this.styleId + '"> ' + this.buildStyle() + ' </style>').appendTo('head');
+		}
+	};
+
+	// Construct trees style based on user options
+	Tree.prototype.buildStyle = function () {
+
+		var style = '.node-' + this.elementId + '{';
+
+		if (this.options.color) {
+			style += 'color:' + this.options.color + ';';
+		}
+
+		if (this.options.backColor) {
+			style += 'background-color:' + this.options.backColor + ';';
+		}
+
+		if (!this.options.showBorder) {
+			style += 'border:none;';
+		}
+		else if (this.options.borderColor) {
+			style += 'border:1px solid ' + this.options.borderColor + ';';
+		}
+		style += '}';
+
+		if (this.options.onhoverColor) {
+			style += '.node-' + this.elementId + ':not(.node-disabled):hover{' +
+				'background-color:' + this.options.onhoverColor + ';' +
+			'}';
+		}
+
+		return this.css + style;
+	};
+
+	Tree.prototype.template = {
+		list: '<ul class="list-group"></ul>',
+		item: '<li class="list-group-item"></li>',
+		indent: '<span class="indent"></span>',
+		icon: '<span class="icon"></span>',
+		link: '<a href="#" style="color:inherit;"></a>',
+		badge: '<span class="badge"></span>'
+	};
+
+	Tree.prototype.css = '.treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}'
+
+
+	/**
+		Returns a single node object that matches the given node id.
+		@param {Number} nodeId - A node's unique identifier
+		@return {Object} node - Matching node
+	*/
+	Tree.prototype.getNode = function (nodeId) {
+		return this.nodes[nodeId];
+	};
+
+	/**
+		Returns the parent node of a given node, if valid otherwise returns undefined.
+		@param {Object|Number} identifier - A valid node or node id
+		@returns {Object} node - The parent node
+	*/
+	Tree.prototype.getParent = function (identifier) {
+		var node = this.identifyNode(identifier);
+		return this.nodes[node.parentId];
+	};
+
+	/**
+		Returns an array of sibling nodes for a given node, if valid otherwise returns undefined.
+		@param {Object|Number} identifier - A valid node or node id
+		@returns {Array} nodes - Sibling nodes
+	*/
+	Tree.prototype.getSiblings = function (identifier) {
+		var node = this.identifyNode(identifier);
+		var parent = this.getParent(node);
+		var nodes = parent ? parent.nodes : this.tree;
+		return nodes.filter(function (obj) {
+				return obj.nodeId !== node.nodeId;
+			});
+	};
+
+	/**
+		Returns an array of selected nodes.
+		@returns {Array} nodes - Selected nodes
+	*/
+	Tree.prototype.getSelected = function () {
+		return this.findNodes('true', 'g', 'state.selected');
+	};
+
+	/**
+		Returns an array of unselected nodes.
+		@returns {Array} nodes - Unselected nodes
+	*/
+	Tree.prototype.getUnselected = function () {
+		return this.findNodes('false', 'g', 'state.selected');
+	};
+
+	/**
+		Returns an array of expanded nodes.
+		@returns {Array} nodes - Expanded nodes
+	*/
+	Tree.prototype.getExpanded = function () {
+		return this.findNodes('true', 'g', 'state.expanded');
+	};
+
+	/**
+		Returns an array of collapsed nodes.
+		@returns {Array} nodes - Collapsed nodes
+	*/
+	Tree.prototype.getCollapsed = function () {
+		return this.findNodes('false', 'g', 'state.expanded');
+	};
+
+	/**
+		Returns an array of checked nodes.
+		@returns {Array} nodes - Checked nodes
+	*/
+	Tree.prototype.getChecked = function () {
+		return this.findNodes('true', 'g', 'state.checked');
+	};
+
+	/**
+		Returns an array of unchecked nodes.
+		@returns {Array} nodes - Unchecked nodes
+	*/
+	Tree.prototype.getUnchecked = function () {
+		return this.findNodes('false', 'g', 'state.checked');
+	};
+
+	/**
+		Returns an array of disabled nodes.
+		@returns {Array} nodes - Disabled nodes
+	*/
+	Tree.prototype.getDisabled = function () {
+		return this.findNodes('true', 'g', 'state.disabled');
+	};
+
+	/**
+		Returns an array of enabled nodes.
+		@returns {Array} nodes - Enabled nodes
+	*/
+	Tree.prototype.getEnabled = function () {
+		return this.findNodes('false', 'g', 'state.disabled');
+	};
+
+
+	/**
+		Set a node state to selected
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.selectNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setSelectedState(node, true, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Set a node state to unselected
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.unselectNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setSelectedState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Toggles a node selected state; selecting if unselected, unselecting if selected.
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.toggleNodeSelected = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.toggleSelectedState(node, options);
+		}, this));
+
+		this.render();
+	};
+
+
+	/**
+		Collapse all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.collapseAll = function (options) {
+		var identifiers = this.findNodes('true', 'g', 'state.expanded');
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setExpandedState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Collapse a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.collapseNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setExpandedState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Expand all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.expandAll = function (options) {
+		options = $.extend({}, _default.options, options);
+
+		if (options && options.levels) {
+			this.expandLevels(this.tree, options.levels, options);
+		}
+		else {
+			var identifiers = this.findNodes('false', 'g', 'state.expanded');
+			this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+				this.setExpandedState(node, true, options);
+			}, this));
+		}
+
+		this.render();
+	};
+
+	/**
+		Expand a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.expandNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setExpandedState(node, true, options);
+			if (node.nodes && (options && options.levels)) {
+				this.expandLevels(node.nodes, options.levels-1, options);
+			}
+		}, this));
+
+		this.render();
+	};
+
+	Tree.prototype.expandLevels = function (nodes, level, options) {
+		options = $.extend({}, _default.options, options);
+
+		$.each(nodes, $.proxy(function (index, node) {
+			this.setExpandedState(node, (level > 0) ? true : false, options);
+			if (node.nodes) {
+				this.expandLevels(node.nodes, level-1, options);
+			}
+		}, this));
+	};
+
+	/**
+		Reveals a given tree node, expanding the tree from node to root.
+		@param {Object|Number|Array} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.revealNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			var parentNode = this.getParent(node);
+			while (parentNode) {
+				this.setExpandedState(parentNode, true, options);
+				parentNode = this.getParent(parentNode);
+			};
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Toggles a nodes expanded state; collapsing if expanded, expanding if collapsed.
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.toggleNodeExpanded = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.toggleExpandedState(node, options);
+		}, this));
+		
+		this.render();
+	};
+
+
+	/**
+		Check all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.checkAll = function (options) {
+		var identifiers = this.findNodes('false', 'g', 'state.checked');
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setCheckedState(node, true, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Check a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.checkNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setCheckedState(node, true, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Uncheck all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.uncheckAll = function (options) {
+		var identifiers = this.findNodes('true', 'g', 'state.checked');
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setCheckedState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Uncheck a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.uncheckNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setCheckedState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Toggles a nodes checked state; checking if unchecked, unchecking if checked.
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.toggleNodeChecked = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.toggleCheckedState(node, options);
+		}, this));
+
+		this.render();
+	};
+
+
+	/**
+		Disable all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.disableAll = function (options) {
+		var identifiers = this.findNodes('false', 'g', 'state.disabled');
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setDisabledState(node, true, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Disable a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.disableNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setDisabledState(node, true, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Enable all tree nodes
+		@param {optional Object} options
+	*/
+	Tree.prototype.enableAll = function (options) {
+		var identifiers = this.findNodes('true', 'g', 'state.disabled');
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setDisabledState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Enable a given tree node
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.enableNode = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setDisabledState(node, false, options);
+		}, this));
+
+		this.render();
+	};
+
+	/**
+		Toggles a nodes disabled state; disabling is enabled, enabling if disabled.
+		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
+		@param {optional Object} options
+	*/
+	Tree.prototype.toggleNodeDisabled = function (identifiers, options) {
+		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
+			this.setDisabledState(node, !node.state.disabled, options);
+		}, this));
+
+		this.render();
+	};
+
+
+	/**
+		Common code for processing multiple identifiers
+	*/
+	Tree.prototype.forEachIdentifier = function (identifiers, options, callback) {
+
+		options = $.extend({}, _default.options, options);
+
+		if (!(identifiers instanceof Array)) {
+			identifiers = [identifiers];
+		}
+
+		$.each(identifiers, $.proxy(function (index, identifier) {
+			callback(this.identifyNode(identifier), options);
+		}, this));	
+	};
+
+	/*
+		Identifies a node from either a node id or object
+	*/
+	Tree.prototype.identifyNode = function (identifier) {
+		return ((typeof identifier) === 'number') ?
+						this.nodes[identifier] :
+						identifier;
+	};
+
+	/**
+		Searches the tree for nodes (text) that match given criteria
+		@param {String} pattern - A given string to match against
+		@param {optional Object} options - Search criteria options
+		@return {Array} nodes - Matching nodes
+	*/
+	Tree.prototype.search = function (pattern, options) {
+		options = $.extend({}, _default.searchOptions, options);
+
+		this.clearSearch({ render: false });
+
+		var results = [];
+		if (pattern && pattern.length > 0) {
+
+			if (options.exactMatch) {
+				pattern = '^' + pattern + '$';
+			}
+
+			var modifier = 'g';
+			if (options.ignoreCase) {
+				modifier += 'i';
+			}
+
+			results = this.findNodes(pattern, modifier);
+
+			// Add searchResult property to all matching nodes
+			// This will be used to apply custom styles
+			// and when identifying result to be cleared
+			$.each(results, function (index, node) {
+				node.searchResult = true;
+			})
+		}
+
+		// If revealResults, then render is triggered from revealNode
+		// otherwise we just call render.
+		if (options.revealResults) {
+			this.revealNode(results);
+		}
+		else {
+			this.render();
+		}
+
+		this.$element.trigger('searchComplete', $.extend(true, {}, results));
+
+		return results;
+	};
+
+	/**
+		Clears previous search results
+	*/
+	Tree.prototype.clearSearch = function (options) {
+
+		options = $.extend({}, { render: true }, options);
+
+		var results = $.each(this.findNodes('true', 'g', 'searchResult'), function (index, node) {
+			node.searchResult = false;
+		});
+
+		if (options.render) {
+			this.render();	
+		}
+		
+		this.$element.trigger('searchCleared', $.extend(true, {}, results));
+	};
+
+	/**
+		Find nodes that match a given criteria
+		@param {String} pattern - A given string to match against
+		@param {optional String} modifier - Valid RegEx modifiers
+		@param {optional String} attribute - Attribute to compare pattern against
+		@return {Array} nodes - Nodes that match your criteria
+	*/
+	Tree.prototype.findNodes = function (pattern, modifier, attribute) {
+
+		modifier = modifier || 'g';
+		attribute = attribute || 'text';
+
+		var _this = this;
+		return $.grep(this.nodes, function (node) {
+			var val = _this.getNodeValue(node, attribute);
+			if (typeof val === 'string') {
+				return val.match(new RegExp(pattern, modifier));
+			}
+		});
+	};
+
+	/**
+		Recursive find for retrieving nested attributes values
+		All values are return as strings, unless invalid
+		@param {Object} obj - Typically a node, could be any object
+		@param {String} attr - Identifies an object property using dot notation
+		@return {String} value - Matching attributes string representation
+	*/
+	Tree.prototype.getNodeValue = function (obj, attr) {
+		var index = attr.indexOf('.');
+		if (index > 0) {
+			var _obj = obj[attr.substring(0, index)];
+			var _attr = attr.substring(index + 1, attr.length);
+			return this.getNodeValue(_obj, _attr);
+		}
+		else {
+			if (obj.hasOwnProperty(attr)) {
+				return obj[attr].toString();
+			}
+			else {
+				return undefined;
+			}
+		}
+	};
+
+	var logError = function (message) {
+		if (window.console) {
+			window.console.error(message);
+		}
+	};
+
+	// Prevent against multiple instantiations,
+	// handle updates and method calls
+	$.fn[pluginName] = function (options, args) {
+
+		var result;
+
+		this.each(function () {
+			var _this = $.data(this, pluginName);
+			if (typeof options === 'string') {
+				if (!_this) {
+					logError('Not initialized, can not call method : ' + options);
+				}
+				else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
+					logError('No such method : ' + options);
+				}
+				else {
+					if (!(args instanceof Array)) {
+						args = [ args ];
+					}
+					result = _this[options].apply(_this, args);
+				}
+			}
+			else if (typeof options === 'boolean') {
+				result = _this;
+			}
+			else {
+				$.data(this, pluginName, new Tree(this, $.extend(true, {}, options)));
+			}
+		});
+
+		return result || this;
+	};
+
+})(jQuery, window, document);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_skin/resources/js/ext/bootstrap-treeview.min.js	Sun Nov 26 10:09:52 2017 +0100
@@ -0,0 +1,1 @@
+!function(e,t,o,s){"use strict";var n={};n.settings={injectStyle:!0,levels:2,expandIcon:"glyphicon glyphicon-plus",collapseIcon:"glyphicon glyphicon-minus",emptyIcon:"glyphicon",nodeIcon:"",selectedIcon:"",checkedIcon:"glyphicon glyphicon-check",uncheckedIcon:"glyphicon glyphicon-unchecked",color:void 0,backColor:void 0,borderColor:void 0,onhoverColor:"#F5F5F5",selectedColor:"#FFFFFF",selectedBackColor:"#428bca",unselectableBackColor:void 0,searchResultColor:"#D9534F",searchResultBackColor:void 0,enableLinks:!1,highlightSelected:!0,highlightSearchResults:!0,showBorder:!0,showIcon:!0,showCheckbox:!1,showTags:!1,toggleUnselectable:!0,multiSelect:!1,onNodeChecked:void 0,onNodeCollapsed:void 0,onNodeDisabled:void 0,onNodeEnabled:void 0,onNodeExpanded:void 0,onNodeSelected:void 0,onNodeUnchecked:void 0,onNodeUnselected:void 0,onSearchComplete:void 0,onSearchCleared:void 0},n.options={silent:!1,ignoreChildren:!1},n.searchOptions={ignoreCase:!0,exactMatch:!1,revealResults:!0};var i=function(t,o){return this.$element=e(t),this.elementId=t.id,this.styleId=this.elementId+"-style",this.init(o),{options:this.options,init:e.proxy(this.init,this),remove:e.proxy(this.remove,this),getNode:e.proxy(this.getNode,this),getParent:e.proxy(this.getParent,this),getSiblings:e.proxy(this.getSiblings,this),getSelected:e.proxy(this.getSelected,this),getUnselected:e.proxy(this.getUnselected,this),getExpanded:e.proxy(this.getExpanded,this),getCollapsed:e.proxy(this.getCollapsed,this),getChecked:e.proxy(this.getChecked,this),getUnchecked:e.proxy(this.getUnchecked,this),getDisabled:e.proxy(this.getDisabled,this),getEnabled:e.proxy(this.getEnabled,this),selectNode:e.proxy(this.selectNode,this),unselectNode:e.proxy(this.unselectNode,this),toggleNodeSelected:e.proxy(this.toggleNodeSelected,this),collapseAll:e.proxy(this.collapseAll,this),collapseNode:e.proxy(this.collapseNode,this),expandAll:e.proxy(this.expandAll,this),expandNode:e.proxy(this.expandNode,this),toggleNodeExpanded:e.proxy(this.toggleNodeExpanded,this),revealNode:e.proxy(this.revealNode,this),checkAll:e.proxy(this.checkAll,this),checkNode:e.proxy(this.checkNode,this),uncheckAll:e.proxy(this.uncheckAll,this),uncheckNode:e.proxy(this.uncheckNode,this),toggleNodeChecked:e.proxy(this.toggleNodeChecked,this),disableAll:e.proxy(this.disableAll,this),disableNode:e.proxy(this.disableNode,this),enableAll:e.proxy(this.enableAll,this),enableNode:e.proxy(this.enableNode,this),toggleNodeDisabled:e.proxy(this.toggleNodeDisabled,this),search:e.proxy(this.search,this),clearSearch:e.proxy(this.clearSearch,this)}};i.prototype.init=function(t){this.tree=[],this.nodes=[],t.data&&("string"==typeof t.data&&(t.data=e.parseJSON(t.data)),this.tree=e.extend(!0,[],t.data),delete t.data),this.options=e.extend({},n.settings,t),this.destroy(),this.subscribeEvents(),this.setInitialStates({nodes:this.tree},0),this.render()},i.prototype.remove=function(){this.destroy(),e.removeData(this,"treeview"),e("#"+this.styleId).remove()},i.prototype.destroy=function(){this.initialized&&(this.$wrapper.remove(),this.$wrapper=null,this.unsubscribeEvents(),this.initialized=!1)},i.prototype.unsubscribeEvents=function(){this.$element.off("click"),this.$element.off("nodeChecked"),this.$element.off("nodeCollapsed"),this.$element.off("nodeDisabled"),this.$element.off("nodeEnabled"),this.$element.off("nodeExpanded"),this.$element.off("nodeSelected"),this.$element.off("nodeUnchecked"),this.$element.off("nodeUnselected"),this.$element.off("searchComplete"),this.$element.off("searchCleared")},i.prototype.subscribeEvents=function(){this.unsubscribeEvents(),this.$element.on("click",e.proxy(this.clickHandler,this)),"function"==typeof this.options.onNodeChecked&&this.$element.on("nodeChecked",this.options.onNodeChecked),"function"==typeof this.options.onNodeCollapsed&&this.$element.on("nodeCollapsed",this.options.onNodeCollapsed),"function"==typeof this.options.onNodeDisabled&&this.$element.on("nodeDisabled",this.options.onNodeDisabled),"function"==typeof this.options.onNodeEnabled&&this.$element.on("nodeEnabled",this.options.onNodeEnabled),"function"==typeof this.options.onNodeExpanded&&this.$element.on("nodeExpanded",this.options.onNodeExpanded),"function"==typeof this.options.onNodeSelected&&this.$element.on("nodeSelected",this.options.onNodeSelected),"function"==typeof this.options.onNodeUnchecked&&this.$element.on("nodeUnchecked",this.options.onNodeUnchecked),"function"==typeof this.options.onNodeUnselected&&this.$element.on("nodeUnselected",this.options.onNodeUnselected),"function"==typeof this.options.onSearchComplete&&this.$element.on("searchComplete",this.options.onSearchComplete),"function"==typeof this.options.onSearchCleared&&this.$element.on("searchCleared",this.options.onSearchCleared)},i.prototype.setInitialStates=function(t,o){if(t.nodes){o+=1;var s=t,n=this;e.each(t.nodes,function(e,t){t.nodeId=n.nodes.length,t.parentId=s.nodeId,t.hasOwnProperty("selectable")||(t.selectable=!0),t.state=t.state||{},t.state.hasOwnProperty("checked")||(t.state.checked=!1),t.state.hasOwnProperty("disabled")||(t.state.disabled=!1),t.state.hasOwnProperty("expanded")||(!t.state.disabled&&o<n.options.levels&&t.nodes&&t.nodes.length>0?t.state.expanded=!0:t.state.expanded=!1),t.state.hasOwnProperty("selected")||(t.state.selected=!1),n.nodes.push(t),t.nodes&&n.setInitialStates(t,o)})}},i.prototype.clickHandler=function(t){this.options.enableLinks||t.preventDefault();var o=e(t.target),s=this.findNode(o);if(s&&!s.state.disabled){var i=o.attr("class")?o.attr("class").split(" "):[];-1!==i.indexOf("expand-icon")?(this.toggleExpandedState(s,n.options),this.render()):-1!==i.indexOf("check-icon")?(this.toggleCheckedState(s,n.options),this.render()):(s.selectable?this.toggleSelectedState(s,n.options):this.options.toggleUnselectable&&this.toggleExpandedState(s,n.options),this.render())}},i.prototype.findNode=function(e){var t=e.closest("li.list-group-item").attr("data-nodeid"),o=this.nodes[t];return o||console.log("Error: node does not exist"),o},i.prototype.toggleExpandedState=function(e,t){e&&this.setExpandedState(e,!e.state.expanded,t)},i.prototype.setExpandedState=function(t,o,s){o!==t.state.expanded&&(o&&t.nodes?(t.state.expanded=!0,s.silent||this.$element.trigger("nodeExpanded",e.extend(!0,{},t))):o||(t.state.expanded=!1,s.silent||this.$element.trigger("nodeCollapsed",e.extend(!0,{},t)),t.nodes&&!s.ignoreChildren&&e.each(t.nodes,e.proxy(function(e,t){this.setExpandedState(t,!1,s)},this))))},i.prototype.toggleSelectedState=function(e,t){e&&this.setSelectedState(e,!e.state.selected,t)},i.prototype.setSelectedState=function(t,o,s){o!==t.state.selected&&(o?(this.options.multiSelect||e.each(this.findNodes("true","g","state.selected"),e.proxy(function(e,t){this.setSelectedState(t,!1,s)},this)),t.state.selected=!0,s.silent||this.$element.trigger("nodeSelected",e.extend(!0,{},t))):(t.state.selected=!1,s.silent||this.$element.trigger("nodeUnselected",e.extend(!0,{},t))))},i.prototype.toggleCheckedState=function(e,t){e&&this.setCheckedState(e,!e.state.checked,t)},i.prototype.setCheckedState=function(t,o,s){o!==t.state.checked&&(o?(t.state.checked=!0,s.silent||this.$element.trigger("nodeChecked",e.extend(!0,{},t))):(t.state.checked=!1,s.silent||this.$element.trigger("nodeUnchecked",e.extend(!0,{},t))))},i.prototype.setDisabledState=function(t,o,s){o!==t.state.disabled&&(o?(t.state.disabled=!0,this.setExpandedState(t,!1,s),this.setSelectedState(t,!1,s),this.setCheckedState(t,!1,s),s.silent||this.$element.trigger("nodeDisabled",e.extend(!0,{},t))):(t.state.disabled=!1,s.silent||this.$element.trigger("nodeEnabled",e.extend(!0,{},t))))},i.prototype.render=function(){this.initialized||(this.$element.addClass("treeview"),this.$wrapper=e(this.template.list),this.injectStyle(),this.initialized=!0),this.$element.empty().append(this.$wrapper.empty()),this.buildTree(this.tree,0)},i.prototype.buildTree=function(t,o){if(t){o+=1;var s=this;e.each(t,function(t,n){for(var i=e(s.template.item).addClass("node-"+s.elementId).addClass(n.state.checked?"node-checked":"").addClass(n.state.disabled?"node-disabled":"").addClass(n.state.selected?"node-selected":"").addClass(n.searchResult?"search-result":"").attr("data-nodeid",n.nodeId).attr("style",s.buildStyleOverride(n)),d=0;d<o-1;d++)i.append(s.template.indent);r=[];if(n.nodes?(r.push("expand-icon"),n.state.expanded?r.push(s.options.collapseIcon):r.push(s.options.expandIcon)):r.push(s.options.emptyIcon),i.append(e(s.template.icon).addClass(r.join(" "))),s.options.showIcon&&((r=["node-icon"]).push(n.icon||s.options.nodeIcon),n.state.selected&&(r.pop(),r.push(n.selectedIcon||s.options.selectedIcon||n.icon||s.options.nodeIcon)),i.append(e(s.template.icon).addClass(r.join(" ")))),s.options.showCheckbox){var r=["check-icon"];n.state.checked?r.push(s.options.checkedIcon):r.push(s.options.uncheckedIcon),i.append(e(s.template.icon).addClass(r.join(" ")))}if(s.options.enableLinks?i.append(e(s.template.link).attr("href",n.href).append(n.text)):i.append(n.text),s.options.showTags&&n.tags&&e.each(n.tags,function(t,o){i.append(e(s.template.badge).append(o))}),s.$wrapper.append(i),n.nodes&&n.state.expanded&&!n.state.disabled)return s.buildTree(n.nodes,o)})}},i.prototype.buildStyleOverride=function(e){if(e.state.disabled)return"";var t=e.color,o=e.backColor;return e.selectable||(this.options.unselectableColor&&(t=this.options.unselectableColor),this.options.unselectableBackColor&&(o=this.options.unselectableBackColor)),this.options.highlightSelected&&e.state.selected&&(this.options.selectedColor&&(t=this.options.selectedColor),this.options.selectedBackColor&&(o=this.options.selectedBackColor)),this.options.highlightSearchResults&&e.searchResult&&!e.state.disabled&&(this.options.searchResultColor&&(t=this.options.searchResultColor),this.options.searchResultBackColor&&(o=this.options.searchResultBackColor)),"color:"+t+";background-color:"+o+";"},i.prototype.injectStyle=function(){this.options.injectStyle&&!o.getElementById(this.styleId)&&e('<style type="text/css" id="'+this.styleId+'"> '+this.buildStyle()+" </style>").appendTo("head")},i.prototype.buildStyle=function(){var e=".node-"+this.elementId+"{";return this.options.color&&(e+="color:"+this.options.color+";"),this.options.backColor&&(e+="background-color:"+this.options.backColor+";"),this.options.showBorder?this.options.borderColor&&(e+="border:1px solid "+this.options.borderColor+";"):e+="border:none;",e+="}",this.options.onhoverColor&&(e+=".node-"+this.elementId+":not(.node-disabled):hover{background-color:"+this.options.onhoverColor+";}"),this.css+e},i.prototype.template={list:'<ul class="list-group"></ul>',item:'<li class="list-group-item"></li>',indent:'<span class="indent"></span>',icon:'<span class="icon"></span>',link:'<a href="#" style="color:inherit;"></a>',badge:'<span class="badge"></span>'},i.prototype.css=".treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}",i.prototype.getNode=function(e){return this.nodes[e]},i.prototype.getParent=function(e){var t=this.identifyNode(e);return this.nodes[t.parentId]},i.prototype.getSiblings=function(e){var t=this.identifyNode(e),o=this.getParent(t);return(o?o.nodes:this.tree).filter(function(e){return e.nodeId!==t.nodeId})},i.prototype.getSelected=function(){return this.findNodes("true","g","state.selected")},i.prototype.getUnselected=function(){return this.findNodes("false","g","state.selected")},i.prototype.getExpanded=function(){return this.findNodes("true","g","state.expanded")},i.prototype.getCollapsed=function(){return this.findNodes("false","g","state.expanded")},i.prototype.getChecked=function(){return this.findNodes("true","g","state.checked")},i.prototype.getUnchecked=function(){return this.findNodes("false","g","state.checked")},i.prototype.getDisabled=function(){return this.findNodes("true","g","state.disabled")},i.prototype.getEnabled=function(){return this.findNodes("false","g","state.disabled")},i.prototype.selectNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setSelectedState(e,!0,t)},this)),this.render()},i.prototype.unselectNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setSelectedState(e,!1,t)},this)),this.render()},i.prototype.toggleNodeSelected=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.toggleSelectedState(e,t)},this)),this.render()},i.prototype.collapseAll=function(t){var o=this.findNodes("true","g","state.expanded");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setExpandedState(e,!1,t)},this)),this.render()},i.prototype.collapseNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setExpandedState(e,!1,t)},this)),this.render()},i.prototype.expandAll=function(t){if((t=e.extend({},n.options,t))&&t.levels)this.expandLevels(this.tree,t.levels,t);else{var o=this.findNodes("false","g","state.expanded");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setExpandedState(e,!0,t)},this))}this.render()},i.prototype.expandNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setExpandedState(e,!0,t),e.nodes&&t&&t.levels&&this.expandLevels(e.nodes,t.levels-1,t)},this)),this.render()},i.prototype.expandLevels=function(t,o,s){s=e.extend({},n.options,s),e.each(t,e.proxy(function(e,t){this.setExpandedState(t,o>0,s),t.nodes&&this.expandLevels(t.nodes,o-1,s)},this))},i.prototype.revealNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){for(var o=this.getParent(e);o;)this.setExpandedState(o,!0,t),o=this.getParent(o)},this)),this.render()},i.prototype.toggleNodeExpanded=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.toggleExpandedState(e,t)},this)),this.render()},i.prototype.checkAll=function(t){var o=this.findNodes("false","g","state.checked");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setCheckedState(e,!0,t)},this)),this.render()},i.prototype.checkNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setCheckedState(e,!0,t)},this)),this.render()},i.prototype.uncheckAll=function(t){var o=this.findNodes("true","g","state.checked");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setCheckedState(e,!1,t)},this)),this.render()},i.prototype.uncheckNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setCheckedState(e,!1,t)},this)),this.render()},i.prototype.toggleNodeChecked=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.toggleCheckedState(e,t)},this)),this.render()},i.prototype.disableAll=function(t){var o=this.findNodes("false","g","state.disabled");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setDisabledState(e,!0,t)},this)),this.render()},i.prototype.disableNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setDisabledState(e,!0,t)},this)),this.render()},i.prototype.enableAll=function(t){var o=this.findNodes("true","g","state.disabled");this.forEachIdentifier(o,t,e.proxy(function(e,t){this.setDisabledState(e,!1,t)},this)),this.render()},i.prototype.enableNode=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setDisabledState(e,!1,t)},this)),this.render()},i.prototype.toggleNodeDisabled=function(t,o){this.forEachIdentifier(t,o,e.proxy(function(e,t){this.setDisabledState(e,!e.state.disabled,t)},this)),this.render()},i.prototype.forEachIdentifier=function(t,o,s){o=e.extend({},n.options,o),t instanceof Array||(t=[t]),e.each(t,e.proxy(function(e,t){s(this.identifyNode(t),o)},this))},i.prototype.identifyNode=function(e){return"number"==typeof e?this.nodes[e]:e},i.prototype.search=function(t,o){o=e.extend({},n.searchOptions,o),this.clearSearch({render:!1});var s=[];if(t&&t.length>0){o.exactMatch&&(t="^"+t+"$");var i="g";o.ignoreCase&&(i+="i"),s=this.findNodes(t,i),e.each(s,function(e,t){t.searchResult=!0})}return o.revealResults?this.revealNode(s):this.render(),this.$element.trigger("searchComplete",e.extend(!0,{},s)),s},i.prototype.clearSearch=function(t){t=e.extend({},{render:!0},t);var o=e.each(this.findNodes("true","g","searchResult"),function(e,t){t.searchResult=!1});t.render&&this.render(),this.$element.trigger("searchCleared",e.extend(!0,{},o))},i.prototype.findNodes=function(t,o,s){o=o||"g",s=s||"text";var n=this;return e.grep(this.nodes,function(e){var i=n.getNodeValue(e,s);if("string"==typeof i)return i.match(new RegExp(t,o))})},i.prototype.getNodeValue=function(e,t){var o=t.indexOf(".");if(o>0){var s=e[t.substring(0,o)],n=t.substring(o+1,t.length);return this.getNodeValue(s,n)}return e.hasOwnProperty(t)?e[t].toString():void 0};var d=function(e){t.console&&t.console.error(e)};e.fn.treeview=function(t,o){var s;return this.each(function(){var n=e.data(this,"treeview");"string"==typeof t?n?e.isFunction(n[t])&&"_"!==t.charAt(0)?(o instanceof Array||(o=[o]),s=n[t].apply(n,o)):d("No such method : "+t):d("Not initialized, can not call method : "+t):"boolean"==typeof t?s=n:e.data(this,"treeview",new i(this,e.extend(!0,{},t)))}),s||this}}(jQuery,window,document);