LogicNode = Object.subClass({ init: function(config) { this.level = config.level || 0; this.type = config.type; this.expression = config.expression; this.condition = config.condition; this.valuation = config.valuation; this.parent = config.parent; this.children = []; }, toString: function(content) { var result = ""; if (!this.condition) { this.expression = this.valuation.expression = this.valuation.editor.val(); } else { this.condition.expression = this.condition.editor.val(); this.valuation.expression = this.valuation.editor.val(); this.expression = this.condition.expression + "?" + this.valuation.expression; } result = result + this.expression; var flag = this.level == 0 ? ";" : ","; var max = this.children.length; for (var i = 0; i < max; i++) { var child = this.children[i]; result = result + flag + child.toString(); } }, show: function() { if (!this.condition) { this.valuation.editor.val(this.valuation.expression); } else { this.condition.editor.val(this.condition.expression); this.valuation.editor.val(this.valuation.expression); } }, clear: function() { if (this.condition && this.condition.el) { this.condition.el.remove(); } if (this.valuation && this.valuation.el) { this.valuation.el.remove(); } if (this.el) { this.el.remove(); } } }); LogicTree = Object.subClass({ init: function(config) { this.type; this.condition = null; this.valuation = null; this.eidtor = null; this.children = []; }, parse: function(content, flag) { this.clear(); //1. if empty add default line if (!content) { this.parseMathValue(this, content, 0); return; } flag = flag || ";"; //2. math if (content.indexOf(";") < 0) { this.parseMathValue(this, content, 0); return; } //3. logic var sequences = content.split(";"); var max = sequences.length; for (var i = 0; i < max; i++) { sequence = sequences[i]; var node = this.parseIFValue(this, sequence, 0); } }, parseMathValue: function(parent, value, level) { var node = new LogicNode({ parent: parent, level: level, type: "Math", valuation: { expression: value, el: null } }); parent.children.push(node); return node; }, parseIFValue: function(parent, content, level) { var pos = content.indexOf("?"); //1. else if if (pos < 0) { var node = new LogicNode({ parent: parent, level: level, type: "ELSE", expression: content, valuation: { expression: content, el: null } }); parent.children.push(node); return; } //2. if condition var node = new LogicNode({ parent: parent, level: level, type: "IF", condition: { expression: content.substring(0, pos), el: null } }); node.expression = content; parent.children.push(node); //3. if valuation var pos_subflag = content.indexOf(","); if (pos_subflag < 0) { var math = content.substring(pos + 1); node.valuation = { expression: math, el: null } return; } //4. children this.parse(node, ",", 1); }, gotoEmptyMathLogic: function() { if ("Math" == this.type) { return; } this.clear(); this.parseMathValue(this, null, 0); }, gotoEmptyIFLogic: function() { if ("IF" == this.type) { return; } this.clear(); //1. if var node = new LogicNode({ parent: this, level: 0, type: "IF", expression: null, condition: { expression: null, el: null }, valuation: { expression: null, el: null } }); this.children.push(node); //2. else node = new LogicNode({ parent: this, level: 0, type: "ELSE", expression: null, valuation: { expression: null, el: null } }); this.children.push(node); }, getString: function() { var result = ""; var max = this.children.length; for (var i = 0; i < max; i++) { var child = this.children[i]; result = result + child.toString(); } return result; }, show: function() { var max = this.children.length; for (var i = 0; i < max; i++) { var child = this.children[i]; child.show(); } }, clear: function() { var max = this.children.length; for (var i = 0; i < max; i++) { var child = this.children[i]; child.clear(); } this.children = []; } }); FormulaDesigner = Control.subClass({ items: true, init: function(config) { config.css = util.combine(config.css, { container: null }); this.columnSetting = this.createSetting(); this.logicTree = new LogicTree({}); this.mathLine = {}; this.selected = null; this.Code = { BackSpace: 8, Delete: 46, Enter: 13, TrigerBegin: 219, TrigerEnd: 221 }; this.tipList = { }; Control.call(this, config); }, getContainerTemplate: function() { return $(Formula_Template.container.join("")); }, createContent: function() { this.logicTree.el = this.container; //1. body this.logicTree.el.body = $("#formula_body", this.container); //2. header this.header = $("#formula_header", this.container); var me = this; //2.1 header - name var codeEditor = this.header.codeEditor = $("#editor_code", this.header); codeEditor.change(function() { me.onChangeVariantCode.call(me) }); var nameEditor = this.header.nameEditor = $("#editor_name", this.header); nameEditor.change(function() { me.onChangeVariantName.call(me) }); nameEditor.focus(function () { nameEditor.preValue = nameEditor.val(); }) //2.2 header - align var alignEditor = this.header.alignEditor = $("#editor_align", this.header); alignEditor.change(function() { me.onChangeLayoutSetting() }); //2.3 header - formatter var formatterEditor = this.header.formatterEditor = $("#editor_formatter", this.header); formatterEditor.change(function() { me.onChangeLayoutSetting() }); //2.4 header - width var widthEditor = this.header.widthEditor = $("#width_editor", this.header); widthEditor.change(function() { me.onChangeLayoutSetting() }); var typeEditor = this.header.typeEditor = $("#width_editor", this.header); typeEditor.change(function() { me.onChangeLayoutSetting() }); }, createLines: function() { var lines = this.logicTree.children; var max = lines.length; for (var i = 0; i < max; i++) { this.createOneLine(lines[i]); } }, createOneLine: function(logic) { if ("Math" == logic.type) { this.createMathLine(logic); } else if ("IF" == logic.type) { this.createIFLine(logic); } else if ("ELSE" == logic.type) { this.createElseLine(logic); } }, createMathLine: function(logic) { //1. element var el = logic.valuation.el = $(Formula_Template.mathLine.join("")); var editor = logic.valuation.editor = $("#mathEditor", el); // editor.prop({ disabled: true }); //2. this.linkDropdown(editor); //3. change event var me = this; editor.change(function() { me.onExpressionChange.call(me); }); //3. append to parent logic.parent.el.body.append(el); return result; }, createIFLogic: function(parentLogic) { //1. clear math line if (this.mathLine.el) { this.mathLine.el.remove(); this.mathLine = {}; } //2. login tree this.createIFLine(parentLogic); this.createElseLine(parentLogic); }, createIFLine: function(logic) { this.createLogicLine(logic, "IF"); }, createElseIFLine: function(logic, priorLogic) { this.createLogicLine(logic, "ELSE-IF", priorLogic); }, createElseLine: function(logic) { this.createLogicLine(logic, "ELSE"); }, createLogicLine: function(logic, type, priorLogic) { //1. create element var el; var existsButtons; if ("IF" == type || "ELSE-IF" == type) { el = $(Formula_Template.logicIF.join("")); logic.condition = {}; logic.valuation = {}; logic.condition.editor = $("#conditionEditor", el); logic.valuation.editor = $("#valuationEditor", el); existsButtons = true; } else { el = logic.valuation.el = $(Formula_Template.logicELSE.join("")); logic.valuation.editor = $("#valuationEditor", el); existsButtons = false; } logic.el = el; el.body = $("#logic_body", el); //2. change event var me = this; if (logic.condition && logic.condition.editor) { var editor = logic.condition.editor; this.linkDropdown(editor); editor.change(function() { me.onExpressionChange.call(me); }); } if (logic.valuation && logic.valuation.editor) { var editor = logic.valuation.editor; this.linkDropdown(editor); editor.change(function() { me.onExpressionChange.call(me); }); } //3. caption var caption = $("#conditionCaption", el); caption.html(this.getLogicLabel(type)); //4. event if (existsButtons) { var me = this; el.bntAddOne = $("#btn_addOne", el); el.bntAddOne.click(function() { me.createElseIFLine(parentLogic, result); }); el.bntAddChild = $("#btn_addChild", el); el.bntAddChild.click(function() { me.createIFLogic(result); }); el.bntDeleteOne = $("#btn_deleteOne", el); el.bntDeleteOne.click(function() { me.deleteLogic(result); }); } //5. append to parent var parentLogic = logic.parent; if (priorLogic) { result.el.insertBefore(priorLogic.el); } else { var parentEl = parentLogic.el.body; parentEl.append(el); } //6. animate return result; }, deleteLogic: function(logic) { var parent = logic.parent; var logicList = parent.children; //1. 只有if和else if (logicList.length <= 2) { this.initLogicBody(parent); } else { //1. delete from parent var index = logicList.indexOf(logic); logicList.splice(index, 1); //2. delete el logic.el.remove(); } }, initLogicBody: function(logic) { //1. empty parent children logic.children = []; //2. empty body logic.el.body.empty(); //3. append valuation var valuation = $(Formula_Template.valuation.join("")); logic.el.body.append(valuation); }, removeLogicValuation: function(logic) { if (!logic.el.valuation) { return; } logic.el.valuation.remove(); logic.el.valuation = null; }, getExpressionString: function() { if (logicLine) { var result = logicLine.expression.getString(); return result; } if (this.activeEditor) { return this.activeEditor.val(); } var result = ""; var logicTree = this.logicTree; var max = logicTree.children.length; for (var i = 0; i < max; i++) { var logicLine = logicTree.children[i]; result = result + logicLine.expression.getString(); } return result; }, gotoMathLogic: function() { this.logicTree.gotoEmptyMathLogic(); this.createLines(); }, gotoIFLogic: function() { this.logicTree.gotoEmptyIFLogic(); this.createLines(); }, getLogicLabel: function(type) { if ("IF" == type) { return "如果:"; } else if ("ELSE-IF" == type) { return "如果:"; } else if ("ELSE" == type) { return "否则:"; } }, clear: function() { //1. math if (this.mathLine.el) { this.mathLine.el.remove(); } this.mathLine = {}; //2. var children = this.logicTree.children; var max = children.length; for (var i = 0; i < max; i++) { var logic = children[i]; logic.el.remove(); } this.logicTree.children = []; }, createSetting: function() { var me = this; var result = new ColumnSetting({ owner: "formula", getActiveColumn: function() { return me.selected; }, onSetCode: function(column, code) { me.header.codeEditor.val(code); }, onSetName: function(column, name) { me.header.nameEditor.val(name); }, onSetExpression: function(column, expression) { me.logicTree.parse(expression); me.createLines(); me.logicTree.show(); }, onSetFormatter: function(column, formatter) { var filter = "[value='" + formatter + "']"; var option = $(filter, me.header.formatterEditor); if (option) { option.attr("selected", "selected"); } }, onSetAlign: function(column, align) { var filter = "[value='" + align + "']"; var option = $(filter, me.header.alignEditor); if (option) { option.attr("selected", "selected"); } }, onSetWidth: function(column, width) { var filter = "[value='" + width + "']"; var option = $(filter, me.header.widthEditor); if (option) { option.attr("selected", "selected"); } }, onBeginSetting: function() { me.loading = true; if (this.expressionChanged) { me.onSaveExpression(); } this.expressionChanged = false; }, onEndSetting: function() { me.loading = false; } }); return result; }, setColumnSetting: function(sender, column, option) { this.selected = column; this.columnSetting.set(sender, column, option); }, onChangeVariantCode: function() { if (this.loading) { return; } if (this.config.settingListener) { var value = this.header.codeEditor.val(); this.config.settingListener.setColumnSetting(this, null, { code: value }); } }, onChangeVariantName: function() { if (this.loading) { return; } let editor = this.activeEditor; if (this.config.settingListener) { var value = this.header.nameEditor.val(); let afterTip = { name: value, text:"[" + value + "]" }; if( this.header.nameEditor.preValue) { let preTip = { name: this.header.nameEditor.preValue, text:"[" + this.header.nameEditor.preValue + "]" }; this.updateTip(preTip, afterTip); }else { this.addOneTip(afterTip); } this.config.settingListener.setColumnSetting(this, null, { name: value }); } }, onExpressionChange: function() { this.columnSetting.expressionChanged = true; }, onSaveExpression: function() { if (this.loading) { return; } if (this.config.settingListener) { var value = this.getExpressionString(); // value = value.replace("+", "~"); this.config.settingListener.setColumnSetting(this, null, { expression: value }); } }, onChangeLayoutSetting: function() { if (this.loading) { return; } if (this.config.settingListener) { var setting = { align: this.header.alignEditor.val(), formatter: this.header.formatterEditor.val(), width: this.header.widthEditor.val(), data_type: this.header.typeEditor.val(), }; this.config.settingListener.setColumnSetting(this, null, setting); } }, setTipList: function(tipList) { this.tipList = tipList; if (this.dropdown) { this.dropdown.dirty = true; } }, addOneTip: function(tip) { if (!tip || !tip.name) { return; } this.tipList[tip.name] = tip; }, updateTip: function(preTip, afterTip) { if (!afterTip || !afterTip.name) { return; } this.tipList[preTip.name] = afterTip; }, deleteOneTip: function(tip) { if (!tip || !tip.name) { return; } delete this.tipList[tip.name]; }, linkDropdown: function(editor) { var me = this; me.activeEditor = editor; editor.on("keydown", function(e) { me.onEditorKeyDown.call(me, editor, e); }); editor.on("keyup", function(e) { me.onEditorKeyUp.call(me, editor, e); }); }, getCurrentPos: function(editor) { editor.focus(); var currentPos = editor.get(0).selectionStart; return currentPos; }, setCurrentPos: function(editor, currentPosition) { editor.focus(); var currentPos = editor.get(0).selectionStart; if (currentPos != undefined && currentPos >= currentPosition) { editor.get(0).selectionStart = (currentPosition); editor.get(0).selectionEnd = (currentPosition); } }, onEditorKeyDown: function(editor, e) { var config = this.config, Code = this.Code; var event = e || window.event; var keyCode = event.keyCode; if (keyCode == Code.BackSpace) { this.editMode = "DeleteFront"; } else if (keyCode == Code.Delete) { this.editMode = "DeleteBack"; } else if (keyCode == Code.TrigerBegin) { this.editMode = "ShowDropDown"; } else if (keyCode == Code.Return) { this.editMode = "ReShowDropDown"; } else { this.editMode = "Edit"; } }, onEditorKeyUp: function(editor, e) { this.activeEditor = editor; var editMode = this.editMode; if (editMode == "DeleteFront") { this.deleteFront(editor); e.preventDefault(); } else if (editMode == "DeleteBack") { this.deleteBack(editor); e.preventDefault(); } else if (editMode == "ShowDropDown") { this.showDropdown(editor); } else if (editMode == "ReShowDropDown") { this.reshowDropdown(editor); } }, deleteFront: function(editor) { var value = editor.val(); var pos = this.getCurrentPos(editor); if (pos <= 0) { return; } var location = this.getVariantLocation(value, pos, "toFront"); var from = location ? location.from : pos; var to = location ? location.to : pos; value = value.substring(0, from) + value.substring(to + 1); editor.val(value); }, deleteBack: function() { var value = editor.val(); var pos = this.getCurrentPos(editor); if (pos >= value.length) { return; } var header = val.substring(pos, pos + 1); var location; if (header == this.Code.TrigerBegin) { location = this.getVariantLocation(value, pos, "toBack"); } var from = location ? location.from : pos; var to = location ? location.to : pos; value = value.substring(0, from) + value.substring(to + 1); editor.val(value); }, reshowDropdown: function(editor) { var value = editor.val(); var pos = this.getCurrentPos(editor); if (pos <= 0) { return; } var header = val.substring(pos, pos + 1); if (header == this.Code.TrigerBegin) { this.showDropdown(editor, pos); } }, showDropdown: function(editor, pos) { if (pos == undefined) { pos = this.getCurrentPos(editor); } if (!this.dropdown || this.dropdown.dirty) { this.createDropDown(editor); } this.setDropDownPosition(editor, this.dropdown.el); this.dropdown.el.show(); }, hideDropdown: function() { this.dropdown.el.hide(); }, createDropDown: function(editor) { if (this.dropdown) { this.dropdown.el.empty(); } else { this.dropdown = { el: $(Formula_Template.dropdown) }; } var tipList = this.tipList, dropdownEl = this.dropdown.el; if (tipList) { var me = this; for (var prop in tipList) { var item = tipList[prop]; var el = $(Formula_Template.dropdownItem); el.html(item.name); el.prop("data-code", prop); el.click(function() { var code = $(this).prop("data-code"); var item = me.tipList[code]; me.onSelectDropdown(item); }); dropdownEl.append(el); } } $("body").append(this.dropdown.el); this.dropdown.dirty = false; return this.dropdown; }, insertMathOperator: function (item, idx) { var editor = this.activeEditor; if(!editor || !item) { return; } item = " "+ item +" " let currentPos = this.getCurrentPos(editor); var value = editor.val(); // check same type tag let result = value.substring(0, currentPos) + item + value.substring(currentPos); editor.val(result); let nextPosition = currentPos + item.length; if(idx) { nextPosition = currentPos + item.length - idx - 1; } this.setCurrentPos(editor, nextPosition); }, onSelectDropdown: function(item) { var editor = this.activeEditor; var pos = this.getCurrentPos(editor); var value = editor.val(); var result = value.substring(0, pos - 1) + item.text + value.substring(pos + 1); editor.val(result); this.hideDropdown(); }, getVariantLocation: function(value, pos, direction) { var result = { from: pos, to: pos } if ("toBack" == direction) { var max = value.length; for (var i = pos + 1; i < max; i++) { var char = value.charCodeAt(i); if ("[" == char) { return result; } else if ("]" == char) { result.from = pos; result.to = i; return result; } } } else if ("toFront" == direction) { for (var i = pos - 1; i >= 0; i--) { var char = value[i]; if ("]" == char) { return result; } else if ("[" == char) { result.from = i; result.to = pos; return result; } } } return result; }, setDropDownPosition: function(editor, dropdown) { dropdown.css({ bottom: "50px", left: "300px" }); } }); //************************************* Formula_Template = {}; Formula_Template.container = [ '