/* ========================================================= * 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(' ') : []; //修改 后加 || (classlist.indexof('list-group-item') !== -1 ) if ((classlist.indexof('expand-icon') !== -1) || (classlist.indexof('list-group-item') !== -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)); // document.getelementbyid("0").innerhtml = '666'; //后加 修改 } } 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)); } } // document.getelementbyid("0").innerhtml = '666';//后加 修改 }; 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); document.getelementbyid("0").innerhtml = '所有分类'; }; // 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 aaaac = node.href; 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 )//修改 node.nodeid node.id .attr('id', node.id)//后加 修改 .attr('href', node.href)//后加 修改 .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)) { $('').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: '