/** * 路径选择 * * Created by GUY on 2015/12/4. * @class BI.PathChooser * @extends BI.Widget */ BI.PathChooser = BI.inherit(BI.Widget, { _const: { lineColor: "#d4dadd", selectLineColor: "#3f8ce8" }, _defaultConfig: function () { return BI.extend(BI.PathChooser.superclass._defaultConfig.apply(this, arguments), { baseCls: "bi-path-chooser", items: [] }) }, _init: function () { BI.PathChooser.superclass._init.apply(this, arguments); this.populate(this.options.items); }, _createRegions: function (regions) { var self = this; this.regions = BI.createWidgets(BI.map(regions, function (i, region) { return { type: "bi.path_region", title: self.texts[region] || region } })); this.regionMap = {}; BI.each(regions, function (i, region) { self.regionMap[region] = i; }); this.container = BI.createWidget({ type: "bi.horizontal", verticalAlign: "top", scrollx: false, scrolly: false, hgap: 10, items: this.regions }); BI.createWidget({ type: "bi.vertical_adapt", element: this, scrollable: true, hgap: 10, items: [this.container] }); }, getRegionIndexById: function (id) { var node = this.cache[id]; var regionType = node.get("region"); return this.regionMap[regionType]; }, _drawPath: function (start, offset, index) { var self = this; var starts = []; if (BI.contains(this.start, start)) { starts = this.start; } else { starts = [start]; } BI.each(starts, function (i, s) { BI.each(self.radios[s], function (i, rad) { rad.setSelected(false); }); BI.each(self.lines[s], function (i, line) { line.attr("stroke", self._const.lineColor); }); BI.each(self.regionIndexes[s], function (i, idx) { self.regions[idx].reset(); }); }); BI.each(this.routes[start][index], function (i, id) { var regionIndex = self.getRegionIndexById(id); self.regions[regionIndex].setSelect(offset + index, id); }); var current = BI.last(this.routes[start][index]); while (current && this.routes[current] && this.routes[current].length === 1) { BI.each(this.routes[current][0], function (i, id) { var regionIndex = self.getRegionIndexById(id); self.regions[regionIndex].setSelect(0, id); }); this.lines[current][0].attr("stroke", self._const.selectLineColor).toFront(); current = BI.last(this.routes[current][0]); } this.lines[start][index].attr("stroke", self._const.selectLineColor).toFront(); this.radios[start] && this.radios[start][index] && this.radios[start][index].setSelected(true); }, _drawRadio: function (start, offset, index, x, y) { var self = this; var radio = BI.createWidget({ type: "bi.radio", cls: "path-chooser-radio", selected: offset + index === 0, start: start, index: index }); radio.on(BI.Radio.EVENT_CHANGE, function () { self._drawPath(start, offset, index); self.fireEvent(BI.PathChooser.EVENT_CHANGE, start, index); }); if (!this.radios[start]) { this.radios[start] = []; } this.radios[start].push(radio); BI.createWidget({ type: "bi.absolute", element: this.container, items: [{ el: radio, left: x - 6.5, top: y - 6.5 }] }) }, _drawLine: function (start, lines) { var self = this; if (!this.lines[start]) { this.lines[start] = []; } if (!this.pathes[start]) { this.pathes[start] = []; } var startRegionIndex = this.getRegionIndexById(start); //start所在的位置,然后接着往下画其他的路径 var offset = this.regions[startRegionIndex].getIndexByValue(start); BI.each(lines, function (i, line) { self.pathes[start][i] = []; var idx = i + offset; var path = ""; var stop = 47.5 + 29 * idx; var sleft = 50 + 100 * startRegionIndex; var radioStartX = sleft, radioStartY = stop; var etop = stop; var endRegionIndex = self.getRegionIndexById(BI.last(line)); var endOffset = self.regions[endRegionIndex].getIndexByValue(BI.last(line)); var eleft = 50 + 100 * endRegionIndex; if (BI.contains(self.start, start)) { radioStartX = sleft - 50; path += "M" + (sleft - 50) + "," + stop; self.pathes[start][i].push({ x: sleft - 50, y: stop }) } else if (idx === 0) { radioStartX = sleft + 50; path += "M" + sleft + "," + stop; self.pathes[start][i].push({ x: sleft, y: stop }) } else { radioStartX = sleft + 50; path += "M" + sleft + "," + 47.5 + "L" + (sleft + 50) + "," + 47.5 + "L" + (sleft + 50) + "," + stop; self.pathes[start][i].push({ x: sleft, y: 47.5 }); self.pathes[start][i].push({ x: sleft + 50, y: 47.5 }); self.pathes[start][i].push({ x: sleft + 50, y: stop }); } if (idx > 0) { var endY = endOffset * 29 + 47.5; path += "L" + (eleft - 50) + "," + etop + "L" + (eleft - 50) + "," + endY + "L" + eleft + "," + endY; self.pathes[start][i].push({ x: eleft - 50, y: etop }); self.pathes[start][i].push({ x: eleft - 50, y: endY }); self.pathes[start][i].push({ x: eleft, y: endY }); } else { path += "L" + eleft + "," + etop; self.pathes[start][i].push({ x: eleft, y: etop }); } var graph = self.svg.path(path) .attr({ stroke: idx === 0 ? self._const.selectLineColor : self._const.lineColor, 'stroke-dasharray': '-' }); self.lines[start].push(graph); if (lines.length > 1) { self.lines[start][0].toFront(); } //第一个元素无论有多少个都要显示radio if (BI.contains(self.start, start)) { self.lines[self.regions[0].getValueByIndex(0)][0].toFront(); } if (lines.length > 1 || BI.contains(self.start, start)) { self._drawRadio(start, offset, i, radioStartX, radioStartY); } }); }, _drawLines: function (routes) { var self = this; this.lines = {}; this.pathes = {}; this.radios = {}; this.regionIndexes = {}; BI.each(routes, function (k, route) { if (!self.regionIndexes[k]) { self.regionIndexes[k] = []; } BI.each(route, function (i, rs) { BI.each(rs, function (j, id) { var regionIndex = self.getRegionIndexById(id); if (!BI.contains(self.regionIndexes[k], regionIndex)) { self.regionIndexes[k].push(regionIndex); } }); }) }); BI.each(routes, function (k, route) { self._drawLine(k, route); }); }, _pushNodes: function (nodes) { var self = this; var indexes = []; for (var i = 0; i < nodes.length; i++) { var id = nodes[i]; var index = self.getRegionIndexById(id); indexes.push(index); var region = self.regions[index]; if (i === nodes.length - 1) { if (!region.hasItem(id)) { region.addItem(id, self.texts[id]); } break; } if (i > 0 || BI.contains(self.start, id)) { region.addItem(id, self.texts[id]); } } for (var i = BI.first(indexes); i < BI.last(indexes); i++) { if (!BI.contains(indexes, i)) { self.regions[i].addItem(""); } } }, _createNodes: function () { var self = this, o = this.options; this.cache = {}; this.texts = {}; this.start = []; this.end = []; BI.each(o.items, function (i, item) { self.start.push(BI.first(item).value); self.end.push(BI.last(item).value); }); this.start = BI.uniq(this.start); this.end = BI.uniq(this.end); var regions = []; var tree = new BI.Tree(); var branches = {}, max = 0; BI.each(o.items, function (i, items) { BI.each(items, function (j, item) { if (!BI.has(branches, item.value)) { branches[item.value] = 0; } branches[item.value]++; max = Math.max(max, branches[item.value]); var prev = {}; if (j > 0) { prev = items[j - 1]; } var parent = self.cache[prev.value || ""]; var node = self.cache[item.value] || new BI.Node(item.value); node.set(item); self.cache[item.value] = node; self.texts[item.value] = item.text; self.texts[item.region] = item.regionText; parent = BI.isNull(parent) ? tree.getRoot() : parent; if (parent.getChildIndex(item.value) === -1) { tree.addNode(parent, node); } }) }); //算出区域列表 tree.traverse(function (node) { BI.each(node.getChildren(), function (i, child) { if (BI.contains(regions, child.get("region"))) { var index1 = BI.indexOf(regions, node.get("region")); var index2 = BI.indexOf(regions, child.get("region")); //交换区域 if (index1 > index2) { var t = regions[index2]; for (var j = index2; j < index1; j++) { regions[j] = regions[j + 1]; } regions[index1] = t; } } else { regions.push(child.get("region")); } }); }); this._createRegions(regions); //算出节点 BI.each(branches, function (k, branch) { if (branch < max) { delete branches[k]; } }); //过滤节点 var nodes = []; var n = tree.getRoot(); while (n && n.getChildrenLength() === 1) { if (BI.has(branches, n.getChildren()[0].id)) { delete branches[n.getChildren()[0].id]; n = n.getChildren()[0]; } else { n = null; } } tree.traverse(function (node) { if (BI.has(branches, node.id)) { nodes.push(node.id); delete branches[node.id]; } }); //填充节点 var routes = {}; var s, e; for (var i = 0, len = nodes.length; i < len + 1; i++) { if (len === 0) { s = []; BI.each(this.start, function (i, id) { s.push(tree.search(id)); }); e = []; BI.each(this.end, function (i, id) { e.push(tree.search(id)); }); } else if (i === len) { s = e; e = []; BI.each(this.end, function (i, id) { e.push(tree.search(id)); }); } else if (i === 0) { s = []; BI.each(this.start, function (i, id) { s.push(tree.search(id)); }); e = [tree.search(nodes[i])]; } else { s = [tree.search(e[0] || tree.getRoot(), nodes[i - 1])]; e = [tree.search(s[0], nodes[i])]; } BI.each(s, function (i, n) { tree._recursion(n, [n.id], function (node, route) { if (BI.contains(e, node)) { if (!routes[n.id]) { routes[n.id] = []; } routes[n.id].push(route); self._pushNodes(route); if (e.length <= 1) { return true; } } }) }); } this.routes = routes; this._drawLines(routes); }, _unselectAllPath: function () { var self = this; BI.each(this.radios, function (idx, rad) { BI.each(rad, function (i, r) { r.setSelected(false); }); }); BI.each(this.lines, function (idx, line) { BI.each(line, function (i, li) { li.attr("stroke", self._const.lineColor); }); }); BI.each(this.regions, function (idx, region) { region.reset(); }); }, populate: function (items) { this.options.items = items || []; var self = this; this.empty(); if (this.options.items.length <= 0) { return; } this.svg = BI.createWidget({ type: "bi.svg" }); this._createNodes(); BI.createWidget({ type: "bi.absolute", element: this.container, items: [{ el: this.svg, top: 0, left: 0, right: 0, bottom: 0 }] }); }, setValue: function (v) { this._unselectAllPath(); var nodes = BI.keys(this.routes), self = this; var result = [], array = []; BI.each(v, function (i, val) { if (BI.contains(nodes, val)) { if (array.length > 0) { array.push(val); result.push(array); array = []; } } array.push(val); }); if (array.length > 0) { result.push(array); } //画这n条路径 BI.each(result, function (idx, path) { var start = path[0]; var index = BI.findIndex(self.routes[start], function (idx, p) { if (BI.isEqual(path, p)) { return true; } }); if (index >= 0) { var startRegionIndex = self.getRegionIndexById(start); var offset = self.regions[startRegionIndex].getIndexByValue(start); self._drawPath(start, offset, index); } }); }, getValue: function () { var path = []; BI.each(this.regions, function (i, region) { var val = region.getValue(); if (BI.isKey(val)) { path.push(val); } }); return path; } }); BI.PathChooser.EVENT_CHANGE = "PathChooser.EVENT_CHANGE"; BI.shortcut("bi.path_chooser", BI.PathChooser);