1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- /* =========================================================
- * bootstrap-treeview.js v1.2.0
- * =========================================================
- * 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',
- searchResultColor: '#D9534F',
- searchResultBackColor: undefined, //'#FFFFFF',
- enableLinks: false,
- highlightSelected: true,
- highlightSearchResults: true,
- showBorder: true,
- showIcon: true,
- showCheckbox: false,
- showTags: false,
- 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 {
- 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 (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);
|