/** * 布局 * * Created by GUY on 2016/2/23. * @class BI.Arrangement * @extends BI.Widget */ BI.Arrangement = BI.inherit(BI.Widget, { _defaultConfig: function () { return BI.extend(BI.Arrangement.superclass._defaultConfig.apply(this, arguments), { baseCls: "bi-arrangement", layoutType: BI.Arrangement.LAYOUT_TYPE.FREE, isNeedReLayout: true, items: [] }); }, _init: function () { BI.Arrangement.superclass._init.apply(this, arguments); var self = this, o = this.options; this.arrangement = BI.createWidget({ type: "bi.arrangement_droppable", cls: "arrangement-block", invisible: true }); this.block = BI.createWidget({ type: "bi.arrangement_block", invisible: true }); this.droppable = BI.createWidget({ type: "bi.layout", cls: "arrangement-drop-container", invisible: true }); this.container = BI.createWidget({ type: "bi.absolute", items: o.items.concat([this.block, this.arrangement, { el: this.droppable, left: 0, right: 0, top: 0, bottom: 0 }]) }); this.scrollContainer = BI.createWidget({ type: "bi.adaptive", width: "100%", height: "100%", scrollable: true, items: [this.container] }); this.scrollContainer.element.scroll(function () { self.fireEvent(BI.Arrangement.EVENT_SCROLL, { scrollLeft: self.scrollContainer.element.scrollLeft(), scrollTop: self.scrollContainer.element.scrollTop(), clientWidth: self.scrollContainer.element[0].clientWidth, clientHeight: self.scrollContainer.element[0].clientHeight }); }); BI.createWidget({ type: "bi.adaptive", element: this, items: [this.scrollContainer] }); this.regions = {}; this.locations = {}; this.drops = {}; this.storeDrops = {}; if (o.items.length > 0) { BI.nextTick(function () { self.populate(o.items); }); } }, ////初始化操作//// _calculateRegions: function (items) { var self = this, o = this.options; this.regions = {}; this.drops = {}; BI.each(items, function (i, item) { var region = self._createOneRegion(item); self.regions[region.id] = region; var drop = self._createOneDrop(region); self.drops[drop.id] = drop; self.storeDrops[drop.id] = drop; }); }, //定方位 _locationRegion: function () { var self = this, o = this.options; this.locations = {}; var reg = []; BI.each(this.regions, function (id, region) { var t = new BI.Region(region.left, region.top, region.width, region.height); t.id = id; reg.push(t); self.locations[id] = {top: [], left: [], right: [], bottom: []}; }); BI.each(reg, function (i, dim) { var topRegion = new BI.Region(dim.x, 0, dim.w, dim.y), bottomRegion = new BI.Region(dim.x, dim.y + dim.h, dim.w, BI.MAX), leftRegion = new BI.Region(0, dim.y, dim.x, dim.h), rightRegion = new BI.Region(dim.x + dim.w, dim.y, BI.MAX, dim.h); BI.each(reg, function (j, tar) { if (i !== j) { if (tar.isIntersects(topRegion) && self._isLessThanEqual(tar.y + tar.h, dim.y)) { self.locations[dim.id].top.push(self.regions[tar.id]); } if (tar.isIntersects(bottomRegion) && self._isMoreThanEqual(tar.y, dim.y + dim.h)) { self.locations[dim.id].bottom.push(self.regions[tar.id]); } if (tar.isIntersects(leftRegion) && self._isLessThanEqual(tar.x + tar.w, dim.x)) { self.locations[dim.id].left.push(self.regions[tar.id]); } if (tar.isIntersects(rightRegion) && self._isMoreThanEqual(tar.x, dim.x + dim.w)) { self.locations[dim.id].right.push(self.regions[tar.id]); } } }); }); }, _isEqual: function (num1, num2) { return Math.abs(num1 - num2) < 2; }, _isLessThan: function (num1, num2) { return num1 < num2 && !this._isEqual(num1, num2); }, _isMoreThan: function (num1, num2) { return num1 > num2 && !this._isEqual(num1, num2); }, _isLessThanEqual: function (num1, num2) { return num1 <= num2 || this._isEqual(num1, num2); }, _isMoreThanEqual: function (num1, num2) { return num1 >= num2 || this._isEqual(num1, num2); }, ////方法//// _isPositionInBounds: function (position, bound) { var region = new BI.Region(bound.left, bound.top, bound.width, bound.height); return region.isPointInside(position.left, position.top); }, //获取某区域等量相关联的区域 _getEquivalentRelativeRegions: function (name, direction) { var self = this; direction || (direction = ["top", "bottom", "left", "right"]); var result = []; var target = this.regions[name]; var tops = this.locations[name].top; var bottoms = this.locations[name].bottom; var lefts = this.locations[name].left; var rights = this.locations[name].right; var finded = direction.contains("top") && BI.some(tops, function (i, region) { if (self._isEqual(region.top + region.height, target.top) && self._isEqual(region.left, target.left) && self._isEqual(region.width, target.width)) { var clone = BI.clone(region); clone.height = region.height + target.height; result.push(clone); return true; } }); if (!finded) { finded = direction.contains("bottom") && BI.some(bottoms, function (i, region) { if (self._isEqual(target.top + target.height, region.top) && self._isEqual(region.left, target.left) && self._isEqual(region.width, target.width)) { var clone = BI.clone(region); clone.top = region.top - target.height; clone.height = region.height + target.height; result.push(clone); return true; } }); if (!finded) { finded = direction.contains("left") && BI.some(lefts, function (i, region) { if (self._isEqual(region.left + region.width, target.left) && self._isEqual(region.top, target.top) && self._isEqual(region.height, target.height)) { var clone = BI.clone(region); clone.width = region.width + target.width; result.push(clone); return true; } }); if (!finded) { finded = direction.contains("right") && BI.some(rights, function (i, region) { if (self._isEqual(target.left + target.width, region.left) && self._isEqual(region.top, target.top) && self._isEqual(region.height, target.height)) { var clone = BI.clone(region); clone.left = region.left - target.width; clone.width = region.width + target.width; result.push(clone); return true; } }); if (!finded) { var findTopRegions = [], findBottomRegions = []; direction.contains("top") && BI.each(tops, function (i, region) { if (self._isEqual(region.top + region.height, target.top) && self._isMoreThanEqual(region.left, target.left) && self._isLessThanEqual(region.left + region.width, target.left + target.width)) { findTopRegions.push(region); } }); direction.contains("bottom") && BI.each(bottoms, function (i, region) { if (self._isEqual(target.top + target.height, region.top) && self._isMoreThanEqual(region.left, target.left) && self._isLessThanEqual(region.left + region.width, target.left + target.width)) { findBottomRegions.push(region); } }); var topValid = isRegionsValid(findTopRegions, "top"), bottomValid = isRegionsValid(findBottomRegions, "bottom"); if (topValid && bottomValid) { BI.each(findTopRegions, function (i, region) { var clone = BI.clone(region); clone.height = region.height + target.height / 2; result.push(clone); }); BI.each(findBottomRegions, function (i, region) { var clone = BI.clone(region); clone.top = region.top - target.height / 2; clone.height = region.height + target.height / 2; result.push(clone); }); } else if (topValid) { BI.each(findTopRegions, function (i, region) { var clone = BI.clone(region); clone.height = region.height + target.height; result.push(clone); }); } else if (bottomValid) { BI.each(findBottomRegions, function (i, region) { var clone = BI.clone(region); clone.top = region.top - target.height; clone.height = region.height + target.height; result.push(clone); }); } if (!topValid && !bottomValid) { var findLeftRegions = [], findRightRegions = []; direction.contains("left") && BI.each(lefts, function (i, region) { if (self._isEqual(region.left + region.width, target.left) && self._isMoreThanEqual(region.top, target.top) && self._isLessThanEqual(region.top + region.height, target.top + target.height)) { findLeftRegions.push(region); } }); direction.contains("right") && BI.each(rights, function (i, region) { if (self._isEqual(target.left + target.width, region.left) && self._isMoreThanEqual(region.top, target.top) && self._isLessThanEqual(region.top + region.height, target.top + target.height)) { findRightRegions.push(region); } }); var leftValid = isRegionsValid(findLeftRegions, "left"), rightValid = isRegionsValid(findRightRegions, "right"); if (leftValid && rightValid) { BI.each(findLeftRegions, function (i, region) { var clone = BI.clone(region); clone.width = region.width + target.width / 2; result.push(clone); }); BI.each(findRightRegions, function (i, region) { var clone = BI.clone(region); clone.left = region.left - target.width / 2; clone.width = region.width + target.width / 2; result.push(clone); }); } else if (leftValid) { BI.each(findLeftRegions, function (i, region) { var clone = BI.clone(region); clone.width = region.width + target.width; result.push(clone); }); } else if (rightValid) { BI.each(findRightRegions, function (i, region) { var clone = BI.clone(region); clone.left = region.left - target.width; clone.width = region.width + target.width; result.push(clone); }); } //上下左右都不可行 if (!leftValid && !rightValid) { } } } } } } return result; function isRegionsValid(regions, dir) { var occupied = self._getRegionOccupied(regions); switch (dir) { case "top": case "bottom": return self._isEqual(occupied.left, target.left) && self._isEqual(occupied.width, target.width); case "left": case "right": return self._isEqual(occupied.top, target.top) && self._isEqual(occupied.height, target.height); } return false; } }, //获取某区域直接相关联的区域 _getDirectRelativeRegions: function (name, direction) { var self = this; direction || (direction = ["top", "bottom", "left", "right"]); var result = []; var target = this.regions[name]; var tops = this.locations[name].top; var bottoms = this.locations[name].bottom; var lefts = this.locations[name].left; var rights = this.locations[name].right; var finded = direction.contains("top") && BI.some(tops, function (i, region) { if (self._isEqual(region.top + region.height, target.top) && ((self._isMoreThanEqual(region.left, target.left) && self._isLessThan(region.left, target.left + target.width)) || (self._isMoreThan(region.left + region.width, target.left) && self._isLessThanEqual(region.left + region.width, target.left + target.width)) || (self._isLessThan(region.left, target.left) && self._isMoreThan(region.left + region.width, target.left + target.width)))) { var clone = BI.clone(region); clone.height = region.height + target.height; result.push(clone); } }); if (!finded) { finded = direction.contains("bottom") && BI.some(bottoms, function (i, region) { if (self._isEqual(target.top + target.height, region.top) && ((self._isMoreThanEqual(region.left, target.left) && self._isLessThan(region.left, target.left + target.width)) || (self._isMoreThan(region.left + region.width, target.left) && self._isLessThanEqual(region.left + region.width, target.left + target.width)) || (self._isLessThan(region.left, target.left) && self._isMoreThan(region.left + region.width, target.left + target.width)))) { var clone = BI.clone(region); clone.top = region.top - target.height; clone.height = region.height + target.height; result.push(clone); } }); if (!finded) { finded = direction.contains("left") && BI.some(lefts, function (i, region) { if (self._isEqual(region.left + region.width, target.left) && ((self._isMoreThanEqual(region.top, target.top) && self._isLessThan(region.top, target.top + target.height)) || (self._isMoreThan(region.top + region.height, target.top) && self._isLessThanEqual(region.top + region.height, target.top + target.height)) || (self._isLessThan(region.top, target.top) && self._isMoreThan(region.top + region.height, target.top + target.height)))) { var clone = BI.clone(region); clone.width = region.width + target.width; result.push(clone); } }); if (!finded) { finded = direction.contains("right") && BI.some(rights, function (i, region) { if (self._isEqual(target.left + target.width, region.left) && ((self._isMoreThanEqual(region.top, target.top) && self._isLessThan(region.top, target.top + target.height)) || (self._isMoreThan(region.top + region.height, target.top) && self._isLessThanEqual(region.top + region.height, target.top + target.height)) || (self._isLessThan(region.top, target.top) && self._isMoreThan(region.top + region.height, target.top + target.height)))) { var clone = BI.clone(region); clone.left = region.left - target.width; clone.width = region.width + target.width; result.push(clone); } }); } } } return result; }, //获取间接相关联的区域,即调整name区域后需要附带调整的所有相关区域(包括自身) _getInDirectRelativeRegions: function (name, direction) { var self = this, dict = ["top", "left", "right", "bottom"]; var result = {}; direction || (direction = dict); function recursion(regions, dir, store, cache) { BI.each(regions, function (i, region) { if (cache[region.id]) { return; } cache[region.id] = true; if (!store[dict[3 - dir]]) { store[dict[3 - dir]] = []; } store[dict[3 - dir]].push(region); recursion(self._getDirectRelativeRegions(region.id, [dict[dir]]), 3 - dir, store, cache); }) } if (direction.contains("top")) { var store = {}, cache = {}; recursion([this.regions[name]], dict.indexOf("top"), store, cache); store["top"] = BI.sortBy(store["top"], "left"); store["bottom"] = BI.sortBy(store["bottom"], "left"); result["top"] = store; } if (direction.contains("bottom")) { var store = {}, cache = {}; recursion([this.regions[name]], dict.indexOf("bottom"), store, cache); store["top"] = BI.sortBy(store["top"], "left"); store["bottom"] = BI.sortBy(store["bottom"], "left"); result["bottom"] = store; } if (direction.contains("left")) { var store = {}, cache = {}; recursion([this.regions[name]], dict.indexOf("left"), store, cache); store["left"] = BI.sortBy(store["left"], "top"); store["right"] = BI.sortBy(store["right"], "top"); result["left"] = store; } if (direction.contains("right")) { var store = {}, cache = {}; recursion([this.regions[name]], dict.indexOf("right"), store, cache); store["left"] = BI.sortBy(store["left"], "top"); store["right"] = BI.sortBy(store["right"], "top"); result["right"] = store; } return result; }, _getLeftAlignRegions: function (name) { var self = this; var tops = this._getDirectRelativeRegions(name, ["top"]); var bottoms = this._getDirectRelativeRegions(name, ["bottom"]); var current = this.regions[name]; var rtop = [], rbottom = []; BI.each(tops, function (i, region) { if (self._isEqual(region.left, current.left)) { rtop.push(region); } }); BI.each(bottoms, function (i, region) { if (self._isEqual(region.left, current.left)) { rbottom.push(region); } }); return { top: rtop, bottom: rbottom } }, _getRightAlignRegions: function (name) { var self = this; var tops = this._getDirectRelativeRegions(name, ["top"]); var bottoms = this._getDirectRelativeRegions(name, ["bottom"]); var current = this.regions[name]; var rtop = [], rbottom = []; BI.each(tops, function (i, region) { if (self._isEqual(region.left + region.width, current.left + current.width)) { rtop.push(region); } }); BI.each(bottoms, function (i, region) { if (self._isEqual(region.left + region.width, current.left + current.width)) { rbottom.push(region); } }); return { top: rtop, bottom: rbottom } }, _getTopAlignRegions: function (name) { var self = this; var lefts = this._getDirectRelativeRegions(name, ["left"]); var rights = this._getDirectRelativeRegions(name, ["right"]); var current = this.regions[name]; var rleft = [], rright = []; BI.each(lefts, function (i, region) { if (self._isEqual(region.top, current.top)) { rleft.push(region); } }); BI.each(rights, function (i, region) { if (self._isEqual(region.top, current.top)) { rright.push(region); } }); return { left: rleft, right: rright } }, _getBottomAlignRegions: function (name) { var self = this; var lefts = this._getDirectRelativeRegions(name, ["left"]); var rights = this._getDirectRelativeRegions(name, ["right"]); var current = this.regions[name]; var rleft = [], rright = []; BI.each(lefts, function (i, region) { if (self._isEqual(region.top + region.height, current.top + current.height)) { rleft.push(region); } }); BI.each(rights, function (i, region) { if (self._isEqual(region.top + region.height, current.top + current.height)) { rright.push(region); } }); return { left: rleft, right: rright } }, //获取占有的最大Region _getRegionOccupied: function (regions) { var self = this, o = this.options; if (BI.size(regions || this.regions) <= 0) { return { left: 0, top: 0, width: 0, height: 0 } } var minLeft = BI.MAX, maxLeft = BI.MIN, minTop = BI.MAX, maxTop = BI.MIN; BI.each(regions || this.regions, function (id, region) { minLeft = Math.min(minLeft, region.left); maxLeft = Math.max(maxLeft, region.left + region.width); minTop = Math.min(minTop, region.top); maxTop = Math.max(maxTop, region.top + region.height); }); return { left: minLeft, top: minTop, width: maxLeft - minLeft, height: maxTop - minTop } }, //两个区域的交叉面积 _getCrossArea: function (region1, region2) { if (region1.left <= region2.left) { if (region1.top <= region2.top) { if (region1.top + region1.height > region2.top && region1.left + region1.width > region2.left) { if (this._isEqual(region1.top + region1.height, region2.top) || this._isEqual(region1.left + region1.width, region2.left)) { return 0; } return (region1.top + region1.height - region2.top) * (region1.left + region1.width - region2.left); } } else { if (region2.top + region2.height > region1.top && region1.left + region1.width > region2.left) { if (this._isEqual(region2.top + region2.height, region1.top) || this._isEqual(region1.left + region1.width, region2.left)) { return 0; } return (region2.top + region2.height - region1.top) * (region1.left + region1.width - region2.left); } } } else { if (region1.top <= region2.top) { if (region1.top + region1.height > region2.top && region2.left + region2.width > region1.left) { if (this._isEqual(region1.top + region1.height, region2.top) || this._isEqual(region2.left + region2.width, region1.left)) { return 0; } return (region1.top + region1.height - region2.top) * (region2.left + region2.width - region1.left); } } else { if (region2.top + region2.height > region1.top && region2.left + region2.width > region1.left) { if (this._isEqual(region2.top + region2.height, region1.top) || this._isEqual(region2.left + region2.width, region1.left)) { return 0; } return (region2.top + region2.height - region1.top) * (region2.left + region2.width - region1.left); } } } return 0; }, //是否有覆盖的组件 _isRegionOverlay: function (regions) { var reg = []; BI.each(regions || this.regions, function (id, region) { reg.push(new BI.Region(region.left, region.top, region.width, region.height)); }); for (var i = 0, len = reg.length; i < len; i++) { for (var j = i + 1; j < len; j++) { var area1 = { left: reg[i].x, top: reg[i].y, width: reg[i].w, height: reg[i].h }; var area2 = { left: reg[j].x, top: reg[j].y, width: reg[j].w, height: reg[j].h }; if (reg[i].isIntersects(reg[j]) && this._getCrossArea(area1, area2) > 1) { return true; } } } return false; }, //布局是否是优良的 _isArrangeFine: function (regions) { switch (this.options.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (this._isRegionOverlay()) { return false; } var maxRegion = this._getRegionOccupied(regions); var area = maxRegion.width * maxRegion.height; var all = 0; BI.each(regions || this.regions, function (id, region) { all += region.width * region.height; }); return Math.abs(area - all) < 8; case BI.Arrangement.LAYOUT_TYPE.FREE: return true; case BI.Arrangement.LAYOUT_TYPE.GRID: if (this._isRegionOverlay()) { return false; } } return true; }, _getRegionNames: function (regions) { var names = []; BI.each(regions || this.regions, function (i, region) { names.push(region.id || region.attr("id")); }); return names; }, _getRegionsByNames: function (names, regions) { names = BI.isArray(names) ? names : [names]; regions = regions || this.regions; if (BI.isArray(regions)) { var result = []; BI.each(regions, function (i, region) { if (names.contains(region.id || region.attr("id"))) { result.push(region); } }); } else { var result = {}; BI.each(names, function (i, name) { result[name] = regions[name]; }); } return result; }, _cloneRegion: function (regions) { var clone = {}; BI.each(regions || this.regions, function (id, region) { clone[id] = {}; clone[id].el = region.el; clone[id].id = region.id; clone[id].left = region.left; clone[id].top = region.top; clone[id].width = region.width; clone[id].height = region.height; }); return clone; }, //测试合法性 _test: function (regions) { var self = this; return !BI.any(regions || this.regions, function (i, region) { if (BI.isNaN(region.width) || BI.isNaN(region.height) || region.width <= 21 || region.height <= 21) { return true; } }) }, //对区域进行划分 _splitRegions: function (name) { var result = []; var tid = BI.UUID(); var _left = this._getLeftAlignRegions(name); var _right = this._getRightAlignRegions(name); var _top = this._getTopAlignRegions(name); var _bottom = this._getBottomAlignRegions(name); if (_left.top.length > 0) { var upid = _left.top[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].width, clone[upid].width) / 2;//两个组件中较小宽度的一半 var left = (clone[name].left + clone[upid].left) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 left: left, top: clone[upid].top, width: split, height: clone[name].height + clone[upid].height }; BI.extend(clone[name], { left: left + split, width: clone[name].width - split - (left - clone[name].left) }); BI.extend(clone[upid], { left: left + split, width: clone[upid].width - split - (left - clone[upid].left) }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "left-top", regions: clone, insert: insert }); } } if (_left.bottom.length > 0) { var bottomid = _left.bottom[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].width, clone[bottomid].width) / 2; var left = (clone[name].left + clone[bottomid].left) / 2; var insert = clone[tid] = {//新增的区域 left: left, top: clone[name].top, width: split, height: clone[name].height + clone[bottomid].height }; BI.extend(clone[name], { left: left + split, width: clone[name].width - split - (left - clone[name].left) }); BI.extend(clone[bottomid], { left: left + split, width: clone[bottomid].width - split - (left - clone[bottomid].left) }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom-left", regions: clone, insert: insert }); } } if (_right.top.length > 0) { var upid = _right.top[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].width, clone[upid].width) / 2;//两个组件中较小宽度的一半 var right = (clone[name].left + clone[name].width + clone[upid].left + clone[upid].width) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 left: right - split, top: clone[upid].top, width: split, height: clone[name].height + clone[upid].height }; BI.extend(clone[name], { width: right - clone[name].left - split }); BI.extend(clone[upid], { width: right - clone[upid].left - split }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "top-right", regions: clone, insert: insert }); } } if (_right.bottom.length > 0) { var bottomid = _right.bottom[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].width, clone[bottomid].width) / 2;//两个组件中较小宽度的一半 var right = (clone[name].left + clone[name].width + clone[bottomid].left + clone[bottomid].width) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 left: right - split, top: clone[name].top, width: split, height: clone[name].height + clone[bottomid].height }; BI.extend(clone[name], { width: right - clone[name].left - split }); BI.extend(clone[bottomid], { width: right - clone[bottomid].left - split }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom-right", regions: clone, insert: insert }); } } if (_top.left.length > 0) { var leftid = _top.left[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].height, clone[leftid].height) / 2;//两个组件中较小高度的一半 var top = (clone[name].top + clone[leftid].top) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 top: top, left: clone[leftid].left, height: split, width: clone[name].width + clone[leftid].width }; BI.extend(clone[name], { top: top + split, height: clone[name].height - split - (top - clone[name].top) }); BI.extend(clone[leftid], { top: top + split, height: clone[leftid].height - split - (top - clone[leftid].top) }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "top-left", regions: clone, insert: insert }); } } if (_top.right.length > 0) { var rightid = _top.right[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].height, clone[rightid].height) / 2;//两个组件中较小高度的一半 var top = (clone[name].top + clone[rightid].top) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 top: top, left: clone[name].left, height: split, width: clone[name].width + clone[rightid].width }; BI.extend(clone[name], { top: top + split, height: clone[name].height - split - (top - clone[name].top) }); BI.extend(clone[rightid], { top: top + split, height: clone[rightid].height - split - (top - clone[rightid].top) }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "top-right-second", regions: clone, insert: insert }); } } if (_bottom.left.length > 0) { var leftid = _bottom.left[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].height, clone[leftid].height) / 2;//两个组件中较小高度的一半 var bottom = (clone[name].top + clone[name].height + clone[leftid].top + clone[leftid].height) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 left: clone[leftid].left, top: bottom - split, height: split, width: clone[name].width + clone[leftid].width }; BI.extend(clone[name], { height: bottom - clone[name].top - split }); BI.extend(clone[leftid], { height: bottom - clone[leftid].top - split }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom-left-second", regions: clone, insert: insert }); } } if (_bottom.right.length > 0) { var rightid = _bottom.right[0].id; var tid = BI.UUID(); var clone = this._cloneRegion(); var _cur = clone[name]; var split = Math.min(clone[name].height, clone[rightid].height) / 2;//两个组件中较小高度的一半 var bottom = (clone[name].top + clone[name].height + clone[rightid].top + clone[rightid].height) / 2;//求平均,减少误差 var insert = clone[tid] = {//新增的区域 left: clone[name].left, top: bottom - split, height: split, width: clone[name].width + clone[rightid].width }; BI.extend(clone[name], { height: bottom - clone[name].top - split }); BI.extend(clone[rightid], { height: bottom - clone[rightid].top - split }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom-right-second", regions: clone, insert: insert }); } } //有上方居中drop var lefts = _top.left, rights = _top.right; if (lefts.length > 0 && rights.length > 0) { var count = 0; var store = [this.regions[name]]; while (lefts.length > 0 || rights.length > 0) { var clone = this._cloneRegion(); if (lefts.length > 0) { store.push(this.regions[lefts[0].id]); } if (rights.length > 0) { store.push(this.regions[rights[0].id]); } count++; var top = BI.average(store, function (i, r) {//求平均,减少误差 return r.top; }); var occupied = this._getRegionOccupied(store); var split = BI.min(store, function (i, r) { return r.height; }).height / 2; var insert = clone[tid] = { left: occupied.left, width: occupied.width, top: top, height: split }; BI.each(store, function (i, region) { BI.extend(clone[region.id], { top: top + split, height: region.height - split - (top - region.top) }); }); if (this._test(store)) { delete clone[tid]; result.push({ type: "top-center" + count, regions: clone, insert: insert }); } else { break; } if (lefts.length > 0) { lefts = this._getTopAlignRegions(lefts[0].id).left; } if (rights.length > 0) { rights = this._getTopAlignRegions(rights[0].id).right; } } } //有下方居中drop var lefts = _bottom.left, rights = _bottom.right; if (lefts.length > 0 && rights.length > 0) { var count = 0; var store = [this.regions[name]]; while (lefts.length > 0 || rights.length > 0) { var clone = this._cloneRegion(); if (lefts.length > 0) { store.push(this.regions[lefts[0].id]); } if (rights.length > 0) { store.push(this.regions[rights[0].id]); } count++; var bottom = BI.average(store, function (i, r) {//求平均,减少误差 return r.top + r.height; }); var occupied = this._getRegionOccupied(store); var split = BI.min(store, function (i, r) { return r.height; }).height / 2; var insert = clone[tid] = { left: occupied.left, width: occupied.width, top: bottom - split, height: split }; BI.each(store, function (i, region) { BI.extend(clone[region.id], { height: bottom - region.top - split }); }); if (this._test(store)) { delete clone[tid]; result.push({ type: "bottom-center" + count, regions: clone, insert: insert }); } else { break; } if (lefts.length > 0) { lefts = this._getBottomAlignRegions(lefts[0].id).left; } if (rights.length > 0) { rights = this._getBottomAlignRegions(rights[0].id).right; } } } //有左方居中drop var tops = _left.top, bottoms = _left.bottom; if (tops.length > 0 && bottoms.length > 0) { var count = 0; var store = [this.regions[name]]; while (tops.length > 0 || bottoms.length > 0) { var clone = this._cloneRegion(); if (tops.length > 0) { store.push(this.regions[tops[0].id]); } if (bottoms.length > 0) { store.push(this.regions[bottoms[0].id]); } count++; var left = BI.average(store, function (i, r) {//求平均,减少误差 return r.left; }); var occupied = this._getRegionOccupied(store); var split = BI.min(store, function (i, r) { return r.width; }).width / 2; var insert = clone[tid] = { left: left, width: split, top: occupied.top, height: occupied.height }; BI.each(store, function (i, region) { BI.extend(clone[region.id], { left: left + split, width: region.width - split - (left - region.left) }); }); if (this._test(store)) { delete clone[tid]; result.push({ type: "left-center" + count, regions: clone, insert: insert }); } else { break; } if (tops.length > 0) { tops = this._getLeftAlignRegions(tops[0].id).top; } if (bottoms.length > 0) { bottoms = this._getLeftAlignRegions(bottoms[0].id).bottom; } } } //有右方居中drop var tops = _right.top, bottoms = _right.bottom; if (tops.length > 0 && bottoms.length > 0) { var count = 0; var store = [this.regions[name]]; while (tops.length > 0 || bottoms.length > 0) { var clone = this._cloneRegion(); if (tops.length > 0) { store.push(this.regions[tops[0].id]); } if (bottoms.length > 0) { store.push(this.regions[bottoms[0].id]); } count++; var right = BI.average(store, function (i, r) {//求平均,减少误差 return r.left + r.width; }); var occupied = this._getRegionOccupied(store); var split = BI.min(store, function (i, r) { return r.width; }).width / 2; var insert = clone[tid] = { left: right - split, width: split, top: occupied.top, height: occupied.height }; BI.each(store, function (i, region) { BI.extend(clone[region.id], { width: right - region.left - split }); }); if (this._test(store)) { delete clone[tid]; result.push({ type: "right-center" + count, regions: clone, insert: insert }); } else { break; } if (tops.length > 0) { tops = this._getRightAlignRegions(tops[0].id).top; } if (bottoms.length > 0) { bottoms = this._getRightAlignRegions(bottoms[0].id).bottom; } } } var lefts = this._getEquivalentRelativeRegions(name, ["left"]); if (lefts.length === 1) { var clone = this._cloneRegion(); var _cur = clone[name]; var left = this.regions[lefts[0].id]; var width = left.width; var all = left.width + _cur.width; var ratio = width / all; var split = all / 3; var lleft = width - split * ratio, rleft = _cur.width - split * (1 - ratio); var insert = false; if (lleft <= 21) { rleft = _cur.width - split; if (rleft <= 21) { } else { insert = clone[tid] = { top: _cur.top, height: _cur.height, left: _cur.left, width: split, }; BI.extend(clone[name], { top: _cur.top, height: _cur.height, left: _cur.left + split, width: _cur.width - split }); } } else if (rleft <= 21) { lleft = width - split; if (lleft <= 21) { } else { insert = clone[tid] = { top: _cur.top, height: _cur.height, left: _cur.left - split, width: split, }; BI.extend(clone[left.id], { top: left.top, height: left.height, left: left.left, width: left.width - split }); } } else { insert = clone[tid] = { top: _cur.top, height: _cur.height, left: left.left + lleft, width: split, }; BI.extend(clone[name], { top: _cur.top, height: _cur.height, left: _cur.left + _cur.width - rleft, width: rleft }); BI.extend(clone[left.id], { top: left.top, height: left.height, left: left.left, width: lleft }); } if (insert !== false) { if (this._test(clone)) { delete clone[tid]; result.push({ type: "left-line", regions: clone, insert: insert }) } } } var rights = this._getEquivalentRelativeRegions(name, ["right"]); if (rights.length === 1) { var clone = this._cloneRegion(); var _cur = clone[name]; var right = this.regions[rights[0].id]; var width = right.width; var all = right.width + _cur.width; var ratio = width / all; var split = all / 3; var lleft = _cur.width - split * (1 - ratio), rleft = width - split * ratio; var insert = false; if (lleft <= 21) { rleft = width - split; if (rleft <= 21) { } else { insert = clone[tid] = { top: right.top, height: right.height, left: right.left, width: split }; BI.extend(clone[right.id], { top: right.top, height: right.height, left: right.left + split, width: right.width - split }) } } else if (rleft <= 21) { lleft = _cur.width - split; if (lleft <= 21) { } else { insert = clone[tid] = { top: _cur.top, height: _cur.height, left: _cur.left + lleft, width: split }; BI.extend(clone[name], { top: _cur.top, height: _cur.height, left: _cur.left, width: _cur.width - split }) } } else { insert = clone[tid] = { top: _cur.top, height: _cur.height, left: _cur.left + lleft, width: split }; BI.extend(clone[name], { top: _cur.top, height: _cur.height, left: _cur.left, width: lleft }); BI.extend(clone[right.id], { top: right.top, height: right.height, left: right.left + right.width - rleft, width: rleft }); } if (insert !== false) { if (this._test(clone)) { delete clone[tid]; result.push({ type: "right-line", regions: clone, insert: insert }) } } } var tops = this._getEquivalentRelativeRegions(name, ["top"]); if (tops.length === 1) { var clone = this._cloneRegion(); var _cur = clone[name]; var top = this.regions[tops[0].id]; var height = top.height; var all = top.height + _cur.height; var ratio = height / all; var split = all / 3; var tleft = height - split * ratio, bleft = _cur.height - split * (1 - ratio); var insert = false; if (tleft <= 21) { bleft = _cur.height - split; if (bleft <= 21) { } else { insert = clone[tid] = { top: _cur.top, width: _cur.width, left: _cur.left, height: split }; BI.extend(clone[name], { top: _cur.top + split, height: _cur.height - split, left: _cur.left, width: _cur.width }); } } if (bleft <= 21) { tleft = height - split; if (tleft <= 21) { } else { insert = clone[tid] = { top: _cur.top - split, height: split, left: _cur.left, width: _cur.width }; BI.extend(clone[top.id], { top: top.top, height: top.height - split, left: top.left, width: top.width }); } } else { insert = clone[tid] = { top: top.top + tleft, height: split, left: _cur.left, width: _cur.width }; BI.extend(clone[name], { top: _cur.top + _cur.height - bleft, height: bleft, left: _cur.left, width: _cur.width }); BI.extend(clone[top.id], { top: top.top, height: tleft, left: top.left, width: top.width }); } if (insert !== false) { if (this._test(clone)) { delete clone[tid]; result.push({ type: "top-line", regions: clone, insert: insert }) } } } var bottoms = this._getEquivalentRelativeRegions(name, ["bottom"]); if (bottoms.length === 1) { var clone = this._cloneRegion(); var _cur = clone[name]; var bottom = this.regions[bottoms[0].id]; var height = bottom.height; var all = bottom.height + _cur.height; var ratio = height / all; var split = all / 3; var tleft = _cur.height - split * (1 - ratio), bleft = height - split * ratio; var insert = false; if (tleft <= 21) { bleft = height - split; if (bleft <= 21) { } else { insert = clone[tid] = { top: bottom.top, height: bottom.height, left: bottom.left, width: split }; BI.extend(clone[bottom.id], { top: bottom.top + split, height: bottom.height - split, left: bottom.left, width: bottom.width }); } } else if (bleft <= 21) { tleft = _cur.height - split; if (tleft <= 21) { } else { insert = clone[tid] = { top: _cur.top + tleft, height: split, left: _cur.left, width: _cur.width }; BI.extend(clone[name], { top: _cur.top, height: _cur.height - split, left: _cur.left, width: _cur.width }); } } else { insert = clone[tid] = { top: _cur.top + tleft, height: split, left: _cur.left, width: _cur.width, }; BI.extend(clone[name], { top: _cur.top, height: tleft, left: _cur.left, width: _cur.width }); BI.extend(clone[bottom.id], { top: bottom.top + bottom.height - bleft, height: bleft, left: bottom.left, width: bottom.width }); } if (insert !== false) { if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom-line", regions: clone, insert: insert }) } } } if (tops.length >= 1) { result.push({ type: "top-gap" }); } if (bottoms.length >= 1) { result.push({ type: "bottom-gap" }); } //上 var clone = this._cloneRegion(); var _cur = clone[name]; var insert = clone[tid] = { top: _cur.top, left: _cur.left, width: _cur.width, height: _cur.height / 2, }; BI.extend(clone[name], { top: _cur.top + _cur.height / 2, left: _cur.left, width: _cur.width, height: _cur.height / 2 }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "top", regions: clone, insert: insert }) } //下 var clone = this._cloneRegion(); var _cur = clone[name]; var insert = clone[tid] = { top: _cur.top + _cur.height / 2, left: _cur.left, width: _cur.width, height: _cur.height / 2 }; BI.extend(clone[name], { top: _cur.top, left: _cur.left, width: _cur.width, height: _cur.height / 2 }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "bottom", regions: clone, insert: insert }) } //左 var clone = this._cloneRegion(); var _cur = clone[name]; var insert = clone[tid] = { top: _cur.top, left: _cur.left, width: _cur.width / 2, height: _cur.height }; BI.extend(clone[name], { top: _cur.top, left: _cur.left + _cur.width / 2, width: _cur.width / 2, height: _cur.height }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "left", regions: clone, insert: insert }) } //右 var clone = this._cloneRegion(); var _cur = clone[name]; var insert = clone[tid] = { top: _cur.top, left: _cur.left + _cur.width / 2, width: _cur.width / 2, height: _cur.height }; BI.extend(clone[name], { top: _cur.top, left: _cur.left, width: _cur.width / 2, height: _cur.height }); if (this._test(clone)) { delete clone[tid]; result.push({ type: "right", regions: clone, insert: insert }) } return result; }, _positionAt: function (position, bound) { var left = position.left - bound.left, top = position.top - bound.top; var right = bound.width - left, bottom = bound.height - top; var halfW = bound.width / 2, halfH = bound.height / 2; if (left < 0 || top < 0 || right < 0 || bottom < 0) { return; } var devides = this._splitRegions(bound.id); var types = []; var result = {}; var topcenterCount = 0, bottomcenterCount = 0, leftcenterCount = 0, rightcenterCount = 0; BI.each(devides, function (i, devide) { if (devide.type.indexOf("top-center") > -1) { topcenterCount++; } else if (devide.type.indexOf("bottom-center") > -1) { bottomcenterCount++; } else if (devide.type.indexOf("left-center") > -1) { leftcenterCount++; } else if (devide.type.indexOf("right-center") > -1) { rightcenterCount++; } types.push(devide.type); result[devide.type] = devide; }); //drop if (top >= 5 && top <= 15 && left >= 5 && left <= 15 && types.contains("left-top")) { return result["left-top"]; } if (top >= 5 && top <= 15 && right >= 5 && right <= 15 && types.contains("top-right")) { return result["top-right"]; } if (bottom >= 5 && bottom <= 15 && left >= 5 && left <= 15 && types.contains("bottom-left")) { return result["bottom-left"]; } if (bottom >= 5 && bottom <= 15 && right >= 5 && right <= 15 && types.contains("bottom-right")) { return result["bottom-right"]; } if (top >= 5 && top <= 15 && left >= 25 && left <= 35 && types.contains("top-left")) { return result["top-left"]; } if (top >= 5 && top <= 15 && right >= 25 && right <= 35 && types.contains("top-right-second")) { return result["top-right-second"]; } if (bottom >= 5 && bottom <= 15 && left >= 25 && left <= 35 && types.contains("bottom-left-second")) { return result["bottom-left-second"]; } if (bottom >= 5 && bottom <= 15 && right >= 25 && right <= 35 && types.contains("bottom-right-second")) { return result["bottom-right-second"]; } //topcenter或bottomcenter if (left >= halfW - 10 && left <= halfW + 10 && (topcenterCount > 0 || bottomcenterCount > 0)) { for (var i = 1; i <= topcenterCount; i++) { var s = (topcenterCount - i) * 20 + 5, e = s + 10; if (top >= s && top <= e) { return result["top-center" + i]; } } for (var i = 1; i <= bottomcenterCount; i++) { var s = (bottomcenterCount - i) * 20 + 5, e = s + 10; if (bottom >= s && bottom <= e) { return result["bottom-center" + i]; } } } //leftcenter或rightcenter if (top >= halfH - 10 && top <= halfH + 10 && (leftcenterCount > 0 || rightcenterCount > 0)) { for (var i = 1; i <= leftcenterCount; i++) { var s = (leftcenterCount - i) * 20 + 5, e = s + 10; if (left >= s && left <= e) { return result["left-center" + i]; } } for (var i = 1; i <= rightcenterCount; i++) { var s = (rightcenterCount - i) * 20 + 5, e = s + 10; if (right >= s && right <= e) { return result["right-center" + i]; } } } //缝隙 if (top <= 8 && types.contains("top-gap")) { return result["top-gap"]; } if (bottom <= 8 && types.contains("bottom-gap")) { return result["bottom-gap"]; } //三分 if (top <= 15 && left >= 15 && right >= 15 && types.contains("top-line")) { return result["top-line"]; } if (left <= 15 && top >= 15 && bottom >= 15 && types.contains("left-line")) { return result["left-line"]; } if (right <= 15 && top >= 15 && bottom >= 15 && types.contains("right-line")) { return result["right-line"]; } if (bottom <= 15 && left >= 15 && right >= 15 && types.contains("bottom-line")) { return result["bottom-line"]; } //平分 if (top <= 1 / 4 * bound.height && types.contains("top")) { return result["top"]; } if (top >= 3 / 4 * bound.height && types.contains("bottom")) { return result["bottom"]; } if (left <= bound.width / 2 && types.contains("left")) { return result["left"]; } return result["right"]; }, _getScrollOffset: function () { return { left: this.scrollContainer.element[0].scrollLeft, top: this.scrollContainer.element[0].scrollTop } }, ////操作//// _createOneRegion: function (item) { var el = BI.createWidget(item.el); el.setVisible(true); return { id: el.attr("id"), left: item.left, top: item.top, width: item.width, height: item.height, el: el } }, _createOneDrop: function (region) { if (this.storeDrops[region.id]) { var drop = this.storeDrops[region.id].el; drop.setVisible(true); } else { var drop = BI.createWidget({ type: "bi.layout", cls: "arrangement-drop-region", widgetName: region.id }); } return { id: region.id, left: region.left, top: region.top, width: region.width, height: region.height, el: drop } }, _calculateDrops: function () { var self = this; BI.each(this.drops, function (id, drop) { drop.el.empty(); var devides = self._splitRegions(id); var topcenterCount = 0, bottomcenterCount = 0, leftcenterCount = 0, rightcenterCount = 0; var absolutes = []; var horizontals = []; var verticals = []; BI.each(devides, function (i, devide) { if (devide.type.indexOf("top-center") > -1) { topcenterCount++; } else if (devide.type.indexOf("bottom-center") > -1) { bottomcenterCount++; } else if (devide.type.indexOf("left-center") > -1) { leftcenterCount++; } else if (devide.type.indexOf("right-center") > -1) { rightcenterCount++; } }); BI.each(devides, function (i, devide) { switch (devide.type) { case "left-top": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, left: 5, top: 5 }); break; case "top-right": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, right: 5, top: 5 }); break; case "bottom-left": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, left: 5, bottom: 5 }); break; case "bottom-right": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, right: 5, bottom: 5 }); break; case "top-left": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, left: 25, top: 5 }); break; case "top-right-second": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, right: 25, top: 5 }); break; case "bottom-left-second": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, left: 25, bottom: 5 }); break; case "bottom-right-second": absolutes.push({ el: { type: "bi.layout", width: 10, height: 10, cls: "drop-devider" }, right: 25, bottom: 5 }); break; default: if (devide.type.indexOf("top-center") > -1) { var num = devide.type.split("top-center")[1]; horizontals.push({ el: { type: "bi.layout", width: 20, height: 10, cls: "drop-devider" }, tgap: 20 * (topcenterCount - BI.parseInt(num)) + 5 }) } else if (devide.type.indexOf("bottom-center") > -1) { var num = devide.type.split("bottom-center")[1]; horizontals.push({ el: { type: "bi.layout", width: 20, height: 10, cls: "drop-devider" }, bgap: 20 * (bottomcenterCount - BI.parseInt(num)) + 5 }) } else if (devide.type.indexOf("left-center") > -1) { var num = devide.type.split("left-center")[1]; verticals.push({ el: { type: "bi.layout", width: 10, height: 20, cls: "drop-devider" }, lgap: 20 * (leftcenterCount - BI.parseInt(num)) + 5 }) } else if (devide.type.indexOf("right-center") > -1) { var num = devide.type.split("right-center")[1]; verticals.push({ el: { type: "bi.layout", width: 10, height: 20, cls: "drop-devider" }, rgap: 20 * (rightcenterCount - BI.parseInt(num)) + 5 }) } break; } }); BI.createWidget({ type: "bi.absolute", element: drop.el, items: absolutes }); BI.createWidget({ type: "bi.absolute_horizontal_adapt", element: drop.el, items: horizontals }); BI.createWidget({ type: "bi.absolute_vertical_adapt", element: drop.el, items: verticals }); }); }, _applyRegion: function (regions) { var self = this, o = this.options; BI.each(regions || this.regions, function (i, region) { region.el.element.css({ left: region.left, top: region.top, width: region.width, height: region.height }); self.drops[region.id].left = region.left; self.drops[region.id].top = region.top; self.drops[region.id].width = region.width; self.drops[region.id].height = region.height; }); BI.each(this.drops, function (i, region) { region.el.element.css({ left: region.left, top: region.top, width: region.width, height: region.height }); }); this._applyContainer(); this._calculateDrops(); this.ratio = this.getLayoutRatio(); }, _renderRegion: function () { var self = this; BI.createWidget({ type: "bi.absolute", element: this.container, items: BI.toArray(this.regions) }); BI.createWidget({ type: "bi.absolute", element: this.droppable, items: BI.toArray(this.drops) }); }, getClientWidth: function () { return this.scrollContainer.element[0].clientWidth; }, getClientHeight: function () { return this.scrollContainer.element[0].clientHeight; }, _applyContainer: function () { //先掩藏后显示能够明确滚动条是否出现 this.scrollContainer.element.css("overflow", "hidden"); var occupied = this._getRegionOccupied(); this.container.element.width(occupied.left + occupied.width).height(occupied.top + occupied.height); this.scrollContainer.element.css("overflow", "auto"); return occupied; }, _modifyRegion: function (regions) { BI.each(this.regions, function (id, region) { if (regions[id]) { region.left = regions[id].left; region.top = regions[id].top; region.width = regions[id].width; region.height = regions[id].height; } }); BI.each(this.drops, function (id, region) { if (regions[id]) { region.left = regions[id].left; region.top = regions[id].top; region.width = regions[id].width; region.height = regions[id].height; } }); }, _addRegion: function (item) { var region = this._createOneRegion(item); this.regions[region.id] = region; var drop = this._createOneDrop(region); this.drops[drop.id] = drop; this.storeDrops[drop.id] = drop; this._locationRegion(); BI.createWidget({ type: "bi.absolute", element: this.container, items: [region] }); BI.createWidget({ type: "bi.absolute", element: this.droppable, items: [drop] }) }, _deleteRegionByName: function (name) { this.regions[name].el.setVisible(false); this.drops[name].el.setVisible(false); delete this.regions[name]; delete this.drops[name]; this._locationRegion(); }, _setArrangeSize: function (size) { this.arrangement.element.css({ left: size.left, top: size.top, width: size.width, height: size.height }) }, //Grid _getOneWidthPortion: function () { return this.getClientWidth() / BI.Arrangement.PORTION; }, _getGridPositionAndSize: function (position) { var perWidth = this._getOneWidthPortion(); var widthPortion = Math.round(position.width / perWidth); var leftPortion = Math.round(position.left / perWidth); var topPortion = Math.round(position.top / BI.Arrangement.GRID_HEIGHT); var heightPortion = Math.round(position.height / BI.Arrangement.GRID_HEIGHT); // if (leftPortion > BI.Arrangement.PORTION) { // leftPortion = BI.Arrangement.PORTION; // } // if (widthPortion > BI.Arrangement.PORTION) { // widthPortion = BI.Arrangement.PORTION; // } // if (leftPortion + widthPortion > BI.Arrangement.PORTION) { // leftPortion = BI.Arrangement.PORTION - widthPortion; // } if (widthPortion === 0) { widthPortion = 1; } if (heightPortion === 0) { heightPortion = 1; } return { x: leftPortion, y: topPortion, w: widthPortion, h: heightPortion } }, _getBlockPositionAndSize: function (position) { var perWidth = this._getOneWidthPortion(); return { left: position.x * perWidth, top: position.y * BI.Arrangement.GRID_HEIGHT, width: position.w * perWidth, height: position.h * BI.Arrangement.GRID_HEIGHT }; }, _getLayoutsByRegions: function (regions) { var self = this; var result = []; BI.each(regions || this.regions, function (id, region) { result.push(BI.extend(self._getGridPositionAndSize(region), { i: region.id })) }); return result; }, _getLayoutIndexByName: function (layout, name) { return BI.findIndex(layout, function (i, l) { return l.i === name; }); }, _setBlockPositionAndSize: function (size) { this.block.element.css({ left: size.left, top: size.top, width: size.width, height: size.height }); }, _getRegionsByLayout: function (layout) { var self = this; var regions = {}; BI.each(layout, function (i, ly) { regions[ly.i] = BI.extend(self._getBlockPositionAndSize(ly), { id: ly.i }); }); return regions; }, _setRegionsByLayout: function (regions, layout) { var self = this; regions || (regions = this.regions); BI.each(layout, function (i, ly) { if (regions[ly.i]) { BI.extend(regions[ly.i], self._getBlockPositionAndSize(ly)); } }); return regions; }, _moveElement: function (layout, l, x, y, isUserAction) { var self = this; if (l._static) { return layout; } if (l.y === y && l.x === x) { return layout; } var movingUp = y && l.y > y; if (typeof x === 'number') { l.x = x; } if (typeof y === 'number') { l.y = y; } l.moved = true; var sorted = this._sortLayoutItemsByRowCol(layout); if (movingUp) { sorted = sorted.reverse(); } var collisions = getAllCollisions(sorted, l); for (var i = 0, len = collisions.length; i < len; i++) { var collision = collisions[i]; if (collision.moved) { continue; } if (l.y > collision.y && l.y - collision.y > collision.h / 4) { continue; } if (collision._static) { layout = this._moveElementAwayFromCollision(layout, collision, l, isUserAction); } else { layout = this._moveElementAwayFromCollision(layout, l, collision, isUserAction); } } return layout; function getAllCollisions(layout, layoutItem) { return BI.filter(layout, function (i, l) { return self._collides(l, layoutItem); }); } }, _sortLayoutItemsByRowCol: function (layout) { return [].concat(layout).sort(function (a, b) { if (a.y > b.y || (a.y === b.y && a.x > b.x)) { return 1; } return -1; }); }, _collides: function (l1, l2) { if (l1 === l2) { return false; } // same element if (l1.x + l1.w <= l2.x) { return false; } // l1 is left of l2 if (l1.x >= l2.x + l2.w) { return false; } // l1 is right of l2 if (l1.y + l1.h <= l2.y) { return false; } // l1 is above l2 if (l1.y >= l2.y + l2.h) { return false; } // l1 is below l2 return true; // boxes overlap }, _getFirstCollision: function (layout, layoutItem) { for (var i = 0, len = layout.length; i < len; i++) { if (this._collides(layout[i], layoutItem)) { return layout[i]; } } }, _moveElementAwayFromCollision: function (layout, collidesWith, itemToMove, isUserAction) { if (isUserAction) { var fakeItem = { x: itemToMove.x, y: itemToMove.y, w: itemToMove.w, h: itemToMove.h, i: '-1' }; fakeItem.y = Math.max(collidesWith.y - itemToMove.h, 0); if (!this._getFirstCollision(layout, fakeItem)) { return this._moveElement(layout, itemToMove, undefined, fakeItem.y); } } return this._moveElement(layout, itemToMove, undefined, itemToMove.y + 1); }, _compactItem: function (compareWith, l, verticalCompact) { if (verticalCompact) { while (l.y > 0 && !this._getFirstCollision(compareWith, l)) { l.y--; } } var collides; while ((collides = this._getFirstCollision(compareWith, l))) { l.y = collides.y + collides.h; } return l; }, compact: function (layout, verticalCompact) { var compareWith = getStatics(layout); var sorted = this._sortLayoutItemsByRowCol(layout); var out = []; for (var i = 0, len = sorted.length; i < len; i++) { var l = sorted[i]; if (!l._static) { l = this._compactItem(compareWith, l, verticalCompact); compareWith.push(l); } out[layout.indexOf(l)] = l; l.moved = false; } return out; function getStatics(layout) { return BI.filter(layout, function (i, l) { return l._static; }); } }, ////公有方法//// getRegionByName: function (name) { var obj = {}; obj[name] = this.regions[name]; return this._cloneRegion(obj)[name]; }, getAllRegions: function () { return BI.toArray(this._cloneRegion()); }, getHelper: function () { var helper = BI.createWidget({ type: "bi.layout", width: 18, height: 18, cls: "arrangement-helper" }); BI.createWidget({ type: "bi.absolute", element: this, items: [helper] }); return helper; }, _start: function (cur) { this.arrangement.setVisible(true); this.droppable.setVisible(true); if (this.options.layoutType === BI.Arrangement.LAYOUT_TYPE.GRID) { this.block.setVisible(true); } BI.each(this.drops, function (i, drop) { drop.el.setVisible(false); }); if (cur) { if (this.drops[cur]) { this.drops[cur].el.setVisible(true); } } }, _stop: function () { this.arrangement.setVisible(false); this.droppable.setVisible(false); this.block.setVisible(false); }, getDirectRelativeRegions: function (name, direction) { direction || (direction = ["top", "bottom", "left", "right"]); var self = this, result = {}; BI.each(direction, function (i, dir) { result[dir] = self._getDirectRelativeRegions(name, [dir]); }); return result; }, ////公有操作//// setLayoutType: function (type) { var self = this, o = this.options; if (type !== o.layoutType) { o.layoutType = type; switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: this.relayout(); break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: this.relayout(); break; } } }, getLayoutType: function () { return this.options.layoutType; }, getLayoutRatio: function () { var occupied = this._getRegionOccupied(); var width = this.getClientWidth(), height = this.getClientHeight(); return { x: BI.parseFloat(BI.contentFormat((occupied.left + occupied.width) / width, "#.##;-#.##")), y: BI.parseFloat(BI.contentFormat((occupied.top + occupied.height) / height, "#.##;-#.##")) } }, addRegion: function (region, position) { if (position) { this.setPosition(position, region); } var self = this, o = this.options; if (!this.position) { return false; } var test = this._cloneRegion(); BI.each(this.position.regions, function (i, region) { test[region.id].left = region.left; test[region.id].top = region.top; test[region.id].width = region.width; test[region.id].height = region.height; }); var item = BI.extend({}, region, { left: this.position.insert.left, top: this.position.insert.top, width: this.position.insert.width, height: this.position.insert.height }); var added = this._createOneRegion(item); test[added.id] = added; if (this._test(test)) { delete test[added.id]; this._modifyRegion(test); this._addRegion(item); this._populate(this.getAllRegions()); return true; } return false; }, deleteRegion: function (name) { if (!this.regions[name]) { return false; } var self = this, o = this.options; switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: var clone = this._cloneRegion(); var regions = this._getEquivalentRelativeRegions(name); if (regions.length > 0) { BI.each(regions, function (i, region) { BI.extend(clone[region.id], region); }); this._modifyRegion(clone); } this._deleteRegionByName(name); this._populate(this.getAllRegions()); return true; case BI.Arrangement.LAYOUT_TYPE.FREE: this._deleteRegionByName(name); this._populate(this.getAllRegions()); return true; case BI.Arrangement.LAYOUT_TYPE.GRID: this._deleteRegionByName(name); this._populate(this.getAllRegions()); this.resize(); return true; } return false; }, setRegionSize: function (name, size) { var self = this, o = this.options; var flag = false; switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: var current = this.regions[name]; if (size.width !== current.width) { var regions = this._getInDirectRelativeRegions(name, ["right"]).right; var lefts = regions.left || [], rights = regions.right || []; var offset = size.width - current.width; var cloned = this._cloneRegion(); BI.some(lefts, function (i, left) { var region = cloned[left.id]; region.width = region.width + offset; }); BI.some(rights, function (i, right) { var region = cloned[right.id]; region.width = region.width - offset; region.left = region.left + offset; }); if (this._test(cloned) && this._isArrangeFine(cloned)) { this._modifyRegion(cloned); flag = true; } } if (size.height !== current.height) { var regions = this._getInDirectRelativeRegions(name, ["bottom"]).bottom; var tops = regions.top || [], bottoms = regions.bottom || []; var offset = size.height - current.height; var cloned = this._cloneRegion(); BI.some(tops, function (i, top) { var region = cloned[top.id]; region.height = region.height + offset; }); BI.some(bottoms, function (i, bottom) { var region = cloned[bottom.id]; region.height = region.height - offset; region.top = region.top + offset; }); if (this._test(cloned) && this._isArrangeFine(cloned)) { this._modifyRegion(cloned); flag = true; } } break; case BI.Arrangement.LAYOUT_TYPE.FREE: var clone = this._cloneRegion(); BI.extend(clone[name], { width: size.width, height: size.height }); if (this._test(clone)) { this._modifyRegion(clone); flag = true; } break; case BI.Arrangement.LAYOUT_TYPE.GRID: var clone = this._cloneRegion(); BI.extend(clone[name], { width: size.width, height: size.height }); if (this._test(clone)) { var layout = this._getLayoutsByRegions(clone); layout = this.compact(layout, true); var regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); flag = true; } break; } this._locationRegion(); this._applyRegion(); return flag; }, setPosition: function (position, size) { var self = this, o = this.options; var insert, regions = [], cur; if (position.left < 0 || position.top < 0) { switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: this.resize(); break; } this._stop(); this.position = null; return null; } var offset = this._getScrollOffset(); position = { left: position.left + offset.left, top: position.top + offset.top }; switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (BI.isEmpty(this.regions)) { if (self._isPositionInBounds(position, { left: 0, top: 0, width: this.element[0].clientWidth, height: this.element[0].clientHeight })) { insert = { left: 0, top: 0, width: this.element[0].clientWidth, height: this.element[0].clientHeight }; } } else { if (BI.some(this.regions, function (id, region) { if (self._isPositionInBounds(position, region)) { var at = self._positionAt(position, region); if (!at) { insert = null; } else { insert = at.insert; regions = at.regions; } cur = id; return true; } })) { } else { insert = null; regions = []; } } if (insert == null) { this._stop(); self.position = null; break; } this.position = { insert: insert, regions: regions }; this._setArrangeSize(insert); this._start(cur); break; case BI.Arrangement.LAYOUT_TYPE.FREE: var insert = { top: position.top < 0 ? 0 : position.top, left: position.left < 0 ? 0 : position.left, width: size.width, height: size.height }; this.position = { insert: insert }; this._setArrangeSize(insert); this._start(); break; case BI.Arrangement.LAYOUT_TYPE.GRID: var p = { top: position.top < 0 ? 0 : position.top, left: position.left < 0 ? 0 : position.left, width: size.width, height: size.height }; this._setArrangeSize(p); var cur = this._getGridPositionAndSize(p); var layout = [{ x: 0, y: BI.MAX, w: cur.w, h: cur.h, i: cur.i }].concat(this._getLayoutsByRegions()); layout = this._moveElement(layout, layout[0], cur.x, cur.y, true); layout = this.compact(layout, true); var regions = this._setRegionsByLayout(this._cloneRegion(), layout); var insert = this._getBlockPositionAndSize(layout[0]); this.position = { insert: insert, regions: regions }; this._applyRegion(regions); this._setBlockPositionAndSize(insert); this._start(); break; } return this.position; }, setRegionPosition: function (name, position) { var self = this, o = this.options; var offset = this._getScrollOffset(); position = BI.extend(position, { left: position.left + offset.left, top: position.top + offset.top }); switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: break; case BI.Arrangement.LAYOUT_TYPE.FREE: BI.extend(this.regions[name], { left: position.left < 0 ? 0 : position.left, top: position.top < 0 ? 0 : position.top }); this._applyRegion(); break; case BI.Arrangement.LAYOUT_TYPE.GRID: if (!position.stop) { BI.extend(this.regions[name], { left: position.left < 0 ? 0 : position.left, top: position.top < 0 ? 0 : position.top }); var cloned = this._cloneRegion(); var cur = this._getGridPositionAndSize(BI.extend(cloned[name], { left: position.left < 0 ? 0 : position.left, top: position.top < 0 ? 0 : position.top })); var x = cur.x, y = cur.y; cur = BI.extend(cur, { x: 0, y: BI.MAX, i: -1 }); delete cloned[name]; var layout = this._getLayoutsByRegions(cloned); layout = this._moveElement([cur].concat(layout), cur, x, y, true); layout = this.compact(layout, true); var regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); this._applyRegion(); this._setBlockPositionAndSize(this._getBlockPositionAndSize(cur)); this.block.setVisible(true); } else { BI.extend(this.regions[name], { left: position.left < 0 ? 0 : position.left, top: position.top < 0 ? 0 : position.top }); var cloned = this._cloneRegion(); var layout = this._getLayoutsByRegions(cloned); layout = this.compact(layout, true); var regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); this._applyRegion(); this.block.setVisible(false); } break; } }, setContainerSize: function (size) { var self = this, o = this.options; var occupied = this._getRegionOccupied(); switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (this._isArrangeFine()) { var width = size.width, height = size.height; var regions = this._cloneRegion(); BI.each(regions, function (i, region) { region.width = region.width / occupied.width * width; //region.height = region.height / occupied.height * height; }); BI.each(regions, function (id, region) { var lefts = self.locations[id].left; var tops = self.locations[id].top; var bottoms = self.locations[id].bottom; var maxRegion; if (lefts.length > 0) { var ids = self._getRegionNames(lefts); var rs = self._getRegionsByNames(ids); maxRegion = self._getRegionOccupied(rs); region.left = maxRegion.left + maxRegion.width / occupied.width * width; } else { region.left = 0; } if (bottoms.length === 0) { region.height = height - region.top; } //if (tops.length > 0) { // var ids = self._getRegionNames(tops); // var rs = self._getRegionsByNames(ids); // maxRegion = self._getRegionOccupied(rs); // region.top = maxRegion.top + maxRegion.height / occupied.height * height; //} //if (tops.length === 0) { // region.top = 0; //} }); if (this._test(regions)) { this._modifyRegion(regions); this._applyRegion(); } } break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: break; } this.resize(); }, scrollTo: function (top) { this.scrollContainer.element.scrollTop(top); }, zoom: function (ratio) { var self = this, o = this.options; if (!ratio) { return; } var occupied = this._applyContainer(); switch (this.getLayoutType()) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (this._isArrangeFine()) { var width = this.getClientWidth(); var xRatio = (ratio.x || 1) * width / (occupied.left + occupied.width); //var yRatio = ratio.y * height / (occupied.top + occupied.height); var regions = this._cloneRegion(); BI.each(regions, function (i, region) { region.left = region.left * xRatio; //region.top = region.top * yRatio; region.width = region.width * xRatio; //region.height = region.height * yRatio; }); if (this._test(regions)) { this._modifyRegion(regions); this._applyRegion(); } this.resize(); // } else { this.relayout(); } break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: if (this._isArrangeFine()) { var width = this.getClientWidth(); var xRatio = (ratio.x || 1) * width / (occupied.left + occupied.width); var regions = this._cloneRegion(); BI.each(regions, function (i, region) { region.left = region.left * xRatio; region.width = region.width * xRatio; }); if (this._test(regions)) { var layout = this._getLayoutsByRegions(regions); layout = this.compact(layout, true); regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); this._applyRegion(); } } else { this.relayout(); } break; } }, resize: function () { var self = this, o = this.options; var occupied = this._applyContainer(); switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (this._isArrangeFine()) { var width = this.getClientWidth(), height = this.getClientHeight(); var isHeightAdjust = height > occupied.top + occupied.height; var regions = this._cloneRegion(); BI.each(regions, function (i, region) { region.width = region.width / occupied.width * width; //if (isHeightAdjust) { // region.height = region.height / occupied.height * height; //} }); BI.each(regions, function (id, region) { var lefts = self.locations[id].left; var tops = self.locations[id].top; var bottoms = self.locations[id].bottom; var maxRegion; if (lefts.length > 0) { var ids = self._getRegionNames(lefts); var rs = self._getRegionsByNames(ids); maxRegion = self._getRegionOccupied(rs); region.left = maxRegion.left + maxRegion.width / occupied.width * width; } else { region.left = 0; } if (tops.length === 0) { region.top = 0; } if (isHeightAdjust && bottoms.length === 0) { region.height = height - region.top; } //if (isHeightAdjust && tops.length > 0) { // var ids = self._getRegionNames(tops); // var rs = self._getRegionsByNames(ids); // maxRegion = self._getRegionOccupied(rs); // region.top = maxRegion.top + maxRegion.height / occupied.height * height; //} }); if (this._test(regions)) { this._modifyRegion(regions); this._applyRegion(); } } else { this.relayout(); } break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: this.zoom(this.ratio); var regions = this._cloneRegion(); var layout = this._getLayoutsByRegions(regions); layout = this.compact(layout, true); regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); this._applyRegion(); break; } }, relayout: function () { var self = this, o = this.options; if (o.isNeedReLayout === false) { return; } //var occupied = this._applyContainer(); switch (o.layoutType) { case BI.Arrangement.LAYOUT_TYPE.ADAPTIVE: if (!this._isArrangeFine()) { var width = this.getClientWidth(), height = this.getClientHeight(); var clone = BI.toArray(this._cloneRegion()); clone.sort(function (r1, r2) { if (self._isEqual(r1.top, r2.top)) { return r1.left - r2.left; } return r1.top - r2.top; }); var count = clone.length; var cols = 3, rows = Math.floor((count - 1) / 3 + 1); var w = width / cols, h = height / rows; var store = {}; BI.each(clone, function (i, region) { var row = Math.floor(i / 3), col = i % 3; BI.extend(region, { top: row * 380, left: col * w, width: w, height: 380 }); if (!store[row]) { store[row] = {}; } store[row][col] = region; }); //非3的倍数 if (count % 3 !== 0) { var lasts = store[rows - 1]; var perWidth = width / (count % 3); BI.each(lasts, function (i, region) { BI.extend(region, { left: BI.parseInt(i) * perWidth, width: perWidth }); }); } if (this._test(clone)) { this._populate(clone); this.resize(); } } else { this.resize(); } break; case BI.Arrangement.LAYOUT_TYPE.FREE: break; case BI.Arrangement.LAYOUT_TYPE.GRID: var width = this.getClientWidth(), height = this.getClientHeight(); var regions = this._cloneRegion(); var clone = BI.toArray(regions); clone.sort(function (r1, r2) { if (self._isEqual(r1.top, r2.top)) { return r1.left - r2.left; } return r1.top - r2.top; }); var count = clone.length; var cols = 3, rows = Math.floor((count - 1) / 3 + 1); var w = width / cols, h = height / rows; var store = {}; BI.each(clone, function (i, region) { var row = Math.floor(i / 3), col = i % 3; BI.extend(region, { top: row * 380, left: col * w, width: w, height: 380 }); if (!store[row]) { store[row] = {}; } store[row][col] = region; }); //非3的倍数 if (count % 3 !== 0) { var lasts = store[rows - 1]; var perWidth = width / (count % 3); BI.each(lasts, function (i, region) { BI.extend(region, { left: BI.parseInt(i) * perWidth, width: perWidth }); }); } if (this._test(clone)) { var layout = this._getLayoutsByRegions(regions); layout = this.compact(layout, true); regions = this._getRegionsByLayout(layout); this._modifyRegion(regions); this._populate(clone); } break; } }, _populate: function (items) { this._stop(); this._calculateRegions(items); this._locationRegion(); this._applyRegion(); }, populate: function (items) { var self = this; BI.each(this.regions, function (name, region) { self.regions[name].el.setVisible(false); self.drops[name].el.setVisible(false); delete self.regions[name]; delete self.drops[name]; }); this._populate(items); this._renderRegion(); } }); BI.Arrangement.EVENT_SCROLL = "EVENT_SCROLL"; BI.extend(BI.Arrangement, { PORTION: 24, GRID_HEIGHT: 50, LAYOUT_TYPE: { ADAPTIVE: 0, FREE: 1, GRID: 2 } }); BI.shortcut('bi.arrangement', BI.Arrangement);