|
|
@@ -0,0 +1,275 @@
|
|
|
++function ($) {
|
|
|
+ 'use strict';
|
|
|
+
|
|
|
+ var Node = function (row) {
|
|
|
+ var template = /treetable-([A-Za-z0-9_-]+)/;
|
|
|
+ var parentTemplate = /treetable-parent-([A-Za-z0-9_-]+)/;
|
|
|
+ this.row = row;
|
|
|
+ this.id = null;
|
|
|
+ if (template.test(this.row.attr('data-node'))) {
|
|
|
+ this.id = template.exec(this.row.attr('data-node'))[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ this.parentId = null;
|
|
|
+ if (parentTemplate.test(this.row.attr('data-pnode'))) {
|
|
|
+ this.parentId = parentTemplate.exec(this.row.attr('data-pnode'))[1];
|
|
|
+ }
|
|
|
+ this.children = [];
|
|
|
+ this.status = true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ Node.prototype.addChildren = function(treeContainer) {
|
|
|
+ var self = this;
|
|
|
+ var templateData = "treetable-parent-" + self.id;
|
|
|
+ var $children = treeContainer.find('[data-pnode="' + templateData + '"]');
|
|
|
+ if ($children.length != 0) {
|
|
|
+ $.each($children, function(index, child) {
|
|
|
+ var childNode = new Node($(child));
|
|
|
+ self.children.push(childNode)
|
|
|
+ childNode.addChildren(treeContainer);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.initIndent = function(treeFy) {
|
|
|
+ this.row.find('.treetable-indent').remove();
|
|
|
+ var expander = this.row.find('.treetable-expander');
|
|
|
+ var depth = this.getDepth(treeFy.$table);
|
|
|
+ for (var i = 0; i < depth; i++) {
|
|
|
+ var indentTemplate = treeFy.options.indentTemplate;
|
|
|
+ $(indentTemplate).insertBefore(expander);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.initExpander = function(treeFy) {
|
|
|
+ var self = this;
|
|
|
+ var element = self.row.find('td').get(treeFy.options.treeColumn);
|
|
|
+ var $expander = self.row.find('.treetable-expander');
|
|
|
+ if ($expander) {
|
|
|
+ $expander.remove();
|
|
|
+ }
|
|
|
+ var expanderTemplate = treeFy.options.expanderTemplate;
|
|
|
+ $(expanderTemplate).prependTo(element).click(function(event) {
|
|
|
+ //self.toggle($(this).closest('tr'));
|
|
|
+ self.toggle();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.renderExpand = function(treeFy) {
|
|
|
+ var $expander = this.row.find('.treetable-expander');
|
|
|
+ if ($expander) {
|
|
|
+ if (!this.row.hasClass('treetable-collapsed')) {
|
|
|
+ $expander.removeClass(treeFy.options.expanderCollapsedClass);
|
|
|
+ $expander.addClass(treeFy.options.expanderExpandedClass);
|
|
|
+ } else {
|
|
|
+ $expander.removeClass(treeFy.options.expanderExpandedClass);
|
|
|
+ $expander.addClass(treeFy.options.expanderCollapsedClass);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.initExpander(treeFy);
|
|
|
+ this.renderExpand(treeFy);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.toggle = function() {
|
|
|
+ if (this.row.hasClass('treetable-expanded')) {
|
|
|
+ if (!this.isLeaf() && !this.row.hasClass('treetable-collapsed')) {
|
|
|
+ this.row.removeClass('treetable-expanded');
|
|
|
+ this.row.addClass('treetable-collapsed');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!this.isLeaf() && !this.row.hasClass('treetable-expanded')) {
|
|
|
+ this.row.removeClass('treetable-collapsed');
|
|
|
+ this.row.addClass('treetable-expanded');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /* 是否为叶子节点 */
|
|
|
+ Node.prototype.isLeaf = function() {
|
|
|
+ return this.children.length === 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.isCollapsed = function(treeContainer) {
|
|
|
+ var isRoot = (this.getDepth(treeContainer) === 0);
|
|
|
+ if (isRoot) {
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ if (this.getParentNode(treeContainer).row.hasClass('treetable-collapsed')) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return this.getParentNode(treeContainer).isCollapsed(treeContainer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.getDepth = function(treeContainer) {
|
|
|
+ if (this.getParentNode(treeContainer) === null) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return this.getParentNode(treeContainer).getDepth(treeContainer) + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.getParentNode = function(treeContainer) {
|
|
|
+ if (this.parentId === null) {
|
|
|
+ return null;
|
|
|
+ } else {
|
|
|
+ return this.getNodeById(this.parentId, treeContainer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node.prototype.getNodeById = function(id, treeContainer) {
|
|
|
+ var templateData = "treetable-" + id;
|
|
|
+ var $row = treeContainer.find('[data-node="' + templateData + '"]');
|
|
|
+ var node = new Node($row);
|
|
|
+ node.addChildren(treeContainer);
|
|
|
+ return node;
|
|
|
+ }
|
|
|
+
|
|
|
+ var TreeFy = function (element, options) {
|
|
|
+
|
|
|
+ this.options = options;
|
|
|
+
|
|
|
+ this.$table = $(element);
|
|
|
+
|
|
|
+ var allNodes = this.getAllNodes();
|
|
|
+ this.initTree(allNodes);
|
|
|
+ return TreeFy;
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeFy.VERSION = '0.0.1'
|
|
|
+
|
|
|
+ TreeFy.prototype.getAllNodes = function() {
|
|
|
+ var self = this;
|
|
|
+ var result = $.grep(self.$table.find('tr'), function(trElement) {
|
|
|
+ var nodeData = $(trElement).attr('data-node');
|
|
|
+ var template = /treetable-([A-Za-z0-9_-]+)/;
|
|
|
+ return template.test(nodeData);
|
|
|
+ });
|
|
|
+ var $allNodes = $(result);
|
|
|
+ var allNodes = [];
|
|
|
+ $.each($allNodes, function() {
|
|
|
+ var node = new Node($(this));
|
|
|
+ node.addChildren(self.$table);
|
|
|
+ allNodes.push(node);
|
|
|
+ });
|
|
|
+ return allNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeFy.prototype.initTree = function(allNodes) {
|
|
|
+ var self = this;
|
|
|
+ var rootNodes = [];
|
|
|
+ $.each(allNodes, function() {
|
|
|
+ var noChildren = this.children.length === 0;
|
|
|
+ if (!noChildren) {
|
|
|
+ this.row.addClass(self.options.initStatusClass);
|
|
|
+ }
|
|
|
+ if (!this.parentId) {
|
|
|
+ rootNodes.push(this);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ self.initNode(rootNodes);
|
|
|
+ self.render(rootNodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeFy.prototype.initNode = function(nodes) {
|
|
|
+ var self = this;
|
|
|
+ $.each(nodes, function() {
|
|
|
+ self.initNode(this.children);
|
|
|
+ this.initExpander(self);
|
|
|
+ this.initIndent(self);
|
|
|
+ var $row = this.row;
|
|
|
+ var click_nodes = [];
|
|
|
+ click_nodes.push(this);
|
|
|
+ $row.find('.treetable-expander').on("click", function(event) {
|
|
|
+ event.stopPropagation();
|
|
|
+ self.render(click_nodes);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeFy.prototype.render = function(nodes) {
|
|
|
+ var self = this;
|
|
|
+ $.each(nodes, function(node) {
|
|
|
+ //若父节点折叠, 隐藏子节点
|
|
|
+ if (this.isCollapsed(self.$table)) {
|
|
|
+ this.row.hide();
|
|
|
+ } else {
|
|
|
+ this.row.show();
|
|
|
+ }
|
|
|
+ if (!this.isLeaf()) {
|
|
|
+ this.renderExpand(self);
|
|
|
+ self.render(this.children);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // PLUGIN DEFINITION
|
|
|
+ // =======================
|
|
|
+
|
|
|
+ function Plugin(option) {
|
|
|
+ var args = arguments;
|
|
|
+ var ret;
|
|
|
+ return this.each(function () {
|
|
|
+ var $this = $(this)
|
|
|
+ var data = $this.data('treeFy')
|
|
|
+
|
|
|
+ if (!data) {
|
|
|
+ var options = $.extend(true, {}, $.fn.treeFy.defaults, typeof option == 'object' && option);
|
|
|
+ $this.data('treeFy', (data = new TreeFy(this, options)));
|
|
|
+ }
|
|
|
+ if (typeof option == 'string') data[option].call($this)
|
|
|
+ })
|
|
|
+
|
|
|
+ if (typeof option == 'string') {
|
|
|
+ if (args.length == 1) {
|
|
|
+ var _ret = data[option].call(data);
|
|
|
+ if (typeof _ret != 'undefined') {
|
|
|
+ ret = _ret;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ var _ret = data[option].apply(data, Array.prototype.slice.call(args, 1));
|
|
|
+ if (typeof _ret != 'undefined') {
|
|
|
+ ret = _ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof ret != 'undefined') {
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ var old = $.fn.treeFy
|
|
|
+
|
|
|
+ $.fn.treeFy = Plugin
|
|
|
+ $.fn.treeFy.Constructor = TreeFy
|
|
|
+ $.fn.treeFy.defaults = {
|
|
|
+
|
|
|
+ expanderTemplate: '<span class="treetable-expander"></span>',
|
|
|
+
|
|
|
+ indentTemplate: '<span class="treetable-indent"></span>',
|
|
|
+
|
|
|
+ expanderExpandedClass: 'fa fa-angle-down',
|
|
|
+
|
|
|
+ expanderCollapsedClass: 'fa fa-angle-right',
|
|
|
+
|
|
|
+ treeColumn: 0,
|
|
|
+
|
|
|
+ initStatusClass: 'treetable-expanded'
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // ALERT NO CONFLICT
|
|
|
+ // =================
|
|
|
+
|
|
|
+ $.fn.treeFy.noConflict = function () {
|
|
|
+ $.fn.treeFy = old
|
|
|
+ return this
|
|
|
+ }
|
|
|
+
|
|
|
+}(jQuery);
|