1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951 |
- /* =========================================================
- * patternfly-bootstrap-treeview.js v2.1.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',
- loadingIcon: 'glyphicon glyphicon-hourglass',
- emptyIcon: 'glyphicon',
- nodeIcon: '',
- selectedIcon: '',
- checkedIcon: 'glyphicon glyphicon-check',
- partiallyCheckedIcon: 'glyphicon glyphicon-expand',
- uncheckedIcon: 'glyphicon glyphicon-unchecked',
- color: undefined,
- backColor: undefined,
- borderColor: undefined,
- changedNodeColor: '#39A5DC',
- onhoverColor: '#F5F5F5',
- selectedColor: '#FFFFFF',
- selectedBackColor: '#428bca',
- searchResultColor: '#D9534F',
- searchResultBackColor: undefined,
- highlightSelected: true,
- highlightSearchResults: true,
- showBorder: true,
- showIcon: true,
- showImage: false,
- showCheckbox: false,
- checkboxFirst: false,
- highlightChanges: false,
- showTags: false,
- multiSelect: false,
- preventUnselect: false,
- allowReselect: false,
- hierarchicalCheck: false,
- propagateCheckEvent: false,
- wrapNodeText: false,
- // Event handlers
- onLoading: undefined,
- onLoadingFailed: undefined,
- onInitialized: undefined,
- onNodeRendered: undefined,
- onRendered: undefined,
- onDestroyed: undefined,
- 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
- };
- _default.dataUrl = {
- method: 'GET',
- dataType: 'json',
- cache: false
- };
- 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),
- // Query methods
- findNodes: $.proxy(this.findNodes, this),
- getNodes: $.proxy(this.getNodes, this), // todo document + test
- getParents: $.proxy(this.getParents, 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),
- // Tree manipulation methods
- addNode: $.proxy(this.addNode, this),
- addNodeAfter: $.proxy(this.addNodeAfter, this),
- addNodeBefore: $.proxy(this.addNodeBefore, this),
- removeNode: $.proxy(this.removeNode, this),
- updateNode: $.proxy(this.updateNode, this),
- editNode: $.proxy(this.editNode, 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),
- // Check / uncheck 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),
- unmarkCheckboxChanges: $.proxy(this.unmarkCheckboxChanges, 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._initialized = false;
- this._options = $.extend({}, _default.settings, options);
- // Cache empty icon DOM template
- this._template.icon.empty.addClass(this._options.emptyIcon);
- this._destroy();
- this._subscribeEvents();
- this._triggerEvent('loading', null, _default.options);
- this._load(options)
- .then($.proxy(function (data) {
- // load done
- return this._tree = $.extend(true, [], data);
- }, this), $.proxy(function (error) {
- // load fail
- this._triggerEvent('loadingFailed', error, _default.options);
- }, this))
- .then($.proxy(function (treeData) {
- // initialize data
- return this._setInitialStates({ nodes: treeData }, 0);
- }, this))
- .then($.proxy(function () {
- // render to DOM
- this._render();
- }, this));
- };
- Tree.prototype._load = function (options) {
- var done = new $.Deferred();
- if (options.data) {
- this._loadLocalData(options, done);
- } else if (options.dataUrl) {
- this._loadRemoteData(options, done);
- }
- return done.promise();
- };
- Tree.prototype._loadRemoteData = function (options, done) {
- $.ajax($.extend(true, {}, _default.dataUrl, options.dataUrl))
- .done(function (data) {
- done.resolve(data);
- })
- .fail(function (xhr, status, error) {
- done.reject(error);
- });
- };
- Tree.prototype._loadLocalData = function (options, done) {
- done.resolve((typeof options.data === 'string') ?
- JSON.parse(options.data) :
- $.extend(true, [], options.data));
- };
- Tree.prototype._remove = function () {
- this._destroy();
- $.removeData(this, pluginName);
- $('#' + this._styleId).remove();
- };
- Tree.prototype._destroy = function () {
- if (!this._initialized) return;
- this._initialized = false;
- this._triggerEvent('destroyed', null, _default.options);
- // Switch off events
- this._unsubscribeEvents();
- // Tear down
- this.$wrapper.remove();
- this.$wrapper = null;
- };
- Tree.prototype._unsubscribeEvents = function () {
- this.$element.off('loading');
- this.$element.off('loadingFailed');
- this.$element.off('initialized');
- this.$element.off('nodeRendered');
- this.$element.off('rendered');
- this.$element.off('destroyed');
- 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();
- if (typeof (this._options.onLoading) === 'function') {
- this.$element.on('loading', this._options.onLoading);
- }
- if (typeof (this._options.onLoadingFailed) === 'function') {
- this.$element.on('loadingFailed', this._options.onLoadingFailed);
- }
- if (typeof (this._options.onInitialized) === 'function') {
- this.$element.on('initialized', this._options.onInitialized);
- }
- if (typeof (this._options.onNodeRendered) === 'function') {
- this.$element.on('nodeRendered', this._options.onNodeRendered);
- }
- if (typeof (this._options.onRendered) === 'function') {
- this.$element.on('rendered', this._options.onRendered);
- }
- if (typeof (this._options.onDestroyed) === 'function') {
- this.$element.on('destroyed', this._options.onDestroyed);
- }
- 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);
- }
- };
- Tree.prototype._triggerEvent = function (event, data, options) {
- if (options && !options.silent) {
- this.$element.trigger(event, $.extend(true, {}, data));
- }
- }
- /*
- 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 ordered structure
- */
- Tree.prototype._setInitialStates = function (node, level) {
- this._nodes = {};
- return $.when.apply(this, this._setInitialState(node, level))
- .done($.proxy(function () {
- this._orderedNodes = this._sortNodes();
- this._inheritCheckboxChanges();
- this._triggerEvent('initialized', this._orderedNodes, _default.options);
- return;
- }, this));
- };
- Tree.prototype._setInitialState = function (node, level, done) {
- if (!node.nodes) return;
- level += 1;
- done = done || [];
- var parent = node;
- $.each(node.nodes, $.proxy(function (index, node) {
- var deferred = new $.Deferred();
- done.push(deferred.promise());
- // level : hierarchical tree level, starts at 1
- node.level = level;
- // index : relative to siblings
- node.index = index;
- // nodeId : unique, hierarchical identifier
- node.nodeId = (parent && parent.nodeId) ?
- parent.nodeId + '.' + node.index :
- (level - 1) + '.' + node.index;
- // parentId : transversing up the tree
- node.parentId = parent.nodeId;
- // if not provided set selectable default value
- if (!node.hasOwnProperty('selectable')) {
- node.selectable = true;
- }
- // if not provided set checkable default value
- if (!node.hasOwnProperty('checkable')) {
- node.checkable = 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;
- }
- // convert the undefined string if hierarchical checks are enabled
- if (this._options.hierarchicalCheck && node.state.checked === 'undefined') {
- node.state.checked = undefined;
- }
- // 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;
- }
- // set visible state; based parent state plus levels
- if ((parent && parent.state && parent.state.expanded) ||
- (level <= this._options.levels)) {
- node.state.visible = true;
- }
- else {
- node.state.visible = false;
- }
- // recurse child nodes and transverse the tree, depth-first
- if (node.nodes) {
- if (node.nodes.length > 0) {
- this._setInitialState(node, level, done);
- }
- else {
- delete node.nodes;
- }
- }
- // add / update indexed collection
- this._nodes[node.nodeId] = node;
- // mark task as complete
- deferred.resolve();
- }, this));
- return done;
- };
- Tree.prototype._sortNodes = function () {
- return $.map(Object.keys(this._nodes).sort(function (a, b) {
- if (a === b) return 0;
- var a = a.split('.').map(function (level) { return parseInt(level); });
- var b = b.split('.').map(function (level) { return parseInt(level); });
- var c = Math.max(a.length, b.length);
- for (var i=0; i<c; i++) {
- if (a[i] === undefined) return -1;
- if (b[i] === undefined) return +1;
- if (a[i] - b[i] > 0) return +1;
- if (a[i] - b[i] < 0) return -1;
- };
- }), $.proxy(function (value, index) {
- return this._nodes[value];
- }, this));
- };
- Tree.prototype._clickHandler = function (event) {
- var target = $(event.target);
- var node = this.targetNode(target);
- if (!node || node.state.disabled) return;
- var classList = target.attr('class') ? target.attr('class').split(' ') : [];
- if ((classList.indexOf('expand-icon') !== -1)) {
- this._toggleExpanded(node, $.extend({}, _default.options));
- }
- else if ((classList.indexOf('check-icon') !== -1)) {
- if (node.checkable) {
- this._toggleChecked(node, $.extend({}, _default.options));
- }
- }
- else {
- if (node.selectable) {
- this._toggleSelected(node, $.extend({}, _default.options));
- } else {
- this._toggleExpanded(node, $.extend({}, _default.options));
- }
- }
- };
- // 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.targetNode = 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._toggleExpanded = function (node, options) {
- if (!node) return;
- // Lazy-load the child nodes if possible
- if (typeof(this._options.lazyLoad) === 'function' && node.lazyLoad) {
- this._lazyLoad(node);
- } else {
- this._setExpanded(node, !node.state.expanded, options);
- }
- };
- Tree.prototype._lazyLoad = function (node) {
- // Show a different icon while loading the child nodes
- node.$el.children('span.expand-icon')
- .removeClass(this._options.expandIcon)
- .addClass(this._options.loadingIcon);
- var _this = this;
- this._options.lazyLoad(node, function (nodes) {
- // Adding the node will expand its parent automatically
- _this.addNode(nodes, node);
- });
- // Only the first expand should do a lazy-load
- delete node.lazyLoad;
- };
- Tree.prototype._setExpanded = function (node, state, options) {
- // We never pass options when rendering, so the only time
- // we need to validate state is from user interaction
- if (options && state === node.state.expanded) return;
- if (state && node.nodes) {
- // Set node state
- node.state.expanded = true;
- // Set element
- if (node.$el) {
- node.$el.children('span.expand-icon')
- .removeClass(this._options.expandIcon)
- .removeClass(this._options.loadingIcon)
- .addClass(this._options.collapseIcon);
- }
- // Expand children
- if (node.nodes && options) {
- $.each(node.nodes, $.proxy(function (index, node) {
- this._setVisible(node, true, options);
- }, this));
- }
- // Optionally trigger event
- this._triggerEvent('nodeExpanded', node, options);
- }
- else if (!state) {
- // Set node state
- node.state.expanded = false;
- // Set element
- if (node.$el) {
- node.$el.children('span.expand-icon')
- .removeClass(this._options.collapseIcon)
- .addClass(this._options.expandIcon);
- }
- // Collapse children
- if (node.nodes && options) {
- $.each(node.nodes, $.proxy(function (index, node) {
- this._setVisible(node, false, options);
- this._setExpanded(node, false, options);
- }, this));
- }
- // Optionally trigger event
- this._triggerEvent('nodeCollapsed', node, options);
- }
- };
- Tree.prototype._setVisible = function (node, state, options) {
- if (options && state === node.state.visible) return;
- if (state) {
- // Set node state
- node.state.visible = true;
- // Set element
- if (node.$el) {
- node.$el.removeClass('node-hidden');
- }
- }
- else {
- // Set node state to unchecked
- node.state.visible = false;
- // Set element
- if (node.$el) {
- node.$el.addClass('node-hidden');
- }
- }
- };
- Tree.prototype._toggleSelected = function (node, options) {
- if (!node) return;
- this._setSelected(node, !node.state.selected, options);
- return this;
- };
- Tree.prototype._setSelected = function (node, state, options) {
- // We never pass options when rendering, so the only time
- // we need to validate state is from user interaction
- if (options && (state === node.state.selected)) return;
- if (state) {
- // If multiSelect false, unselect previously selected
- if (!this._options.multiSelect) {
- $.each(this._findNodes('true', 'state.selected'), $.proxy(function (index, node) {
- this._setSelected(node, false, $.extend(options, {unselecting: true}));
- }, this));
- }
- // Set node state
- node.state.selected = true;
- // Set element
- if (node.$el) {
- node.$el.addClass('node-selected');
- if (node.selectedIcon || this._options.selectedIcon) {
- node.$el.children('span.node-icon')
- .removeClass(node.icon || this._options.nodeIcon)
- .addClass(node.selectedIcon || this._options.selectedIcon);
- }
- }
- // Optionally trigger event
- this._triggerEvent('nodeSelected', node, options);
- }
- else {
- // If preventUnselect true + only one remaining selection, disable unselect
- if (this._options.preventUnselect &&
- (options && !options.unselecting) &&
- (this._findNodes('true', 'state.selected').length === 1)) {
- // Fire the nodeSelected event if reselection is allowed
- if (this._options.allowReselect) {
- this._triggerEvent('nodeSelected', node, options);
- }
- return this;
- }
- // Set node state
- node.state.selected = false;
- // Set element
- if (node.$el) {
- node.$el.removeClass('node-selected');
- if (node.selectedIcon || this._options.selectedIcon) {
- node.$el.children('span.node-icon')
- .removeClass(node.selectedIcon || this._options.selectedIcon)
- .addClass(node.icon || this._options.nodeIcon);
- }
- }
- // Optionally trigger event
- this._triggerEvent('nodeUnselected', node, options);
- }
- return this;
- };
- Tree.prototype._inheritCheckboxChanges = function () {
- if (this._options.showCheckbox && this._options.highlightChanges) {
- this._checkedNodes = $.grep(this._orderedNodes, function (node) {
- return node.state.checked;
- });
- }
- };
- Tree.prototype._toggleChecked = function (node, options) {
- if (!node) return;
- if (this._options.hierarchicalCheck) {
- // Event propagation to the parent/child nodes
- var childOptions = $.extend({}, options, {silent: options.silent || !this._options.propagateCheckEvent});
- var state, currentNode = node;
- // Temporarily swap the tree state
- node.state.checked = !node.state.checked;
- // Iterate through each parent node
- while (currentNode = this._nodes[currentNode.parentId]) {
- // Calculate the state
- state = currentNode.nodes.reduce(function (acc, curr) {
- return (acc === curr.state.checked) ? acc : undefined;
- }, currentNode.nodes[0].state.checked);
- // Set the state
- this._setChecked(currentNode, state, childOptions);
- }
- if (node.nodes && node.nodes.length > 0) {
- // Copy the content of the array
- var child, children = node.nodes.slice();
- // Iterate through each child node
- while (children && children.length > 0) {
- child = children.pop();
- // Set the state
- this._setChecked(child, node.state.checked, childOptions);
- // Append children to the end of the list
- if (child.nodes && child.nodes.length > 0) {
- children = children.concat(child.nodes.slice());
- }
- }
- }
- // Swap back the tree state
- node.state.checked = !node.state.checked;
- }
- this._setChecked(node, !node.state.checked, options);
- };
- Tree.prototype._setChecked = function (node, state, options) {
- // We never pass options when rendering, so the only time
- // we need to validate state is from user interaction
- if (options && state === node.state.checked) return;
- // Highlight the node if its checkbox has unsaved changes
- if (this._options.highlightChanges) {
- node.$el.toggleClass('node-check-changed', (this._checkedNodes.indexOf(node) == -1) == state);
- }
- if (state) {
- // Set node state
- node.state.checked = true;
- // Set element
- if (node.$el) {
- node.$el.addClass('node-checked').removeClass('node-checked-partial');
- node.$el.children('span.check-icon')
- .removeClass(this._options.uncheckedIcon)
- .removeClass(this._options.partiallyCheckedIcon)
- .addClass(this._options.checkedIcon);
- }
- // Optionally trigger event
- this._triggerEvent('nodeChecked', node, options);
- }
- else if (state === undefined && this._options.hierarchicalCheck) {
- // Set node state to partially checked
- node.state.checked = undefined;
- // Set element
- if (node.$el) {
- node.$el.addClass('node-checked-partial').removeClass('node-checked');
- node.$el.children('span.check-icon')
- .removeClass(this._options.uncheckedIcon)
- .removeClass(this._options.checkedIcon)
- .addClass(this._options.partiallyCheckedIcon);
- }
- // Optionally trigger event, partially checked is technically unchecked
- this._triggerEvent('nodeUnchecked', node, options);
- } else {
- // Set node state to unchecked
- node.state.checked = false;
- // Set element
- if (node.$el) {
- node.$el.removeClass('node-checked node-checked-partial');
- node.$el.children('span.check-icon')
- .removeClass(this._options.checkedIcon)
- .removeClass(this._options.partiallyCheckedIcon)
- .addClass(this._options.uncheckedIcon);
- }
- // Optionally trigger event
- this._triggerEvent('nodeUnchecked', node, options);
- }
- };
- Tree.prototype._setDisabled = function (node, state, options) {
- // We never pass options when rendering, so the only time
- // we need to validate state is from user interaction
- if (options && state === node.state.disabled) return;
- if (state) {
- // Set node state to disabled
- node.state.disabled = true;
- // Disable all other states
- if (options && !options.keepState) {
- this._setSelected(node, false, options);
- this._setChecked(node, false, options);
- this._setExpanded(node, false, options);
- }
- // Set element
- if (node.$el) {
- node.$el.addClass('node-disabled');
- }
- // Optionally trigger event
- this._triggerEvent('nodeDisabled', node, options);
- }
- else {
- // Set node state to enabled
- node.state.disabled = false;
- // Set element
- if (node.$el) {
- node.$el.removeClass('node-disabled');
- }
- // Optionally trigger event
- this._triggerEvent('nodeEnabled', node, options);
- }
- };
- Tree.prototype._setSearchResult = function (node, state, options) {
- if (options && state === node.searchResult) return;
- if (state) {
- node.searchResult = true;
- if (node.$el) {
- node.$el.addClass('node-result');
- }
- }
- else {
- node.searchResult = false;
- if (node.$el) {
- node.$el.removeClass('node-result');
- }
- }
- };
- Tree.prototype._render = function () {
- if (!this._initialized) {
- // Setup first time only components
- this.$wrapper = this._template.tree.clone();
- this.$element.empty()
- .addClass(pluginName)
- .append(this.$wrapper);
- this._injectStyle();
- this._initialized = true;
- }
- var previousNode;
- $.each(this._orderedNodes, $.proxy(function (id, node) {
- this._renderNode(node, previousNode);
- previousNode = node;
- }, this));
- this._triggerEvent('rendered', this._orderedNodes, _default.options);
- };
- Tree.prototype._renderNode = function (node, previousNode) {
- if (!node) return;
- if (!node.$el) {
- node.$el = this._newNodeEl(node, previousNode)
- .addClass('node-' + this._elementId);
- }
- else {
- node.$el.empty();
- }
- // Append .classes to the node
- node.$el.addClass(node.class);
- // Set the #id of the node if specified
- if (node.id) {
- node.$el.attr('id', node.id);
- }
- // Append custom data- attributes to the node
- if (node.dataAttr) {
- $.each(node.dataAttr, function (key, value) {
- node.$el.attr('data-' + key, value);
- });
- }
- // Set / update nodeid; it can change as a result of addNode etc.
- node.$el.attr('data-nodeId', node.nodeId);
- // Set the tooltip attribute if present
- if (node.tooltip) {
- node.$el.attr('title', node.tooltip);
- }
- // Add indent/spacer to mimic tree structure
- for (var i = 0; i < (node.level - 1); i++) {
- node.$el.append(this._template.indent.clone());
- }
- // Add expand / collapse or empty spacer icons
- node.$el
- .append(
- node.nodes || node.lazyLoad ? this._template.icon.expand.clone() : this._template.icon.empty.clone()
- );
- // Add checkbox and node icons
- if (this._options.checkboxFirst) {
- this._addCheckbox(node);
- this._addIcon(node);
- this._addImage(node);
- } else {
- this._addIcon(node);
- this._addImage(node);
- this._addCheckbox(node);
- }
- // Add text
- if (this._options.wrapNodeText) {
- var wrapper = this._template.text.clone();
- node.$el.append(wrapper);
- wrapper.append(node.text);
- } else {
- node.$el.append(node.text);
- }
- // Add tags as badges
- if (this._options.showTags && node.tags) {
- $.each(node.tags, $.proxy(function addTag(id, tag) {
- node.$el
- .append(this._template.badge.clone()
- .append(tag)
- );
- }, this));
- }
- // Set various node states
- this._setSelected(node, node.state.selected);
- this._setChecked(node, node.state.checked);
- this._setSearchResult(node, node.searchResult);
- this._setExpanded(node, node.state.expanded);
- this._setDisabled(node, node.state.disabled);
- this._setVisible(node, node.state.visible);
- // Trigger nodeRendered event
- this._triggerEvent('nodeRendered', node, _default.options);
- };
- // Add checkable icon
- Tree.prototype._addCheckbox = function (node) {
- if (this._options.showCheckbox && (node.hideCheckbox === undefined || node.hideCheckbox === false)) {
- node.$el
- .append(this._template.icon.check.clone());
- }
- }
- // Add node icon
- Tree.prototype._addIcon = function (node) {
- if (this._options.showIcon && !(this._options.showImage && node.image)) {
- node.$el
- .append(this._template.icon.node.clone()
- .addClass(node.icon || this._options.nodeIcon)
- );
- }
- }
- Tree.prototype._addImage = function (node) {
- if (this._options.showImage && node.image) {
- node.$el
- .append(this._template.image.clone()
- .addClass('node-image')
- .css('background-image', "url('" + node.image + "')")
- );
- }
- }
- // Creates a new node element from template and
- // ensures the template is inserted at the correct position
- Tree.prototype._newNodeEl = function (node, previousNode) {
- var $el = this._template.node.clone();
- if (previousNode) {
- // typical usage, as nodes are rendered in
- // sort order we add after the previous element
- previousNode.$el.after($el);
- } else {
- // we use prepend instead of append,
- // to cater for root inserts i.e. nodeId 0.0
- this.$wrapper.prepend($el);
- }
- return $el;
- };
- // Recursively remove node elements from DOM
- Tree.prototype._removeNodeEl = function (node) {
- if (!node) return;
- if (node.nodes) {
- $.each(node.nodes, $.proxy(function (index, node) {
- this._removeNodeEl(node);
- }, this));
- }
- node.$el.remove();
- };
- // Expand node, rendering it's immediate children
- Tree.prototype._expandNode = function (node) {
- if (!node.nodes) return;
- $.each(node.nodes.slice(0).reverse(), $.proxy(function (index, childNode) {
- childNode.level = node.level + 1;
- this._renderNode(childNode, node.$el);
- }, this));
- };
- // 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 + '{';
- // Basic bootstrap style overrides
- 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 + ';' +
- '}';
- }
- // Style search results
- if (this._options.highlightSearchResults && (this._options.searchResultColor || this._options.searchResultBackColor)) {
- var innerStyle = ''
- if (this._options.searchResultColor) {
- innerStyle += 'color:' + this._options.searchResultColor + ';';
- }
- if (this._options.searchResultBackColor) {
- innerStyle += 'background-color:' + this._options.searchResultBackColor + ';';
- }
- style += '.node-' + this._elementId + '.node-result{' + innerStyle + '}';
- style += '.node-' + this._elementId + '.node-result:hover{' + innerStyle + '}';
- }
- // Style selected nodes
- if (this._options.highlightSelected && (this._options.selectedColor || this._options.selectedBackColor)) {
- var innerStyle = ''
- if (this._options.selectedColor) {
- innerStyle += 'color:' + this._options.selectedColor + ';';
- }
- if (this._options.selectedBackColor) {
- innerStyle += 'background-color:' + this._options.selectedBackColor + ';';
- }
- style += '.node-' + this._elementId + '.node-selected{' + innerStyle + '}';
- style += '.node-' + this._elementId + '.node-selected:hover{' + innerStyle + '}';
- }
- // Style changed nodes
- if (this._options.highlightChanges) {
- var innerStyle = 'color: ' + this._options.changedNodeColor + ';';
- style += '.node-' + this._elementId + '.node-check-changed{' + innerStyle + '}';
- }
- // Node level style overrides
- $.each(this._orderedNodes, $.proxy(function (index, node) {
- if (node.color || node.backColor) {
- var innerStyle = '';
- if (node.color) {
- innerStyle += 'color:' + node.color + ';';
- }
- if (node.backColor) {
- innerStyle += 'background-color:' + node.backColor + ';';
- }
- style += '.node-' + this._elementId + '[data-nodeId="' + node.nodeId + '"]{' + innerStyle + '}';
- }
- }, this));
- return this._css + style;
- };
- Tree.prototype._template = {
- tree: $('<ul class="list-group"></ul>'),
- node: $('<li class="list-group-item"></li>'),
- indent: $('<span class="indent"></span>'),
- icon: {
- node: $('<span class="icon node-icon"></span>'),
- expand: $('<span class="icon expand-icon"></span>'),
- check: $('<span class="icon check-icon"></span>'),
- empty: $('<span class="icon"></span>')
- },
- image: $('<span class="image"></span>'),
- badge: $('<span class="badge"></span>'),
- text: $('<span class="text"></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 an array of matching node objects.
- @param {String} pattern - A pattern to match against a given field
- @return {String} field - Field to query pattern against
- */
- Tree.prototype.findNodes = function (pattern, field) {
- return this._findNodes(pattern, field);
- };
- /**
- Returns an ordered aarray of node objects.
- @return {Array} nodes - An array of all nodes
- */
- Tree.prototype.getNodes = function () {
- return this._orderedNodes;
- };
- /**
- Returns parent nodes for given nodes, if valid otherwise returns undefined.
- @param {Array} nodes - An array of nodes
- @returns {Array} nodes - An array of parent nodes
- */
- Tree.prototype.getParents = function (nodes) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- var parentNodes = [];
- $.each(nodes, $.proxy(function (index, node) {
- var parentNode = node.parentId ? this._nodes[node.parentId] : false;
- if (parentNode) {
- parentNodes.push(parentNode);
- }
- }, this));
- return parentNodes;
- };
- /**
- Returns an array of sibling nodes for given nodes, if valid otherwise returns undefined.
- @param {Array} nodes - An array of nodes
- @returns {Array} nodes - An array of sibling nodes
- */
- Tree.prototype.getSiblings = function (nodes) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- var siblingNodes = [];
- $.each(nodes, $.proxy(function (index, node) {
- var parent = this.getParents([node]);
- var nodes = parent[0] ? parent[0].nodes : this._tree;
- siblingNodes = nodes.filter(function (obj) {
- return obj.nodeId !== node.nodeId;
- });
- }, this));
- // flatten possible nested array before returning
- return $.map(siblingNodes, function (obj) {
- return obj;
- });
- };
- /**
- Returns an array of selected nodes.
- @returns {Array} nodes - Selected nodes
- */
- Tree.prototype.getSelected = function () {
- return this._findNodes('true', 'state.selected');
- };
- /**
- Returns an array of unselected nodes.
- @returns {Array} nodes - Unselected nodes
- */
- Tree.prototype.getUnselected = function () {
- return this._findNodes('false', 'state.selected');
- };
- /**
- Returns an array of expanded nodes.
- @returns {Array} nodes - Expanded nodes
- */
- Tree.prototype.getExpanded = function () {
- return this._findNodes('true', 'state.expanded');
- };
- /**
- Returns an array of collapsed nodes.
- @returns {Array} nodes - Collapsed nodes
- */
- Tree.prototype.getCollapsed = function () {
- return this._findNodes('false', 'state.expanded');
- };
- /**
- Returns an array of checked nodes.
- @returns {Array} nodes - Checked nodes
- */
- Tree.prototype.getChecked = function () {
- return this._findNodes('true', 'state.checked');
- };
- /**
- Returns an array of unchecked nodes.
- @returns {Array} nodes - Unchecked nodes
- */
- Tree.prototype.getUnchecked = function () {
- return this._findNodes('false', 'state.checked');
- };
- /**
- Returns an array of disabled nodes.
- @returns {Array} nodes - Disabled nodes
- */
- Tree.prototype.getDisabled = function () {
- return this._findNodes('true', 'state.disabled');
- };
- /**
- Returns an array of enabled nodes.
- @returns {Array} nodes - Enabled nodes
- */
- Tree.prototype.getEnabled = function () {
- return this._findNodes('false', 'state.disabled');
- };
- /**
- Add nodes to the tree.
- @param {Array} nodes - An array of nodes to add
- @param {optional Object} parentNode - The node to which nodes will be added as children
- @param {optional number} index - Zero based insert index
- @param {optional Object} options
- */
- Tree.prototype.addNode = function (nodes, parentNode, index, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- if (parentNode instanceof Array) {
- parentNode = parentNode[0];
- }
- options = $.extend({}, _default.options, options);
- // identify target nodes; either the tree's root or a parent's child nodes
- var targetNodes;
- if (parentNode && parentNode.nodes) {
- targetNodes = parentNode.nodes;
- } else if (parentNode) {
- targetNodes = parentNode.nodes = [];
- } else {
- targetNodes = this._tree;
- }
- // inserting nodes at specified positions
- $.each(nodes, $.proxy(function (i, node) {
- var insertIndex = (typeof(index) === 'number') ? (index + i) : (targetNodes.length + 1);
- targetNodes.splice(insertIndex, 0, node);
- }, this));
- // initialize new state and render changes
- this._setInitialStates({nodes: this._tree}, 0)
- .done($.proxy(function () {
- if (parentNode && !parentNode.state.expanded) {
- this._setExpanded(parentNode, true, options);
- }
- this._render();
- }, this));
- }
- /**
- Add nodes to the tree after given node.
- @param {Array} nodes - An array of nodes to add
- @param {Object} node - The node to which nodes will be added after
- @param {optional Object} options
- */
- Tree.prototype.addNodeAfter = function (nodes, node, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- if (node instanceof Array) {
- node = node[0];
- }
- options = $.extend({}, _default.options, options);
- this.addNode(nodes, this.getParents(node)[0], (node.index + 1), options);
- }
- /**
- Add nodes to the tree before given node.
- @param {Array} nodes - An array of nodes to add
- @param {Object} node - The node to which nodes will be added before
- @param {optional Object} options
- */
- Tree.prototype.addNodeBefore = function (nodes, node, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- if (node instanceof Array) {
- node = node[0];
- }
- options = $.extend({}, _default.options, options);
- this.addNode(nodes, this.getParents(node)[0], node.index, options);
- }
- /**
- Removes given nodes from the tree.
- @param {Array} nodes - An array of nodes to remove
- @param {optional Object} options
- */
- Tree.prototype.removeNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- var targetNodes, parentNode;
- $.each(nodes, $.proxy(function (index, node) {
- // remove nodes from tree
- parentNode = this._nodes[node.parentId];
- if (parentNode) {
- targetNodes = parentNode.nodes;
- } else {
- targetNodes = this._tree;
- }
- targetNodes.splice(node.index, 1);
- // remove node from DOM
- this._removeNodeEl(node);
- }, this));
- // initialize new state and render changes
- this._setInitialStates({nodes: this._tree}, 0)
- .done(this._render.bind(this));
- };
- /**
- Updates / replaces a given tree node
- @param {Object} node - A single node to be replaced
- @param {Object} newNode - THe replacement node
- @param {optional Object} options
- */
- Tree.prototype.updateNode = function (node, newNode, options) {
- if (node instanceof Array) {
- node = node[0];
- }
- options = $.extend({}, _default.options, options);
- // insert new node
- $.extend(node,newNode);
- // remove old node from DOM
- //this._removeNodeEl(node);
- // initialize new state and render changes
- this._setInitialStates({nodes: this._tree}, 0)
- .done(this._render.bind(this));
- };
- /**
- Selects given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.selectNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setSelected(node, true, options);
- }, this));
- };
- /**
- Unselects given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.unselectNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setSelected(node, false, options);
- }, this));
- };
- /**
- Toggles a node selected state; selecting if unselected, unselecting if selected.
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.toggleNodeSelected = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._toggleSelected(node, options);
- }, this));
- };
- /**
- Collapse all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.collapseAll = function (options) {
- options = $.extend({}, _default.options, options);
- options.levels = options.levels || 999;
- this.collapseNode(this._tree, options);
- };
- /**
- Collapse a given tree node
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.collapseNode = function (nodes, options) {
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setExpanded(node, false, options);
- }, this));
- };
- /**
- Expand all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.expandAll = function (options) {
- options = $.extend({}, _default.options, options);
- options.levels = options.levels || 999;
- this.expandNode(this._tree, options);
- };
- /**
- Expand given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.expandNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- // Do not re-expand already expanded nodes
- if (node.state.expanded) return;
- if (typeof(this._options.lazyLoad) === 'function' && node.lazyLoad) {
- this._lazyLoad(node);
- }
- this._setExpanded(node, true, options);
- if (node.nodes) {
- this._expandLevels(node.nodes, options.levels-1, options);
- }
- }, this));
- };
- Tree.prototype._expandLevels = function (nodes, level, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setExpanded(node, (level > 0) ? true : false, options);
- if (node.nodes) {
- this._expandLevels(node.nodes, level-1, options);
- }
- }, this));
- };
- /**
- Reveals given tree nodes, expanding the tree from node to root.
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.revealNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- var parentNode = node;
- var tmpNode;
- while (tmpNode = this.getParents([parentNode])[0]) {
- parentNode = tmpNode;
- this._setExpanded(parentNode, true, options);
- };
- }, this));
- };
- /**
- Toggles a node's expanded state; collapsing if expanded, expanding if collapsed.
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.toggleNodeExpanded = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._toggleExpanded(node, options);
- }, this));
- };
- /**
- Check all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.checkAll = function (options) {
- options = $.extend({}, _default.options, options);
- var nodes = $.grep(this._orderedNodes, function (node) {
- return !node.state.checked;
- });
- $.each(nodes, $.proxy(function (index, node) {
- this._setChecked(node, true, options);
- }, this));
- };
- /**
- Checks given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.checkNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setChecked(node, true, options);
- }, this));
- };
- /**
- Uncheck all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.uncheckAll = function (options) {
- options = $.extend({}, _default.options, options);
- var nodes = $.grep(this._orderedNodes, function (node) {
- return node.state.checked || node.state.checked === undefined;
- });
- $.each(nodes, $.proxy(function (index, node) {
- this._setChecked(node, false, options);
- }, this));
- };
- /**
- Uncheck given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.uncheckNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setChecked(node, false, options);
- }, this));
- };
- /**
- Toggles a node's checked state; checking if unchecked, unchecking if checked.
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.toggleNodeChecked = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._toggleChecked(node, options);
- }, this));
- };
- /**
- Saves the current state of checkboxes as default, cleaning up any highlighted changes
- */
- Tree.prototype.unmarkCheckboxChanges = function () {
- this._inheritCheckboxChanges();
- $.each(this._nodes, function (index, node) {
- node.$el.removeClass('node-check-changed');
- });
- };
- /**
- Disable all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.disableAll = function (options) {
- options = $.extend({}, _default.options, options);
- var nodes = this._findNodes('false', 'state.disabled');
- $.each(nodes, $.proxy(function (index, node) {
- this._setDisabled(node, true, options);
- }, this));
- };
- /**
- Disable given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.disableNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setDisabled(node, true, options);
- }, this));
- };
- /**
- Enable all tree nodes
- @param {optional Object} options
- */
- Tree.prototype.enableAll = function (options) {
- options = $.extend({}, _default.options, options);
- var nodes = this._findNodes('true', 'state.disabled');
- $.each(nodes, $.proxy(function (index, node) {
- this._setDisabled(node, false, options);
- }, this));
- };
- /**
- Enable given tree nodes
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.enableNode = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setDisabled(node, false, options);
- }, this));
- };
- /**
- Toggles a node's disabled state; disabling is enabled, enabling if disabled.
- @param {Array} nodes - An array of nodes
- @param {optional Object} options
- */
- Tree.prototype.toggleNodeDisabled = function (nodes, options) {
- if (!(nodes instanceof Array)) {
- nodes = [nodes];
- }
- options = $.extend({}, _default.options, options);
- $.each(nodes, $.proxy(function (index, node) {
- this._setDisabled(node, !node.state.disabled, options);
- }, this));
- };
- /**
- 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);
- var previous = this._getSearchResults();
- var results = [];
- if (pattern && pattern.length > 0) {
- if (options.exactMatch) {
- pattern = '^' + pattern + '$';
- }
- var modifier = 'g';
- if (options.ignoreCase) {
- modifier += 'i';
- }
- results = this._findNodes(pattern, 'text', modifier);
- }
- // Clear previous results no longer matched
- $.each(this._diffArray(results, previous), $.proxy(function (index, node) {
- this._setSearchResult(node, false, options);
- }, this));
- // Set new results
- $.each(this._diffArray(previous, results), $.proxy(function (index, node) {
- this._setSearchResult(node, true, options);
- }, this));
- // Reveal hidden nodes
- if (results && options.revealResults) {
- this.revealNode(results);
- }
- this._triggerEvent('searchComplete', results, options);
- return results;
- };
- /**
- Clears previous search results
- */
- Tree.prototype.clearSearch = function (options) {
- options = $.extend({}, { render: true }, options);
- var results = $.each(this._getSearchResults(), $.proxy(function (index, node) {
- this._setSearchResult(node, false, options);
- }, this));
- this._triggerEvent('searchCleared', results, options);
- };
- Tree.prototype._getSearchResults = function () {
- return this._findNodes('true', 'searchResult');
- };
- Tree.prototype._diffArray = function (a, b) {
- var diff = [];
- $.grep(b, function (n) {
- if ($.inArray(n, a) === -1) {
- diff.push(n);
- }
- });
- return diff;
- };
- /**
- Find nodes that match a given criteria
- @param {String} pattern - A given string to match against
- @param {optional String} attribute - Attribute to compare pattern against
- @param {optional String} modifier - Valid RegEx modifiers
- @return {Array} nodes - Nodes that match your criteria
- */
- Tree.prototype._findNodes = function (pattern, attribute, modifier) {
- attribute = attribute || 'text';
- modifier = modifier || 'g';
- return $.grep(this._orderedNodes, $.proxy(function (node) {
- var val = this._getNodeValue(node, attribute);
- if (typeof val === 'string') {
- return val.match(new RegExp(pattern, modifier));
- }
- }, this));
- };
- /**
- 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) && obj[attr] !== undefined) {
- return obj[attr].toString();
- }
- else {
- return undefined;
- }
- }
- };
-
- /**
- des:扩展bootstrap-treeview的编辑节点方法
- 编辑节点
- author:qlx 2017-3-31
- */
- Tree.prototype.editNode = function (identifiers, options) {
- $.each(identifiers,$.proxy(function (i, node) {
- this.setEditNode(node, options);
- }, this));
- this._setInitialStates({ nodes: this._tree }, 0);
- this._render();
- }
- /**
- * 编辑节点
- */
- Tree.prototype.setEditNode = function (node, options) {
- if (options) {
- $.extend(node, options);
- };
- };
- 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;
- if (this.length == 0) {
- throw "No element has been found!";
- }
- 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);
|