jacky6024 пре 8 година
родитељ
комит
653d6d99bf
100 измењених фајлова са 10346 додато и 0 уклоњено
  1. 17 0
      urule-console-js/bower.json
  2. 49 0
      urule-console-js/package.json
  3. 80 0
      urule-console-js/src/Remark.js
  4. 148 0
      urule-console-js/src/Styles.js
  5. 57 0
      urule-console-js/src/Utils.js
  6. 175 0
      urule-console-js/src/action/action.js
  7. 151 0
      urule-console-js/src/action/components/ActionEditor.jsx
  8. 68 0
      urule-console-js/src/action/components/SelectMethodDialog.jsx
  9. 9 0
      urule-console-js/src/action/event.js
  10. 36 0
      urule-console-js/src/action/index.jsx
  11. 99 0
      urule-console-js/src/action/reducer.js
  12. 207 0
      urule-console-js/src/bootstrap-contextmenu.js
  13. 62 0
      urule-console-js/src/client/action.js
  14. 52 0
      urule-console-js/src/client/component/ClientConfigEditor.jsx
  15. 27 0
      urule-console-js/src/client/index.jsx
  16. 22 0
      urule-console-js/src/client/reducer.js
  17. 149 0
      urule-console-js/src/components/componentAction.js
  18. 15 0
      urule-console-js/src/components/componentEvent.js
  19. 57 0
      urule-console-js/src/components/dialog/component/CommonDialog.jsx
  20. 78 0
      urule-console-js/src/components/dialog/component/Dialog.jsx
  21. 96 0
      urule-console-js/src/components/dialog/component/KnowledgeTreeDialog.jsx
  22. 67 0
      urule-console-js/src/components/dialog/component/VersionSelectDialog.jsx
  23. 172 0
      urule-console-js/src/components/frametab/component/FrameTab.jsx
  24. 33 0
      urule-console-js/src/components/frametab/component/IFrame.jsx
  25. 132 0
      urule-console-js/src/components/grid/component/Cell.jsx
  26. 73 0
      urule-console-js/src/components/grid/component/CellEditor.jsx
  27. 66 0
      urule-console-js/src/components/grid/component/ChildListDialog.jsx
  28. 123 0
      urule-console-js/src/components/grid/component/Grid.jsx
  29. 61 0
      urule-console-js/src/components/grid/component/Row.jsx
  30. 12 0
      urule-console-js/src/components/grid/componentEvent.js
  31. 3 0
      urule-console-js/src/components/grid/css/grid.css
  32. 66 0
      urule-console-js/src/components/loading/component/Loading.jsx
  33. 119 0
      urule-console-js/src/components/loading/css/loading.css
  34. 22 0
      urule-console-js/src/components/menu/component/Menu.jsx
  35. 28 0
      urule-console-js/src/components/menu/component/MenuItem.jsx
  36. 36 0
      urule-console-js/src/components/splitter/component/Splitter.jsx
  37. 294 0
      urule-console-js/src/components/splitter/component/jquery-splitter.js
  38. 59 0
      urule-console-js/src/components/splitter/css/jquery.splitter.css
  39. BIN
      urule-console-js/src/components/splitter/icons/hgrabber.gif
  40. BIN
      urule-console-js/src/components/splitter/icons/vgrabber.gif
  41. 34 0
      urule-console-js/src/components/tree/component/CommonTree.jsx
  42. 29 0
      urule-console-js/src/components/tree/component/Tree.jsx
  43. 120 0
      urule-console-js/src/components/tree/component/TreeItem.jsx
  44. 22 0
      urule-console-js/src/components/tree/component/TreeParentItem.jsx
  45. 56 0
      urule-console-js/src/components/tree/css/tree.css
  46. 114 0
      urule-console-js/src/constant/action.js
  47. 100 0
      urule-console-js/src/constant/components/ConstantEditor.jsx
  48. 36 0
      urule-console-js/src/constant/index.jsx
  49. 48 0
      urule-console-js/src/constant/reducer.js
  50. 57 0
      urule-console-js/src/css/iconfont.css
  51. BIN
      urule-console-js/src/css/iconfont.eot
  52. 242 0
      urule-console-js/src/css/iconfont.svg
  53. BIN
      urule-console-js/src/css/iconfont.ttf
  54. BIN
      urule-console-js/src/css/iconfont.woff
  55. 59 0
      urule-console-js/src/editor/Math.uuid.js
  56. 0 0
      urule-console-js/src/editor/bootstrap-table.min.css
  57. 6 0
      urule-console-js/src/editor/bootstrap-table.min.js
  58. 269 0
      urule-console-js/src/editor/common/ComparisonOperator.js
  59. 120 0
      urule-console-js/src/editor/common/ComplexArithmetic.js
  60. 74 0
      urule-console-js/src/editor/common/ConditionListDialog.js
  61. 99 0
      urule-console-js/src/editor/common/ConstantValue.js
  62. 35 0
      urule-console-js/src/editor/common/Context.js
  63. 265 0
      urule-console-js/src/editor/common/DSLEditor.js
  64. 52 0
      urule-console-js/src/editor/common/FunctionParameter.js
  65. 55 0
      urule-console-js/src/editor/common/FunctionProperty.js
  66. 113 0
      urule-console-js/src/editor/common/FunctionValue.js
  67. 236 0
      urule-console-js/src/editor/common/InputType.js
  68. 71 0
      urule-console-js/src/editor/common/MethodAction.js
  69. 35 0
      urule-console-js/src/editor/common/MethodParameter.js
  70. 125 0
      urule-console-js/src/editor/common/MethodValue.js
  71. 87 0
      urule-console-js/src/editor/common/NextType.js
  72. 110 0
      urule-console-js/src/editor/common/ParameterValue.js
  73. 54 0
      urule-console-js/src/editor/common/Paren.js
  74. 66 0
      urule-console-js/src/editor/common/Permission.js
  75. 137 0
      urule-console-js/src/editor/common/ResourceListDialog.js
  76. 68 0
      urule-console-js/src/editor/common/ResourceVersionDialog.js
  77. 160 0
      urule-console-js/src/editor/common/ReteDiagram.js
  78. 87 0
      urule-console-js/src/editor/common/SimpleValue.js
  79. 173 0
      urule-console-js/src/editor/common/URule.js
  80. 153 0
      urule-console-js/src/editor/common/VariableValue.js
  81. 332 0
      urule-console-js/src/editor/common/contextMenu.js
  82. 209 0
      urule-console-js/src/editor/common/if-hint.js
  83. 44 0
      urule-console-js/src/editor/common/if_mode.js
  84. 19 0
      urule-console-js/src/editor/common/jquery.utils.js
  85. 226 0
      urule-console-js/src/editor/context.standalone.css
  86. 45 0
      urule-console-js/src/editor/decisiontable/CellCondition.js
  87. 29 0
      urule-console-js/src/editor/decisiontable/CellContent.js
  88. 99 0
      urule-console-js/src/editor/decisiontable/CellExecuteMethod.js
  89. 47 0
      urule-console-js/src/editor/decisiontable/Condition.js
  90. 140 0
      urule-console-js/src/editor/decisiontable/Connection.js
  91. 1378 0
      urule-console-js/src/editor/decisiontable/DecisionTable.js
  92. 227 0
      urule-console-js/src/editor/decisiontable/Join.js
  93. 68 0
      urule-console-js/src/editor/decisiontable/index.jsx
  94. 56 0
      urule-console-js/src/editor/decisiontable/renderers.js
  95. 99 0
      urule-console-js/src/editor/decisiontree/ActionTreeNode.js
  96. 232 0
      urule-console-js/src/editor/decisiontree/ConditionLeft.js
  97. 92 0
      urule-console-js/src/editor/decisiontree/ConditionTreeNode.js
  98. 222 0
      urule-console-js/src/editor/decisiontree/DecisionTree.js
  99. 132 0
      urule-console-js/src/editor/decisiontree/TreeNode.js
  100. 63 0
      urule-console-js/src/editor/decisiontree/VariableTreeNode.js

+ 17 - 0
urule-console-js/bower.json

@@ -0,0 +1,17 @@
+{
+  "name": "urule-console-js",
+  "description": "URule Console Javascript Project",
+  "main": "index.js",
+  "authors": [
+    "Jacky.Gao"
+  ],
+  "license": "GPL",
+  "keywords": [
+    "rule"
+  ],
+  "homepage": "https://github.com/jacky6024/urule-console-js",
+  "dependencies": {
+    "bootstrap": "^3.3.7",
+    "bootstrapvalidator": "bootstrapValidator#^0.5.3"
+  }
+}

+ 49 - 0
urule-console-js/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "urule-console-js",
+  "version": "1.0.0",
+  "description": "URule Console Javascript Project",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "Jacky.Gao",
+  "license": "GPL",
+  "dependencies": {
+    "bootbox": "^4.4.0",
+    "bootstrap": "^3.3.6",
+    "codemirror": "^5.17.0",
+    "css": "^2.2.1",
+    "events": "^1.1.0",
+    "flowdesigner": "^1.1.55",
+    "flux": "^2.1.1",
+    "font-awesome": "^4.6.1",
+    "jquery": "^2.2.3",
+    "object-assign": "^4.1.0",
+    "raphael": "^2.2.1",
+    "react": "^15.4.1",
+    "react-dom": "^15.4.1",
+    "react-redux": "^4.4.6",
+    "react-splitter": "^0.2.0",
+    "redux": "^3.6.0",
+    "redux-logger": "^2.7.4",
+    "redux-thunk": "^2.1.0"
+  },
+  "devDependencies": {
+    "babel-cli": "^6.9.0",
+    "babel-core": "^6.8.0",
+    "babel-loader": "^6.2.4",
+    "babel-preset-es2015": "^6.6.0",
+    "babel-preset-react": "^6.5.0",
+    "clean-webpack-plugin": "^0.1.10",
+    "css-loader": "^0.23.1",
+    "expose-loader": "^0.7.1",
+    "file-loader": "^0.8.5",
+    "redux-devtools": "^3.3.1",
+    "redux-devtools-dock-monitor": "^1.1.1",
+    "redux-devtools-log-monitor": "^1.0.11",
+    "style-loader": "^0.13.1",
+    "url-loader": "^0.5.7",
+    "webpack": "^1.13.3",
+    "webpack-cleanup-plugin": "^0.2.0"
+  }
+}

+ 80 - 0
urule-console-js/src/Remark.js

@@ -0,0 +1,80 @@
+/**
+ * Created by Jacky.gao on 2016/10/25.
+ */
+window.Remark=function (container) {
+    this.remark="";
+    this.defaultRemark="请输入备注内容";
+    this.init(container);
+};
+Remark.prototype.init=function (container) {
+    var toolbar=$("<div style='cursor:pointer;color:#777;font-size:12px'>备注</div>");
+    container.append(toolbar);
+    var icon=$("<i class='glyphicon glyphicon-circle-arrow-down'></i>");
+    toolbar.append(icon);
+    var contentContainer=$("<div class='collapse in'></div>");
+    container.append(contentContainer);
+    this.remarkLabel=$("<div style='color:#999;background: #fdfdfd;padding:5px;border:solid 1px #ddd;border-radius: 5px;font-size: 12px'>"+this.defaultRemark+"</div>");
+    contentContainer.append(this.remarkLabel);
+    this.remarkEditor=$("<textarea class='form-control' rows='4'>"+this.defaultRemark+"</textarea>");
+    contentContainer.append(this.remarkEditor);
+    this.remarkEditor.hide();
+    var _this=this;
+    this.remarkLabel.click(function () {
+        _this.remarkEditor.show();
+        _this.remarkEditor.focus();
+        _this.remarkLabel.hide();
+    });
+
+    this.remarkEditor.change(function () {
+        _this.remark=$(this).val();
+        if(_this.remark===""){
+            _this.remarkLabel.text(_this.defaultRemark);
+        }else{
+            _this.remarkLabel.html(_this.parseBreak(_this.remark));
+        }
+        if(window.setDirty){
+            window.setDirty();
+        }
+        if(window._setDirty){
+            window._setDirty();
+        }
+    });
+    this.remarkEditor.blur(function () {
+        _this.remarkEditor.hide();
+        _this.remarkLabel.show();
+    });
+
+    toolbar.click(function () {
+        contentContainer.collapse("toggle");
+    });
+
+    contentContainer.on('show.bs.collapse', function () {
+        icon.removeClass("glyphicon-circle-arrow-right");
+        icon.addClass("glyphicon-circle-arrow-down");
+    });
+    contentContainer.on('hide.bs.collapse', function () {
+        icon.removeClass("glyphicon-circle-arrow-down");
+        icon.addClass("glyphicon-circle-arrow-right");
+    });
+    contentContainer.collapse('hide');
+};
+
+Remark.prototype.setData=function (data) {
+    if(!data || data===""){
+        return;
+    }
+    this.remark=data;
+    this.remarkEditor.val(data);
+    this.remarkLabel.html(this.parseBreak(data));
+};
+
+Remark.prototype.toXml=function () {
+    return "<remark><![CDATA["+this.remark+"]]></remark>";
+};
+
+Remark.prototype.parseBreak=function (data) {
+    data=data.replace(new RegExp("<",'gm'),'&lt;');
+    data=data.replace(new RegExp(">",'gm'),'&gt;');
+    data=data.replace(new RegExp("\n",'gm'),'</br>');
+    return data;
+};

+ 148 - 0
urule-console-js/src/Styles.js

@@ -0,0 +1,148 @@
+/**
+ * Created by Jacky.gao on 2016/5/26.
+ */
+const Styles={};
+Styles.frameStyle={
+    getRootIcon:function () {
+        return _getStyle("rootIcon","rf rf-root");
+    },
+    getRootIconStyle:function () {
+        return _getStyle("rootIconStyle",{color:'rgb(102, 102, 102)'});
+    },
+    getProjectIcon:function () {
+        return _getStyle("projectIcon","rf rf-project");
+    },
+    getProjectIconStyle:function () {
+        return _getStyle("projectIconStyle",{color:'rgb(188, 15, 105)'});
+    },
+    getResourceIcon:function () {
+        return _getStyle("resourceIcon","rf rf-library");
+    },
+    getResourceIconStyle:function () {
+        return _getStyle("resourceIconStyle",{color:'rgb(88, 45, 170)'});
+    },
+    getResourcePackageIcon:function () {
+        return _getStyle("resourcePackageIcon","rf rf-package");
+    },
+    getResourcePackageIconStyle:function () {
+        return _getStyle("resourcePackageIconStyle",{color:'rgb(180, 133, 19)'});
+    },
+    getLibIcon:function () {
+        return _getStyle("libIcon","rf rf-database");
+    },
+    getLibIconStyle:function () {
+        return _getStyle("libIconStyle",{color:'rgb(24, 121, 27)'});
+    },
+    getActionIcon:function () {
+        return _getStyle("actionIcon","rf rf-action");
+    },
+    getActionIconStyle:function () {
+        return _getStyle("actionIconStyle",{color:'rgb(24, 121, 27)'});
+    },
+    getVariableIcon:function () {
+        return _getStyle("variableIcon","rf rf-variable");
+    },
+    getVariableIconStyle:function () {
+        return _getStyle("variableIconStyle",{color:'rgb(24, 121, 27)'});
+    },
+    getConstantIcon:function () {
+        return _getStyle("constantIcon","rf rf-constant");
+    },
+    getConstantIconStyle:function () {
+        return _getStyle("constantIconStyle",{color:'rgb(24, 121, 27)'});
+    },
+    getParameterIcon:function () {
+        return _getStyle("parameterIcon","rf rf-parameter");
+    },
+    getParameterIconStyle:function () {
+        return _getStyle("parameterIconStyle",{color:'rgb(24, 121, 27)'});
+    },
+    getRuleIcon:function () {
+        return _getStyle("ruleIcon","rf rf-rule");
+    },
+    getRuleIconStyle:function () {
+        return _getStyle("ruleIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getUlIcon:function () {
+        return _getStyle("ulIcon","rf rf-script");
+    },
+    getUlIconStyle:function () {
+        return _getStyle("ulIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getRuleLibIcon:function () {
+        return _getStyle("ruleLibIcon","rf rf-rule");
+    },
+    getRuleLibIconStyle:function () {
+        return _getStyle("ruleLibIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getDecisionTableIcon:function () {
+        return _getStyle("decisionTableIcon","rf rf-table");
+    },
+    getDecisionTableIconStyle:function () {
+        return _getStyle("decisionTableIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getScriptDecisionTableIcon:function () {
+        return _getStyle("scriptDecisionTableIcon","rf rf-scripttable");
+    },
+    getScriptDecisionTableIconStyle:function () {
+        return _getStyle("scriptDecisionTableIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getDecisionTableLibIcon:function () {
+        return _getStyle("decisionTableLibIcon","rf rf-table");
+    },
+    getDecisionTableLibIconStyle:function () {
+        return _getStyle("decisionTableLibIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getDecisionTreeIcon:function () {
+        return _getStyle("decisionTreeIcon","rf rf-tree");
+    },
+    getDecisionTreeIconStyle:function () {
+        return _getStyle("decisionTreeIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getDecisionTreeLibIcon:function () {
+        return _getStyle("decisionTreeLibIcon","rf rf-tree");
+    },
+    getDecisionTreeLibIconStyle:function () {
+        return _getStyle("decisionTreeLibIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+
+    getScorecardIcon:function () {
+        return _getStyle("scorecardIcon","rf rf-scorecard");
+    },
+    getScorecardIconStyle:function () {
+        return _getStyle("scorecardIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getScorecardLibIcon:function () {
+        return _getStyle("scorecardLibIcon","rf rf-scorecard");
+    },
+    getScorecardLibIconStyle:function () {
+        return _getStyle("scorecardLibIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+
+    getFlowIcon:function () {
+        return _getStyle("flowIcon","rf rf-flow");
+    },
+    getFlowIconStyle:function () {
+        return _getStyle("flowIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getFlowLibIcon:function () {
+        return _getStyle("flowLibIcon","rf rf-flow");
+    },
+    getFlowLibIconStyle:function () {
+        return _getStyle("flowLibIconStyle",{color:'rgb(31, 90, 163)'});
+    },
+    getFolderIcon:function () {
+        return _getStyle("flowIcon","rf rf-folder");
+    },
+    getFolderIconStyle:function () {
+        return _getStyle("flowIconStyle",{color:'rgb(224, 126, 1)'});
+    },
+};
+function _getStyle(styleName,defaultValue) {
+    if(window._frameStyles){
+        return window._frameStyles[styleName];
+    }else{
+        return defaultValue;
+    }
+}
+export default Styles;

+ 57 - 0
urule-console-js/src/Utils.js

@@ -0,0 +1,57 @@
+/**
+ * Created by Jacky.gao on 2016/7/27.
+ */
+
+window.iframe_id_=1;
+
+export function nextIFrameId(){
+    window.iframe_id_++;
+    return '_iframe'+window.iframe_id_;
+};
+
+export function getParameter(name) {
+    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+    var r = window.location.search.substr(1).match(reg);
+    if (r != null)return r[2];
+    return null;
+};
+
+export function ajaxSave(url,parameters,callback) {
+    $.ajax({
+        type:'POST',
+        url,
+        data:parameters,
+        success:function (result) {
+            callback(result);
+        },
+        error:function (req) {
+            if(req.status===401){
+                bootbox.alert("权限不足,不能进行此操作.");
+            }else{
+                bootbox.alert('服务端错误,操作失败!');
+            }
+        }
+    });
+}
+
+export function formatDate(date,format){
+    if(typeof date === 'number'){
+        date=new Date(date);
+    }
+    if(typeof date==='string'){
+        return date;
+    }
+    var o = {
+        "M+" : date.getMonth()+1,
+        "d+" : date.getDate(),
+        "H+" : date.getHours(),
+        "m+" : date.getMinutes(),
+        "s+" : date.getSeconds()
+    };
+    if(/(y+)/.test(format))
+        format=format.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));
+    for(var k in o)
+        if(new RegExp("("+ k +")").test(format))
+            format = format.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
+    return format;
+};

+ 175 - 0
urule-console-js/src/action/action.js

@@ -0,0 +1,175 @@
+/**
+ * Created by jacky on 2016/6/15.
+ */
+import {ajaxSave} from '../Utils.js';
+
+export const LOAD_MASTER_COMPLETED='load_master_completed';
+export const LOAD_SLAVE_COMPLETE='load_slave_completed';
+export const LOAD_METHOD_COMPLETED='load_method_data';
+export const DO_NOTHING='do_nothing';
+export const ADD_MASTER='add_master';
+export const DEL_MASTER='del_master';
+export const ADD_SLAVE='add_slave';
+export const DEL_SLAVE='del_slave';
+export const DEL_PARAMETER='del_parameter';
+export const ADD_PARAMETER='add_parameter';
+export const IMPORT_FIELDS='IMPORT_FIELDS';
+export const SAVE='save';
+export const SAVE_COMPLETED='save_completed';
+export const LOADED_BEAN_METHODS='loaded_bean_methods';
+
+
+export function save(newVersion,file){
+    return {newVersion,file,type:SAVE};
+};
+
+export function saveData(data,newVersion,file) {
+    let xml='<?xml version="1.0" encoding="utf-8"?>';
+    xml+='<action-library>';
+    let errorInfo='';
+    for(let item of data){
+        if(!item.name || item.name.length<1){
+            errorInfo='动作名称不能为空.';
+            break;
+        }
+        if(!item.id || item.id.length<1){
+            errorInfo='Bean Id不能为空.';
+            break;
+        }
+        xml+="<spring-bean id='"+item.id+"' name='"+item.name+"'>";
+        var methods=item.methods;
+        if(!methods){
+            errorInfo="动作分类["+item.name+"]下未定义具体的动作方法.";
+            break;
+        }
+        for(let method of methods){
+            if(!method.name || method.name.length<1){
+                errorInfo='名称不能为空.';
+                break;
+            }
+            if(!method.methodName || method.methodName.length<1){
+                errorInfo='方法名不能为空.';
+                break;
+            }
+            xml+="<method name='"+method.name+"' method-name='"+method.methodName+"'>";
+            var parameters=method.parameters;
+            if(parameters){
+                for(let p of parameters){
+                    if(!p.name || p.name.length<1){
+                        errorInfo='参数名不能为空!';
+                        break;
+                    }
+                    if(!p.type || p.type.length<1){
+                        errorInfo='参数类型不能为空!';
+                        break;
+                    }
+                    xml+="<parameter name='"+p.name+"' type='"+p.type+"'/>";
+                }
+            }
+            xml+="</method>";
+        }
+        if(errorInfo.length>1){
+            break;
+        }
+        xml+='</spring-bean>';
+    }
+    if(errorInfo.length>1){
+        bootbox.alert(errorInfo+',不能保存!');
+        return;
+    }
+    xml+='</action-library>';
+    let postData={content:xml,file,newVersion};
+    const url=window._server+'/common/saveFile';
+    if(newVersion){
+        bootbox.prompt("请输入新版本描述.",function (versionComment) {
+            if(!versionComment){
+                return;
+            }
+            postData.versionComment=versionComment;
+            ajaxSave(url,postData,function () {
+                bootbox.alert('保存成功!');
+            })
+        });
+    }else{
+        ajaxSave(url,postData,function () {
+            bootbox.alert('保存成功!');
+        })
+    }
+    return {type:SAVE_COMPLETED};
+};
+
+export function loadBeanMethods(beanId){
+    return function(dispatch){
+        var url=window._server+'/actioneditor/loadMethods';
+        $.ajax({
+            url,
+            type:'POST',
+            data:{beanId},
+            success:function(result){
+                dispatch({type:LOADED_BEAN_METHODS,result});
+            },
+            error:function(){
+                alert('加载方法失败.');
+            }
+        });
+    }
+};
+
+export function addMaster() {
+    return {type:ADD_MASTER};
+};
+
+export function deleteMaster(rowIndex) {
+    return {rowIndex,type:DEL_MASTER};
+}
+
+export function deleteSlave(rowIndex) {
+    return {rowIndex,type:DEL_SLAVE};
+};
+
+export function deleteParameter(rowIndex){
+    return {rowIndex,type:DEL_PARAMETER};
+};
+
+export function addSlave(newSlaveData) {
+    if(newSlaveData){
+        return {type:ADD_SLAVE,newSlaveData};
+    }else{
+        return {type:ADD_SLAVE};
+    }
+};
+
+export function addParameter(){
+    return {type:ADD_PARAMETER};
+};
+
+export function loadMasterData(files) {
+    return function (dispatch) {
+        var url=window._server+"/xml";
+        $.ajax({
+            url,
+            type:'POST',
+            data:{files},
+            success:function (data) {
+                dispatch({type:LOAD_MASTER_COMPLETED,masterData:data[0]});
+            },
+            error:function () {
+                alert("加载数据失败.");
+            }
+        });
+    }
+};
+
+let currentMasterGridRowIndex=null;
+
+export function loadSlaveData(masterData,rowIndex) {
+   if(currentMasterGridRowIndex!==null && currentMasterGridRowIndex===rowIndex){
+        return {type:DO_NOTHING};
+    }
+    currentMasterGridRowIndex=rowIndex;
+    return {type:LOAD_SLAVE_COMPLETE,masterRowData:masterData};
+};
+
+export function loadMethodData(slaveData){
+    return {type:LOAD_METHOD_COMPLETED,slaveData};
+}

+ 151 - 0
urule-console-js/src/action/components/ActionEditor.jsx

@@ -0,0 +1,151 @@
+/**
+ * Created by jacky on 2016/6/15.
+ */
+import React,{Component,PropTypes} from 'react';
+import {connect} from 'react-redux';
+import Splitter from '../../components/splitter/component/Splitter.jsx';
+import Grid from '../../components/grid/component/Grid.jsx';
+import * as event from '../event.js';
+import * as action from '../action.js';
+import SelectMethodDialog from './SelectMethodDialog.jsx';
+import * as refEvent from '../../reference/event.js';
+import ReferenceDialog from '../../reference/ReferenceDialog.jsx';
+
+class ActionEditor extends Component{
+    render(){
+        const masterGridHeaders=[
+            {id:'m-id',name:'id',label:'Bean Id',filterable:true,editable:true,width:'180px'},
+            {id:'m-name',name:'name',label:'动作名称',filterable:true,editable:true}
+        ];
+        const slaveGridHeaders=[
+            {id:'s-name',name:'name',label:'方法名称',filterable:true,width:'160px',editable:true},
+            {id:'s-methodName',name:'methodName',label:'方法名',filterable:true,editable:true}
+        ];
+        const parametersHeaders=[
+            {id:'p-name',name:'name',label:'参数名称',filterable:true,width:'160px',editable:true},
+            {id:'p-type',name:'type',label:'数据类型',editorType:'select',selectData:['String','Integer','Char','Double','Long','Float','BigDecimal','Boolean','Date','List','Set','Map','Enum','Object'],editable:true}
+        ];
+        const {masterData,masterRowData,slaveRowData,file,dispatch}=this.props;
+        const masterGridOperationCol={
+            width:'70px',
+            operations:[
+                {
+                    label:'选择当前指定Bean中定义的方法',
+                    icon:'glyphicon glyphicon-hand-up',
+                    style:{fontSize:'18px',color:'#337ab7',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex,rowData){
+                        let id=rowData.id;
+                        if(!id || id.length<1){
+                            bootbox.alert('请先指定Bean Id');
+                            return;
+                        }
+                        event.eventEmitter.emit(event.OPEN_SELECT_METHOD_DIALOG,id);
+                    }
+                },
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'18px',color:'#d9534f',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.deleteMaster(rowIndex));
+                            dispatch(action.loadSlaveData({}));
+                        });
+                    }
+                }
+            ]
+        };
+
+        const slaveGridOperationCol={
+            width:'65px',
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'18px',color:'#d9534f',padding:'0px 10px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.deleteSlave(rowIndex));
+                            dispatch(action.loadMethodData({}));
+                        })
+                    }
+                }
+            ]
+        };
+        const methodGridOperationCol={
+            width:'65px',
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'18px',color:'#d9534f',padding:'0px 10px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.deleteParameter(rowIndex));
+                        })
+                    }
+                }
+            ]
+        };
+        return (
+            <div>
+                <ReferenceDialog/>
+                <Splitter orientation='vertical' position='35%'>
+                    <div>
+                        <div style={{margin:'2px'}}>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-primary" type="button" onClick={(e)=>{dispatch(action.addMaster())}}><i className="glyphicon glyphicon-plus-sign"></i> 添加Bean</button>
+                            </div>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-danger" type="button" onClick={()=>{dispatch(action.save(false,file))}}><i className="rf rf-save"></i> 保存</button>
+                                <button className="btn btn-danger" type="button" onClick={()=>{dispatch(action.save(true,file))}}><i className="rf rf-savenewversion"></i> 保存为新版本</button>
+                            </div>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-info" type="button" onClick={(e)=>{
+                                    refEvent.eventEmitter.emit(refEvent.OPEN_REFERENCE_DIALOG,file);
+                                }}><i className="rf rf-link"></i> 查看引用</button>
+                            </div>
+                        </div>
+                        <Grid headers={masterGridHeaders} rows={masterData} operationConfig={masterGridOperationCol} rowClick={(rowData,rowIndex)=>{
+                            dispatch(action.loadSlaveData(rowData,rowIndex));
+                        }}/>
+                        <SelectMethodDialog/>
+                    </div>
+                    <div>
+                        <div className="row" style={{margin:'0px'}}>
+                            <div className="col-md-6 col-xs-6" style={{padding:'0px 4px 0px 2px'}}>
+                                <div>
+                                    <div className="btn-group btn-group-sm" >
+                                        <button className="btn btn-primary" type="button" onClick={e=>{dispatch(action.addSlave())}}><i className="glyphicon glyphicon-plus"></i> 添加方法</button>
+                                    </div>
+                                </div>
+                                <Grid headers={slaveGridHeaders} rows={masterRowData.methods} operationConfig={slaveGridOperationCol} rowClick={(rowData,rowIndex)=>{
+                                    dispatch(action.loadMethodData(rowData,rowIndex));
+                                }}/>
+                            </div>
+                            <div className="col-md-6 col-xs-6" style={{padding:'0px 2px 0px 1px'}}>
+                                <div>
+                                    <div className="btn-group btn-group-sm" >
+                                        <button className="btn btn-primary" type="button" onClick={e=>{dispatch(action.addParameter())}}><i className="glyphicon glyphicon-plus-sign"></i> 添加参数</button>
+                                    </div>
+                                </div>
+                                <Grid headers={parametersHeaders} rows={slaveRowData.parameters} operationConfig={methodGridOperationCol}/>
+                            </div>
+                        </div>
+                    </div>
+                </Splitter>
+            </div>
+        );
+    }
+};
+function select(state){
+    return {
+        masterData:state.master.data || [],
+        masterRowData:state.slave.data || {},
+        slaveRowData:state.method.data || {}
+    };
+};
+export default connect(select)(ActionEditor);

+ 68 - 0
urule-console-js/src/action/components/SelectMethodDialog.jsx

@@ -0,0 +1,68 @@
+/**
+ * Created by jacky on 2016/6/16.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import {connect} from 'react-redux';
+import CommonDialog from '../../components/dialog/component/CommonDialog.jsx';
+import Grid from '../../components/grid/component/Grid.jsx';
+import * as action from '../action.js';
+import * as event from '../event.js';
+
+class SelectMethodDialog extends Component{
+    componentDidMount(){
+        event.eventEmitter.on(event.CLOSE_SELECT_METHOD_DIALOG,()=>{
+           $( ReactDOM.findDOMNode(this)).modal('hide');
+        });
+        const {dispatch}=this.props;
+        event.eventEmitter.on(event.OPEN_SELECT_METHOD_DIALOG,(beanId)=>{
+            $( ReactDOM.findDOMNode(this)).modal('show');
+            dispatch(action.loadBeanMethods(beanId));
+        });
+    }
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.CLOSE_SELECT_METHOD_DIALOG);
+    }
+    render(){
+        const {data,dispatch}=this.props;
+        const headers=[
+            {id:'method-methodName',name:'methodName',label:'方法名称',filterable:true,width:'200px'},
+            {id:'method-name',name:'name',label:'名称',filterable:true}
+        ];
+        const operationCol={
+            width:'80px',
+            operations:[
+                {
+                    label:'选择此方法',
+                    icon:'glyphicon glyphicon-hand-up',
+                    style:{fontSize:'18px',color:'#337ab7',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex,rowData){
+                        dispatch(action.addSlave(rowData));
+                        bootbox.alert('添加成功.');
+                    }
+                }
+            ]
+        };
+        const body=(
+            <Grid rows={data} headers={headers} operationConfig={operationCol}/>
+        );
+        const buttons=[
+            {
+                name:'关闭',
+                className:'btn btn-primary',
+                icon:'glyphicon glyphicon-remove',
+                click:function(){
+                    event.eventEmitter.emit(event.CLOSE_SELECT_METHOD_DIALOG);
+                }
+            }
+        ];
+        return (
+            <CommonDialog title="选择方法" body={body} buttons={buttons}/>
+        );
+    }
+};
+
+function select(state){
+    return {data:state.methodList.data || []};
+}
+export default connect(select)(SelectMethodDialog);

+ 9 - 0
urule-console-js/src/action/event.js

@@ -0,0 +1,9 @@
+/**
+ * Created by jacky on 2016/6/15.
+ */
+import events from 'events';
+
+export const OPEN_SELECT_METHOD_DIALOG='open_select_method_dialog';
+export const CLOSE_SELECT_METHOD_DIALOG='close_select_method_dialog';
+
+export const eventEmitter = new events.EventEmitter();

+ 36 - 0
urule-console-js/src/action/index.jsx

@@ -0,0 +1,36 @@
+/**
+ * Created by jacky on 2016/6/15.
+ */
+import '../../node_modules/bootstrap/dist/css/bootstrap.css';
+import '../css/iconfont.css';
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {createStore,applyMiddleware} from 'redux';
+import thunk from 'redux-thunk';
+import {Provider} from 'react-redux';
+import reducer from './reducer.js';
+import * as action from './action.js';
+import ActionEditor from './components/ActionEditor.jsx';
+
+$(document).ready(function(){
+    const store= createStore(reducer,applyMiddleware(thunk));
+    const file=_getParameter('file');
+    if(!file || file.length<1){
+        bootbox.alert('请先指定要加载的变量库文件.');
+        return;
+    }
+    store.dispatch(action.loadMasterData(file));
+    ReactDOM.render(
+        <Provider store={store}>
+            <ActionEditor file={file}/>
+        </Provider>,
+        document.getElementById("container")
+    );
+});
+function _getParameter(name) {
+    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+    var r = window.location.search.substr(1).match(reg);
+    if (r != null)return unescape(r[2]);
+    return null;
+};

+ 99 - 0
urule-console-js/src/action/reducer.js

@@ -0,0 +1,99 @@
+/**
+ * Created by jacky on 2016/6/15.
+ */
+import {combineReducers} from 'redux';
+import * as ACTIONS from './action.js';
+import {uniqueID} from '../components/componentAction.js';
+
+function master(state=[],action){
+    switch (action.type){
+        case ACTIONS.LOAD_MASTER_COMPLETED:
+            var data=action.masterData.springBeans;
+            return Object.assign({},state.prototype,{data});
+        case ACTIONS.ADD_MASTER:
+            var newData=[];
+            if(state.data){
+                newData=[...state.data];
+            }
+            newData.push({id:'',name:'',methods:[]});
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.DEL_MASTER:
+            var newData=[...state.data];
+            var index=action.rowIndex;
+            newData.splice(index,1);
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.SAVE:
+            var data=state.data;
+            var newVersion=action.newVersion;
+            var file=action.file;
+            ACTIONS.saveData(data,newVersion,file);
+            return state;
+        default:
+            return state;
+    }
+};
+
+function slave(state={},action){
+    switch (action.type){
+        case ACTIONS.LOAD_SLAVE_COMPLETE:
+            var masterRowData=action.masterRowData;
+            if(masterRowData && masterRowData.methods){
+                masterRowData.methods.forEach((m,index)=>{
+                    m.id=uniqueID();
+                });
+            }
+            return Object.assign({},state.prototype,{data:action.masterRowData});
+        case ACTIONS.ADD_SLAVE:
+            if(!state.data || !state.data.methods){
+                bootbox.alert('请先指定方法所属的Bean');
+                return state;
+            }
+            var newData=Object.assign({},state.data);
+            var newSlaveData=action.newSlaveData || {name:'',methodName:'',parameters:[]};
+            newData.methods.push(newSlaveData);
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.DEL_SLAVE:
+            var index=action.rowIndex;
+            var newData=Object.assign({},state.data);
+            newData.methods.splice(index,1);
+            return Object.assign({},state.prototype,{data:newData});
+        default:
+            return state;
+    }
+};
+
+function method(state={},action){
+    switch (action.type){
+        case ACTIONS.LOAD_METHOD_COMPLETED:
+            return Object.assign({},state.prototype,{data:action.slaveData});
+        case ACTIONS.LOAD_SLAVE_COMPLETE:
+            return Object.assign({},state.prototype,{data:{}});
+        case ACTIONS.ADD_PARAMETER:
+            if(!state.data || !state.data.parameters){
+                bootbox.alert('请先指定参数所属的方法');
+                return state;
+            }
+            var newData=Object.assign({},state.data);
+            newData.parameters.push({name:'',type:'String'});
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.DEL_PARAMETER:
+            var index=action.rowIndex;
+            var newData=Object.assign({},state.data);
+            newData.parameters.splice(index,1);
+            return Object.assign({},state.prototype,{data:newData});
+        default:
+            return state;
+    }
+};
+function methodList(state=[],action){
+    switch (action.type){
+        case ACTIONS.LOADED_BEAN_METHODS:
+            return Object.assign({},state.prototype,{data:action.result});
+        default:
+            return state;
+    }
+}
+
+export default combineReducers({
+    master,slave,method,methodList
+});

+ 207 - 0
urule-console-js/src/bootstrap-contextmenu.js

@@ -0,0 +1,207 @@
+/*!
+ * Bootstrap Context Menu
+ * Author: @sydcanem
+ * https://github.com/sydcanem/bootstrap-contextmenu
+ *
+ * Inspired by Bootstrap's dropdown plugin.
+ * Bootstrap (http://getbootstrap.com).
+ *
+ * Licensed under MIT
+ * ========================================================= */
+
+;(function($) {
+
+	'use strict';
+
+	/* CONTEXTMENU CLASS DEFINITION
+	 * ============================ */
+	var toggle = '[data-toggle="context"]';
+
+	var ContextMenu = function (element, options) {
+		this.$element = $(element);
+
+		this.before = options.before || this.before;
+		this.onItem = options.onItem || this.onItem;
+		this.scopes = options.scopes || null;
+
+		if (options.target) {
+			this.$element.data('target', options.target);
+		}
+
+		this.listen();
+	};
+
+	ContextMenu.prototype = {
+
+		constructor: ContextMenu
+		,show: function(e) {
+			var $menu
+				, evt
+				, tp
+				, items
+				, relatedTarget = { relatedTarget: this, target: e.currentTarget };
+
+			if (this.isDisabled()) return;
+
+			this.closemenu();
+
+			if (this.before.call(this,e,$(e.currentTarget)) === false) return;
+
+			$menu = this.getMenu();
+			$menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
+
+			tp = this.getPosition(e, $menu);
+			items = 'li:not(.divider)';
+			$menu.attr('style', '')
+				.css(tp)
+				.addClass('open')
+				.on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
+				.trigger('shown.bs.context', relatedTarget);
+
+			// Delegating the `closemenu` only on the currently opened menu.
+			// This prevents other opened menus from closing.
+			//$('html').on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
+			$('html').click(function () {
+				this.closemenu();
+			}.bind(this));
+			return false;
+		}
+
+		,closemenu: function(e) {
+			var $menu
+				, evt
+				, items
+				, relatedTarget;
+
+			$menu = this.getMenu();
+
+			if(!$menu.hasClass('open')) return;
+
+			relatedTarget = { relatedTarget: this };
+			$menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
+
+			items = 'li:not(.divider)';
+			$menu.removeClass('open')
+				.off('click.context.data-api', items)
+				.trigger('hidden.bs.context', relatedTarget);
+
+			$('html')
+				.off('click.context.data-api', $menu.selector);
+			// Don't propagate click event so other currently
+			// opened menus won't close.
+			if (e) {
+				e.stopPropagation();
+			}
+		}
+
+		,keydown: function(e) {
+			if (e.which == 27) this.closemenu(e);
+		}
+
+		,before: function(e) {
+			return true;
+		}
+
+		,onItem: function(e) {
+			return true;
+		}
+
+		,listen: function () {
+			this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this));
+			//$('html').on('click.context.data-api', $.proxy(this.closemenu, this));
+			$('html').on('keydown.context.data-api', $.proxy(this.keydown, this));
+		}
+
+		,destroy: function() {
+			this.$element.off('.context.data-api').removeData('context');
+			$('html').off('.context.data-api');
+		}
+
+		,isDisabled: function() {
+			return this.$element.hasClass('disabled') || 
+					this.$element.attr('disabled');
+		}
+
+		,getMenu: function () {
+			var selector = this.$element.data('target')
+				, $menu;
+
+			if (!selector) {
+				selector = this.$element.attr('href');
+				selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
+			}
+
+			$menu = $(selector);
+
+			return $menu && $menu.length ? $menu : this.$element.find(selector);
+		}
+
+		,getPosition: function(e, $menu) {
+			var mouseX = e.clientX
+				, mouseY = e.clientY
+				, boundsX = $(window).width()
+				, boundsY = $(window).height()
+				, menuWidth = $menu.find('.dropdown-menu').outerWidth()
+				, menuHeight = $menu.find('.dropdown-menu').outerHeight()
+				, tp = {"position":"absolute","z-index":9999}
+				, Y, X, parentOffset;
+
+			if (mouseY + menuHeight > boundsY) {
+				Y = {"top": mouseY - menuHeight + $(window).scrollTop()};
+			} else {
+				Y = {"top": mouseY + $(window).scrollTop()};
+			}
+
+			if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) {
+				X = {"left": mouseX - menuWidth + $(window).scrollLeft()};
+			} else {
+				X = {"left": mouseX + $(window).scrollLeft()};
+			}
+
+			// If context-menu's parent is positioned using absolute or relative positioning,
+			// the calculated mouse position will be incorrect.
+			// Adjust the position of the menu by its offset parent position.
+			parentOffset = $menu.offsetParent().offset();
+			X.left = X.left - parentOffset.left;
+			Y.top = Y.top - parentOffset.top;
+ 
+			return $.extend(tp, Y, X);
+		}
+
+	};
+
+	/* CONTEXT MENU PLUGIN DEFINITION
+	 * ========================== */
+
+	$.fn.contextmenu = function (option,e) {
+		return this.each(function () {
+			var $this = $(this)
+				, data = $this.data('context')
+				, options = (typeof option == 'object') && option;
+
+			if (!data) $this.data('context', (data = new ContextMenu($this, options)));
+			if (typeof option == 'string') data[option].call(data, e);
+		});
+	};
+
+	$.fn.contextmenu.Constructor = ContextMenu;
+
+	/* APPLY TO STANDARD CONTEXT MENU ELEMENTS
+	 * =================================== */
+
+	$(document)
+	   .on('contextmenu.context.data-api', function() {
+			$(toggle).each(function () {
+				var data = $(this).data('context');
+				if (!data) return;
+				data.closemenu();
+			});
+		})
+		.on('contextmenu.context.data-api', toggle, function(e) {
+			$(this).contextmenu('show', e);
+
+			e.preventDefault();
+			e.stopPropagation();
+		});
+
+}(jQuery));

+ 62 - 0
urule-console-js/src/client/action.js

@@ -0,0 +1,62 @@
+/**
+ * Created by Jacky.gao on 2016/8/11.
+ */
+export const ADD='add';
+export const DEL='del';
+export const LOADED_DATA='loaded_data';
+
+export function loadData(project) {
+    return function (dispatch) {
+        $.ajax({
+            url:window._server+'/clientconfig/loadData?project='+project,
+            type:'POST',
+            success:function (data) {
+                dispatch({type:LOADED_DATA,data});
+            },
+            error:function () {
+                alert('加载数据失败!');
+            }
+        });
+    };
+};
+export function save(data,project) {
+    let xml="<?xml version=\"1.0\" encoding=\"utf-8\"?><client-config>",error=null;
+    for(let item of data){
+        if(!item.name){
+            error='客户端名不能为空';
+            break;
+        }
+        if(!item.client){
+            error='客户端地址不能为空';
+            break;
+        }
+        xml+=`<item name="${item.name}" client="${item.client}"/>`;
+    }
+    if(error){
+        bootbox.alert(error);
+        return;
+    }
+    xml+="</client-config>";
+    $.ajax({
+        url:window._server+'/clientconfig/save',
+        type:'POST',
+        data:{project,content:xml},
+        success:function () {
+            bootbox.alert('保存成功!');
+        },
+        error:function (req) {
+            if(req.status===401){
+                alert("权限不足,不能进行此操作.");
+            }else{
+                alert('服务端错误,操作失败!');
+            }
+        }
+    });
+};
+export function del(index) {
+    return {type:DEL,index};
+};
+export function add() {
+    return {type: ADD};
+};
+

+ 52 - 0
urule-console-js/src/client/component/ClientConfigEditor.jsx

@@ -0,0 +1,52 @@
+/**
+ * Created by Jacky.gao on 2016/8/11.
+ */
+import React,{Component} from 'react';
+import {connect} from 'react-redux';
+import Grid from '../../components/grid/component/Grid.jsx';
+import * as action from '../action.js';
+
+class ClientConfigEditor extends Component{
+    render(){
+        const {data,dispatch,project}=this.props;
+        const headers=[
+            {id:'c-name',name:'name',label:'客户端名称',filterable:true,editable:true,width:'220px'},
+            {id:'c-url',name:'client',label:'客户端地址',filterable:true,editable:true}
+        ];
+        const operationConfig={
+            width:'100px',
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'16px',color:'#d9534f',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.del(rowIndex));
+                        });
+                    }
+                }
+            ]
+        };
+        return (
+            <div>
+                <div style={{margin:'2px'}}>
+                    <div className="btn-group" style={{margin:'2px'}}>
+                        <button className="btn btn-primary" type="button" onClick={e=>{dispatch(action.add())}}><i className="glyphicon glyphicon-plus-sign"></i> 添加</button>
+                    </div>
+                    <div className="btn-group" style={{margin:'2px'}}>
+                        <button className="btn btn-danger" type="button" onClick={()=>{
+                                action.save(data,project);
+                            }}><i className="glyphicon glyphicon-floppy-saved"></i> 保存</button>
+                    </div>
+                </div>
+                <Grid headers={headers} rows={data} dispatch={dispatch} operationConfig={operationConfig}/>
+            </div>
+        );
+    }
+};
+function select(state={}) {
+    return {data:state.data || []}
+};
+export default connect(select)(ClientConfigEditor);

+ 27 - 0
urule-console-js/src/client/index.jsx

@@ -0,0 +1,27 @@
+/**
+ * Created by Jacky.gao on 2016/8/11.
+ */
+import '../../node_modules/bootstrap/dist/css/bootstrap.css';
+import '../css/iconfont.css';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {createStore,applyMiddleware} from 'redux';
+import {Provider} from 'react-redux';
+import thunk from 'redux-thunk';
+
+import reducer from './reducer.js';
+import ClientConfigEditor from './component/ClientConfigEditor.jsx';
+import * as action from './action.js';
+import {getParameter} from '../Utils.js';
+
+$(document).ready(function () {
+    const store=createStore(reducer,applyMiddleware(thunk));
+    const project=getParameter('project');
+    store.dispatch(action.loadData(project));
+    ReactDOM.render(
+        <Provider store={store}>
+            <ClientConfigEditor project={project}/>
+        </Provider>,
+        document.getElementById('container')
+    );
+});

+ 22 - 0
urule-console-js/src/client/reducer.js

@@ -0,0 +1,22 @@
+/**
+ * Created by Jacky.gao on 2016/8/11.
+ */
+import * as ACTIONS from './action.js';
+
+export default function clientConfig(state={},action) {
+    switch (action.type){
+        case ACTIONS.LOADED_DATA:
+            var data=action.data;
+            return Object.assign({},state.prototype,{data})
+            break;
+        case ACTIONS.DEL:
+            var index=action.index,newData=[...state.data];
+            newData.splice(index,1);
+            return Object.assign({},state.prototype,{data:newData});
+            break;
+        case ACTIONS.ADD:
+            var data=[...state.data];
+            data.push({});
+            return Object.assign({},state.prototype,{data});
+    }
+}

+ 149 - 0
urule-console-js/src/components/componentAction.js

@@ -0,0 +1,149 @@
+/**
+ * Created by jacky on 2016/6/10.
+ */
+import Styles from '../Styles.js';
+
+let __ui_id=1;
+export function uniqueID(){
+    const id='_ui_'+(__ui_id++);
+    return id;
+};
+
+export function loadFileVersions(file,callback){
+    var url=window._server+'/frame/loadFileVersions';
+    $.ajax({
+        url,
+        data:{file},
+        type:'POST',
+        success:function(data){
+            buildData(data);
+            callback(data);
+        },
+        error:function(){
+            alert("加载文件["+file+"]的版本信息失败.");
+        }
+    });
+};
+
+export function loadResourceTreeData(data,callback){
+    var url=window._server+'/common/loadResourceTreeData';
+    $.ajax({
+        url,
+        data,
+        type:'POST',
+        success:function(data){
+            buildData(data);
+            callback(data);
+        },
+        error:function(){
+            alert('加载资源失败.');
+        }
+    });
+};
+
+function buildData(data) {
+    switch (data.type){
+        case "root":
+            data._icon=Styles.frameStyle.getRootIcon();
+            data._style=Styles.frameStyle.getRootIconStyle();
+            break;
+        case "folder":
+            data._icon=Styles.frameStyle.getFolderIcon();
+            data._style=Styles.frameStyle.getFolderIconStyle();
+            break;
+        case "rule":
+            data._icon=Styles.frameStyle.getRuleIcon();
+            data._style=Styles.frameStyle.getRuleIconStyle();
+            data.editorPath="/ruleeditor";
+            break;
+        case "project":
+            data._icon=Styles.frameStyle.getProjectIcon();
+            data._style=Styles.frameStyle.getProjectIconStyle();
+            break;
+        case "resource":
+            data._icon=Styles.frameStyle.getResourceIcon();
+            data._style=Styles.frameStyle.getResourceIconStyle();
+            break;
+        case "resourcePackage":
+            data._icon=Styles.frameStyle.getResourcePackageIcon();
+            data._style=Styles.frameStyle.getResourcePackageIconStyle();
+            data.editorPath="/packageeditor";
+            break;
+        case "lib":
+            data._icon=Styles.frameStyle.getLibIcon();
+            data._style=Styles.frameStyle.getLibIconStyle();
+            break;
+        case "action":
+            data._icon=Styles.frameStyle.getActionIcon();
+            data._style=Styles.frameStyle.getActionIconStyle();
+            data.editorPath="/actioneditor";
+            break;
+        case "parameter":
+            data._icon=Styles.frameStyle.getParameterIcon();
+            data._style=Styles.frameStyle.getParameterIconStyle();
+            data.editorPath="/parametereditor";
+            break;
+        case "constant":
+            data._icon=Styles.frameStyle.getConstantIcon();
+            data._style=Styles.frameStyle.getConstantIconStyle();
+            data.editorPath="/constanteditor";
+            break;
+        case "variable":
+            data._icon=Styles.frameStyle.getVariableIcon();
+            data._style=Styles.frameStyle.getVariableIconStyle();
+            data.editorPath="/variableeditor";
+            break;
+        case "ruleLib":
+            data._icon=Styles.frameStyle.getRuleLibIcon();
+            data._style=Styles.frameStyle.getRuleLibIconStyle();
+            break;
+        case "decisionTableLib":
+            data._icon=Styles.frameStyle.getDecisionTableLibIcon();
+            data._style=Styles.frameStyle.getDecisionTableLibIconStyle();
+            break;
+        case "decisionTreeLib":
+            data._icon=Styles.frameStyle.getDecisionTreeLibIcon();
+            data._style=Styles.frameStyle.getDecisionTreeLibIconStyle();
+            break;
+        case "flowLib":
+            data._icon=Styles.frameStyle.getFlowLibIcon();
+            data._style=Styles.frameStyle.getFlowLibIconStyle();
+            break;
+        case "ul":
+            data._icon=Styles.frameStyle.getUlIcon();
+            data._style=Styles.frameStyle.getUlIconStyle();
+            data.editorPath="/uleditor";
+            break;
+        case "decisionTable":
+            data._icon=Styles.frameStyle.getDecisionTableIcon();
+            data._style=Styles.frameStyle.getDecisionTableIconStyle();
+            data.editorPath="/decisiontableeditor";
+            break;
+        case "scriptDecisionTable":
+            data._icon=Styles.frameStyle.getScriptDecisionTableIcon();
+            data._style=Styles.frameStyle.getScriptDecisionTableIconStyle();
+            data.editorPath="/scriptdecisiontableeditor";
+            break;
+        case "decisionTree":
+            data._icon=Styles.frameStyle.getDecisionTreeIcon();
+            data._style=Styles.frameStyle.getDecisionTreeIconStyle();
+            data.editorPath="/decisiontreeditor";
+            break;
+        case "flow":
+            data._icon=Styles.frameStyle.getFlowIcon();
+            data._style=Styles.frameStyle.getFlowIconStyle();
+            data.editorPath="/floweditor";
+            break;
+        case "scorecard":
+            data._icon=Styles.frameStyle.getScorecardIcon();
+            data._style=Styles.frameStyle.getScorecardIconStyle();
+            data.editorPath="/scorecardeditor";
+            break;
+    }
+    var children=data.children;
+    if(children){
+        children.forEach((child,index)=>{
+            buildData(child);
+        });
+    }
+};

+ 15 - 0
urule-console-js/src/components/componentEvent.js

@@ -0,0 +1,15 @@
+/**
+ * Created by jacky on 2016/6/18.
+ */
+import events from 'events';
+export const SHOW_LOADING='show_loading';
+export const HIDE_LOADING='hide_loading';
+export const TREE_NODE_CLICK='tree_node_click';
+export const TREE_DIR_NODE_CLICK='tree_dir_node_click';
+export const OPEN_KNOWLEDGE_TREE_DIALOG='open_knowledge_tree_dialog';
+export const HIDE_KNOWLEDGE_TREE_DIALOG='hide_knowledge_tree_dialog';
+export const OPEN_VERSION_SELECT_DIALOG='open_version_select_dialog';
+export const HIDE_VERSION_SELECT_DIALOG='hide_version_select_dialog';
+
+export const eventEmitter=new events.EventEmitter();
+

+ 57 - 0
urule-console-js/src/components/dialog/component/CommonDialog.jsx

@@ -0,0 +1,57 @@
+/**
+ * Created by Jacky.gao on 2016/5/27.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+
+export default class CommonDialog extends Component{
+    componentDidMount(){
+        const $dom=$(ReactDOM.findDOMNode(this));
+        $dom.on('show.bs.modal',function () {
+            let zIndex=1050;
+            $(document).find('.modal').each(function (index,modal) {
+                const zindex=$(modal).css('z-index');
+                if(zindex && zindex!=='' && !isNaN(zindex)){
+                    const z=parseInt(zindex);
+                    if(z>zIndex){
+                        zIndex=z;
+                    }
+                }
+            });
+            $dom.css('z-index',zIndex+1);
+        });
+    }
+    render(){
+        const buttons=[];
+        this.props.buttons.forEach((btn,index)=>{
+            buttons.push(<button type="button" key={index} className={btn.className} onClick={(e)=>{
+                btn.click(this.props.dispatch);
+            }}><i className={btn.icon}></i> {btn.name}</button>)
+        });
+        const large=this.props.large;
+        const className='modal-dialog'+ (large ? ' modal-lg' : '');
+        return (
+            <div className='modal fade' tabIndex="-1" role="dialog" aria-hidden="true" style={{'overflow':'auto'}}>
+                <div className={className}>
+                    <div className="modal-content">
+                        <div className="modal-header">
+                            <button type="button" className="close" data-dismiss="modal" aria-hidden="true">
+                                &times;
+                            </button>
+                            <h4 className="modal-title" id="myModalLabel">
+                                {this.props.title}
+                                <div className="text-danger" style={{fontSize:'12pt'}}>{this.props.info ? this.props.info : null}</div>
+                            </h4>
+                        </div>
+                        <div className="modal-body" style={{padding:'10px'}}>
+                            {this.props.body}
+                        </div>
+                        <div className="modal-footer">
+                            {buttons}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+};

+ 78 - 0
urule-console-js/src/components/dialog/component/Dialog.jsx

@@ -0,0 +1,78 @@
+/**
+ * Created by Jacky.gao on 2016/5/27.
+ */
+import React,{Component,PropTypes} from 'react';
+import * as event from '../../../frame/event.js';
+import ReactDOM from 'react-dom';
+
+export default class Dialog extends Component{
+    constructor(props){
+        super(props);
+        this.state={title:this.props.title || '',buttons:this.props.buttons || [],body:this.props.body || []};
+    }
+    componentDidMount(){
+        event.eventEmitter.on(event.OPEN_DIALOG,(data)=>{
+            const title=data.title || this.state.title;
+            const body=data.body || this.state.body;
+            const buttons=data.buttons || this.state.buttons;
+            const init=data.init,destroy=data.destroy;
+            this.setState({title,body,buttons,init,destroy});
+            $(ReactDOM.findDOMNode(this)).modal('show');
+        });
+        event.eventEmitter.on(event.CLOSE_DIALOG,()=>{
+            $(ReactDOM.findDOMNode(this)).modal('hide');
+        });
+        event.eventEmitter.on(event.DIALOG_CONTNET_CHANGE,(data)=>{
+            const title=data.title || this.state.title;
+            const body=data.body || this.state.body;
+            const buttons=data.buttons || this.state.buttons;
+            this.setState({title,body,buttons});
+        });
+    }
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.OPEN_DIALOG);
+        event.eventEmitter.removeAllListeners(event.CLOSE_DIALOG);
+    }
+    componentDidUpdate(){
+        const init=this.state.init;
+        if(init){
+            init(this.props.dispatch);
+        }
+    }
+    componentWillUpdate(){
+        const destroy=this.state.destroy;
+        if(destroy){
+            destroy();
+        }
+    }
+    render(){
+        const buttons=[];
+        this.state.buttons.forEach((btn,index)=>{
+            buttons.push(<button type="button" key={index} className={btn.className} onClick={(e)=>{
+                btn.click(this.props.dispatch);
+            }}><i className={btn.icon}></i> {btn.name}</button>)
+        });
+        return (
+            <div className="modal fade" tabIndex="-1" role="dialog" aria-hidden="true">
+                <div className="modal-dialog">
+                    <div className="modal-content">
+                        <div className="modal-header">
+                            <button type="button" className="close" data-dismiss="modal" aria-hidden="true">
+                                &times;
+                            </button>
+                            <h4 className="modal-title" id="myModalLabel">
+                                {this.state.title}
+                            </h4>
+                        </div>
+                        <div className="modal-body">
+                            {this.state.body}
+                        </div>
+                        <div className="modal-footer">
+                            {buttons}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+};

+ 96 - 0
urule-console-js/src/components/dialog/component/KnowledgeTreeDialog.jsx

@@ -0,0 +1,96 @@
+/**
+ * Created by jacky on 2016/6/19.
+ */
+import '../../../css/iconfont.css';
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import CommonDialog from './CommonDialog.jsx';
+import CommonTree from '../../tree/component/CommonTree.jsx';
+import * as action from '../../componentAction.js';
+import * as event from '../../componentEvent.js';
+import VersionSelectDialog from './VersionSelectDialog.jsx';
+
+export default class KnowledgeTreeDialog extends Component {
+    constructor(props){
+        super(props);
+        this.state={title:'选择资源'};
+    }
+    componentDidMount(){
+        event.eventEmitter.on(event.OPEN_KNOWLEDGE_TREE_DIALOG,(config)=>{
+            this._config=config;
+            this.callback=config.callback;
+            action.loadResourceTreeData({project:config.project,forLib:config.forLib,fileType:config.fileType},function(data){
+                this.setState({data,fileType:config.fileType});
+                $("#_knowledge_tree_dialog_container").children('.modal').modal('show');
+            }.bind(this));
+        });
+        event.eventEmitter.on(event.HIDE_KNOWLEDGE_TREE_DIALOG,()=>{
+            $("#_knowledge_tree_dialog_container").children('.modal').modal('hide');
+        });
+        event.eventEmitter.on(event.TREE_NODE_CLICK,(nodeData)=>{
+            this.currentNodeData=nodeData;
+        });
+        event.eventEmitter.on(event.TREE_DIR_NODE_CLICK,(nodeData)=>{
+            this.currentNodeData=nodeData;
+        });
+    }
+    search(){
+        const searchFileName=$('.resSearchText').val();
+        const config=this._config;
+        action.loadResourceTreeData({project:config.project,forLib:config.forLib,fileType:config.fileType,searchFileName},function(data){
+            this.setState({data,fileType:config.fileType});
+        }.bind(this));
+    }
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.OPEN_KNOWLEDGE_TREE_DIALOG);
+        event.eventEmitter.removeAllListeners(event.HIDE_KNOWLEDGE_TREE_DIALOG);
+        event.eventEmitter.removeAllListeners(event.TREE_NODE_CLICK);
+    }
+    render(){
+        const body=(
+            <div className='tree' style={{marginLeft:'10px'}}>
+                <div>
+                    <input type="text" className="form-control resSearchText" placeholder="请输入要查询的文件名..." style={{display:'inline-block',width:'220px'}}></input>
+                    <a href="###" onClick={this.search.bind(this)} style={{margin:'6px',fontSize:'16px'}}><i className="glyphicon glyphicon-search"></i></a>
+                </div>
+                <CommonTree data={this.state.data} selectDir={this.props.selectDir}/>
+            </div>
+        );
+        const fileType=this.state.fileType || '';
+        const buttons=[
+            {
+                name:'确定',
+                className:'btn btn-danger',
+                icon:'glyphicon glyphicon-floppy-saved',
+                click:function () {
+                    if(this.currentNodeData){
+                        this.callback(this.currentNodeData.fullPath,'LATEST');
+                        event.eventEmitter.emit(event.HIDE_KNOWLEDGE_TREE_DIALOG);
+                    }else{
+                        bootbox.alert("请先选择一个文件");
+                    }
+                }.bind(this)
+            }
+        ];
+        buttons.push({
+            name:'选择版本',
+            className:'btn btn-primary',
+            icon:'glyphicon glyphicon-hand-up',
+            click:function () {
+                if(this.currentNodeData){
+                    event.eventEmitter.emit(event.OPEN_VERSION_SELECT_DIALOG,{file:this.currentNodeData.fullPath,callback:this.callback});
+                }else{
+                    bootbox.alert("请先选择一个文件");
+                }
+            }.bind(this)
+        });
+        return (
+            <div>
+                <VersionSelectDialog/>
+                <div id="_knowledge_tree_dialog_container">
+                    <CommonDialog title={this.state.title} body={body} buttons={buttons}/>
+                </div>
+            </div>
+        );
+    }
+}

+ 67 - 0
urule-console-js/src/components/dialog/component/VersionSelectDialog.jsx

@@ -0,0 +1,67 @@
+/**
+ * Created by jacky on 2016/6/20.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import Grid from '../../../components/grid/component/Grid.jsx';
+import CommonDialog from './CommonDialog.jsx';
+import * as event from '../../componentEvent.js';
+import * as action from '../../componentAction.js';
+
+export default class VersionSelectDialog extends Component{
+    constructor(props){
+        super(props);
+        this.state={title:''};
+    }
+    componentDidMount(){
+        event.eventEmitter.on(event.OPEN_VERSION_SELECT_DIALOG,(config)=>{
+            var file=config.file;
+            this.callback=config.callback;
+            this.file=file;
+            action.loadFileVersions(file,function(data){
+                $(ReactDOM.findDOMNode(this)).modal('show');
+                this.setState({data,title:"选择文件["+file+"]的版本"});
+            }.bind(this));
+        });
+        event.eventEmitter.on(event.HIDE_VERSION_SELECT_DIALOG,()=>{
+            $(ReactDOM.findDOMNode(this)).modal('hide');
+        });
+        $(ReactDOM.findDOMNode(this)).modal('hide');
+    }
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.OPEN_VERSION_SELECT_DIALOG);
+        event.eventEmitter.removeAllListeners(event.HIDE_VERSION_SELECT_DIALOG);
+    }
+    render(){
+        const headers=[
+            {id:'v-name',name:'name',label:'版本名称',filterable:true,width:'100px'},
+            {id:'v-comment',name:'comment',label:'版本描述'},
+            {id:'v-createUser',name:'createUser',label:'创建人',filterable:true,width:'140px'},
+            {id:'v-createDate',name:'createDate',label:'创建日期',width:'140px',dateFormat:'yyyy-MM-dd HH:mm:ss'}
+        ];
+
+        const operationConfig={
+            width:'75px',
+            operations:[
+                {
+                    label:'选择该版本',
+                    icon:'rf rf-select',
+                    style:{fontSize:'18px',color:'#337ab7',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex,rowData){
+                        this.callback(this.file,rowData.name);
+                        event.eventEmitter.emit(event.HIDE_VERSION_SELECT_DIALOG);
+                        event.eventEmitter.emit(event.HIDE_KNOWLEDGE_TREE_DIALOG);
+                    }.bind(this)
+                }
+            ]
+        };
+
+        const body=(
+            <Grid headers={headers} operationConfig={operationConfig} rows={this.state.data || []}/>
+        );
+        const buttons=[];
+        return (
+            <CommonDialog title={this.state.title} body={body} buttons={buttons} large={true}/>
+        );
+    }
+}

+ 172 - 0
urule-console-js/src/components/frametab/component/FrameTab.jsx

@@ -0,0 +1,172 @@
+/**
+ * Created by Jacky.gao on 2016/5/24.
+ */
+import React,{Component,PropTypes} from 'react';
+import QuickStart from '../../../frame/QuickStart.js';
+import IFrame from './IFrame.jsx';
+import * as event from '../../componentEvent.js';
+import * as action from '../../../frame/action.js';
+import Menu from '../../menu/component/Menu.jsx';
+import {nextIFrameId} from '../../../Utils.js';
+
+export default class FrameTab extends Component{
+    constructor(props){
+        super(props);
+        this.state={data:[]};
+    }
+    addTab(newTabData){
+        let data=this.state.data,exist=false,fullPath=this._processFullPath(newTabData.fullPath);
+        for(let item of data){
+            if(this._processFullPath(item.fullPath)===fullPath){
+                exist=true;
+            }
+        }
+        if(exist){
+            setTimeout(function() {
+                $('#tabLink'+fullPath).trigger('click');
+            },100);
+            return;
+        }
+        if(!exist){
+            data.push(newTabData);
+            this.setState({data});
+        }
+        setTimeout(function() {
+            $('#tabLink'+fullPath).trigger('click');
+        },100);
+        const menuId='tabmenu'+fullPath;
+        setTimeout(function () {
+            $('#li'+fullPath).contextmenu({
+                target:'#'+menuId
+            });
+        },100);
+    }
+
+    componentDidMount(){
+        event.eventEmitter.on(event.TREE_NODE_CLICK,(data)=>{
+           this.addTab(data);
+        });
+    };
+
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.TREE_NODE_CLICK);
+    }
+
+    _processFullPath(fullPath){
+        fullPath=fullPath.replace(new RegExp('/','gm'),'');
+        fullPath=fullPath.replace(new RegExp('\\.','gm'),'');
+        fullPath=fullPath.replace(new RegExp(':','gm'),'');
+        return fullPath;
+    }
+    
+    render(){
+        const data=this.state.data,{welcomePage}=this.props;
+        let tabs=[],tabContainers=[];
+        data.forEach((item,index)=>{
+            const fullPath=this._processFullPath(item.fullPath),tabContainerId='iframeTab-'+fullPath,tableContainerLink='#'+tabContainerId;
+            const active='',paneClass='tab-pane '+active,key='key'+fullPath;
+            const liId='li'+fullPath,linkId='tabLink'+fullPath,menuId='tabmenu'+fullPath;
+            const iframeId=nextIFrameId();
+            tabContainers.push(
+                <div className={paneClass} id={tabContainerId}  key={key}>
+                    <IFrame id={iframeId} path={item.path}/>
+                </div>
+            );
+            const fileName=item.name;
+            const pointPos=fileName.indexOf('.');
+            const fileType=fileName.substring(pointPos+1,fileName.length);
+            let type='';
+            if(fileType==='推送客户端配置'){
+                type='>>'+item.project;
+            }else if(fileType==='资源权限配置'){
+                type='AUTH';
+            }else if(fileType==='客户端访问权限配置'){
+                type='AUTH';
+            }else{
+                type=action.buildType(fileType);
+            }
+            if(type==='package'){
+                type=item.fullPath.substring(1,item.fullPath.length);
+            }
+            tabs.push(
+                <li id={liId} className={active} key={key}>
+                    <a id={linkId} href={tableContainerLink} data-toggle="tab">
+                        <button className="close closeTab" type="button" style={{marginLeft:'5px'}} onClick={()=>{
+                            const frame=$(`#${iframeId}`).get(0);
+                            if(frame && frame.contentWindow && frame.contentWindow._dirty){
+                                const result=confirm('当前页面内容未保存,确实要关闭吗?');
+                                if(!result){
+                                    return;
+                                }
+                            }
+                            let pos=data.indexOf(item);
+                            data.splice(pos,1);
+                            let nextLinkId;
+                            if(pos>0){
+                                nextLinkId ='tabLink'+this._processFullPath(data[pos-1].fullPath);
+                            }else if(data.length>0){
+                                data[data.length-1].active=true;
+                                nextLinkId ='tabLink'+this._processFullPath(data[data.length-1].fullPath);
+                            }
+                            this.setState({data});
+                            if(nextLinkId){
+                                setTimeout(function() {
+                                      $('#'+nextLinkId).trigger('click');
+                                    },100);
+                            }
+                        }}>×</button>
+                        {(type==='AUTH') ? fileName : type+':'+fileName}
+                    </a>
+                    {
+                        <Menu menuId={menuId} items={[
+                            {
+                                name:'关闭所有标签页',
+                                click:function () {
+                                    data.splice(0,data.length);
+                                    this.setState({data});
+                                }.bind(this)
+                            },
+                            {
+                                name:'关闭其它标签页',
+                                click:function () {
+                                    data.splice(0,data.length);
+                                    data.push(item);
+                                    this.setState({data});
+                                    setTimeout(function() {
+                                      $('#'+linkId).trigger('click');
+                                    },100);
+                                }.bind(this)
+                            }
+                        ]} data={{id:'tab'+fullPath}}/>
+                    }
+                </li>
+            );
+        });
+        if(tabs.length===0){
+            if(welcomePage && welcomePage.length>0){
+                if(welcomePage==='none'){
+                    return (<div/>);
+                }else{
+                    return (
+                        <iframe frameBorder="0" style={{border:0,width:'100%',height:'100%'}} src={welcomePage}></iframe>
+                    );
+                }
+            }else{
+                return <QuickStart/>;
+            }
+        }else{
+            return (
+                <div>
+                    <div>
+                        <ul className="nav nav-tabs" id='fornavframetab_' style={{fontSize:"12px"}}>
+                            {tabs}
+                        </ul>
+                    </div>
+                    <div className="tab-content">
+                        {tabContainers}
+                    </div>
+                </div>
+            );
+        }
+    }
+};

+ 33 - 0
urule-console-js/src/components/frametab/component/IFrame.jsx

@@ -0,0 +1,33 @@
+/**
+ * Created by Jacky.gao on 2016/5/24.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import * as event from '../../componentEvent.js';
+
+export default class IFrame extends Component{
+    componentDidMount(){
+        const $iframe=$(ReactDOM.findDOMNode(this));
+        $iframe.ready(function(){
+            event.eventEmitter.emit(event.SHOW_LOADING);
+        });
+        $iframe.load(function(){
+            event.eventEmitter.emit(event.HIDE_LOADING);
+        });
+        $iframe.css({
+            height:($(document).height()-47)+"px"
+        });
+        $(window).resize(()=>{
+            $iframe.css({
+                height:$(document).height()+"px"
+            });
+        });
+    }
+    render(){
+        const path=encodeURI(encodeURI(this.props.path));
+        const iframeId=this.props.id;
+        return (
+            <iframe src={path} id={iframeId} style={{width:'100%',border:0}} frameBorder="none"></iframe>
+        );
+    }
+}

+ 132 - 0
urule-console-js/src/components/grid/component/Cell.jsx

@@ -0,0 +1,132 @@
+/**
+ * Created by Jacky.gao on 2016/5/30.
+ */
+import React,{Component} from 'react';
+import ReactDOM from 'react-dom';
+import * as componentEvent from '../componentEvent.js';
+import {formatDate} from '../../../Utils.js';
+import {MsgBox} from 'flowdesigner';
+
+export default class Cell extends Component{
+    constructor(props){
+        super(props);
+    }
+    _storeData(){
+
+    }
+    _showEditor(colName){
+        const rowData=this.props.rowData;
+        var $dom=$(ReactDOM.findDOMNode(this));
+        if(!this.cellEditor){
+            this.cellEditor=null;
+            switch (rowData._editorType){
+                case 'number':
+                    this.cellEditor=$('<input type="number" class="form-control" style="height:31px">');
+                    break;
+                case 'date':
+                    this.cellEditor=$('<input type="date" class="form-control" style="height:31px">');
+                    break;
+                case 'boolean':
+                    this.cellEditor=$('<select class="form-control" style="height:31px"><option value="true">true</option><option value="false" selected="selected">false</option></select>');
+                    break;
+                case 'list':
+                    this.cellEditor=$('<input type="text" class="form-control" style="height:31px" title="双击打开窗口编辑列表值">');
+                    this.cellEditor.dblclick(function(){
+                        const _this=this;
+                        let editorValue=rowData[colName];
+                        if(!editorValue || editorValue===''){
+                            editorValue='{}';
+                        }
+                        let cellJsonData=JSON.parse(editorValue) || {};
+                        let rows= cellJsonData.rows || [],targetType=cellJsonData.type;
+                        const callback=function(rows){
+                            const jsonData={type:targetType,rows};
+                            const data=JSON.stringify(jsonData);
+                            rowData[colName]=data;
+                            var $div=$dom.find('div');
+                            $div.html(data);
+                        };
+                        if(!targetType){
+                            const simulatorCategoryData=window.simulatorCategoryData || [];
+                            const categorySelect=$(`<select class="form-control"></select>`);
+                            for(let category of simulatorCategoryData){
+                                categorySelect.append(`<option value="${category.clazz}">${category.name}</option>`);
+                            }
+                            categorySelect.append(`<option value="" selected></option>`);
+                            MsgBox.dialog('选择子对象类型',categorySelect,function(){
+                                targetType=categorySelect.val();
+                                if(!targetType || targetType===''){
+                                    MsgBox.alert('请先选择子对象类型!');
+                                    return;
+                                }
+                                let categoryTarget=null;
+                                for(let category of simulatorCategoryData){
+                                    if(targetType===category.clazz){
+                                        categoryTarget=category;
+                                        break;
+                                    }
+                                }
+                                const variables=categoryTarget.variables || [];
+                                componentEvent.eventEmitter.emit(componentEvent.SHOW_CHILD_LIST_DIALOG,{variables,rows,callback});
+                            });
+                        }else{
+                            let categoryTarget=null;
+                            for(let category of simulatorCategoryData){
+                                if(targetType===category.clazz){
+                                    categoryTarget=category;
+                                    break;
+                                }
+                            }
+                            const variables=categoryTarget.variables || [];
+                            componentEvent.eventEmitter.emit(componentEvent.SHOW_CHILD_LIST_DIALOG,{variables,rows,callback});
+                        }
+                    });
+                    break;
+                default:
+                    this.cellEditor=$('<input type="text" class="form-control" style="height:31px">');
+                    break;
+            }
+            $dom.append(this.cellEditor);
+            this.cellEditor.blur(function(){
+                rowData[colName]=$(this).val();
+                var $div=$dom.find('div');
+                $div.html(rowData[colName]);
+                $div.show();
+                $(this).hide();
+            });
+        }
+        this.cellEditor.val(rowData[colName]);
+        this.cellEditor.show();
+        this.cellEditor.focus();
+    }
+    render(){
+        const {rowData,header}=this.props;
+        const dateFormat=header.dateFormat;
+        let data=rowData[header.name];
+        if(dateFormat){
+            var d=new Date(data);
+            data=formatDate(d,dateFormat);
+        }
+        if(data && (typeof data === 'object')){
+            data=JSON.stringify(data);
+        }
+        const styleObj={marginTop:'5px',minHeight:'26px',wordBreak:'break-all'};
+        return (
+            <td style={{padding:'1px 5px'}}>
+                    <div style={styleObj} onClick={(e)=>{
+                                if(header.editable){
+                                    const targetDiv=e.target;
+                                    $(targetDiv).css({display:'none'});
+                                    if(rowData._editorType){
+                                        this._showEditor(header.name);
+                                    }else{
+                                        componentEvent.eventEmitter.emit(componentEvent.SHOW_CELL_EDITOR,{targetDiv,rowData,colId:header.id})
+                                    }
+                                }
+                            }}>{data}
+                    </div>
+            </td>
+
+        );
+    }
+}

+ 73 - 0
urule-console-js/src/components/grid/component/CellEditor.jsx

@@ -0,0 +1,73 @@
+/**
+ * Created by Jacky.gao on 2016/5/30.
+ */
+import React,{Component} from 'react';
+import ReactDOM from 'react-dom';
+import * as componentEvent from '../componentEvent.js';
+
+export default class CellEditor extends Component{
+    constructor(props){
+        super(props);
+        this.state={display:'none'};
+    }
+    componentDidMount(){
+        componentEvent.eventEmitter.on(componentEvent.SHOW_CELL_EDITOR,(data)=>{
+            const header=this.props.header;
+            if(data.colId!==header.id){
+                return;
+            }
+            const $targetDiv=$(data.targetDiv);
+            const $targetTD=$targetDiv.parent();
+            const $dom=$(ReactDOM.findDOMNode(this));
+            $targetTD.append($dom);
+            const rowData=data.rowData;
+            $dom.val(rowData[header.name]);
+            this.setState({$targetDiv,rowData,display:''});
+        });
+    }
+    componentWillUnmount(){
+        componentEvent.eventEmitter.removeAllListeners(componentEvent.SHOW_CELL_EDITOR);
+    }
+    blur(){
+        const $targetDiv=this.state.$targetDiv;
+        if(!$targetDiv)return;
+        $targetDiv.css({display:''});
+        const value=ReactDOM.findDOMNode(this).value;
+        $targetDiv.html(value);
+        const header=this.props.header;
+        const rowData=this.state.rowData;
+        rowData[header.name]=value;
+        this.setState({display:'none'});
+    }
+    componentDidUpdate(){
+        if(this.state.display===''){
+            ReactDOM.findDOMNode(this).focus();
+        }
+    }
+    render(){
+        const styleObj={display:this.state.display,height:'31px',padding:'0px 5px'};
+        const header=this.props.header;
+        const {editorType,selectData}=header;
+        switch (editorType){
+            case "select":
+                return (<select style={styleObj} onBlur={this.blur.bind(this)} className="form-control">
+                    {selectData.map((option,index)=>{
+                        return (<option key={index}>{option}</option>);
+                    })}
+                </select>);
+            case "boolean":
+                return (
+                    <select  onBlur={this.blur.bind(this)} className="form-control">
+                        <option value="true">true</option>
+                        <option value="false" selected="selected">false</option>
+                    </select>
+                );
+            case "date":
+                return (<input style={styleObj} onBlur={this.blur.bind(this)} type="date" className="form-control"></input>);
+            case "number":
+                return (<input style={styleObj} onBlur={this.blur.bind(this)} type="number" className="form-control"></input>);
+            default:
+                return (<input style={styleObj} onBlur={this.blur.bind(this)} type="text" className="form-control"></input>);
+        }
+    }
+};

+ 66 - 0
urule-console-js/src/components/grid/component/ChildListDialog.jsx

@@ -0,0 +1,66 @@
+/**
+ * Created by jacky on 2016/9/4.
+ */
+import React,{Component} from 'react';
+import ReactDOM from 'react-dom';
+import Grid from './Grid.jsx';
+import CommonDialog from '../../dialog/component/CommonDialog.jsx';
+import * as event from '../componentEvent.js';
+
+export default class ChildListDialog extends Component{
+    constructor(props){
+        super(props);
+        this.state={rows:[],variables:[]};
+    }
+    componentDidMount(){
+        event.eventEmitter.on(event.SHOW_CHILD_LIST_DIALOG,config=>{
+            $(ReactDOM.findDOMNode(this)).modal('show');
+            this.setState({rows:config.rows,variables:config.variables,callback:config.callback});
+        });
+    }
+    render(){
+        const {variables,rows,callback} = this.state;
+        const headers=[],_this=this;
+        for(let item of variables){
+            headers.push({id:`list-${item.name}`,name:item.name,label:item.label,editable:true,width:'200px'});
+        }
+        const gridOperationCol={
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'20px',color:'#d9534f',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex,rowData){
+                        const pos=rows.indexOf(rowData);
+                        rows.splice(pos,1);
+                        _this.setState({rows});
+                    }
+                }
+            ]
+        };
+        const buttons=[
+            {
+                name:'添加记录',
+                className:'btn btn-primary',
+                click:function(){
+                    rows.push({});
+                    _this.setState({rows});
+                }
+            },
+            {
+                name:'确定',
+                className:'btn btn-danger',
+                click:function(){
+                    callback(rows);
+                    $(ReactDOM.findDOMNode(_this)).modal('hide');
+                }
+            }
+        ];
+        const body=(
+            <div style={{overflow:'scroll',padding:'10px'}}>
+                <Grid headers={headers} rows={rows} operationConfig={gridOperationCol} uniqueKey={true}/>
+            </div>
+        );
+        return (<CommonDialog title="定义子对象" body={body} buttons={buttons} large={true} holdState={true}/>);
+    }
+}

+ 123 - 0
urule-console-js/src/components/grid/component/Grid.jsx

@@ -0,0 +1,123 @@
+/**
+ * Created by Jacky.gao on 2016/5/30.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import Row from './Row.jsx';
+import CellEditor from './CellEditor.jsx';
+import {uniqueID} from '../../componentAction.js';
+
+class Grid extends Component{
+    constructor(props){
+        super(props);
+        this.state={display:'none'};
+        this.filterData={};
+    }
+    onFilter(colIndex,e){
+        console.log(e.key);
+        if(e.key!=='Enter'){
+            return;
+        }
+        const value=e.target.value;
+        const name=e.target.name;
+        var oldData=this.filterData[name];
+        if(value===oldData){
+            return;
+        }
+        this.filterData[name]=value;
+        const $dom=$(ReactDOM.findDOMNode(this));
+        const $tbody=$dom.children('tbody');
+        const $trs=$tbody.children('tr');
+        for(var i=1;i<$trs.length;i++){
+            const $tr=$($trs[i]);
+            let contain=false;
+            $tr.children('td').each((index,td)=>{
+                let $td=$(td);
+                if(index===colIndex){
+                    let content=$td.children('div').html();
+                    if(content.indexOf(value)>-1){
+                        contain=true;
+                    }
+                    return false;
+                }
+            });
+            if(!contain){
+                $tr.hide();
+            }else{
+                $tr.show();
+            }
+        }
+    }
+    render(){
+        const {headers,operationConfig,dispatch,selectFirst,uniqueKey}=this.props;
+        const rows=this.props.rows || [];
+        const headerContent=[];
+        const bodyContent=[];
+        headers.forEach((header,index)=>{
+            if(header.editable){
+                headerContent.push(
+                    <td key={uniqueID()} style={{width:header.width}}>
+                        <label>{header.label}</label>
+                        <CellEditor onchange={this.props.onchange} onblur={(e)=>{
+                         this.setState({display:'none'});
+                         this.props.onblur(e);
+                    }} header={header} display={this.state.display}/>
+                    </td>
+                );
+            }else{
+                headerContent.push(
+                    <td key={uniqueID()} style={{width:header.width}}>
+                        <label>{header.label}</label>
+                    </td>
+                );
+            }
+        });
+        if(operationConfig){
+            headerContent.push(
+                <td key={uniqueID()} style={{width:operationConfig.width}}><label>操作列</label></td>
+            )
+        }
+        const filterRow=(
+            <tr key='filterrow' style={{background:'#eee'}}>
+                {headers.map((header,index)=>{
+                    if(header.filterable){
+                        return (<td key={uniqueID()}>
+                            <input type="text" onKeyPress={this.onFilter.bind(this,index)} name={header.id} className="form-control" style={{height:'26px'}} placeholder='请输入过滤条件,回车查询...'></input>
+                        </td>);
+                    }else{
+                        return (<td key={uniqueID()}>&nbsp;</td>);
+                    }
+                })}
+                {operationConfig ? (<td></td>) : null}
+            </tr>
+        );
+        bodyContent.push(filterRow);
+        rows.forEach((row,rowIndex)=>{
+            if(!row.id){
+                row.id=uniqueID();
+            }
+            var rowKey=row.id;
+            if(rowIndex===0 && selectFirst){
+                bodyContent.push(
+                    <Row key={rowKey} select={true} ready={this.props.ready} headers={headers} dispatch={dispatch} rowClick={this.props.rowClick} operations={operationConfig ? operationConfig.operations : null} rowData={row} rowIndex={rowIndex}/>
+                );
+            }else{
+                bodyContent.push(
+                    <Row key={rowKey} ready={this.props.ready} headers={headers} dispatch={dispatch} rowClick={this.props.rowClick} operations={operationConfig ? operationConfig.operations : null} rowData={row} rowIndex={rowIndex}/>
+                );
+            }
+        });
+        const tableStyle={margin:0,width:(this.props.width ? this.props.width : '100%')};
+        return (
+            <table className="table table-bordered" style={tableStyle}>
+                <thead><tr className="well">{headerContent}</tr></thead>
+                <tbody>{bodyContent}</tbody>
+            </table>
+        );
+    }
+};
+Grid.propTypes={
+    headers:React.PropTypes.array.isRequired,
+    operationConfig:PropTypes.object
+};
+export default Grid;

+ 61 - 0
urule-console-js/src/components/grid/component/Row.jsx

@@ -0,0 +1,61 @@
+/**
+ * Created by Jacky.gao on 2016/6/3.
+ */
+import '../css/grid.css';
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import Cell from './Cell.jsx';
+import {uniqueID} from '../../componentAction.js';
+
+export default class Row extends Component{
+    componentDidMount(){
+        const $dom=$(ReactDOM.findDOMNode(this));
+        const {rowData,rowIndex,rowClick}=this.props;
+        $dom.click(function(e){
+            if(rowClick){
+                rowClick(rowData,rowIndex);
+            }
+            $(this).addClass("bg-warning").siblings().removeClass("bg-warning");
+        });
+    }
+    render(){
+        const {headers,rowData,rowIndex,operations,select}=this.props;
+        const tds=[];
+        headers.forEach((header,headerIndex)=>{
+            tds.push(
+                <Cell key={uniqueID()} onchange={(newValue)=>{
+                                rowData[header.name]=newValue;
+                            }} rowData={rowData} header={header}/>
+            );
+        });
+        if(operations){
+            const key='op-td'+rowIndex;
+            tds.push(
+                <td key={uniqueID()} style={{padding:"5px 5px"}}>
+                    {
+                        operations.map((op,index)=>{
+                        if(op.icon){
+                            return (
+                                <i key={uniqueID()} className={op.icon} title={op.label} style={op.style} onClick={op.click.bind(this,rowIndex,rowData)}></i>
+                             );
+                        }else{
+                            return (
+                                <button key={uniqueID()} type="button" className="btn btn-link" style={{padding:'0px 1px'}}
+                                        onClick={op.click.bind(this,rowIndex,rowData)}>{op.label}
+                                </button>
+                            );
+                        }
+                        })
+                    }
+                </td>
+            );
+        }
+        let trClass=select ? 'bg-warning' : '';
+        trClass+=' content-tr';
+        return (
+            <tr style={{height:'26px'}} className={trClass}>
+            {tds}
+            </tr>
+        );
+    }
+}

+ 12 - 0
urule-console-js/src/components/grid/componentEvent.js

@@ -0,0 +1,12 @@
+/**
+ * Created by jacky on 2016/6/8.
+ */
+import events from 'events';
+
+export const SHOW_CELL_EDITOR='show_cell_editor';
+export const HIDE_CELL_EDITOR='hide_cell_editor';
+
+export const SHOW_CHILD_LIST_DIALOG='show_child_list_dialog';
+export const HIDE_CHILD_LIST_DIALOG='hide_child_list_dialog';
+
+export const eventEmitter=new events.EventEmitter();

+ 3 - 0
urule-console-js/src/components/grid/css/grid.css

@@ -0,0 +1,3 @@
+.content-tr:hover{
+    background: #fcf8e3;
+}

+ 66 - 0
urule-console-js/src/components/loading/component/Loading.jsx

@@ -0,0 +1,66 @@
+/**
+ * Created by jacky on 2016/6/18.
+ */
+import '../css/loading.css';
+import * as event from '../../componentEvent.js';
+import React,{Component,PropTypes} from 'react';
+export default class Loading extends Component{
+    constructor(){
+        super();
+        this.state={content:'数据加载中...'};
+    }
+    componentDidMount(){
+        event.eventEmitter.on(event.SHOW_LOADING,content=>{
+            const $window=$(window);
+            const width=$window.width();
+            const height=$window.height();
+            if(content){
+                this.setState({content});
+            }else{
+                this.setState({content:'数据加载中...'});
+            }
+            $('.loading-cover').css({height:height,width:width}).fadeIn();
+        });
+        event.eventEmitter.on(event.HIDE_LOADING,()=>{
+            $('.loading-cover').fadeOut();
+        });
+    }
+    componentWillUnmount(){
+        event.eventEmitter.removeAllListeners(event.SHOW_LOADING);
+        event.eventEmitter.removeAllListeners(event.HIDE_LOADING);
+    }
+    render(){
+        const $window=$(window);
+        const width=$window.width();
+        const height=$window.height();
+        const show=this.props.show;
+        const styleObj={width:width,height:height,display:show?'block':'none'};
+        const coverTop=(height/2)-20;
+        const coverLeft=(width/2)-20;
+        const loadStyle={marginTop:coverTop,marginLeft:coverLeft,width:'40px',height:'40px'};
+        return (
+            <div className="loading-cover" style={styleObj} >
+                <div className="spinner" style={loadStyle}>
+                    <div className="spinner-container container1">
+                        <div className="circle1"></div>
+                        <div className="circle2"></div>
+                        <div className="circle3"></div>
+                        <div className="circle4"></div>
+                    </div>
+                    <div className="spinner-container container2">
+                        <div className="circle1"></div>
+                        <div className="circle2"></div>
+                        <div className="circle3"></div>
+                        <div className="circle4"></div>
+                    </div>
+                    <div className="spinner-container container3">
+                        <div className="circle1"></div>
+                        <div className="circle2"></div>
+                        <div className="circle3"></div>
+                        <div className="circle4"></div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}

+ 119 - 0
urule-console-js/src/components/loading/css/loading.css

@@ -0,0 +1,119 @@
+.loading-cover {
+    position: absolute;
+    left:0;
+    top:0;
+    display: none;
+    background: rgba(153, 153, 153, 0.5);
+    z-index: 10000000;
+}
+
+.spinner {
+    margin: 100px auto;
+    width: 20px;
+    height: 20px;
+    position: relative;
+}
+
+.container1 > div, .container2 > div, .container3 > div {
+    width: 10px;
+    height: 10px;
+    background-color: #019dff;
+
+    border-radius: 100%;
+    position: absolute;
+    -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
+    animation: bouncedelay 1.2s infinite ease-in-out;
+    -webkit-animation-fill-mode: both;
+    animation-fill-mode: both;
+}
+
+.spinner .spinner-container {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+}
+
+.container2 {
+    -webkit-transform: rotateZ(45deg);
+    transform: rotateZ(45deg);
+}
+
+.container3 {
+    -webkit-transform: rotateZ(90deg);
+    transform: rotateZ(90deg);
+}
+
+.circle1 { top: 0; left: 0; }
+.circle2 { top: 0; right: 0; }
+.circle3 { right: 0; bottom: 0; }
+.circle4 { left: 0; bottom: 0; }
+
+.container2 .circle1 {
+    -webkit-animation-delay: -1.1s;
+    animation-delay: -1.1s;
+}
+
+.container3 .circle1 {
+    -webkit-animation-delay: -1.0s;
+    animation-delay: -1.0s;
+}
+
+.container1 .circle2 {
+    -webkit-animation-delay: -0.9s;
+    animation-delay: -0.9s;
+}
+
+.container2 .circle2 {
+    -webkit-animation-delay: -0.8s;
+    animation-delay: -0.8s;
+}
+
+.container3 .circle2 {
+    -webkit-animation-delay: -0.7s;
+    animation-delay: -0.7s;
+}
+
+.container1 .circle3 {
+    -webkit-animation-delay: -0.6s;
+    animation-delay: -0.6s;
+}
+
+.container2 .circle3 {
+    -webkit-animation-delay: -0.5s;
+    animation-delay: -0.5s;
+}
+
+.container3 .circle3 {
+    -webkit-animation-delay: -0.4s;
+    animation-delay: -0.4s;
+}
+
+.container1 .circle4 {
+    -webkit-animation-delay: -0.3s;
+    animation-delay: -0.3s;
+}
+
+.container2 .circle4 {
+    -webkit-animation-delay: -0.2s;
+    animation-delay: -0.2s;
+}
+
+.container3 .circle4 {
+    -webkit-animation-delay: -0.1s;
+    animation-delay: -0.1s;
+}
+
+@-webkit-keyframes bouncedelay {
+    0%, 80%, 100% { -webkit-transform: scale(0.0) }
+    40% { -webkit-transform: scale(1.0) }
+}
+
+@keyframes bouncedelay {
+    0%, 80%, 100% {
+        transform: scale(0.0);
+        -webkit-transform: scale(0.0);
+    } 40% {
+          transform: scale(1.0);
+          -webkit-transform: scale(1.0);
+      }
+}

+ 22 - 0
urule-console-js/src/components/menu/component/Menu.jsx

@@ -0,0 +1,22 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import React,{Component,PropTypes} from 'react';
+import MenuItem from './MenuItem.jsx';
+
+export default class Menu extends Component{
+    render(){
+        const {items,data,dispatch,menuId}=this.props;
+        let result=[];
+        items.forEach((item,index)=>{
+            result.push(
+                <MenuItem item={item} key={index} data={data} dispatch={dispatch}/>
+            );
+        });
+        return (
+            <div id={menuId}>
+                <ul className="dropdown-menu" style={{color:'rgb(11, 54, 106)'}}>{result}</ul>
+            </div>
+        );
+    }
+}

+ 28 - 0
urule-console-js/src/components/menu/component/MenuItem.jsx

@@ -0,0 +1,28 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+
+export default class MenuItem extends Component{
+    constructor(props){
+        super(props);
+    }
+    componentDidMount(){
+        const {item,data,dispatch}=this.props;
+        if(item.click){
+            const dom=ReactDOM.findDOMNode(this);
+            $(dom).click(function (e) {
+                item.click(data,dispatch);
+            });
+        }
+    }
+    render(){
+        const item=this.props.item;
+        return (
+            <li>
+                <a href='###'><i className={item.icon} style={{color:'#00A0E8'}}></i> {item.name}</a>
+            </li>
+        );
+    }
+}

+ 36 - 0
urule-console-js/src/components/splitter/component/Splitter.jsx

@@ -0,0 +1,36 @@
+/**
+ * Created by Jacky.gao on 2016/5/24.
+ */
+import '../css/jquery.splitter.css';
+import {} from './jquery-splitter.js';
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+
+export default class Splitter extends Component{
+    _sanitizeSplitterPositionValue(position) {
+        var newPosition = Math.min(position, window.innerWidth - this.minWidth);
+        newPosition = Math.max(newPosition, this.minWidth);
+        return newPosition;
+    }
+    componentDidMount(){
+        const $domNode=$(ReactDOM.findDOMNode(this));
+        const $splitter=$domNode.split(this.props);
+        $(window).resize(function(){
+            var newPosition = this._sanitizeSplitterPositionValue($splitter.position());
+            if (newPosition !== $splitter.position()) {
+                $splitter.position(newPosition);
+            }
+            let height=$(window).height();
+            $domNode.css({height:height});
+        }.bind(this));
+        let height=$(window).height();
+        $domNode.css({height:height});
+    }
+    render(){
+        return (
+            <div>
+                {this.props.children}
+            </div>
+        );
+    }
+}

+ 294 - 0
urule-console-js/src/components/splitter/component/jquery-splitter.js

@@ -0,0 +1,294 @@
+/*!
+ * JQuery Spliter Plugin
+ * Copyright (C) 2010-2013 Jakub Jankiewicz <http://jcubic.pl>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+(function ($, undefined) {
+    var count = 0;
+    var splitter_id = null;
+    var splitters = [];
+    var current_splitter = null;
+    $.fn.split = function (options) {
+        var data = this.data('splitter');
+        if (data) {
+            return data;
+        }
+
+        var panel_1;
+        var panel_2;
+        var settings = $.extend({
+            limit: 100,
+            orientation: 'horizontal',
+            position: '50%',
+            invisible: false,
+            onDragStart: $.noop,
+            onDragEnd: $.noop,
+            onDrag: $.noop
+        }, options || {});
+        this.settings = settings;
+        var cls;
+        var children = this.children();
+        if (settings.orientation == 'vertical') {
+            panel_1 = children.first().addClass('left_panel');
+            panel_2 = panel_1.next().addClass('right_panel');
+            cls = 'vsplitter';
+        } else if (settings.orientation == 'horizontal') {
+            panel_1 = children.first().addClass('top_panel')
+            panel_2 = panel_1.next().addClass('bottom_panel');
+            cls = 'hsplitter';
+        }
+        if (settings.invisible) {
+            cls += ' splitter-invisible';
+        }
+        var width = this.width();
+        var height = this.height();
+        var id = count++;
+        this.addClass('splitter_panel');
+        var splitter = $('<div/>').addClass(cls).bind('mouseenter touchstart', function () {
+            splitter_id = id;
+        }).bind('mouseleave touchend', function () {
+            splitter_id = null;
+        }).insertAfter(panel_1);
+        var position;
+
+        function get_position(position) {
+            if (typeof position === 'number') {
+                return position;
+            } else if (typeof position === 'string') {
+                var match = position.match(/^([0-9\.]+)(px|%)$/);
+                if (match) {
+                    if (match[2] == 'px') {
+                        return +match[1];
+                    } else {
+                        if (settings.orientation == 'vertical') {
+                            return (width * +match[1]) / 100;
+                        } else if (settings.orientation == 'horizontal') {
+                            return (height * +match[1]) / 100;
+                        }
+                    }
+                } else {
+                    //throw position + ' is invalid value';
+                }
+            } else {
+                //throw 'position have invalid type';
+            }
+        }
+
+        var self = $.extend(this, {
+            refresh: function () {
+                var new_width = this.width();
+                var new_height = this.height();
+                if (width != new_width || height != new_height) {
+                    width = this.width();
+                    height = this.height();
+                    self.position(position);
+                }
+            },
+            position: (function () {
+                if (settings.orientation == 'vertical') {
+                    return function (n, silent) {
+                        if (n === undefined) {
+                            return position;
+                        } else {
+                            position = get_position(n);
+                            var sw = splitter.width();
+                            var sw2 = sw / 2;
+                            if (settings.invisible) {
+                                var pw = panel_1.width(position).outerWidth();
+                                panel_2.width(self.width() - pw);
+                                splitter.css('left', pw - sw2);
+                            } else {
+                                var pw = panel_1.width(position - sw2).outerWidth();
+                                panel_2.width(self.width() - pw - sw);
+                                splitter.css('left', pw);
+                            }
+                        }
+                        if (!silent) {
+                            self.find('.splitter_panel').trigger('splitter.resize');
+                        }
+                        return self;
+                    };
+                } else if (settings.orientation == 'horizontal') {
+                    return function (n, silent) {
+                        if (n === undefined) {
+                            return position;
+                        } else {
+                            position = get_position(n);
+                            var sw = splitter.height();
+                            var sw2 = sw / 2;
+                            if (settings.invisible) {
+                                var pw = panel_1.height(position).outerHeight();
+                                panel_2.height(self.height() - pw);
+                                splitter.css('top', pw - sw2);
+                            } else {
+                                var pw = panel_1.height(position - sw2).outerHeight();
+                                panel_2.height(self.height() - pw - sw);
+                                splitter.css('top', pw);
+                            }
+                        }
+                        if (!silent) {
+                            self.find('.splitter_panel').trigger('splitter.resize');
+                        }
+                        return self;
+                    };
+                } else {
+                    return $.noop;
+                }
+            })(),
+            orientation: settings.orientation,
+            limit: settings.limit,
+            isActive: function () {
+                return splitter_id === id;
+            },
+            destroy: function () {
+                self.removeClass('splitter_panel');
+                splitter.unbind('mouseenter');
+                splitter.unbind('mouseleave');
+                splitter.unbind('touchstart');
+                splitter.unbind('touchmove');
+                splitter.unbind('touchend');
+                splitter.unbind('touchleave');
+                splitter.unbind('touchcancel');
+                if (settings.orientation == 'vertical') {
+                    panel_1.removeClass('left_panel');
+                    panel_2.removeClass('right_panel');
+                } else if (settings.orientation == 'horizontal') {
+                    panel_1.removeClass('top_panel');
+                    panel_2.removeClass('bottom_panel');
+                }
+                self.unbind('splitter.resize');
+                self.find('.splitter_panel').trigger('splitter.resize');
+                splitters[id] = null;
+                splitter.remove();
+                var not_null = false;
+                for (var i = splitters.length; i--;) {
+                    if (splitters[i] !== null) {
+                        not_null = true;
+                        break;
+                    }
+                }
+                //remove document events when no splitters
+                if (!not_null) {
+                    $(document.documentElement).unbind('.splitter');
+                    $(window).unbind('resize.splitter');
+                    self.data('splitter', null);
+                    splitters = [];
+                    count = 0;
+                }
+            }
+        });
+        self.bind('splitter.resize', function (e) {
+            var pos = self.position();
+            if (self.orientation == 'vertical' &&
+                pos > self.width()) {
+                pos = self.width() - self.limit - 1;
+            } else if (self.orientation == 'horizontal' &&
+                pos > self.height()) {
+                pos = self.height() - self.limit - 1;
+            }
+            if (pos < self.limit) {
+                pos = self.limit + 1;
+            }
+            self.position(pos, true);
+        });
+        //inital position of splitter
+        var pos;
+        if (settings.orientation == 'vertical') {
+            if (pos > width - settings.limit) {
+                pos = width - settings.limit;
+            } else {
+                pos = get_position(settings.position);
+            }
+        } else if (settings.orientation == 'horizontal') {
+            //position = height/2;
+            if (pos > height - settings.limit) {
+                pos = height - settings.limit;
+            } else {
+                pos = get_position(settings.position);
+            }
+        }
+        if (pos < settings.limit) {
+            pos = settings.limit;
+        }
+        self.position(pos, true);
+        if (splitters.length == 0) { // first time bind events to document
+            $(window).bind('resize.splitter', function () {
+                $.each(splitters, function (i, splitter) {
+                    splitter.refresh();
+                });
+            });
+            $(document.documentElement).bind('mousedown.splitter touchstart.splitter', function (e) {
+                if (splitter_id !== null) {
+                    current_splitter = splitters[splitter_id];
+                    $('<div class="splitterMask"></div>').css('cursor', current_splitter.children().eq(1).css('cursor')).insertAfter(current_splitter);
+                    current_splitter.settings.onDragStart(e);
+                    return false;
+                }
+            }).bind('mouseup.splitter touchend.splitter touchleave.splitter touchcancel.splitter', function (e) {
+                if (current_splitter) {
+                    $('.splitterMask').remove();
+                    current_splitter.settings.onDragEnd(e);
+                    current_splitter = null;
+                }
+            }).bind('mousemove.splitter touchmove.splitter', function (e) {
+                if (current_splitter !== null) {
+                    var limit = current_splitter.limit;
+                    var offset = current_splitter.offset();
+                    if (current_splitter.orientation == 'vertical') {
+                        var pageX = e.pageX;
+                        if (e.originalEvent && e.originalEvent.changedTouches) {
+                            pageX = e.originalEvent.changedTouches[0].pageX;
+                        }
+                        var x = pageX - offset.left;
+                        if (x <= current_splitter.limit) {
+                            x = current_splitter.limit + 1;
+                        } else if (x >= current_splitter.width() - limit) {
+                            x = current_splitter.width() - limit - 1;
+                        }
+                        if (x > current_splitter.limit &&
+                            x < current_splitter.width() - limit) {
+                            current_splitter.position(x, true);
+                            current_splitter.find('.splitter_panel').
+                                trigger('splitter.resize');
+                            e.preventDefault();
+                        }
+                    } else if (current_splitter.orientation == 'horizontal') {
+                        var pageY = e.pageY;
+                        if (e.originalEvent && e.originalEvent.changedTouches) {
+                            pageY = e.originalEvent.changedTouches[0].pageY;
+                        }
+                        var y = pageY - offset.top;
+                        if (y <= current_splitter.limit) {
+                            y = current_splitter.limit + 1;
+                        } else if (y >= current_splitter.height() - limit) {
+                            y = current_splitter.height() - limit - 1;
+                        }
+                        if (y > current_splitter.limit &&
+                            y < current_splitter.height() - limit) {
+                            current_splitter.position(y, true);
+                            current_splitter.find('.splitter_panel').
+                                trigger('splitter.resize');
+                            e.preventDefault();
+                        }
+                    }
+                    current_splitter.settings.onDrag(e);
+                }
+            });
+        }
+        splitters.push(self);
+        self.data('splitter', self);
+        return self;
+    };
+})(jQuery);

+ 59 - 0
urule-console-js/src/components/splitter/css/jquery.splitter.css

@@ -0,0 +1,59 @@
+/*!
+ * StyleSheet for JQuery splitter Plugin
+ * Copyright (C) 2010 Jakub Jankiewicz <http://jcubic.pl>
+ *
+ * Same license as plugin
+ */
+.splitter_panel {
+    position: relative;
+}
+.splitter_panel .vsplitter {
+    border-left:solid 1px #999;
+    border-right:solid 1px #999;
+    background-color: #f5f5f5;
+    cursor: col-resize;
+    z-index:900;
+    width: 7px;
+}
+
+.splitter_panel .hsplitter {
+    background-color: #5F5F5F;
+    cursor: row-resize;
+    z-index: 800;
+    height: 7px;
+}
+.splitter_panel .vsplitter.splitter-invisible,
+.splitter_panel .hsplitter.splitter-invisible {
+    background: none;
+}
+.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel,
+.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
+    position: absolute;
+    overflow: auto;
+}
+.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel {
+    height: 100%;
+}
+.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
+    width: 100%;
+}
+.splitter_panel .top_panel, .splitter_panel .left_panel, .splitter_panel .vsplitter {
+    top: 0;
+}
+.splitter_panel .top_panel, .splitter_panel .bottom_panel, .splitter_panel .left_panel, .splitter_panel .hsplitter {
+    left: 0;
+}
+.splitter_panel .bottom_panel {
+    bottom: 0;
+}
+.splitter_panel .right_panel {
+    right: 0;
+}
+.splitterMask {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 1000;
+}

BIN
urule-console-js/src/components/splitter/icons/hgrabber.gif


BIN
urule-console-js/src/components/splitter/icons/vgrabber.gif


+ 34 - 0
urule-console-js/src/components/tree/component/CommonTree.jsx

@@ -0,0 +1,34 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import '../css/tree.css';
+import '../../../css/iconfont.css';
+import TreeItem from './TreeItem.jsx';
+import React,{Component,PropTypes} from 'react';
+
+export default class CommonTree extends Component{
+    render(){
+        const {data,selectDir}=this.props;
+        if(data){
+            buildTreeDataLevel(data,1);
+            return (
+                <ul style={{paddingLeft:'20px'}}>
+                    <TreeItem data={data} selectDir={selectDir} expandLevel={this.props.expandLevel}/>
+                </ul>
+            );
+        }else{
+            return (<ul></ul>);
+        }
+    }
+};
+
+function buildTreeDataLevel(data,level) {
+    data._level=level++;
+    var children=data.children;
+    if(children){
+        children.forEach((child,index)=>{
+            buildTreeDataLevel(child,level);
+        });
+    }
+};
+CommonTree.defaultProps={ expandLevel:3};

+ 29 - 0
urule-console-js/src/components/tree/component/Tree.jsx

@@ -0,0 +1,29 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import '../css/tree.css';
+import '../../../css/iconfont.css';
+import TreeItem from './TreeItem.jsx';
+import React,{Component,PropTypes} from 'react';
+import {connect} from 'react-redux';
+
+class Tree extends Component{
+    render(){
+        const {data,dispatch,draggable}=this.props;
+        if(data){
+            return (
+                <ul style={{paddingLeft:'20px'}}>
+                    <TreeItem data={data} dispatch={dispatch} expandLevel={this.props.expandLevel} draggable={draggable}/>
+                </ul>
+            );
+        }else{
+            return (<ul></ul>);
+        }
+    }
+};
+Tree.defaultProps={expandLevel:3};
+function selector(state) {
+    let data=state.data;
+    return {data};
+};
+export default connect(selector)(Tree);

+ 120 - 0
urule-console-js/src/components/tree/component/TreeItem.jsx

@@ -0,0 +1,120 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import React,{Component,PropTypes} from 'react';
+import ReactDOM from 'react-dom';
+import TreeParentItem from './TreeParentItem.jsx';
+import Menu from '../../menu/component/Menu.jsx';
+import * as event from '../../componentEvent.js';
+
+class TreeItem extends Component{
+    componentDidMount(){
+        const $li=$(ReactDOM.findDOMNode(this));
+        const $childrenSpan= $li.children("span");
+        const {data,dispatch}=this.props;
+        const _this=this;
+        $childrenSpan.click(function (e) {
+            let $span=$(this);
+            if($li.hasClass("parent_li")){
+                let $liChildren = $span.parent('li.parent_li').find(' > ul > li');
+                if ($liChildren.is(":visible")) {
+                    $liChildren.hide('fast');
+                    $span.children('i:first').addClass('rf-plus').removeClass('rf-minus');
+                } else {
+                    $liChildren.show('fast');
+                    $span.children('i:first').addClass('rf-minus').removeClass('rf-plus');
+                }
+                e.stopPropagation();
+            }
+        });
+        if($li.hasClass("parent_li")){
+            const expandLevel=this.props.expandLevel;
+            if(data._level>=expandLevel){
+                var $liChildren =  $childrenSpan.parent('li.parent_li').find(' > ul > li');
+                $liChildren.hide();
+                $childrenSpan.children('i:first').addClass('rf-plus').removeClass('rf-minus');
+            }else{
+                $childrenSpan.children('i:first').addClass('rf-minus').removeClass('rf-plus');
+            }
+        }
+        this._bindContextMenu(data);
+    }
+
+    isFile(){
+        const data=this.props.data;
+        const name=data.name;
+        let isFile=false;
+        if(name.indexOf(".")>-1 || name==="ul" || name==='知识包'){
+            isFile=true;
+        }
+        return isFile;
+    }
+    componentWillUnmount(){
+        const data=this.props.data;
+        const contextMenu=data.contextMenu;
+        if(!contextMenu || contextMenu.length===0){
+            return;
+        }
+        $("#node-"+data.id).contextmenu("destroy");
+    }
+    componentDidUpdate(){
+        this._bindContextMenu(this.props.data);
+    }
+    _bindContextMenu(data){
+        const $node=$("#node-"+data.id);
+        $node.contextmenu("destroy");
+        const contextMenu=data.contextMenu;
+        if(!contextMenu || contextMenu.length===0){
+            return;
+        }
+        const menuId='treenodemenu'+data.id;
+        $node.contextmenu({
+            target:'#'+menuId
+        });
+    }
+    render(){
+        const {data,dispatch}=this.props;
+        const children=data.children;
+        const spanId="node-"+data.id,menuId='treenodemenu'+data.id;
+        let menu=[];
+        if(data.contextMenu){
+            menu.push(<Menu items={data.contextMenu} key={data.id} data={data} dispatch={dispatch} menuId={menuId}/>);
+        }
+        if(children && children.length>0){
+            return (
+                <li className='parent_li'>
+                    <span id={spanId}>
+                        <i className='rf rf-minus' style={{marginRight:"2px"}}></i>
+                        <i className={data._icon} style={data._style}></i> <a href='###' style={data._style}>{data.name}</a>
+                    </span>
+                    {menu}
+                    <TreeParentItem dispatch={dispatch} children={children} expandLevel={this.props.expandLevel}/>
+                </li>
+            );
+        }else{
+            let isFile=this.isFile();
+            return (
+                <li>
+                    <span id={spanId} onClick={(e)=>{
+                        if(isFile){
+                            const url=window._server+data.editorPath+"?file="+data.fullPath;
+                            event.eventEmitter.emit(event.TREE_NODE_CLICK,{
+                                id:data.id,
+                                name:data.name,
+                                fullPath:data.fullPath,
+                                path:url,
+                                active:true
+                            });
+                            $('.tree').find('.tree-active').removeClass('tree-active');
+                            $("#"+spanId).addClass('tree-active');
+                        }
+                    }}>
+                        <i className={data._icon} style={data._style}></i> <a href='###' style={data._style}>{data.name}</a>
+                    </span>
+                    {menu}
+                </li>
+            );
+        }
+    }
+};
+export default TreeItem;

+ 22 - 0
urule-console-js/src/components/tree/component/TreeParentItem.jsx

@@ -0,0 +1,22 @@
+/**
+ * Created by Jacky.gao on 2016/5/23.
+ */
+import React,{Component,PropTypes} from 'react';
+import TreeItem from './TreeItem.jsx';
+
+export default class TreeParentItem extends Component{
+    render(){
+        const {children,dispatch,selectDir}=this.props;
+        let result=[];
+        children.forEach((item,index)=>{
+            result.push(
+                <TreeItem data={item} key={index} dispatch={dispatch} selectDir={selectDir} expandLevel={this.props.expandLevel}/>
+            );
+        });
+        return (
+            <ul style={{marginLeft:"-18px"}}>
+                {result}
+            </ul>
+        );
+    }
+};

+ 56 - 0
urule-console-js/src/components/tree/css/tree.css

@@ -0,0 +1,56 @@
+.tree {
+}
+.tree li {
+    list-style-type:none;
+    margin:0;
+    padding:2px 5px 0 0px;
+    position:relative;
+    line-height: 1;
+}
+.tree li::before, .tree li::after {
+    content:'';
+    left:-35px;
+    position:absolute;
+    right:auto
+}
+.tree li::before {
+    border-left:1px solid #999;
+    bottom:50px;
+    height:100%;
+    top:-6px;
+    width:1px;
+}
+.tree li div li::before {
+    border:none;
+}
+.tree li::after {
+    border-top:1px solid #999;
+    height:20px;
+    top:23px;
+    width:15px
+}
+.tree li div li::after {
+    border:none
+}
+.tree li {
+    margin-top:-5px;
+}
+.tree li span {
+    border:1px solid transparent;
+    display:inline-block;
+    padding:2px 2px;
+    margin:10px 0px 5px -20px;
+    cursor:pointer;
+    white-space: nowrap;
+}
+.tree li.parent_li>span {
+}
+.tree>ul>li::before, .tree>ul>li::after {
+    border:0
+}
+.tree li:last-child::before {
+    height:30px;
+}
+.tree-active{
+    border:solid 1px #2196F3 !important
+}

+ 114 - 0
urule-console-js/src/constant/action.js

@@ -0,0 +1,114 @@
+/**
+ * Created by jacky on 2016/6/12.
+ */
+import {ajaxSave} from '../Utils.js';
+export const LOAD_MASTER_COMPLETED='load_master_completed';
+export const LOAD_SLAVE_COMPLETE='load_slave_completed';
+export const ADD_MASTER='add_master';
+export const DEL_MASTER='del_master';
+export const ADD_SLAVE='add_slave';
+export const DEL_SLAVE='del_slave';
+export const SAVE='save';
+
+export function save(newVersion,file){
+    return {newVersion,file,type:SAVE};
+}
+
+export function saveData(data,newVersion,file) {
+    let xml='<?xml version="1.0" encoding="utf-8"?>';
+    xml+='<constant-library>';
+    let errorInfo='';
+    data.forEach((item,index)=>{
+        if(!item.name || item.name.length<1){
+            errorInfo='常量分类名称不能为空.';
+            return false;
+        }
+        if(!item.label || item.label.length<1){
+            errorInfo='常量分类标题不能为空.';
+            return false;
+        }
+        xml+="<category name='"+item.name+"' label='"+item.label+"'>";
+        var constants=item.constants;
+        if(!constants){
+            errorInfo="常量分类["+item.label+"]下未定义具体的常量信息";
+            return false;
+        }
+        constants.forEach((constant,i)=>{
+            if(!constant.name || constant.name.length<1){
+                errorInfo='常量名不能为空.';
+                return false;
+            }
+            if(!constant.label || constant.label.length<1){
+                errorInfo='常量标题不能为空.';
+                return false;
+            }
+            if(!constant.type || constant.type.length<1){
+                errorInfo='常量数据类型不能为空.';
+                return false;
+            }
+            xml+="<constant name='"+constant.name+"' label='"+constant.label+"' type='"+constant.type+"'/>";
+        });
+        if(errorInfo.length>1){
+            return false;
+        }
+        xml+='</category>';
+    });
+    if(errorInfo.length>1){
+        bootbox.alert(errorInfo+',不能保存!');
+        return;
+    }
+    xml+='</constant-library>';
+    let postData={content:xml,file,newVersion};
+    const url=window._server+'/common/saveFile';
+    if(newVersion){
+        bootbox.prompt("请输入新版本描述.",function (versionComment) {
+            if(!versionComment){
+                return;
+            }
+            postData.versionComment=versionComment;
+            ajaxSave(url,postData,function () {
+                bootbox.alert('保存成功!');
+            })
+        });
+    }else{
+        ajaxSave(url,postData,function () {
+            bootbox.alert('保存成功!');
+        })
+    }
+};
+
+export function addMaster() {
+    return {type:ADD_MASTER};
+};
+
+export function deleteMaster(rowIndex) {
+    return {rowIndex,type:DEL_MASTER};
+}
+
+export function deleteSlave(rowIndex) {
+    return {rowIndex,type:DEL_SLAVE};
+};
+
+export function addSlave() {
+    return {type:ADD_SLAVE};
+};
+
+export function loadMasterData(files) {
+    return function (dispatch) {
+        var url=window._server+"/xml";
+        $.ajax({
+            url,
+            type:'POST',
+            data:{files},
+            success:function (data) {
+                dispatch({type:LOAD_MASTER_COMPLETED,masterData:data[0].categories});
+            },
+            error:function () {
+                alert("加载数据失败.");
+            }
+        });
+    }
+};
+export function loadSlaveData(masterData) {
+    return {type:LOAD_SLAVE_COMPLETE,masterRowData:masterData};
+};

+ 100 - 0
urule-console-js/src/constant/components/ConstantEditor.jsx

@@ -0,0 +1,100 @@
+/**
+ * Created by jacky on 2016/6/11.
+ */
+import React from 'react';
+import {connect} from 'react-redux';
+import Grid from '../../components/grid/component/Grid.jsx';
+import Splitter from '../../components/splitter/component/Splitter.jsx';
+import * as action from '../action.js';
+import * as refEvent from '../../reference/event.js';
+import ReferenceDialog from '../../reference/ReferenceDialog.jsx';
+
+class ConstantEditor extends React.Component{
+    render(){
+        const {dispatch,masterData,masterRowData,file}=this.props;
+        const masterHeaders=[
+            {id:'master-name',name:'name',label:'名称',filterable:true,editable:true,width:'200px'},
+            {id:'master-label',name:'label',label:'标题',filterable:true,editable:true}
+        ];
+        const slaveHeaders=[
+            {id:'slave-name',name:'name',label:'名称',filterable:true,editable:true,width:'160px'},
+            {id:'slave-label',name:'label',label:'标题',filterable:true,editable:true},
+            {id:'slave-type',name:'type',label:'数据类型',width:'160px',editable:true,editorType:'select',selectData:['String','Integer','Char','Double','Long','Float','BigDecimal','Boolean','Date','List','Set','Map','Enum','Object']},
+        ];
+        const masterGridOperationCol={
+            width:'100px',
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'18px',color:'#d9534f',padding:'0px 4px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.deleteMaster(rowIndex));
+                            dispatch(action.loadSlaveData({}));
+                        });
+                    }
+                }
+            ]
+        };
+        const slaveGridOperationCol={
+            width:'70px',
+            operations:[
+                {
+                    label:'删除',
+                    icon:'glyphicon glyphicon-trash',
+                    style:{fontSize:'18px',color:'#d9534f',padding:'0px 10px',cursor:'pointer'},
+                    click:function(rowIndex){
+                        bootbox.confirm('真的要删除当前记录?',function(result) {
+                            if(!result)return;
+                            dispatch(action.deleteSlave(rowIndex));
+                        })
+                    }
+                }
+            ]
+        };
+        return (
+            <div className="row" style={{margin:'0px'}}>
+                <ReferenceDialog/>
+                <Splitter  orientation='vertical' position='40%'>
+                    <div>
+                        <div style={{margin:'2px'}}>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-primary" type="button" onClick={(e)=>{dispatch(action.addMaster())}}><i className="glyphicon glyphicon-plus-sign"></i> 添加分类</button>
+                            </div>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-danger" type="button" onClick={()=>{dispatch(action.save(false,file))}}><i className="rf rf-save"></i> 保存</button>
+                                <button className="btn btn-danger" type="button" onClick={()=>{dispatch(action.save(true,file))}}><i className="rf rf-savenewversion"></i> 保存为新版本</button>
+                            </div>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-info" type="button" onClick={(e)=>{
+                                    refEvent.eventEmitter.emit(refEvent.OPEN_REFERENCE_DIALOG,file);
+                                }}><i className="rf rf-link"></i> 查看引用</button>
+                            </div>
+                        </div>
+                        <Grid headers={masterHeaders} dispatch={dispatch} rows={masterData} operationConfig={masterGridOperationCol} rowClick={(rowData)=>{
+                            dispatch(action.loadSlaveData(rowData));
+                        }}></Grid>
+                    </div>
+                    <div>
+                        <div style={{margin:'2px'}}>
+                            <div className="btn-group btn-group-sm" style={{margin:'2px'}}>
+                                <button className="btn btn-primary" type="button" onClick={(e)=>{dispatch(action.addSlave())}}><i className="glyphicon glyphicon-plus"></i> 添加常量</button>
+                            </div>
+                        </div>
+                        <Grid headers={slaveHeaders} dispatch={dispatch} rows={masterRowData.constants || []} operationConfig={slaveGridOperationCol}></Grid>
+                    </div>
+                </Splitter>
+            </div>
+        )
+    }
+};
+
+function select(state){
+    return {
+        masterData:state.master.data || [],
+        masterRowData:state.slave.data || {}
+    };
+};
+export default connect(select)(ConstantEditor);

+ 36 - 0
urule-console-js/src/constant/index.jsx

@@ -0,0 +1,36 @@
+/**
+ * Created by jacky on 2016/6/11.
+ */
+import '../../node_modules/bootstrap/dist/css/bootstrap.css';
+import '../css/iconfont.css';
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {Provider} from 'react-redux';
+import {createStore,applyMiddleware} from 'redux';
+import thunk from 'redux-thunk';
+import reducer from './reducer.js';
+import ConstantEditor from './components/ConstantEditor.jsx';
+import * as action from './action.js';
+
+$(document).ready(function(){
+    const store=createStore(reducer,applyMiddleware(thunk));
+    const file=_getParameter('file');
+    if(!file || file.length<1){
+        bootbox.alert('请先指定要加载的常量库文件.');
+        return;
+    }
+    store.dispatch(action.loadMasterData(file));
+    ReactDOM.render(
+        <Provider store={store}>
+            <ConstantEditor file={file}/>
+        </Provider>,
+        document.getElementById('container')
+    );
+});
+function _getParameter(name) {
+    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+    var r = window.location.search.substr(1).match(reg);
+    if (r != null)return unescape(r[2]);
+    return null;
+};

+ 48 - 0
urule-console-js/src/constant/reducer.js

@@ -0,0 +1,48 @@
+/**
+ * Created by jacky on 2016/6/11.
+ */
+import {combineReducers} from 'redux';
+import * as ACTIONS from './action.js';
+
+function master(state={},action){
+    switch (action.type){
+        case ACTIONS.LOAD_MASTER_COMPLETED:
+            return Object.assign({},state.prototype,{data:action.masterData});
+        case ACTIONS.DEL_MASTER:
+            var rowIndex=action.rowIndex;
+            var newData=[...state.data];
+            newData.splice(rowIndex,1);
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.ADD_MASTER:
+            var newData=[...state.data];
+            newData.push({name:'',type:'Custom',constants:[]});
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.SAVE:
+            var data=state.data;
+            ACTIONS.saveData(data,action.newVersion,action.file);
+            return state;
+        default:
+            return state;
+    }
+};
+function slave(state={},action){
+    switch (action.type){
+        case ACTIONS.LOAD_SLAVE_COMPLETE:
+            return Object.assign({},state.prototype,{data:action.masterRowData});
+        case ACTIONS.DEL_SLAVE:
+            var rowIndex=action.rowIndex;
+            var newData=Object.assign({},state.data);
+            newData.constants.splice(rowIndex,1);
+            return Object.assign({},state.prototype,{data:newData});
+        case ACTIONS.ADD_SLAVE:
+            var newData=Object.assign({},state.data);
+            newData.constants.push({name:'',label:'',type:'String'});
+            return Object.assign({},state.prototype,{data:newData});
+        default:
+            return state;
+    }
+};
+
+export default combineReducers({
+    master,slave
+});

+ 57 - 0
urule-console-js/src/css/iconfont.css

@@ -0,0 +1,57 @@
+
+@font-face {font-family: "rf";
+  src: url('iconfont.ttf') format('truetype')/* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+}
+
+.rf {
+  font-family:"rf" !important;
+  font-style:normal;
+  -webkit-font-smoothing: antialiased;
+  -webkit-text-stroke-width: 0.2px;
+  -moz-osx-font-smoothing: grayscale;
+}
+.rf-fork:before { content: "\e600"; }
+.rf-check:before { content: "\e625"; }
+.rf-search:before { content: "\e623"; }
+.rf-table:before { content: "\e60b"; }
+.rf-display:before { content: "\e624"; }
+.rf-save:before { content: "\e609"; }
+.rf-link:before { content: "\e628"; }
+.rf-folder:before { content: "\e622"; }
+.rf-library:before { content: "\e60d"; }
+.rf-minus:before { content: "\e619"; }
+.rf-plus:before { content: "\e60c"; }
+.rf-code:before { content: "\e617"; }
+.rf-scripttable:before { content: "\e618"; }
+.rf-database:before { content: "\e60e"; }
+.rf-paste:before { content: "\e629"; }
+.rf-remove:before { content: "\e61a"; }
+.rf-export:before { content: "\e61f"; }
+.rf-start:before { content: "\e601"; }
+.rf-rule:before { content: "\e602"; }
+.rf-copy:before { content: "\e62a"; }
+.rf-tree:before { content: "\e60f"; }
+.rf-decision1:before { content: "\e603"; }
+.rf-flow:before { content: "\e610"; }
+.rf-list:before { content: "\e620"; }
+.rf-root:before { content: "\e60a"; }
+.rf-select:before { content: "\e615"; }
+.rf-rename:before { content: "\e61c"; }
+.rf-script:before { content: "\e604"; }
+.rf-constant:before { content: "\e605"; }
+.rf-package:before { content: "\e616"; }
+.rf-savenewversion:before { content: "\e614"; }
+.rf-createpro:before { content: "\e61d"; }
+.rf-version:before { content: "\e61b"; }
+.rf-type:before { content: "\e626"; }
+.rf-action:before { content: "\e606"; }
+.rf-authority:before { content: "\e621"; }
+.rf-operation:before { content: "\e627"; }
+.rf-join:before { content: "\e607"; }
+.rf-import:before { content: "\e61e"; }
+.rf-cut:before { content: "\e62b"; }
+.rf-project:before { content: "\e611"; }
+.rf-decision:before { content: "\e608"; }
+.rf-variable:before { content: "\e612"; }
+.rf-parameter:before { content: "\e613"; }
+.rf-scorecard:before { content: "\e62c"; }

BIN
urule-console-js/src/css/iconfont.eot


+ 242 - 0
urule-console-js/src/css/iconfont.svg

@@ -0,0 +1,242 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+Created by FontForge 20120731 at Wed Sep 21 16:32:16 2016
+ By admin
+</metadata>
+<defs>
+<font id="rf" horiz-adv-x="1024" >
+  <font-face 
+    font-family="rf"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1024"
+    panose-1="2 0 6 3 0 0 0 0 0 0"
+    ascent="896"
+    descent="-128"
+    x-height="792"
+    bbox="0 -212 1147 928"
+    underline-thickness="50"
+    underline-position="-100"
+    unicode-range="U+0078-E62C"
+  />
+<missing-glyph horiz-adv-x="374" 
+d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
+    <glyph glyph-name=".notdef" horiz-adv-x="374" 
+d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="341" 
+ />
+    <glyph glyph-name="x" unicode="x" horiz-adv-x="1001" 
+d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
+t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
+t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
+    <glyph glyph-name="uniE600" unicode="&#xe600;" 
+d="M803 181v90q0 70 -49.5 120t-120.5 50q-30 0 -51 21t-21 51v74q42 15 69.5 52.5t27.5 84.5q0 60 -43 102.5t-103 42.5t-103 -42.5t-43 -102.5q0 -47 27.5 -84t69.5 -53v-73q0 -31 -21 -52t-51 -21q-71 0 -120.5 -50t-49.5 -120v-90q-43 -15 -70 -52.5t-27 -84.5
+q0 -60 42.5 -102.5t102.5 -42.5t103 42.5t43 102.5q0 47 -27 84.5t-70 52.5v90q0 30 21 51.5t52 21.5q71 0 121 51q50 -51 121 -51q30 0 51.5 -21.5t21.5 -51.5v-90q-43 -15 -70 -52.5t-27 -84.5q0 -60 42.5 -102.5t103 -42.5t103 42.5t42.5 102.5q0 47 -27 84.5t-70 52.5z
+M269 -4q-20 0 -34 14t-14 34t14 34.5t34 14.5t34.5 -14.5t14.5 -34.5t-14.5 -34t-34.5 -14zM512 772q20 0 34.5 -14t14.5 -34t-14.5 -34.5t-34.5 -14.5t-34.5 14.5t-14.5 34.5t14.5 34t34.5 14zM755 -4q-20 0 -34.5 14t-14.5 34t14.5 34.5t34.5 14.5t34 -14.5t14 -34.5
+t-14 -34t-34 -14z" />
+    <glyph glyph-name="uniE601" unicode="&#xe601;" 
+d="M851 723q-67 68 -155 104.5t-184 36.5t-183.5 -36.5t-155.5 -104.5t-104.5 -155.5t-36.5 -183.5t36.5 -184t104.5 -155.5t155.5 -104t183.5 -36.5t184 36.5t156 104.5q92 92 124.5 215.5t0 247t-125.5 215.5zM794 102q-57 -56 -129.5 -86t-152.5 -30t-152.5 30t-129 86.5
+t-86.5 129t-30 152.5t30 152.5t86.5 129t129 87t152.5 30.5t152.5 -30.5t129.5 -86.5q76 -77 103.5 -179.5t0 -205t-103.5 -179.5zM486 607q-13 15 -31 15t-30 -12t-12 -29v-394q0 -17 12 -29t29 -12h1q18 0 31 15l194 194q12 12 12 29t-12 29z" />
+    <glyph glyph-name="uniE602" unicode="&#xe602;" 
+d="M992 299q0 6 -3 11l-129 262q36 20 36 61q0 28 -20.5 48.5t-49.5 20.5h-235v73q0 32 -23 54.5t-55 22.5t-54.5 -22.5t-22.5 -54.5v-73h-236q-29 0 -49 -20.5t-20 -48.5q0 -20 10 -36.5t27 -25.5l-134 -262v-1v-1l-2 -4v-4v-1v0q0 -68 48.5 -116t116.5 -48t116.5 48
+t48.5 116v1q0 6 -3 11l-125 253h202v-493h-37q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5h228q32 0 55 22.5t23 54.5t-23 54.5t-55 22.5h-36v493h203l-130 -254v-1q0 -1 -1 -1q0 -2 -1 -4v-4v-1v0q0 -68 48.5 -116t116.5 -48t116 48t49 116v1zM197 178
+q-44 0 -77.5 28t-41.5 71h238q-8 -43 -41.5 -71t-77.5 -28zM90 321l109 214l106 -214h-215zM661 -7q0 -14 -10 -23.5t-24 -9.5h-228q-14 0 -23.5 9.5t-9.5 23.5t9.5 23.5t23.5 9.5h228q14 0 24 -9.5t10 -23.5zM569 607q-9 0 -15.5 -6.5t-6.5 -15.5v-515h-67v515
+q0 9 -6.5 15.5t-15.5 6.5h-258q-11 0 -18 7.5t-7 18t7 18t18 7.5h258q9 0 15.5 6.5t6.5 15.5v95q0 14 9.5 23.5t23.5 9.5t24 -9.5t10 -23.5v-95q0 -9 6.5 -15.5t15.5 -6.5h257q11 0 18.5 -7.5t7.5 -18t-7.5 -18t-18.5 -7.5h-257zM829 535l106 -214h-215zM827 178
+q-44 0 -77.5 28t-41.5 71h238q-8 -43 -41.5 -71t-77.5 -28z" />
+    <glyph glyph-name="uniE603" unicode="&#xe603;" 
+d="M513 896q-109 4 -211 -44.5t-175 -134t-105 -190.5q-25 -75 -21 -157t29.5 -156.5t75.5 -139.5t117 -108q122 -83 272.5 -90.5t278.5 69.5q84 49 144 126.5t84 170.5q41 146 -3 297t-162 241q-69 56 -153 86t-171 30zM513 800q85 4 166 -34t137.5 -100.5t88 -146.5
+t22.5 -168q-8 -111 -74 -204.5t-168 -136.5q-96 -46 -210 -37.5t-201 72.5q-56 37 -96 93.5t-59.5 120.5t-19 133.5t22.5 132.5q39 122 150.5 199t240.5 76zM292 649l-45 -45l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1
+l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1
+l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -2l-45 -45q30 -6 87.5 -19.5t88.5 -20.5q-5 28 -12.5 56.5t-15 62.5t-12.5 57l-45 -45l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1
+l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1
+l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1zM769 646q-89 -22 -178 -43l45 -46l-272 -272q-45 56 -51 22q0 -5 -1 -13q-5 -24 -16.5 -70
+t-16.5 -68q30 6 88 19t87 20l-45 45l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1
+l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1
+l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1
+l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1
+l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l1 1l42 -42zM544 352v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1
+v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1
+v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-1v-1l1 -1v-1v-1v-1l1 -1v-1v-1v-2l45 45l1 -1l1 -1l1 -1l1 -1
+l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l2 -1l1 -1l1 -1l1 -1l1 -1
+l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l1 -1l45 45l-1 1l-1 1l-1 1l-1 1
+l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1
+l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l-1 1l45 46q-32 7 -90.5 20.5
+t-85.5 19.5z" />
+    <glyph glyph-name="uniE604" unicode="&#xe604;" 
+d="M331 853l-9 -5q-128 -76 -188 -147q-60 -69 -60 -139q-1 -32 9 -62t32 -60t37.5 -48t45.5 -49q55 -56 84.5 -94.5t32.5 -70.5q4 -39 -29 -81q-44 -55 -150 -117l-112 -65h692l8 5q117 68 169 133q51 65 44 133q-1 15 -6 31t-10 28t-16 28t-17 25t-21 25.5t-21 22.5
+t-23.5 24t-21.5 22q-51 53 -78 93t-27 76q1 45 43 95q53 62 171 132l110 65h-689zM350 783h426q-56 -41 -90 -82q-59 -69 -60 -139q0 -32 9.5 -62t31.5 -60t37.5 -48t46.5 -49q54 -56 83.5 -94.5t32.5 -70.5q5 -39 -29 -81q-42 -53 -142 -112h-426q44 35 71 68q51 65 44 133
+q-2 15 -7 31t-9.5 28t-15.5 28t-17 25t-21.5 25.5t-21 22.5t-23.5 24t-21 22q-52 53 -79 93t-26 76q0 45 43 95q51 59 163 127zM250 663v-32h333v32h-333zM238 489v-32h344v32h-344zM394 315v-33h333v33h-333zM442 140v-32h345v32h-345z" />
+    <glyph glyph-name="uniE605" unicode="&#xe605;" 
+d="M591 402h315q22 0 38 15.5t16 38.5v321q0 22 -16 37.5t-38 15.5h-315q-22 0 -38 -15.5t-16 -37.5v-321q0 -23 16 -38.5t38 -15.5zM575 777q0 6 4.5 11t11.5 5h315q7 0 11.5 -5t4.5 -11v-321q0 -7 -4.5 -11.5t-11.5 -4.5h-315q-7 0 -11.5 4.5t-4.5 11.5v321zM591 -63h315
+q22 0 38 15.5t16 37.5v322q0 22 -16 37.5t-38 15.5h-315q-22 0 -38 -15.5t-16 -37.5v-322q0 -22 16 -37.5t38 -15.5zM119 -63h316q22 0 37.5 15.5t15.5 37.5v322q0 22 -15.5 37.5t-37.5 15.5h-316q-22 0 -37.5 -15.5t-15.5 -37.5v-322q0 -22 15.5 -37.5t37.5 -15.5zM103 312
+q0 6 5 10.5t11 4.5h316q6 0 11 -4.5t5 -10.5v-322q0 -6 -5 -11t-11 -5h-316q-6 0 -11 5t-5 11v322zM119 403h316q22 0 37.5 15.5t15.5 37.5v322q0 22 -15.5 37.5t-37.5 15.5h-316q-22 0 -37.5 -15.5t-15.5 -37.5v-322q0 -22 15.5 -37.5t37.5 -15.5zM103 778q0 6 5 11t11 5
+h316q6 0 11 -5t5 -11v-322q0 -6 -5 -10.5t-11 -4.5h-316q-6 0 -11 4.5t-5 10.5v322z" />
+    <glyph glyph-name="uniE606" unicode="&#xe606;" 
+d="M512 850q-94 0 -180.5 -37t-148.5 -99t-99 -148.5t-37 -181t37 -180.5t99 -148.5t148.5 -99.5t181 -37t180.5 37t148.5 99.5t99.5 148.5t37 180.5t-37 181t-99.5 148.5t-148.5 99t-181 37zM512 -36q-115 0 -211 55.5t-151.5 152.5t-56.5 213q0 114 56 210.5t152.5 152.5
+t210.5 56t210.5 -56t152.5 -152.5t56 -210.5q0 -116 -56 -213t-152.5 -152.5t-210.5 -55.5zM551 440h166q0 -1 -70 -99t-140 -195l-70 -98l51 280h-167q0 1 140 196t140 196l-12 -64q-12 -63 -25 -132.5t-13 -83.5z" />
+    <glyph glyph-name="uniE607" unicode="&#xe607;" 
+d="M694 65q0 -75 -53.5 -128.5t-128.5 -53.5t-128.5 53.5t-53.5 128.5q0 63 38.5 112t97.5 65q0 20 -1 31.5t-8.5 32t-22.5 43.5q-32 45 -163 176q-51 -9 -95 8q-71 25 -103 93.5t-6 139.5q27 71 94.5 103t138.5 6t103 -94.5t6 -138.5q-16 -47 -56 -79q8 -27 48 -72.5
+t75.5 -83t35.5 -46.5q0 9 35.5 46.5t75.5 83t48 72.5q-40 32 -56 79q-26 70 6 138.5t103 94.5t138.5 -6t94.5 -103q26 -71 -6 -139.5t-103 -93.5q-44 -17 -95 -8q-131 -131 -163 -176q-15 -23 -22.5 -43.5t-8.5 -32t-1 -31.5q59 -16 97.5 -65t38.5 -112zM512 156
+q-38 0 -64.5 -26.5t-26.5 -64t26.5 -64.5t64.5 -27t64.5 27t26.5 64.5t-26.5 64t-64.5 26.5zM269 790q-35 12 -69 -4t-48 -51q-12 -36 4 -70t51 -47t69.5 3t47.5 52q13 35 -3.5 69.5t-51.5 47.5zM755 790q-35 -13 -51.5 -47.5t-3.5 -69.5q13 -36 47.5 -52t69.5 -3t51 47
+t4 70q-14 35 -48 51t-69 4z" />
+    <glyph glyph-name="uniE608" unicode="&#xe608;" 
+d="M509 -128q-28 0 -75 27l-70 39q-8 5 -12.5 14.5t-2 19t10.5 15t19 5.5h263q11 0 19 -5.5t10.5 -15t-2 -19t-12.5 -14.5l-67 -36q-41 -24 -72 -27q-3 -3 -9 -3zM593 125h-51h-51h-84h-9h-11h-13q-4 0 -10.5 0.5t-12.5 3t-11 5.5q-6 3 -11 7.5t-9 10.5t-7 10t-6.5 11
+t-5.5 9q-4 14 -9 18q-1 2 -5.5 5t-6.5 4q-6 6 -9 6q-36 30 -51 46q-46 50 -71.5 115t-25.5 134q-4 174 127 287q66 57 150.5 81.5t171.5 11.5q79 -11 148.5 -54.5t116.5 -111.5q93 -134 61 -286q-29 -121 -121 -205q-12 -12 -21 -15q-9 -6 -24 -21q-8 -7 -12 -21
+q-7 -13 -12.5 -21t-21.5 -18.5t-38 -11.5h-55zM404 188h84h51h103q9 0 11.5 2.5t9.5 15.5q1 3 8 14.5t12.5 18.5t36.5 31q6 3 15 12q79 70 102 171q28 126 -51 238q-38 57 -96.5 93.5t-126.5 45.5q-74 11 -145.5 -10.5t-128.5 -71.5q-52 -42 -80 -103t-28 -128
+q0 -57 21.5 -112.5t59.5 -95.5q12 -15 42 -39q2 -2 5 -3.5t4 -2.5q3 -3 10.5 -9.5t10.5 -9.5q9 -12 21 -36q5 -13 12 -21h25h12zM614 -2h-207q-25 0 -41.5 17.5t-16.5 40.5v9q0 24 17 40.5t41 16.5h207q25 0 41.5 -17t16.5 -40v-9q2 -23 -15.5 -40.5t-42.5 -17.5zM614 65
+l-204 -3l3 -6l201 3v6zM735 486q-14 0 -22 8.5t-8 22.5q0 81 -61 139.5t-147 58.5q-14 0 -22 8.5t-8 22t8 22t22 8.5q111 0 189.5 -77t78.5 -185q0 -12 -9.5 -20t-20.5 -8z" />
+    <glyph glyph-name="uniE609" unicode="&#xe609;" 
+d="M932 -99h-823q-24 0 -41 17t-17 40v825q0 23 17 40t41 17h670l210 -203v-679q0 -23 -17 -40t-40 -17zM120 791q-8 0 -14 -6t-6 -14v-801q0 -8 6 -14t14 -6h801q8 0 13.5 6t5.5 14v652l-179 169h-641zM742 396h-542v444h542v-444zM249 445h444v346h-444v-346zM642 519h-48
+v246h48v-246zM747 198h-530q-7 0 -13 7.5t-6 16t6 17t13 8.5h530q8 0 14 -8.5t6 -17t-6 -16t-14 -7.5zM747 99h-530q-7 0 -13 8t-6 16.5t6 16.5t13 8h530q8 0 14 -8t6 -16.5t-6 -16.5t-14 -8zM747 1h-530q-7 0 -13 7.5t-6 16t6 17t13 8.5h530q8 0 14 -8.5t6 -17t-6 -16
+t-14 -7.5z" />
+    <glyph glyph-name="uniE60A" unicode="&#xe60a;" 
+d="M346 605q-26 0 -44.5 -18t-18.5 -44t18.5 -44.5t44 -18.5t44 18.5t18.5 44.5t-18 44t-44 18zM366 542.5q0 -8.5 -6 -14.5t-14.5 -6t-14.5 6t-6 14.5t6 15t14.5 6.5t14.5 -6.5t6 -15zM346 439q-26 0 -44.5 -18.5t-18.5 -44t18.5 -44t44 -18.5t44 18.5t18.5 44t-18 44
+t-44 18.5zM366 376.5q0 -8.5 -6 -14.5t-14.5 -6t-14.5 6t-6 14.5t6 14.5t14.5 6t14.5 -6t6 -14.5zM345.5 272q-25.5 0 -44 -18t-18.5 -44t18.5 -44t44.5 -18t44 18t18 44t-18.5 44t-44 18zM366 210q0 -9 -6 -15t-14.5 -6t-14.5 6t-6 15t6 15t14.5 6t14.5 -6t6 -15z
+M741 376.5q0 -8.5 -6 -14.5t-15 -6h-250q-8 0 -14 6t-6 14.5t6 14.5t14 6h250q9 0 15 -6t6 -14.5zM741 543q0 -9 -6 -15t-15 -6h-250q-8 0 -14 6t-6 15t6 15t14 6h250q9 0 15 -6t6 -15zM741 210.5q0 -8.5 -6 -15t-15 -6.5h-250q-8 0 -14 6.5t-6 15t6 14.5t14 6h250
+q9 0 15 -6t6 -14.5zM955 766q0 25 -17.5 43t-43.5 18h-765q-25 0 -43 -18t-18 -43v-765q0 -25 18 -43t43 -18h765q26 0 43.5 18t17.5 43v765zM928 1q0 -14 -10 -23.5t-24 -9.5h-765q-14 0 -23.5 9.5t-9.5 23.5v765q0 14 9.5 23.5t23.5 9.5h765q14 0 24 -9.5t10 -23.5v-765z
+" />
+    <glyph glyph-name="uniE60B" unicode="&#xe60b;" 
+d="M808 706h-592q-70 0 -119 -49t-49 -118v-477q0 -70 49 -119t119 -49h592q69 0 118.5 49t49.5 119v477q0 69 -49.5 118t-118.5 49zM78 62v406h179v-545h-41q-57 0 -97.5 41t-40.5 98zM489 -77h-202v176h202v-176zM489 129h-202v155h202v-155zM489 313h-202v155h202v-155z
+M720 -77h-202v176h202v-176zM720 129h-202v155h202v-155zM720 313h-202v155h202v-155zM946 62q0 -57 -40.5 -98t-97.5 -41h-58v176h196v-37zM946 129h-196v155h196v-155zM946 313h-196v155h196v-155zM946 498h-868v41q0 57 40 97.5t98 40.5h592q57 0 97.5 -40.5t40.5 -97.5
+v-41z" />
+    <glyph glyph-name="uniE60C" unicode="&#xe60c;" horiz-adv-x="1025" 
+d="M760 383v-256q0 -26 -10 -44q-24 -43 -72 -46q-5 -1 -16 -1h-247h-258q-22 0 -37 7q-45 19 -53 66q-1 8 -1 18v251v262q0 47 37 74q22 16 53 16h381h133q28 0 49 -13q37 -25 40 -68q1 -6 1 -16v-250zM93 384v-259q0 -26 17.5 -43.5t43.5 -17.5h517q26 0 43.5 17.5
+t17.5 43.5v517q0 8 -1 14q-5 21 -21.5 34t-38.5 13h-517q-8 0 -14 -2q-21 -4 -34 -20.5t-13 -38.5v-258zM437 360v-6v-225q0 -4 -1 -4.5t-5 -1.5h-39h-4v237h-235v49h235v6q0 169 -1 223q0 5 6 5h39h5v-234h235v-49h-235z" />
+    <glyph glyph-name="uniE60D" unicode="&#xe60d;" horiz-adv-x="1088" 
+d="M1024 -148v64h-64v384h64v64h-192v-64h64v-384h-192v384h64v64h-192v-64h64v-384h-192v384h64v64h-192v-64h64v-384h-192v384h64v64h-192v-64h64v-384h-64v-64h-64v-64h1088v64h-64zM512 812h64l512 -320v-64h-1088v64z" />
+    <glyph glyph-name="uniE60E" unicode="&#xe60e;" 
+d="M925 730q-38 25 -106 44q-138 38 -333 38q-194 0 -332 -38q-68 -19 -106 -44q-48 -32 -48 -72v-614q0 -40 48 -71q38 -25 106 -44q138 -39 332.5 -39t332.5 39q68 19 106 44q48 31 48 71v614q0 40 -48 72zM167 724q132 37 319.5 37t318.5 -37q43 -12 71 -26t37 -23.5
+t9 -16.5t-9 -16.5t-37 -23.5t-71 -25q-131 -37 -318.5 -37t-319.5 37q-42 11 -70 25t-37 23.5t-9 16.5t9 16.5t37 23.5t70 26zM805 -22q-131 -36 -318.5 -36t-319.5 36q-42 12 -70 26t-37 23.5t-9 16.5v131q38 -23 103 -41q138 -39 332.5 -39t332.5 39q65 18 103 41v-131
+q0 -7 -9 -16.5t-37 -23.5t-71 -26zM805 183q-131 -37 -318.5 -37t-319.5 37q-42 12 -70 26t-37 23.5t-9 16.5v131q38 -24 103 -42q138 -38 332 -38q195 0 333 38q65 18 103 42v-131q0 -7 -9 -16.5t-37 -23.5t-71 -26zM805 388q-131 -37 -318.5 -37t-319.5 37q-42 12 -70 26
+t-37 23.5t-9 16.5v131q38 -24 103 -42q138 -38 332 -38q195 0 333 38q65 18 103 42v-131q0 -7 -9 -16.5t-37 -23.5t-71 -26z" />
+    <glyph glyph-name="uniE60F" unicode="&#xe60f;" 
+d="M910 -111h-140q-20 0 -33.5 13.5t-13.5 32.5v94q0 20 13.5 33.5t33.5 13.5h94v235h-329v-235h47q19 0 33 -13.5t14 -33.5v-94q0 -19 -14 -32.5t-33 -13.5h-141q-20 0 -33.5 13.5t-13.5 32.5v94q0 20 13.5 33.5t33.5 13.5h47v235h-329v-235h94q19 0 33 -13.5t14 -33.5v-94
+q0 -19 -14 -32.5t-33 -13.5h-141q-20 0 -33.5 13.5t-13.5 32.5v94q0 20 13.5 33.5t33.5 13.5v282h376v188h-141q-20 0 -33.5 14t-13.5 33v235q0 20 13.5 33.5t33.5 13.5h329q19 0 33 -13.5t14 -33.5v-235q0 -19 -14 -33t-33 -14h-141v-188h376v-282q19 0 33 -13.5t14 -33.5
+v-93q-1 -20 -14.5 -33.5t-33.5 -13.5zM112 29v-94h141v94h-141zM676 593v235h-329v-235h329zM441 29v-94h141v94h-141zM910 29h-140v-94h140v94z" />
+    <glyph glyph-name="uniE610" unicode="&#xe610;" 
+d="M807 341q-2 51 -39.5 84.5t-91.5 33.5q-54 -4 -86 -40q-2 -3 -6 -7t-5 -6l-166 16q-4 46 -40 77l69 126q8 -2 27 -2q52 0 89 36.5t37 86.5q0 52 -36.5 87.5t-89.5 35.5q-52 0 -89 -36t-37 -87t35 -86l-72 -131q-4 0 -12.5 1.5t-12.5 1.5q-54 -5 -85 -41q-37 -40 -33 -88
+q2 -50 38.5 -84t85.5 -34h5q54 4 86 40q11 11 19 27l158 -13v-6q0 -33 24 -67l-59 -107h-11q-52 0 -89 -36.5t-37 -86.5q0 -52 37 -88t89 -36t89 36.5t37 87.5q0 61 -48 96l51 89q20 -8 43 -8h5q54 4 86 40q39 35 35 88v0zM466 800q23 0 38.5 -16t15.5 -38t-15.5 -38
+t-38 -16t-38 16t-15.5 38q2 22 17.5 38t35.5 16zM327 371q-16 -16 -38 -16h-2q-22 0 -37 14t-17 37q0 19 14 37q15 15 40 19q22 0 36.5 -14.5t16.5 -36.5q4 -23 -13 -40zM512 -21q-22 0 -38 16t-16 38t16 37.5t38 15.5t38 -15.5t16 -37.5t-16 -38t-38 -16zM721 301
+q-14 -19 -35 -19h-2q-22 0 -37 14.5t-17 36.5q0 20 13 38q16 15 41 18q22 0 36.5 -14t16.5 -36q0 -26 -16 -38zM721 301z" />
+    <glyph glyph-name="uniE611" unicode="&#xe611;" 
+d="M781 371h-334q-20 0 -34.5 14.5t-14.5 35t14.5 34.5t34.5 14h334q20 0 34.5 -14t14.5 -34.5t-14.5 -35t-34.5 -14.5v0zM781 371zM781 176h-334q-20 0 -34.5 14.5t-14.5 35t14.5 34.5t34.5 14h334q20 0 34.5 -14t14.5 -34.5t-14.5 -35t-34.5 -14.5v0zM781 176zM1007 15
+v623q0 21 -7 36t-17 21t-20.5 9t-17.5 3h-7h-31q-20 0 -73.5 -0.5t-99.5 -0.5h-93h-73q-25 0 -20 1q-10 -1 -19 2.5t-12 6.5l-4 3q-16 28 -46 73q-14 20 -30.5 30t-26.5 9h-10h-303q-23 0 -40.5 -8.5t-25.5 -20.5t-13 -23.5t-5 -20.5l-1 -8v-731q0 -25 7 -43t17 -24
+t20.5 -10t17 -2.5t6.5 0.5h865q20 0 34 8t19.5 18t7.5 23t2 18t-1 8v0zM945 52q0 -21 -14.5 -36t-35.5 -15h-771q-20 0 -35 15t-15 36v521q0 21 15 36t35 15h771q21 0 35.5 -15t14.5 -36v-521zM945 52zM211 420q0 -22 15.5 -37t37.5 -15t37.5 15t15.5 37t-15.5 37.5
+t-37.5 15.5t-37.5 -15.5t-15.5 -37.5zM211 420zM211 225q0 -22 15.5 -37t37.5 -15t37.5 15t15.5 37t-15.5 37.5t-37.5 15.5t-37.5 -15.5t-15.5 -37.5zM211 225z" />
+    <glyph glyph-name="uniE612" unicode="&#xe612;" 
+d="M512 -80q-95 0 -181 37t-148.5 99t-99 148.5t-36.5 181t36.5 180.5t99 148.5t148.5 99.5t181 37t181 -37t148.5 -99.5t99 -148.5t36.5 -180.5t-36.5 -181t-99 -148.5t-148.5 -99t-181 -37zM512 779q-107 0 -197.5 -52.5t-143.5 -143.5t-53 -198t53 -197.5t143.5 -143.5
+t197.5 -53t197.5 53t143.5 143.5t53 197.5t-53 198t-143.5 143.5t-197.5 52.5zM646 561q46 -36 57 -107h-79q-7 40 -32 57q-24 19 -64 19q-50 0 -77 -37q-28 -34 -28 -101q0 -66 27 -102q26 -37 77 -37q88 0 99 88h78q-13 -76 -59 -115q-45 -37 -119 -37q-88 0 -137 58
+q-48 55 -48 144q0 87 47 143q50 61 140 61q73 0 118 -34z" />
+    <glyph glyph-name="uniE613" unicode="&#xe613;" 
+d="M776 -63h-528q-76 0 -129.5 53.5t-53.5 129.5v528q0 76 53.5 129.5t129.5 53.5h528q76 0 129.5 -53.5t53.5 -129.5v-528q0 -76 -53.5 -129.5t-129.5 -53.5zM248 789q-58 0 -99.5 -41.5t-41.5 -99.5v-528q0 -58 41.5 -99.5t99.5 -41.5h528q58 0 99.5 41.5t41.5 99.5v528
+q0 58 -41.5 99.5t-99.5 41.5h-528zM524 604v99h311v-99h-311zM524 453v99h311v-99h-311zM524 217v99h311v-99h-311zM524 66v99h311v-99h-311zM192 700h248v-248h-248v248zM192 315h248v-248h-248v248z" />
+    <glyph glyph-name="uniE614" unicode="&#xe614;" 
+d="M704 352q0 -14 -9 -23t-23 -9h-384q-14 0 -23 9t-9 23t9 23t23 9h384q14 0 23 -9t9 -23zM768 224q0 14 -9 23t-23 9h-448q-14 0 -23 -9t-9 -23t9 -23t23 -9h448q14 0 23 9t9 23zM768 96q0 14 -9 23t-23 9h-448q-14 0 -23 -9t-9 -23t9 -23t23 -9h448q14 0 23 9t9 23z
+M928 256q-14 0 -23 -9t-9 -23v-192q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v704q0 14 9 23t23 9h96v-224q0 -38 29 -67t67 -29h499l-70 -70q-10 -10 -10 -23t9.5 -22.5t22.5 -9.5t23 10l128 128q6 0 6 19q0 13 -13 26l-128 128q-9 9 -22 9t-22.5 -9.5t-9.5 -22.5t9 -22
+l71 -77h-13h-480q-14 0 -23 9t-9 23v224h128v-128h192v160q0 14 -9 23t-23 9h-448q-38 0 -67 -29t-29 -67v-704q0 -38 29 -67t67 -29h704q38 0 67 29t29 67v192q0 14 -9 23t-23 9z" />
+    <glyph glyph-name="uniE615" unicode="&#xe615;" 
+d="M51 384q0 94 36.5 179t98.5 147t147 98.5t179 36.5t179 -36.5t147 -98.5t98.5 -147t36.5 -179t-36.5 -179t-98.5 -147t-147 -98.5t-179 -36.5t-179 36.5t-147 98.5t-98.5 147t-36.5 179zM0 384q0 -139 68.5 -257t186.5 -186.5t257 -68.5t257 68.5t186.5 186.5t68.5 257
+t-68.5 257t-186.5 186.5t-257 68.5t-257 -68.5t-186.5 -186.5t-68.5 -257zM736 530q-13 12 -30 12t-29 -12l-235 -241l-97 99q-12 13 -29 13t-29 -12.5t-12 -30t12 -29.5l124 -127l2 -2q12 -13 29 -13t30 13l263 270q13 12 13 29.5t-12 30.5z" />
+    <glyph glyph-name="uniE616" unicode="&#xe616;" 
+d="M990 68v515q2 24 -12 46t-38 22q-16 2 -128 1.5t-127 -0.5v20v24q0 8 -2 21.5t-6 21t-12.5 15t-20.5 10.5q-147 7 -265 -1q-11 -3 -19.5 -10.5t-12.5 -15t-6 -21t-2.5 -21t0 -23.5t0.5 -20h-247q-26 2 -43 -20t-14 -49q1 -64 0.5 -262.5t-0.5 -261.5q-1 -23 16.5 -40.5
+t40.5 -16.5h420h419q26 -2 44 19t15 47zM384 721q16 0 61.5 1.5t68.5 2t59 -1t65 -4.5q4 -10 5.5 -20.5t1.5 -26.5v-20h-265q2 49 4 69zM948 47q-264 -8 -869 -2q-4 66 -3.5 178t-0.5 178q53 1 103 0q7 -20 24.5 -33t39.5 -13h109q23 0 40.5 13.5t23.5 34.5h198
+q6 -21 23.5 -34.5t40.5 -13.5h109q22 0 39.5 13t23.5 33q34 1 100 0v-177q0 -154 -1 -177zM380 422q0 -12 -8.5 -20.5t-20.5 -8.5h-109q-12 0 -20.5 8.5t-8.5 20.5v3q0 11 8.5 19.5t20.5 8.5h109q12 0 20.5 -8.5t8.5 -19.5v-3zM648 422v3q0 11 8.5 19.5t20.5 8.5h109
+q12 0 20.5 -8.5t8.5 -19.5v-3q0 -12 -8.5 -20.5t-20.5 -8.5h-109q-12 0 -20.5 8.5t-8.5 20.5zM949 442h-47h-52q-6 22 -23.5 35.5t-40.5 13.5h-109q-24 0 -42 -14t-23 -36h-196q-6 22 -24 36t-41 14h-109q-23 0 -41 -14t-24 -35h-102q3 156 3 165q152 3 407.5 2.5t406.5 0.5
+q10 -1 19.5 0t19.5 -1.5t16 -9.5q3 -7 2.5 -79t-0.5 -78z" />
+    <glyph glyph-name="uniE617" unicode="&#xe617;" 
+d="M292 126q-10 0 -19 5l-238 143q-19 11 -19 32.5t19 33.5l238 142q13 8 28.5 4.5t23.5 -17t4 -29t-17 -23.5l-183 -110l183 -110q13 -8 17 -23.5t-4 -28.5q-11 -19 -33 -19zM732 126q-22 0 -33 19q-8 13 -4 28.5t17 23.5l183 110l-183 110q-13 8 -17 23.5t4 29t23.5 17
+t28.5 -4.5l238 -142q19 -12 19 -33.5t-19 -32.5l-238 -143q-9 -5 -19 -5zM393 33q-6 0 -12 1q-15 5 -22 19.5t-2 29.5l150 458q5 15 19 22.5t29 2.5t22.5 -19.5t2.5 -29.5l-151 -458q-4 -12 -14 -19t-22 -7z" />
+    <glyph glyph-name="uniE618" unicode="&#xe618;" 
+d="M491 504q0 -41 -29 -70t-70 -29h-244q-40 0 -69 29t-29 70v244q0 40 29 69t69 29h244q41 0 70 -29t29 -69v-244zM434 748q0 17 -12.5 29t-29.5 12h-244q-17 0 -29 -12t-12 -29v-244q0 -17 12 -29.5t29 -12.5h244q17 0 29.5 12.5t12.5 29.5v244zM163.5 619q-5.5 0 -10 4
+t-4.5 10v26q0 30 18 51.5t45 21.5h88q6 0 10.5 -4t4.5 -10t-4.5 -10t-10.5 -4h-88q-16 0 -25 -14t-9 -31v-26q0 -6 -4.5 -10t-10 -4zM158 570q-6 0 -10 4t-4 10t4 10q10 10 20 0q4 -4 4 -10t-4 -10t-10 -4zM974 504q0 -41 -29 -70t-69 -29h-244q-41 0 -70 29t-29 70v244
+q0 40 29 69t70 29h244q40 0 69 -29t29 -69v-244zM917 748q0 17 -12 29t-29 12h-244q-17 0 -29.5 -12t-12.5 -29v-244q0 -17 12.5 -29.5t29.5 -12.5h244q17 0 29 12.5t12 29.5v244zM491 20q0 -40 -29 -69t-70 -29h-244q-40 0 -69 29t-29 69v244q0 41 29 70t69 29h244
+q41 0 70 -29t29 -70v-244zM434 264q0 17 -12.5 29.5t-29.5 12.5h-244q-17 0 -29 -12.5t-12 -29.5v-244q0 -17 12 -29t29 -12h244q17 0 29.5 12t12.5 29v244zM974 20q0 -40 -29 -69t-69 -29h-244q-41 0 -70 29t-29 69v244q0 41 29 70t70 29h244q40 0 69 -29t29 -70v-244z
+M917 264q0 17 -12 29.5t-29 12.5h-244q-17 0 -29.5 -12.5t-12.5 -29.5v-244q0 -17 12.5 -29t29.5 -12h244q17 0 29 12t12 29v244z" />
+    <glyph glyph-name="uniE619" unicode="&#xe619;" 
+d="M405 730h256q26 0 44 -9q43 -24 46 -72q1 -5 1 -16v-245v-260q0 -22 -7 -37q-19 -45 -66 -53q-8 -2 -18 -2h-251h-262q-47 0 -74 37q-16 22 -16 54v385v128q0 29 13 50q25 37 68 40h16h250zM405 64h257q9 0 15 1q21 5 34 21.5t13 38.5v517q0 8 -1 14q-5 21 -21.5 34
+t-38.5 13h-517q-8 0 -14 -2q-21 -4 -34 -20.5t-13 -38.5v-517q0 -8 2 -14q4 -21 20.5 -34t38.5 -13h259zM145 360v48h519v-48h-519z" />
+    <glyph glyph-name="uniE61A" unicode="&#xe61a;" horiz-adv-x="1280" 
+d="M133 300q0 103 40 197t108 162t162 108t197 40t197 -40t162 -108t108 -162t40 -197t-40 -197t-108 -162t-162 -108t-197 -40t-197 40t-162 108t-108 162t-40 197zM235 300q0 -110 54 -203.5t147.5 -147.5t203.5 -54t203.5 54t147.5 147.5t54 203.5t-54 203.5
+t-147.5 147.5t-203.5 54t-203.5 -54t-147.5 -147.5t-54 -203.5zM759 495l-119 -110l-119 110l-93 -82l119 -113l-130 -119l93 -93l130 124l130 -124l93 93l-130 119l119 113z" />
+    <glyph glyph-name="uniE61B" unicode="&#xe61b;" 
+d="M496 836h28h4l36 -3q51 -6 102 -24q88 -33 155 -98q77 -74 112 -175q20 -56 24 -113q0 -1 1 -20v-28v-5q-2 -39 -10 -78q-19 -84 -67 -155q-60 -88 -153 -139q-79 -45 -169 -54q-51 -6 -102 0q-53 7 -102 25q-88 32 -155 97q-77 75 -112 175q-20 56 -24 113q0 1 -1 21v28
+v5q2 59 20 114q38 122 135 205q63 56 143 84q57 20 115 24q1 0 20 1zM573 265q-5 5 -4 10q1 10 5 58q1 10 4.5 50.5t5.5 62.5q1 13 5.5 63.5t7.5 77.5q1 9 -1 13q-20 56 -75 62q-26 3 -47 -11q-22 -15 -35 -46q-3 -8 -2 -16q3 -32 8 -95q2 -17 4.5 -49t4.5 -46q3 -41 9 -108
+q0 -2 3 -26h107zM458 139h123v89h-123v-89z" />
+    <glyph glyph-name="uniE61C" unicode="&#xe61c;" 
+d="M872 754q-42 40 -91 40q-55 0 -99 -43q-2 -3 -20 -21l-49 -49q-30 -30 -67.5 -68t-79 -79.5t-82.5 -83t-78 -78.5l-66 -66l-45 -45l-17 -17q-5 -5 -7 -11q-19 -72 -67 -225q-4 -15 7 -26q8 -8 18 -8q4 0 8 1q161 53 220 71q6 2 11 6q463 460 506 505q48 48 47 100
+q-1 51 -49 97zM143 15q7 25 30 96.5t28 91.5l6 6q5 -11 17 -12q33 -5 66 -38q13 -13 22 -29.5t10.5 -23.5t2.5 -11q1 -8 7 -13l-5 -5q-17 -6 -86.5 -29.5t-97.5 -32.5zM364 113q-12 44 -44 76q-38 39 -84 49q341 342 442 444q5 -12 17 -14q34 -4 67 -38q13 -13 21.5 -29.5
+t10.5 -24t2 -9.5q1 -9 9 -15q-103 -103 -441 -439zM846 593q-3 -2 -10 -10q-12 44 -45 78q-39 39 -86 49q3 2 7 6l4 4l2 2q31 31 67 31q31 0 60 -28q34 -33 35 -64q0 -32 -34 -68z" />
+    <glyph glyph-name="uniE61D" unicode="&#xe61d;" 
+d="M545 39h-374q-17 0 -29 13q-12 12 -12 29v704q0 17 12 29q12 13 29 13h377v-114q0 -14 4 -30q5 -16 13 -29q9 -13 22 -22q13 -8 32 -8h127v-228l-14 2q-8 1 -13 1h-13q-56 0 -102 -26q-47 -27 -75 -73l-4 -6h-235q-16 0 -27 -11q-11 -12 -11 -28t11 -27t27 -11h208
+l-2 -14q-1 -5 -1 -10v-12q0 -35 10 -66t28 -57zM171 803q-7 0 -12 -5q-5 -6 -5 -13v-704q0 -7 5 -12.5t12 -5.5h330q-12 21 -19 45q-11 34 -11 73v12h-181q-26 0 -44 18t-18 44t18 44.5t44 18.5h222q31 47 80 75q4 3 9 5q-8 -2 -16 -2h-295q-26 0 -44 19q-18 18 -18 44.5
+t18 44.5t44 18h295q25 0 43 -18.5t18 -44.5t-18 -44q-4 -5 -9 -8q41 16 87 16h14h2v177h-103q-26 0 -45 12q-18 11 -29 28t-16 37q-5 19 -5 36v90h-353zM290 498q-16 0 -27 -11t-11 -27.5t11 -28t27 -11.5h295q15 0 26 11.5t11 28t-11 27.5t-26 11h-295zM708 -12
+q-37 0 -71 16q-34 15 -59 41t-40 61q-15 34 -15 74q0 39 15 74t40 61t59 41t72 15t72 -15q33 -15 58.5 -41.5t40.5 -60.5q14 -35 14 -74q0 -40 -14 -74q-15 -35 -40 -61t-59 -41q-34 -16 -73 -16zM708 347q-32 0 -61.5 -13t-51.5 -36t-35 -53t-13 -65t13 -65.5t35 -53
+t51 -36.5q63 -28 125 1q29 13 51.5 36t35 53t12.5 65t-13 65q-12 30 -34.5 53t-51.5 36t-63 13zM705 42q-14 0 -25 10q-11 11 -11 27l1 62l-63 -1q-15 0 -25.5 11t-10.5 26t11 26t26 11l64 2l1 63q0 15 11 26t26 11q16 0 26 -11q11 -10 11 -26l-1 -62l62 1q16 0 26.5 -11
+t10.5 -26t-11 -26t-26 -11l-63 -1l-2 -64q0 -15 -11 -26t-26 -11h-1zM695 165l-2 -87q0 -5 3.5 -8.5t9.5 -3.5q5 0 9 4t4 9v1l2 86l87 2q5 0 9 4t4 9t-3.5 9t-8.5 4l-87 -2l1 88q0 5 -4 8.5t-9 3.5t-9 -4t-4 -9l-2 -87l-87 -2q-5 0 -9 -4t-4 -9t3.5 -9t8.5 -4zM746 652h-101
+q-30 0 -50.5 21.5t-20.5 51.5v102h130q17 0 29 -12q13 -13 13 -30v-133zM597 803v-78q0 -21 14 -35t34 -14l77 1v108q0 7 -6 13q-5 5 -12 5h-107z" />
+    <glyph glyph-name="uniE61E" unicode="&#xe61e;" 
+d="M67 -32h742v60h-742v-60zM682 308l-43 41l-172 -169q0 47 6 93q22 175 115 289q125 154 369 178l-6 59q-269 -27 -411 -200q-57 -71 -91 -164q-26 -73 -36 -157q-6 -49 -6 -97l-170 167l-43 -42l243 -239z" />
+    <glyph glyph-name="uniE61F" unicode="&#xe61f;" 
+d="M741 361q-3 9 -12 13t-18 0.5t-13 -12t-1 -17.5l112 -313l-681 -144v828l463 -91l19 -42q4 -9 13 -12.5t17.5 0.5t12 13t-0.5 18l-23 53q-5 11 -17 13l-503 99q-11 2 -19.5 -5t-8.5 -18v-884q0 -12 9 -19q7 -5 15 -5q2 0 5 1l734 155q11 2 16 11.5t2 19.5zM964 635.5
+q-5 10.5 -17 13.5l-162 34q-10 2 -18 -3t-10 -14.5t3 -17.5t15 -10l90 -19q-72 -20 -134 -52t-104 -65t-77 -72t-54 -69.5t-34 -62.5q-4 -8 -1 -17.5t12 -13.5q5 -2 10 -2q15 0 21 13q14 30 32 58.5t51.5 65.5t73.5 67.5t99.5 60t129.5 47.5l-49 -91q-5 -9 -2 -18t11 -14
+q5 -3 11 -3q14 0 21 12l81 152q6 10 1 20.5z" />
+    <glyph glyph-name="uniE620" unicode="&#xe620;" 
+d="M104 192q30 0 51 -21t21 -51t-21 -51t-51 -21t-51 21t-21 51t21 51t51 21zM104 456q30 0 51 -21t21 -51t-21 -51t-51 -21t-51 21t-21 51t21 51t51 21zM104 720q30 0 51 -21t21 -51t-21 -51t-51 -21t-51 21t-21 51t21 51t51 21zM320 696h624q20 0 34 -14t14 -34t-14 -34
+t-34 -14h-624q-20 0 -34 14t-14 34t14 34t34 14zM320 432h624q20 0 34 -14t14 -34t-14 -34t-34 -14h-624q-20 0 -34 14t-14 34t14 34t34 14zM320 432h624q20 0 34 -14t14 -34t-14 -34t-34 -14h-624q-20 0 -34 14t-14 34t14 34t34 14zM320 168h624q20 0 34 -14t14 -34
+t-14 -34t-34 -14h-624q-20 0 -34 14t-14 34t14 34t34 14z" />
+    <glyph glyph-name="uniE621" unicode="&#xe621;" 
+d="M951 699q-10 0 -26.5 2t-53 14.5t-57.5 31.5q-22 20 -35.5 51.5t-16.5 53.5l-3 22q-122 22 -246 22q-54 0 -115.5 -5.5t-96.5 -11.5l-34 -5q-1 -10 -3.5 -24.5t-16.5 -48t-35 -52.5q-22 -20 -58 -32.5t-54.5 -15t-24.5 -2.5q-10 -161 4 -300q5 -57 15 -103.5t29 -96.5
+t51 -94.5t78 -81.5t114 -76.5t96.5 -53t46.5 -21.5q121 53 208 110q40 27 71 54t61.5 64.5t51.5 87t31 108.5q13 77 17.5 178t2.5 163zM682 52q-64 -42 -162 -87v422h-366v5q0 3 -1 4q-5 52 -5.5 114t0.5 98l2 36q8 1 21.5 2.5t43.5 12.5t49 27q17 16 28 41.5t14 44.5l3 18
+q103 18 204 18h7v-421h349q-4 -41 -10 -76q-15 -93 -61 -153.5t-116 -105.5z" />
+    <glyph glyph-name="uniE622" unicode="&#xe622;" 
+d="M148 -83q-35 0 -60 24.5t-25 59.5v598q0 35 25 60t60 25h263q33 0 57 -22t27 -54v-4q2 -14 12.5 -23.5t24.5 -9.5h344q35 0 60 -25t25 -60v-485q0 -35 -25 -59.5t-60 -24.5h-728zM128 17q0 -15 10.5 -26t25.5 -11h695q15 0 25.5 11t10.5 26v385h-767v-385zM895 467
+q0 42 -36 42h-327q-32 0 -56 20.5t-28 51.5l-1 8q-1 14 -11.5 23.5t-24.5 9.5h-247q-15 0 -25.5 -11t-10.5 -26v-118h767z" />
+    <glyph glyph-name="uniE623" unicode="&#xe623;" 
+d="M1020 -79l-271 271q63 99 63 216q0 110 -54.5 203t-147.5 147t-202.5 54t-202.5 -54t-147 -147t-54 -203t54 -203t147 -147t203 -54q111 0 206 57l273 -273zM154 408q0 105 74.5 179t179.5 74t179 -74t74 -179t-74 -179t-179 -74t-179.5 74t-74.5 179z" />
+    <glyph glyph-name="uniE624" unicode="&#xe624;" 
+d="M842 714h-660q-12 0 -21 -8.5t-9 -21.5v-420q0 -12 9 -21t21 -9h660q12 0 21 9t9 21v420q0 13 -9 21.5t-21 8.5zM842 264h-660v420h660v-420zM902 834h-780q-37 0 -63.5 -26.5t-26.5 -63.5v-600q0 -37 26.5 -63.5t63.5 -26.5h300v-36l-187 -25q-10 -2 -16.5 -10.5
+t-6.5 -18.5q0 -12 9 -21t21 -9h540q12 0 21 9t9 21q0 10 -6.5 18.5t-16.5 10.5l-187 25v36h300q37 0 63.5 26.5t26.5 63.5v600q0 37 -26.5 63.5t-63.5 26.5zM932 144q0 -12 -9 -21t-21 -9h-780q-12 0 -21 9t-9 21v600q0 12 9 21t21 9h780q12 0 21 -9t9 -21v-600z" />
+    <glyph glyph-name="uniE625" unicode="&#xe625;" 
+d="M21 800zM43 800zM64 800zM85 800zM107 800zM128 800zM149 800zM171 800zM192 800zM213 800zM235 800zM256 800zM277 800zM299 800zM320 800zM341 800zM363 800zM384 800zM405 800zM427 800zM448 800zM469 800zM491 800zM512 800zM533 800zM555 800zM576 800zM597 800z
+M619 800zM640 800zM661 800zM683 800zM704 800zM725 800zM747 800zM768 800zM789 800zM811 800zM832 800zM853 800zM875 800zM896 800zM917 800zM939 800zM960 800zM981 800zM1003 800zM0 779zM0 757zM0 736zM0 715zM0 693zM0 672zM0 651zM0 629zM0 608zM0 587zM0 565z
+M0 544zM0 523zM0 501zM0 480zM0 459zM0 437zM0 416zM0 395zM0 373zM0 352zM0 331zM0 309zM0 288zM0 267zM0 245zM0 224zM0 203zM0 181zM0 160zM0 139zM0 117zM0 96zM0 75zM0 53zM0 32zM0 11zM0 -11zM0 -32zM0 -53zM0 -75zM0 -96zM0 -117zM0 -139zM0 -160zM0 -181zM0 -203z
+M896 587q18 0 30.5 -12.5t12.5 -30.5t-13 -30l-512 -512q-12 -13 -30 -13t-30 13l-256 256q-13 12 -13 30t12.5 30.5t30.5 12.5t30 -13l226 -226l482 482q12 13 30 13z" />
+    <glyph glyph-name="uniE626" unicode="&#xe626;" 
+d="M52 647v125q0 29 21.5 50.5t50.5 21.5h249q6 0 13 -1q28 -7 44 -27.5t16 -48.5v-240q0 -7 -1 -15q-3 -22 -21 -40t-40 -21q-8 -1 -16 -1h-239q-28 0 -48.5 16t-27.5 43q-1 8 -1 20v118zM446 242q0 8 -2 16q-6 27 -26.5 43.5t-48.5 16.5h-241q-6 0 -13 -1
+q-26 -4 -44.5 -24.5t-18.5 -46.5v-125v-119q0 -12 1 -20q7 -26 27 -42.5t48 -16.5h242q9 0 17 2q26 6 42.5 26.5t16.5 47.5v243zM369 0h-241v241h241v-241zM973 768q0 13 -4 25q-8 24 -28 37.5t-46 13.5h-239q-6 0 -14 -1q-26 -3 -44.5 -23.5t-18.5 -46.5q-1 -10 -1 -126
+v-120v-2q1 -32 22.5 -53.5t53.5 -21.5q121 -1 243 0q32 0 54 22t22 54v242zM896 527h-241v241h241v-241zM973 242q0 12 -4 24q-8 24 -28 38t-46 14h-239q-7 0 -14 -1q-26 -4 -44.5 -24.5t-18.5 -46.5q-1 -10 -1 -125v-118q0 -10 1 -13q4 -30 25 -48.5t51 -18.5h241
+q33 0 55 22t22 54v243zM896 0h-241v241h241v-241z" />
+    <glyph glyph-name="uniE627" unicode="&#xe627;" 
+d="M778 553l-55 5q-11 43 -32 80l34 42q6 7 5.5 17t-7.5 17l-69 69q-7 6 -16.5 6.5t-17.5 -5.5l-41 -33q-38 22 -81 33l-5 52q-1 10 -8.5 16.5t-17.5 6.5h-98q-9 0 -16.5 -6.5t-8.5 -16.5l-5 -52q-43 -11 -82 -34l-40 34q-8 6 -17.5 5.5t-16.5 -7.5l-69 -69
+q-7 -7 -7.5 -16.5t5.5 -17.5l34 -41q-22 -38 -33 -81l-53 -5q-10 -1 -16.5 -8t-6.5 -17v-97q0 -10 6.5 -17t16.5 -8l55 -5q11 -42 34 -79h-1l-35 -43q-6 -7 -6 -17t7 -17l70 -68q7 -7 16.5 -7.5t17.5 5.5l42 35q1 0 1 0.5t1 1.5q36 -22 78 -32v-1v-1l5 -55q1 -10 8.5 -16.5
+t16.5 -6.5h98q10 0 17 6.5t8 16.5l6 55l-1 1v1q42 11 79 32l1 -1l43 -36q7 -6 17 -5.5t17 7.5l69 69q7 7 7.5 16.5t-5.5 17.5l-36 42v1h-1q22 37 33 79v-1h1l55 6q10 1 16.5 8t6.5 17v97q0 10 -6.5 17t-16.5 8zM418.5 279q-84.5 0 -144 59.5t-59.5 143.5t59.5 143.5
+t144 59.5t144 -59.5t59.5 -143.5t-59.5 -143.5t-144 -59.5zM549 482q0 -54 -38 -92t-92.5 -38t-92.5 38t-38 92t38 92t92.5 38t92.5 -38t38 -92zM826 118l12 -3q6 -2 12 -5l10 -6l6 -11q4 -6 5 -11l3 -12l-3 -11q-2 -6 -5 -12l-6 -10l-11 -6q-6 -4 -11 -5l-12 -3l-12 3
+q-6 1 -12 5l-11 6l-6 10q-3 5 -5 12l-3 11l3 12q1 5 5 11l6 11l11 6q5 3 12 5zM846 231h-41q-10 0 -11 -10l-2 -22q-18 -5 -35 -15l-17 15q-3 2 -7 2t-7 -3l-30 -29q-7 -7 -1 -14l15 -18q-9 -17 -14 -34l-23 -2q-10 -1 -10 -11v-41q0 -10 10 -11l24 -2q5 -18 14 -34v0
+l-15 -18q-6 -7 0 -14l30 -30q3 -3 7 -3t7 3l18 15q1 0 1 1q16 -10 34 -14h-1v-1l3 -24q0 -9 10 -9h42q9 0 10 9l3 24v1h-1q18 5 34 14v-1l19 -15q3 -2 6 -2q5 0 8 3l29 29q7 7 1 14l-15 18q0 1 -1 1q10 16 14 33h1l23 2q10 1 10 11v41q0 10 -10 11l-23 2q-5 18 -14 34l15 18
+q6 7 -1 14l-29 29q-3 4 -8 4q-4 0 -7 -3l-17 -14q-17 9 -35 14l-2 22q-1 10 -11 10z" />
+    <glyph glyph-name="uniE628" unicode="&#xe628;" 
+d="M768 -68h-299q-106 0 -181 76t-75 183.5t75 184t181 76.5h171v-87h-171q-70 0 -120 -50.5t-50 -122.5t50 -122.5t120 -50.5h299q71 0 121 50.5t50 122.5q0 82 -63 134l19 91q59 -34 94 -94t35 -131q0 -108 -75 -184t-181 -76zM806 362q3 -1 5 -1v-2l-5 2v1v0zM806 361
+q-16 -91 -87 -151.5t-164 -60.5h-171v86h171q70 0 120 50.5t50 122.5t-50 122.5t-120 50.5h-299q-71 0 -121 -50.5t-50 -122.5q0 -81 64 -133l-20 -92q-59 34 -94 94t-35 131q0 108 75 184t181 76h299q94 0 165 -62t87 -154h-7v-3q1 0 3.5 -0.5t3.5 -0.5q4 -22 4 -40
+q0 -22 -5 -46l-6 3zM811 452v-5q-1 0 -2 0.5t-2 0.5v2v2h4z" />
+    <glyph glyph-name="uniE629" unicode="&#xe629;" 
+d="M671 744h-120v60q0 25 -18 42.5t-42 17.5h-120q-25 0 -42.5 -17.5t-17.5 -42.5v-60h-120v-120h480v120zM491 744h-120v60h120v-60zM791 564v150q0 12 -9 21t-21 9h-60v-60h30v-120h-180l-180 -180v-240h-240v540h30v60h-60q-13 0 -21.5 -9t-8.5 -21v-600q0 -12 8.5 -21
+t21.5 -9h270v-180h600v660h-180zM551 479v-95h-95zM911 -36h-480v360h180v180h300v-540z" />
+    <glyph glyph-name="uniE62A" unicode="&#xe62a;" 
+d="M124 695q-32 0 -55 -23t-23 -55v-699q0 -32 23 -55t55 -23h543q32 0 55 23t23 55v544l-233 233h-388zM667 -82h-543v699h310v-233h233v-466zM512 462v123l123 -123h-123zM201 306h389v-77h-389v77zM201 151h389v-78h-389v78zM745 928h-388q-32 0 -55 -23t-23 -55v-78h78
+v78h310v-233h233v-466h-77v-78h77q32 0 55 23t23 55v544zM745 695v123l123 -123h-123z" />
+    <glyph glyph-name="uniE62B" unicode="&#xe62b;" 
+d="M947 186q13 -13 13 -26q0 -16 -19 -26l-64 -32q-3 0 -6.5 -3t-6.5 -3q-13 0 -13 6l-345 192l-58 -32h-6q6 -19 6 -51q-5 -40 -29.5 -75.5t-66.5 -58.5q-78 -45 -141 -45q-70 0 -109 38q-20 21 -31.5 48.5t-6.5 54.5q5 40 29.5 75.5t66.5 58.5q78 45 141 45q25 0 77 -13
+q0 3 1.5 4.5t5 4t5.5 4.5l64 38l-70 32q-3 0 -4.5 2t-3.5 5.5t-5 5.5q-29 -19 -77 -19q-78 0 -140 45q-38 23 -61.5 59t-28.5 75q0 64 38.5 102.5t108.5 38.5q78 0 141 -45q42 -23 66.5 -58.5t29.5 -75.5q0 -32 -6 -45h6l58 -32l345 192q3 0 6 1.5t5 3.5l2 1q13 0 13 -6
+l64 -32q19 -10 19 -32q0 -13 -13 -26l-256 -198zM314 627q-54 32 -96 32q-7 -1 -18 -3.5t-16.5 -3.5t-12 -4t-11.5 -8q-24 -24 -12 -57.5t50 -57.5q54 -32 96 -32q39 0 58 19q24 24 12 57.5t-50 57.5zM352 256q-13 13 -58 13q-42 0 -96 -32q-38 -20 -50 -52.5t12 -56.5
+q19 -19 58 -19q42 0 96 32q38 24 50 57.5t-12 57.5zM557 320l371 288l-64 32l-384 -218v-57l-83 -45l6 -6h7l12 -13l13 -13l77 45zM448 416q0 29 19 38l7 7l-45 19l-13 -13q0 -3 -3 -6t-3 -7h-7l-6 -6zM576 384q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23z
+M928 160l-262 205l-84 -71q-6 0 -6 -6l288 -166z" />
+    <glyph glyph-name="uniE62C" unicode="&#xe62c;" 
+d="M932 506q-12 0 -20.5 -8.5t-8.5 -20.5v-514h-813v813h553q12 0 20.5 8.5t8.5 20.5t-8.5 20.5t-20.5 8.5h-611v-929h929v572q0 12 -8.5 20.5t-20.5 8.5zM206 515h233q12 0 20.5 8.5t8.5 20.5t-8.5 20.5t-20.5 8.5h-233q-12 0 -20.5 -8.5t-8.5 -20.5t8.5 -20.5t20.5 -8.5z
+M497 399h-291q-12 0 -20.5 -8.5t-8.5 -20.5t8.5 -20.5t20.5 -8.5h291q12 0 20.5 8.5t8.5 20.5t-8.5 20.5t-20.5 8.5zM177 195q0 -12 8.5 -20.5t20.5 -8.5h581q12 0 20.5 8.5t8.5 20.5t-8.5 20.5t-20.5 8.5h-581q-12 0 -20.5 -8.5t-8.5 -20.5zM980.5 856.5
+q-9.5 7.5 -21.5 6.5t-20 -10l-348 -406q-8 -9 -7 -21t10 -20t21 -7t20 10l349 407q7 9 6.5 21t-10 19.5z" />
+  </font>
+</defs></svg>

BIN
urule-console-js/src/css/iconfont.ttf


BIN
urule-console-js/src/css/iconfont.woff


+ 59 - 0
urule-console-js/src/editor/Math.uuid.js

@@ -0,0 +1,59 @@
+(function() {
+  // Private array of chars to use
+  var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
+
+  Math.uuid = function (len, radix) {
+    var chars = CHARS, uuid = [], i;
+    radix = radix || chars.length;
+
+    if (len) {
+      // Compact form
+      for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
+    } else {
+      // rfc4122, version 4 form
+      var r;
+
+      // rfc4122 requires these characters
+      uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
+      uuid[14] = '4';
+
+      // Fill in random data.  At i==19 set the high bits of clock sequence as
+      // per rfc4122, sec. 4.1.5
+      for (i = 0; i < 36; i++) {
+        if (!uuid[i]) {
+          r = 0 | Math.random()*16;
+          uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
+        }
+      }
+    }
+
+    return uuid.join('');
+  };
+
+  // A more performant, but slightly bulkier, RFC4122v4 solution.  We boost performance
+  // by minimizing calls to random()
+  Math.uuidFast = function() {
+    var chars = CHARS, uuid = new Array(36), rnd=0, r;
+    for (var i = 0; i < 36; i++) {
+      if (i==8 || i==13 ||  i==18 || i==23) {
+        uuid[i] = '-';
+      } else if (i==14) {
+        uuid[i] = '4';
+      } else {
+        if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
+        r = rnd & 0xf;
+        rnd = rnd >> 4;
+        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
+      }
+    }
+    return uuid.join('');
+  };
+
+  // A more compact, but less performant, RFC4122v4 solution:
+  Math.uuidCompact = function() {
+    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+      return v.toString(16);
+    });
+  };
+})();

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
urule-console-js/src/editor/bootstrap-table.min.css


Разлика између датотеке није приказан због своје велике величине
+ 6 - 0
urule-console-js/src/editor/bootstrap-table.min.js


+ 269 - 0
urule-console-js/src/editor/common/ComparisonOperator.js

@@ -0,0 +1,269 @@
+/**
+ * @author GJ
+ */
+urule.ComparisonOperator=function(menuCallFun){
+	this.inputType=null;
+	this.operator="";
+	this.container=generateContainer();
+	URule.setDomContent(this.container,"请选择比较操作符");
+	this.container.css({
+		"font-size":"13px",
+		"color":"red",
+		"fontWeight":"bold",
+		"margin-right":"3px"
+	});
+	var self=this;
+	var onClick=function(menu){
+		self.setOperator(menu.name);
+	};
+	self.menu=new URule.menu.Menu({
+		onHide:function(){
+			menuCallFun();
+		},
+		menuItems:[{
+			label : "大于",
+			name:"GreaterThen",
+			onClick : onClick
+		}, {
+			label : "大于或等于",
+			name:"GreaterThenEquals",
+			onClick : onClick
+		}, {
+			label : "小于",
+			name:"LessThen",
+			onClick : onClick
+		}, {
+			label : "小于或等于",
+			name:"LessThenEquals",
+			onClick : onClick
+		}, {
+			label : "等于",
+			name:"Equals",
+			onClick : onClick
+		},  {
+			label : "等于(不分大小写)",
+			name:"EqualsIgnoreCase",
+			onClick : onClick
+		},  {
+			label : "开始于",
+			name:"StartWith",
+			onClick : onClick
+		}, {
+			label : "不开始于",
+			name:"NotStartWith",
+			onClick : onClick
+		},{
+			label : "结束于",
+			name:"EndWith",
+			onClick : onClick
+		},{
+			label : "不结束于",
+			name:"NotEndWith",
+			onClick : onClick
+		}, {
+			label : "不等于",
+			name:"NotEquals",
+			onClick : onClick
+		}, {
+			label : "不等于(不分大小写)",
+			name:"NotEqualsIgnoreCase",
+			onClick : onClick
+		}, {
+			label : "在集合",
+			name:"In",
+			onClick : onClick
+		}, {
+			label : "不在集合",
+			name:"NotIn",
+			onClick : onClick
+		}, {
+			label : "为空",
+			name:"Null",
+			onClick : onClick
+		}, {
+			label : "不为空",
+			name:"NotNull",
+			onClick : onClick
+		}, {
+			label : "匹配正则表达式",
+			name:"Match",
+			onClick : onClick
+		}, {
+			label : "不匹配正则表达式",
+			name:"NotMatch",
+			onClick : onClick
+		}]
+	});
+	this.container.click(function(e){
+		self.menu.show(e);
+	});
+	
+};
+urule.ComparisonOperator.prototype.initRightValue=function(data){
+	if(!this.inputType){
+		return;
+	}
+	this.inputType.setValueType(data["valueType"],data);
+};
+
+urule.ComparisonOperator.prototype.setOperator=function(operator){
+	switch(operator){
+	case "GreaterThen":
+		this.operator="GreaterThen";
+		URule.setDomContent(this.container,"大于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "GreaterThenEquals":
+		this.operator="GreaterThenEquals";
+		URule.setDomContent(this.container,"大于或等于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "LessThen":
+		this.operator="LessThen";
+		URule.setDomContent(this.container,"小于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "LessThenEquals":
+		this.operator="LessThenEquals";
+		URule.setDomContent(this.container,"小于或等于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "StartWith":
+		this.operator="StartWith";
+		URule.setDomContent(this.container,"开始于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "Equals":
+		this.operator="Equals";
+		URule.setDomContent(this.container,"等于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "EqualsIgnoreCase":
+		this.operator="EqualsIgnoreCase";
+		URule.setDomContent(this.container,"等于(不分大小写)");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "NotStartWith":
+		this.operator="NotStartWith";
+		URule.setDomContent(this.container,"不开始于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "EndWith":
+		this.operator="EndWith";
+		URule.setDomContent(this.container,"结束于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();	
+		break;
+	case "NotEndWith":
+		this.operator="NotEndWith";
+		URule.setDomContent(this.container,"不结束于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();
+		break;
+	case "NotEquals":
+		this.operator="NotEquals";
+		URule.setDomContent(this.container,"不等于");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();
+		break;
+	case "NotEqualsIgnoreCase":
+		this.operator="NotEqualsIgnoreCase";
+		URule.setDomContent(this.container,"不等于(不分大小写)");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();
+		break;
+	case "In":
+		this.operator="In";
+		URule.setDomContent(this.container,"在集合");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType("之中");		
+		break;
+	case "NotIn":
+		this.operator="NotIn";
+		URule.setDomContent(this.container,"不在集合");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType("之中");		
+		break;
+	case "Null":
+		this.operator="Null";
+		URule.setDomContent(this.container,"为空");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+			this.inputType=null;
+		}
+		break;
+	case "NotNull":
+		this.operator="NotNull";
+		URule.setDomContent(this.container,"不为空");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+			this.inputType=null;
+		}
+		break;
+	case "Match":
+		this.operator="Match";
+		URule.setDomContent(this.container,"匹配正则表达式");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();
+		break;
+	case "NotMatch":
+		this.operator="NotMatch";
+		URule.setDomContent(this.container,"不匹配正则表达式");
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+		}
+		this.inputType=new urule.InputType();
+		break;
+	}
+	window._setDirty();
+};
+urule.ComparisonOperator.prototype.getOperator=function(){
+	if(this.operator==""){
+		throw "请选择比较操作符!";
+	}
+	return this.operator;
+};
+urule.ComparisonOperator.prototype.getInputType=function(){
+	return this.inputType;
+};
+urule.ComparisonOperator.prototype.getContainer=function(){
+	return this.container;
+};

+ 120 - 0
urule-console-js/src/editor/common/ComplexArithmetic.js

@@ -0,0 +1,120 @@
+/**
+ * @author GJ
+ */
+urule.ComplexArithmetic=function(rule){
+	this.container=$("<span>");
+	this.operator="";
+	this.rule=rule;
+	this.selectorLabel=generateContainer();
+	this.selectorLabel.css({
+		"fontWeight":"blod"
+	});
+	this.container.append(this.selectorLabel);
+	this.nextType=null;
+	var self=this;
+	var onClick=function(menu){
+		self.setOperator(menu.name);
+	}
+	self.menu=new URule.menu.Menu({
+		menuItems:[{
+			label : "+",
+			name:"Add",
+			onClick : onClick
+		}, {
+			label : "-",
+			name:"Sub",
+			onClick : onClick
+		}, {
+			label : "x",
+			name:"Mul",
+			onClick : onClick
+		}, {
+			label : "÷",
+			name:"Div",
+			onClick : onClick
+		}, {
+			label : "%",
+			name:"Mod",
+			onClick : onClick
+		}, {
+			label : "删除",
+			onClick : function(){
+				window._setDirty();
+				if(self.nextType){
+					self.nextType.getContainer().remove();
+					self.nextType=null;
+					URule.setDomContent(self.selectorLabel,".");
+					self.selectorLabel.css({
+						"color":"#fff",
+						"padding-left":"0px",
+						"padding-right":"0px"
+					});
+				}
+			}
+		}]
+	});
+	this.selectorLabel.click(function(e){
+		self.menu.show(e);
+	});
+	
+};
+urule.ComplexArithmetic.prototype.setOperator=function(operator){
+	window._setDirty();
+	this.operator=operator;
+	this.info="";
+	switch(operator){
+	case "Add":
+		this.info="+";
+		break;
+	case "Sub":
+		this.info="-";
+		break;
+	case "Mul":
+		this.info="x";
+		break;
+	case "Div":
+		this.info="÷";
+		break;
+	case "Mod":
+		this.info="%";
+		break;
+	}
+	URule.setDomContent(this.selectorLabel,this.info);
+	this.selectorLabel.css({
+		"color":"green",
+		"padding-left":"4px",
+		"padding-right":"4px"
+	});
+	if(!this.nextType){
+		this.nextType = new urule.NextType(this.rule);
+		this.container.append(this.nextType.getContainer());
+	}
+};
+urule.ComplexArithmetic.prototype.initData=function(data){
+	if(!data){
+		return;
+	}
+	var type=data["type"];
+	this.setOperator(type);
+	this.nextType.initData(data);
+};
+urule.ComplexArithmetic.prototype.getDisplayContainer=function(){
+	if(this.nextType){
+		var container=$("<span>"+this.info+"</span>");
+		container.append(this.nextType.getDisplayContainer());
+		return container;
+	}
+	return null;
+};
+urule.ComplexArithmetic.prototype.toXml=function(){
+	if(!this.nextType){
+		return "";
+	}
+	var xml="<complex-arith type=\""+this.operator+"\">";
+	xml+=this.nextType.toXml();		
+	xml+="</complex-arith>";
+	return xml;
+};
+urule.ComplexArithmetic.prototype.getContainer=function(){
+	return this.container;
+};

+ 74 - 0
urule-console-js/src/editor/common/ConditionListDialog.js

@@ -0,0 +1,74 @@
+/**
+ * @author GJ
+ */
+urule.ConditionListDialog=function(project, category, colData){
+	this.project = project;
+	this.category = category;
+	this.colData = colData;
+	this.init();
+};
+urule.ConditionListDialog.prototype.open=function(doSuccess){
+	var self = this;
+	self.dialog.dialog("open");
+	self.doSuccess=doSuccess;
+};
+
+urule.ConditionListDialog.prototype.setOption=function(option){
+	this.dialog.dialog("option",option);
+};
+
+urule.ConditionListDialog.prototype.refresh=function(project, category, variable){
+	var url="urule?action=loadcommonconditions&project="+project + "&category=" + category + "&variable=" + this.colData.variableCategory + "." + this.colData.variableLabel;
+	this.table.bootstrapTable("refresh", {url : url});
+};
+
+urule.ConditionListDialog.prototype.init=function(){
+	this.dialog=$("<div>");
+	var tableHtml="<table data-height=\"299\">";
+	tableHtml+="<thead><tr>";
+	tableHtml+="<th data-field=\"name\">名称</th>";
+	tableHtml+="<th data-field=\"condition\">条件</th>";
+	tableHtml+="</tr></thead></table>";
+	this.table=$(tableHtml);
+	var url="urule?action=loadcommonconditions&project="+this.project + "&category=" + this.category + "&variable=" + this.colData.variableCategory + "." + this.colData.variableLabel;
+	this.table.bootstrapTable({
+		url:url,
+		onDblClickRow : function(data){
+			self.doSuccess(data.condition);
+		}
+	});
+	this.dialog.append(this.table);
+	var self=this;
+	this.dialog.dialog({
+		title:"常用条件列表【"+(self.variable || "")+"】",
+		height:300,
+		width:500,
+		autoOpen:false,
+		modal:false,
+		show:false,
+		open : function(){
+			self.isShow = true;
+		},
+		close :function(){
+			self.isShow = false;
+		},
+		buttons:[{
+			text:"关闭",
+			click:function(){
+				$(this).dialog("destroy");
+			}
+		}]
+	});
+};
+
+function __formatDate(time){
+	var datetime = new Date();  
+    datetime.setTime(time);
+	var year = datetime.getFullYear();  
+	var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;  
+	var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();  
+	var hour = datetime.getHours()< 10 ? "0" + datetime.getHours() : datetime.getHours();  
+	var minute = datetime.getMinutes()< 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();  
+	var second = datetime.getSeconds()< 10 ? "0" + datetime.getSeconds() : datetime.getSeconds();  
+	return year + "-" + month + "-" + date+" "+hour+":"+minute+":"+second;  
+};

+ 99 - 0
urule-console-js/src/editor/common/ConstantValue.js

@@ -0,0 +1,99 @@
+/**
+ * @author GJ
+ */
+urule.ConstantValue=function(arithmetic,data){
+	this.arithmetic=arithmetic;
+	this.container=$("<span>");
+	this.label=generateContainer();
+	this.container.append(this.label);
+	this.label.css({
+		"color":"#0174DF",
+	});
+	this.label.prop("innerText","请选择常量");
+	if(arithmetic){
+		this.container.append(arithmetic.getContainer());		
+	}
+	if(data){
+		this.setValue(data);
+		arithmetic.initData(data["arithmetic"]);
+	}
+	this.initMenu();
+	window._ConstantValueArray.push(this);
+};
+urule.ConstantValue.prototype.setValue=function(data){
+	this.category=data["constantCategory"];
+	this.constantName=data["constantName"];
+	this.constantLabel=data["constantLabel"];
+	URule.setDomContent(this.label,this.category+"."+this.constantLabel);
+	window._setDirty();
+};
+
+urule.ConstantValue.prototype.initMenu=function(constantLibraries){
+	var data=window._uruleEditorConstantLibraries;
+	if(constantLibraries){
+		data=constantLibraries;
+	}
+	if(!data){
+		return;
+	}
+	var self,onClick,config;
+	self=this;
+	onClick=function(menuItem){
+		self.setValue({
+			constantCategory:menuItem.parent.parent.label,
+			constantLabel:menuItem.label,
+			constantName:menuItem.name
+		});
+	};
+	config={menuItems:[]};
+	$.each(data,function(index,item){
+		var categories=item["categories"]; 
+		$.each(categories,function(i,category){
+			var menuItem={
+				label:category.label
+			}
+			var constants=category["constants"];
+			$.each(constants,function(j,constant){
+				if(!menuItem.subMenu){
+					menuItem.subMenu={menuItems:[]};
+				}
+				menuItem.subMenu.menuItems.push({
+					name:constant.name,
+					label:constant.label,
+					onClick:onClick
+				});
+			});
+			config.menuItems.push(menuItem);
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+};
+
+urule.ConstantValue.prototype.getDisplayContainer=function(){
+	var container=$("<span>"+this.category+"."+this.constantLabel+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);			
+		}	
+	}
+	return container;
+};
+
+urule.ConstantValue.prototype.toXml=function(){
+	if(!this.category){
+		throw "常量不能为空!";
+	}
+	var xml="const-category=\""+this.category+"\" const=\""+this.constantName+"\" const-label=\""+this.constantLabel+"\"";
+	return xml;
+};
+urule.ConstantValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 35 - 0
urule-console-js/src/editor/common/Context.js

@@ -0,0 +1,35 @@
+/**
+ * @author GJ
+ */
+import Raphael from 'raphael';
+
+urule.Context=function(container,rule){
+	this.container=container;
+	this.paper = new Raphael(this.container.get(0), "100%", "100%");
+	this.rule=rule;
+	this.rootJoin=null;
+};
+urule.Context.prototype.putToNamedMap=function(referenceName,variableCategory){
+	this.rule.namedMap.set(referenceName,variableCategory);
+};
+urule.Context.prototype.deleteFromNamedMap=function(referenceName){
+	this.rule.namedMap.delete(referenceName);
+};
+urule.Context.prototype.getVariableCategoryFromNamedMap=function(referenceName){
+	return this.rule.namedMap.get(referenceName);
+};
+urule.Context.prototype.getCanvas=function(){
+	return this.container;
+};
+urule.Context.prototype.getPaper=function(){
+	return this.paper;
+};
+urule.Context.prototype.setRootJoin=function(join){
+	this.rootJoin=join;
+};
+urule.Context.prototype.getRootJoin=function(){
+	return this.rootJoin;
+};
+urule.Context.prototype.getTotalChildrenCount=function(){
+	return this.rootJoin.getChildrenCount();
+};

+ 265 - 0
urule-console-js/src/editor/common/DSLEditor.js

@@ -0,0 +1,265 @@
+/**
+ * @author GJ
+ */
+var urule={};
+var codeMirror=null;
+$(document).ready(function() {
+	CodeMirror.commands.autocomplete = function(cm) {
+        cm.showHint({hint: CodeMirror.hint.urule});
+    };
+	var codeEditor=document.getElementById("codeEditor");
+	codeMirror = CodeMirror.fromTextArea(codeEditor, {
+		lineNumbers: true,
+	    mode: "rulemixed",
+        extraKeys: {"Alt-/": "autocomplete"}
+	});
+	codeMirror.on("change",function(cm,e){
+		var value = cm.getValue();
+		if(e.text=="."){
+			CodeMirror.commands.autocomplete(codeMirror);
+		}
+		$(".CodeMirror-code").find("pre").children(".error").removeClass("error");
+		$(".CodeMirror-code").find("pre").attr("title", "");
+		if(cm.validator){
+			clearTimeout(cm.validator);
+		}
+		if($.trim(value)){
+			cm.validator = setTimeout(function() {
+				$.ajax({
+					url:"urule?action=checkdsl",
+					type : "POST",
+					data :{
+						content : value
+					},
+					error:function(req,error){
+						URule.alert("校验失败!");
+					},
+					success:function(infos){
+						if(infos && infos.length>0){
+							for(var i = 0; i<infos.length; i ++) {
+								var pre = $(".CodeMirror-code").find("pre").eq(infos[i].line-1);
+								pre.children("span").addClass("error");
+								pre.attr("title",infos[i].message);
+								
+							}
+						}
+					}
+				});
+				cm.validator = null;
+			}, 1000);
+		}
+	})
+	init();
+});
+
+function init(){
+	var height=$(document).height()-55;
+	codeMirror.setSize("100%",height)
+	window._dirty=false;
+	var file=_getRequestParameter("file");
+	if(!file || file.length<1){
+		URule.alert("当前编辑器未指定具体规则文件!");
+		return;
+	}
+	var saveButton = '<div class="btn-group navbar-btn" style="margin-right:10px;" role="group" aria-label="...">'+
+						'<button id="saveButton" type="button" class="btn btn-default navbar-btn" ><i class="icon-save"></i> 保存</button>' + 
+						'<button id="saveButtonNewVersion" type="button" class="btn btn-default navbar-btn" ><i class="icon-save"></i> 保存新版本</button>' +
+					'</div>';
+	if(!hasPermission()) {
+		saveButton = '';
+	}else {
+		$(window).keydown(function(event) {
+			if(event.ctrlKey) {
+				if(event.keyCode == 83) {
+					save(file, false);
+				}
+			} else if (event.altKey) {
+				if(event.keyCode == 83) {
+					save(file, true);
+				}
+			}
+		});
+	}
+	var toolbarHtml='<nav class="navbar navbar-default">'+
+		'<div>'+
+	        '<div class="collapse navbar-collapse">'+ saveButton +
+	            '<button id="checkDSL" type="button" class="btn btn-default navbar-btn"><i class="icon-ok-sign"></i> 语法检查</button>'+
+	            '<div class="btn-group navbar-btn" style="margin-left:10px;" role="group" aria-label="...">'+
+	                '<button id="addVarButton" type="button" class="btn btn-default"><i class="icon-tasks"></i> 导入变量库</button>'+
+	                '<button id="addConstantsButton" type="button" class="btn btn-default"><i class="icon-th-list"></i> 导入常量库</button>'+
+	                '<button id="addActionButton" type="button" class="btn btn-default"><i class="icon-bolt"></i> 导入动作库</button>'+
+	                '<button id="configParameterButton" type="button" class="btn btn-default"><i class="icon-th"></i> 导入参数库</button>'+	
+	            '</div>'+
+	       ' </div>'+
+	    '</div>'+
+	'</nav>';
+	var toolbar=$(toolbarHtml);
+	toolbar.css({
+		padding:"4px",
+		diaplay:"inline-block"
+	});
+	$("#toolbarContainer").append(toolbar);
+	$("#saveButton").click(function(){
+		save(file,false);
+	});
+	$("#saveButtonNewVersion").click(function(){
+		save(file,true);
+	});
+	$("#checkDSL").click(function(){
+		checkDSL();
+	});
+	
+	$("#configParameterButton").click(function(){
+		var dialog=new urule.ResourceListDialog("ParameterLibrary",null,selectResource);
+		dialog.open();
+	});
+	$("#addVarButton").click(function(){
+		var dialog=new urule.ResourceListDialog("VariableLibrary",null,selectResource);
+		dialog.open();
+	});
+	$("#addConstantsButton").click(function(){
+		var dialog=new urule.ResourceListDialog("ConstantLibrary",null,selectResource);
+		dialog.open();
+	});
+	$("#addActionButton").click(function(){
+		var dialog=new urule.ResourceListDialog("ActionLibrary",null,selectResource);
+		dialog.open();
+	});
+	
+	window._dirty=false;
+	var url="urule?action=loaddsl&file="+file+"";
+	$.ajax({
+		cache:false,
+		url:url,
+		type:"GET",
+		error:function(req,error){
+			URule.alert("文件加载失败!");
+		},
+		success:function(data){
+			codeMirror.setValue(data);
+			$("#saveButton").addClass("disabled");
+			$("#saveButtonNewVersion").addClass("disabled");
+
+			codeMirror.on("change",function(){
+				setDirty();
+			});
+			loadResLib();
+		}
+	});
+};
+
+function selectResource(type,res){
+	codeMirror.replaceSelection("import"+type+" \""+res+"\";");
+	loadResLib();
+};
+
+function checkDSL(doSuccess){
+	var content=codeMirror.getValue();
+	if(!content || content.length<10){
+		URule.alert("请正确输入规则内容!");
+		return;
+	}
+	var url="urule?action=checkdsl";
+	$.ajax({
+		cache:false,
+		url:url,
+		type:"POST",
+		data:{content:content},
+		error:function(req,error){
+			URule.alert("语法检查失败!");
+
+		},
+		success:function(data){
+			if(!data || data.length <= 0){
+				if(doSuccess){
+					doSuccess();
+				}else{
+					URule.alert("语法正确!");
+
+				}
+				return;
+			}
+			URule.alert("语法不正确!");
+		}
+	});
+};
+function loadResLib(){
+	var file=_getRequestParameter("file");
+	var content=codeMirror.getValue();
+	if(!content || content.length<10){
+		URule.alert("请正确输入规则内容!");
+		return;
+	}
+	var url="urule?action=loaddslreslib";
+	$.ajax({
+		cache:false,
+		url:url,
+		type:"POST",
+		data:{content:content},
+		error:function(req,error){
+			//alert("资源库加载失败!");
+		},
+		success:function(data){
+			codeMirror._library=data;
+		}
+	});
+};
+function save(file,newVersion){
+	if($("#saveButton").hasClass("disabled")){
+		return false;
+	}
+	var url="urule?action=savedsl&file="+file+"";
+	var content=codeMirror.getValue();
+	$.ajax({
+		cache:false,
+		url:url,
+		type:"POST",
+		data:{content:content, newVersion:newVersion},
+		error:function(req,error){
+			URule.alert("保存失败!");
+		},
+		success:function(data){
+			cancelDirty();
+		}
+	});
+};
+
+function setDirty(){
+	if(window._dirty){
+		return;
+	}
+	window._dirty=true;
+	$("#saveButton").html("<i class='icon-save'></i> *保存");
+	$("#saveButton").removeClass("disabled");
+	$("#saveButtonNewVersion").html("<i class='icon-save'></i> *保存新版本");
+	$("#saveButtonNewVersion").removeClass("disabled");
+};
+
+function cancelDirty(){
+	if(!window._dirty){
+		return;
+	}
+	window._dirty=false;
+	$("#saveButton").html("<i class='icon-save'></i> 保存");
+	$("#saveButton").addClass("disabled");
+	$("#saveButtonNewVersion").html("<i class='icon-save'></i> 保存新版本");
+	$("#saveButtonNewVersion").addClass("disabled");
+};
+
+function _getRequestParameter(name){
+	var value=null;
+	var params=window.location.search.substring(1).split("&");
+	for(var i=0;i<params.length;i++){
+		var param=params[i];
+		if(param.indexOf("=")==-1){
+			continue;
+		}
+		var pair=param.split("=");
+		var key=pair[0];
+		if(key==name){
+			value=pair[1];
+			break;
+		}
+	}
+	return value;
+};

+ 52 - 0
urule-console-js/src/editor/common/FunctionParameter.js

@@ -0,0 +1,52 @@
+/**
+ * @author GJ
+ */
+urule.FunctionParameter=function(rule){
+	this.container=$("<span>");
+	this.nameContainer=$("<span>");
+	this.rule=rule;
+	this.container.append(this.nameContainer);
+	this.nameContainer.css({
+		"color":"gray"
+	});
+};
+urule.FunctionParameter.prototype.initData=function(data){
+	if(!data){
+		return;
+	}
+	this.name=data.name;
+	URule.setDomContent(this.nameContainer,this.name+":");
+	if(data.needProperty || data.property){
+		this.functionProperty=new urule.FunctionProperty();
+		this.functionProperty.setProperty({name:data.property,label:data.propertyLabel});
+	}
+	this.inputType=new urule.InputType(null,null,this.functionProperty,this.rule);
+	var value=data.objectParameter;
+	if(value){
+		var valueType=value.valueType;		
+		this.inputType.setValueType(valueType, value);
+	}
+	this.container.append(this.inputType.getContainer());
+	if(this.functionProperty){
+		this.container.append($("<span>,</span>"));
+		this.container.append($("<span style='color:gray'>属性:</span>"));
+		this.container.append(this.functionProperty.getContainer());
+	}
+};
+urule.FunctionParameter.prototype.toXml=function(){
+	if(!this.name){
+		return "";
+	}
+	var xml="<function-parameter ";
+	xml+="name=\""+this.name+"\" ";
+	if(this.functionProperty){
+		xml+=this.functionProperty.toXml();
+	}
+	xml+=">";
+	xml+=this.inputType.toXml();
+	xml+="</function-parameter>";
+	return xml;
+};
+urule.FunctionParameter.prototype.getContainer=function(){
+	return this.container;
+};

+ 55 - 0
urule-console-js/src/editor/common/FunctionProperty.js

@@ -0,0 +1,55 @@
+/**
+ * @author GJ
+ */
+urule.FunctionProperty=function(){
+	this.container=$("<span>");
+	this.label=generateContainer();
+	this.container.append(this.label);
+	URule.setDomContent(this.label,"选择属性");
+	this.label.css({
+		"color":"#004C85",
+	});
+};
+urule.FunctionProperty.prototype.toXml=function(){
+	if(!this.variableName){
+		throw "请选择函数属性";
+	}
+	var xml="property-name=\""+this.variableName+"\"";
+	xml+=" property-label=\""+this.variableLabel+"\"";
+	return xml;
+};
+urule.FunctionProperty.prototype.initMenu=function(data){
+	if(!data){
+		return;
+	}
+	var self=this;
+	var onClick=function(menuItem){
+		self.setProperty({
+			name:menuItem.name,
+			label:menuItem.label,
+			datatype:menuItem.type
+		});
+	};
+	var menuConfig={menuItems:[]};
+	$.each(data,function(index,item){
+		menuConfig.menuItems.push({
+			name:item.name,
+			label:item.label,
+			datatype:item.type,
+			onClick:onClick
+		});
+	});
+	this.menu=new URule.menu.Menu(menuConfig);
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.FunctionProperty.prototype.setProperty=function(data){
+	window._setDirty();
+	this.variableName=data.name;
+	this.variableLabel=data.label,
+	URule.setDomContent(this.label,this.variableLabel);
+};
+urule.FunctionProperty.prototype.getContainer=function(){
+	return this.container;
+};

+ 113 - 0
urule-console-js/src/editor/common/FunctionValue.js

@@ -0,0 +1,113 @@
+/**
+ * @author GJ
+ */
+urule.FunctionValue=function(arithmetic,data,rule){
+	this.arithmetic=arithmetic;
+	this.container=$("<span>");
+	this.rule=rule;
+	this.leftParn=$("<span style='color:blue'>(</span>");
+	this.rightParn=$("<span style='color:blue'>)</span>");
+	this.label=generateContainer();
+	this.container.append(this.label);
+	this.label.css({
+		"color":"#008080"
+	});
+	this.functionContainer=$("<span></span>");
+	this.container.append(this.functionContainer);
+	URule.setDomContent(this.label,"请选择函数");
+	if(arithmetic){
+		this.container.append(arithmetic.getContainer());		
+	}
+	if(data){
+		this.setFunction(data);
+		if(arithmetic){
+			arithmetic.initData(data["arithmetic"]);
+		}
+	}
+	window._FunctionValueArray.push(this);
+	this.initMenu();
+};
+
+urule.FunctionValue.prototype.getDisplayContainer=function(){
+	var container=$("<span>"+this.functionName+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);			
+		}	
+	}
+	return container;
+};
+urule.FunctionValue.prototype.toXml=function(){
+	if(!this.functionLabel){
+		throw "请选择函数";
+	}
+	if(!this.functionName){
+		throw "请选择函数";
+	}
+	var xml=" function-label=\""+this.functionLabel+"\"";
+	xml+=" function-name=\""+this.functionName+"\"";
+	return xml;
+};
+
+urule.FunctionValue.prototype.initMenu=function(functionLibraries){
+	var data=window._uruleEditorFunctionLibraries;
+	if(functionLibraries){
+		data=functionLibraries;
+	}
+	var self,onClick,config;
+	self=this;
+	onClick=function(menuItem){
+		self.setFunction({
+			parameter:menuItem.parameter,
+			label:menuItem.label,
+			name:menuItem.name
+		});
+	};
+	config={menuItems:[]};
+	$.each(data||[],function(index,item){
+		config.menuItems.push({
+			name:item.name,
+			label:item.label,
+			parameter:item.argument,
+			onClick:onClick
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.FunctionValue.prototype.initData=function(data){
+	if(data){
+		this.setFunction(data);		
+	}
+};
+
+urule.FunctionValue.prototype.setFunction=function(data){
+	window._setDirty();
+	this.functionContainer.empty();
+	URule.setDomContent(this.label,data.label);
+	this.functionContainer.append(this.leftParn);
+	this.functionLabel=data.label;
+	this.functionName=data.name;
+	this.parameter=new urule.FunctionParameter(this.rule);
+	this.parameter.initData(data.parameter);
+	this.functionContainer.append(this.parameter.getContainer());
+	this.functionContainer.append(this.rightParn);
+};
+urule.FunctionValue.prototype.getFirstParameter=function(){
+	return this.firstParameter;
+};
+
+urule.FunctionValue.prototype.getParameter=function(){
+	return this.parameter;
+};
+
+urule.FunctionValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 236 - 0
urule-console-js/src/editor/common/InputType.js

@@ -0,0 +1,236 @@
+/**
+ * @author GJ
+ */
+urule.InputType=function(endInfo,tip,functionProperty,rule){
+	this.container=$("<span>");
+	this.label=generateContainer();
+	this.rule=rule;
+	this.container.append(this.label);
+	if(tip){
+		URule.setDomContent(this.label,tip);
+		this.label.css({
+			"color":"gray"
+		});		
+	}else{
+		URule.setDomContent(this.label,"选择值类型");
+		this.label.css({
+			"color":"blue"
+		});
+	}
+	this.functionProperty=functionProperty;
+	this.variableValue=null;
+	this.simpleValue=null;
+	this.referenceValue=null;
+	this.methodValue=null;
+	this.constantValue=null;
+	this.functionValue=null;
+	this.dataContainer=$("<span>");
+	this.container.append(this.dataContainer);
+	this.type="";
+	var self=this;
+	var onClick=function(menuItem){
+		self.setValueType(menuItem.name);
+	};
+	self.menu=new URule.menu.Menu({
+		menuItems:[{
+			label : "输入值",
+			name:"Input",
+			onClick : onClick
+		}, {
+			label : "选择变量",
+			name:"Variable",
+			onClick : onClick
+		}, {
+			label : "选择常量",
+			name:"Constant",
+			onClick : onClick
+		}, {
+			label : "选择参数",
+			name:"Parameter",
+			onClick : onClick
+		}]
+	});
+	if(this.rule){
+		self.menu.menuItems.push({
+			label:"选择命名变量",
+			name:"NamedReference",
+			onClick : onClick
+		});
+	}
+	self.menu.menuItems.push({
+		label : "选择方法",
+		name  : "Method",
+		onClick : onClick
+	}, {
+		label : "选择函数",
+		name  : "CommonFunction",
+		onClick : onClick
+	});
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+
+	if(endInfo){
+		var endInfoContainer=$("<span style='color:red;font-size:11pt'><strong>"+endInfo+"</strong></span>");
+		this.container.append(endInfoContainer);
+	}
+};
+
+urule.InputType.prototype.getDisplayContainer=function(){
+	var container=$("<span>");
+	if(this.type=="Input"){
+		container.append(this.simpleValue.getDisplayContainer());
+	}else if(this.type=="Variable" || this.type=="VariableCategory"){
+		container.append(this.variableValue.getDisplayContainer());
+	}else if(this.type=="Constant"){
+		container.append(this.constantValue.getDisplayContainer());
+	}else if(this.type=="Method"){
+		container.append(this.methodValue.getDisplayContainer());
+	}else if(this.type=="Parameter"){
+		container.append(this.parameterValue.getDisplayContainer());
+	}else if(this.type=="CommonFunction"){
+		container.append(this.functionValue.getDisplayContainer());
+	}else if(this.type=="NamedReference"){
+		container.append(this.referenceValue.getDisplayContainer());
+	}
+	return container;
+};
+
+urule.InputType.prototype.setValueType=function(valueType,value){
+	window._setDirty();
+	this.type=valueType;
+	if(this.variableValue){
+		this.variableValue.getContainer().hide();
+	}
+	if(this.constantValue){
+		this.constantValue.getContainer().hide();
+	}
+	if(this.simpleValue){
+		this.simpleValue.getContainer().hide();
+	}
+	if(this.referenceValue){
+		this.referenceValue.getContainer().hide();
+	}
+	if(this.methodValue){
+		this.methodValue.getContainer().hide();
+	}
+	if(this.parameterValue){
+		this.parameterValue.getContainer().hide();
+	}
+	if(this.functionValue){
+		this.functionValue.getContainer().hide();
+	}
+	URule.setDomContent(this.label,".");
+	this.label.css({
+		"color":"#FDFDFD"
+	});
+	switch(valueType){
+	case "Input":
+		if(!this.simpleValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.simpleValue=new urule.SimpleValue(this.arithmetic,value);
+			this.dataContainer.append(this.simpleValue.getContainer());
+		}
+		this.simpleValue.getContainer().show();
+		this.type="Input";
+		break;
+	case "NamedReference":
+		if(!this.referenceValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.referenceValue=new urule.NamedReferenceValue(this.arithmetic,value,this.rule);
+			this.dataContainer.append(this.referenceValue.getContainer());
+		}
+		this.referenceValue.getContainer().show();
+		this.type="NamedReference";
+		break;
+	case "Constant":
+		if(!this.constantValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.constantValue=new urule.ConstantValue(this.arithmetic,value);
+			this.dataContainer.append(this.constantValue.getContainer());
+		}
+		this.constantValue.getContainer().show();
+		this.type="Constant";
+		break;
+	case "Method":
+		if(!this.methodValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.methodValue=new urule.MethodValue(this.arithmetic,value,this.dataContainer);
+			this.dataContainer.append(this.methodValue.getContainer());
+		}
+		this.methodValue.getContainer().show();
+		this.type="Method";
+		break;
+	case "Parameter":
+		if(!this.parameterValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.parameterValue=new urule.ParameterValue(this.arithmetic,value,"In");
+			this.dataContainer.append(this.parameterValue.getContainer());
+		}
+		this.parameterValue.getContainer().show();
+		this.type="Parameter";
+		break;
+	case "CommonFunction":
+		if(!this.functionValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.functionValue=new urule.FunctionValue(this.arithmetic,value,"In");
+			this.dataContainer.append(this.functionValue.getContainer());
+		}
+		this.functionValue.getContainer().show();
+		this.type="CommonFunction";
+		break;
+	default :
+		if(!this.variableValue){
+			this.arithmetic=new urule.ComplexArithmetic(this.rule);
+			this.variableValue=new urule.VariableValue(this.arithmetic,value,"In",this.functionProperty);
+			this.dataContainer.append(this.variableValue.getContainer());
+		}
+		this.variableValue.getContainer().show();
+		this.type="Variable";
+		break;
+	}
+};
+urule.InputType.prototype.toXml=function(){
+	if(this.type==""){
+		return "";
+	}
+	var xml="<value ";
+	if(this.type=="Input"){
+		var value=this.simpleValue.getValue();
+		if(!value || value==""){
+			throw "输入值不能为空!";
+		}
+		xml+=" content=\""+value+"\"";
+	}else if(this.type=="NamedReference"){
+        xml+=this.referenceValue.toXml();
+    }else if(this.type=="Variable" || this.type=="VariableCategory"){
+		xml+=this.variableValue.toXml();
+		this.type=this.variableValue.getType();
+	}else if(this.type=="Method"){
+		xml+=this.methodValue.toXml();		
+	}else if(this.type=="Parameter"){
+		xml+=this.parameterValue.toXml();		
+	}else if(this.type=="CommonFunction"){
+		xml+=this.functionValue.toXml();		
+	}else{
+		xml+=this.constantValue.toXml();
+	}
+	xml+=" type=\""+this.type+"\" ";
+	xml+=">";
+	xml+=this.arithmetic.toXml();
+	if(this.type=="Method"){
+		var parameters=this.methodValue.action.parameters;
+		for(var i=0;i<parameters.length;i++){
+			var p=parameters[i];
+			xml+=p.toXml();
+		}
+	}else if(this.type=="CommonFunction"){
+		var parameter=this.functionValue.getParameter();
+		xml+=parameter.toXml();
+	}
+	xml+="</value>";
+	return xml;
+};
+urule.InputType.prototype.getContainer=function(){
+	return this.container;
+};

+ 71 - 0
urule-console-js/src/editor/common/MethodAction.js

@@ -0,0 +1,71 @@
+/**
+ * @author GJ
+ */
+urule.MethodAction=function(rule){
+	this.parameters=[];
+	this.rule=rule;
+	this.init();
+};
+urule.MethodAction.prototype.init=function(){
+	this.container=$("<span>");
+	this.nameContainer=$("<span>");
+	this.container.append(this.nameContainer);
+	this.nameContainer.css({
+		"color":"darkblue"
+	});
+};
+urule.MethodAction.prototype.initData=function(data){
+	if(!data){
+		return;
+	}
+	this.bean=data["beanId"];
+	this.name=data["beanLabel"];
+	this.method=data["methodName"];
+	this.methodLabel=data["methodLabel"];
+	var parameters=data["parameters"];
+	this.parameterCount=0;
+	if(parameters){
+		this.parameterCount=parameters.length;
+	}
+	if(this.parameterCount===0){
+        URule.setDomContent(this.nameContainer,this.methodLabel);
+		var parameterLabel=$("<span style='color:gray'>(无参数)</span>");
+		this.container.append(parameterLabel);
+	}else{
+        URule.setDomContent(this.nameContainer,this.methodLabel+"(");
+    }
+	if(this.parameterCount==0){
+		return;
+	}
+	for(var i=0;i<this.parameterCount;i++){
+		var p=parameters[i];
+		if(i>0){
+			var comma=$("<span>;</span>");
+			this.container.append(comma);
+		}
+		if(this.parameterCount>0){
+			var seqLabel=$("<span style='color:purple'>&nbsp;"+p["name"]+":</span>");
+			this.container.append(seqLabel);			
+		}
+		var parameter=new urule.MethodParameter(this.rule);
+		this.parameters.push(parameter);
+		this.container.append(parameter.getContainer());
+		parameter.initData(p);
+	}
+	this.container.append(")");
+};
+urule.MethodAction.prototype.toXml=function(){
+	if(!this.name || this.name==""){
+		throw "请选择要执行的方法!";
+	}
+	var xml="<execute-method bean=\""+this.bean+"\" bean-label=\""+this.name+"\" method-label=\""+this.methodLabel+"\" method-name=\""+this.method+"\">";
+	for(var i=0;i<this.parameters.length;i++){
+		var p=this.parameters[i];
+		xml+=p.toXml();
+	}
+	xml+="</execute-method>";
+	return xml;
+};
+urule.MethodAction.prototype.getContainer=function(){
+	return this.container;
+};

+ 35 - 0
urule-console-js/src/editor/common/MethodParameter.js

@@ -0,0 +1,35 @@
+/**
+ * @author GJ
+ */
+urule.MethodParameter=function(rule){
+	this.inputType=new urule.InputType(null,null,null,rule);
+	this.container=this.inputType.getContainer();
+};
+urule.MethodParameter.prototype.initData=function(data){
+	if(!data){
+		return;
+	}
+	this.name=data["name"];
+	this.type=data["type"];
+	if(!data["value"]){
+		return;
+	}
+	var value=data["value"];
+	if(!value["valueType"]){
+		return;
+	}
+	this.inputType.setValueType(value["valueType"], value);
+};
+
+urule.MethodParameter.prototype.toXml=function(){
+	var xml="<parameter name=\""+this.name+"\" type=\""+this.type+"\">";
+	xml+=this.inputType.toXml();
+	xml+="</parameter>";
+	return xml;
+};
+urule.MethodParameter.prototype.getContainer=function(){
+	return this.container;
+};
+urule.MethodParameter.prototype.getInputType=function(){
+	return this.inputType;
+};

+ 125 - 0
urule-console-js/src/editor/common/MethodValue.js

@@ -0,0 +1,125 @@
+/**
+ * @author GJ
+ */
+urule.MethodValue=function(arithmetic,data){
+	this.arithmetic=arithmetic;
+	this.container=$("<span>");
+	this.rightParn=$("<span style='color:blue'>]</span>");
+	this.label=generateContainer();
+	this.fetchLength=false;
+	this.uppercase=false;
+	this.lowercase=false;
+	this.fetchSize=false;
+	this.container.append(this.label);
+	this.label.css({
+		"color":"blue"
+	});
+	this.actionContainer=$("<span></span>");
+	this.container.append(this.actionContainer);
+	URule.setDomContent(this.label,"请选择方法");
+	if(arithmetic){
+		this.container.append(arithmetic.getContainer());		
+	}
+	if(data){
+		this.setAction(data);
+		arithmetic.initData(data["arithmetic"]);
+	}
+	window._ActionTypeArray.push(this);
+	this.initMenu();
+};
+
+urule.MethodValue.prototype.initMenu=function(actionLibraries){
+	var data=window._uruleEditorActionLibraries;
+	if(actionLibraries){
+		data=actionLibraries;
+	}
+	var self,onClick,config;
+	self=this;
+	onClick=function(menuItem){
+		var parent=menuItem.parent.parent;
+		self.setAction({
+			beanLabel:parent.label,
+			beanId:parent.name,
+			methodLabel:menuItem.label,
+			methodName:menuItem.name,
+			parameters:menuItem.parameters
+		});
+	};
+	config={menuItems:[]};
+	$.each(data||[],function(index,item){
+		var springBeans=item.springBeans||[]; 
+		$.each(springBeans,function(i,springBean){
+			var menuItem={
+				name:springBean.id,
+				label:springBean.name
+			};
+			var methods=springBean.methods||[];
+			$.each(methods,function(j,method){
+				if(!menuItem.subMenu){
+					menuItem.subMenu={menuItems:[]};
+				}
+				menuItem.subMenu.menuItems.push({
+					name:method.methodName,
+					label:method.name,
+					parameters:method.parameters,
+					onClick:onClick
+				});
+			});
+
+			config.menuItems.push(menuItem);
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+	
+};
+
+urule.MethodValue.prototype.setAction=function(data){
+	window._setDirty();
+	if(this.action){
+		this.action.getContainer().remove();
+	}
+	this.action=new urule.MethodAction();
+	URule.setDomContent(this.label,"[");
+	this.actionContainer.append(this.action.getContainer());
+	this.actionContainer.append(this.rightParn);
+	
+	this.action.initData(data);
+};
+
+urule.MethodValue.prototype.getDisplayContainer=function(){
+	var name="",method="";
+	if(this.action){
+		name=this.action.name;
+		method=this.action.methodLabel;
+	}
+	var container=$("<span>"+method+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);
+		}
+	}
+	return container;
+};
+
+urule.MethodValue.prototype.toXml=function(){
+	if(!this.action.bean || this.action.name==""){
+		throw "执行方法不能为空!";
+	}
+	var xml="bean-name=\""+this.action.bean+"\"";
+	xml+=" bean-label=\""+this.action.name+"\"";
+	xml+=" method-name=\""+this.action.method+"\"";
+	xml+=" method-label=\""+this.action.methodLabel+"\"";
+	return xml;
+};
+
+urule.MethodValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 87 - 0
urule-console-js/src/editor/common/NextType.js

@@ -0,0 +1,87 @@
+/**
+ * @author GJ
+ */
+urule.NextType=function(rule){
+	this.container=$("<span>");
+	this.rule=rule;
+	this.inputType=null;
+	this.paren=null;
+	this.selectorLabel=generateContainer();
+	this.selectorLabel.css({
+		"fontWeight":"blod",
+		"color":"#fff"
+	});
+	this.container.append(this.selectorLabel);
+	URule.setDomContent(this.selectorLabel,".");
+	var self=this;
+	var onClick=function(menu){
+		var type=menu.name;
+		self.doNext(type);
+		window._setDirty();
+	};
+	self.menu=new URule.menu.Menu({
+		menuItems:[{
+			label : "值",
+			name:"value",
+			onClick : onClick
+		},{
+			label : "括号",
+			name:"paren",
+			onClick : onClick
+		}]
+	});	
+	this.selectorLabel.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.NextType.prototype.initData=function(data){
+	var value=data["value"];
+	var valueType=value["valueType"];
+	if(valueType=="Paren"){
+		this.doNext("paren");
+		this.paren.initData(value);
+	}else{
+		this.doNext("value");
+		this.inputType.setValueType(valueType, value);
+	}
+};
+urule.NextType.prototype.toXml=function(){
+	if(this.paren){
+		return this.paren.toXml();
+	}else if(this.inputType){
+		return this.inputType.toXml();
+	}
+	return "";
+};
+urule.NextType.prototype.getDisplayContainer=function(){
+	if(this.inputType){
+		return this.inputType.getDisplayContainer();
+	}else if(this.paren){
+		return this.paren.getDisplayContainer();
+	}
+	return null;
+};
+urule.NextType.prototype.getContainer=function(){
+	return this.container;
+};
+urule.NextType.prototype.doNext=function(type){
+	if(type=="value"){
+		if(this.paren){
+			this.paren.getContainer().remove();
+			this.paren=null;
+		}
+		if(!this.inputType){
+			this.inputType = new urule.InputType(null,null,null,this.rule);
+			this.container.append(this.inputType.getContainer());
+		}
+	}else if(type=="paren"){
+		if(this.inputType){
+			this.inputType.getContainer().remove();
+			this.inputType=null;
+		}
+		if(!this.paren){
+			this.paren=new urule.Paren(this.rule);
+			this.container.append(this.paren.getContainer());
+		}
+	}
+};

+ 110 - 0
urule-console-js/src/editor/common/ParameterValue.js

@@ -0,0 +1,110 @@
+/**
+ * @author GJ
+ */
+urule.ParameterValue=function(arithmetic,data,act){
+	this.arithmetic=arithmetic;
+	this.container=$("<span>");
+	this.label=generateContainer();
+	this.container.append(this.label);
+	this.label.css({
+		"color":"#6b3db0"
+	});
+	URule.setDomContent(this.label,"请选择参数");
+	if(arithmetic){
+		this.container.append(arithmetic.getContainer());		
+	}
+	if(data){
+		this.initData(data);
+	}
+	window._ParameterValueArray.push(this);
+	this.act=act;
+	this.initMenu();
+};
+
+urule.ParameterValue.prototype.getDisplayContainer=function(){
+	var container=$("<span>参数."+this.parameterLabel+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);			
+		}	
+	}
+	return container;
+};
+
+urule.ParameterValue.prototype.matchAct=function(act){
+	if(!this.act){
+		return true;
+	}
+	if(act.indexOf(this.act)>-1){
+		return true;
+	}
+	return false;
+};
+urule.ParameterValue.prototype.initMenu=function(parameterLibraries){
+	var data=window._uruleEditorParameterLibraries;
+	if(parameterLibraries){
+		data=parameterLibraries;
+	}
+	if(!data){
+		return;
+	}
+	var self,onClick,config;
+	self=this;
+	onClick=function(menuItem){
+		self.setValue({
+			variableName:menuItem.name,
+			variableLabel:menuItem.label,
+			datatype:menuItem.datatype
+		});
+
+	};
+	config={menuItems:[]};
+	$.each(data,function(index,variables){
+		$.each(variables||[],function(i,variable){
+			if(self.matchAct(variable.act)){
+				var menuItem={
+					name:variable.name,
+					label:variable.label,
+					datatype:variable.type,
+					act:variable.act,
+					onClick:onClick
+				};
+				config.menuItems.push(menuItem);
+			}
+
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.ParameterValue.prototype.setValue=function(data){
+	this.parameterName=data["variableName"];
+	this.parameterLabel=data["variableLabel"];
+	this.datatype=data["datatype"];
+	URule.setDomContent(this.label,"参数."+this.parameterLabel);
+	window._setDirty();
+};
+urule.ParameterValue.prototype.initData=function(data){
+	this.setValue(data);
+	if(this.arithmetic){
+		this.arithmetic.initData(data["arithmetic"]);			
+	}
+};
+
+urule.ParameterValue.prototype.toXml=function(){
+	if(!this.parameterLabel || this.parameterLabel==""){
+		throw "参数不能为空!";
+	}
+	var xml=" var-category=\"参数\" var=\""+this.parameterName+"\" var-label=\""+this.parameterLabel+"\" datatype=\""+this.datatype+"\"";
+	return xml;
+};
+urule.ParameterValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 54 - 0
urule-console-js/src/editor/common/Paren.js

@@ -0,0 +1,54 @@
+/**
+ * @author GJ
+ */
+urule.Paren=function(rule){
+	this.container=$("<span>");
+	this.leftParen=$("<span>(</span>");
+	this.leftParen.css({
+		"color":"#000",
+		"fontWeight":"blod",
+		"padding-left":"3px",
+		"padding-right":"3px"
+	});
+	this.rightParen=$("<span>)</span>");
+	this.rightParen.css({
+		"color":"#000",
+		"fontWeight":"blod",
+		"padding-left":"3px",
+		"padding-right":"3px"
+	});
+	this.parenContainer=$("<span>");
+	this.container.append(this.leftParen);
+	this.container.append(this.parenContainer);
+	this.container.append(this.rightParen);
+	this.inputType=new urule.InputType(null,null,null,rule);
+	this.parenContainer.append(this.inputType.getContainer());
+	this.arithmetic=new urule.ComplexArithmetic(rule);
+	this.container.append(this.arithmetic.getContainer());
+};
+urule.Paren.prototype.initData=function(data){
+	var value=data["value"];
+	var valueType=value["valueType"];
+	this.inputType.setValueType(valueType, value);
+	this.arithmetic.initData(data["arithmetic"]);
+};
+
+urule.Paren.prototype.getDisplayContainer=function(){
+	return this.inputType.getDisplayContainer();
+};
+
+urule.Paren.prototype.toXml=function(){
+	if(!this.inputType){
+		throw "请输入括号内容!";
+	}
+	var xml="<paren>";
+	xml+=this.inputType.toXml();
+	if(this.arithmetic){
+		xml+=this.arithmetic.toXml();
+	}
+	xml+="</paren>";
+	return xml;
+};
+urule.Paren.prototype.getContainer=function(){
+	return this.container;
+};

+ 66 - 0
urule-console-js/src/editor/common/Permission.js

@@ -0,0 +1,66 @@
+isAdmin = function() {
+	if(window._isAdmin === undefined) {
+		$.ajax({
+			url:"urule?action=checkadmin",
+			async: false,
+			type : "POST",
+			success:function(data){
+				window._isAdmin = data;
+			}
+		});
+	}
+	return window._isAdmin;
+};
+
+hasPermission = function(type) {	
+	if(isAdmin()) {
+		return true;
+	}
+	var permissions = getPermissions(type);
+	for(var i = 0; i < permissions.length; i ++) {
+		var p = permissions[i];
+		if(p.granted == true){
+			return true;
+		}
+		return false;
+	}
+	
+	return false;
+};
+
+getPermissions = function(type) {
+	var path = getRequestParameter("file");
+	$.ajax({
+		url:"urule?action=loadpermission",
+		async: false,
+		type : "POST",
+		data : {
+			path : path.substring(0, path.lastIndexOf("/")),
+			type : type || "Mod" + getRequestParameter("type")
+		},
+		success:function(data){
+			_permissions = data || [];
+		}
+	});
+	
+	return _permissions;
+};
+
+getRequestParameter=function(name){
+	var value=null;
+	var params=window.location.search.substring(1).split("&");
+	for(var i=0;i<params.length;i++){
+		var param=params[i];
+		if(param.indexOf("=")==-1){
+			continue;
+		}
+		var pair=param.split("=");
+		var key=pair[0];
+		if(key==name){
+			value=pair[1];
+			break;
+		}
+	}
+	
+	return value;
+};

+ 137 - 0
urule-console-js/src/editor/common/ResourceListDialog.js

@@ -0,0 +1,137 @@
+/**
+ * @author Administrator
+ */
+urule.ResourceListDialog=function(type,select,doSuccess){
+	this.type=type;
+	this.select=select;
+	this.doSuccess=doSuccess;
+	this.init();
+};
+urule.ResourceListDialog.prototype.open=function(){
+	this.initData();
+	this.dialog.dialog("open");
+};
+urule.ResourceListDialog.prototype.close=function(){
+	this.dialog.dialog("close");
+};
+urule.ResourceListDialog.prototype.getSelectedFile=function(){
+	var selected=false;
+	var selectedNode=null;
+	var nodes = this.tree.getSelectedNodes();
+	if(nodes && nodes.length>0){
+		selectedNode=nodes[0];
+		if(!selectedNode["isParent"]){
+			selected=true;
+		}
+	}
+	if(!selected){
+		URule.alert("请先选择一个库文件!");
+		return null;
+	}
+	return selectedNode["fullPath"];
+};
+
+urule.ResourceListDialog.prototype.init=function(){
+	var self=this;
+	this.dialog=$("<div>");
+	this.treeContainer=$("<div class='ztree'>");
+	this.dialog.append(this.treeContainer);
+	this.dialog.dialog({
+		title:"库列表",
+		height:500,
+		width:400,
+		autoOpen:false,
+		modal:true,
+		show:false,
+		buttons:[{
+			text:"确认",
+			click:function(){
+				var selectedFile=self.getSelectedFile();
+				if(!selectedFile){
+					return;
+				}
+				self.doSelectFile(selectedFile);
+			}
+		},{
+			text:"选择版本",
+			click:function(){
+				var selectedFile=self.getSelectedFile();
+				if(!selectedFile){
+					return;
+				}
+				var versionDialog=new urule.ResourceVersionDialog(selectedFile);
+				versionDialog.open(function(file){
+					self.doSelectFile(file);
+				});
+			}
+		},{
+			text:"取消",
+			click:function(){
+				$(this).dialog("close");
+			}
+		}]
+	});
+};
+urule.ResourceListDialog.prototype.doSelectFile=function(selectedFile){
+	var fullPath="jcr:"+selectedFile;
+	if(this.doSuccess){
+		this.doSuccess(this.type,fullPath);
+		this.dialog.dialog("close");
+		return;
+	}
+	var dup=false;
+	this.select.each(function(){
+		$(this.childNodes).each(function(){
+			var path=this.textContent;
+			if(path==fullPath){
+				dup=true;
+			}
+		});
+	});
+	if(!dup){
+		var self=this;
+		var item=$("<a href='javascript:void(0)' class='list-group-item'>"+fullPath+"</a>");
+		item.click(function(){
+			self.select.find(".active").removeClass("active");
+			item.addClass("active");
+		});
+		this.select.append(item);
+		this.dialog.dialog("close");
+	}else{
+		URule.alert("当前库文件已被添加!");
+	}
+};
+urule.ResourceListDialog.prototype.initData=function(){
+	var self=this;
+	var url=(uruleServer || "")+"urule?action=loadrepo&filetype="+this.type+"&path="+self.getRequestParameter("file");
+	$.ajax({
+		cache:false,
+		dataType:"json",
+		url:url,
+		error:function(req,error){
+			URule.alert("加载资源失败:"+error);
+		},
+		success:function(data){
+			self.tree=$.fn.zTree.init(self.treeContainer, {}, data);
+		}
+	});
+};
+
+urule.ResourceListDialog.prototype.getRequestParameter=function(name){
+	var value=null;
+	var params=window.location.search.substring(1).split("&");
+	for(var i=0;i<params.length;i++){
+		var param=params[i];
+		if(param.indexOf("=")==-1){
+			continue;
+		}
+		var pair=param.split("=");
+		var key=pair[0];
+		if(key==name){
+			value=pair[1];
+			break;
+		}
+	}
+	
+	return value;
+};

+ 68 - 0
urule-console-js/src/editor/common/ResourceVersionDialog.js

@@ -0,0 +1,68 @@
+/**
+ * @author GJ
+ */
+urule.ResourceVersionDialog=function(path){
+	this.path=path;
+	this.init();
+};
+urule.ResourceVersionDialog.prototype.open=function(doSuccess){
+	this.dialog.dialog("open");
+	this.doSuccess=doSuccess;
+};
+urule.ResourceVersionDialog.prototype.init=function(){
+	this.dialog=$("<div>");
+	var tableHtml="<table data-height=\"299\" data-click-to-select=\"true\" data-show-columns=\"true\" data-select-item-name=\"radioName\">";
+	tableHtml+="<thead><tr>";
+	tableHtml+="<th data-field=\"state\" data-radio=\"true\"></th>";
+	tableHtml+="<th data-field=\"name\" data-align=\"center\">版本号</th>";
+	tableHtml+="<th data-field=\"createUser\" data-align=\"center\">创建人</th>";
+	tableHtml+="<th data-field=\"createDate\" data-align=\"center\" data-formatter=\"__formatDate\">创建时间</th>";
+	tableHtml+="</tr></thead></table>";
+	this.table=$(tableHtml);
+	var url="urule?action=loadversion&file="+this.path;
+	this.table.bootstrapTable({
+		url:url,
+		onLoadSuccess:function(){
+			self.table.css("margin-top", "");
+		}
+	});
+	this.dialog.append(this.table);
+	var self=this;
+	this.dialog.dialog({
+		title:"版本列表【"+self.path+"】",
+		height:400,
+		width:700,
+		autoOpen:false,
+		modal:true,
+		show:false,
+		buttons:[{
+			text:"确认",
+			click:function(){
+				var selctedData=self.table.bootstrapTable("getSelections");
+				if(selctedData.length==0){
+					URule.alert("请选择一个版本!");
+					return;
+				}
+				self.doSuccess(self.path+":"+selctedData[0].name);
+				$(this).dialog("close");
+			}
+		},{
+			text:"取消",
+			click:function(){
+				$(this).dialog("close");
+			}
+		}]
+	});
+};
+
+function __formatDate(time){
+	var datetime = new Date();  
+    datetime.setTime(time);
+	var year = datetime.getFullYear();  
+	var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;  
+	var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();  
+	var hour = datetime.getHours()< 10 ? "0" + datetime.getHours() : datetime.getHours();  
+	var minute = datetime.getMinutes()< 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();  
+	var second = datetime.getSeconds()< 10 ? "0" + datetime.getSeconds() : datetime.getSeconds();  
+	return year + "-" + month + "-" + date+" "+hour+":"+minute+":"+second;  
+};

+ 160 - 0
urule-console-js/src/editor/common/ReteDiagram.js

@@ -0,0 +1,160 @@
+/**
+ * @author GJ
+ */
+var connections = [];
+var nodes=[];
+var r=null;
+var _dragger = function () {
+	this.ox = this.attr("x");
+	this.oy = this.attr("y");
+};
+var _move = function (dx, dy) {
+	var att = {x: this.ox + dx, y: this.oy + dy};
+	this.attr(att);
+	if(this.type == "rect"){
+		var w=this.attr("width");
+		var h=this.attr("height");
+		att = {x: this.ox + dx + w/2, y: this.oy + dy + h/2};
+		this.text.attr(att);
+	}else{
+		var w=this.rect.attr("width");
+		var h=this.rect.attr("height");
+		att = {x: this.ox + dx - w/2, y: this.oy + dy -h/2};
+		this.rect.attr(att);		
+	}
+	for (var i = connections.length; i--;) {
+		r.connection(connections[i]);
+	}
+	r.safari();
+};
+
+Raphael.fn.connection = function (obj1, obj2) {
+	var line=null;
+    if (obj1.line && obj1.from && obj1.to) {
+        line = obj1;
+        obj1 = line.from;
+        obj2 = line.to;
+    }
+    var bb1 = obj1.getBBox(),bb2 = obj2.getBBox();
+    var x1=bb1.x+bb1.width/2;
+    var y1=bb1.y+bb1.height/2;
+    var x2=bb2.x+bb2.width/2;
+    var y2=bb2.y+bb2.height/2;
+    var newPath=["M"];
+    var path="M "+x1+","+y1+" L "+x2+","+y2;
+    var objPath=this.elementPath(obj1);
+    var dot=Raphael.pathIntersection(path,objPath);
+    newPath.push(dot[0].x);
+    newPath.push(dot[0].y);
+    objPath=this.elementPath(obj2);
+    dot=Raphael.pathIntersection(path,objPath);
+    newPath.push(dot[0].x);
+    newPath.push(dot[0].y);
+    if (line && line.line) {
+        line.bg && line.bg.attr({path: newPath});
+        line.line.attr({path: newPath});
+    } else {
+    	var newLine= this.path(newPath).attr({stroke: "#2B547E", fill: "none","arrow-end":"block-wide-long","stroke-width":1.5});
+    	newLine.toBack();
+    	return {
+    		line:newLine,
+    		from: obj1,
+    		to: obj2
+    	};
+    }
+};
+Raphael.fn.elementPath = function(element) {
+	var p1 = element.attr("x");
+	var p2 = element.attr("y");
+	var p3 = element.attr("x")+element.attr("width");
+	var p4 = element.attr("y");
+	var p5 = element.attr("x")+element.attr("width");
+	var p6 = element.attr("y")+element.attr("height");
+	var p7 = element.attr("x");
+	var p8 = element.attr("y")+element.attr("height");
+	return "M "+p1+" "+p2+" L "+p3+" "+p4+" L "+p5+" "+p6+" L "+p7+"  "+p8+" L "+p1+"  "+p2+"";
+};
+
+var ReteNode=function(node,r){
+	this.id=node["id"];
+	var x=node["x"];
+	var y=node["y"];
+	var w=node["width"];
+	var h=node["height"];
+	var corner=node["roundCorner"];
+	this.rect=r.rect(x, y, w, h, corner);
+	var label=node["label"];
+	var title=node["title"];
+	var text=r.text(x+w/2,y+h/2,label);
+	text.drag(_move, _dragger);
+	text.attr({fill: "blue", "title": title,cursor: "move"});
+	this.rect.text=text;
+	text.rect=this.rect;
+	var color = node["color"];
+	this.rect.attr({fill: color, stroke: color, "stroke-width": 2, "title": title, cursor: "move"});
+	this.rect.drag(_move, _dragger);
+	nodes.push(this);
+	var children=node["children"];
+	if(!children){
+		return;
+	}
+	for(var i=0;i<children.length;i++){
+		var childNode=children[i];
+		new ReteNode(childNode,r);
+	}
+};
+ReteNode.prototype.getRect=function(){
+	return this.rect;
+};
+ReteNode.prototype.getId=function(){
+	return this.id;
+};
+var _drawReteTree=function(container,filelist){
+	var url = "urule?action=loadrete";
+	$.ajax({
+		cache : false,
+		url : url,
+		type : "POST",
+		data : {
+			files : filelist
+		},
+		error : function(req, error) {
+			alert("规则树加载失败!");
+		},
+		success : function(data) {
+			var width=data["width"];
+			var height=data["height"];
+			var winWidth=$(window).height();
+			var winHeight=$(window).width();
+			if(winWidth>width){
+				width=winWidth;
+			}
+			if(winHeight>height){
+				height=winHeight;
+			}
+			r = Raphael(container, width, height);
+			new ReteNode(data["rootNode"],r);
+			var edges=data["edges"];
+			if(!edges){
+				return;
+			}
+			for(var i=0;i<edges.length;i++){
+				var edge=edges[i];
+				var fromId=edge["from"];
+				var toId=edge["to"];
+				var fromNode=_findNode(fromId);
+				var toNode=_findNode(toId);
+				connections.push(r.connection(fromNode.getRect(),toNode.getRect()));
+			}
+		}
+	});
+};
+var _findNode=function(id){
+	for(var i=0;i<nodes.length;i++){
+		var node=nodes[i];
+		if(node.getId()==id){
+			return node;
+		}
+	}
+	throw "Node ["+id+"] not found.";
+};

+ 87 - 0
urule-console-js/src/editor/common/SimpleValue.js

@@ -0,0 +1,87 @@
+/**
+ * @author GJ
+ */
+urule.SimpleValue=function(arithmetic,data){
+	var TIP="请输入值";
+	this.container=$("<span>");
+	this.valueContainer=generateContainer();
+	this.valueContainer.css({
+		color:"rgb(180,95,4)"
+	});
+	this.editor=$(`<input type='text' class="form-control rule-text-editor" style="height: 22px;">`);
+	var self=this;
+	this.container.append(this.valueContainer).append(this.editor);
+	this.editor.blur(function(){
+		self.editor.hide();
+		var text=self.editor.prop("value");
+		if(text!=""){
+			URule.setDomContent(self.valueContainer,text);
+		}
+		self.valueContainer.show();
+		$(this).trigger("DOMSubtreeModified");
+		window._setDirty();
+	}).mousedown(function(evt){
+		evt.stopPropagation();
+	}).keydown(function(evt){
+		evt.stopPropagation();
+	});
+	self.editor.hide();
+	this.valueContainer.prop("innerText",TIP);
+	this.valueContainer.click(function(){
+		self.valueContainer.hide();
+		var parent=self.container.parent();
+		var maxWidth=120;
+		if(parent && parent.parent() && parent.parent().parent()){
+			parent=parent.parent().parent();
+			var css=parent.prop("class");
+			if(css && css=="htMiddle htDimmed current"){
+				maxWidth=parent.width()-20;
+			}
+		}
+		self.editor.css({
+			width:maxWidth
+		});
+		self.editor.css({display:'inline'});
+		self.editor.focus();
+		$(this).trigger("DOMSubtreeModified");
+	});
+	this.arithmetic=arithmetic;
+	this.container.append(arithmetic.getContainer());
+	this.initData(data);
+};
+
+urule.SimpleValue.prototype.getDisplayContainer=function(){
+	var container=$("<span>"+this.editor.prop("value")+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);			
+		}
+	}
+	return container;
+};
+
+urule.SimpleValue.prototype.initData=function(data){
+	if(!data){
+		return;
+	}
+	var text=data["content"];
+	//var disText=text.length>15?(text.substring(0,15)+"..."):text;
+	URule.setDomContent(this.valueContainer,text);
+	this.editor.prop("value",text);
+	if(this.arithmetic){
+		this.arithmetic.initData(data["arithmetic"]);
+	}
+};
+urule.SimpleValue.prototype.getValue=function(){
+	var value=this.editor.prop("value");
+	value=value.replace(new RegExp("&","gm"),"&amp;");
+	value=value.replace(new RegExp("<","gm"),"&lt;");
+	value=value.replace(new RegExp(">","gm"),"&gt;");
+	value=value.replace(new RegExp("'","gm"),"&apos;");
+	value=value.replace(new RegExp("\"","gm"),"&quot;");
+	return value;
+};
+urule.SimpleValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 173 - 0
urule-console-js/src/editor/common/URule.js

@@ -0,0 +1,173 @@
+/**
+ * @author GJ
+ */
+import {MsgBox} from 'flowdesigner';
+
+window._ConstantValueArray=[];
+window._ActionTypeArray=[];
+window._VariableValueArray=[];
+window._ParameterValueArray=[];
+window._FunctionValueArray=[];
+window.actionLibraries=[];
+window.variableLibraries=[];
+window.constantLibraries=[];
+window.parameterLibraries=[];
+window.urule={};
+
+window.generateContainer=function(){
+	var container=$("<span>.</span>");
+	container.css({
+		height:"20px",
+		cursor:"pointer",
+		margin:"0px",
+		color:"white",
+		border:"dashed transparent 1px"
+	});
+	container.mouseover(function(){
+		container.css({
+			border:"dashed gray 1px"
+		});
+	});
+	container.mouseout(function(){
+		container.css({
+			border:"dashed transparent 1px"
+		});
+	});
+	return container;
+};
+
+window.refreshParameterLibraries=function(){
+	var parameterFiles="";
+	for(var i=0;i<parameterLibraries.length;i++){
+		var parameter=parameterLibraries[i];
+		if(i==0){
+			parameterFiles=parameter;
+		}else{
+			parameterFiles+=";"+parameter;
+		}
+	}
+	if(parameterFiles=="" || parameterFiles.length<2){
+		return;
+	}
+	var url=window._server+'/common/loadXml';
+	$.ajax({
+		type:'POST',
+		data:{files:parameterFiles},
+		url:url,
+		error:function(req,error){
+			MsgBox.alert("加载文件失败!");
+		},
+		success:function(data){
+			window._uruleEditorParameterLibraries=data;
+			$.each(window._ParameterValueArray,function(index,item){
+				item.initMenu(data);
+			});
+		}
+	});	
+};
+
+window.refreshVariableLibraries=function(){
+	var variableFiles="";
+	for(var i=0;i<variableLibraries.length;i++){
+		var variable=variableLibraries[i];
+		if(i==0){
+			variableFiles=variable;
+		}else{
+			variableFiles+=";"+variable;
+		}
+	}
+	if(variableFiles=="" || variableFiles.length<2){
+		return;
+	}
+	var url=window._server+'/common/loadXml';
+	$.ajax({
+		type:'POST',
+		data:{files:variableFiles},
+		url:url,
+		error:function(req,error){
+			MsgBox.alert("加载文件失败!");
+		},
+		success:function(data){
+			window._uruleEditorVariableLibraries=data;
+			$.each(window._VariableValueArray,function(index,item){
+				item.initMenu(data);
+			});
+		}
+	});
+};
+window.refreshActionLibraries=function(){
+	var actionFiles="";
+	for(var i=0;i<actionLibraries.length;i++){
+		var action=actionLibraries[i];
+		if(i==0){
+			actionFiles=action;
+		}else{
+			actionFiles+=";"+action;
+		}
+	}
+	if(actionFiles=="" || actionFiles.length<2){
+		actionFiles="builtinactions";
+	}
+	var url=window._server+'/common/loadXml';
+	$.ajax({
+		type:'POST',
+		data:{files:actionFiles},
+		url:url,
+		error:function(req,error){
+			MsgBox.alert("加载文件失败!");
+		},
+		success:function(data){
+			window._uruleEditorActionLibraries=data;
+			$.each(window._ActionTypeArray,function(index,item){
+				item.initMenu(data);
+			});
+		}
+	});
+};
+window.refreshFunctionLibraries=function(){
+	var url=window._server+'/common/loadFunctions';
+	$.ajax({
+		url:url,
+		error:function(req,error){
+			MsgBox.alert("加载函数失败!");
+		},
+		success:function(data){
+			window._uruleEditorFunctionLibraries=data;
+			$.each(window._FunctionValueArray,function(index,item){
+				item.initMenu(data);
+			});
+		}
+	});
+};
+
+window.refreshConstantLibraries=function(){
+	var constantFiles="";
+	for(var i=0;i<constantLibraries.length;i++){
+		var constant=constantLibraries[i];
+		if(i==0){
+			constantFiles=constant;
+		}else{
+			constantFiles+=";"+constant;
+		}
+	}
+	if(constantFiles=="" || constantFiles.length<2){
+		return;
+	}
+	var url=window._server+'/common/loadXml';
+	$.ajax({
+		data:{files:constantFiles},
+		url:url,
+		type:'POST',
+		error:function(req,error){
+			MsgBox.alert("加载文件失败!");
+		},
+		success:function(data){
+			window._uruleEditorConstantLibraries=data;
+			$.each(window._ConstantValueArray,function(index,item){
+				item.initMenu(data);
+			});
+		}
+	});
+};
+
+

+ 153 - 0
urule-console-js/src/editor/common/VariableValue.js

@@ -0,0 +1,153 @@
+/**
+ * @author GJ
+ */
+urule.VariableValue=function(arithmetic,data,act,functionProperty){
+	this.arithmetic=arithmetic;
+	this.container=$("<span>");
+
+    var self=this;
+    this.label=generateContainer();
+    this.functionProperty=functionProperty;
+    this.container.append(this.label);
+    this.label.css({
+		"color":"darkcyan"
+	});
+	URule.setDomContent(this.label,"请选择变量");
+	if(arithmetic){
+		this.container.append(arithmetic.getContainer());		
+	}
+	if(data){
+		this.initData(data);
+	}
+	window._VariableValueArray.push(this);
+	this.act=act;
+	this.initMenu();
+};
+
+urule.VariableValue.prototype.getDisplayContainer=function(){
+	var container=$("<span>"+this.category+"."+this.variableLabel+"</span>");
+	if(this.arithmetic){
+		var dis=this.arithmetic.getDisplayContainer();
+		if(dis){
+			container.append(dis);			
+		}	
+	}
+	return container;
+};
+
+urule.VariableValue.prototype.matchAct=function(act){
+	if(!this.act){
+		return true;
+	}
+	if(act.indexOf(this.act)>-1){
+		return true;
+	}
+	return false;
+};
+urule.VariableValue.prototype.initMenu=function(variableLibraries){
+	var data=window._uruleEditorVariableLibraries;
+	if(variableLibraries){
+		data=variableLibraries;
+	}
+	if(!data){
+		return;
+	}
+	var self,onCategoryClick,onVariableClick,config;
+	self=this;
+	onCategoryClick=function(menuItem){
+		self.setValue({variableCategory:menuItem.label,variables:menuItem.variables});
+	};
+	onVariableClick=function(menuItem){
+		self.setValue({
+			variables:menuItem.parent.parent.variables,
+			variableCategory:menuItem.parent.parent.label,
+			variableLabel:menuItem.label,
+			variableName:menuItem.name,
+			datatype:menuItem.datatype
+		});
+	};
+	config={menuItems:[]};
+	$.each(data,function(index,categories){
+		$.each(categories,function(i,category){
+			var variables=category.variables;
+			if(self.functionProperty && self.category){
+				if(category.name==self.category){
+					self.functionProperty.initMenu(variables);
+				}
+			}
+			var menuItem={
+				label:category.name,
+				variables:variables,
+				onClick:onCategoryClick
+			}
+			$.each(variables||[],function(j,variable){
+				if(!menuItem.subMenu){
+					menuItem.subMenu={menuItems:[]};
+				}
+				if(self.matchAct(variable.act)){
+					var subMenuItem={
+						name:variable.name,
+						label:variable.label,
+						datatype:variable.type,
+						act:variable.act,
+						variables:variables,
+						onClick:onVariableClick
+					};
+					menuItem.subMenu.menuItems.push(subMenuItem);
+				}
+			});
+			config.menuItems.push(menuItem);
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.VariableValue.prototype.setValue=function(data){
+	var self=this;
+	this.category=data["variableCategory"];
+	this.variableName=data["variableName"];
+	this.variableLabel=data["variableLabel"];
+	this.datatype=data["datatype"];
+	if(this.functionProperty){
+		this.functionProperty.initMenu(data["variables"]);			
+	}
+	if(this.variableLabel){
+		URule.setDomContent(this.label,this.category+"."+this.variableLabel);
+	}else{
+		URule.setDomContent(this.label,this.category);
+	}
+	window._setDirty();
+};
+urule.VariableValue.prototype.initData=function(data){
+	this.setValue(data);
+	if(this.arithmetic){
+		this.arithmetic.initData(data["arithmetic"]);			
+	}
+};
+
+urule.VariableValue.prototype.toXml=function(){
+	if(!this.category || this.category==""){
+		throw "变量不能为空!";
+	}
+	var xml="var-category=\""+this.category+"\"";
+	if(this.variableName){
+		xml+=" var=\""+this.variableName+"\" var-label=\""+this.variableLabel+"\" datatype=\""+this.datatype+"\"";
+	}
+	return xml;
+};
+urule.VariableValue.prototype.getType=function(){
+	if(this.variableName){
+		return "Variable";
+	}else{
+		return "VariableCategory";
+	}
+};
+urule.VariableValue.prototype.getContainer=function(){
+	return this.container;
+};

+ 332 - 0
urule-console-js/src/editor/common/contextMenu.js

@@ -0,0 +1,332 @@
+
+(function(){
+	if(!window.URule){
+		window.URule={};
+	}
+    URule.menu={};
+    URule.menu.AbstractMenu=function(config){
+    };
+    URule.menu.AbstractMenu.prototype={
+        fadeSpeed: 100,
+        above: 'auto',
+        preventDoubleContext: true,
+        compress: false,
+        createDom:function(){
+
+        },getDom:function(){
+            if(!this._dom){
+                this._dom=this.createDom();
+                $(this._dom).data("ref",this);
+            }
+            return this._dom;
+        },render:function(target){
+            if(!this._rendered){
+                if(target){
+                    $(target).append(this.getDom());
+                }else{
+                    $("body").append(this.getDom());
+                }
+            }
+            this._rendered=true;
+        }, setConfig:function(config){
+            this.remove();
+            this.constructor.call(this,config);
+
+        },remove:function(){
+            if(this._dom){
+                this._dom.remove();
+                this._dom=null;
+            }
+            this._rendered=false;
+        }
+    };
+
+    URule.menu.Menu=function(config){
+        URule.menu.Menu.prototype.superClass.call(this,config);
+        $.extend(this,config);
+    };
+
+    URule.menu.Menu.prototype=new URule.menu.AbstractMenu();
+    URule.menu.Menu.prototype.superClass=URule.menu.AbstractMenu;
+    URule.menu.Menu.prototype.constructor=URule.menu.Menu;
+
+    URule.menu.Menu.prototype.createDom=function(){
+        var compressed,dom,menuItems,ul;
+        compressed= this.compress ? ' compressed-context' : '';
+        ul=$("<ul class='dropdown-menu dropdown-context" + compressed+"' style='font-size:12px'></ul>");
+        dom = ul[0];
+        this._dom=dom;
+        menuItems=this.menuItems;
+        var self=this;
+        if(menuItems.length>20){
+            var searchContainer=$("<div style='margin-left: 2px;margin-right: 2px'>");
+            searchContainer.append("<i class='glyphicon glyphicon-filter' style='color:#006600;margin-left: 2px;margin-right: 2px'></i>  ");
+            this.search=$("<input type='text' class='form-control' placeholder='输入值后回车查询' style='width: 85%;display: inline-block;height: 26px;padding: 1px;font-size: 12px;'>");
+            searchContainer.append(this.search);
+            ul.append(searchContainer);
+            this.search.click(function(e){
+                e.stopPropagation();
+            });
+            this.search.dblclick(function(e){
+                e.stopPropagation();
+            });
+            this.search.keypress(function(event){
+                var keynum = (event.keyCode ? event.keyCode : event.which);
+                if(keynum !== '13' && keynum !== 13){
+                    return;
+                }
+                var value=$(this).val();
+                if(self.oldSearchValue && self.oldSearchValue===value){
+                    return;
+                }
+                self.oldSearchValue=value;
+                while(self.menuItems.length>0){
+                    self.menuItems[0].remove();
+                }
+                for(var i=0;i<menuItems.length;i++){
+                    var item=menuItems[i];
+                    var label=item.label;
+                    if(!value || value===""){
+                        self.addMenuItem(item);
+                    }else if(label && label.indexOf(value)>-1){
+                        self.addMenuItem(item);
+                    }
+                }
+            });
+        }
+        this.menuItems=[];
+        for(var i=0;i<menuItems.length;i++){
+            this.addMenuItem(menuItems[i]);
+        }
+        return dom;
+    };
+
+    URule.menu.Menu.prototype.addMenuItem=function(menuItem){
+        var item;
+        if(menuItem instanceof URule.menu.MenuItem){
+            item=menuItem;
+        }else{
+            if(menuItem.$type){
+                item=eval("(URule.menu."+menuItem.$type+"(menuItem))")
+            }else{
+                item=new URule.menu.MenuItem(menuItem);
+            }
+        }
+        item.parent=this;
+        item.render(this.getDom());
+        this.menuItems.push(item);
+        return item;
+    };
+
+    URule.menu.Menu.prototype.getMenuItem=function(nameOrIndex){
+        var target;
+        for(var i=0;i<this.menuItems.length;i++){
+            target=this.menuItems[i];
+            if(typeof nameOrIndex==="string"){
+                if(target.name===nameOrIndex){
+                    return target;
+                }
+            }else{
+                return this.menuItems[nameOrIndex];
+            }
+            if(target.subMenu){
+                target=target.subMenu.getMenuItem(nameOrIndex);
+                if(target){
+                	return target;
+                }
+            }
+        }
+    };
+
+    URule.menu.Menu.prototype.remove=function(){
+        URule.menu.Menu.prototype.superClass.prototype.remove.call(this);
+        if(this.parent){
+            this.parent.subMenu=null;
+            this.parent.getDom().removeClass("dropdown-submenu");
+        }
+    };
+
+    URule.menu.Menu.prototype.show=function(e){
+        e.preventDefault();
+        e.stopPropagation();
+        this.render();
+        $('.modal').removeAttr('tabindex');
+        var $dd=$(this.getDom());
+        var target=$(e.target),z=3;
+        while(!target.is("body")){
+        	var pz=target.css("z-index");
+        	if(!isNaN(pz) && pz!=='0'){
+                z = parseInt(pz) + 1;
+        		break;
+        	}
+        	target=target.parent();
+        }
+        $dd.css("z-index", z);
+        $('.dropdown-context:not(.dropdown-context-sub)').hide();
+        if (typeof this.above == 'boolean' && this.above) {
+            $dd.addClass('dropdown-context-up')
+                .css({
+                    top: e.pageY - 20 - $dd.height(),
+                    left: e.pageX - 13
+                }).fadeIn(this.fadeSpeed);
+        } else if (typeof this.above == 'string' && this.above == 'auto') {
+            $dd.removeClass('dropdown-context-up');
+            var autoH = $dd.height() + 12;
+            if ((e.pageY + autoH) > ($('html').height()+10) && e.pageY > autoH) {
+                $dd.addClass('dropdown-context-up').css({
+                    top: e.pageY - 20 - autoH,
+                    left: e.pageX - 13
+                }).fadeIn(this.fadeSpeed);
+            } else {
+                $dd.css({
+                    top: e.pageY + 10,
+                    left: e.pageX - 13
+                }).fadeIn(this.fadeSpeed);
+            }
+        }
+        if(this.onShow){
+        	this.onShow(this);
+        }
+    };
+    
+    URule.menu.Menu.prototype.hide=function(){
+    	var $dom=$(this._dom);
+    	if($dom.is(":visible")){
+    		if(this.onHide){
+        		this.onHide(this);
+        	}
+        	$dom.fadeOut(this.fadeSpeed, function(){
+        		$dom.css({display:''}).find('.drop-left').removeClass('drop-left');
+        	});
+        	if(this.parent){
+        		this.parent.parent.hide();
+        	}
+    	}
+    	
+    };
+
+    URule.menu.MenuItem=function(config){
+        URule.menu.MenuItem.prototype.superClass.call(this,config);
+        $.extend(this,config);
+    };
+
+    URule.menu.MenuItem.prototype=new URule.menu.AbstractMenu();
+    URule.menu.MenuItem.prototype.superClass=URule.menu.AbstractMenu;
+    URule.menu.MenuItem.prototype.constructor=URule.menu.MenuItem;
+
+    URule.menu.MenuItem.prototype.createDom=function(){
+        var $li,iconAndLabel,self;
+        self=this;
+        $li=$("<li style='cursor: default'></li>");
+        this._dom=$li[0];
+        if(this.icon){
+            iconAndLabel="<i class='"+this.icon+"'></i> "+this.label;
+        }else{
+            iconAndLabel=this.label;
+        }
+        
+        $li.on("mouseenter",function(){
+        	$li.siblings(".dropdown-submenu").each(function(){
+        		$(this).find("ul.dropdown-context-sub").each(function(){
+        			var menu=$(this).data("ref");
+            		$(this).fadeOut(menu.fadeSpeed);
+        		});
+        	});
+
+        });
+
+        if(this.type==="divider"){
+            $li.addClass("divider");
+            $li.append(iconAndLabel);
+        }else if(this.type=="header"){
+            $li.addClass("nav-header");
+            $li.append(iconAndLabel);
+        }else{
+            $li.append("<a>"+iconAndLabel+"</a>");
+            if(this.subMenu){
+                this.setSubMenu(this.subMenu);
+            }
+        }
+        if(self.onClick){
+            if(this.parent && this.parent.search){
+                $li.click(function(e){
+                    e.stopPropagation();
+                });
+                $li.dblclick(function(e){
+                    self.onClick(self,{event:e});
+                    e.preventDefault();
+                    e.stopPropagation();
+                    self.parent.hide();
+                });
+            }else{
+                $li.click(function(e){
+                    e.preventDefault();
+                    e.stopPropagation();
+                    self.onClick(self,{event:e});
+                    self.parent.hide();
+                });
+            }
+        }
+        return $li[0];
+    };
+
+
+    URule.menu.MenuItem.prototype.setSubMenu=function(menu){
+        var dom,self;
+        self=this;
+        dom=self.getDom();
+        if(menu instanceof URule.menu.Menu){
+            self.subMenu=menu;
+        }else{
+            self.subMenu=new URule.menu.Menu(menu);
+        }
+        self.subMenu.parent=this;
+        $(dom).attr("class","dropdown-submenu")
+            .on("mouseenter", function(){
+                var $sub = $(this).find(".dropdown-context-sub:first"),
+                    subWidth = $sub.width(),
+                    subLeft = $sub.offset().left,
+                    collision = (subWidth+subLeft) > window.innerWidth;
+                if(collision){
+                    $sub.addClass('drop-left');
+                }
+                $(self.subMenu.getDom()).fadeIn(self.subMenu.fadeSpeed);
+            });
+        this.subMenu.render(dom);
+        $(this.subMenu.getDom()).addClass("dropdown-context-sub");
+        return this.subMenu;
+
+    };
+
+    URule.menu.MenuItem.prototype.remove=function(){
+        URule.menu.MenuItem.prototype.superClass.prototype.remove.call(this);
+        var i;
+        i=this.parent.menuItems.indexOf(this);
+        this.parent.menuItems.splice(i,1);
+    };
+    
+    URule.menu.MenuItem.prototype.show=function(){
+        $(this._dom).show();
+    };
+    
+    URule.menu.MenuItem.prototype.hide=function(){
+        $(this._dom).hide();
+    };
+
+
+
+    $(document).on('dblclick', 'html', function () {
+        $('.dropdown-context').each(function(){
+            var menu;
+            menu=$(this).data("ref");
+            menu.hide();
+        })
+    });
+
+    if(URule.menu.AbstractMenu.preventDoubleContext){
+        $(document).on('contextmenu', '.dropdown-context', function (e) {
+            e.preventDefault();
+        });
+    }
+
+})();

+ 209 - 0
urule-console-js/src/editor/common/if-hint.js

@@ -0,0 +1,209 @@
+
+(function(mod) {
+	if (typeof exports == "object" && typeof module == "object"){
+		mod(require("../../lib/codemirror"));
+	}else if (typeof define == "function" && define.amd){
+	    define(["../../lib/codemirror"], mod);
+	}else {
+	    mod(CodeMirror);
+	}
+})(function(CodeMirror) {
+	var Pos = CodeMirror.Pos;
+
+	function forEach(arr, f) {
+		for (var i = 0, e = arr.length; i < e; ++i){
+			f(arr[i])
+		};
+	}
+
+	function arrayContains(arr, item) {
+		if (!Array.prototype.indexOf) {
+			var i = arr.length;
+			while (i--) {
+				if (arr[i] === item) {
+					return true;
+				}
+			}
+			return false;
+		}
+		return arr.indexOf(item) != -1;
+	}
+	
+	function getMode(editor){
+		var cur=editor.getCursor();
+		var mode=editor.getModeAt({line:cur.line,ch:cur.ch});
+		return mode;
+	}
+	
+	function getBlock(){
+		return "if";
+	}
+	
+	function scriptHint(editor, keywords, getToken, options) {
+		var cur = editor.getCursor(), 
+			token = getToken(editor, cur),
+			comment=/\b(?:string|comment)\b/,
+			property=/^[\w$_\u4e00-\u9fa5]*$/;
+		if (comment.test(token.type)) return;
+		token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
+		if (token.type=="property") {
+			token = {
+				start: cur.ch-token.string.length+1, 
+				end: cur.ch+token.string.length-1, 
+				string: token.string.replace(".",""), 
+				state: token.state
+			};
+		}else if(!property.test(token.string)){
+			token = {
+					start: cur.ch, 
+					end: cur.ch, 
+					string: "", 
+					state: token.state
+				};
+		}else if (token.end > cur.ch) {
+			token.end = cur.ch;
+			token.string = token.string.slice(0, cur.ch - token.start);
+		}
+		var tprop = token;
+		while (true) {
+			tprop = getToken(editor, Pos(cur.line, tprop.start));
+			if (tprop.type != "property") break;
+			tprop = getToken(editor, Pos(cur.line, tprop.start));
+			if (!context){ 
+				var context = [];
+			}
+			context.push(tprop);
+		}
+		return {list: getCompletions( token, context, keywords, options),
+            from: Pos(cur.line, token.start),
+            to: Pos(cur.line, token.end)};
+  		}
+
+  		function uruleHint(editor, options) {
+			mode=getMode(editor);
+			var block=mode.name, keywords;
+			if(block=="if"){
+				keywords=ifKeywords;
+			}else if(block=="then"){
+				keywords=thenKeywords;
+			}
+  			return scriptHint(editor, keywords,
+                      function (e, cur) {return e.getTokenAt(cur);},
+                      options);
+  		};
+  		CodeMirror.registerHelper("hint", "if", uruleHint);
+
+  		var ifKeywords = ( "参数 > >= < <= == != Endwith NotEndwith Startwith NotStartwith In NotIn Match NotMatch EqualsIgnoreCase NotEqualsIgnoreCase eval()").split(" ");
+  		var thenKeywords = ( "参数 out").split(" ");
+  		
+  		function getCompletions(token, context, keywords, options) {
+  			var found = [], start = token.string, global = options && options.globalScope || window;
+  			function maybeAdd(str) {
+  				if (str.toUpperCase().lastIndexOf(start.replace(/\s*/,"").toUpperCase(), 0) == 0 && !arrayContains(found, str)) found.push(str);
+  			}
+  			function gatherCompletions(base) {
+  				var data = _dataSetResourceLibrary.getData();
+	  			if(data){
+	  				var library=data.toJSON(),
+	  					actionLibraries=library.actionLibraries||[],
+	  					variableCategories=library.variableCategories||[],
+	  					constantCategories=library.constantCategories||[];
+	  				for(var i=0;i<actionLibraries.length;i++){
+	  					var actionLibrary=actionLibraries[i],
+	  						springBeans=actionLibrary.springBeans||[];
+	  					for(var j=0;j<springBeans.length;j++){
+	  						var springBean=springBeans[j],
+	  							methods=springBean.methods||[];
+	  						if(springBean.name.toUpperCase().lastIndexOf(base.toUpperCase(), 0) != 0)continue;
+	  						for(var k=0;k<methods.length;k++){
+	  	  						var method=methods[k],
+	  	  							parameters=method.parameters||[],
+	  	  							name=method.name+"(",
+	  	  							ps=[];
+		  	  					for(var z=0;z<parameters.length;z++){
+		  	  						var parameter=parameters[z];
+		  	  						ps.push(parameter.name);
+		  	  					}
+		  	  					
+		  	  					name+=ps.join(",")+")";
+		  	  					maybeAdd(name);
+	  	  					}
+	  					}
+	  				}
+	  				
+	  				for(var i=0;i<variableCategories.length;i++){
+	  					var variableCategorie=variableCategories[i],
+	  						variables=variableCategorie.variables||[];
+						if(variableCategorie.name.toUpperCase().lastIndexOf(base.toUpperCase(), 0) != 0)continue;
+	  					for(var j=0;j<variables.length;j++){
+	  						var variable=variables[j],
+	  							name=variable.label;
+		  	  				maybeAdd(name);
+	  					}
+	  				}
+	  				
+	  				for(var i=0;i<constantCategories.length;i++){
+	  					var constantCategory=constantCategories[i],
+	  						constants=constantCategory.constants||[],
+	  						name="$"+constantCategory.label;
+						if(name.toUpperCase().lastIndexOf(base.toUpperCase(), 0) != 0)continue;
+	  					for(var j=0;j<constants.length;j++){
+	  						var constant=constants[j],
+	  							name=constant.label;
+		  	  				maybeAdd(name);
+	  					}
+	  				}
+	  				
+	  			}
+  				
+  			}
+
+	  		if (context && context.length) {
+	  			var obj = context.pop(), base;
+	  			if (obj.type && (obj.type.indexOf("variable") === 0||obj.type.indexOf("atom-2")===0)) {
+	  				base = obj.string;
+	  				if(base){
+	  					if(base=="参数"){
+		  					gatherCompletions("参数");
+	  					}else{
+	  						gatherCompletions(base);
+	  					}
+	  				}
+	  			}
+	  		} else {
+	  			var data = _dataSetResourceLibrary.getData();
+	  			if(data){
+	  				var library=data.toJSON(),
+					actionLibraries=library.actionLibraries,
+					variableCategories=library.variableCategories,
+					constantCategories=library.constantCategories;
+		  			for(var i=0;i<actionLibraries.length;i++){
+	  					var actionLibrary=actionLibraries[i],
+	  						springBeans=actionLibrary.springBeans;
+	  					for(var j=0;j<springBeans.length;j++){
+	  						var springBean=springBeans[j],
+	  							name=springBean.name;
+	  						maybeAdd(name);
+	  					}
+	  				}
+	  				
+	  				for(var i=0;i<variableCategories.length;i++){
+	  					var variableCategorie=variableCategories[i],
+	  						name=variableCategorie.name;
+	  					if(name != "参数") {
+		  					maybeAdd(name);
+	  					}
+	  				}
+	  				
+	  				for(var i=0;i<constantCategories.length;i++){
+	  					var constantCategorie=constantCategories[i],
+	  						name="$"+constantCategorie.label;
+	  	  				maybeAdd(name);
+	  				}
+	  			}
+	  			forEach(keywords, maybeAdd);
+	  			
+	  		}
+	  		return found;
+  		}
+});

+ 44 - 0
urule-console-js/src/editor/common/if_mode.js

@@ -0,0 +1,44 @@
+/**
+ * @author GJ
+ */
+CodeMirror.defineSimpleMode("if", {
+  // The start state contains the rules that are intially used
+  start: [
+    // The regex matches the token, the token property contains the type
+    {regex: /"(?:[^\\]|\\.)*?"/, token: "string"},
+    {regex: /(true|false|null|and|or)\b/, token: "atom"},
+    {regex: /\s+(\u6216\u8005|\u6216|\u5E76\u4E14|\u4E14)\s+/, token: "atom"},
+    {regex: /\u53C2\u6570/, token: "atom-2"},
+    {regex: /(out|eval)\b/, token: "atom-3"},
+    {regex: /\.([\w$_\u4e00-\u9fa5][\w$_\u4e00-\u9fa5\d]*)*/, token: "property"},
+    {regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i, token: "number"},
+    {regex: /\/\/.*/, token: "comment"},
+    {regex: /\/(?:[^\\]|\\.)*?\//, token: "comment"},
+    // A next property will cause the mode to move to a different state
+    {regex: /\/\*/, token: "comment", next: "comment"},
+    {regex: /[-+\/*=<>!]+/, token: "operator"},
+    {regex: /(Endwith|NotEndwith|Startwith|NotStartwith|In|NotIn|Match|NotMatch|'EqualsIgnoreCase|NotEqualsIgnoreCase)\b/, token: "operator"},
+
+    // indent and dedent properties guide autoindentation
+    {regex: /[\{\[\(]/, indent: true},
+    {regex: /[\}\]\)]/, dedent: true},
+    {regex: /[\w$_\u4e00-\u9fa5][\w$_\u4e00-\u9fa5\d]*/, token: "variable"},
+    // You can embed other modes with the mode property. This rule
+    // causes all code between << and >> to be highlighted with the XML
+    // mode.
+    {regex: /<</, token: "meta", mode: {spec: "xml", end: />>/}}
+  ],
+  // The multi-line comment state.
+  comment: [
+    {regex: /.*?\*\//, token: "comment", next: "start"},
+    {regex: /.*/, token: "comment"}
+  ],
+  // The meta property contains global information about the mode. It
+  // can contain properties like lineComment, which are supported by
+  // all modes, and also directives like dontIndentStates, which are
+  // specific to simple modes.
+  meta: {
+    dontIndentStates: ["comment"],
+    lineComment: "//"
+  }
+});

+ 19 - 0
urule-console-js/src/editor/common/jquery.utils.js

@@ -0,0 +1,19 @@
+(function($){
+	if(!window.URule){
+		window.URule={};
+	}
+	URule.setDomContent=function(jqueryObject,value){
+		if(navigator.userAgent.indexOf("Firefox")>0){
+			jqueryObject.prop("textContent",value);
+		}else{
+			jqueryObject.prop("innerText",value);
+		}
+	};
+	URule.getDomContent=function(jqueryObject){
+		if(navigator.userAgent.indexOf("Firefox")>0){
+			return jqueryObject.prop("textContent");
+		}else{
+			return jqueryObject.prop("innerText");
+		}
+	};
+})($);

+ 226 - 0
urule-console-js/src/editor/context.standalone.css

@@ -0,0 +1,226 @@
+/**
+ * ContextJS Styles
+ * For use WITHOUT Twitters Bootstrap CSS
+ */
+
+.nav-header {
+	display: block;
+	padding: 3px 15px;
+	font-size: 11px;
+	font-weight: bold;
+	line-height: 20px;
+	color: #999;
+	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+	text-transform: uppercase;
+}
+.dropdown-menu {
+	position: absolute;
+	top: 100%;
+	left: 0;
+	z-index: 1000;
+	display: none;
+	float: left;
+	min-width: 160px;
+	padding: 5px 0;
+	margin: 2px 0 0;
+	list-style: none;
+	background-color: #ffffff;
+	border: 1px solid #ccc;
+	border: 1px solid rgba(0, 0, 0, 0.2);
+	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+	font-size: 14px;
+	*border-right-width: 2px;
+	*border-bottom-width: 2px;
+	-webkit-border-radius: 6px;
+	-moz-border-radius: 6px;
+	border-radius: 6px;
+	-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+	-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+	box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+	-webkit-background-clip: padding-box;
+	-moz-background-clip: padding;
+	background-clip: padding-box;
+	text-align:left;
+}
+.dropdown-menu.pull-right {
+	right: 0;
+	left: auto;
+}
+.dropdown-menu .divider {
+	*width: 100%;
+	height: 1px;
+	margin: 9px 1px;
+	*margin: -5px 0 5px;
+	overflow: hidden;
+	background-color: #e5e5e5;
+	*border-bottom: 1px solid #ffffff;
+}
+.dropdown-menu a {
+	display: block;
+	padding: 3px 20px;
+	clear: both;
+	font-weight: normal;
+	line-height: 20px;
+	color: #333333;
+	white-space: nowrap;
+	text-decoration: none;
+}
+.dropdown-menu li > a:hover, .dropdown-menu li > a:focus, .dropdown-submenu:hover > a {
+	color: #ffffff;
+	text-decoration: none;
+	background-color: #0088cc;
+	background-color: #0081c2;
+	background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+	background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+	background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+	background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+	background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+	background-repeat: repeat-x;
+	filter: progid: dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+.dropdown-menu .active > a, .dropdown-menu .active > a:hover {
+	color: #ffffff;
+	text-decoration: none;
+	background-color: #0088cc;
+	background-color: #0081c2;
+	background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+	background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+	background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+	background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+	background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+	background-repeat: repeat-x;
+	outline: 0;
+}
+.dropdown-menu .disabled > a, .dropdown-menu .disabled > a:hover {
+	color: #999999;
+}
+.dropdown-menu .disabled > a:hover {
+	text-decoration: none;
+	cursor: default;
+	background-color: transparent;
+}
+.open {
+	*z-index: 1000;
+}
+.open > .dropdown-menu {
+	display: block;
+}
+.pull-right > .dropdown-menu {
+	right: 0;
+	left: auto;
+}
+.dropup .caret, .navbar-fixed-bottom .dropdown .caret {
+	border-top: 0;
+	border-bottom: 4px solid #000000;
+	content: "\2191";
+}
+.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu {
+	top: auto;
+	bottom: 100%;
+	margin-bottom: 1px;
+}
+.dropdown-submenu {
+	position: relative;
+}
+.dropdown-submenu > .dropdown-menu {
+	top: 0;
+	left: 100%;
+	margin-top: -6px;
+	margin-left: -1px;
+	-webkit-border-radius: 0 6px 6px 6px;
+	-moz-border-radius: 0 6px 6px 6px;
+	border-radius: 0 6px 6px 6px;
+}
+.dropdown-submenu > .dropdown-menu.drop-left{
+	left:-80%;
+}
+.dropdown-submenu:hover .dropdown-menu {
+	display: block;
+}
+.dropdown-submenu > a:after {
+	display: block;
+	float: right;
+	width: 0;
+	height: 0;
+	margin-top: 5px;
+	margin-right: -10px;
+	border-color: transparent;
+	border-left-color: #cccccc;
+	border-style: solid;
+	border-width: 5px 0 5px 5px;
+	content: " ";
+}
+.dropdown-submenu:hover > a:after {
+	border-left-color: #ffffff;
+}
+.dropdown .dropdown-menu .nav-header {
+	padding-right: 20px;
+	padding-left: 20px;
+}
+/**
+ * 	Context Styles
+ */
+
+.dropdown-context .nav-header {
+	cursor: default;
+}
+.dropdown-context:before, .dropdown-context-up:before {
+	position: absolute;
+	top: -7px;
+	left: 9px;
+	display: inline-block;
+	border-right: 7px solid transparent;
+	border-bottom: 7px solid #ccc;
+	border-left: 7px solid transparent;
+	border-bottom-color: rgba(0, 0, 0, 0.2);
+	content: '';
+}
+.dropdown-context:after, .dropdown-context-up:after {
+	position: absolute;
+	top: -6px;
+	left: 10px;
+	display: inline-block;
+	border-right: 6px solid transparent;
+	border-bottom: 6px solid #ffffff;
+	border-left: 6px solid transparent;
+	content: '';
+}
+.dropdown-context-up:before, .dropdown-context-up:after {
+	top: auto;
+	bottom: -7px;
+	z-index: 9999;
+}
+.dropdown-context-up:before {
+	border-right: 7px solid transparent;
+	border-top: 7px solid #ccc;
+	border-bottom: none;
+	border-left: 7px solid transparent;
+}
+.dropdown-context-up:after {
+	border-right: 6px solid transparent;
+	border-top: 6px solid #ffffff;
+	border-left: 6px solid transparent;
+	border-bottom: none;
+}
+.dropdown-context-sub:before, .dropdown-context-sub:after {
+	display: none;
+}
+.dropdown-context .dropdown-submenu:hover .dropdown-menu {
+	display: none;
+}
+.dropdown-context .dropdown-submenu:hover > .dropdown-menu {
+	display: none;
+}
+
+.compressed-context a{
+	padding-left: 14px;
+	padding-top: 0;
+	padding-bottom: 0;
+	font-size: 13px;
+	}
+.compressed-context .divider{
+	margin: 5px 1px;
+	}
+.compressed-context .nav-header{
+	padding:1px 13px;
+	}

+ 45 - 0
urule-console-js/src/editor/decisiontable/CellCondition.js

@@ -0,0 +1,45 @@
+/**
+ * @author GJ
+ */
+window._conditionId=0;
+urule.CellCondition=function(element){
+	this.container=$(element);
+	this.container.css({
+		height:"40px",
+		position:"relative"
+	});
+	var context=new urule.Context(this.container);
+	this.join=new urule.Join(context);
+	this.join.init(null);
+	this.join.initTopJoin(this.container);
+	this.join.setType("and");
+	this.id=window._conditionId++;
+};
+urule.CellCondition.prototype.clean=function(){
+	this.join.clean();
+	window._setDirty();
+};
+urule.CellCondition.prototype.getId=function(){
+	return this.id;
+};
+urule.CellCondition.prototype.renderTo=function(container){
+	container.append(this.container);
+};
+urule.CellCondition.prototype.getDisplayContainer=function(){
+	var dis=null;
+	if(this.join){
+	    dis= this.join.getDisplayContainer();
+	}
+	if(!dis){
+		dis= $("<span style='color:gray'>无</span>");
+	}
+	return dis;
+};
+urule.CellCondition.prototype.initData=function(data){
+	if(this.join){
+		this.join.initData(data);
+	}
+};
+urule.CellCondition.prototype.toXml=function(){
+	return this.join.toXml();
+};

+ 29 - 0
urule-console-js/src/editor/decisiontable/CellContent.js

@@ -0,0 +1,29 @@
+/**
+ * @author GJ
+ */
+urule.CellContent=function(element){
+	this.container=$(element);
+	this.container.css({
+		height:"40px",
+		width:"100%"
+	});
+	this.inputType=new urule.InputType(null,"无");
+	this.container.append(this.inputType.getContainer());
+};
+urule.CellContent.prototype.clean=function(data){
+	if(this.inputType){
+		this.inputType.getContainer().remove();
+	}
+	this.inputType=new urule.InputType(null,"无");
+	this.container.append(this.inputType.getContainer());
+	window._setDirty();
+};
+urule.CellContent.prototype.initData=function(data){
+	this.inputType.setValueType(data["valueType"],data);
+};
+urule.CellContent.prototype.toXml=function(){
+	if(this.inputType.type==""){
+		return "";
+	}
+	return this.inputType.toXml();
+};

+ 99 - 0
urule-console-js/src/editor/decisiontable/CellExecuteMethod.js

@@ -0,0 +1,99 @@
+/**
+ * @author GJ
+ */
+urule.CellExecuteMethod=function(element){
+	this.parentContainer=$(element);
+	this.parentContainer.css({
+		height:"40px",
+		width:"100%"
+	});
+	this.container=generateContainer();
+//	this.container.prop("innerText","无");
+	URule.setDomContent(this.container,"无");
+	this.container.css({
+		"color":"gray"
+	});
+	this.parentContainer.append(this.container);
+	window._ActionTypeArray.push(this);
+	this.initMenu();
+};
+urule.CellExecuteMethod.prototype.initMenu=function(actionLibraries){
+	var data=window._uruleEditorActionLibraries;
+	if(actionLibraries){
+		data=actionLibraries;
+	}
+	var self,onClick,config;
+	self=this;
+	onClick=function(menuItem){
+		var parent=menuItem.parent.parent;
+		self.setAction({
+			beanLabel:parent.label,
+			beanId:parent.name,
+			methodLabel:menuItem.label,
+			methodName:menuItem.name,
+			parameters:menuItem.parameters
+		});
+	};
+	config={menuItems:[]};
+	$.each(data||[],function(index,item){
+		var springBeans=item.springBeans||[]; 
+		$.each(springBeans,function(i,springBean){
+			var menuItem={
+				name:springBean.id,
+				label:springBean.name
+			}
+			var methods=springBean.methods||[];
+			$.each(methods,function(j,method){
+				if(!menuItem.subMenu){
+					menuItem.subMenu={menuItems:[]};
+				}
+				menuItem.subMenu.menuItems.push({
+					name:method.methodName,
+					label:method.name,
+					parameters:method.parameters,
+					onClick:onClick
+				});
+			});
+
+			config.menuItems.push(menuItem);
+		});
+	});
+	if(self.menu){
+		self.menu.setConfig(config);
+	}else{
+		self.menu=new URule.menu.Menu(config);
+	}
+	this.container.click(function(e){
+		self.menu.show(e);
+	});
+};
+urule.CellExecuteMethod.prototype.clean=function(){
+	window._setDirty();
+	if(this.action){
+		this.action.getContainer().remove();
+	}
+	URule.setDomContent(this.container,"无");
+	this.container.css({
+		"color":"gray"
+	});
+	this.action=null;
+};
+urule.CellExecuteMethod.prototype.setAction=function(data){
+	window._setDirty();
+	if(this.action){
+		this.action.getContainer().remove();
+	}
+	this.action=new urule.MethodAction();
+	URule.setDomContent(this.container,".");
+	this.container.css({
+		"color":"white"
+	});
+	this.parentContainer.append(this.action.getContainer());
+	this.action.initData(data);
+};
+urule.CellExecuteMethod.prototype.toXml=function(){
+	if(this.action){
+		return this.action.toXml();		
+	}
+	return "";
+};

+ 47 - 0
urule-console-js/src/editor/decisiontable/Condition.js

@@ -0,0 +1,47 @@
+/**
+ * @author GJ
+ */
+urule.Condition=function(parentContainer){
+	this.container=$("<span>");
+	parentContainer.append(this.container);
+	var self=this;
+	this.operator=new urule.ComparisonOperator(function(){
+		self.inputType=self.operator.getInputType();
+		if(self.inputType){
+			self.container.append(self.inputType.getContainer());		
+		}
+	});
+	self.container.append(this.operator.getContainer());
+};
+urule.Condition.prototype.initData=function(data){
+	var op=data["op"];
+	this.operator.setOperator(op);
+	this.operator.initRightValue(data["value"]);
+	this.inputType=this.operator.getInputType();
+	if(this.inputType){
+		this.container.append(this.inputType.getContainer());		
+	}
+};
+urule.Condition.prototype.getDisplayContainer=function(){
+	var container=$("<span>");
+	var operator=URule.getDomContent(this.operator.getContainer());
+	container.append($("<span style='color:blue'>"+operator+"</span>"));
+	if(this.inputType){
+		container.append(this.inputType.getDisplayContainer());
+	}
+	return container;
+};
+urule.Condition.prototype.toXml=function(){
+	var xml="<condition op=\""+this.operator.getOperator()+"\">";
+	if(this.inputType){
+		xml+=this.inputType.toXml();
+	}
+	xml+="</condition>";
+	return xml;
+};
+urule.Condition.prototype.getOperator=function(){
+	return this.operator;
+};
+urule.Condition.prototype.getInputType=function(){
+	return this.inputType;
+};

+ 140 - 0
urule-console-js/src/editor/decisiontable/Connection.js

@@ -0,0 +1,140 @@
+/**
+ * @author GJ
+ */
+urule.Connection=function(context,isJoin,parentJoin){
+	this.isJoin=isJoin;
+	this.context=context;
+	this.parentJoin=parentJoin;
+};
+urule.Connection.prototype.drawPath=function(startX,startY,endX,endY){
+	this.startX=startX;
+	this.endX=endX;
+	if(this.isJoin){
+		this.startY=startY-3;
+		this.endY=endY+2;
+	}else{
+		this.startY=startY-3;
+		this.endY=endY-3;
+	}
+	this.path=this.context.getPaper().path(this.buildPathInfo());
+	this.path.attr({'stroke':'#777'});
+	if(this.isJoin){
+		this.initJoin();
+	}else{
+		this.initCondition();
+	}
+};
+urule.Connection.prototype.toXml=function(){
+	var xml="";
+	if(this.isJoin){
+		xml=this.join.toXml();
+	}else{
+		xml=this.condition.toXml();
+	}
+	return xml;
+};
+urule.Connection.prototype.initJoin=function(){
+	this.join=new urule.Join(this.context);
+	this.join.init(this);
+	var joinContainer=this.join.getContainer();
+	var left=(this.endX+10)+"px";
+	var top=this.endY+"px";
+	joinContainer.css({
+		"position":"absolute",
+		"left":left,
+		"top":top
+	});
+	this.context.getCanvas().append(joinContainer);
+};
+
+urule.Connection.prototype.getDisplayContainer=function(){
+	if(this.join){
+		return this.join.getDisplayContainer();
+	}else{
+		return this.condition.getDisplayContainer();
+	}
+};
+
+urule.Connection.prototype.remove=function(){
+	this.path.remove();
+	if(this.join){
+		this.join.getContainer().remove();
+	}else{
+		this.conditionContainer.remove();
+	}
+	window._setDirty();
+};
+
+urule.Connection.prototype.initCondition=function(){
+	this.conditionContainer=$("<div>");
+	var left=(this.endX+10)+"px";
+	var top=this.endY+"px";
+	this.conditionContainer.css({
+		"position":"absolute",
+		"left":left,
+		"top":top
+	});
+	this.condition=new urule.Condition(this.conditionContainer);
+	var del=$(`<i class="glyphicon glyphicon-trash" style="color: #019dff;cursor: pointer;font-size: 9pt;padding-left:5px"></i>`);
+	var self=this;
+	del.click(function(){
+		self.parentJoin.removeConnection(self);
+	});
+	this.conditionContainer.append(del);
+	this.context.getCanvas().append(this.conditionContainer);
+};
+urule.Connection.prototype.update=function(add){
+	var pathInfo=this.buildPathInfo();
+	this.path.attr("path",pathInfo);
+	var top=this.endY+"px";
+	if(this.conditionContainer){
+		this.conditionContainer.css({
+			"top":top
+		});						
+	}else{
+		this.join.getContainer().css({
+			"top":top
+		});		
+	}
+	if(this.join){
+		this.join.resetItemPosition(0,add);
+	}
+};
+urule.Connection.prototype.getParentJoin=function(){
+	return this.parentJoin;
+};
+urule.Connection.prototype.getCondition=function(){
+	return this.condition;
+};
+urule.Connection.prototype.getJoin=function(){
+	return this.join;
+};
+urule.Connection.prototype.getStartX=function(){
+	return this.startX;
+};
+urule.Connection.prototype.getStartY=function(){
+	return this.startY;
+};
+urule.Connection.prototype.getEndX=function(){
+	return this.endX;
+};
+urule.Connection.prototype.getEndY=function(){
+	return this.endY;
+};
+urule.Connection.prototype.setStartX=function(startX){
+	this.startX=startX;
+};
+urule.Connection.prototype.setStartY=function(startY){
+	this.startY=startY;
+};
+urule.Connection.prototype.setEndX=function(endX){
+	this.endX=endX;
+};
+urule.Connection.prototype.setEndY=function(endY){
+	this.endY=endY;
+};
+urule.Connection.prototype.buildPathInfo=function(){
+	var left=10;
+	var top=8;
+	return "M" + (this.startX+left) + "," + (this.startY+top) + " C" + (this.startX+left) + "," + (this.endY+top) + "," + (this.startX+left) + "," + (this.endY+top) + "," + (this.endX+left) + "," + (this.endY+top);
+};

+ 1378 - 0
urule-console-js/src/editor/decisiontable/DecisionTable.js

@@ -0,0 +1,1378 @@
+import {getParameter,ajaxSave} from '../../Utils.js';
+import {MsgBox} from 'flowdesigner';
+
+window._dirty=false;
+window._setDirty=function(){
+	if(window._dirty){
+		return;
+	}
+	window._dirty=true;
+	$("#saveButton").html("<i class='rf rf-save'></i> *保存");
+	$("#saveButton").removeClass("disabled");
+	$("#saveButtonNewVersion").html("<i class='rf rf-savenewversion'></i> *保存新版本");
+	$("#saveButtonNewVersion").removeClass("disabled");
+};
+
+(function(Handsontable){
+	if(!window.URule){
+		window.URule={};
+	}
+	URule.DecisionTable=function(id){
+		var table;
+		window._VariableValueArray.push(this);
+		window._ParameterValueArray.push(this);
+		const container=$("#"+id);
+		this.hasMod = true;
+		this.container=container;
+		const self=this;
+		var saveButton = `<div class="btn-group btn-group-sm navbar-btn" style="margin-top:0px;margin-bottom: 0px" role="group" aria-label="...">
+								<button id="saveButton" type="button" class="btn btn-default navbar-btn" ><i class="rf rf-save"></i> 保存</button>
+								<button id="saveButtonNewVersion" type="button" class="btn btn-default navbar-btn" ><i class="rf rf-savenewversion"></i> 保存新版本</button>
+							</div>`;
+		var addCriteriaButton = `<button id="addCriteriaButton" type="button" class="btn btn-default btn-sm"><i class="glyphicon glyphicon-plus"></i> 添加条件行</button>`;
+		var deleteCriteriaButton = 	`<button id="deleteCriteriaButton" type="button" class="btn btn-default btn-sm"><i class="glyphicon glyphicon-minus"></i> 删除条件行</button>`;
+
+		const buttons=`<nav class="navbar navbar-default" style="margin: 5px">
+			<div>
+				<div>
+					<div class="btn-group navbar-btn" style="margin-top:0px;margin-bottom: 0px;margin-left: 5px" role="group" aria-label="...">
+					 ${addCriteriaButton}
+					 ${deleteCriteriaButton}
+					</div>
+					<div class="btn-group btn-group-sm navbar-btn" style="margin-left:5px;margin-top:0px;margin-bottom: 0px" role="group" aria-label="...">
+						<button id="configVarButton" type="button" class="btn btn-default"><i class="rf rf-variable"></i> 变量库</button>
+						<button id="configConstantsButton" type="button" class="btn btn-default"><i class="rf rf-constant"></i> 常量库</button>
+						<button id="configActionButton" type="button" class="btn btn-default"><i class="rf rf-action"></i> 动作库</button>
+						<button id="configParameterButton" type="button" class="btn btn-default"><i class="rf rf-parameter"></i> 参数库</button>
+					</div>
+					${saveButton}
+		        </div>
+			</div>
+		</nav>`;
+		
+		container.append(buttons);
+
+        const remarkContainer=$("<div style='margin:5px 5px 5px 15px'></div>");
+        container.append(remarkContainer);
+        this.remark=new Remark(remarkContainer);
+
+		this.properties=[];
+
+		var propContainer=$("<div style='margin:5px 5px 5px 15px;border: solid 1px #dddddd;border-radius:5px'></div>");
+		container.append(propContainer);
+		this.propertyContainer=$("<span>");
+		this.propertyContainer.css({
+			"padding":"10px"
+		});
+		var addProp=$("<button type='button' class='rule-add-property btn btn-link'>添加属性</button>");
+		propContainer.append(addProp);
+		propContainer.append(this.propertyContainer);
+		var onClick=function(menuItem){
+			var prop=new urule.RuleProperty(self,menuItem.name,menuItem.defaultValue,menuItem.editorType);
+			self.addProperty(prop);
+		};
+		self.menu=new URule.menu.Menu({
+			menuItems:[{
+				label:"优先级",
+				name:"salience",
+				defaultValue:"10",
+				editorType:1,
+				onClick:onClick
+			},{
+				label:"生效日期",
+				name:"effective-date",
+				defaultValue:"",
+				editorType:2,
+				onClick:onClick
+			},{
+				label:"失效日期",
+				name:"expires-date",
+				defaultValue:"",
+				editorType:2,
+				onClick:onClick
+			},{
+				label:"是否启用",
+				name:"enabled",
+				defaultValue:true,
+				editorType:3,
+				onClick:onClick
+			}]
+		});
+		addProp.click(function(e){
+			self.menu.show(e);
+		});
+
+		$("#addCriteriaButton").click(function(){
+			var cellData=self.getCurrentCellData(),
+				row=cellData.row+cellData.rowspan,
+				col=cellData.col;
+			self.mergeRange(col-1);
+			self.translateRow(row);
+			self.createRowData(row);
+			self.createCellDataRange(row,row,col,self.getLastColIndex());
+			self.insertRow(row-1);
+			self.renderCells();
+			self.setDirty();
+			$("#deleteCriteriaButton").removeClass("disabled");
+			self.invoke("render");
+		});
+
+		$("#deleteCriteriaButton").click(function(){
+			var highlight=self.getHighlight(),
+				row=highlight.row,
+				col=highlight.col,
+				cell=self.getCellData(row, col),
+				rowspan=cell.rowspan;
+			self.mergeRange(col-1,-rowspan);
+			self.removeRowDataRange(row,rowspan);
+			self.removeCellDataRange(row,row+rowspan-1,col,self.getLastColIndex());
+			self.translateRow(row+rowspan,-rowspan);
+			self.removeRow(row,rowspan);
+			self.renderCells();
+			self.setDirty();
+			self.invoke("render");
+			if(self.countRows()==1){
+				$(this).addClass("disabled");
+			}
+		});
+
+
+		$("#configVarButton").click(function(){
+			if(!self.configVarDialog){
+				self.configVarDialog=new urule.ConfigVariableDialog(self);				
+			}
+			self.configVarDialog.open();
+		});
+				
+		$("#configConstantsButton").click(function(){
+			if(!self.configConstantDialog){
+				self.configConstantDialog=new urule.ConfigConstantDialog(self);				
+			}
+			self.configConstantDialog.open();
+		});
+				
+		$("#configActionButton").click(function(){
+			if(!self.configActionDialog){
+				self.configActionDialog=new urule.ConfigActionDialog(self);				
+			}
+			self.configActionDialog.open();
+		});
+
+		$("#configParameterButton").click(function(){
+			if(!self.configParameterDialog){
+				self.configParameterDialog=new urule.ConfigParameterDialog(self);				
+			}
+			self.configParameterDialog.open();			
+		});
+		
+		$("#saveButton").click(function(){			
+			save(false);
+		});
+		
+		$("#saveButtonNewVersion").click(function(){			
+			save(true);
+		});
+		
+		function save(newVersion) {
+			if($("#saveButton").hasClass("disabled")){
+				return false;
+			}
+			const file=getParameter('file'),xml=self.toXml();
+			let postData={content:xml,file,newVersion};
+			const url=window._server+'/common/saveFile';
+			if(newVersion){
+				bootbox.prompt("请输入新版本描述.",function (versionComment) {
+					if(!versionComment){
+						return;
+					}
+					postData.versionComment=versionComment;
+					ajaxSave(url,postData,function () {
+						self.resetState();
+					})
+				});
+			}else{
+				ajaxSave(url,postData,function () {
+					self.resetState();
+				})
+			}
+		}
+
+		self.load();
+		window.ht=self;
+		var config={
+			"type":"urule",
+			"manualRowResize":true,
+			"manualColumnResize":true,
+			"autoWrapCol":true,
+			"startCols":self.getColDatas().length,
+			"maxRows":2147483647,
+			"startRows":self.getRowDatas().length,
+			"fillHandle":false,
+			"multiSelect":false,
+			"className":"htMiddle",
+			"rowHeaders":true,
+			"maxCols":2147483647,
+			"mergeCells":true,
+			"autoWrapRow":true,
+			"outsideClickDeselects":false,
+			"colWidths":120
+		};
+		table=$("<div style='margin-left:15px'></div>");
+		container.append(table);
+		
+		table.handsontable(config);
+		self._handsontable=table.handsontable("getInstance");
+		self._handsontable.ht=self;
+		config.colHeaders=function(col){
+			var column=self.getColData(col),
+				type=column.type,
+				category=column.variableCategory=="parameter"?"参数":column.variableCategory,
+				variable=column.variableLabel,
+				width=column.width,
+				title=category+"."+variable,
+				icon,iconClass;
+			self.setColWidth(col,width);
+			if(!category||!variable){
+				title="";
+			}
+			if(type=="Criteria"){
+				iconClass="glyphicon glyphicon-filter";
+				icon="glyphicon glyphicon-filter";
+			}else if(type=="ExecuteMethod"){
+				title="执行方法";
+				iconClass="glyphicon glyphicon-flash";
+			}else if(type=="Assignment"){
+				iconClass="glyphicon glyphicon-tasks";
+			}else if(type=="ConsolePrint"){
+				title="控制台输出";
+				iconClass="glyphicon glyphicon-print";
+			}
+			return "<i class='"+iconClass+"' style='line-height:21px;'></i> "+title;
+		};
+		config.rowHeaders=function(row){
+			var rowData=self.getRowData(row),
+			height=rowData.height;
+			self.setRowHeight(row,height);
+			return row+1;
+		};
+		config.cells=function(row,col,prop){
+			return {
+				readOnly:true
+			};
+		};
+		self.updateSettings(config);
+		self.renderCells();
+
+		self.addHook("afterSelectionEnd",function(){
+			var colData=self.getCurrentColData(),
+			rowData=self.getCurrentRowData(),
+			cellData=self.getCurrentCellData();
+
+			if(colData.type=="Criteria"){
+				$("#addCriteriaButton").removeClass("disabled");
+			}else{
+				$("#addCriteriaButton").addClass("disabled");
+			}
+		});
+		
+		self.addHook("beforeColumnResize",function(col,size){
+			var colData=self.getColData(col);
+			colData.width=size;
+		    self.setDirty();
+		    self.invoke("render")
+		});
+
+		self.addHook("beforeRowResize",function(row,size){
+			var rowData=self.getRowData(row);
+			rowData.height=size;
+		    self.setDirty();
+		});
+		
+		self.addHook("afterRender",function(){
+//			$(".htCore th").css("background-color","rgb(223, 222, 203)")
+			$(".htCore tr").each(function(){
+				$(this).children().css({"border-right-width":""});
+				$(this).children().eq(self.countCriteriaCols()).css({"border-right-width":"4px"});
+			});
+
+		});
+		self.initMenu();
+		self.resetState();
+		table.find(".handsontable").remove();
+		self.invoke("render");
+	};
+	
+	URule.DecisionTable.prototype={
+		addProperty:function(property){
+			this.propertyContainer.append(property.getContainer());
+			this.properties.push(property);
+			window._setDirty();
+		},
+		updateSettings:function(options){
+			this._handsontable.updateSettings(options);
+		},
+		getCellRenderer:function(cellProperties){
+			return this._handsontable.getCellRenderer(cellProperties);
+		},
+		
+		getValue:function(){
+			return this._handsontable.getValue();
+		},
+
+		alter:function(operate, index, amount, source){
+			this._handsontable.alter(operate, index, amount, source);
+		},
+
+		getCell:function(row, col){
+			return this._handsontable.getCell(row, col);
+		},
+
+		getCellMeta:function(row, col){
+			return this._handsontable.getCellMeta(row, col);
+		},
+
+		selectCell:function(row, col, row2, col2,scrollToSelection){
+			this._handsontable.selectCell(row, col, row2, col2,scrollToSelection);
+		},
+
+		deselectCell:function(){
+			this._handsontable.deselectCell();
+		},
+
+		getSelected:function(){
+			return this._handsontable.getSelected();
+		},
+
+		getSelectedRange:function(){
+			return this._handsontable.getSelectedRange();
+		},
+		
+		getMergeInfo:function(row,col){
+			return this._handsontable.mergeCells.mergedCellInfoCollection.getInfo(row,col);
+		},
+		
+		setMergeInfo:function(info){
+			this._handsontable.mergeCells.mergedCellInfoCollection.setInfo(info);
+		},
+		
+		removeMergeInfo:function(row,col){
+			return this._handsontable.mergeCells.mergedCellInfoCollection.removeInfo(row,col);
+		},
+
+		clear:function(){
+			this._handsontable.clear();
+		},
+
+		countRows:function(){
+			return this._handsontable.countRows();
+		},
+
+		countCols:function(){
+			return this._handsontable.countCols();
+		},
+
+		colToProp:function(column){
+			return this._handsontable.colToProp();
+		},
+
+		getRowHeader:function(row){
+			return this._handsontable.getRowHeader(row);
+		},
+
+		getColHeader:function(col){
+			return this._handsontable.getColHeader(col);
+		},
+
+		getColWidth:function(col){
+			return this._handsontable.getColWidth();
+		},
+
+		getRowHeight:function(row){
+			return this._handsontable.getRowHeight();
+		},
+
+		propToCol:function(property){
+			return this._handsontable.propToCol(property);
+		},
+		
+		addHook:function(name,func){
+			this._handsontable.addHook(name,func);
+		},
+		
+		invoke:function(methodName,args){
+			if(methodName=="render"){
+				if(args===true){
+					this._handsontable.forceFullRender=false;
+					this._handsontable.view.render();
+				}else{
+					this._handsontable.render();
+
+				}
+			}else{
+				jQuery(this._dom).handsontable(methodName,args);
+			}
+		},
+		
+		getInstance:function(){
+			return this._handsontable;
+		},
+		
+		setDirty:function(){
+			window._setDirty();
+		},
+		
+		resetState:function(){
+			window._dirty=false;
+			$("#saveButton").html("<i class='rf rf-save'></i> 保存");
+			$("#saveButton").addClass("disabled");
+			$("#saveButtonNewVersion").html("<i class='rf rf-savenewversion'></i> 保存新版本");
+			$("#saveButtonNewVersion").addClass("disabled");
+		},
+		
+		insertRow:function(index){
+			this.alter("insert_row");
+			$(".htCore > tbody > tr:eq("+this.getLastRowIndex()+")").insertAfter(".htCore > tbody > tr:eq("+index+")");
+		},
+		
+		removeRow:function(index,count){
+			for(var r=index;r<index+count;r++){
+				$(".htCore > tbody > tr:eq("+index+")").insertAfter(".htCore > tbody > tr:last");
+			}
+			this.alter("remove_row",index,count);
+		},
+		
+		insertCol:function(index){
+			this.alter("insert_col");
+			$(".htCore > tbody > tr").each(function(){
+				$(this).find("td:last").insertAfter($(this).find("td:eq("+index+")"));
+			});
+		},
+		
+		removeCol:function(index){
+			$(".htCore > tbody > tr").each(function(){
+				$(this).find("td:eq("+index+")").insertAfter($(this).find("td:last"));
+			});
+			this.alter("remove_col");
+		},
+		
+		renderSelection:function(){
+			var range=this.getSelectedRange();
+			if(range){
+				var from = range.getTopLeftCorner();
+				var to = range.getBottomRightCorner();
+				for(var row=from.row;row<=to.row;row++){
+					for(var col=from.col;col<=to.col;col++){
+						this.renderCell(row,col);
+					}
+				}
+        	}
+			
+		},
+		
+		getTableData:function(){
+			return this.decisionTable;
+		},
+		
+		getCurrentCellData:function(){
+			var highlight=this.getHighlight(),
+			row=highlight.row,
+			col=highlight.col;
+			return this.getCellData(row,col);
+		},
+		
+		getCurrentRowData:function(){
+			var highlight=this.getHighlight(),
+			row=highlight.row;
+			return this.getRowData(row);
+		},
+		
+		getCurrentColData:function(){
+			var highlight=this.getHighlight(),
+			col=highlight.col;
+			return this.getColData(col);
+		},
+		
+		getCellDatas:function(){
+			var cellMap=this.decisionTable.cellMap;
+			if(!this.decisionTable.cells&&cellMap){
+				this.decisionTable.cells=[];
+				for(var p in cellMap){
+					this.decisionTable.cells.push(cellMap[p]);
+				}
+			}
+			return this.decisionTable.cells;
+		},
+		
+		
+		getRowDatas:function(){
+			return this.decisionTable.rows||[];
+		},
+		
+		getColDatas:function(){
+			return this.decisionTable.columns||[];
+		},
+		
+		getColData:function(col){
+			var colDatas=this.getColDatas();
+			for(var i=0;i<colDatas.length;i++){
+				if(colDatas[i].num==col){
+					return colDatas[i];
+				}
+			}
+		},
+		
+		getRowData:function(row){
+			var rowDatas=this.getRowDatas();
+			for(var i=0;i<rowDatas.length;i++){
+				if(rowDatas[i].num==row){
+					return rowDatas[i];
+				}
+			}
+		},
+		
+		getCellData:function(row,col){
+			var cells=this.getCellDatas();
+			for(var i=0;i<cells.length;i++){
+				if(cells[i].row==row&&cells[i].col==col){
+					return cells[i];
+				}
+			}
+			return null;
+		},
+		
+		getCellDataByCol:function(col){
+			var cells=this.getCellDatas(),result=[];
+			for(var i=0;i<cells.length;i++){
+				if(cells[i].col==col){
+					result.push(cells[i])
+				}
+			}
+			return result;
+		},
+		
+		getCellDataByRow:function(row){
+			var cells=this.getCellDatas(),result=[];
+			for(var i=0;i<cells.length;i++){
+				if(cells[i].row==row){
+					result.push(cells[i])
+				}
+			}
+			return result;
+		},
+		
+		createCellDataRange:function(fromRow,toRow,fromCol,toCol){
+			for(var r=fromRow;r<=toRow;r++){
+				for(var c=fromCol;c<=toCol;c++){
+					this.createCellData(r,c);
+				}
+			}
+		},
+		
+		createCellData:function(row,col){
+			var cellData={
+				row:row,
+				col:col,
+				rowspan:1
+			};
+			this.getCellDatas().push(cellData);
+			return cellData;
+		},
+		
+		createCellDataByCopyNextCol:function(col){
+			var self=this,
+			cellDatas=self.getCellDataByCol(col+1);
+			$.each(cellDatas,function(index,cellData){
+				var cell=self.createCellData(cellData.row,col);
+				cell.rowspan=cellData.rowspan;
+			});
+		},
+		
+		removeCellDataRange:function(fromRow,toRow,fromCol,toCol){
+			for(var r=fromRow;r<=toRow;r++){
+				for(var c=fromCol;c<=toCol;c++){
+					this.removeCellData(r,c);
+				}
+			}
+		},
+		
+		removeCellData:function(row,col){
+			var cellDatas=this.getCellDatas();
+			var cellData=this.getCellData(row,col);
+			if(cellData){
+				var index=cellDatas.indexOf(cellData);
+				cellDatas.splice(index,1);
+			}
+		},
+		
+		createRowData:function(row){
+			var rowData={
+				num:row,
+				height:40
+			};
+			this.getRowDatas().push(rowData);
+			return rowData;
+		},
+		
+		removeRowData:function(row){
+			var rowDatas=this.getRowDatas();
+			var rowData=this.getRowData(row);
+			if(rowData){
+				var index=rowDatas.indexOf(rowData);
+				rowDatas.splice(index,1);
+			}
+		},
+		
+		removeRowDataRange:function(start,count){
+			count=count||1;
+			for(var r=start;r<start+count;r++){
+				this.removeRowData(r);
+			}
+		},
+		
+		createColData:function(col){
+			var colData={
+					num:col
+			};
+			this.getColDatas().push(colData);
+			return colData;
+		},
+		
+		removeColData:function(col){
+			var colDatas=this.getColDatas();
+			var colData=this.getColData(col);
+			if(colData){
+				var index=colDatas.indexOf(colData);
+				colDatas.splice(index,1);
+			}
+		},
+		
+		translateRow:function(start,count){
+			count=count||1;
+			if(count>0){
+				for(var r=this.getLastRowIndex();r>=start;r--){
+					this.translateRowHeader(r,count);
+				}
+				for(var r=this.getLastRowIndex();r>=start;r--){
+					for(var c=0;c<this.countCols();c++){
+						this.translateCell(r,c,count,0);
+					}
+				}
+			}else if(count<0){
+				for(var r=start;r<this.countRows();r++){
+					this.translateRowHeader(r,count);
+				}
+				for(var r=start;r<this.countRows();r++){
+					for(var c=0;c<this.countCols();c++){
+						this.translateCell(r,c,count,0);
+					}
+				}
+			}
+			
+		},
+		
+		translateCol:function(start,count){
+			count=count||1;
+			if(count>0){
+				for(var c=this.getLastColIndex();c>=start;c--){
+					this.translateColHeader(c,count);
+				}
+				for(var r=0;r<this.countRows();r++){
+					for(var c=this.getLastColIndex();c>=start;c--){
+						this.translateCell(r,c,0,count);
+					}
+				}
+			}else if(count<0){
+				for(var c=start;c<this.countCols();c++){
+					this.translateColHeader(c,count);
+				}
+				for(var r=0;r<this.countRows();r++){
+					for(var c=start;c<this.countCols();c++){
+						this.translateCell(r,c,0,count);
+					}
+				}
+			}
+		},
+		
+		translateCell:function(row,col,rowCount,colCount){
+			var cellData=this.getCellData(row,col);
+			if(cellData){
+				cellData.row=cellData.row+rowCount;
+				cellData.col=cellData.col+colCount;
+			}
+		},
+		
+		translateRowHeader:function(row,count){
+			var rowData=this.getRowData(row);
+			if(rowData){
+				rowData.num=rowData.num+count;
+			}
+		},
+		
+		translateColHeader:function(col,count){
+			var colData=this.getColData(col);
+			if(colData){
+				colData.num=colData.num+count;
+			}
+		},
+		
+		translateColHeaderRange:function(start,count){
+			count=count||1;
+			for(var c=start;c<this.countCols();c++){
+				this.translateColHeader(c,count);
+			}
+		},
+		
+		getCellContent:function(row,col){
+			var cellData;
+			if(typeof row == "object"){
+				cellData=row;	
+			}else{
+				cellData=this.getCellData(row,col);
+			}
+			if(!cellData.cellContent){
+				var colData=this.getColData(cellData.col),
+					type=colData.type,
+					container=cellData.container;
+			    if(type=="Criteria"){
+			    	cellData.cellContent=new urule.CellCondition("<div/>");
+			    	if(cellData.joint){
+			    		cellData.cellContent.initData(cellData.joint);			    		
+			    	}
+			    }else if(type=="Assignment"){
+			    	cellData.cellContent=new urule.CellContent(container);
+			    	if(cellData.value){
+			    		cellData.cellContent.initData(cellData.value);			    		
+			    	}
+			    }else if(type=="ConsolePrint"){
+			    	cellData.cellContent=new urule.CellContent(container);
+			    	if(cellData.value){
+			    		cellData.cellContent.initData(cellData.value);			    		
+			    	}
+			    }else if(type=="ExecuteMethod"){
+			    	cellData.cellContent=new urule.CellExecuteMethod(container);
+			    	if(cellData.action){
+			    		cellData.cellContent.setAction(cellData.action);
+			    	}
+			    }
+			    if(cellData.cellContent.initData){
+			    }
+			   
+			}
+			return cellData.cellContent;
+		},
+		
+		countCriteriaCols:function(){
+			var colDatas=this.getColDatas();
+			var count=0;
+			for(var i=0;i<colDatas.length;i++){
+				if(colDatas[i].type=="Criteria"){
+					++count;
+				}
+			}
+			return count;
+		},
+		
+		countActionCols:function(){
+			var colDatas=this.getColDatas();
+			var count=0;
+			for(var i=0;i<colDatas.length;i++){
+				if(colDatas[i].type!="Criteria"){
+					++count;
+				}
+			}
+			return count;
+		},
+		
+		
+		getLastRowIndex:function(){
+			return this.countRows()-1;
+		},
+		
+		getLastColIndex:function(){
+			return this.countCols()-1;
+		},
+		
+		getHighlight:function(){
+			var range= this._handsontable.getSelectedRange();
+			if(range){
+				return range.highlight;
+			}
+			return null;
+		},
+		
+		setRowHeight:function(row,height){
+			this.getInstance().manualRowHeights[row]=height;
+		},
+		
+		setColWidth:function(col,width){
+			this.getInstance().manualColumnWidths[col]=width;
+		},
+		
+		mergeRange:function(end,rowspan){
+			var cellData=this.getCurrentCellData(),
+			row=cellData.row+cellData.rowspan-1,
+			col=cellData.col;
+			rowspan=rowspan||1;
+			for(var c=0;c<=end;c++){
+				this.merge(row,c,rowspan);
+			}
+		},
+		
+		merge:function(row,col,rowspan){
+			var cellData=this.getCellData(row,col);
+			while(!cellData){
+				row--;
+				cellData=this.getCellData(row,col);
+			}
+			if(cellData.rowspan+rowspan==0){
+				this.removeCellData(row, col);
+			}else{
+				cellData.rowspan=cellData.rowspan+rowspan;
+			}
+		},
+		
+		unmerge:function(row,col){
+			var cellData=this.getCellData(row,col);
+			cellData.rowspan=1;
+		},
+		
+		renderRowRange:function(start){
+			for(r=start;r<this.countRows();r++){
+				this.renderCells(r);
+			}
+		},
+		
+		renderColRange:function(start){
+			for(c=start;c<this.countCols();c++){
+				this.renderCells(null,c);
+			}
+		},
+		
+		renderCells:function(row,col){
+			if(row&&col){
+				this.renderCell(row,col);
+			}else if(row){
+				for(var c=0;c<this.countCols();c++){
+					this.renderCell(row,c);
+				}
+			}else if(col){
+				for(var r=0;r<this.countRows();r++){
+					this.renderCell(r,col);
+				}
+			}else{
+				for(var r=0;r<this.countRows();r++){
+					for(var c=0;c<this.countCols();c++){
+						this.renderCell(r,c);
+					}
+				}
+			}
+		},
+		
+		renderCell:function(row,col){
+			var prop = this.colToProp(col),
+    	    cellProperties = this.getCellMeta(row, col),
+    	    renderer = this.getCellRenderer(cellProperties),
+    	    TD=this.getCell(row,col);
+    	    var value = this.getValue();
+    	    renderer(this._handsontable, TD, row,col, prop, value, cellProperties);
+    	    Handsontable.hooks.run(this._handsontable, 'afterRenderer', TD,row, col, prop, value, cellProperties);
+		},
+		
+		toXml:function(){
+			var decisionTable=this.getTableData(),
+				cells=decisionTable.cells||[],
+				rows=decisionTable.rows||[],
+				cols=decisionTable.columns||[],
+				libraries=[],
+				self=this,
+				xml;
+			$.each(constantLibraries,function(index,path){
+				libraries.push({
+					type:"Constant",
+					path:path
+				});
+			});
+			
+			$.each(actionLibraries,function(index,path){
+				libraries.push({
+					type:"Action",
+					path:path
+				});
+			});
+			
+			$.each(variableLibraries,function(index,path){
+				libraries.push({
+					type:"Variable",
+					path:path
+				});
+			});
+			$.each(parameterLibraries,function(index,path){
+				libraries.push({
+					type:"Parameter",
+					path:path
+				});
+			});
+			xml="<decision-table";
+			for(var i=0;i<this.properties.length;i++){
+				var prop=this.properties[i];
+				xml+=" "+prop.toXml();
+			}
+			xml+=">";
+            xml+=this.remark.toXml();
+			$.each(libraries,function(index,library){
+				var type=library.type,
+					path=library.path;
+				if(type=="Variable"){
+					xml+="<import-variable-library path=\""+path+"\"/>";
+				}else if(type=="Constant"){
+					xml+="<import-constant-library path=\""+path+"\"/>";
+				}else if(type=="Action"){
+					xml+="<import-action-library path=\""+path+"\"/>";
+				}else if(type=="Parameter"){
+					xml+="<import-parameter-library path=\""+path+"\"/>";
+				}
+			});
+			
+			$.each(cells,function(index,cell){
+				xml+="<cell row=\""+cell.row+"\" col=\""+cell.col+"\" rowspan=\""+cell.rowspan+"\">";
+				xml+=self.getCellContent(cell).toXml();
+				xml+="</cell>"
+				
+			});
+			
+			$.each(rows,function(index,row){
+				xml+="<row num=\""+row.num+"\" height=\""+row.height+"\"/>"
+			});
+			
+			$.each(cols,function(index,col){
+				var variableName=col.variableName;
+				if(variableName){
+					xml+="<col num=\""+col.num+"\" width=\""+col.width+"\" type=\""+col.type+"\" var-category=\""+(col.variableCategory=="parameter"?"参数":col.variableCategory)+"\" var-label=\""+col.variableLabel+"\" var=\""+col.variableName+"\" datatype=\""+col.datatype+"\"/>"
+				}else{
+					xml+="<col num=\""+col.num+"\" width=\""+col.width+"\" type=\""+col.type+"\"/>"	
+				}
+			});
+			
+			xml+="</decision-table>";
+			return xml;
+				
+		},
+		load:function(callback){
+			var files,version,self,url;
+			self=this;
+			files=getParameter("file");
+			version=getParameter("version");
+			url=window._server+'/common/loadXml';
+			$.ajax({
+				url,
+				async:false,
+				type:'POST',
+				data:{files},
+				error:function(req,error){
+					alert("加载文件失败!");
+				},
+				success:function(data){
+					var decisionTable=data[0];
+                    self.remark.setData(decisionTable["remark"]);
+					var salience=decisionTable["salience"];
+					if(salience){
+						self.addProperty(new urule.RuleProperty(self,"salience",salience,1));
+					}
+					var loop=decisionTable["loop"];
+					if(loop!=null){
+						self.addProperty(new urule.RuleProperty(self,"loop",loop,3));
+					}
+					var effectiveDate=decisionTable["effectiveDate"];
+					if(effectiveDate){
+						self.addProperty(new urule.RuleProperty(self,"effective-date",effectiveDate,2));
+					}
+					var expiresDate=decisionTable["expiresDate"];
+					if(expiresDate){
+						self.addProperty(new urule.RuleProperty(self,"expires-date",expiresDate,2));
+					}
+					var enabled=decisionTable["enabled"];
+					if(enabled!=null){
+						self.addProperty(new urule.RuleProperty(self,"enabled",enabled,3));
+					}
+
+					var libraries=decisionTable.libraries||[];
+					$.each(libraries,function(index,library){
+						var type,path;
+						type=library.type;
+						path=library.path;
+						switch(type){
+							case "Constant":
+								constantLibraries.push(path);
+								break;
+							case "Action":
+								actionLibraries.push(path);
+								break;
+							case "Variable":
+								variableLibraries.push(path);
+								break;
+							case "Parameter":
+								parameterLibraries.push(path);
+								break;
+						}
+					});
+					self.decisionTable=decisionTable;
+					refreshActionLibraries();
+					refreshConstantLibraries();
+					refreshVariableLibraries();
+					refreshParameterLibraries();
+					refreshFunctionLibraries();
+					if(callback){
+						callback();
+					}
+				}
+			});
+		},
+		initMenu:function(){
+			var self=this,variableLibrary=[];
+			var oldVariableLibrary=window._uruleEditorVariableLibraries || [];
+			$.each(oldVariableLibrary,function(index,lib){
+				if(lib.type!="parameter"){
+					variableLibrary.push(lib);
+				}
+			});
+			var parameter=window._uruleEditorParameterLibraries || [];
+			if(parameter.length>0){
+				$.each(parameter,function(index,p){
+					variableLibrary.push([{
+						name:"参数",
+						type:"parameter",
+						variables:parameter.length?p:[]
+					}]);
+				});
+			}
+			var onInsert=function(type,width,add){
+				var highlight=self.getHighlight(),
+					row=highlight.row,
+					col=highlight.col+(add||0);
+					self.translateCol(col);
+				var column=self.createColData(col);
+				column.type=type;
+				column.width=width||200;
+				if(type=="Criteria"){
+					self.createCellDataByCopyNextCol(col);
+				}else{
+					self.createCellDataRange(0,self.getLastRowIndex(),col,col);
+				}
+				self.insertCol(col-1);
+	            self.renderCells(null,col);
+				self.setDirty();
+				self.invoke("render");
+			};
+			
+			var onShow=function(){
+				var ationCount=self.countActionCols();
+				var criteriaCount=self.countCriteriaCols();
+				var menuItem=this.getMenuItem("delete");
+				if(!menuItem) return;
+				if(menuItem.label=="删除动作列"){
+					if(ationCount==1){
+						menuItem.hide();
+					}else{
+						menuItem.show();
+					}
+				}else{
+					if(criteriaCount==1){
+						menuItem.hide();
+					}else{
+						menuItem.show();
+					}
+
+				}
+			};
+			var menuItems=[{
+				label : "插入条件列",
+				icon:"glyphicon glyphicon-filter",
+				subMenu:{
+					menuItems:[{
+						label : "前",
+						icon:"glyphicon glyphicon-chevron-left",
+						onClick:function(){
+							onInsert("Criteria",120);
+						}
+					}, {
+						label : "后",
+						icon:"glyphicon glyphicon-chevron-right",
+						onClick:function(){
+							onInsert("Criteria",120,1);
+						}
+					}]
+				}
+			}, {
+				label : "删除条件列",
+				icon:"glyphicon glyphicon-minus-sign",
+				name:"delete",
+				onClick:function(){
+					var highlight=self.getHighlight(),
+					col=highlight.col;
+					self.removeCellDataRange(0,self.getLastRowIndex(),col,col);
+					self.removeColData(col);
+					self.translateCol(col,-1);
+					self.removeCol(col);
+					self.setDirty();
+					self.invoke("render");
+				}
+			}, {
+				label : "插入执行方法动作列",
+				icon:"glyphicon glyphicon-flash",
+				subMenu:{
+					menuItems:[{
+						label : "前",
+						icon:"glyphicon glyphicon-chevron-left",
+						onClick:function(){
+							onInsert("ExecuteMethod");
+						}
+					}, {
+						label : "后",
+						icon:"glyphicon glyphicon-chevron-right",
+						onClick:function(){
+							onInsert("ExecuteMethod",200,1);
+						}
+					}]
+				}
+			},{
+				label : "插入变量赋值动作列",
+				icon:"glyphicon glyphicon-tasks",
+				subMenu:{
+					menuItems:[{
+						label : "前",
+						icon:"glyphicon glyphicon-chevron-left",
+						onClick:function(){
+							onInsert("Assignment");
+						}
+					}, {
+						label : "后",
+						icon:"glyphicon glyphicon-chevron-right",
+						onClick:function(){
+							onInsert("Assignment",200,1);
+						}
+
+					}]
+				}
+			}, {
+				label : "插入控制台输出动作列",
+				icon:"glyphicon glyphicon-print",
+				subMenu:{
+					menuItems:[{
+						label : "前",
+						icon:"glyphicon glyphicon-chevron-left",
+						onClick:function(){
+							onInsert("ConsolePrint");
+						}
+					}, {
+						label : "后",
+						icon:"glyphicon glyphicon-chevron-right",
+						onClick:function(){
+							onInsert("ConsolePrint",200,1);
+						}
+					}]
+				}
+			}, {
+				label : "删除动作列",
+				name:"delete",
+				icon:"glyphicon glyphicon-minus-sign",
+				onClick:function(){
+					var highlight=self.getHighlight(),
+						col=highlight.col;
+					self.removeColData(col);
+					self.removeCellDataRange(0,self.getLastRowIndex(),col,col);
+					self.translateCol(col,-1);
+					self.removeCol(col);
+					self.setDirty();
+					self.invoke("render");
+				}
+			}, {
+				label : "配置条件",
+				icon:"glyphicon glyphicon-cog",
+				onClick:function(){
+					const colData=self.getCurrentColData();
+					if(!colData.variableCategory){
+						MsgBox.alert('当前条件列未定义对应的参数或变量,不能进行[配置条件]操作.');
+						return;
+					}
+					const dialogContent=$("<div/>");
+					let cellData=self.getCurrentCellData(),content=self.getCellContent(cellData);
+					content.renderTo(dialogContent);
+					const category=colData.variableCategory==="parameter"?"参数":colData.variableCategory;
+					const caption=category+"."+colData.variableLabel;
+					MsgBox.showDialog(caption,dialogContent,[],[{
+						name:'hide.bs.modal',
+						callback:function(){
+							self.renderSelection();
+						}
+					}],true);
+				}
+			}, {
+				label : "清空",
+				icon:"glyphicon glyphicon-trash",
+				name:"clean",
+				onClick:function(){
+					var cellData=self.getCurrentCellData();
+					MsgBox.confirm("确定要清空当前单元格吗?",function(){
+						self.getCellContent(cellData).clean();
+						self.renderSelection();
+					});
+				}
+			}/*, {
+				label : "复制",
+				icon:"glyphicon glyphicon-copyright-mark",
+				name:"copy",
+				onClick:function(){
+					let cellData=self.getCurrentCellData();
+					window._tableCellContent=self.getCellContent(cellData);
+				}
+			}, {
+				label : "粘贴",
+				icon:"glyphicon glyphicon-registration-mark",
+				name:"paste",
+				onClick:function(){
+					if(!window._tableCellContent){
+						MsgBox.alert("请先复制目标单元格内容!");
+						return;
+					}
+
+				}
+			}*/];
+			
+			var onClick=function(menuItem){
+				var highlight=self.getHighlight(),
+					row=highlight.row,
+					col=highlight.col,
+					parent=menuItem.parent.parent,
+					column=self.getColData(col);
+				column.variableCategory=parent.label=="参数"?"parameter":parent.label;
+				column.variableLabel=menuItem.label;
+				column.variableName=menuItem.name;
+				column.datatype=menuItem.datatype;
+				self.setDirty();
+				self.invoke("render");
+			};
+			var variabeMenuItem=[];
+			$.each(variableLibrary,function(index,categories){
+				$.each(categories,function(i,category){
+					var menuItem={
+						label:category.name=="parameter"?"参数":category.name,
+						icon:category.type=="parameter"?"glyphicon glyphicon-th-list":"glyphicon glyphicon-tasks"
+					};
+					var variables=category.variables;
+					$.each(variables||[],function(j,variable){
+						if(!menuItem.subMenu){
+							menuItem.subMenu={menuItems:[]};
+						}
+						var subMenuItem={
+							icon:"glyphicon glyphicon-tasks",
+							name:variable.name,
+							label:variable.label,
+							datatype:variable.type,
+							act:variable.act,
+							onClick:onClick
+						};
+						menuItem.subMenu.menuItems.push(subMenuItem);
+						
+					});
+					variabeMenuItem.push(menuItem);
+				});
+			});
+			
+			var criteriaConfig={
+					onShow:onShow,
+					menuItems:[]
+			};
+
+			criteriaConfig.menuItems.push(menuItems[0]);
+			criteriaConfig.menuItems.push(menuItems[1]);
+			criteriaConfig.menuItems=criteriaConfig.menuItems.concat(variabeMenuItem);
+
+			var actionConfig={
+				onShow:onShow,
+				menuItems:[]
+			};
+			
+			var assignmentConfig={
+				onShow:onShow
+			};
+
+			actionConfig.menuItems.push(menuItems[2]);
+			actionConfig.menuItems.push(menuItems[3]);
+			actionConfig.menuItems.push(menuItems[4]);
+
+			actionConfig.menuItems.push(menuItems[5]);
+
+			assignmentConfig.menuItems=actionConfig.menuItems;
+			assignmentConfig.menuItems=assignmentConfig.menuItems.concat(variabeMenuItem);
+
+			var criteriaCellConfig={
+					menuItems:[menuItems[6],menuItems[7]]
+			};
+			var actionCellConfig={
+					menuItems:[menuItems[7]]
+			};
+			if(!self.criteriaMenu){
+				self.criteriaMenu=new URule.menu.Menu(criteriaConfig);
+			}else{
+				self.criteriaMenu.setConfig(criteriaConfig);
+			}
+			if(!self.assignmentMenu){
+				self.assignmentMenu=new URule.menu.Menu(assignmentConfig);
+			}else{
+				self.assignmentMenu.setConfig(assignmentConfig);
+			}
+			if(!self.actionMenu){
+				self.actionMenu=new URule.menu.Menu(actionConfig);
+			}else{
+				self.actionMenu.setConfig(actionConfig);
+			}
+			if(!self.criteriaCellMenu){
+				self.criteriaCellMenu=new URule.menu.Menu(criteriaCellConfig);
+			}else{
+				self.criteriaCellMenu.setConfig(criteriaCellConfig);
+			}
+			if(!self.actionCellMenu){
+				self.actionCellMenu=new URule.menu.Menu(actionCellConfig);
+			}else{
+				self.actionCellMenu.setConfig(actionCellConfig);
+			}
+
+			this.container.contextmenu(function(e){
+				var parent= $(e.target);
+				for(var i=0;i<50;i++){
+					if(parent.is("th")){
+						break;
+					}
+					if(parent.is("tr")){
+						break;
+					}
+					parent=parent.parent();
+				}
+				if(parent.is("th")||parent.is("tr")){
+					var isCriteriaColHeader=parent.has("span.colHeader .glyphicon-filter").length,
+					    isAssignmentColHeader=parent.has("span.colHeader .glyphicon-tasks").length,
+					    isColHeader=parent.has("span.colHeader").length,
+					    isRowHeader=parent.has("span.rowHeader").length && $.trim(parent.has(".rowHeader").text()),
+						isCell=parent.has("td").length,
+						colData=self.getCurrentColData(),
+						highlight=self.getHighlight(),
+						col=highlight.col,
+						count=self.countCriteriaCols();
+					if(isCell && self.hasMod){
+						if(count>col && self.criteriaCellMenu.menuItems.length >0){
+							self.criteriaCellMenu.show(e);
+						}else if(self.actionCellMenu.menuItems.length >0){
+							self.actionCellMenu.show(e);
+						}
+					}else if(isCriteriaColHeader && self.criteriaMenu.menuItems.length >0){
+						self.criteriaMenu.show(e);
+					}else if(isAssignmentColHeader && self.assignmentMenu.menuItems.length >0){
+						self.assignmentMenu.show(e);
+					}else if(isColHeader && self.actionMenu.menuItems.length >0){
+						self.actionMenu.show(e);
+					}
+				}
+			});
+		}
+	};
+})(Handsontable);

+ 227 - 0
urule-console-js/src/editor/decisiontable/Join.js

@@ -0,0 +1,227 @@
+/**
+ * @author GJ
+ */
+urule.Join=function(context){
+	this.type="and";
+	this.context=context;
+	this.H=30;
+	this.W=60;
+	this.children=[];
+	this.joinContainer=$("<span class='btn btn-default dropdown-toggle'></span>");
+	this.joinContainer.css({
+		border:"solid gray 1px",
+		padding:"3px",
+		background:"#fff"
+	});
+	this.joinLabel=$("<span>并且</span>");
+	this.joinContainer.append(this.joinLabel);
+};
+urule.Join.prototype.initData=function(data){
+	var conditions=[];
+	var criterions=data["conditions"];
+	var joints=data["joints"];
+	this.setType(data["type"]);
+	if(criterions){
+		conditions=criterions;
+	}
+	if(joints){
+		conditions=conditions.concat(joints);
+	}
+	if(conditions.length==0){
+		return;
+	}
+	for(var i=0;i<conditions.length;i++){
+		var criterion=conditions[i];
+		var junctionType=criterion["type"];
+		var isJoin=false;
+		if(junctionType){
+			isJoin=true;
+		}
+		var newConnection=this.addItem(isJoin);
+		if(isJoin){
+			newConnection.getJoin().initData(criterion);			
+		}else{
+			newConnection.getCondition().initData(criterion);						
+		}
+	}
+};
+urule.Join.prototype.setType=function(type){
+	this.type=type;
+	if(type=="or"){
+		URule.setDomContent(this.joinLabel,"或者");
+	}else{
+		URule.setDomContent(this.joinLabel,"并且");
+	}
+	window._setDirty();
+};
+urule.Join.prototype.init=function(parentConnection){
+	if(parentConnection){
+		this.parentConnection=parentConnection;
+		this.parent=parentConnection.getParentJoin();		
+	}
+	var joinArrow=$(`<i class="glyphicon glyphicon-chevron-down rule-join-node"></i>`);
+	var self=this;
+	self.menu=new URule.menu.Menu({
+		menuItems:[{
+			label:"并且",
+			onClick:function(){
+				self.setType("and");
+			}
+		},{
+			label:"或者",
+			onClick:function(){
+				self.setType("or");
+			}
+		},{
+			label:"添加条件",
+			onClick:function(){
+				self.addItem(false);
+			}
+		}]
+	});
+	this.joinContainer.click(function(e){
+		self.menu.show(e);
+	});
+	this.joinContainer.append(joinArrow);
+};
+urule.Join.prototype.clean=function(){
+	while(this.children.length>0){
+		var connection=this.children[0];
+		this.removeConnection(connection);
+	}
+};
+
+urule.Join.prototype.removeConnection=function(connection){
+	var pos=this.children.indexOf(connection);
+	if(this.children.length>1){
+		this.resetItemPosition(pos+1, false);
+	}
+	connection.remove();
+	this.children.splice(pos, 1);
+	this.resetContainerSize();
+	window._setDirty();
+};
+urule.Join.prototype.addItem=function(isJoin){
+	window._setDirty();
+	var childrenCount=this.getChildrenCount();
+	if(childrenCount>0 && this.parent){
+		var parentChildren=this.parent.getChildren();
+		var pos=parentChildren.indexOf(this.parentConnection);
+		this.parent.resetItemPosition(pos+1,true);
+	}
+	var totalHeight=childrenCount*this.H;
+	var parentLeft=parseInt(this.joinContainer.css("left"));
+	var parentTop=parseInt(this.joinContainer.css("top"));
+	var startX=parentLeft+this.W/2;
+	var startY=parentTop+this.H/5;
+	var endX=startX+this.W-25;
+	var endY=startY+totalHeight;
+	if(isJoin){
+		endY-=5;
+	}
+	var connection=new urule.Connection(this.context,isJoin,this);
+	connection.drawPath(startX,startY,endX,endY);
+	this.children.push(connection);
+	this.resetContainerSize();
+	return connection;
+};
+urule.Join.prototype.toXml=function(){
+	var xml="<joint type=\""+this.type+"\">";
+	for(var i=0;i<this.children.length;i++){
+		var conn=this.children[i];
+		xml+=conn.toXml();
+	}
+	xml+="</joint>";
+	return xml;
+};
+urule.Join.prototype.resetItemPosition=function(index,add){
+	if(index==-1){
+		return;
+	}
+	for(var i=index;i<this.children.length;i++){
+		var connection=this.children[i];
+		var offset=this.H;
+		if(!add){
+			offset=-this.H;
+		}
+		connection.setEndY(connection.getEndY()+offset);
+		if(index==0){
+			connection.setStartY(connection.getStartY()+offset);
+		}
+		connection.update(add);
+	}
+	if(index>0 && this.parent){
+		var parentChildren=this.parent.getChildren();
+		var pos=parentChildren.indexOf(this.parentConnection);
+		var parentJoin=this.parentConnection.getParentJoin();
+		parentJoin.resetItemPosition(pos+1,add);
+	}
+	window._setDirty();
+};
+urule.Join.prototype.resetContainerSize=function(){
+	var container=this.context.getCanvas();
+	var height=container.css("height");
+	height=parseInt(height);
+	var childrenCount=this.context.getTotalChildrenCount();
+	if(childrenCount==0)childrenCount=1;
+	var totalHeight=childrenCount*this.H+10;
+	container.css({
+		"height":totalHeight+"px"
+	});
+};
+urule.Join.prototype.getChildrenCount=function(){
+	var total=0;
+	for(var i=0;i<this.children.length;i++){
+		var child=this.children[i].getJoin();
+		if(child){
+			var count=child.getChildrenCount();
+			if(count==0){
+				count=1;
+			}
+			total+=count;
+		}else{
+			total++;
+		}
+	}
+	return total;
+};
+urule.Join.prototype.initTopJoin=function(container){
+	var left=5;
+	var top=5;
+	this.joinContainer.css({
+		"position":"absolute",
+		"left":left,
+		"top":top
+	});
+	container.append(this.joinContainer);
+	this.context.setRootJoin(this);
+};
+urule.Join.prototype.getDisplayContainer=function(){
+	if(this.children.length==0){
+		return null;
+	}
+	var container=$("<span>");
+	for(var i=0;i<this.children.length;i++){
+		var child=this.children[i];
+		var childDisplayContainer=child.getDisplayContainer();
+		if(!childDisplayContainer){
+			continue;
+		}
+		if(i>0){
+			if(this.type=="or"){
+				container.append($("<span style='color:green'>&nbsp或&nbsp</span>"));
+			}else{
+				container.append($("<span style='color:red'>&nbsp并且&nbsp</span>"));
+			}
+		}
+		container.append(childDisplayContainer);
+	}
+	return container;
+};
+
+urule.Join.prototype.getChildren=function(){
+	return this.children;
+};
+urule.Join.prototype.getContainer=function(){
+	return this.joinContainer;
+};

+ 68 - 0
urule-console-js/src/editor/decisiontable/index.jsx

@@ -0,0 +1,68 @@
+/**
+ * Created by Jacky.gao on 2016/8/3.
+ */
+import '../../../node_modules/bootstrap/dist/css/bootstrap.css';
+import '../jquery.handsontable.full.min.css';
+import '../bootstrap-table.min.css';
+import '../context.standalone.css';
+import '../../css/iconfont.css';
+import '../urule/ruleset.css';
+import '../common/jquery.utils.js';
+import '../jquery.handsontable.full.js';
+import '../bootstrap-table.min.js';
+import '../Math.uuid.js';
+import '../../Remark.js';
+import '../common/URule.js';
+import '../common/contextMenu.js';
+import '../common/Context.js';
+import '../urule/RuleFactory.js';
+import '../common/ComparisonOperator.js';
+import '../common/ComplexArithmetic.js';
+import '../common/VariableValue.js';
+import '../common/ResourceListDialog.js';
+import '../common/ResourceVersionDialog.js';
+import '../common/ConstantValue.js';
+import '../urule/ConfigActionDialog.js';
+import '../urule/ConfigConstantDialog.js';
+import '../urule/ConfigParameterDialog.js';
+import '../urule/ConfigVariableDialog.js';
+import '../urule/ActionType.js';
+import '../urule/PrintAction.js';
+import '../urule/AssignmentAction.js';
+import '../urule/ActionType.js';
+import '../urule/SimpleArithmetic.js';
+import '../urule/RuleProperty.js';
+import './Join.js';
+import './Connection.js';
+import '../common/InputType.js';
+import '../common/NextType.js';
+import '../common/Paren.js';
+import '../common/MethodParameter.js';
+import '../common/MethodAction.js';
+import '../common/ParameterValue.js';
+import '../common/MethodValue.js';
+import '../common/FunctionProperty.js';
+import '../common/FunctionParameter.js';
+import '../common/FunctionValue.js';
+import '../common/SimpleValue.js';
+import './Condition.js';
+import './CellCondition.js';
+import './CellContent.js';
+import './CellExecuteMethod.js';
+import './renderers.js';
+import '../table/manualColumnResize.js';
+import '../table/manualRowResize.js';
+import '../urule/Rule.js';
+import './DecisionTable.js';
+
+import KnowledgeTreeDialog from '../../components/dialog/component/KnowledgeTreeDialog.jsx';
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+$(document).ready(function () {
+    ReactDOM.render(
+        <KnowledgeTreeDialog/>,
+        document.getElementById('dialogContainer')
+    );
+    new URule.DecisionTable('container');
+});

+ 56 - 0
urule-console-js/src/editor/decisiontable/renderers.js

@@ -0,0 +1,56 @@
+(function (Handsontable) {
+	'use strict';
+	var URuleRenderer = function (instance, TD, row, col, prop, value, cellProperties) {
+		Handsontable.renderers.cellDecorator.apply(this, arguments);
+	    if(!value && cellProperties.placeholder) {
+	    	value = cellProperties.placeholder;
+	    }
+	    var cellData=ht.getCellData(row,col),
+	    	content;
+	    applySpanProperties(TD,cellData);
+	    if(!cellData){
+	    	return;
+	    }
+	    cellData.container=TD;
+	    content=ht.getCellContent(cellData)
+	    if(content instanceof urule.CellCondition){
+		    $(TD).empty();
+
+	    	//$(TD).css("background-color","rgb(253, 252, 233)");
+	    	var disContainer=content.getDisplayContainer();
+	    	$(TD).append($("<div/>").append(disContainer));
+	    }else if(content instanceof urule.CellContent){
+	    	//$(TD).append(content.inputType.getContainer());
+	    }else{
+	    	//$(TD).append(content.container);
+	    }
+	};
+	
+	var applySpanProperties = function (TD, cellData) {
+		  if (cellData) {
+			  var rowspan=cellData.rowspan;
+			  TD.style.display="table-cell";
+			  if (rowspan>1) {
+			      TD.setAttribute('rowspan', rowspan);
+			  }else{
+				  TD.removeAttribute('rowspan');
+			  }
+		  }else {
+			  TD.style.display="none";
+			  TD.removeAttribute('rowspan');
+			  TD.removeAttribute('colspan');
+		  }
+	};
+	
+	Handsontable.renderers.URuleRenderer = URuleRenderer;
+	Handsontable.renderers.registerRenderer('urule', URuleRenderer);
+})(Handsontable);
+
+(function(){
+	Handsontable.URuleCell = {
+		editor:Handsontable.editors.TextEditor,
+		renderer: Handsontable.renderers.URuleRenderer
+	};
+	Handsontable.cellTypes.urule=Handsontable.URuleCell;
+})();
+  

+ 99 - 0
urule-console-js/src/editor/decisiontree/ActionTreeNode.js

@@ -0,0 +1,99 @@
+/**
+ * Created by Jacky.gao on 2016/2/24.
+ */
+ActionTreeNode=function(parentNode){
+    TreeNode.call(this,parentNode);
+    this.actionTypes=[];
+    this.initNode();
+};
+ActionTreeNode.prototype=Object.create(TreeNode.prototype);
+ActionTreeNode.prototype.constructor=ActionTreeNode;
+ActionTreeNode.prototype.initNode=function(){
+    this.nodeContainer=$("<div class='node actionNode'>");
+    this.col.append(this.nodeContainer);
+    this.actionsContainer=$("<span style='display: inline-block;'>");
+    this.nodeContainer.append(this.actionsContainer);
+    this.addAction();
+    var self=this;
+    var operations=$("<span class='operations'><i class='icon-ok-circle'></i></span>");
+    this.nodeContainer.append(operations);
+    var menuItems=[];
+    menuItems.push({
+        name:"delete",
+        label:"删除",
+        onClick:function(){
+            URule.confirm("真的要删除当前节点?",function(){
+                self.delete();
+            });
+        }
+    });
+    menuItems.push({
+        name:"addAction",
+        label:"添加动作",
+        onClick:function(){
+            self.addAction(true);
+        }
+    });
+    var menu=new URule.menu.Menu({menuItems:menuItems});
+    operations.click(function(e){
+        menu.show(e);
+    });
+};
+ActionTreeNode.prototype.addAction=function(notfirst){
+    var actionContainer=$("<span>");
+    if(notfirst){
+        actionContainer.css("display","block");
+    }
+    var delIcon=$("<i class='icon-minus-sign icon-large' style='color: #ac2925;padding-right: 5px'></i>");
+    actionContainer.append(delIcon);
+    this.actionsContainer.append(actionContainer);
+    var newActionType=new urule.ActionType(actionContainer);
+    this.actionTypes.push(newActionType);
+    actionContainer.actionType=newActionType;
+    var self=this;
+    delIcon.click(function(){
+        if(self.actionTypes.length===1){
+            URule.alert("动作至少要有一个.");
+            return;
+        }
+        var pos=-1;
+        $.each(self.actionTypes,function(i,at){
+            if(at===actionContainer.actionType){
+                pos=i;
+                return false;
+            }
+        });
+        if(pos!==-1){
+            self.actionTypes.splice(pos,1);
+            actionContainer.remove();
+        }else{
+            URule.alert("未找到要删除的动作对象.");
+        }
+    });
+    return newActionType;
+};
+ActionTreeNode.prototype.initData=function(data){
+    if(!data){
+        return;
+    }
+    var actions=data["actions"];
+    if(!actions || actions.length===0){
+        return;
+    }
+    this.actionTypes[0].parentContainer.remove();
+    this.actionTypes.splice(0,1);
+    for(var i=0;i<actions.length;i++){
+        var action=actions[i];
+        var newActionType=this.addAction(i!==0);
+        newActionType.initData(action);
+    }
+};
+
+ActionTreeNode.prototype.toXml=function(){
+    var xml="<action-tree-node>";
+    $.each(this.actionTypes,function(i,actionType){
+        xml+=actionType.toXml();
+    });
+    xml+="</action-tree-node>";
+    return xml;
+};

+ 232 - 0
urule-console-js/src/editor/decisiontree/ConditionLeft.js

@@ -0,0 +1,232 @@
+/**
+ * @author GJ
+ */
+urule.ConditionLeft=function(parentContainer){
+	this.container=$("<span>");
+	parentContainer.append(this.container);
+	this.arithmetic=new urule.SimpleArithmetic();
+	
+	this.label=generateContainer();
+	this.container.append(this.label);
+	this.label.css({
+		"color":"blue"
+	});
+	URule.setDomContent(this.label,"请选择类型");
+	this.valueContainer=$("<span>");
+	this.container.append(this.valueContainer);
+	this.initMenu();
+	
+};
+urule.ConditionLeft.prototype.initMenu=function(constantLibraries){
+	var self=this;
+	self.menu=new URule.menu.Menu({
+		menuItems:[{
+			label:"选择变量",
+			onClick:function(){
+				self.type="variable";
+				if(self.parameterValue){
+					self.parameterValue.getContainer().hide();
+				}
+				if(self.functionValue){
+					self.functionValue.getContainer().hide();
+				}
+				if(self.methodValue){
+					self.methodValue.getContainer().hide();				
+				}
+				if(self.variableValue){
+					self.variableValue.getContainer().show();
+				}else{
+					self.variableValue=new urule.VariableValue(self.arithmetic,null,"In");
+					self.valueContainer.append(self.variableValue.getContainer());				
+				}
+				self.label.css({
+					"color":"white"
+				});
+				URule.setDomContent(self.label,".");
+				window._setDirty();
+			}
+		},{
+			label:"选择参数",
+			onClick:function(){
+				self.type="parameter";
+				if(self.variableValue){
+					self.variableValue.getContainer().hide();
+				}
+				if(self.methodValue){
+					self.methodValue.getContainer().hide();				
+				}
+				if(self.functionValue){
+					self.functionValue.getContainer().hide();
+				}
+				if(self.parameterValue){
+					self.parameterValue.getContainer().show();
+				}else{
+					self.parameterValue=new urule.ParameterValue(self.arithmetic,null,"In");
+					self.valueContainer.append(self.parameterValue.getContainer());				
+				}
+				self.label.css({
+					"color":"white"
+				});
+				URule.setDomContent(self.label,".");
+				window._setDirty();
+			}
+		},{
+			label:"选择方法",
+			onClick:function(){
+				self.type="method";
+				if(self.variableValue){
+					self.variableValue.getContainer().hide();
+				}
+				if(self.parameterValue){
+					self.parameterValue.getContainer().hide();
+				}
+				if(self.functionValue){
+					self.functionValue.getContainer().hide();
+				}
+				if(self.methodValue){
+					self.methodValue.getContainer().show();				
+				}else{
+					self.methodValue=new urule.MethodValue(self.arithmetic,null);
+					self.valueContainer.append(self.methodValue.getContainer());				
+				}
+				self.label.css({
+					"color":"white"
+				});
+				URule.setDomContent(self.label,".");
+				window._setDirty();
+			}
+		},{
+			label:"选择函数",
+			onClick:function(){
+				self.type="commonfunction";
+				if(self.variableValue){
+					self.variableValue.getContainer().hide();
+				}
+				if(self.parameterValue){
+					self.parameterValue.getContainer().hide();
+				}
+				if(self.methodValue){
+					self.methodValue.getContainer().hide();				
+				}
+				if(self.functionValue){
+					self.functionValue.getContainer().show();
+				}else{
+					self.functionValue=new urule.FunctionValue(self.arithmetic,null,"In");
+					self.valueContainer.append(self.functionValue.getContainer());				
+				}
+				self.label.css({
+					"color":"white"
+				});
+				URule.setDomContent(self.label,".");
+				window._setDirty();
+			}
+		}]
+	});
+	this.label.click(function(e){
+		self.menu.show(e);
+	});
+	
+};
+urule.ConditionLeft.prototype.initData=function(leftData){
+	if(!leftData){
+		return;
+	}
+	this.label.css({
+		"color":"white",
+	});
+	URule.setDomContent(this.label,".");
+	var leftPart=leftData["leftPart"];
+	leftPart.arithmetic=leftData["arithmetic"];
+	this.type=leftData["type"];
+	if(!this.type){
+		this.type="variable";
+	}
+	if(this.type=="parameter"){
+		if(this.variableValue){
+			this.variableValue.getContainer().hide();
+		}
+		if(this.methodValue){
+			this.methodValue.getContainer().hide();
+		}
+		if(this.functionValue){
+			this.functionValue.getContainer().hide();
+		}
+		this.parameterValue=new urule.ParameterValue(this.arithmetic,leftPart,"In");
+		this.valueContainer.append(this.parameterValue.getContainer());						
+	}else if(this.type=="variable"){
+		if(this.parameterValue){
+			this.parameterValue.getContainer().hide();
+		}
+		if(this.methodValue){
+			this.methodValue.getContainer().hide();
+		}
+		if(this.functionValue){
+			this.functionValue.getContainer().hide();
+		}
+		this.variableValue=new urule.VariableValue(this.arithmetic,leftPart,"In");
+		this.valueContainer.append(this.variableValue.getContainer());			
+	}else if(this.type=="method"){
+		if(this.parameterValue){
+			this.parameterValue.getContainer().hide();
+		}
+		if(this.variableValue){
+			this.variableValue.getContainer().hide();
+		}
+		if(this.functionValue){
+			this.functionValue.getContainer().hide();
+		}
+		this.methodValue=new urule.MethodValue(this.arithmetic,leftPart);
+		this.valueContainer.append(this.methodValue.getContainer());			
+		
+	}else if(this.type=="commonfunction"){
+		if(this.parameterValue){
+			this.parameterValue.getContainer().hide();
+		}
+		if(this.variableValue){
+			this.variableValue.getContainer().hide();
+		}
+		if(this.methodValue){
+			this.methodValue.getContainer().hide();
+		}
+		this.functionValue=new urule.FunctionValue(this.arithmetic,leftPart);
+		this.valueContainer.append(this.functionValue.getContainer());
+	}
+};
+urule.ConditionLeft.prototype.toXml=function(){
+	var xml="";
+	xml+="<left ";
+	if(this.type=="variable"){
+		xml+=this.variableValue.toXml();
+	}else if(this.type=="parameter"){
+		xml+=this.parameterValue.toXml();		
+	}else if(this.type=="method"){
+		xml+=this.methodValue.toXml();				
+	}else if(this.type=="commonfunction"){
+		xml+=this.functionValue.toXml();
+	}
+	xml+=" type=\""+this.type+"\">";
+	if(this.type=="method"){
+		var parameters=this.methodValue.action.parameters;
+		for(var i=0;i<parameters.length;i++){
+			var p=parameters[i];
+			xml+=p.toXml();
+		}
+	}else if(this.type=="commonfunction"){
+		xml+=this.functionValue.getParameter().toXml();
+	}
+	xml+=this.arithmetic.toXml();
+	xml+="</left>";
+	if(this.inputType){
+		xml+=this.inputType.toXml();
+	}
+	return xml;
+};
+urule.ConditionLeft.prototype.getVariableValue=function(){
+	return this.variableValue;
+};
+urule.ConditionLeft.prototype.getOperator=function(){
+	return this.operator;
+};
+urule.ConditionLeft.prototype.getInputType=function(){
+	return this.inputType;
+};

+ 92 - 0
urule-console-js/src/editor/decisiontree/ConditionTreeNode.js

@@ -0,0 +1,92 @@
+/**
+ * Created by Jacky.gao on 2016/2/24.
+ */
+ConditionTreeNode=function(parentNode){
+    TreeNode.call(this,parentNode);
+    this.initNode();
+};
+ConditionTreeNode.prototype=Object.create(TreeNode.prototype);
+ConditionTreeNode.prototype.constructor=ConditionTreeNode;
+ConditionTreeNode.prototype.initNode=function(){
+    var nodeContainer=$("<div class='node conditionNode'>");
+    this.col.append(nodeContainer);
+    var self=this;
+    var contentContainer=$("<span>");
+    this.contentContainer=contentContainer;
+    nodeContainer.append(contentContainer);
+    this.operator=new urule.ComparisonOperator(function(){
+        self.inputType=self.operator.getInputType();
+        if(self.inputType){
+            contentContainer.append(self.inputType.getContainer());
+        }
+    });
+    contentContainer.append(this.operator.getContainer());
+
+    var operations=$("<span class='operations'><i class='icon-ok-circle'></i></span>");
+    nodeContainer.append(operations);
+    var menuItems=[];
+    menuItems.push({
+        name:"addCondition",
+        label:"添加条件",
+        onClick:function(){
+            self.addChild("condition");
+        }
+    });
+    menuItems.push({
+        name:"addVariable",
+        label:"添加变量",
+        onClick:function(){
+            self.addChild("variable");
+        }
+    });
+    menuItems.push({
+        name:"addAction",
+        label:"添加动作",
+        onClick:function(){
+            self.addChild("action");
+        }
+    });
+    menuItems.push({
+        name:"delete",
+        label:"删除",
+        onClick:function(){
+            URule.confirm("真的要删除当前节点?",function(){
+                self.delete();
+            });
+        }
+    });
+    var menu=new URule.menu.Menu({menuItems:menuItems});
+    operations.click(function(e){
+        menu.show(e);
+    });
+};
+
+ConditionTreeNode.prototype.initData=function(data){
+    if(!data){
+        return;
+    }
+    var op=data["op"];
+    this.operator.setOperator(op);
+    var value=data["value"];
+    this.operator.initRightValue(value);
+    this.inputType=this.operator.getInputType();
+    if(this.inputType){
+        this.contentContainer.append(this.inputType.getContainer());
+    }
+    TreeNode.prototype.initChildrenNodeData.call(this,data);
+};
+
+ConditionTreeNode.prototype.toXml=function(){
+    if(this.childrenNodes.length==0){
+        throw "条件节点下至少要有一个动作节点.";
+    }
+    var xml="<condition-tree-node op=\""+this.operator.getOperator()+"\">";
+    if(this.inputType){
+        xml+=this.inputType.toXml();
+    }
+    $.each(this.childrenNodes,function(i,childNode){
+        xml+=childNode.toXml();
+    });
+    xml+="</condition-tree-node>";
+    return xml;
+};

+ 222 - 0
urule-console-js/src/editor/decisiontree/DecisionTree.js

@@ -0,0 +1,222 @@
+/**
+ * Created by Jacky.gao on 2016/2/22.
+ */
+DecisionTree=function(container){
+    this.container=container;
+    this.topNode=new VariableTreeNode();
+    this.initToolbar();
+    var content=$("<div style='text-align: center'>");
+    container.append(content);
+    content.append(this.topNode.container);
+};
+
+DecisionTree.prototype.initToolbar=function(){
+    var file=_getRequestParameter("file");
+    var version=_getRequestParameter("version")||"";
+    if(!file || file.length<1){
+        URule.alert("未指定具体的决策树文件!");
+        return;
+    }
+
+    var saveButton = '<div class="btn-group navbar-btn" style="margin-top:0px;margin-bottom: 0px" role="group" aria-label="...">'+
+        '<button id="saveButton" type="button" class="btn btn-default navbar-btn" ><i class="icon-save"></i> 保存</button>' +
+        '<button id="saveButtonNewVersion" type="button" class="btn btn-default navbar-btn" ><i class="icon-save"></i> 保存新版本</button>' +
+        '</div>';
+    var toolbarHtml='<nav class="navbar navbar-default" style="margin: 5px">'+
+        '<div>'+
+        '<div>'+
+        '<div class="btn-group navbar-btn" style="margin-left:5px;margin-top:0px;margin-bottom: 0px" role="group" aria-label="...">'+
+        '<button id="configVarButton" type="button" class="btn btn-default"><i class="icon-tasks"></i> 导入变量库</button>'+
+        '<button id="configConstantsButton" type="button" class="btn btn-default"><i class="icon-th-list"></i> 导入常量库</button>'+
+        '<button id="configActionButton" type="button" class="btn btn-default"><i class="icon-bolt"></i> 导入动作库</button>'+
+        '<button id="configParameterButton" type="button" class="btn btn-default"><i class="icon-th"></i> 导入参数库</button>'+
+        '</div>'+
+        saveButton +
+        ' </div>'+
+        '</div>'+
+        '</nav>';
+    var toolbar=$(toolbarHtml);
+    toolbar.css({
+        diaplay:"inline-block"
+    });
+    var toolbarContainer=$("<div>");
+    toolbarContainer.append(toolbar);
+    this.container.append(toolbarContainer);
+    var self=this;
+    $("#configVarButton").click(function(){
+        if(!self.configVarDialog){
+            self.configVarDialog=new urule.ConfigVariableDialog(self);
+        }
+        self.configVarDialog.open();
+    });
+
+    $("#configConstantsButton").click(function(){
+        if(!self.configConstantDialog){
+            self.configConstantDialog=new urule.ConfigConstantDialog(self);
+        }
+        self.configConstantDialog.open();
+    });
+
+    $("#configActionButton").click(function(){
+        if(!self.configActionDialog){
+            self.configActionDialog=new urule.ConfigActionDialog(self);
+        }
+        self.configActionDialog.open();
+    });
+
+    $("#configParameterButton").click(function(){
+        if(!self.configParameterDialog){
+            self.configParameterDialog=new urule.ConfigParameterDialog(self);
+        }
+        self.configParameterDialog.open();
+    });
+
+    $("#saveButton").click(function(){
+        _save(false);
+    });
+    $("#saveButtonNewVersion").click(function(){
+        _save(true);
+    });
+    $("#saveButton").addClass("disabled");
+    $("#saveButtonNewVersion").addClass("disabled");
+    _loadDecisionTreeFileData();
+
+    function _save(newVersion){
+        if($("#saveButton").hasClass("disabled")){
+            return false;
+        }
+        var xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+        xml+="<decision-tree>";
+        $.each(parameterLibraries,function(index,item){
+            xml+="<import-parameter-library path=\""+item+"\"/>";
+        });
+        $.each(variableLibraries,function(index,item){
+            xml+="<import-variable-library path=\""+item+"\"/>";
+        });
+        $.each(constantLibraries,function(index,item){
+            xml+="<import-constant-library path=\""+item+"\"/>";
+        });
+        $.each(actionLibraries,function(index,item){
+            xml+="<import-action-library path=\""+item+"\"/>";
+        });
+        try{
+            xml+=self.topNode.toXml();
+        }catch(error){
+            URule.alert(error);
+            return;
+        }
+        xml+="</decision-tree>";
+        var url=(uruleServer || "" )+"urule?action=savexml&file="+file+"";
+        var dialog=$("<div style='width:100px;height:50px'>文件保存中...</div>");
+        $.ajax({
+            cache:false,
+            url:url,
+            type:"POST",
+            data:{xml:xml,newVersion:newVersion},
+            beforeSend:function(req){
+                dialog.dialog({
+                    modal : true,
+                    height:80,
+                    width:50,
+                    open : function(event, ui) {
+                        $(".ui-dialog-titlebar",$(this).parent()).hide();
+                    }
+                });
+            },
+            error:function(req,error){
+                dialog.dialog("close");
+                URule.alert("保存失败!");
+            },
+            success:function(data){
+                cancelDirty();
+                dialog.dialog("close");
+            }
+        });
+    };
+
+    function _loadDecisionTreeFileData(){
+        var url=(uruleServer || "")+"urule?action=loadxml&files="+file+","+version+"";
+        $.ajax({
+            cache:false,
+            dataType:"json",
+            type:'POST',
+            url:url,
+            error:function(req,error){
+                URule.alert("加载文件失败!");
+            },
+            success:function(data){
+                var treeData=data[0];
+                var libraries=treeData["libraries"];
+                if(libraries){
+                    for(var i=0;i<libraries.length;i++){
+                        var lib=libraries[i];
+                        var type=lib["type"];
+                        var path=lib["path"];
+                        switch(type){
+                            case "Constant":
+                                constantLibraries.push(path);
+                                break;
+                            case "Action":
+                                actionLibraries.push(path);
+                                break;
+                            case "Variable":
+                                variableLibraries.push(path);
+                                break;
+                            case "Parameter":
+                                parameterLibraries.push(path);
+                                break;
+                        }
+                    }
+                }
+                refreshActionLibraries();
+                refreshConstantLibraries();
+                refreshVariableLibraries();
+                refreshParameterLibraries();
+                refreshFunctionLibraries();
+                self.topNode.initData(treeData["variableTreeNode"]);
+                cancelDirty();
+            }
+        });
+    };
+
+};
+
+function _getRequestParameter(name){
+    var value=null;
+    var params=window.location.search.substring(1).split("&");
+    for(var i=0;i<params.length;i++){
+        var param=params[i];
+        if(param.indexOf("=")==-1){
+            continue;
+        }
+        var pair=param.split("=");
+        var key=pair[0];
+        if(key==name){
+            value=pair[1];
+            break;
+        }
+    }
+    return value;
+};
+
+window._setDirty=function(){
+    if(window._dirty){
+        return;
+    }
+    window._dirty=true;
+    $("#saveButton").html("<i class='icon-save'></i> *保存");
+    $("#saveButton").removeClass("disabled");
+    $("#saveButtonNewVersion").html("<i class='icon-save'></i> *保存新版本");
+    $("#saveButtonNewVersion").removeClass("disabled");
+};
+
+function cancelDirty(){
+    if(!window._dirty){
+        return;
+    }
+    window._dirty=false;
+    $("#saveButton").html("<i class='icon-save'></i> 保存");
+    $("#saveButton").addClass("disabled");
+    $("#saveButtonNewVersion").html("<i class='icon-save'></i> 保存新版本");
+    $("#saveButtonNewVersion").addClass("disabled");
+};

+ 132 - 0
urule-console-js/src/editor/decisiontree/TreeNode.js

@@ -0,0 +1,132 @@
+/**
+ * Created by Jacky.gao on 2016/2/22.
+ */
+TreeNode=function(parentNode){
+    this.parentNode=parentNode;
+    this.container=$("<table class='nodeTable' border='0' cellpadding='0' cellspacing='0'>");
+    var row=$("<tr>");
+    this.col=$("<td align='center'>");
+    row.append(this.col);
+    this.container.append(row);
+    this.nextColCount=0;
+    this.lineCols=[];
+    this.childrenNodes=[];
+};
+
+TreeNode.prototype.delete=function(){
+    if(!this.parentNode){
+        return;
+    }
+    _setDirty();
+    var pos;
+    var parentChildrenNodes=this.parentNode.childrenNodes;
+    for(var i= 0;i<parentChildrenNodes.length;i++){
+        if(parentChildrenNodes[i]===this){
+            pos=i;
+            break;
+        }
+    }
+    parentChildrenNodes.splice(pos,1);
+    this.parentNode.nextColCount-=2;
+    this.parentNode.col.prop("colspan",this.parentNode.nextColCount);
+    this.parentNode.lineCol.prop("colspan",this.parentNode.nextColCount);
+    if(parentChildrenNodes.length>0){
+        this.parentCol.remove();
+        var parentLineCols=this.parentNode.lineCols;
+        var pos;
+        for(var j=0;j<this.newLineCols.length;j++){
+            for(var i=0;i<parentLineCols.length;i++){
+                if(parentLineCols[i]===this.newLineCols[j]){
+                    pos=i;
+                    break;
+                }
+            }
+            parentLineCols.splice(pos,1);
+        }
+        if(parentLineCols.length>1){
+            parentLineCols[0].css("border-top-style","none");
+            parentLineCols[parentLineCols.length-1].css("border-top-style","none");
+        }
+        this.newLineCols[0].remove();
+        this.newLineCols[1].remove();
+    }else{
+        this.parentNode.lineRow.remove();
+        this.parentNode.nextRow.remove();
+        this.parentNode.nextLineRow.remove();
+        this.parentNode.nextRow=null;
+        this.parentNode.lineCols.splice(0,this.parentNode.lineCols.length);
+    }
+};
+
+TreeNode.prototype.addChild=function(type){
+    _setDirty();
+    if(!this.nextRow){
+        this.nextRow=$("<tr style='min-height:40px'>");
+        this.nextLineRow=$("<tr style='height:20px;'>");
+        this.lineRow=$("<tr style='height:20px;'>");
+        this.lineCol=$("<td><div class='vertical-line'></div></td>");
+        this.lineRow.append(this.lineCol);
+        this.container.append(this.lineRow);
+        this.container.append(this.nextLineRow);
+        this.container.append(this.nextRow);
+    }
+    var newLineCols=this.newLine();
+
+    var newCol=$("<td class='nodeContainer' align='center' colspan='2'>");
+    var childNode;
+    if(type==="condition"){
+        childNode=new ConditionTreeNode(this);
+    }else if(type==="action"){
+        childNode=new ActionTreeNode(this);
+    }else if(type==="variable"){
+        childNode=new VariableTreeNode(this,true);
+    }
+    childNode.newLineCols=newLineCols;
+    childNode.parentCol=newCol;
+    this.childrenNodes.push(childNode);
+    newCol.append(childNode.container);
+    this.nextColCount+=2;
+    this.nextRow.append(newCol);
+    if(this.nextColCount>1){
+        this.col.prop("colspan",this.nextColCount);
+        this.lineCol.prop("colspan",this.nextColCount);
+    }
+    return childNode;
+};
+TreeNode.prototype.initChildrenNodeData=function(data){
+    var childrenNodes=[];
+    var conditionTreeNodes=data["conditionTreeNodes"];
+    if(conditionTreeNodes){
+        childrenNodes=conditionTreeNodes;
+    }
+    var variableTreeNodes=data["variableTreeNodes"];
+    if(variableTreeNodes){
+        childrenNodes=childrenNodes.concat(variableTreeNodes);
+    }
+    var actionTreeNodes=data["actionTreeNodes"];
+    if(actionTreeNodes){
+        childrenNodes=childrenNodes.concat(actionTreeNodes);
+    }
+    if(!childrenNodes || childrenNodes.length===0){
+        return;
+    }
+    for(var i=0;i<childrenNodes.length;i++){
+        var childNodeData=childrenNodes[i];
+        var newNode=this.addChild(childNodeData["nodeType"]);
+        newNode.initData(childNodeData);
+    }
+};
+TreeNode.prototype.newLine=function(){
+    var lineCol1=$("<td style='border-right:solid #4cae4c 2px;'></td>");
+    this.nextLineRow.append(lineCol1);
+    var lineCol2=$("<td style='border-left:solid #4cae4c 2px;'></td>");
+    this.nextLineRow.append(lineCol2);
+    if(this.lineCols.length>0){
+        var lastLineCol=this.lineCols[this.lineCols.length-1];
+        lastLineCol.css("border-top","solid #4cae4c 2px");
+        lineCol1.css("border-top","solid #4cae4c 2px");
+    }
+    this.lineCols.push(lineCol1);
+    this.lineCols.push(lineCol2);
+    return [lineCol1,lineCol2];
+};

+ 63 - 0
urule-console-js/src/editor/decisiontree/VariableTreeNode.js

@@ -0,0 +1,63 @@
+/**
+ * Created by Jacky.gao on 2016/2/24.
+ */
+VariableTreeNode=function(parentNode,allowDelete){
+    this.allowDelete=allowDelete;
+    TreeNode.call(this,parentNode);
+    this.initNode();
+};
+VariableTreeNode.prototype=Object.create(TreeNode.prototype);
+VariableTreeNode.prototype.constructor=VariableTreeNode;
+VariableTreeNode.prototype.initNode=function(){
+    var nodeContainer=$("<div class='node varNode'>");
+    this.col.append(nodeContainer);
+    var contentContainer=$("<span>");
+    nodeContainer.append(contentContainer);
+    this.condition=new urule.ConditionLeft(contentContainer);
+    var self=this;
+    var operations=$("<span class='operations'><i class='icon-ok-circle'></i></span>");
+    nodeContainer.append(operations);
+    var menuItems=[];
+    menuItems.push({
+        name:"addCondition",
+        label:"添加条件",
+        onClick:function(){
+            self.addChild("condition");
+        }
+    });
+    if(this.allowDelete){
+        menuItems.push({
+            name:"delete",
+            label:"删除",
+            onClick:function(){
+                URule.confirm("真的要删除当前节点?",function(){
+                    self.delete();
+                });
+            }
+        });
+    }
+    var menu=new URule.menu.Menu({menuItems:menuItems});
+    operations.click(function(e){
+        menu.show(e);
+    });
+};
+VariableTreeNode.prototype.initData=function(data){
+    if(!data){
+        return;
+    }
+    var left=data["left"];
+    this.condition.initData(left);
+    TreeNode.prototype.initChildrenNodeData.call(this,data);
+};
+VariableTreeNode.prototype.toXml=function(){
+    if(this.childrenNodes.length==0){
+        throw "变量节点下至少要有一个条件节点.";
+    }
+    var xml="<variable-tree-node>";
+    xml+=this.condition.toXml();
+    $.each(this.childrenNodes,function(i,childNode){
+        xml+=childNode.toXml();
+    });
+    xml+="</variable-tree-node>";
+    return xml;
+};

Неке датотеке нису приказане због велике количине промена