一个简单的树插件

/**
 * 所包括的功能:
 * 1.对标准数据格式及简单数据格式
 * 2.树的外观支持自定义设置(在treeStyle中设置每级树的样式/leafStyle设置叶子的样式/在数据中附加样式信息)
 * 3.支持两种数据载入方式(直接传入数据/设置url加载数据)
 * 4.外部对树的操作(全部展开\全部收缩\刷新)
 * 5.结点的点击事件(beforeClock和onClick)
 * 6.复选框的显示、隐藏、禁用
 * 7.复选框的关联方式(例如:选父-->子全选)
 * 8.得到复选框所选中的结点(getCheckedNodes)
 * 9.选中结点(不是选中复选框,只是添加上选中样式那种),获取选中的结点
 * 10.树的结点的id:给树的地点单独创建id,与data中的id无关。id命名取绑定的dom元素的id,
 * 11.要在每次运用变量前,先判定变量是否存在
 * 12.右键+拖动
 *
 */

(function($){
    var defaults = {
        ajax : {
            enable : false,
            url : "", //请求数据的url
            method : "POST",
            sync : true,
            dataType : "json",
            param : ""
        },
        data : {
            data : null, //传入的数据 (数组类型,支持一次传入多棵树)
            format : "normal", //标准数据格式or简单数据格式
            normal : {
                id : "id",
                name : "name",
                children : "children"
            },
            simple : {
                id : "id",
                pId : "pId",
                level : "level"
            }
        },
        callback : {
            beforeClick : null,
            onClick : null, //点击结点调用此方法
            initTree : null, //树初始化完成调用此方法
            onRClick : {
                addCallback : null,
                editCallback : null,
                delCallback : null
            },
            onDrag : null
        },
        view : { //style的格式为{style : "", expand : "", collapse : ""}
            initState : "expand", //展开or收缩(默认状态为全部展开)
            showLine : true,
            showIcon : true,
            iconStyle : null, //没有设置默认样式
            textStyle : null,
            leafIconStyle : null,
            leafTextStyle : null
        },
        checkbox : {
            enable : true,
            checkboxType : { //默认不存在关联
                Y : "",
                N : ""
            }
        },
        /*root : { //动态加入一个根结点
            enable : false,
            data : null, //此处的key必须与数据的key相同
            rootIconStyle : null,
            rootTextStyle : null
        }*/
        root : null,
        rclick : true, //是否支持右键
        drag : true //是否支持拖动
    };
    function makeVdiTree(dom, opt) {
        var options = $.extend(true, {}, defaults, opt); //此处要进行深度拷贝
        var treeDom = dom;
        var treeId = $(treeDom).attr('id');
        var count = 0; //记录结点的数量,命名结点id
        var menu = null; //设置成全局变量,保证页面上仅会有一个右键菜单
        var normalData = null; //存储转换后的数据,刷新时使用
        var tMethods = {
            _initTree : function(data) {
                var tmpData = (data ? data : options.data.data);
                if ("simple" == options.data.format) {
                    normalData = tMethods._changeData(tmpData);
                    //$(treeDom).append(tMethods._createNodes(normalData));
                } else {
                    normalData = tmpData;
                    //$(treeDom).append(tMethods._createNodes(tmpData));
                }
                if (options.root) {
                    tmpData = tMethods._insertRoot(normalData);
                } else {
                    tmpData = normalData;
                }
                $(treeDom).append(tMethods._createNodes(tmpData));
                if (options.callback.initTree) options.callback.initTree.call(treeDom); //树初始化完成,调用此方法
            },
            _createNodes : function(data) { //生成树的接口,传入标准数据
                var treeul = document.createElement('ul');
                $.each(data, function(idx, node) {
                    var treeli = document.createElement('li');
                    node = tMethods._addProperties(node, 0, "root");
                    treeli = tMethods._initNode(treeli, node);
                    treeli = tMethods._initEvent(treeli, node);
                    if (node[node.key.children]) {
                        var cDom = tMethods._appendNodes(node[node.key.children], 0+1);
                        $(treeli).append(cDom);
                    }
                    $(treeul).append(treeli);
                });
                return treeul;
            },
            _appendNodes : function(children, level) { //生成树的迭代方法,返回一层的结点
                var treeul = document.createElement('ul');
                var length = children.length;
                $.each(children, function(idx, child) {
                    var treeli = document.createElement('li');
                    child = tMethods._addProperties(child, level, (idx == length -1 ? "bottom" : "center"));
                    treeli = tMethods._initNode(treeli, child);
                    treeli = tMethods._initEvent(treeli, child);
                    if (child[child.key.children]) {
                        var cDom = tMethods._appendNodes(child[child.key.children], level+1);
                        $(treeli).append(tMethods._initState(cDom, child));
                    }
                    $(treeul).append(treeli);
                });
                return treeul;
            },
            _addProperties : function(node, level, style) {
                node["key"] = tMethods._getNormalKey();
                node["level"] = level;
                node["style"] = style;
                node["line"] = (options.view.showLine ? true : false);
                node["state"] = (options.view.initState ? options.view.initState : "expand");
                node["leaf"] = (node[node.key.children] ? false : true);
                return node;
            },
            _initNode : function(dom, node) { //初始化单个结点,添加图片、样式等(node结点中有key、level、style、line、state、leaf这几个属性)
                $(dom).attr('id', treeId + "_" + count);
                count ++;
                $(dom).append(tMethods._newLine(node));
                if (options.checkbox.enable) {
                    $(dom).append(tMethods._newCheckBox(dom));
                }
                $(dom).append(tMethods._newClick(node));
                if (!node.leaf) { //在初始化结点的时候标明该结点的状态
                    if ("expand" == node.state) {
                        $(dom).addClass("vdiopen");
                    } else if ("collapse" == node.state) {
                        $(dom).addClass("vdiclose");
                    }
                }
                $(dom).data("node", node); //将数据存储到dom中
                return dom;
            },
            _initEvent : function(dom, node) { //初始化结点上的事件
                var nodeDom = tMethods._getNodeDom(dom);
                //要判断此结点是否是叶子结点
                if (nodeDom.linespan.length > 0 && !node.leaf) { //点击图片展开收缩的功能,叶子结点不存在此功能
                    $(nodeDom.linespan).bind("click", function() {
                        tMethods._changeState(dom, node);
                    });
                }
                if (nodeDom.clicka.length > 0) {
                    $(nodeDom.clicka).bind("click", function() {
                        var callbackflag = true;
                        if (options.callback.beforeClick) callbackflag = options.callback.beforeClick.call(dom, node);
                        if (callbackflag) { //如果beforeClick返回false或什么都不返回,则结点不会被选中且不触发onClick
                            $(treeDom).find('a.selected').removeClass("selected");
                            $(this).addClass("selected");
                            if (options.callback.onClick) options.callback.onClick.call(dom, node);
                        }
                    });
                }
                if (options.rclick) { //右键点击事件
                    tMethods._initRClickEvent(dom, node);
                }
                if (options.drag) {
                    tMethods._initDragEvent(dom, node);
                }
                return dom;
            },
            _initRClickEvent : function(dom, node) {
                var nodeDom = tMethods._getNodeDom(dom);
                $(nodeDom.clicka).bind("contextmenu", function(e) {
                    $(treeDom).find('a.selected').removeClass("selected"); //右键点击也要加上选中样式
                    $(this).addClass("selected");
                    if (menu) tMethods._destoryMenu(menu);
                    menu = tMethods._createMenu(dom, node, e);
                    return false;
                });
                $("body").bind("click", function() {
                    if (menu) tMethods._destoryMenu(menu);
                }).bind("contextmenu", function(){
                    if (menu) tMethods._destoryMenu(menu);
                });
            },
            _initDragEvent : function(dom, node) {
                var $doc = $(document);
                var nodeDom = tMethods._getNodeDom(dom);
                $(nodeDom.clicka).mousedown(function(e) { //鼠标在结点上按按下,触发拖动
                    // 拖动时,将选中的样式清除
                    $(treeDom).find('a.selected').removeClass("selected");
                    // 点击时产生一个临时拖动结点
                    var tmpNode = $(dom).clone();
                    var dragUl = $('<ul class="vdiTree vdiTreeDragUL"></ul>').append(tmpNode);
                    $(dragUl).appendTo('body').hide();
                    $doc.mousemove(function(e) {
                        // 拖动时鼠标状态为小手
                        $("body").css('cursor', 'pointer');
                        // 临时拖动结点跟随鼠标移动
                        var dX = (document.body.scrollLeft == 0) ? document.documentElement.scrollLeft: document.body.scrollLeft;
                        var dY = (document.body.scrollTop == 0) ? document.documentElement.scrollTop: document.body.scrollTop;
                        dragUl.css({
                            "top": (e.clientY + dY + 3) + "px",
                            "left": (e.clientX + dX + 3) + "px"
                        }).show();
                        // 拖动事件发生时调用
                        if (options.callback.onDrag) {
                            options.callback.onDrag.call(dom, node);
                        }
                        $(treeDom).find('a').mouseover(function() {
                            $(treeDom).find('a.targetNode').removeClass("targetNode");
                            $(this).addClass("targetNode");
                            return false;
                        });
                        return false;
                    }).mouseup(function(e) {
                        // 临时拖动结点
                        $(this).unbind("mousemove").unbind("mouseup");
                        $(nodeDom.clicka).unbind("mousemove");
                        $("body").css("cursor", "auto");
                        $(dragUl).remove();
                        // 目标结点
                        $(treeDom).find('a').unbind("mouseover");
                        var clicka = $(treeDom).find('a.targetNode');
                        if (clicka.length > 0) {
                            $(clicka).removeClass("targetNode");
                            var treeli = $(clicka).parent('li');
                            if(tMethods._chkDragAvailable(dom, treeli)) {
                                tMethods._moveNode(dom, treeli); //将结点移动到新位置
                            }
                        }
                        return false;
                    }); 

                    return false; // 阻止拖动事件触发(不会出现禁止拖动的图标了)
                });
            },
            _chkDragAvailable : function(drag, target) {
                // 拖动不起作用的情况:1.将结点拖动到自身或者是自身的子结点上面 2.将结点拖动到自己的父节点上面
                var available = true;
                var targetId = $(target).attr('id');
                if ($(drag).attr('id') == targetId) {
                    available = false;
                }
                $(drag).find('li').each(function(idx, treeli) {
                    if($(treeli).attr('id') == targetId) {
                        available = false;
                        return false;
                    }
                });
                if ($(drag).parent('ul').parent('li').attr('id') == targetId) {
                    available = false;
                }
                return available;
            },
            _initState : function(dom, node) { //初始化状态(展开or收缩)
                //是否添加虚线
                if (node.line && "center" == node.style) {
                    $(dom).addClass("line");
                } else {
                    $(dom).removeClass("line");
                }
                //要判断结点此时是展开的还是收缩的
                if ("expand" == options.view.initState) {
                    $(dom).css("display", "block");
                } else if("collapse" == options.view.initState) {
                    $(dom).css("display", "none");
                }
                return dom;
            },
            _ajaxData : function(){
                $.ajax({
                    type : options.ajax.method,
                    url : options.ajax.url,
                    data : options.ajax.param,
                    async : options.ajax.sync,
                    dataType : options.ajax.dataType,
                    success : function(data) {
                        tMethods._initTree(data);
                    },
                    error : function() {
                    }
                });
            },
            _changeData : function(simpleData) { //传入简单数据,返回标准数据
                var skey = tMethods._getSimpleKey();
                var levelObj = {};
                var maxLevel = 0;
                //第一步:先按level给对象分类
                $.each(simpleData, function(idx, data) {
                    if (levelObj[data.level]) { //如果存在,则将此对象放入改数组中
                        levelObj[data.level].push(data);
                    } else { //如果不存在,则新建一个数组,将此对象放入改数组中,并将该数组放入大对象中
                        var levelArr = [];
                        levelArr.push(data);
                        levelObj[data.level] = levelArr;
                    }
                    if (data.level > maxLevel) { //记录最大level值
                        maxLevel = data.level;
                    }
                });
                //第二步:将子结点按照映射插入到父节点中
                for (var i = maxLevel; i > 0; i --) {
                    $.each(levelObj[i], function(idx, child) {
                        var pId = child[skey.pId];
                        $.each(levelObj[i-1], function(idx, parent) {
                            var id = parent[skey.id];
                            if(pId == id){
                                if (parent.children) { //parent中存在children这个属性
                                    parent.children.push(child);
                                } else { //不存在
                                    var childrenArr = [];
                                    childrenArr.push(child);
                                    parent.children = childrenArr;
                                }
                                return false;
                            }
                        });
                    });
                }
                return levelObj[0];
            },
            _getNormalKey : function() { //获取数据的key,肯定有值返回,不用再进行判断
                var key = {};
                key.id = (options.data.normal.id ? options.data.normal.id : "id");
                key.name = (options.data.normal.name ? options.data.normal.name : "name");
                key.children = (options.data.normal.children ? options.data.normal.children : "children");
                return key;
            },
            _getSimpleKey : function() {
                var key = {};
                key.id = (options.data.simple.id ? options.data.simple.id : "id");
                key.pId = (options.data.simple.pId ? options.data.simple.pId : "pId");
                key.level = (options.data.simple.level ? options.data.simple.level : "level");
                return key;
            },
            _getLineStyle : function(line) {
                var lineStyle = {};
                if(line){ //存在虚线
                    lineStyle.open = {
                        center : "center_open",
                        root : "root_open",
                        bottom : "bottom_open"
                    };
                    lineStyle.close = {
                        center : "center_close",
                        root : "root_close",
                        bottom : "bottom_close"
                    };
                    lineStyle.leaf = {
                        center : "center_docu",
                        bottom : "bottom_docu"
                    };
                }else{ //不存在虚线
                    lineStyle.open = "noline_open";
                    lineStyle.close = "noline_close";
                    lineStyle.leaf = "noline_docu";
                }
                return lineStyle;
            },
            _getIconStyle : function(level) {
                var iconStyle = {};
                if (options.view.iconStyle) {
                    iconStyle.style = ("string" == typeof options.view.iconStyle[level].style ? tMethods._strToArr(options.view.iconStyle[level].style) : []);
                    iconStyle.expand = ("string" == typeof options.view.iconStyle[level].expand ? tMethods._strToArr(options.view.iconStyle[level].expand) : []);
                    iconStyle.collapse = ("string" == typeof options.view.iconStyle[level].collapse ? tMethods._strToArr(options.view.iconStyle[level].collapse) : []);
                }
                return iconStyle;
            },
            _getTextStyle : function() {
                var textStyle = {};
                if (options.view.textStyle) {
                    textStyle.style = ("string" == typeof options.view.textStyle[level].style ? tMethods._strToArr(options.view.textStyle[level].style) : []);
                    textStyle.expand = ("string" == typeof options.view.textStyle[level].expand ? tMethods._strToArr(options.view.textStyle[level].expand) : []);
                    textStyle.collapse = ("string" == typeof options.view.textStyle[level].collapse ? tMethods._strToArr(options.view.textStyle[level].collapse) : []);
                }
                return textStyle;
            },
            _getLeafIconStyle : function() {
                var leafIconStyle = {};
                if (options.view.leafIconStyle) {
                    leafIconStyle.style = ("string" == typeof options.view.leafIconStyle.style ? tMethods._strToArr(options.view.leafIconStyle.style) : []);
                    leafIconStyle.leaf = ("string" == typeof options.view.leafIconStyle.leaf ? tMethods._strToArr(options.view.leafIconStyle.leaf) : []);
                }
                return leafIconStyle;
            },
            _getLeafTextStyle : function() {
                var leafTextStyle = {};
                if (options.view.leafTextStyle) {
                    leafTextStyle.style = ("string" == typeof options.view.leafTextStyle.style ? tMethods._strToArr(options.view.leafTextStyle.style) : []);
                    leafTextStyle.leaf = ("string" == typeof options.view.leafTextStyle.leaf ? tMethods._strToArr(options.view.leafTextStyle.leaf) : []);
                }
                return leafTextStyle;
            },
            _newLine : function(node) {
                var linespan = document.createElement('span'); //显示虚线
                var lineStyle = tMethods._getLineStyle(node.line);
                $(linespan).addClass("button").addClass("switch");
                if (node.leaf) {
                    $(linespan).addClass(lineStyle.leaf[node.style]);
                } else {
                    if ("expand" == node.state) {
                        $(linespan).addClass(lineStyle.open[node.style]);
                    } else if ("collapse" == node.state) {
                        $(linespan).addClass(lineStyle.close[node.style]);
                    }
                }
                return linespan;
            },
            _newIcon : function(node) {
                var iconspan = document.createElement('span'); //显示图片
                if (!node.leaf) {
                    var iconStyle = tMethods._getIconStyle(node.level);
                    if (iconStyle.style) {
                        $.each(iconStyle.style, function(idx, clss) {
                            $(iconspan).addClass(clss);
                        });
                    }
                    if (iconStyle[node.state]) {
                        $.each(iconStyle[node.state], function(idx, clss) {
                            $(iconspan).addClass(clss);
                        });
                    }
                }else{
                    var leafIconStyle = tMethods._getLeafIconStyle();
                    if (leafIconStyle.style) {
                        $.each(leafIconStyle.style, function(idx, clss) {
                            $(iconspan).addClass(clss);
                        });
                    }
                    if (leafIconStyle.leaf) {
                        $.each(leafIconStyle.leaf, function(idx, clss) {
                            $(iconspan).addClass(clss);
                        });
                    }
                }
                return iconspan;
            },
            _newText : function(node) {
                var textspan = document.createElement('span'); //显示文字
                $(textspan).html(node[node.key.name]);
                if (!node.leaf) {
                    var textStyle = tMethods._getTextStyle(node.level);
                    if (textStyle.style) {
                        $.each(textStyle.style, function(idx, clss) {
                            $(textspan).addClass(clss);
                        });
                    }
                    if (textStyle[node.state]) {
                        $.each(textStyle[node.state], function(idx, clss) {
                            $(textspan).addClass(clss);
                        });
                    }
                }else{
                    var leafTextStyle = tMethods._getLeafTextStyle();
                    if (leafTextStyle.style) {
                        $.each(leafTextStyle.style, function(idx, clss) {
                            $(textspan).addClass(clss);
                        });
                    }
                    if (leafTextStyle.leaf) {
                        $.each(leafTextStyle.leaf, function(idx, clss) {
                            $(textspan).addClass(clss);
                        });
                    }
                }
                return textspan;
            },
            _newCheckBox : function(dom) {
                var checkboxspan = document.createElement("span");
                $(checkboxspan).addClass("button").addClass("chk");
                checkboxspan = tMethods._bindCheckBoxEvent(dom, checkboxspan);
                return checkboxspan;
            },
            _newClick : function(node) {
                var clicka = document.createElement("a");
                clicka.setAttribute('href', "#");
                if (options.view.showIcon) {
                    $(clicka).append(tMethods._newIcon(node));
                }
                $(clicka).append(tMethods._newText(node));
                return clicka;
            },
            _bindCheckBoxEvent : function(dom, checkbox) {
                var selectflag;
                $(checkbox).bind("click", function() { //最基本的选中
                    if ($(this).hasClass("checkbox_true_full")) {
                        $(this).removeClass("checkbox_true_full");
                        selectflag = false;
                    } else {
                        $(this).addClass("checkbox_true_full");
                        selectflag = true;
                    }
                    if (!selectflag && options.checkbox.checkboxType.N.indexOf("s") > -1) {
                        tMethods._setChildCheckBox(dom);
                    }
                    if (!selectflag && options.checkbox.checkboxType.N.indexOf("p") > -1) {
                        tMethods._setParentCheckBox(dom);
                    }
                    if (selectflag && options.checkbox.checkboxType.Y.indexOf("s") > -1) {
                        tMethods._setChildCheckBox(dom);
                    }
                    if (selectflag && options.checkbox.checkboxType.Y.indexOf("p") > -1){
                        tMethods._setParentCheckBox(dom);
                    }
                });
                return checkbox;
            },
            _setParentCheckBox : function(dom) { //关联父
                if ($(dom).data("node").level > 0) {
                    var parentflag = false;
                    $(dom).siblings('li').add(dom).each(function(idx, brother) {
                        if ($(brother).children('span:eq(1)').hasClass("checkbox_true_full")) {
                            parentflag = true;
                            return false;
                        }
                    });
                    var parent = $(dom).parents('li:first');
                    var chkbox = parent.children('span:eq(1)');
                    parentflag ? $(chkbox).addClass("checkbox_true_full") : $(chkbox).removeClass("checkbox_true_full");
                    tMethods._setParentCheckBox(parent);
                }
            },
            _setChildCheckBox : function(dom) { //关联子
                if (!$(dom).data("node").leaf) {
                    var chkbox = $(dom).children('span:eq(1)');
                    $(dom).children('ul:first').find('span.chk').each(function(idx, child) {
                        if ($(chkbox).hasClass("checkbox_true_full")) {
                            $(child).addClass("checkbox_true_full");
                        } else {
                            $(child).removeClass("checkbox_true_full");
                        }
                    });
                }
            },
            _strToArr : function(str) { //工具方法,将字符串转化成数组
                return str.split(",");
            },
            _getNodeDom : function(dom) {
                return {
                    linespan : $(dom).children('span:first'),
                    clicka : $(dom).children('a:first'),
                    iconspan : $(dom).children('a:first').children('span:first'),
                    textspan : $(dom).children('a:first').children('span:eq(1)')
                };
            },
            _expandNode : function(dom, node) {
                var lineStyle = tMethods._getLineStyle(node.line);
                var iconStyle = tMethods._getIconStyle(node.level);
                var textStyle = tMethods._getTextStyle(node.level);
                var nodeDom = tMethods._getNodeDom(dom);
                $(nodeDom.linespan).removeClass(lineStyle.close[node.style]).addClass(lineStyle.open[node.style]);
                if (iconStyle.style) {
                    $.each(iconStyle.collapse, function(idx, clss) {
                        $(nodeDom.iconspan).removeClass(clss);
                    });
                    $.each(iconStyle.expand, function(idx, clss) {
                        $(nodeDom.iconspan).addClass(clss);
                    });
                }
                if (textStyle.style) {
                    $.each(textStyle.collapse, function(idx, clss) {
                        $(nodeDom.textspan).removeClass(clss);
                    });
                    $.each(textStyle.expand, function(idx, clss) {
                        $(nodeDom.textspan).addClass(clss);
                    });
                }
                var child = $(dom).children('ul:first');
                if (child.length > 0) {
                    $(child).slideToggle("normal");
                }
            },
            _collapseNode : function(dom, node) {
                var lineStyle = tMethods._getLineStyle(node.line);
                var iconStyle = tMethods._getIconStyle(node.level);
                var textStyle = tMethods._getTextStyle(node.level);
                var nodeDom = tMethods._getNodeDom(dom);
                $(nodeDom.linespan).removeClass(lineStyle.open[node.style]).addClass(lineStyle.close[node.style]);
                if (iconStyle.style) {
                    $.each(iconStyle.expand, function(idx, clss) {
                        $(nodeDom.iconspan).removeClass(clss);
                    });
                    $.each(iconStyle.collapse, function(idx, clss) {
                        $(nodeDom.iconspan).addClass(clss);
                    });
                }
                if (textStyle.style) {
                    $.each(textStyle.expand, function(idx, clss) {
                        $(nodeDom.textspan).removeClass(clss);
                    });
                    $.each(textStyle.collapse, function(idx, clss) {
                        $(nodeDom.textspan).addClass(clss);
                    });
                }
                var child = $(dom).children('ul:first');
                if (child.length > 0) {
                    $(child).slideToggle("normal");
                }
            },
            _changeState : function(dom, node) { //改变结点的状态(展开or收缩)
                if ($(dom).hasClass("vdiclose")) {
                    $(dom).removeClass("vdiclose").addClass("vdiopen");
                    tMethods._expandNode(dom, node);
                } else if ($(dom).hasClass("vdiopen")) {
                    $(dom).removeClass("vdiopen").addClass("vdiclose");
                    tMethods._collapseNode(dom, node);
                }
            },
            _removeTree : function() { //将树清空
                $(treeDom).empty();
            },
            /*v0.2:鼠标右键操纵树结点的增删改 */
            _getSettings : function() { //对右键弹出菜单进行设置
                var operate = [
                    {
                        text : "add",
                        callback : function(node) {
                            tMethods._addNode(this, node);
                        }
                    },{
                        text : "delete",
                        callback : function(node) {
                            tMethods._deleteNode(this, node);
                        }
                    },{
                        text : "edit",
                        callback : function(node) {
                            tMethods._editNode(this, node);
                        }
                    }
                ];
                return operate;
            },
            _createMenu : function(dom, node, event) { //创建右键弹出菜单
                var operate = tMethods._getSettings();
                var menudiv = document.createElement('div');
                $(menudiv).addClass("menu");
                var menuul = document.createElement('ul');
                $.each(operate, function(idx, op) {
                    var menuli = document.createElement('li');
                    menuli = tMethods._initOption(menuli, op, dom, node);
                    $(menuul).append(menuli);
                });
                $(menudiv).append(menuul).css({
                    top : event.pageY,
                    left : event.pageX
                });
                $('body').append(menudiv);
                return menudiv;
            },
            _destoryMenu : function(menu) {
                $(menu).remove();
                menu = null;
            },
            _initOption : function(option, operate, dom, node) { //初始化每个菜单选项
                $(option).html(operate.text).bind("click", function() {
                    if (operate.callback) {
                        operate.callback.call(dom, node);
                    }
                });
                return option;
            },
            _addNode : function(dom, node) { //增加结点操作
                var callbackflag = true;
                // 判断被插入结点是否为父结点(1.是,不变;2,不是,则变为父结点,并插入ul)
                if (node.leaf) {
                    dom = tMethods._leafToParent(dom);
                }
                // 插入叶子结点
                var leaf = tMethods._insertLeaf(dom, node);
                // 如果叶子结点有兄弟结点,则紧邻的兄弟结点要改变样式
                var lastLeaf = $(leaf).prev('li');
                if (lastLeaf.length > 0) {
                    tMethods._bottomToCenter(lastLeaf);
                }
                // 创建input框(传入的是父结点的node)
                if (options.callback.onRClick.addCallback) callbackflag = options.callback.onRClick.addCallback.call(leaf, node);
                if (callbackflag) { //如果addCallback返回false或什么都不返回,则不会触发以下操作
                    tMethods._createInput(leaf);
                }
            },
            _deleteNode : function(dom, node) { //删除结点操作:1.子结点会一并删除;2.父节点会产生变化
                var brothers = $(dom).siblings('li');
                var parent = $(dom).parent('ul').parent('li');
                var lastLeaf = $(dom).prev('li');
                $(dom).remove(); //删除该结点
                if (brothers.length == 0 && parent.length > 0) { // 删除的结点没有兄弟结点,则将父节点改为叶子结点
                    tMethods._parentToLeaf(parent);
                    $(dom).parent('ul').remove();
                }
                if(lastLeaf.length > 0) { //删除的结点有前一个结点,将前一个结点的样式由center改为bottom
                    tMethods._centerToBottom(lastLeaf);
                }
                if(options.callback.onRClick.delCallback) options.callback.onRClick.delCallback.call(dom, node);
            },
            _editNode : function(dom, node) { //编辑结点操作, 优先调用
                var callbackflag = true;
                if (options.callback.onRClick.editCallback) callbackflag = options.callback.onRClick.editCallback.call(dom, node);
                if (callbackflag) { //如果editCallback返回false或什么都不返回,则不会触发以下操作
                    tMethods._createInput(dom, node);
                }
            },
            _moveNode : function(drag, target) {
                var brothers = $(drag).siblings('li');
                var parent = $(drag).parent('ul').parent('li');
                var lastLeaf = $(drag).prev('li');
                // 拖动结点的style=bottom
                drag = tMethods._centerToBottom(drag);
                var targetNode = $(target).data("node");
                if (targetNode.leaf) {
                    target = tMethods._leafToParent(target);
                } else { // 目标结点如果有子结点,则最后一个子结点style=center
                    tMethods._bottomToCenter($(target).children('ul:first').children('li:last'));
                }
                $(target).find('ul:first').append(drag);
                if (brothers.length == 0 && parent.length > 0) {
                    tMethods._parentToLeaf(parent);
                    $(dom).parent('ul').remove();
                }
                if (lastLeaf.length > 0) {
                    tMethods._centerToBottom(lastLeaf);
                }
            },
            _createInput : function(dom, node) { //在给定的dom中插入一个input框,并将原有内容写入input框,脱离焦点时候则删除input框,并将内容写会dom(可以再addNode和editNode上复用该方法)
                if (!node) node = $(dom).data("node");
                var input = document.createElement('input');
                var nodeDom = tMethods._getNodeDom(dom);
                $(input).val(nodeDom.textspan.html());
                $(nodeDom.textspan).html(input);
                $(input).select().bind("blur", function() {
                    $(nodeDom.textspan).html($(this).val());
                    node[node.key.name] = $(this).val();
                    $(dom).data("node", node);
                    $(this).remove();
                });
            },
            _insertLeaf : function(dom, node) { //addNode时,插入一个叶子结点
                if(!node) node = $(dom).data("node");
                var leafNode = tMethods._addProperties({}, node.level + 1, "bottom");
                var treeli = tMethods._initEvent(tMethods._initNode(document.createElement('li'), leafNode), leafNode);
                $(dom).find('ul:first').append(treeli);
                return treeli;
            },
            _leafToParent : function(dom, node) { //addNode时,叶子结点变为父结点
                if(!node) node = $(dom).data("node");
                //将现有结点删除,换成新的结点
                node["state"] = "expand";
                node["leaf"] = false;
                var treeli = tMethods._initEvent(tMethods._initNode(document.createElement('li'), node), node);
                $(treeli).attr("id", $(dom).attr("id")).replaceAll(dom);
                // 创建一个ul
                var treeul = document.createElement('ul');
                $(treeli).append(tMethods._initState(treeul, node));
                return treeli;
            },
            _parentToLeaf : function(dom, node) { //deleteNode时,父结点变为叶子结点
                if(!node) node = $(dom).data("node");
                node["leaf"] = true;
                var treeli = tMethods._initEvent(tMethods._initNode(document.createElement('li'), node), node);
                $(treeli).attr("id", $(dom).attr("id")).replaceAll(dom);
                return treeli;
            },
            _bottomToCenter : function(dom, node) { //结点的位置有最底部改为中间
                if (!node) node = $(dom).data("node");
                node["style"] = "center";
                var treeli = tMethods._initEvent(tMethods._initNode(document.createElement('li'), node), node);
                var children = $(dom).children('ul');
                if (children.length > 0) {
                    $(treeli).append(tMethods._initState(children, node));
                }
                $(treeli).attr("id", $(dom).attr("id")).replaceAll(dom);
                return treeli;
            },
            _centerToBottom : function(dom, node) {
                if(!node) node = $(dom).data("node");
                node["style"] = "bottom";
                var treeli = tMethods._initEvent(tMethods._initNode(document.createElement('li'), node), node);
                var children = $(dom).children('ul');
                if (children.length > 0) {
                    $(treeli).append(tMethods._initState(children, node));
                }
                $(treeli).attr("id", $(dom).attr("id")).replaceAll(dom);
                return treeli;
            },
            _refreshOperate : function() { //在结点增删改之后的刷新,只操作存储数据
            },
            //一下方法为动态加载根结点的方法
            /*_getRootIconStyle : function() {
                var rootIconStyle = {};
                if( options.root.rootIconStyle) {
                    rootIconStyle.style = ("string" == typeof options.root.rootIconStyle.style ? tMethods._strToArr(options.root.rootIconStyle.style) : []);
                    rootIconStyle.expand = ("string" == typeof options.root.rootIconStyle.expand ? tMethods._strToArr(options.root.rootIconStyle.expand) : []);
                    rootIconStyle.collapse = ("string" == typeof options.root.rootIconStyle.collapse ? tMethods._strToArr(options.root.rootIconStyle.collapse) : []);
                }
                return rootIconStyle;
            },
            _getRootTextStyle : function() {
                var rootTextStyle = {};
                if (options.root.rootTextStyle) {
                    rootTextStyle.style = ("string" == typeof options.root.rootTextStyle.style ? tMethods._strToArr(options.root.rootTextStyle.style) : []);
                    rootTextStyle.expand = ("string" == typeof options.root.rootTextStyle.expand ? tMethods._strToArr(options.root.rootTextStyle.expand) : []);
                    rootTextStyle.collapse = ("string" == typeof options.root.rootTextStyle.collapse ? tMethods._strToArr(options.root.rootTextStyle.collapse) : []);
                }
                return rootTextStyle;
            },*/
            _insertRoot : function(data) { //为整棵树增加一个根节点
                var key = tMethods._getNormalKey();
                var obj = options.root;
                obj[key.children] = data;
                var arr = [];
                arr.push(obj);
                return arr;
            }
        };
        //树的初始化
        if (options.ajax.enable) { //通过ajax请求得到数据
            tMethods._ajaxData();
        } else { //传入数据
            tMethods._initTree();
        }
        return {
            refresh : function() { //ajax刷新:重新请求数据;data传入刷新:重新画树
                tMethods._removeTree();
                if (options.ajax.enable) {
                    tMethods._ajaxData();
                } else {
                    tMethods._initTree();
                }
            },
            expandAll : function() {
                $(treeDom).find('li.vdiclose').each(function(idx, dom) {
                    tMethods._changeState(dom, $(dom).data("node"));
                });
            },
            collapseAll : function() {
                $(treeDom).find('li.vdiopen').each(function(idx, dom) {
                    tMethods._changeState(dom, $(dom).data("node"));
                });
            },
            getCheckedNodes : function() {
                var res = [];
                $(treeDom).find("span.checkbox_true_full").parent('li').each(function(idx, dom) {
                    res.push($(dom).data("node"));
                });
                return res;
            },
            getSelectedNodes : function() {
                var res = [];
                $(treeDom).find("a.selected").parent('li').each(function(idx, dom) {
                    res.push($(dom).data("node"));
                });
                return res;
            },
            changeState : function(nodeid) {
                var dom = $(treeDom).find('#' + nodeid);
                var node = $(node).data("node");
                if (!node.leaf) {
                    tMethods._changeState(dom, node);
                }
            }
        };
    }
    $.fn.vdiTree = function(options) {
        var $this = $(this);
        return this.each(function() {
            $(this).data("vdiTree", makeVdiTree($this, options));
        });
    };
})(jQuery);

1 Reply to “一个简单的树插件”

发表评论